- 新增 /profile 路由:两个 Tab(个人信息 / 修改密码)+ 退出按钮 - Sidebar 底部用户区改造:绑定真实登录用户(头像/姓名/角色)+ 加 Settings icon 跳 /profile - useAuthStore 加 refreshUser:保存信息后立即同步 Sidebar 显示 - 个人信息表单:姓名/手机/邮箱可改,部门/角色只读,复用 updateMember - 密码修改:原密 + 新密(≥8位且≠原密) + 确认 三档校验,眼睛 icon 切换可见,下次登录生效 - 退出登录:confirm → useAuthStore.logout → /login Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7.4 KiB
7.4 KiB
个人中心管理设计
Context
当前用户登录后无法编辑自己的个人信息和密码——这两件事现在只能由超管在"成员管理"里改。Sidebar 底部"Admin / 管理员"硬编码(与真实登录用户脱节)也是历史遗留 bug。
本期目标:新增 /profile 路由,让用户能改自己的姓名 / 手机 / 邮箱、修改密码、退出当前账号。顺手把 Sidebar 用户区改成绑定真实用户。
决策摘要
| 项 | 决策 |
|---|---|
| 入口 | Sidebar 底部用户区右侧加 <Settings /> icon,仅 icon 跳转(整块不响应) |
| 可编辑字段 | 姓名 / 手机 / 邮箱(部门/角色只读展示) |
| 保存策略 | 复用 useMemberStore.updateMember;姓名/手机/邮箱保存后立刻 useAuthStore.refreshUser 同步 |
| 密码修改 | 原密 + 新密 + 确认 三档;新密 ≥ 8 字符且 ≠ 原密 |
| 密码生效时机 | 下次登录生效(不强制退出) |
| 退出登录 | 复用 useAuthStore.logout → 跳 /login |
| 路由权限 | 不加 RouteGuard(个人中心是当前用户自己的,无需权限点) |
| 默认 Tab | 「个人信息」 |
| 退出按钮位置 | Header 右上角,红色边框/红字 |
| 密码可见切换 | 末尾眼睛 icon(与 login 页风格一致) |
| Sidebar 副名 | 通过 roleId 反查 roles 拿角色名 |
| 部门展示 | 通过 departmentId 反查 departments 拿层级名(如"产品部 / 前端组") |
1. 入口(Sidebar 改造)
apps/web/components/layout/Sidebar.tsx
底部用户区当前硬编码"A / Admin / 管理员"。改造:
┌──────────────────────────────────────┐
│ [头像首字母] 张三 [⚙] │
│ 产品经理 │
└──────────────────────────────────────┘
- 头像首字母 =
user.name?.[0](fallback'?') - 主名 =
user.name - 副名 =
roles.find(r => r.id === user.roleId)?.name ?? '-' - 整块按钮不响应点击(移除 onClick)
- 右侧加
<Settings className="h-4 w-4" />独立按钮 →router.push('/profile') - 未登录:保留显示"未登录"占位,icon 不显示
2. 个人中心页
新增 apps/web/app/profile/page.tsx
┌─ Header (h-14) ──────────────────────────┐
│ 个人中心 [退出登录] │
├──────────────────────────────────────────┤
│ ┌─ 个人信息 ─┐ ┌─ 修改密码 ─┐ │ ← Tab 切换
├──────────────────────────────────────────┤
│ [Tab 内容区] │
└──────────────────────────────────────────┘
入口校验:useAuthStore.user 不存在则 router.push('/login'),避免直访问空 store。
Tab 样式:沿用版本详情页 border-b-2 + accent 色风格。
退出登录按钮:
- Header 右上角,红色边框红字白底
onClick:confirm('确认退出当前账号?')→useAuthStore.logout()→router.push('/login')
3. 个人信息表单
姓名 * [_____________]
手机号 * [_____________] FieldError
邮箱 * [_____________] FieldError
部门 产品部 / 前端组 (只读,灰底)
角色 产品经理 (只读,灰底)
[取消] [保存]
初始值:从 useMemberStore.members.find(m => m.id === user.id) 取最新值(不直接信 useAuthStore.user)。
校验:
- 姓名:必填,trim 后非空,最长 20
- 手机号:必填,正则
/^1[3-9]\d{9}$/ - 邮箱:必填,正则
/^[^\s@]+@[^\s@]+\.[^\s@]+$/ - 失败:用
<FieldError>在对应 input 下方显示文案
部门展示:递归查 departmentId 父子链,拼为"产品部 / 前端组"格式。
角色展示:roles.find(r => r.id === user.roleId)?.name。
提交:
- 校验通过 →
useMemberStore.updateMember(user.id, { name, phone, email }) - 调
useAuthStore.refreshUser()同步 user state(让 Sidebar 立即显示新名字) - 内联绿色提示"已保存"(2 秒自动消失)
取消:恢复初始值。
4. 密码修改表单 + 退出
当前密码 * [_____________ 👁] FieldError
新密码 * [_____________ 👁] FieldError
确认密码 * [_____________ 👁] FieldError
[取消] [保存]
校验:
- 当前密码:必填,等于
member.password,否则FieldError: '当前密码错误' - 新密码:必填,长度 ≥ 8(与
passwordRule.length默认值一致),≠ 当前密码 - 确认密码:必填,必须等于新密码
提交:
- 三项校验通过 →
useMemberStore.updateMember(user.id, { password: newPassword }) - 清空三个输入框
- 内联绿色提示"密码已修改,下次登录生效"
- 不强制退出
眼睛 icon:每个 input 末尾切换 type="password" / "text",复用 login 页 Eye/EyeOff 模式。
取消:清空三个输入框。
数据流
useAuthStore.user (id, name, roleId, departmentId, phone, email)
│
├─ Sidebar 用户区 ─→ 头像/姓名 + 角色名 + 设置 icon
│
└─ /profile 页面入口校验
│
├─ 个人信息表单 ─→ updateMember() ─→ refreshUser() ─→ Sidebar 立即更新
├─ 密码表单 ─→ updateMember() ─→ 提示下次登录生效
└─ 退出按钮 ─→ logout() ─→ router.push('/login')
useAuthStore 改造
新增 refreshUser() 方法:
refreshUser: () => {
const user = get().user;
if (!user) return;
// 从 localStorage 重新读 member(与 login 同源)
const membersRaw = localStorage.getItem('ftb_members_v1');
if (!membersRaw) return;
const parsed = JSON.parse(membersRaw);
const m = parsed.members?.find((x: any) => x.id === user.id);
if (!m) return;
const next = { id: m.id, name: m.name, roleId: m.roleId, departmentId: m.departmentId, phone: m.phone, email: m.email };
set({ user: next });
sessionStorage.setItem('ftb_auth_session', JSON.stringify(next));
if (localStorage.getItem('ftb_auth_persist')) {
localStorage.setItem('ftb_auth_persist', JSON.stringify(next));
}
}
实施清单
新增:
apps/web/app/profile/page.tsx— 个人中心页面(含两个 Tab + 退出按钮)
修改:
apps/web/components/layout/Sidebar.tsx— 用户区绑定真实 user + 加 Settings iconapps/web/stores/useAuthStore.ts— 加refreshUser方法
验证
pnpm type-check0 错误pnpm build14+1 个页面成功(新增/profile)- 登录张三(产品经理):
- Sidebar 底部显示"张 张三 / 产品经理 / ⚙"
- 点 ⚙ 跳
/profile - 改姓名提交后 Sidebar 立即同步新名字
- 改密码后提示下次登录生效,重新登录用新密码可登入旧密码失败
- 点退出 → 弹确认 → 跳 /login + session 清空
- 三个表单的校验:手机/邮箱格式错误、新密短于 8、新密=旧密、确认不一致——都能在对应字段下显示红色文案