'use client' import { Suspense, useState, useMemo } from 'react' import { useSearchParams } from 'next/navigation' interface PasswordStrength { minLength: boolean hasUpper: boolean hasLower: boolean hasDigit: boolean hasSpecial: boolean score: number } function checkPassword(pw: string): PasswordStrength { const minLength = pw.length >= 8 const hasUpper = /[A-Z]/.test(pw) const hasLower = /[a-z]/.test(pw) const hasDigit = /[0-9]/.test(pw) const hasSpecial = /[^A-Za-z0-9]/.test(pw) const score = [hasUpper, hasLower, hasDigit, hasSpecial].filter(Boolean).length return { minLength, hasUpper, hasLower, hasDigit, hasSpecial, score } } const checks: { key: keyof PasswordStrength; label: string }[] = [ { key: 'minLength', label: '至少 8 位字符' }, { key: 'hasUpper', label: '包含大写字母' }, { key: 'hasLower', label: '包含小写字母' }, { key: 'hasDigit', label: '包含数字' }, { key: 'hasSpecial', label: '包含特殊字符' }, ] function SetupPasswordForm() { const searchParams = useSearchParams() const token = searchParams.get('token') || '' const [password, setPassword] = useState('') const [confirm, setConfirm] = useState('') const [error, setError] = useState('') const [success, setSuccess] = useState(false) const [loading, setLoading] = useState(false) const strength = useMemo(() => checkPassword(password), [password]) const isStrong = strength.minLength && strength.score >= 3 const passwordMismatch = confirm.length > 0 && password !== confirm async function handleSubmit(e: React.FormEvent) { e.preventDefault() setError('') if (!isStrong) { setError('请先满足密码复杂度要求') return } if (password !== confirm) { setError('两次输入的密码不一致') return } setLoading(true) try { const res = await fetch('/api/auth/setup-password', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token, password }), }) const data = await res.json() if (!res.ok) { setError(data.error || '设置失败') } else { setSuccess(true) } } catch { setError('网络错误,请重试') } finally { setLoading(false) } } if (!token) { return (

链接无效

缺少设置密码所需的 token,请检查链接是否完整。

返回登录
) } if (success) { return (

密码已设置

您的密码已成功设置,现在可以使用新密码登录。

前往登录
) } return (

设置登录密码

请为您的 OA 账号设置登录密码。

{error &&
{error}
}
) } export default function SetupPasswordPage() { return ( 加载中...}> ) } const styles: Record = { container: { display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '100vh', padding: '20px', background: '#f1f5f9', }, loadingFallback: { display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '100vh', color: '#64748b', fontSize: '14px', }, card: { background: '#fff', borderRadius: '12px', padding: '40px', maxWidth: '420px', width: '100%', boxShadow: '0 2px 12px rgba(0,0,0,0.08)', }, title: { fontSize: '22px', fontWeight: 700, margin: '0 0 12px', color: '#0f172a', }, text: { fontSize: '14px', color: '#475569', margin: '0 0 24px', lineHeight: 1.6, }, label: { display: 'block', marginBottom: '16px', fontSize: '13px', fontWeight: 500, color: '#334155', }, input: { display: 'block', width: '100%', marginTop: '6px', padding: '10px 14px', borderRadius: '8px', border: '1px solid #cbd5e1', background: '#fff', color: '#0f172a', fontSize: '14px', boxSizing: 'border-box' as any, outline: 'none', }, complexityPanel: { marginTop: 10, display: 'flex', flexDirection: 'column', gap: 4, }, complexityHint: { fontSize: 12, color: '#64748b', margin: '0 0 2px', }, checkItem: { display: 'flex', alignItems: 'center', gap: 6, fontSize: 12, }, btn: { width: '100%', padding: '12px', background: '#2563eb', color: '#fff', border: 'none', borderRadius: '8px', fontSize: '15px', fontWeight: 600, marginTop: '8px', }, error: { background: '#fef2f2', color: '#dc2626', fontSize: '13px', padding: '10px 14px', borderRadius: '8px', marginBottom: '16px', }, link: { display: 'inline-block', marginTop: '16px', color: '#2563eb', textDecoration: 'none', fontSize: '14px', fontWeight: 500, }, }