import { Ionicons } from '@expo/vector-icons'; import { LinearGradient } from 'expo-linear-gradient'; import { useRouter } from 'expo-router'; import type { ComponentProps } from 'react'; import { ActivityIndicator, 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 { useGrowthData } from '../../src/hooks'; import { colors, headerGradient } from '../../src/theme'; import { formatThousands, formatViews } from '../../src/utils'; import { JOB_LEVEL_LABEL } from '../../src/constants'; import type { AbilityDimension, JobLevel } from '../../src/types'; type IconName = ComponentProps['name']; /* ===================== 雷达图 ===================== */ const RADAR_SIZE = 132; const RADAR_CENTER = RADAR_SIZE / 2; /** 最大绘制半径(留边距,避免顶点被裁切) */ const RADAR_MAX_RADIUS = RADAR_SIZE / 2 - 4; /** 计算雷达图第 index 个顶点的坐标(从正上方开始,顺时针) */ 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), }; } /** * 四维能力雷达图:3 层网格 + 轴线 + 数据填充多边形 + 顶点圆点 */ function RadarChart({ dimensions }: { dimensions: AbilityDimension[] }) { const total = dimensions.length; // 3 层网格(1/3、2/3、3/3 半径) 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.value) / 100), ); const dataPoints = dataVertices.map((p) => `${p.x},${p.y}`).join(' '); return ( {gridPolygons.map((points, i) => ( ))} {axes.map((p, i) => ( ))} {dataVertices.map((p, i) => ( ))} ); } /* ===================== 配色映射 ===================== */ /** 能力等级 → 文字颜色(与岗位评分标签一致) */ const LEVEL_TEXT_COLOR: Record = { excellent: colors.primaryDark, good: colors.primary, medium: colors.textSecondary, }; /** AI 工具卡片色调映射 */ const TOOL_STYLE = { primary: { bg: colors.primaryLight, border: colors.primaryBorder, icon: colors.primary, action: colors.primaryText, }, success: { bg: colors.successLight, border: colors.successBorder, icon: colors.success, action: colors.successText, }, } as const; /** 文章缩略图配色映射(复用信息/行动/成功三组语义色) */ const THUMBNAIL_STYLE = { violet: { bg: colors.infoLight, color: colors.info }, orange: { bg: colors.actionLight, color: colors.action }, green: { bg: colors.successLight, color: colors.success }, } as const; /* ===================== 页面 ===================== */ /** * 成长中心页 */ export default function GrowthScreen() { const router = useRouter(); const { holland, ability, aiTools, articles, loading, error, refresh } = useGrowthData(); // 错误态 if (error) { return ( 加载失败,请重试 重试 ); } // 加载态 if (loading || !holland || !ability) { return ( ); } return ( {/* 1. 顶部导航 */} 成长中心 { /* TODO: 设置/更多 */ }}> {/* 2. 霍兰德职业兴趣测评 Hero Card */} 霍兰德职业兴趣测评 发现你的职业性格类型 router.push('/holland')}> 立即测评 已有 {formatThousands(holland.totalParticipants)} 人完成 {/* 3. 四维能力测评 */} 四维能力测评 router.push('/ability')}> 查看报告 > {ability.overallScore} 分 {JOB_LEVEL_LABEL[ability.level]} {ability.dimensions.map((dim) => ( {dim.label} {dim.value} ))} router.push('/ability')}> 重新测评 {/* 4. AI 求职工具 */} AI求职工具 {aiTools.map((tool) => { const style = TOOL_STYLE[tool.variant]; return ( { if (tool.id === 'ai-resume') router.push('/resume'); else if (tool.id === 'ai-interview') router.push('/interview'); }} > {tool.title} {tool.description} {tool.action} → ); })} {/* 5. 职场干货 */} 职场干货 { /* TODO: 更多 */ }}> 更多 > {articles.map((article) => { const thumb = THUMBNAIL_STYLE[article.thumbnailColor]; return ( { /* TODO: 阅读文章 */ }} > {article.title} {article.tag} {formatViews(article.views)}阅读 ); })} ); } const styles = StyleSheet.create({ safeArea: { flex: 1, backgroundColor: colors.pageBg, }, stateWrap: { flex: 1, alignItems: 'center', justifyContent: 'center', }, stateText: { marginTop: 12, fontSize: 14, color: colors.textSecondary, }, retryBtn: { marginTop: 16, paddingHorizontal: 24, paddingVertical: 8, borderRadius: 18, backgroundColor: colors.primaryBtn, }, retryText: { fontSize: 13, fontWeight: '600', color: colors.white, }, scroll: { flex: 1, }, content: { paddingHorizontal: 8, paddingBottom: 24, }, /* ---- 顶部导航 ---- */ header: { height: 48, flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-start', }, headerTitle: { fontSize: 17, fontWeight: '700', color: colors.textInverse, }, headerAction: { position: 'absolute', right: 0, width: 40, height: 40, alignItems: 'center', justifyContent: 'center', }, /* ---- 霍兰德 Hero Card ---- */ heroCard: { flexDirection: 'row', marginTop: 8, borderRadius: 8, padding: 20, }, heroIcon: { width: 52, height: 52, borderRadius: 26, backgroundColor: colors.white, alignItems: 'center', justifyContent: 'center', }, heroContent: { flex: 1, marginLeft: 14, }, heroTitle: { fontSize: 18, fontWeight: '700', color: colors.white, }, heroSubtitle: { marginTop: 6, fontSize: 13, color: colors.white, opacity: 0.9, }, heroBottom: { flexDirection: 'row', alignItems: 'center', marginTop: 16, }, heroButton: { borderRadius: 18, paddingHorizontal: 16, paddingVertical: 8, backgroundColor: colors.white, }, heroButtonText: { fontSize: 14, fontWeight: '600', color: colors.primaryText, }, heroParticipants: { marginLeft: 10, fontSize: 12, color: colors.white, opacity: 0.8, }, /* ---- 通用分区标题 ---- */ sectionHeader: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginTop: 24, marginBottom: 12, }, sectionTitle: { fontSize: 18, fontWeight: '700', color: colors.textPrimary, }, sectionMore: { fontSize: 13, color: colors.primary, }, /* ---- 四维能力测评卡片 ---- */ abilityCard: { backgroundColor: colors.white, borderRadius: 8, padding: 16, }, abilityTop: { flexDirection: 'row', alignItems: 'center', }, scoreBlock: { flex: 1, marginLeft: 16, }, scoreRow: { flexDirection: 'row', alignItems: 'center', }, scoreNumber: { fontSize: 40, fontWeight: '700', color: colors.textPrimary, }, scoreUnit: { fontSize: 13, color: colors.textSecondary, marginLeft: 4, marginTop: 16, }, levelBadge: { marginLeft: 10, paddingHorizontal: 8, paddingVertical: 3, borderRadius: 8, backgroundColor: colors.primaryLight, }, levelBadgeText: { fontSize: 12, fontWeight: '600', }, bars: { marginTop: 12, }, barRow: { flexDirection: 'row', alignItems: 'center', marginBottom: 8, }, barLabel: { width: 52, fontSize: 12, color: colors.textSecondary, }, barTrack: { flex: 1, height: 6, borderRadius: 3, backgroundColor: colors.borderLight, overflow: 'hidden', }, barFill: { height: 6, borderRadius: 3, backgroundColor: colors.primary, }, barValue: { width: 26, marginLeft: 6, fontSize: 12, fontWeight: '600', color: colors.textPrimary, textAlign: 'right', }, retestBtn: { marginTop: 16, alignSelf: 'center', paddingHorizontal: 28, paddingVertical: 9, borderRadius: 20, backgroundColor: colors.primaryLight, }, retestText: { fontSize: 14, fontWeight: '600', color: colors.primaryText, }, /* ---- AI 求职工具 ---- */ toolsRow: { flexDirection: 'row', gap: 12, }, toolCard: { flex: 1, borderRadius: 8, padding: 16, borderWidth: 1, }, toolTitle: { marginTop: 12, fontSize: 15, fontWeight: '700', color: colors.textPrimary, }, toolDesc: { marginTop: 6, fontSize: 12, color: colors.textSecondary, }, toolAction: { marginTop: 12, fontSize: 13, fontWeight: '600', }, /* ---- 职场干货 ---- */ articleCard: { flexDirection: 'row', backgroundColor: colors.white, borderRadius: 8, padding: 12, marginBottom: 8, }, articleThumb: { width: 96, height: 72, borderRadius: 12, alignItems: 'center', justifyContent: 'center', }, articleBody: { flex: 1, marginLeft: 12, justifyContent: 'space-between', }, articleTitle: { fontSize: 15, fontWeight: '600', color: colors.textPrimary, lineHeight: 20, }, articleMeta: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginTop: 8, }, articleTag: { fontSize: 11, color: colors.primaryText, backgroundColor: colors.primaryLight, paddingHorizontal: 8, paddingVertical: 2, borderRadius: 8, overflow: 'hidden', }, articleViews: { fontSize: 11, color: colors.textHint, }, });