feat(profile): 修改密码增加确认输入并强制重新登录

- 新增「确认新密码」输入框,实时提示两次输入是否一致
- 校验:新旧密码相同 / 两次输入不一致 均给出明确提示
- 修改成功后强制登出并跳转登录页,避免旧会话继续持有权限

// Logan.Li
This commit is contained in:
Logan.Li committed 2026-09-14 07:59:33 +00:00
1 parent 815c293b28
commit 0996d8a839
1 file changed
+36 -3
+36 -3
View File
@@ -1,5 +1,6 @@
// ProfilePage — 个人中心(基本信息 / 安全 / API 访问密钥)// Logan.Li
import { useState, useEffect, useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
@@ -30,13 +31,16 @@ type Profile = {
export default function ProfilePage() {
const { toast } = useToast();
const navigate = useNavigate();
const storeUser = useUserStore(s => s.user);
const logout = useUserStore(s => s.logout);
const [profile, setProfile] = useState<Profile | null>(null);
const [loading, setLoading] = useState(true);
const [oldPwd, setOldPwd] = useState('');
const [newPwd, setNewPwd] = useState('');
const [confirmPwd, setConfirmPwd] = useState('');
const [pwdSaving, setPwdSaving] = useState(false);
const load = useCallback(async () => {
@@ -64,15 +68,29 @@ export default function ProfilePage() {
toast({ title: '新密码至少 6 位', variant: 'destructive' });
return;
}
// 两次输入必须一致,避免新密码打错后把自己锁在门外 // Logan.Li
if (newPwd !== confirmPwd) {
toast({ title: '两次输入的新密码不一致', description: '请重新确认新密码', variant: 'destructive' });
return;
}
if (newPwd === oldPwd) {
toast({ title: '新密码不能与当前密码相同', variant: 'destructive' });
return;
}
setPwdSaving(true);
try {
await authApi.changePassword({ old_password: oldPwd, new_password: newPwd });
toast({ title: '密码已修改', description: '下次登录请使用新密码' });
setOldPwd('');
setNewPwd('');
setConfirmPwd('');
// 密码变更后强制登出,要求使用新密码重新登录,避免旧会话继续持有权限 // Logan.Li
toast({ title: '密码已修改', description: '即将退出登录,请使用新密码重新登录' });
setTimeout(() => {
logout();
navigate('/login', { replace: true });
}, 1200);
} catch (e: unknown) {
toast({ title: '修改失败', description: getErrorMessage(e), variant: 'destructive' });
} finally {
setPwdSaving(false);
}
};
@@ -127,12 +145,27 @@ export default function ProfilePage() {
</div>
<div>
<Label className="text-xs text-[var(--dg-text-muted)]">新密码</Label>
<Input type="password" value={newPwd} onChange={e => setNewPwd(e.target.value)} className="mt-1 h-9 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)]" />
<Input type="password" value={newPwd} onChange={e => setNewPwd(e.target.value)} placeholder="至少 6 位" className="mt-1 h-9 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)]" />
</div>
<div>
<Label className="text-xs text-[var(--dg-text-muted)]">确认新密码</Label>
<Input
type="password"
value={confirmPwd}
onChange={e => setConfirmPwd(e.target.value)}
placeholder="再次输入新密码"
className="mt-1 h-9 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)]"
/>
{/* 实时提示两次输入是否一致,避免提交后才发现写错 // Logan.Li */}
{confirmPwd.length > 0 && confirmPwd !== newPwd && (
<p className="mt-1 text-xs text-[var(--dg-red)]">两次输入的密码不一致</p>
)}
</div>
<Button size="sm" onClick={handleChangePassword} disabled={pwdSaving}>
{pwdSaving ? <Loader2 className="h-4 w-4 animate-spin mr-1" /> : null}
保存密码
</Button>
<p className="text-xs text-[var(--dg-text-muted)]">修改成功后需使用新密码重新登录</p>
</CardContent>
</Card>
</div>