import { useEffect, useState } from 'react'; import { useNavigate, Link } from 'react-router'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; import { ArrowLeft } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form'; import AuthPasswordInput from '@/components/AuthPasswordInput'; import { notify } from '@/lib/notify'; import { api } from '../api/client'; import { useForumLimits } from '../hooks/useForumLimits'; import { loginPath } from '../utils/authRedirect'; import { useSiteBranding } from '../hooks/useSiteBranding'; import { useNoIndexSEO } from '../hooks/usePageSEO'; import SiteBrandMark from '../components/SiteBrandMark'; const schema = (minLen: number, codeLen: number) => z.object({ email: z.string().min(1, '请输入邮箱').email('请输入有效邮箱'), email_code: z.string().regex(new RegExp(`^\\d{${codeLen}}$`), `请输入 ${codeLen} 位数字验证码`), new_password: z.string().min(minLen, `密码至少 ${minLen} 位`), }); type FormValues = z.infer>; export default function ForgotPasswordPage() { const { limits } = useForumLimits(); const { branding } = useSiteBranding(); useNoIndexSEO('找回密码'); const nav = useNavigate(); const [loading, setLoading] = useState(false); const [sendingCode, setSendingCode] = useState(false); const [countdown, setCountdown] = useState(0); const [mailReady, setMailReady] = useState(null); const codeLen = 6; const form = useForm({ resolver: zodResolver(schema(limits.password_min_len, codeLen)), defaultValues: { email: '', email_code: '', new_password: '' }, }); useEffect(() => { api.registerConfig() .then((c) => setMailReady(!!c.mail_ready)) .catch(() => setMailReady(false)); }, []); useEffect(() => { if (countdown <= 0) return; const t = window.setTimeout(() => setCountdown((c) => c - 1), 1000); return () => window.clearTimeout(t); }, [countdown]); const sendCode = async () => { const email = form.getValues('email'); const parsed = z.string().email().safeParse(email); if (!parsed.success) { form.setError('email', { message: '请先填写有效邮箱' }); return; } setSendingCode(true); try { const r = await api.sendResetEmailCode(email); notify.success(r.message); setCountdown(60); } catch (e: unknown) { notify.error(e instanceof Error ? e.message : '发送失败'); } finally { setSendingCode(false); } }; const onSubmit = async (values: FormValues) => { setLoading(true); try { const r = await api.resetPassword({ email: values.email, emailCode: values.email_code, newPassword: values.new_password, }); notify.success(r.message); nav(loginPath()); } catch (e: unknown) { notify.error(e instanceof Error ? e.message : '重置失败'); } finally { setLoading(false); } }; return (

找回密码

{mailReady === false ? '邮件服务未配置,请联系站长重置密码' : '通过注册邮箱验证码设置新密码'}

( 注册邮箱 )} /> ( 邮箱验证码
{ const digits = e.target.value.replace(/\D/g, '').slice(0, codeLen); field.onChange(digits); }} />
)} /> ( 新密码 )} />

想起密码了?返回登录

返回论坛
); }