import { Ionicons } from '@expo/vector-icons'; import { LinearGradient } from 'expo-linear-gradient'; import { useRouter } from 'expo-router'; import { ActivityIndicator, Alert, Pressable, ScrollView, StyleSheet, Text, View, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import Svg, { Circle, Line, Polygon } from 'react-native-svg'; import { useHollandAssessment } from '../src/hooks'; import { colors, headerGradientShort } from '../src/theme'; import type { HollandDimension, HollandTypeAnalysis } from '../src/types'; const RADAR_SIZE = 160; const RADAR_CENTER = RADAR_SIZE / 2; const RADAR_MAX_RADIUS = RADAR_SIZE / 2 - 8; const TYPE_COLORS: Record = { R: colors.primary, I: colors.info, A: colors.action, S: colors.success, E: colors.compareOrange, C: colors.textHint, }; function radarPoint(index: number, total: number, radius: number) { const angle = (Math.PI * 2 * index) / total - Math.PI / 2; return { x: RADAR_CENTER + radius * Math.cos(angle), y: RADAR_CENTER + radius * Math.sin(angle), }; } function RadarChart({ dimensions }: { dimensions: HollandDimension[] }) { const total = dimensions.length; const gridPolygons = [1, 2, 3].map((level) => dimensions .map((_, i) => { const p = radarPoint(i, total, (RADAR_MAX_RADIUS * level) / 3); return `${p.x},${p.y}`; }) .join(' '), ); const axes = dimensions.map((_, i) => radarPoint(i, total, RADAR_MAX_RADIUS)); const dataVertices = dimensions.map((d, i) => radarPoint(i, total, (RADAR_MAX_RADIUS * d.percent) / 100), ); const dataPoints = dataVertices.map((p) => `${p.x},${p.y}`).join(' '); return ( {gridPolygons.map((points, i) => ( ))} {axes.map((p, i) => ( ))} {dataVertices.map((p, i) => ( ))} ); } /* ===================== 子组件:顶部导航 ===================== */ function Header({ onBack, onShare }: { onBack: () => void; onShare: () => void }) { return ( 测评结果 ); } /* ===================== 子组件:结果 Hero ===================== */ function ResultHero({ code, name, dimensions, }: { code: string; name: string; dimensions: HollandDimension[]; }) { return ( 你的职业兴趣类型 {code} 型 {name} ); } /* ===================== 子组件:主导类型解析 ===================== */ function TypeCard({ analysis, index }: { analysis: HollandTypeAnalysis; index: number }) { const rankColors = [colors.primary, colors.success, colors.info]; return ( {index + 1} {analysis.name}({analysis.type}) {analysis.score}分 {analysis.description} {analysis.careers.map((career) => ( {career} ))} ); } /* ===================== 子组件:推荐岗位 ===================== */ function JobCard({ job }: { job: import('../src/types').HollandJobRecommendation }) { return ( {job.title} {job.companyName} · {job.city} 匹配度 {job.match}% {job.tags.map((tag) => ( {tag} ))} {job.salary} ); } /* ===================== 子组件:行动建议 ===================== */ function ActionCard({ action }: { action: import('../src/types').HollandAction }) { return ( {action.title} {action.desc} ); } /* ===================== 子组件:四态 ===================== */ function Loading() { return ( 正在加载结果… ); } function ErrorState({ onRetry }: { onRetry: () => void }) { return ( 结果加载失败 点击重试 ); } /* ===================== 页面 ===================== */ export default function HollandResultScreen() { const router = useRouter(); const { assessment, loading, error, refresh } = useHollandAssessment(); const result = assessment?.result; // 右上角「分享」暂无对应页面,按项目约定弹提示(此前绑定空函数,点击无任何反馈) const handleShare = () => { Alert.alert('提示', '分享测评报告功能开发中,敬请期待'); }; if (loading) { return (
router.back()} onShare={handleShare} /> ); } if (error || !result) { return (
router.back()} onShare={handleShare} /> ); } return (
router.back()} onShare={handleShare} /> 主导类型解析 {result.analyses.map((a, i) => ( ))} 为你推荐的岗位 {result.jobs.map((job) => ( ))} 下一步行动建议 {result.actions.map((action) => ( ))} ); } 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' }, headerTitle: { fontSize: 17, fontWeight: '700', color: colors.textPrimary }, body: { padding: 16, paddingBottom: 32 }, heroCard: { borderRadius: 16, padding: 20, flexDirection: 'row', alignItems: 'center', marginBottom: 16, }, heroLeft: { flex: 1 }, heroLabel: { fontSize: 13, color: colors.textInverse, opacity: 0.9 }, heroCode: { fontSize: 42, fontWeight: '800', color: colors.textInverse, marginTop: 6 }, heroName: { fontSize: 14, color: colors.textInverse, opacity: 0.95, marginTop: 6, lineHeight: 20 }, heroRadar: { marginLeft: 12 }, section: { marginBottom: 16 }, sectionTitle: { fontSize: 18, fontWeight: '700', color: colors.textPrimary, marginBottom: 12 }, typeCard: { backgroundColor: colors.white, borderRadius: 12, padding: 16, marginBottom: 10, borderWidth: 1, borderColor: colors.borderLight, }, typeHeader: { flexDirection: 'row', alignItems: 'center', marginBottom: 10 }, typeRank: { width: 24, height: 24, borderRadius: 12, alignItems: 'center', justifyContent: 'center', }, typeRankText: { fontSize: 13, fontWeight: '800', color: colors.white }, typeTitleWrap: { flex: 1, marginLeft: 10 }, typeName: { fontSize: 15, fontWeight: '700', color: colors.textPrimary }, typeScore: { fontSize: 13, color: colors.primary, marginTop: 2 }, typeDesc: { fontSize: 13, color: colors.textSecondary, lineHeight: 20 }, careers: { flexDirection: 'row', flexWrap: 'wrap', gap: 8, marginTop: 10 }, careerTag: { backgroundColor: colors.primaryLight, borderRadius: 10, paddingHorizontal: 10, paddingVertical: 4, }, careerTagText: { fontSize: 12, color: colors.primaryText }, jobCard: { backgroundColor: colors.white, borderRadius: 12, padding: 14, marginBottom: 10, borderWidth: 1, borderColor: colors.borderLight, }, jobTop: { flexDirection: 'row', justifyContent: 'space-between' }, jobTitle: { fontSize: 15, fontWeight: '700', color: colors.textPrimary }, jobCompany: { fontSize: 12, color: colors.textSecondary, marginTop: 2 }, matchTag: { backgroundColor: colors.successLight, borderRadius: 10, paddingHorizontal: 8, paddingVertical: 2, }, matchText: { fontSize: 11, color: colors.successText, fontWeight: '600' }, jobTags: { flexDirection: 'row', gap: 8, marginTop: 10 }, tag: { backgroundColor: colors.pageBg, borderRadius: 8, paddingHorizontal: 8, paddingVertical: 2, }, tagText: { fontSize: 11, color: colors.textSecondary }, jobSalary: { fontSize: 16, fontWeight: '800', color: colors.danger, marginTop: 10 }, actionCard: { flexDirection: 'row', alignItems: 'center', backgroundColor: colors.white, borderRadius: 12, padding: 14, marginBottom: 10, borderWidth: 1, borderColor: colors.borderLight, }, actionBody: { flex: 1, marginLeft: 12 }, actionTitle: { fontSize: 14, fontWeight: '700', color: colors.textPrimary }, actionDesc: { fontSize: 12, color: colors.textSecondary, marginTop: 2 }, 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 }, });