import { useState } from 'react'; 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 { useChangePassword } from '../../src/hooks'; import { useAuth } from '../../src/context/AuthContext'; import { PasswordField } from '../../src/components/PasswordField'; import { colors } from '../../src/theme'; /* ===================== 子组件:顶部导航 ===================== */ function Header({ onBack }: { onBack: () => void }) { return ( 修改登录密码 ); } /* ===================== 页面 ===================== */ /** * 账号与安全 → 修改登录密码。 * * 入口由 MOCK_ACCOUNT_SECURITY 的 route 字段驱动(见 src/api/api.ts), * 此前该菜单项没有 route,点击无任何反应。 * 改密成功后强制重新登录,避免旧会话继续持有已失效的密码凭据。 */ export default function ChangePasswordScreen() { const router = useRouter(); const { logout } = useAuth(); const { change, loading, error, checkNewPassword } = useChangePassword(); const [oldPassword, setOldPassword] = useState(''); const [newPassword, setNewPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); /** 规则与一致性校验在 API 层计算,这里只渲染文案 */ const hint = newPassword.length > 0 ? checkNewPassword(oldPassword, newPassword, confirmPassword) : null; const canSubmit = oldPassword.length > 0 && newPassword.length > 0 && confirmPassword.length > 0 && hint === null && !loading; const handleSubmit = async () => { if (!canSubmit) return; try { await change({ oldPassword, newPassword, confirmPassword }); } catch { // 失败原因由 useChangePassword 记录,页面下方展示 return; } Alert.alert('密码已修改', '请使用新密码重新登录', [ { text: '重新登录', onPress: () => { logout(); router.replace('/register'); }, }, ]); }; return (
router.back()} /> 为保障账号安全,修改密码后需要使用新密码重新登录。 {hint ? ( {hint} ) : ( 8–20 位,需同时包含字母和数字 )} {error ? {error.message} : null} {loading ? '提交中…' : '确认修改'} ); } const styles = StyleSheet.create({ safeArea: { flex: 1, backgroundColor: colors.pageBg, }, header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 8, height: 48, backgroundColor: colors.white, borderBottomWidth: 1, borderBottomColor: colors.borderLight, }, headerBtn: { width: 40, height: 40, alignItems: 'center', justifyContent: 'center', }, headerTitle: { flex: 1, textAlign: 'center', fontSize: 17, fontWeight: '700', color: colors.textPrimary, }, scroll: { flex: 1, }, content: { padding: 8, paddingBottom: 24, }, tip: { fontSize: 12, lineHeight: 17, color: colors.textSecondary, marginBottom: 10, paddingHorizontal: 4, }, card: { backgroundColor: colors.white, borderRadius: 8, paddingHorizontal: 8, paddingVertical: 8, }, gap: { height: 6, }, hint: { marginTop: 12, paddingHorizontal: 4, fontSize: 12, color: colors.textHint, }, hintDanger: { marginTop: 12, paddingHorizontal: 4, fontSize: 12, color: colors.danger, }, formError: { marginTop: 10, paddingHorizontal: 4, fontSize: 12, color: colors.danger, }, submitBtn: { marginTop: 24, height: 50, borderRadius: 12, alignItems: 'center', justifyContent: 'center', backgroundColor: colors.primaryBtn, }, submitDisabled: { backgroundColor: colors.textDisabled, }, submitText: { fontSize: 16, fontWeight: '700', color: colors.white, }, });