import { Ionicons } from '@expo/vector-icons'; import { useRouter } from 'expo-router'; import { useState } from 'react'; import { ActivityIndicator, Pressable, ScrollView, StyleSheet, Text, View, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useResume } from '../src/hooks'; import { colors } from '../src/theme'; import type { Resume as ResumeType, ResumeExperience, ResumeOptimizePoint, ResumeProject } from '../src/types'; /* ===================== 子组件:顶部导航 ===================== */ function Header({ onBack, onSave, onExport, }: { onBack: () => void; onSave: () => void; onExport: () => void; }) { return ( AI简历优化 ); } /* ===================== 子组件:评分卡 ===================== */ function ScoreCard({ score, maxScore, onOptimize, optimizing, }: { score: number; maxScore: number; onOptimize: () => void; optimizing: boolean; }) { const pct = Math.round((score / maxScore) * 100); return ( 简历评分 {score} 分 {optimizing ? ( ) : ( 优化简历 )} ); } /* ===================== 子组件:优化点列表 ===================== */ function OptimizeCard({ points, }: { points: ResumeOptimizePoint[]; }) { return ( AI 检测到 {points.length} 项可优化内容 {points.map((p) => ( {p.title} +{p.score}分 ))} 一键全部优化 ); } /* ===================== 子组件:基本信息 ===================== */ function BasicInfoCard({ info }: { info: ResumeType['basicInfo'] }) { return ( 基本信息 编辑基本信息 > {info.avatar} {info.name} {info.phone} {info.email} {info.location} ); } /* ===================== 子组件:求职意向 ===================== */ function JobIntentionCard({ intention }: { intention: ResumeType['jobIntention'] }) { return ( 求职意向 编辑 > ); } function IntentionItem({ label, value, tag, }: { label: string; value: string; tag?: boolean; }) { return ( {label} {tag ? ( {value} ) : ( {value} )} ); } /* ===================== 子组件:教育背景 ===================== */ function EducationCard({ education }: { education: ResumeType['education'] }) { return ( 教育背景 添加教育经历 {education.map((edu) => ( {edu.school} {edu.major} · {edu.degree} {edu.startYear}-{edu.endYear} ))} ); } /* ===================== 子组件:工作经历 ===================== */ function ExperienceCard({ experiences, }: { experiences: ResumeExperience[]; }) { return ( 工作经历 添加工作经历 {experiences.map((exp) => ( {exp.company} {exp.startDate} - {exp.endDate} {exp.position} {exp.description} {!exp.aiOptimized && ( AI 建议优化 AI 优化 )} ))} ); } /* ===================== 子组件:项目经历 ===================== */ function ProjectCard({ projects }: { projects: ResumeProject[] }) { return ( 项目经历 添加项目经历 {projects.map((proj) => ( {proj.name} {proj.role} {proj.description} {proj.aiOptimized && ( 已量化 )} ))} ); } /* ===================== 子组件:技能特长 ===================== */ function SkillsCard({ skills }: { skills: ResumeType['skills'] }) { return ( 技能特长 添加技能 {skills.map((skill) => ( {skill.name} ))} ); } /* ===================== 子组件:自我评价 ===================== */ function EvaluationCard({ content }: { content: string }) { return ( 自我评价 AI 润色 {content} ); } /* ===================== 子组件:四态 ===================== */ function Loading() { return ( 正在加载简历… ); } function ErrorState({ onRetry }: { onRetry: () => void }) { return ( 简历加载失败 点击重试 ); } /* ===================== 页面 ===================== */ export default function ResumeScreen() { const router = useRouter(); const { resume, loading, error, refresh, optimize } = useResume(); const [currentResume, setCurrentResume] = useState(null); const [optimizing, setOptimizing] = useState(false); const displayResume = currentResume ?? resume; const handleOptimize = async () => { setOptimizing(true); try { const optimized = await optimize(); setCurrentResume(optimized); } finally { setOptimizing(false); } }; if (loading) { return (
router.back()} onSave={() => {}} onExport={() => {}} /> ); } if (error || !displayResume) { return (
router.back()} onSave={() => {}} onExport={() => {}} /> ); } return (
router.back()} onSave={() => {}} onExport={() => router.push('/resume/export')} /> 预览 {optimizing ? ( ) : ( AI 一键优化 )} ); } 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 }, headerActions: { flexDirection: 'row', gap: 4 }, headerActionBtn: { width: 36, alignItems: 'center' }, body: { padding: 16, paddingBottom: 100 }, 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 }, card: { backgroundColor: colors.white, borderRadius: 14, padding: 16, marginBottom: 12, borderWidth: 1, borderColor: colors.borderLight, }, scoreCard: { backgroundColor: colors.white, borderRadius: 14, padding: 18, marginBottom: 12, borderWidth: 1, borderColor: colors.borderLight, }, scoreRow: { flexDirection: 'row', alignItems: 'center' }, scoreLabel: { fontSize: 13, color: colors.textSecondary }, scoreValueRow: { flexDirection: 'row', alignItems: 'baseline', marginTop: 6 }, scoreValue: { fontSize: 42, fontWeight: '800', color: colors.textPrimary }, scoreUnit: { fontSize: 14, color: colors.textSecondary, marginLeft: 4 }, scoreBarWrap: { flex: 1, marginLeft: 20 }, scoreTrack: { height: 10, borderRadius: 5, backgroundColor: colors.borderLight, overflow: 'hidden', }, scoreFill: { height: 10, borderRadius: 5, backgroundColor: colors.primary }, optimizeBtn: { marginTop: 12, alignSelf: 'flex-end', backgroundColor: colors.primaryBtn, borderRadius: 16, paddingHorizontal: 18, paddingVertical: 8, }, optimizeBtnDisabled: { backgroundColor: colors.textDisabled }, optimizeBtnText: { fontSize: 14, fontWeight: '700', color: colors.white }, cardHeader: { flexDirection: 'row', alignItems: 'center', marginBottom: 12 }, cardHeaderTitle: { fontSize: 15, fontWeight: '700', color: colors.textPrimary, marginLeft: 8 }, optRow: { flexDirection: 'row', alignItems: 'center', paddingVertical: 10, borderTopWidth: 1, borderTopColor: colors.borderLight, }, optTitle: { flex: 1, fontSize: 14, color: colors.textPrimary, marginLeft: 10 }, optTitleDone: { textDecorationLine: 'line-through', color: colors.textHint }, optScore: { fontSize: 13, color: colors.successText, fontWeight: '600' }, optScoreDone: { color: colors.textHint }, optAllBtn: { marginTop: 12, backgroundColor: colors.primaryLight, borderRadius: 12, paddingVertical: 12, alignItems: 'center', }, optAllText: { fontSize: 14, fontWeight: '700', color: colors.primaryText }, sectionTitleRow: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginBottom: 14, }, sectionTitle: { fontSize: 16, fontWeight: '700', color: colors.textPrimary }, editLink: { fontSize: 13, color: colors.primary }, basicRow: { flexDirection: 'row', alignItems: 'center' }, avatar: { width: 60, height: 60, borderRadius: 30, backgroundColor: colors.primary, alignItems: 'center', justifyContent: 'center', }, avatarText: { fontSize: 24, fontWeight: '700', color: colors.white }, basicBody: { marginLeft: 14, flex: 1 }, nameRow: { flexDirection: 'row', alignItems: 'center' }, name: { fontSize: 18, fontWeight: '700', color: colors.textPrimary }, contactRow: { flexDirection: 'row', alignItems: 'center', marginTop: 6 }, contactText: { fontSize: 13, color: colors.textSecondary, marginLeft: 6 }, intentionGrid: { flexDirection: 'row', flexWrap: 'wrap' }, intentionItem: { width: '50%', marginBottom: 14 }, intentionLabel: { fontSize: 12, color: colors.textHint, marginBottom: 4 }, intentionValue: { fontSize: 14, fontWeight: '700', color: colors.textPrimary }, intentionTag: { alignSelf: 'flex-start', backgroundColor: colors.successLight, borderRadius: 10, paddingHorizontal: 8, paddingVertical: 2, }, intentionTagText: { fontSize: 12, color: colors.successText, fontWeight: '600' }, addBtn: { flexDirection: 'row', alignItems: 'center' }, addText: { fontSize: 13, color: colors.primary, marginLeft: 2 }, eduRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: 10, borderTopWidth: 1, borderTopColor: colors.borderLight, }, eduSchool: { fontSize: 15, fontWeight: '700', color: colors.textPrimary }, eduMajor: { fontSize: 13, color: colors.textSecondary, marginTop: 2 }, eduYear: { fontSize: 13, color: colors.textHint }, expRow: { paddingVertical: 12, borderTopWidth: 1, borderTopColor: colors.borderLight }, expHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }, expCompany: { fontSize: 15, fontWeight: '700', color: colors.textPrimary }, expDate: { fontSize: 12, color: colors.textHint }, expPosition: { fontSize: 13, color: colors.textSecondary, marginTop: 2 }, expDesc: { fontSize: 13, color: colors.textSecondary, marginTop: 8, lineHeight: 20 }, aiTip: { flexDirection: 'row', alignItems: 'center', marginTop: 10, backgroundColor: colors.infoLight, borderRadius: 10, paddingHorizontal: 10, paddingVertical: 6, }, aiTipText: { flex: 1, fontSize: 12, color: colors.infoText, marginLeft: 6 }, aiOptimizeMiniBtn: { backgroundColor: colors.info, borderRadius: 10, paddingHorizontal: 10, paddingVertical: 4, }, aiOptimizeMiniText: { fontSize: 12, fontWeight: '600', color: colors.white }, optimizedTag: { flexDirection: 'row', alignItems: 'center', alignSelf: 'flex-start', marginTop: 8, backgroundColor: colors.successLight, borderRadius: 10, paddingHorizontal: 8, paddingVertical: 2, }, optimizedText: { fontSize: 12, color: colors.successText, marginLeft: 4 }, projRow: { paddingVertical: 12, borderTopWidth: 1, borderTopColor: colors.borderLight }, skillsWrap: { flexDirection: 'row', flexWrap: 'wrap', gap: 10 }, skillTag: { backgroundColor: colors.pageBg, borderRadius: 12, paddingHorizontal: 12, paddingVertical: 6, }, skillText: { fontSize: 13, color: colors.textPrimary }, evalText: { fontSize: 13, color: colors.textSecondary, lineHeight: 22 }, footer: { position: 'absolute', left: 0, right: 0, bottom: 0, flexDirection: 'row', paddingHorizontal: 16, paddingVertical: 12, backgroundColor: colors.white, borderTopWidth: 1, borderTopColor: colors.borderLight, }, previewBtn: { flex: 1, backgroundColor: colors.white, borderRadius: 24, paddingVertical: 14, alignItems: 'center', borderWidth: 1, borderColor: colors.border, marginRight: 12, }, previewBtnText: { fontSize: 15, fontWeight: '700', color: colors.textPrimary }, optimizeFooterBtn: { flex: 2, backgroundColor: colors.primaryBtn, borderRadius: 24, paddingVertical: 14, alignItems: 'center', }, optimizeFooterBtnText: { fontSize: 15, fontWeight: '700', color: colors.white }, });