import { Ionicons } from '@expo/vector-icons'; import { useRouter } from 'expo-router'; import { useEffect, useState } from 'react'; import { ActivityIndicator, Pressable, ScrollView, StyleSheet, Switch, Text, TextInput, View, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useResumeExport } from '../../src/hooks'; import { colors } from '../../src/theme'; import type { ResumeExportFormat, ResumeExportSettings, ResumeTemplate } from '../../src/types'; const FORMATS: { key: ResumeExportFormat; label: string; icon: keyof typeof Ionicons.glyphMap }[] = [ { key: 'pdf', label: 'PDF', icon: 'document-outline' }, { key: 'word', label: 'Word', icon: 'document-text-outline' }, { key: 'image', label: '图片', icon: 'image-outline' }, ]; const TEMPLATES: ResumeTemplate[] = [ { id: 'classic', name: '简约经典', color: 'primary' }, { id: 'creative', name: '创意设计', color: 'info' }, { id: 'tech', name: '技术专业', color: 'success' }, ]; const TEMPLATE_COLORS: Record = { primary: colors.primary, info: colors.info, success: colors.success, }; /* ===================== 子组件:顶部导航 ===================== */ function Header({ onBack, onSave }: { onBack: () => void; onSave: () => void }) { return ( 导出简历 保存 ); } /* ===================== 子组件:预览区 ===================== */ function PreviewCard() { return ( 实时预览你的简历效果 打开预览 ); } /* ===================== 子组件:格式选择 ===================== */ function FormatSelect({ value, onChange, }: { value: ResumeExportFormat; onChange: (v: ResumeExportFormat) => void; }) { return ( 选择导出格式 {FORMATS.map((f) => { const selected = value === f.key; return ( onChange(f.key)} > {f.label} 高清 · 格式友好 {selected && ( )} ); })} ); } /* ===================== 子组件:模板选择 ===================== */ function TemplateSelect({ value, onChange, }: { value: string; onChange: (v: string) => void; }) { return ( 选择简历模板 全部模板 > {TEMPLATES.map((t) => { const selected = value === t.id; return ( onChange(t.id)} > {t.name} {selected && } ); })} ); } /* ===================== 子组件:开关行 ===================== */ function ToggleRow({ label, desc, value, onChange, }: { label: string; desc?: string; value: boolean; onChange: (v: boolean) => void; }) { return ( {label} {desc && {desc}} ); } /* ===================== 子组件:输入行 ===================== */ function InputRow({ label, value, onChange, placeholder, }: { label: string; value: string; onChange: (v: string) => void; placeholder?: string; }) { return ( {label} ); } /* ===================== 子组件:四态 ===================== */ function Loading() { return ( 正在加载设置… ); } function ErrorState({ onRetry }: { onRetry: () => void }) { return ( 设置加载失败 点击重试 ); } /* ===================== 页面 ===================== */ export default function ResumeExportScreen() { const router = useRouter(); const { settings, loading, error, refresh, save, exportFile, exporting } = useResumeExport(); const [localSettings, setLocalSettings] = useState(null); useEffect(() => { if (settings && !localSettings) { setLocalSettings(settings); } }, [settings, localSettings]); const update = (key: K, value: ResumeExportSettings[K]) => { setLocalSettings((prev) => (prev ? { ...prev, [key]: value } : prev)); }; const handleSave = async () => { if (localSettings) { await save(localSettings); } }; const handleExport = async () => { if (!localSettings) return; await handleSave(); const url = await exportFile(localSettings); console.log('[Resume Export] url:', url); }; if (loading) { return (
router.back()} onSave={() => {}} /> ); } if (error || !localSettings) { return (
router.back()} onSave={() => {}} /> ); } return (
router.back()} onSave={handleSave} /> update('format', v)} /> update('templateId', v)} /> 导出设置 update('hideContact', v)} /> update('exportPassword', v)} /> {}} /> {}} /> update('fileName', v)} placeholder="请输入文件名" /> update('includeCover', v)} /> update('includeRecommendation', v)} /> 定向优化(可选) 粘贴岗位描述,AI 自动优化简历匹配度 update('targetJobDescription', v)} placeholder="输入岗位 JD…" placeholderTextColor={colors.textHint} multiline numberOfLines={4} textAlignVertical="top" /> AI 智能优化 分享简历 {exporting ? ( ) : ( 立即导出 )} ); } 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: 60, justifyContent: 'center' }, headerTitle: { fontSize: 17, fontWeight: '700', color: colors.textPrimary }, headerSave: { fontSize: 14, color: colors.primary, textAlign: 'right' }, 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 }, previewCard: { backgroundColor: colors.white, borderRadius: 14, padding: 20, alignItems: 'center', marginBottom: 16, borderWidth: 1, borderColor: colors.borderLight, }, previewThumb: { width: 80, height: 80, borderRadius: 40, backgroundColor: colors.primaryLight, alignItems: 'center', justifyContent: 'center', marginBottom: 10, }, previewText: { fontSize: 14, color: colors.textSecondary }, previewLink: { flexDirection: 'row', alignItems: 'center', marginTop: 8 }, previewLinkText: { fontSize: 13, color: colors.primary, marginRight: 2 }, section: { marginBottom: 16 }, sectionHeader: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginBottom: 12, }, sectionTitle: { fontSize: 16, fontWeight: '700', color: colors.textPrimary, marginBottom: 12 }, moreLink: { fontSize: 13, color: colors.primary }, formatRow: { flexDirection: 'row', gap: 10 }, formatCard: { flex: 1, backgroundColor: colors.white, borderRadius: 12, padding: 14, alignItems: 'center', borderWidth: 1, borderColor: colors.borderLight, }, formatCardActive: { borderColor: colors.primary, backgroundColor: colors.primaryLight }, formatLabel: { fontSize: 13, fontWeight: '700', color: colors.textPrimary, marginTop: 8 }, formatLabelActive: { color: colors.primaryText }, formatHint: { fontSize: 10, color: colors.textHint, marginTop: 2 }, formatCheck: { position: 'absolute', top: 8, right: 8 }, templateRow: { flexDirection: 'row', gap: 10 }, templateCard: { flex: 1, backgroundColor: colors.white, borderRadius: 12, padding: 10, alignItems: 'center', borderWidth: 1, borderColor: colors.borderLight, }, templateCardActive: { borderColor: colors.primary }, templateThumb: { width: '100%', height: 80, borderRadius: 8, backgroundColor: colors.primary, padding: 12, marginBottom: 8, }, templateLines: { gap: 8, marginTop: 8 }, templateLine: { height: 8, borderRadius: 4, backgroundColor: colors.white, opacity: 0.9 }, templateLineShort: { width: '60%', height: 8, borderRadius: 4, backgroundColor: colors.white, opacity: 0.7 }, templateName: { fontSize: 12, color: colors.textPrimary }, templateNameActive: { color: colors.primaryText, fontWeight: '700' }, card: { backgroundColor: colors.white, borderRadius: 14, padding: 16, borderWidth: 1, borderColor: colors.borderLight, }, toggleRow: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingVertical: 12, borderTopWidth: 1, borderTopColor: colors.borderLight, }, toggleText: { flex: 1, marginRight: 12 }, toggleLabel: { fontSize: 14, color: colors.textPrimary }, toggleDesc: { fontSize: 12, color: colors.textHint, marginTop: 2 }, inputRow: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingVertical: 12, borderTopWidth: 1, borderTopColor: colors.borderLight, }, inputLabel: { fontSize: 14, color: colors.textPrimary, width: 80 }, input: { flex: 1, fontSize: 14, color: colors.textPrimary, textAlign: 'right', }, targetLabel: { fontSize: 13, color: colors.textSecondary, marginBottom: 10 }, targetInput: { height: 100, borderRadius: 10, backgroundColor: colors.pageBg, padding: 12, fontSize: 14, color: colors.textPrimary, }, targetBtn: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', backgroundColor: colors.primaryBtn, borderRadius: 12, paddingVertical: 12, marginTop: 12, }, targetBtnText: { fontSize: 14, fontWeight: '700', color: colors.white, marginLeft: 6 }, footer: { position: 'absolute', left: 0, right: 0, bottom: 0, flexDirection: 'row', paddingHorizontal: 16, paddingVertical: 12, backgroundColor: colors.white, borderTopWidth: 1, borderTopColor: colors.borderLight, }, shareBtn: { flex: 1, backgroundColor: colors.white, borderRadius: 24, paddingVertical: 14, alignItems: 'center', borderWidth: 1, borderColor: colors.border, marginRight: 12, }, shareBtnText: { fontSize: 15, fontWeight: '700', color: colors.textPrimary }, exportBtn: { flex: 2, backgroundColor: colors.primaryBtn, borderRadius: 24, paddingVertical: 14, alignItems: 'center', }, exportBtnDisabled: { backgroundColor: colors.textDisabled }, exportBtnText: { fontSize: 15, fontWeight: '700', color: colors.white }, });