import { Ionicons } from '@expo/vector-icons';
import { useRouter } from 'expo-router';
import { useState } from 'react';
import {
ActivityIndicator,
Pressable,
ScrollView,
StyleSheet,
Text,
View,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { useHollandAssessment } from '../../src/hooks';
import { colors, hollandTypeColors } from '../../src/theme';
import type { HollandDimension, HollandOption } from '../../src/types';
/* ===================== 子组件:顶部导航 ===================== */
function Header({ progress, onBack }: { progress: string; onBack: () => void }) {
return (
霍兰德测评
{progress}
);
}
/* ===================== 子组件:六维分布条 ===================== */
function DimensionBars({ dimensions }: { dimensions: HollandDimension[] }) {
return (
当前六维分布
{dimensions.map((d) => (
{d.type}
{d.name}
{d.score}
))}
);
}
/* ===================== 子组件:选项 ===================== */
function OptionButton({
option,
selected,
onPress,
}: {
option: HollandOption;
selected: boolean;
onPress: () => void;
}) {
return (
{option.label}
{selected && }
);
}
/* ===================== 子组件:四态 ===================== */
function Loading() {
return (
正在加载测评…
);
}
function ErrorState({ onRetry }: { onRetry: () => void }) {
return (
测评加载失败
点击重试
);
}
/* ===================== 页面 ===================== */
export default function HollandQuestionScreen() {
const router = useRouter();
const { assessment, loading, error, refresh, submit } = useHollandAssessment();
const [selectedValue, setSelectedValue] = useState(null);
const [localIndex, setLocalIndex] = useState(0);
if (loading) {
return (
);
}
if (error || !assessment) {
return (
);
}
const currentQuestion = Math.min(
assessment.currentQuestionIndex + 1 + localIndex,
assessment.totalQuestions,
);
const progressText = `第 ${currentQuestion}/${assessment.totalQuestions} 题`;
const handleNext = async () => {
if (selectedValue == null) return;
await submit(assessment.question.id, selectedValue);
setSelectedValue(null);
const nextIndex = localIndex + 1;
setLocalIndex(nextIndex);
if (nextIndex >= 3 || assessment.currentQuestionIndex + 1 >= assessment.totalQuestions) {
router.push('/holland-result');
}
};
return (
router.back()} />
请根据你的兴趣程度选择
{assessment.question.text}
{assessment.options.map((option) => (
setSelectedValue(option.value)}
/>
))}
router.back()}>
上一题
下一题
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: colors.pageBg },
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: 12,
paddingVertical: 12,
backgroundColor: colors.white,
},
headerBtn: { width: 40, alignItems: 'center' },
headerCenter: { alignItems: 'center' },
headerTitle: { fontSize: 17, fontWeight: '700', color: colors.textPrimary },
headerSub: { fontSize: 12, color: colors.textHint, marginTop: 2 },
body: { padding: 16, paddingBottom: 32 },
dimCard: {
backgroundColor: colors.white,
borderRadius: 14,
padding: 16,
marginBottom: 12,
borderWidth: 1,
borderColor: colors.borderLight,
},
dimTitle: { fontSize: 15, fontWeight: '700', color: colors.textPrimary, marginBottom: 14 },
dimGrid: { flexDirection: 'row', flexWrap: 'wrap', gap: 10 },
dimRow: {
width: '47%',
flexDirection: 'row',
alignItems: 'center',
},
dimType: {
width: 18,
fontSize: 13,
fontWeight: '800',
color: colors.textPrimary,
},
dimName: {
width: 36,
fontSize: 12,
color: colors.textSecondary,
},
dimTrack: {
flex: 1,
height: 6,
borderRadius: 3,
backgroundColor: colors.borderLight,
overflow: 'hidden',
},
dimFill: { height: 6, borderRadius: 3 },
dimScore: {
width: 28,
fontSize: 12,
fontWeight: '700',
color: colors.textPrimary,
textAlign: 'right',
},
questionCard: {
backgroundColor: colors.white,
borderRadius: 14,
padding: 24,
marginBottom: 12,
borderWidth: 1,
borderColor: colors.borderLight,
alignItems: 'center',
},
questionTitle: { fontSize: 13, color: colors.textHint, marginBottom: 16 },
questionText: {
fontSize: 20,
fontWeight: '700',
color: colors.textPrimary,
textAlign: 'center',
lineHeight: 28,
},
options: { gap: 10, marginBottom: 20 },
option: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
backgroundColor: colors.white,
borderRadius: 12,
paddingVertical: 16,
paddingHorizontal: 18,
borderWidth: 1,
borderColor: colors.borderLight,
},
optionActive: {
backgroundColor: colors.primaryLight,
borderColor: colors.primary,
},
optionText: { fontSize: 15, color: colors.textPrimary },
optionTextActive: { fontWeight: '700', color: colors.primaryText },
footer: {
flexDirection: 'row',
gap: 12,
},
navBtn: {
flex: 1,
borderRadius: 12,
paddingVertical: 14,
alignItems: 'center',
},
navBtnPrev: {
backgroundColor: colors.white,
borderWidth: 1,
borderColor: colors.border,
},
navBtnPrevText: { fontSize: 15, fontWeight: '600', color: colors.textPrimary },
navBtnNext: { backgroundColor: colors.primaryBtn },
navBtnNextDisabled: { backgroundColor: colors.textDisabled },
navBtnNextText: { fontSize: 15, fontWeight: '700', color: colors.white },
center: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingHorizontal: 40,
gap: 12,
},
tip: { fontSize: 14, color: colors.textSecondary },
retryBtn: {
paddingHorizontal: 18,
paddingVertical: 8,
borderRadius: 16,
backgroundColor: colors.primaryLight,
},
retryText: { fontSize: 13, fontWeight: '600', color: colors.primaryText },
});