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