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,
},
});