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 (
能力成长趋势
{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: 14,
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: 12,
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: 12,
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: 12,
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: 12,
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 },
});