Co-Authored-By: Claude <noreply@anthropic.com>
9.5 KiB
微信员工 stat modal 列对齐 + 客户微信→聊天(Phase B)设计
日期:2026-08-08|分支:
feat/yunke-data-mirror|路线图 #2 Phase B 前置:Phase A 完成(GET /api/staff-chat-stats聚合端点 + StaffList 5 数字改读本地 summary + 日期选择器 + modal 按 metric 过滤+合成分页)。 数据/交互:全本地(运行时不碰云客)。所有云客行为 2026-08-08 Playwright 真机核实(crm2.yunkecn.com/cms/wechat/wechatlist)。 上游设计:docs/superpowers/specs/2026-08-05-yunke-ui-alignment-design.md(列+交互对齐云客,element plus 自然样式)。
1. 目标与范围
目标:Phase A 的 5 数字 stat modal 与云客完全对齐——补齐云客的列(序号/员工信息/员工微信),并让 modal 内「客户微信」可点 → 该员工×客户的聊天详情(带 Phase A 日期窗过滤)。
在本 plan(Phase B):
- StaffList stat modal 列对齐云客:
序号 / 客户微信(可点→聊天) / [仅有效沟通:发送消息数·接收消息数] / 员工信息 / 员工微信。 - modal 客户微信 二级可点:点客户微信 →
/chat/wechat/{员工wxid}/{客户talker_id}?start=&end=(同页router.push,带 StaffList 当前日期窗)。 - ChatTimeline 读日期窗 query:
?start=&end=存在时按窗过滤消息(复用getMessages已有的 start/end);不存在时全量(现行行为,向后兼容)。
不在本 plan(后续 / 总 spec 第 5 步):
- 聊天页页内日期选择器、会话列表、
/api/sessions聚合端点 → 总 spec 第 5 步「ChatTimeline 对齐」。 - 员工姓名 → 云客独立员工详情页(
userDetailNew)、主 badge/昵称 → 云客聊天窗口(会话列表)→ 第 5 步(本期维持 Plan 1 降级:drawer / 跳最近一条聊天)。 - 新标签页(
window.open)—— 云客 3 处点击都新开 tab,我们用 SPA 同页router.push(做法 A 已定)。 - 客户微信脱敏显示 —— 不做(见 §4 P2)。
2. 云客真机核实(2026-08-08)
2.1 stat modal 列(点 5 数字弹出)
| modal | 云客列(逐列 DOM 核实) |
|---|---|
| 沟通人数 / 跟进客户数 / 员工最后回复数 / 未回复客户数 | 序号 / 客户微信(可点) / 员工信息 / 员工微信 |
| 有效沟通数 | 序号 / 客户微信(可点) / 发送消息数 / 接收消息数 / 员工信息 / 员工微信 |
- 客户微信 =
<a href="/cms/wechat/detail/message/{ownerWxid}/{talkerWxid}/1?start=&end=">,文本脱敏「昵称(备注)微信号」。唯一可点列。 - 员工信息 =
<div><p>员工姓名</p><p>微信手机号</p></div>(纯展示)。 - 员工微信 = 头像 + 昵称 + 微信号(纯展示)。
- 末尾空表头是 Ant 的滚动条占位 cell,不是操作列(modal 无按行操作按钮)。
2.2 modal 客户微信 → 聊天详情
点客户微信 <a> 新开 tab 到 /cms/wechat/detail/message/{ownerWxid}/{customerWxid}/1?start={ms}&end={ms}:
- 聊天页自带「开始日期/结束日期」选择器,预填 modal 带来的 start/end(实测
2026-08-06 00:00:00 ~ 2026-08-08 23:59:59,即近 3 天 = 今日+前 2 天,与我们 Phase AdefaultDateRange精确一致)。 - 消息按此窗过滤(可在页内改窗)。
2.3 员工姓名 / 主 badge(本期不动,仅记录云客行为)
- 员工姓名 → 新 tab
/cms/customer/userDetailNew?userId={userId}(独立员工详情页)。 - 主 badge / 昵称 → 新 tab
/cms/wechat/salewechat/message/wechat-window/1/{wxid}/filehelper(微信聊天窗口,带会话列表)。 - 两者都是云客独立大页面,本期维持 Plan 1 降级(drawer / 跳最近聊天),完整对齐留总 spec 第 5 步。
3. 前端改动(纯前端,无后端)
3.1 yunkes/src/views/StaffList.vue — modal 列补齐 + 客户微信可点
commModalColsFor(metric)改为返回云客列序:序号:{prop:'_serial', label:'序号', width:70, align:'center'}客户微信:{prop:'friend_name', label:'客户微信', minWidth:160, onClick: goChat}(显示friend_name+talker_id;onClick由YkStaffDetailModal渲染为 link button)- 仅
effective:插入{prop:'send_count', label:'发送消息数', width:110, align:'center'}/{prop:'receive_count', label:'接收消息数', width:110, align:'center'} 员工信息:{prop:'staff_info', label:'员工信息', minWidth:140}员工微信:{prop:'staff_wx', label:'员工微信', minWidth:160}
- fetcher 合成分页时给每行注入字段(在现有 slice 之后 map):
_serial = (page-1)*page_size + i + 1(跨页连续序号)staff_info= 员工姓名 + 微信手机号(取自 staffrow:userName+phone;字段最终映射 plan 核实phonevssalePhone)staff_wx= 昵称 + 微信号(nickName+account_id)
goChat(customer)(闭包捕获 staffrow):router.push({ name: 'chat', params: { channel: 'wechat', ownerId: row.account_id, talkerId: customer.talker_id }, query: { start: String(statsStart.value), end: String(statsEnd.value) }, })- 守卫:
row.account_id与customer.talker_id都存在才跳;缺失静默 no-op(与YkStaffDetailModal的onClick?一致)。
- 守卫:
- 客户微信显示不脱敏(私有镜像,显示
friend_name全名 +talker_id;与 CustomerList/GroupList 一致)。 - 员工姓名→drawer、主badge/昵称→viewChat:不动(维持 Plan 1)。
YkStaffDetailModal.vue不改:序号/员工信息/员工微信都靠 fetcher 注入字段实现(cols 仍数据驱动);客户微信用其已有的onClick渲染为 link button。 单元格视觉:员工信息/员工微信 云客是双行/带头像,我们用单行文本(name 手机号/nick wxid)——总 spec 已声明「不对齐 antd vs el-table 像素级视觉」,信息一致即可。
3.2 yunkes/src/views/ChatTimeline.vue — 读日期窗 query
- 加
useRoute(),读route.query.start/route.query.end(ms-epoch 字符串,可选)。 - 存在且为合法数字 → 透传
getMessages({ channel, owner_account_id, talker_id/room_id, start, end, page, page_size })(getMessages已支持 start/end)。 - 不存在 → 现行全量行为(向后兼容:CustomerList「查看聊天记录」、StaffList badge
viewChat等入口不受影响)。 - 副标题:有窗时追加「· 时段:{start} ~ {end}」(
formatTime格式化)。 - 分页不变(asc 翻页照旧,total 随窗变化)。
3.3 不改
YkStaffDetailModal.vue、后端、api/index.ts、types.ts(复用 Phase A 的StaffChatCustomer/StaffChatStats)。router/index.ts(/chat/:channel/:ownerId/:talkerId已存在;query 透传无需改路由)。
4. 关键决策(已与用户确认 2026-08-08)
- P1 = a:ChatTimeline 仅带窗过滤(从 modal 进时),页内日期选择器留总 spec 第 5 步。理由:钻取的本质行为(按窗看该客户聊天)已对齐云客;选择器属聊天页通用功能,第 5 步加会话列表/查询栏时大概率重排版,现做有返工风险。
- P2 = 显示全名(不脱敏):私有镜像的意义即看全云客脱敏的数据;与 CustomerList/GroupList/ChatTimeline 一致。云客脱敏是其多租户隐私策略,与我们的场景无关。
- 同页
router.push(不window.open新 tab)—— SPA 惯例。 - 员工姓名 / badge 维持降级(与总 spec 第 5 步排序一致)。
- 默认近 3 天窗:Phase A
defaultDateRange(今日+前 2 天,00:00~23:59:59)与云客 stat 窗实测精确一致。
5. 文件清单(预期)
- 改
yunkes/src/views/StaffList.vue:commModalColsFor列补齐 + fetcher 注入_serial/staff_info/staff_wx+goChat(客户微信 onClick → 带窗跳聊天)。 - 改
yunkes/src/views/ChatTimeline.vue:useRoute读 start/end → 透传getMessages+ 副标题显窗。 YkStaffDetailModal.vue不改;无后端;无新表/迁移。
6. 验收
vue-tsc -bexit 0,无 console 报错。- 浏览器实测(本地后端 + smoke.db,不碰云客):
- stat modal 列对齐云客:序号 / 客户微信 / [有效沟通多发送·接收] / 员工信息 / 员工微信;无操作列。
- 点 modal 客户微信 → 跳
/chat/wechat/{owner}/{talker}且 URL 带start/end;ChatTimeline 仅显示窗内消息(改 StaffList 日期窗 → modal 数字与跳转窗同步)。 - 从其它入口进 ChatTimeline(无 query)→ 全量(向后兼容)。
- 员工姓名→drawer、badge→最近聊天 不变。
- network 仅 localhost(无云客)。
7. 风险
- ChatTimeline 过滤 + 分页交互:getMessages 已支持 start/end + 分页,但需确认 asc 翻页 + total 在窗下符合预期(plan 核实 + 浏览器验)。
- 员工信息/员工微信 字段映射:
phonevssalePhone、微信号用account_id还是alias——plan 读 Account raw_json 核实后定(缺失显示--)。 - 客户微信 onClick 数据完备性:
row.account_id(员工 wxid)+customer.talker_id必须都在;Phase A 已保证 customer 有 talker_id,staff row 有 account_id。 - 未对齐项(已声明):页内日期选择器、会话列表、员工详情页、新标签页、脱敏——均为刻意留白(总 spec 第 5 步),非缺陷。