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 (
router.back()} /> ); } if (error || !assessment) { return (
router.back()} /> ); } 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: 8, paddingBottom: 32 }, dimCard: { backgroundColor: colors.white, borderRadius: 8, padding: 8, marginBottom: 8, 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: 8, padding: 8, marginBottom: 8, 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: 8, marginBottom: 20 }, option: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', backgroundColor: colors.white, borderRadius: 8, paddingVertical: 8, paddingHorizontal: 8, 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 }, });