日期: 2026-07-12
状态: 待实施
范围:web-admin顶栏头像下拉 +backend当前用户改密接口
UI 规范:docs/web-admin-ui-spec-fresh-mint.md
参考实现: 并列仓库zkxw(ArtUserMenu.vue+UserPasswordDialog.vue+/web/auth/change-password)
运营人员在 web-admin 顶栏点击头像后,下拉菜单目前仅有电话/区域展示与「退出登录」,无法自助修改密码,只能联系管理员在用户管理页重置为默认密码 111111。
目标:在头像下拉菜单中,于「退出登录」按钮上方增加「修改密码」入口,用户填写原密码与新密码后完成自助改密。
| # | 决策 | 说明 |
|---|---|---|
| D1 | 交互形态 | 方案 A:菜单项 + 独立弹窗 UserPasswordDialog(对齐 zkxw) |
| D2 | 改密后会话 | 不强制重新登录;改密成功后提示并关闭弹窗,当前 Token 会话继续有效 |
| D3 | 权限 | 仅需登录态(Bearer Token),不新增菜单权限码 |
| D4 | 密码规则 | 新密码最少 6 位;校验原密码 bcrypt;确认密码前端一致性校验 |
| D5 | 传输安全 | 走 HTTPS + 现有 API 加密中间件(生产环境);不单独对改密请求做 RSA(与登录区分,同 zkxw) |
| D6 | 范围 | 仅「修改密码」;本期不做「个人信息」编辑 |
| 项 | 状态 |
|---|---|
ArtUserMenu.vue 菜单入口 |
❌ 无「修改密码」 |
UserPasswordDialog.vue |
❌ 不存在 |
fetchChangePassword() |
❌ api/auth.ts 未定义 |
入口文件:web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue
由 art-header-bar/index.vue 引用。
| 项 | 状态 |
|---|---|
ChangePasswordRequest Schema |
✅ app/schemas/auth.py(已有,未接线) |
change_password() CRUD |
❌ 仅有 reset_password()(管理员重置) |
POST /web/auth/change-password |
❌ 未实现 |
已有管理员重置:POST /web/admin/users/{id}/reset-password → 固定重置为 111111。
┌─────────────────────┐
│ [头像] 张三 │
│ 运营管理员 │
├─────────────────────┤
│ 👤 138xxxx │
│ 📍 华东区(若有) │
├─────────────────────┤
│ 🔒 修改密码 ← 新增 │
├─────────────────────┤
│ [ 退出登录 ] │
└─────────────────────┘
ArtUserMenu 关闭 Popover(沿用 closeUserMenu() + setTimeout 模式)UserPasswordDialog(v-model:visible)POST /web/auth/change-passwordlogOut()| 文件 | 操作 |
|---|---|
web-admin/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue |
修改:增加菜单项 + 引入弹窗 |
web-admin/src/components/core/layouts/art-header-bar/widget/UserPasswordDialog.vue |
新建:改密弹窗 |
web-admin/src/api/auth.ts |
修改:新增 fetchChangePassword() |
web-admin/src/locales/langs/zh.json |
可选:topBar.user.changePassword |
web-admin/src/locales/langs/en.json |
可选:同上英文 |
ArtUserMenu.vue 变更要点<li class="btn-item" @click="openPasswordDialog">ArtSvgIcon icon="ri:lock-password-line"修改密码(或 $t('topBar.user.changePassword'))<UserPasswordDialog v-model:visible="passwordDialogVisible" />openPasswordDialog() 模式对齐 zkxw:closeUserMenu() → setTimeout 150ms → passwordDialogVisible = trueUserPasswordDialog.vue 规范对齐 Fresh Mint 弹窗约定(web-admin-crud-dialog.mdc):
el-dialog:class="dialog-form-layout"、align-center、append-to-body、modal-class="protocol-dialog-mask"、destroy-on-close、:close-on-click-modal="false"el-form:label-position="top"、scroll-to-errorform-col-span-2):
type="password"、show-password、autocomplete="current-password"min 6 校验、autocomplete="new-password"newPassword 一致Close 图标)、确定(Check 图标 + :loading="submitting")resetForm() 清空字段与校验态/** 当前用户修改密码 */
export function fetchChangePassword(data: { oldPassword: string; newPassword: string }) {
return request.post({
url: '/web/auth/change-password',
data,
showSuccessMessage: true
})
}
| 文件 | 操作 |
|---|---|
backend/app/crud/user.py |
新增 change_password() |
backend/app/api/public.py |
新增 POST /web/auth/change-password |
change_password() 逻辑def change_password(db: Session, user_id: str, old_password: str, new_password: str) -> None:
obj = get_user(db, user_id)
if not obj:
raise ValueError("用户不存在")
if not verify_password(old_password, obj.password or ""):
raise ValueError("原密码不正确")
new_pw = (new_password or "").strip()
if len(new_pw) < 6:
raise ValueError("新密码长度不能少于6位")
if old_password == new_pw:
raise ValueError("新密码不能与原密码相同")
obj.password = _hash_password(new_pw)
obj.update_time = datetime.now()
db.add(obj)
db.commit()
POST /web/auth/change-password
Authorization: Bearer <access_token>
Content-Type: application/json
Request body(驼峰,与前端一致):
{
"oldPassword": "string",
"newPassword": "string"
}
Response 200:
{ "code": 200, "data": { "message": "密码修改成功" } }
Response 400:
- "请输入原密码"
- "新密码长度不能少于6位"
- "原密码不正确"
- "新密码不能与原密码相同"
Response 401:
- "Token无效或已过期" / "未授权"
鉴权:从 Authorization Bearer Token 解析当前用户 user_id(复用 get_current_user_by_token 或 public.py 现有模式)。
| 规则 | 前端 | 后端 |
|---|---|---|
| 原密码必填 | ✅ | ✅ |
| 新密码 ≥ 6 位 | ✅ | ✅ |
| 确认密码一致 | ✅ | — |
| 原密码 bcrypt 校验 | — | ✅ |
| 新密码 ≠ 原密码 | 建议 | ✅ |
dialog-form-layout、无 backdrop blur、按钮带图标)| 模块 | 预估 |
|---|---|
| 前端 | ~1h |
| 后端 | ~0.5h |
| 联调 | ~0.5h |
| 合计 | ~2h |
无数据库迁移;约 6 个文件改动。