import { Ionicons } from '@expo/vector-icons'; import { useRouter } from 'expo-router'; import { Alert, Pressable, ScrollView, StyleSheet, Text, View } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { colors } from '../src/theme'; import { useAppCache } from '../src/hooks'; import { formatBytes } from '../src/utils'; import { useAuth } from '../src/context/AuthContext'; import { useRole } from '../src/context/RoleContext'; /* ===================== 设置项结构 ===================== */ type SettingsItem = { id: string; title: string; icon: keyof typeof Ionicons.glyphMap; /** 有值则展示在右侧(如缓存大小) */ value?: string; /** 跳转路由;未配置的为待开发项 */ route?: string; }; type SettingsGroup = { title: string; items: SettingsItem[]; }; /** 设置分组(参考 Boss 直聘:账号 / 求职 / 通知 / 通用) */ const SETTINGS_GROUPS: SettingsGroup[] = [ { title: '账号', items: [ { id: 'account', title: '账号与安全', icon: 'shield-checkmark-outline', route: '/settings/account', }, { id: 'privacy', title: '隐私设置', icon: 'lock-closed-outline', route: '/settings/privacy' }, { id: 'identity', title: '身份切换', icon: 'swap-horizontal-outline', }, ], }, { title: '求职', items: [ { id: 'weight-settings', title: '评分权重设置', icon: 'options-outline', route: '/jobs/weight-settings' }, ], }, { title: '通知', items: [ { id: 'notification', title: '消息通知', icon: 'notifications-outline', route: '/settings/notification', }, ], }, { title: '通用', items: [ { id: 'guide', title: '查看引导页', icon: 'compass-outline', route: '/guide' }, // 缓存体积是运行时读出来的,不写死:实际值在渲染时从 useAppCache 取 { id: 'cache', title: '清除缓存', icon: 'trash-outline' }, { id: 'qualifications', title: '资质证照', icon: 'ribbon-outline', route: '/settings/qualifications', }, { id: 'about', title: '关于我们', icon: 'information-circle-outline', route: '/settings/about', }, ], }, ]; /* ===================== 子组件:顶部导航 ===================== */ function Header({ onBack }: { onBack: () => void }) { return ( 设置 ); } /* ===================== 设置页 ===================== */ export default function SettingsScreen() { const router = useRouter(); const { logout } = useAuth(); const { role, setRole } = useRole(); const { label: cacheLabel, loading: cacheLoading, error: cacheError, clear: clearCache, clearing, } = useAppCache(); /** 缓存体积的展示口径:读取中 / 读取失败 / 体积未知 / 实际体积,绝不编造数字 */ const cacheSizeText = cacheLoading ? '读取中' : cacheError || !cacheLabel ? '体积未知' : cacheLabel; /** 清缓存:二次确认后再删,清完按实际清掉的体积给反馈(清不到就说清不到) */ const handleClearCache = () => { if (clearing) return; Alert.alert('清除缓存', `将清空本机缓存文件(当前 ${cacheSizeText})。账号、已填资料与简历不受影响。`, [ { text: '取消', style: 'cancel' }, { text: '清除', style: 'destructive', onPress: () => { void clearCache() .then((freed) => { Alert.alert('已清除缓存', freed > 0 ? `本次清理 ${formatBytes(freed)}` : '没有可清理的缓存文件'); }) .catch((e: unknown) => { Alert.alert('清除失败', e instanceof Error ? e.message : '请稍后重试'); }); }, }, ]); }; const handleLogout = () => { Alert.alert('确认退出登录', '退出后需要重新登录', [ { text: '取消', style: 'cancel' }, { text: '退出', style: 'destructive', onPress: () => { logout(); router.replace('/register'); }, }, ]); }; const handleIdentitySwitch = () => { if (role === 'enterprise') { setRole('candidate'); router.replace('/(tabs)/jobs'); } else { setRole('enterprise'); router.replace('/(enterprise)/enterprise-overview'); } }; const handlePress = (item: SettingsItem) => { if (item.id === 'identity') { handleIdentitySwitch(); return; } if (item.id === 'cache') { handleClearCache(); return; } if (item.route) { router.push(item.route); return; } // 待开发项:仅占位,避免死路由 }; return (
router.back()} /> {SETTINGS_GROUPS.map((group) => ( {group.title} {group.items.map((item, index) => { const isLast = index === group.items.length - 1; return ( handlePress(item)} hitSlop={4} > {item.title} {/* 缓存行显示运行时读到的体积:读取中 / 读取失败 / 实际体积三态, 不显示假数字,也不把读取失败伪装成「--」这种「本来就是空」的样子 */} {item.id === 'cache' ? ( {cacheLoading ? '计算中' : cacheError ? '读取失败' : cacheLabel || '--'} ) : item.value ? ( {item.value} ) : null} ); })} ))} {/* 退出登录 */} 退出登录 ); } const styles = StyleSheet.create({ safeArea: { flex: 1, backgroundColor: colors.pageBg, }, header: { height: 44, flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 8, }, headerBtn: { width: 36, height: 36, alignItems: 'center', justifyContent: 'center', }, headerTitle: { fontSize: 17, fontWeight: '700', color: colors.textPrimary, }, scroll: { flex: 1, }, scrollContent: { paddingBottom: 32, }, group: { marginTop: 16, paddingHorizontal: 8, }, groupTitle: { marginBottom: 8, marginLeft: 8, fontSize: 13, color: colors.textSecondary, }, card: { backgroundColor: colors.surface, borderRadius: 8, paddingHorizontal: 8, }, row: { flexDirection: 'row', alignItems: 'center', paddingVertical: 14, borderBottomWidth: StyleSheet.hairlineWidth, borderBottomColor: colors.borderLight, }, rowLast: { borderBottomWidth: 0, }, iconWrap: { width: 32, height: 32, borderRadius: 8, backgroundColor: colors.primaryLight, alignItems: 'center', justifyContent: 'center', marginRight: 12, }, rowTitle: { flex: 1, fontSize: 14, fontWeight: '500', color: colors.textPrimary, }, rowValue: { marginRight: 4, fontSize: 13, color: colors.textHint, }, logoutBtn: { marginTop: 24, marginHorizontal: 8, backgroundColor: colors.surface, borderRadius: 8, paddingVertical: 14, alignItems: 'center', }, logoutText: { fontSize: 15, fontWeight: '600', color: colors.dangerText, }, });