import { Ionicons } from '@expo/vector-icons'; import { LinearGradient } from 'expo-linear-gradient'; import { useRouter } from 'expo-router'; import { ActivityIndicator, Pressable, ScrollView, StyleSheet, Text, View, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useProfileData } from '../../src/hooks'; import { UNSET_LABEL } from '../../src/constants'; import { Avatar } from '../../components/onboarding/ui/Avatar'; import { isAvatarPreset } from '../../components/onboarding/assets'; import { useRole } from '../../src/context/RoleContext'; import { colors, headerGradientSolid } from '../../src/theme'; import type { Badge, DimensionScore, EnterpriseEntry, JobPreference, MenuItem, UserStats } from '../../src/types'; /* ===================== 展示层映射(全部使用主题令牌) ===================== */ /** 维度等级 → 展示配置(优秀=品牌蓝,良好=灰色) */ const DIMENSION_LEVEL: Record< DimensionScore['level'], { label: string; bar: string; bg: string; text: string } > = { excellent: { label: '优秀', bar: colors.primary, bg: colors.primaryLight, text: colors.primaryDark }, good: { label: '良好', bar: colors.textHint, bg: colors.pageBg, text: colors.textSecondary }, }; /** 求职偏好字段 → 展示标签 */ const PREF_ITEMS: { key: keyof JobPreference; label: string }[] = [ { key: 'position', label: '期望职位' }, { key: 'city', label: '期望城市' }, { key: 'salary', label: '期望薪资' }, { key: 'workType', label: '工作类型' }, { key: 'status', label: '求职状态' }, ]; /** 功能菜单 → 图标底色(浅)与图标色(深),区分不同菜单避免单调 */ const MENU_ICON_THEME: Record = { resume: { bg: colors.primaryLight, color: colors.primaryDark }, favorites: { bg: colors.actionLight, color: colors.actionText }, history: { bg: colors.infoLight, color: colors.infoText }, compare: { bg: colors.primaryLight, color: colors.primaryBtn }, 'offer-compare': { bg: colors.actionLight, color: colors.actionText }, assessment: { bg: colors.successLight, color: colors.successText }, interview: { bg: colors.infoLight, color: colors.infoText }, rank: { bg: colors.primaryLight, color: colors.primaryText }, 'hr-evaluation': { bg: colors.actionLight, color: colors.actionText }, onboarding: { bg: colors.successLight, color: colors.successText }, 'skill-cloud': { bg: colors.successLight, color: colors.successText }, salary: { bg: colors.primaryLight, color: colors.primaryBtn }, progress: { bg: colors.infoLight, color: colors.infoText }, game: { bg: colors.successLight, color: colors.successText }, legal: { bg: colors.primaryLight, color: colors.primaryDark }, 'job-publish': { bg: colors.actionLight, color: colors.actionText }, 'job-evaluations': { bg: colors.successLight, color: colors.successText }, 'candidate-detail': { bg: colors.infoLight, color: colors.infoText }, 'interview-score': { bg: colors.actionLight, color: colors.actionText }, 'job-management': { bg: colors.primaryLight, color: colors.primary }, 'talent-recommendation': { bg: colors.infoLight, color: colors.infoText }, 'enterprise-chat': { bg: colors.successLight, color: colors.successText }, 'operation-rules': { bg: colors.primaryLight, color: colors.primaryText }, 'audit-report': { bg: colors.infoLight, color: colors.infoText }, 'enterprise-certification': { bg: colors.primaryLight, color: colors.primary }, }; function getMenuIconBg(id: string): string { return MENU_ICON_THEME[id]?.bg ?? colors.primaryLight; } function getMenuIconColor(id: string): string { return MENU_ICON_THEME[id]?.color ?? colors.primaryDark; } /* ===================== 子组件 ===================== */ /** 单个维度评分行(横向评分条) */ function DimensionRow({ dimension }: { dimension: DimensionScore }) { const theme = DIMENSION_LEVEL[dimension.level]; return ( {dimension.name} {dimension.score} {theme.label} ); } /** 单个勋章卡片(已获得=彩色,未获得=灰色+进度条) */ function BadgeCard({ badge }: { badge: Badge }) { return ( {badge.icon} {badge.title} {badge.description} {badge.obtained ? ( 已获得 ) : ( <> {badge.progress}/{badge.target} )} ); } /** 用户统计数据行 */ function StatsRow({ stats }: { stats: UserStats }) { const items: { key: keyof UserStats; label: string }[] = [ { key: 'deliveries', label: '投递' }, { key: 'favorites', label: '收藏' }, { key: 'chats', label: '沟通' }, { key: 'offers', label: 'Offer' }, ]; return ( {items.map((item, index) => { const isLast = index === items.length - 1; return ( {stats[item.key]} {item.label} ); })} ); } /** 切换到企业端卡片 */ function EnterpriseEntryCard({ entry }: { entry: EnterpriseEntry }) { const router = useRouter(); const { setRole } = useRole(); return ( { setRole('enterprise'); router.replace('/(enterprise)/enterprise-overview'); }} hitSlop={4} > {entry.title} {entry.description} {entry.action} ); } /** * 我的页 */ export default function ProfileScreen() { const { role } = useRole(); const { profile, userStats, overallScore, dimensions, preference, badges, menuItems, enterpriseEntry, loading, error, refresh, } = useProfileData(role ?? 'candidate'); const router = useRouter(); // 错误态 if (error) { return ( 加载失败,请重试 重试 ); } // 加载态 if (loading || !profile || !userStats || !overallScore || !preference) { return ( ); } return ( {/* 顶部渐变头部:标题 + 用户信息(随页面滚动) */} 我的 router.push('/settings')} hitSlop={8}> {/* P21 选过头像就显示真实选择(预设素材 / 本地图片);没选过才回落到姓名首字 */} {profile.avatarKey ? ( ) : ( {profile.avatar} )} {/* 姓名未采集(P6/P16 可跳过直接进入)时显示「未设置」,不显示别人的名字 */} {profile.name || UNSET_LABEL} {profile.bio !== '' && {profile.bio}} {/* 内容区 */} {/* 切换到企业端 */} {enterpriseEntry ? : null} {/* 用户统计 */} {/* 综合评分卡片 */} {overallScore.score} 分 {overallScore.level} {overallScore.rank} {/* 八大维度评分 */} 八大维度评分 router.push('/appeal')} hitSlop={6}> 评分申诉 {dimensions.map((dimension) => ( ))} {/* 求职偏好 */} 求职偏好 router.push('/job-preference')} hitSlop={6}> 编辑 {PREF_ITEMS.map(({ key, label }, index) => { const value = preference[key]; const isLast = index === PREF_ITEMS.length - 1; // 未采集到的项显示「未设置」,不拿空串冒充已填;也就不再显示状态圆点 const isUnset = value.trim() === ''; const display = isUnset ? UNSET_LABEL : value; const isStatus = key === 'status' && !isUnset; return ( router.push('/job-preference')} > {label} {isStatus ? ( {display} ) : ( {display} )} ); })} {/* 全部勋章 */} 全部勋章 {badges.map((badge) => ( ))} {/* 功能菜单(简历 / 收藏 / 历史 / 岗位对比 / 测评 / 竞争力排名) */} 功能菜单 {menuItems.map((item: MenuItem, idx: number) => { const isLast = idx === menuItems.length - 1; return ( { if (item.route) router.push(item.route); }} hitSlop={4} > {item.title} ); })} ); } 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, }, /* ---- 顶部渐变头部 ---- */ headerGradient: { paddingHorizontal: 8, paddingTop: 8, paddingBottom: 24, borderBottomLeftRadius: 24, borderBottomRightRadius: 24, }, headerRow: { height: 40, flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', }, headerTitle: { fontSize: 17, fontWeight: '700', color: colors.textInverse, }, profileRow: { flexDirection: 'row', alignItems: 'center', marginTop: 8, }, avatar: { width: 60, height: 60, borderRadius: 30, backgroundColor: colors.white, alignItems: 'center', justifyContent: 'center', }, avatarText: { fontSize: 26, fontWeight: '700', color: colors.primary, }, profileInfo: { marginLeft: 14, }, userName: { fontSize: 20, fontWeight: '700', color: colors.textInverse, }, bio: { marginTop: 6, fontSize: 13, color: colors.textInverse, opacity: 0.85, }, /* ---- 内容区 ---- */ scroll: { flex: 1, }, scrollContent: { paddingBottom: 24, }, content: { paddingHorizontal: 8, paddingTop: 16, }, /* ---- 综合评分卡片 ---- */ scoreCard: { flexDirection: 'row', alignItems: 'center', backgroundColor: colors.white, borderRadius: 8, paddingHorizontal: 16, paddingVertical: 16, }, scoreValue: { fontSize: 40, fontWeight: '700', color: colors.primaryDark, }, scoreUnit: { fontSize: 16, fontWeight: '600', color: colors.primaryDark, }, scoreTag: { marginLeft: 12, backgroundColor: colors.primaryLight, paddingHorizontal: 10, paddingVertical: 4, borderRadius: 12, }, scoreTagText: { fontSize: 13, fontWeight: '600', color: colors.primaryDark, }, scoreRank: { flex: 1, textAlign: 'right', marginLeft: 12, fontSize: 13, color: colors.textSecondary, }, /* ---- 分区标题 ---- */ sectionHeader: { flexDirection: 'row', alignItems: 'center', marginTop: 24, marginBottom: 12, }, sectionBar: { width: 4, height: 16, borderRadius: 2, backgroundColor: colors.primary, marginRight: 8, }, sectionTitle: { fontSize: 18, fontWeight: '700', color: colors.textPrimary, }, sectionTitleGrow: { flex: 1, }, sectionLink: { fontSize: 13, color: colors.primary, }, /* ---- 八大维度(横向评分条) ---- */ dimCard: { backgroundColor: colors.white, borderRadius: 8, paddingHorizontal: 16, paddingVertical: 6, }, dimRow: { flexDirection: 'row', alignItems: 'center', paddingVertical: 12, }, dimName: { width: 78, fontSize: 13, color: colors.textSecondary, }, dimTrack: { flex: 1, height: 6, borderRadius: 3, backgroundColor: colors.border, marginHorizontal: 12, }, dimFill: { height: 6, borderRadius: 3, }, dimScore: { width: 28, textAlign: 'right', fontSize: 14, fontWeight: '700', color: colors.textPrimary, }, levelTag: { marginLeft: 10, paddingHorizontal: 8, paddingVertical: 2, borderRadius: 10, }, levelTagText: { fontSize: 11, fontWeight: '600', }, /* ---- 求职偏好 ---- */ prefCard: { backgroundColor: colors.white, borderRadius: 8, paddingHorizontal: 16, paddingVertical: 6, }, prefRow: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingVertical: 12, borderBottomWidth: 1, borderBottomColor: colors.borderLight, }, prefRowLast: { borderBottomWidth: 0, }, prefLabel: { fontSize: 14, color: colors.textSecondary, }, prefValue: { fontSize: 14, fontWeight: '600', color: colors.textPrimary, }, // 未采集态:弱化为占位符视觉,与真实填写的值区分开 prefValueUnset: { fontWeight: '400', color: colors.textHint, }, prefStatus: { flexDirection: 'row', alignItems: 'center', }, prefStatusDot: { width: 8, height: 8, borderRadius: 4, backgroundColor: colors.success, marginRight: 6, }, prefStatusText: { fontSize: 14, fontWeight: '600', color: colors.successText, }, /* ---- 全部勋章 ---- */ hScroll: { flexGrow: 0, marginHorizontal: -8, }, hScrollContent: { paddingHorizontal: 8, gap: 12, }, badgeCard: { width: 140, backgroundColor: colors.white, borderRadius: 8, padding: 16, alignItems: 'center', }, badgeIcon: { fontSize: 32, }, badgeIconLocked: { opacity: 0.35, }, badgeTitle: { marginTop: 10, fontSize: 14, fontWeight: '700', color: colors.textPrimary, }, badgeTitleLocked: { color: colors.textHint, }, badgeDesc: { marginTop: 4, fontSize: 12, color: colors.textSecondary, textAlign: 'center', }, badgeObtainedTag: { marginTop: 10, backgroundColor: colors.successLight, paddingHorizontal: 10, paddingVertical: 3, borderRadius: 10, }, badgeObtainedText: { fontSize: 11, fontWeight: '600', color: colors.successText, }, badgeProgressTrack: { width: '100%', height: 4, borderRadius: 2, backgroundColor: colors.border, marginTop: 12, }, badgeProgressFill: { height: 4, borderRadius: 2, backgroundColor: colors.primary, }, badgeProgressText: { marginTop: 6, fontSize: 11, color: colors.textHint, }, /* ---- 用户统计 ---- */ statsCard: { flexDirection: 'row', backgroundColor: colors.white, borderRadius: 8, paddingVertical: 16, marginBottom: 12, }, statItem: { flex: 1, alignItems: 'center', borderRightWidth: StyleSheet.hairlineWidth, borderRightColor: colors.borderLight, }, statItemLast: { borderRightWidth: 0 }, statValue: { fontSize: 18, fontWeight: '800', color: colors.textPrimary }, statLabel: { fontSize: 12, color: colors.textSecondary, marginTop: 4 }, /* ---- 切换到企业端 ---- */ enterpriseCard: { flexDirection: 'row', alignItems: 'center', backgroundColor: colors.white, borderRadius: 8, paddingHorizontal: 16, paddingVertical: 16, marginBottom: 12, }, enterpriseIconWrap: { width: 44, height: 44, borderRadius: 12, backgroundColor: colors.primaryLight, alignItems: 'center', justifyContent: 'center', }, enterpriseText: { flex: 1, marginLeft: 12 }, enterpriseTitle: { fontSize: 15, fontWeight: '700', color: colors.textPrimary }, enterpriseDesc: { fontSize: 12, color: colors.textSecondary, marginTop: 3 }, enterpriseAction: { fontSize: 13, fontWeight: '600', color: colors.primaryDark }, /* ---- 功能菜单 ---- */ menuCard: { backgroundColor: colors.white, borderRadius: 8, paddingHorizontal: 16, }, menuRow: { flexDirection: 'row', alignItems: 'center', paddingVertical: 14, borderBottomWidth: StyleSheet.hairlineWidth, borderBottomColor: colors.borderLight, }, menuRowLast: { borderBottomWidth: 0, }, menuIconWrap: { width: 32, height: 32, borderRadius: 8, alignItems: 'center', justifyContent: 'center', marginRight: 12, }, menuTitle: { flex: 1, fontSize: 14, fontWeight: '500', color: colors.textPrimary, }, });