// 管理员用户管理已迁「系统管理」分区(components/adminSections.tsx UsersPanel, // 重设计方案 阶段②)。本文件只保留登录者主动改密弹窗(与强制改密共用 API)。 import { useState } from 'react' import { api } from '../api' import { store, useStore } from '../store' import { Icon } from '@ui' import { humanChangePasswordError } from './LoginPage' function Shell({ title, onClose, children }: { title: string; onClose: () => void; children: React.ReactNode }) { return (
{ if (e.target === e.currentTarget) onClose() }}>
{title}
{children}
) } /** 主动改密(账户菜单入口):用户名(只读关联)+ 当前密码 + 新密码 + 确认。 * 表单语义(2026-10-12 增量 3):
承载提交(Enter 可用)、autocomplete * 三段标注(username/current-password/new-password)、label 与 input 关联; * 错误文案复用 humanChangePasswordError——401 不再一刀切成「当前密码不正确」。 */ export function ChangePasswordModal({ onClose }: { onClose: () => void }) { const me = useStore(s => s.me) const [current, setCurrent] = useState('') const [next, setNext] = useState('') const [confirm, setConfirm] = useState('') const [err, setErr] = useState('') const [ok, setOk] = useState(false) const [busy, setBusy] = useState(false) const submit = async () => { setErr('') if (next !== confirm) { setErr('两次输入的新密码不一致'); return } if (next.length < 8) { setErr('新密码至少 8 位'); return } setBusy(true) try { await api.changePassword(current, next) setOk(true) store.toast('密码已更新') setTimeout(onClose, 900) } catch (e) { setErr(humanChangePasswordError(String(e))) } finally { setBusy(false) } } return ( {ok ?
密码已更新,其他设备的会话已注销。
: ( { e.preventDefault(); submit() }}> setCurrent(e.target.value)} autoFocus /> setNext(e.target.value)} /> setConfirm(e.target.value)} /> {err &&
{err}
} )}
) }