Files
yunke/docs/superpowers/specs/2026-08-05-yunke-ui-alignment-design.md
T
浅念 1d815d84b9 docs: UI alignment spec + StaffList interaction plan
Spec: align all list/detail pages to 云客 (columns + interactions); covers interaction taxonomy, backend extensions (customer add_time/filters, /api/sessions), phase-0 capture. Plan 1: StaffList interaction closure + customer backend + YkStaffDetailModal.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-05 18:30:12 +08:00

142 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# yunkes 前端页面对齐云客后台 — 设计文档
- 日期:2026-08-05
- 分支:feat/yunke-data-mirror
- 状态:待评审
## 背景
yunkes 是云客 CRM(crm2.yunkecn.com)数据镜像的查看后台(Vue3 + Element Plus)。当前各列表/详情页的表格列结构与交互均与云客后台(Ant Design Vue)不一致,用户在两套系统间切换认知成本高。
需将所有"有云客对应"的页面**列结构 + 交互功能**对齐云客。StaffList(员工列表)已于 2026-08-05 完成列结构对齐(待补交互),验证了采集与改造方法。
## 目标
所有有云客对应的列表/详情页:
1. **列结构**(列名、顺序、操作按钮、复合单元格字段组合)与云客一致
2. **交互功能**(可点元素 + 点击行为:跳转/modal/新 tab)与云客一致
视觉用 Element Plus 自然样式(不引入 antd,不做像素级复刻)。包含必要的后端筛选参数扩展。
## 范围
### 对齐(6 个页面 + StaffList 补交互)
| 页面 | 路由 | 云客对应 | 列对齐 | 交互 |
|---|---|---|---|---|
| StaffList | `/staff` | 员工微信列表 + WhatsApp 员工列表 | ✅ 已完成 | 本轮补 |
| CustomerList | `/customers` | 客户列表 | 待做 | 待做 |
| GroupList | `/groups` | 群列表/统计 | 待做 | 待做 |
| CallList | `/calls` | 语音通话列表 | 待做 | 待做 |
| SmsList | `/sms` | 短信记录 | 待做 | 待做 |
| ChatTimeline | `/chat`,`/group` | 聊天记录(会话列表+消息流,只读查看器) | 待做 | 待做 |
| SearchView | `/search` | 全局搜索 | 待做 | 待做 |
### 删除
- **AccountList `/accounts`** — 云客无此页面,且与 StaffList 内容重复。删除路由 + 文件 + 导航入口,`/accounts` → `/staff` 客户端重定向。
### 不动
- SyncStatus `/sync` — 运维监控页,云客无对应。
## 列对齐设计
**对齐**:列名、列顺序、操作按钮(文字/位置)、复合单元格字段组合。
**不对齐**:antd vs el-table 像素级视觉(配色/行高/表头用 el-table 默认 + 少量 scoped 微调)。
**方法**(StaffList 已验证):Playwright `browser_evaluate` 采云客表头 + 复合单元格字段(`td.innerText` 按行拆分)+ 操作按钮文字 → dump smoke.db raw_json 核字段 → 改 `.vue`(el-table 列定义,字段经 `readField` 读)→ 浏览器对比 + `vue-tsc`。
## 交互对齐设计
云客列表的可点元素分三类,每类统一处理模式(已在 WhatsApp 员工表验证):
### 类型 1:跳转到列表页(route push)
- **触发**:操作按钮(如"好友列表""群聊")、部分可点数字
- **行为**:`router.push` 到目标列表页,带 query 预筛(`?owner_account_id=` 或 `?user_id=`)
- **目标页改动**:读 `route.query` 的 owner/user 参数 → 传给 API 筛选 + 顶部显示"按员工 X 筛选"chip + 清除按钮
- **云客实例**:WhatsApp 员工表"好友列表"→ `friendlist/0/{userId}`,"群聊"→ `friendlist/1/{userId}`
- **后端**:`/api/customers`、`/api/groups` 已支持 `owner_account_id` ✓;新增 `user_id` 筛选(云客按 userId)
### 类型 2:明细 modal(el-dialog)
- **触发**:统计数字(如"添加人数""沟通人数""消息数")
- **行为**:弹出 `el-dialog`,内嵌精简明细表格,数据按"该员工 + 该维度"筛
- **实现**:通用组件 `YkStaffDetailModal`(props:title / api 函数 / columns / 筛选参数),多页复用。复用 `el-table` + `el-pagination`
- **云客实例**:WhatsApp 员工表"添加人数"→ modal 列 `序号/员工信息/员工WhatsApp/客户WhatsApp/添加时间/最后沟通时间`
- **后端**(customer raw_json 已确认含 `addTime`/`lastChatTime`/`messageCount`):
- 添加人数 → `customer where owner=X AND add_time >= now-N天` → 加 `add_time_since` 参数(依赖新增 `Customer.add_time` 列,见"后端端点需求")
- 沟通人数 → `customer where owner=X AND lastChatTime >= now-N天` → 加 `last_chat_since` 参数
- 其他维度(消息数/好友数等)执行时确认对应查询条件
### 类型 3:新 tab 聊天
- **触发**:昵称 / 账号
- **行为**:`router.push`(或新 tab)到 ChatTimeline `/chat/:channel/:ownerId/:talkerId`
- **云客实例**:WhatsApp 员工表 WA 昵称 → `wschatroom/...`(该账号聊天记录)
- **后端**:`/api/messages` 已支持 `owner_account_id` ✓;点昵称打开的是**会话列表 + 消息流**视图(已验证云客 wschatroom 即此结构,只读、无发消息),需新增 `/api/sessions` 聚合端点(见后端端点需求)
### 交互探测方法(每页执行时)
用 Playwright 探测脚本(已验证):对每行每个可点元素(`button/a`),点击后记录"URL 变化 / modal title / popup",每次点前回到列表页、关闭残留 modal/tab。产出该页"可点元素 → 行为类型 → 目标"映射表。
## 后端端点需求
| 端点 | 扩展 | 依据列 | 用途 |
|---|---|---|---|
| `GET /api/customers` | 加 `user_id` | `Customer.user_id`(已有列) | 按员工筛客户 |
| `GET /api/customers` | 加 `last_chat_since` | `Customer.last_chat_time`(epoch 列) | 沟通人数 modal |
| `GET /api/customers` | 加 `add_time_since` | **需新增 `Customer.add_time` 列**(见下) | 添加人数 modal |
| `GET /api/groups` | 加 `user_id` | `ChatGroup.owner_user_id`(参数名 `user_id` → 此列,注意映射) | 按员工筛群 |
| `GET /api/accounts` | — | 已支持 channel | StaffList 用 |
| `GET /api/messages` | — | 已支持 `owner_account_id` | 昵称跳聊天 ✓ |
| `GET /api/sessions`(新增) | 按账号列会话:好友(customer)+群聊(group),含今日消息数 + 最近联系时间 | 聚合 customer/group + message(当日计数) | ChatTimeline 会话列表、类型 3 昵称入口 |
**`user_id` / `last_chat_since`**:直接走现有 router 模式(`Query()` 参数 + `conditions.append`),单测覆盖。
**`add_time_since` 需要数据模型扩展(本计划唯一 schema 变更)**:`Customer` 当前无 `add_time` 结构化列——`addTime` 仅在 `raw_json` 里且是字符串 `"YYYY-MM-DD HH:mm:ss"`,无法用 `col >= value` 直接筛(JSON-path 查询方言相关且慢,不采用)。方案:新增 `Customer.add_time` 列(BigInteger ms-epoch,对齐 `last_chat_time`)+ Alembic 迁移 + 回填脚本(解析 `raw_json.addTime` 字符串 → epoch)。完成后即可走标准筛选,参照 `last_chat_time` 既有实现与回填方式(项目已有 surrogate 修复回填先例)。
**同步层适配(关键,易漏)**:增量同步引擎(fetcher/ingest)抓取新 customer 时也必须解析 `raw_json.addTime` 填充 `add_time`,否则回填之后到达的增量数据该字段仍为 null。回填脚本只覆盖历史,同步层覆盖增量,两者都要做。
## 顺序
0. **全量采集(plan 第 0 步,必做)**:对每个待做页面,用 Playwright 采云客的列结构 + 可点元素 + 交互行为,dump 对应 raw_json 核字段,产出每页"目标列 + 交互映射 + 字段覆盖"清单。把当前的外推假设变成事实,避免返工。当前仅 WhatsApp 员工表 + 聊天页结构是已采事实,其余为外推。
1. **StaffList 补交互**(已采,先闭环):数字 modal + 操作跳转 + 昵称聊天 + 后端 customer 筛选参数
2. **CustomerList**:列对齐 + 可点元素交互(预计含跳客户详情、消息数 modal 等)
3. **GroupList**:列对齐 + 交互 + 后端 group user_id
4. **CallList + SmsList**(并行):列对齐 + 交互
5. **ChatTimeline**:对齐云客只读聊天查看器——左侧会话列表(好友/群聊 tab + 今日消息数 + 最近联系时间)+ 右侧消息流 + 查询/导出按钮;新增 `/api/sessions` 聚合端点(工作量较大,单独评估)
6. **SearchView**:搜索结果布局对齐 + 交互
7. **AccountList 删除**(收尾)
## 共性策略
- 沿用统一模式:`useChannelStore`、rtl 全字段抽屉(`build*Sections`)、`[20/50/100]` 分页、`row-click` 开抽屉。
- 字段读取复用各 `utils/*Fields.ts` 的 `readField`;新字段补进 `*_FIELD_LABELS`/`*_FIELD_GROUPS`。
- **字符串时间修复**:云客 `addTime`/`lastUpdate` 等是 `"YYYY-MM-DD HH:mm:ss"`,旧代码按 number 解析显示 `--`;统一用 `cellTimeText`(字符串直显、number 才 formatTime)。StaffList 已修,其他页一并修。
- **明细 modal 抽通用组件**(`YkStaffDetailModal`)——多页多数字复用,值得抽(不违反 YAGNI)。
- CallList 的 `channel` 是 `phone_call`/`voice_call`(通话类型),保留其 LOCAL `callType` 筛选。
- 不引入云客有但后端不支持的功能(导出/查询等装饰按钮),除非单独要求。
## AccountList 删除清单
- `router/index.ts`:移除 `/accounts` 路由记录,新增 `/accounts` → `/staff` 重定向。
- 删除 `views/AccountList.vue`。
- 清理导航入口(`layouts/MainLayout.vue` 约 line 126 指向 `/accounts` 的菜单项)。
- 更新 `StaffList.vue` 两处对 `AccountList.vue` 的注释引用(drawer 实际来自 `accountFields.ts`)。
- **保留数据层**:后端 `GET /api/accounts`(yunke-server)与前端 `getAccounts()`(`api/index.ts`,StaffList 依赖)是数据接口,不删。
- 全仓 grep `AccountList`、`/accounts` 确认无 UI 残留引用。
## 验收标准
- 每页列名/顺序/操作按钮与云客一致(截图留档)。
- 每页可点元素的行为与云客一致(跳转目标 / modal 列 / 新 tab),探测对比留档。
- 数据正确填充(raw_json 读字段,无整列 `--`)。
- 字符串时间字段正确显示。
- 后端新筛选参数有单测;`Customer.add_time` 迁移 + 回填 + **同步引擎填充**完成;`/api/sessions` 聚合端点有单测;`pytest` 全绿(≥323 测试)。
- `vue-tsc -b` exit 0,无 console 报错。
- `/accounts` 客户端重定向到 `/staff`(URL 变 `/staff`、StaffList 渲染;非 HTTP 301)。
## 风险与降级
- **字段缺口**:某页云客 KPI 字段不在 raw_json → dump 确认;缺失显示 `--`/`0`,不阻塞,记入附录。
- **modal 维度查询不明**:某些数字(如"消息数")对应的后端筛选条件云客未必直白 → 以云客 modal 实际返回数据为准,反推查询;无法精确复刻的,降级为跳转到对应列表页。
- **会话列表视图(已验证需要)**:云客聊天页 = 会话列表 + 消息流(只读、无发消息),需新增 `/api/sessions` 聚合端点(好友/群聊 + 今日消息数 + 最近联系时间)。今日消息数需按 message 当日计数聚合,注意大数据量下的查询性能。
- **modal 统计窗口**:员工列表统计已验证**不依赖日期选择器**(顶部 pickerCount=0,固定统计),但"添加人数 N"的窗口语义(最近几天/全部)仍未确认 → 执行时从 modal 明细的 addTime 范围反推;无法确认时默认取全部或最近 7 天并标注近似。
- **采集时效**:云客改版则列/交互会变 → 以采集时刻为准,spec 记录采集日期(2026-08-05)。
- **工作量**:列+交互+后端是大工程 → plan 按"页面 × 阶段"分解,每页可独立交付,不阻塞整体。