Files
agent-frontend-web/docs/superpowers/specs/2026-08-06-frontend-sidebar-user-area-design.md
2026-08-07 13:56:29 +08:00

4.9 KiB
Raw Permalink Blame History

Frontend 会话列表底部用户区域块 — Design Spec

日期: 2026-08-06 状态: Approved

背景

src/views/frontend/index.vue 是核心 AI 对话页面,左侧是 ConversationSidebar(会话列表)。当前侧边栏底部没有任何用户相关的入口:用户无法在不离开页面的情况下查看自己的头像/昵称、退出登录、或跳转到智能体广场。

目标

在会话列表底部新增一个区域块,提供:

  1. 用户头像 + 昵称展示,点击打开「个人资料」修改弹窗(可改头像、昵称)
  2. 「退出」按钮(二次确认后退出登录)
  3. 「前往智能体广场」按钮

侧边栏收起时,该区域块仅显示一个小头像,点击弹出菜单。

非目标

  • 不修改智能体广场页本身
  • 不修改会话列表的样式或行为
  • 不引入新的退出 / 上传头像后端接口

方案概览

新增两个组件,ConversationSidebar 引入并放在底部。

新增文件

  • src/views/frontend/components/UserArea.vue

    • 展示用户头像 + 昵称、退出按钮、前往智能体广场按钮
    • 接收 collapsed prop(来自 ConversationSidebar
    • 展开时:完整展示三块内容;收起时:仅显示小圆形头像,点击弹出 dropdown 菜单
    • 通过 defineExpose 暴露 openInfoDialog() 方法,父组件 ConversationSidebar 用 ref 调用(与现有 defineExpose({ refresh: fetchSessions, ... }) 风格一致)
  • src/views/frontend/components/UserInfoDialog.vue

    • Element Plus el-dialog,标题"个人资料",宽 500px
    • 内容:头像裁剪上传区(vue-cropper 固定 1:1+ 昵称输入框
    • 接收 modelValue / user propsemit update:modelValuesaved
    • 保存时调用:
      • uploadAvatar()PUT /system/user/profile/avatar
      • updateUserProfile({ nickName })PUT /system/user/profile
    • 保存成功后更新 useUserStore()avatarnickName,并 emit saved,父组件 ElMessage 成功提示

修改文件

  • src/views/frontend/components/ConversationSidebar.vue
    • sidebar-content 内的 <el-scrollbar> 下方引入 <UserArea ref="userAreaRef" :collapsed="collapsed" />
    • <el-scrollbar>heightcalc(100vh - 160px) 改为 calc(100vh - 240px)(预留底部 user-area 高度)
    • sidebar-content 改为 flex 布局,使 <el-scrollbar> 占中间,<UserArea> 占底部固定高度

组件契约

UserArea.vue

名称 类型 说明
Props
collapsed Boolean 侧边栏是否收起
Methods (expose)
openInfoDialog() () => void 打开个人资料弹窗

UserInfoDialog.vue

名称 类型 说明
Props
modelValue Boolean 弹窗显隐(v-model
user Object 当前用户 { avatar, nickName }
Emit
update:modelValue (val: Boolean) 显隐变化
saved (user: Object) 保存成功后回调

数据流

  1. ConversationSidebar 通过 useUserStore() 取用户信息,传给 UserAreaUserInfoDialog
  2. 用户点击头像 → UserArea.openInfoDialog() → 内部把 infoDialogVisible.value = true<UserInfoDialog v-model="infoDialogVisible" :user="user" @saved="onSaved" />
  3. UserInfoDialog 内部:
    • 选择本地图片 → vue-cropper 裁剪 → 用户点"保存"
    • uploadAvatar(formData) → 成功后调 updateUserProfile({ nickName }) → 更新 store
  4. UserInfoDialog 关闭后 ConversationSidebar 触发 ElMessage.success('修改成功')

错误处理

  • 上传头像失败:ElMessage.error('头像上传失败'),弹窗保持打开以便重试
  • 更新昵称失败:ElMessage.error('修改失败')
  • 退出失败:ElMessage.error('退出失败,请稍后重试')
  • 网络异常:依赖 request.js 的统一错误处理

视觉规范

  • 与现有侧边栏保持一致色系:#e4e7ed 分割线、#409eff 主色、#f56c6c 危险色
  • 头像 36×36 圆形(收起时 28×28)
  • 昵称省略号截断 text-overflow: ellipsis
  • 按钮文字与图标间距 6px
  • 顶部细分割线:border-top: 1px solid #e4e7ed

测试

  • 手动测试:本地 yarn dev,访问 /frontend/index
    • 头像昵称正确展示
    • 点击头像 → 弹窗打开 → 修改头像/昵称 → 保存 → 列表头像昵称同步刷新
    • 点击退出 → 确认 → 跳转到 /login
    • 点击「前往智能体广场」 → 跳转到 /frontend/marketplace
    • 收起侧边栏 → 仅显示小头像 → hover 出 tooltip → 点击出 dropdown 菜单

风险

  • 头像上传接口需要登录态,已通过 request.js 自动带 token 处理
  • 弹窗高度可能在小屏幕溢出,宽度固定 500px,自适应垂直

后续可扩展(不在本次范围)

  • 收起状态下的 dropdown 也可改成 popover
  • 头像昵称展示支持在线状态