import { Ionicons } from '@expo/vector-icons'; import { useRouter } from 'expo-router'; import { ActivityIndicator, Pressable, ScrollView, StyleSheet, Text, View, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import Svg, { Circle, Line, Path, Text as SvgText } from 'react-native-svg'; import { useAbilityAssessment } from '../src/hooks'; import { colors, jobLevelTextColors } from '../src/theme'; import { JOB_LEVEL_LABEL } from '../src/constants'; import type { AbilityDimension, AbilityTrendPoint, JobLevel } from '../src/types'; const DIM_COLORS = [colors.primary, colors.info, colors.success, colors.action]; const DIM_LABELS = ['专业能力', '项目经验', '技能广度', '成长潜力']; /* ===================== 子组件:顶部导航 ===================== */ function Header({ onBack, onRefresh }: { onBack: () => void; onRefresh: () => void }) { return ( 四维能力测评 ); } /* ===================== 子组件:综合评分卡 ===================== */ function ScoreCard({ score, level, percentile, }: { score: number; level: JobLevel; percentile?: number; }) { return ( 四大核心能力综合测评 {score} 分 {JOB_LEVEL_LABEL[level]} 超过 {percentile ?? 0}% 同岗位求职者 ); } /* ===================== 子组件:能力维度卡片 ===================== */ function DimensionCard({ dim, index }: { dim: AbilityDimension; index: number }) { const color = DIM_COLORS[index % DIM_COLORS.length]; return ( {dim.label} {dim.percentileText} {dim.value} 良好 {dim.subItems && ( {dim.subItems.map((item) => ( {item.label} {item.score}分 ))} )} ); } /* ===================== 子组件:趋势折线图 ===================== */ function TrendChart({ trends }: { trends: AbilityTrendPoint[] }) { const width = 320; const height = 160; const padding = { top: 20, right: 20, bottom: 30, left: 30 }; const chartW = width - padding.left - padding.right; const chartH = height - padding.top - padding.bottom; const max = 100; const months = trends.map((t) => t.month); const lines = DIM_LABELS.map((label, dimIndex) => { const key = (['professional', 'project', 'skill', 'growth'] as const)[dimIndex]; const pts = trends.map((t, i) => ({ x: padding.left + (i / (trends.length - 1)) * chartW, y: padding.top + (1 - (t[key] / max)) * chartH, })); const d = pts.map((p, i) => `${i === 0 ? 'M' : 'L'} ${p.x} ${p.y}`).join(' '); return { label, color: DIM_COLORS[dimIndex], d, last: pts[pts.length - 1] }; }); return ( 能力成长趋势 {/* 网格线 */} {[0, 25, 50, 75, 100].map((v, i) => { const y = padding.top + (1 - v / max) * chartH; return ( ); })} {/* X 轴标签 */} {months.map((m, i) => { const x = padding.left + (i / (months.length - 1)) * chartW; return ( {m} ); })} {/* 折线 */} {lines.map((line) => ( ))} {/* 端点 */} {lines.map((line) => ( ))} {lines.map((line) => ( {line.label} ))} ); } /* ===================== 子组件:提升建议 ===================== */ function ImprovementCard({ item }: { item: import('../src/types').AbilityImprovement }) { return ( {item.title} {item.description} ); } /* ===================== 子组件:四态 ===================== */ function Loading() { return ( 正在加载测评… ); } function ErrorState({ onRetry }: { onRetry: () => void }) { return ( 测评加载失败 点击重试 ); } /* ===================== 页面 ===================== */ export default function AbilityScreen() { const router = useRouter(); const { ability, loading, error, refresh } = useAbilityAssessment(); if (loading) { return (
router.back()} onRefresh={refresh} /> ); } if (error || !ability) { return (
router.back()} onRefresh={refresh} /> ); } return (
router.back()} onRefresh={refresh} /> 四维能力详解 {ability.dimensions.map((dim, i) => ( ))} 针对性提升建议 {ability.improvements.length === 0 ? ( 完成测评后,这里会给出针对性的提升建议 ) : ( ability.improvements.map((item) => ) )} ); } 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 }, scoreCard: { flexDirection: 'row', backgroundColor: colors.white, borderRadius: 8, padding: 18, marginBottom: 12, borderWidth: 1, borderColor: colors.borderLight, }, scoreLeft: { flex: 1 }, scoreLabel: { fontSize: 14, color: colors.textSecondary }, scoreRow: { flexDirection: 'row', alignItems: 'center', marginTop: 8 }, scoreValue: { fontSize: 48, fontWeight: '800', color: colors.textPrimary }, scoreUnit: { fontSize: 14, color: colors.textSecondary, marginTop: 16, marginRight: 8 }, levelTag: { borderRadius: 10, paddingHorizontal: 10, paddingVertical: 4 }, levelText: { fontSize: 12, fontWeight: '700' }, percentile: { fontSize: 13, color: colors.successText, marginTop: 8 }, scoreIcon: { justifyContent: 'center' }, section: { marginTop: 16 }, emptyBox: { backgroundColor: colors.surface, borderRadius: 8, paddingVertical: 28, paddingHorizontal: 16, alignItems: 'center', gap: 8, }, emptyText: { fontSize: 13, color: colors.textSecondary, textAlign: 'center' }, sectionTitle: { fontSize: 18, fontWeight: '700', color: colors.textPrimary, marginBottom: 12 }, dimCard: { backgroundColor: colors.white, borderRadius: 8, padding: 16, marginBottom: 10, borderWidth: 1, borderColor: colors.borderLight, }, dimHeader: { flexDirection: 'row', alignItems: 'center' }, dimIcon: { width: 36, height: 36, borderRadius: 18, alignItems: 'center', justifyContent: 'center', }, dimTitleWrap: { flex: 1, marginLeft: 10 }, dimTitle: { fontSize: 15, fontWeight: '700', color: colors.textPrimary }, dimPercentile: { fontSize: 12, color: colors.textSecondary, marginTop: 2 }, dimScoreWrap: { alignItems: 'flex-end' }, dimScore: { fontSize: 24, fontWeight: '800', color: colors.textPrimary }, dimScoreLabel: { fontSize: 11, fontWeight: '600', marginTop: 2 }, dimTrack: { height: 8, borderRadius: 4, backgroundColor: colors.borderLight, marginTop: 12, overflow: 'hidden', }, dimFill: { height: 8, borderRadius: 4 }, subItems: { marginTop: 12, gap: 10 }, subItem: { flexDirection: 'row', alignItems: 'center' }, subItemLabel: { width: 90, fontSize: 12, color: colors.textSecondary }, subItemTrack: { flex: 1, height: 6, borderRadius: 3, backgroundColor: colors.borderLight, overflow: 'hidden' }, subItemFill: { height: 6, borderRadius: 3 }, subItemScore: { width: 50, fontSize: 12, color: colors.textPrimary, textAlign: 'right' }, chartCard: { backgroundColor: colors.white, borderRadius: 8, padding: 16, marginTop: 16, borderWidth: 1, borderColor: colors.borderLight, }, chartTitle: { fontSize: 15, fontWeight: '700', color: colors.textPrimary, marginBottom: 10 }, legend: { flexDirection: 'row', flexWrap: 'wrap', gap: 12, marginTop: 8 }, legendItem: { flexDirection: 'row', alignItems: 'center' }, legendDot: { width: 8, height: 8, borderRadius: 4, marginRight: 4 }, legendText: { fontSize: 11, color: colors.textSecondary }, improveCard: { flexDirection: 'row', alignItems: 'center', backgroundColor: colors.white, borderRadius: 8, padding: 14, marginBottom: 10, borderWidth: 1, borderColor: colors.borderLight, }, improveBody: { flex: 1, marginLeft: 12 }, improveTitle: { fontSize: 14, fontWeight: '700', color: colors.textPrimary }, improveDesc: { fontSize: 12, color: colors.textSecondary, marginTop: 2, lineHeight: 18 }, 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 }, });