import React, { useEffect, useState } from 'react'; import { View, Text, StyleSheet, TextInput, TouchableOpacity, KeyboardAvoidingView, Platform, ScrollView, ActivityIndicator, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useRouter } from 'expo-router'; import { MaterialCommunityIcons } from '@expo/vector-icons'; import { LinearGradient } from 'expo-linear-gradient'; import { colors, spacing, borderRadius, shadows, fontSizes } from '../../theme'; import { authService, resolveAuthApiError } from '../../services/authService'; import { showPrompt } from '../../services/promptService'; export const ForgotPasswordScreen: React.FC = () => { const router = useRouter(); const [email, setEmail] = useState(''); const [verificationCode, setVerificationCode] = useState(''); const [newPassword, setNewPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); const [showPassword, setShowPassword] = useState(false); const [showConfirmPassword, setShowConfirmPassword] = useState(false); const [sendingCode, setSendingCode] = useState(false); const [loading, setLoading] = useState(false); const [countdown, setCountdown] = useState(0); useEffect(() => { if (countdown <= 0) { return; } const timer = setInterval(() => { setCountdown((prev) => (prev <= 1 ? 0 : prev - 1)); }, 1000); return () => clearInterval(timer); }, [countdown]); const validateEmail = (value: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value); const handleSendCode = async () => { if (!validateEmail(email.trim())) { showPrompt({ title: '提示', message: '请输入正确的邮箱地址', type: 'warning' }); return; } if (countdown > 0) { return; } setSendingCode(true); try { const ok = await authService.sendPasswordResetCode(email.trim()); if (ok) { setCountdown(60); showPrompt({ title: '发送成功', message: '验证码已发送,请查收邮箱', type: 'success' }); } } catch (error: any) { showPrompt({ title: '发送失败', message: resolveAuthApiError(error, '验证码发送失败'), type: 'error' }); } finally { setSendingCode(false); } }; const handleResetPassword = async () => { if (!validateEmail(email.trim())) { showPrompt({ title: '提示', message: '请输入正确的邮箱地址', type: 'warning' }); return; } if (!verificationCode.trim()) { showPrompt({ title: '提示', message: '请输入验证码', type: 'warning' }); return; } if (newPassword.length < 6) { showPrompt({ title: '提示', message: '新密码至少 6 位', type: 'warning' }); return; } if (newPassword !== confirmPassword) { showPrompt({ title: '提示', message: '两次输入的密码不一致', type: 'warning' }); return; } setLoading(true); try { const ok = await authService.resetPassword({ email: email.trim(), verification_code: verificationCode.trim(), new_password: newPassword, }); if (ok) { showPrompt({ title: '重置成功', message: '密码已重置,请重新登录', type: 'success' }); router.back(); } } catch (error: any) { showPrompt({ title: '重置失败', message: resolveAuthApiError(error, '请稍后重试'), type: 'error' }); } finally { setLoading(false); } }; return ( 找回密码 请输入邮箱并完成验证码验证 0) && styles.sendCodeButtonDisabled]} onPress={handleSendCode} disabled={sendingCode || countdown > 0} > {sendingCode ? ( ) : ( {countdown > 0 ? `${countdown}s` : '发送验证码'} )} setShowPassword((v) => !v)}> setShowConfirmPassword((v) => !v)}> {loading ? : 重置密码} router.back()}> 返回登录 ); }; const styles = StyleSheet.create({ container: { flex: 1 }, gradient: { flex: 1 }, keyboardView: { flex: 1 }, scrollContent: { flexGrow: 1, justifyContent: 'center', paddingHorizontal: spacing.xl, paddingVertical: spacing['2xl'], }, formCard: { backgroundColor: colors.background.paper, borderRadius: borderRadius['2xl'], padding: spacing.xl, ...shadows.md, }, title: { fontSize: fontSizes['2xl'], fontWeight: '700', color: colors.text.primary, textAlign: 'center', marginBottom: spacing.xs, }, subtitle: { fontSize: fontSizes.sm, color: colors.text.secondary, textAlign: 'center', marginBottom: spacing.lg, }, inputWrapper: { flexDirection: 'row', alignItems: 'center', backgroundColor: colors.background.default, borderRadius: borderRadius.lg, borderWidth: 1.5, borderColor: colors.divider, paddingHorizontal: spacing.md, height: 52, marginBottom: spacing.md, }, inputIcon: { marginRight: spacing.sm, }, input: { flex: 1, fontSize: fontSizes.md, color: colors.text.primary, }, codeRow: { flexDirection: 'row', alignItems: 'center', gap: spacing.sm, marginBottom: spacing.md, }, codeInput: { flex: 1, marginBottom: 0, }, sendCodeButton: { height: 52, minWidth: 110, borderRadius: borderRadius.lg, backgroundColor: colors.primary.main, alignItems: 'center', justifyContent: 'center', paddingHorizontal: spacing.md, }, sendCodeButtonDisabled: { opacity: 0.6, }, sendCodeButtonText: { color: '#fff', fontSize: fontSizes.sm, fontWeight: '600', }, submitButton: { height: 50, borderRadius: borderRadius.lg, backgroundColor: colors.primary.main, alignItems: 'center', justifyContent: 'center', marginTop: spacing.sm, }, submitButtonDisabled: { opacity: 0.6, }, submitButtonText: { color: '#fff', fontSize: fontSizes.lg, fontWeight: '700', }, backButton: { alignItems: 'center', marginTop: spacing.md, }, backButtonText: { color: colors.primary.main, fontSize: fontSizes.md, fontWeight: '500', }, }); export default ForgotPasswordScreen;