f4f0855439
Co-Authored-By: Claude <noreply@anthropic.com>
6.0 KiB
6.0 KiB
CallList 列内可点(④)设计
日期:2026-08-08|分支:
feat/yunke-data-mirror|路线图 ④ 前置:Phase B(stat modal 客户微信→/chat/{ch}/{owner}/{talker})+ step 5(badge/员工→账号级/chat/{ch}/{owner}聊天窗 + ChatSidebar)。 数据/交互:全本地(运行时不碰云客)。云客「微信语音通话列表」2026-08-08 Playwright 真机核实(/cms/wechat/voice-call-list)。
1. 目标与范围
目标:CallList 的「微信语音」「WA 语音」两 tab 里,客户信息 / 员工微信 / 员工信息 三类单元格可点,行为对齐云客——复用 Phase B + step 5 已建的导航。
在本 plan:
- 微信语音(voice_call)/ WA 语音(wa_voice_call) tab,每行使三类单元格可点:
- 客户信息(微信/WA 昵称)→
/chat/{chatChannel}/{owner_account_id}/{talker_id}(该客户 1:1 聊天;复用 Phase B)。 - 员工微信(昵称)→
/chat/{chatChannel}/{owner_account_id}(账号级聊天窗;复用 step 5)。 - 员工信息(姓名)→
openDrawer(row)(通话全字段详情;云客姓名→员工详情页,我们降级 drawer,同 StaffList)。
- 客户信息(微信/WA 昵称)→
不在本 plan:
- 电话(phone_call)tab:号码型,无 wechat/WA chat 映射;云客电话列表是另一套 CRM。维持不可点(row-click→drawer 保留),待电话数据映射明确后再议。
- 客户/员工的云客独立详情页(
userDetailNew等):维持降级(drawer / 聊天窗),与 StaffList 一致。 - 录音播放、行点击→drawer:现状不变。
2. 云客真机核实(2026-08-08,微信语音通话列表)
逐单元格 DOM 核实(首行):
- 客户信息:
<a href="/cms/wechat/detail/message/{ownerWxid}/{customerWxid}/1?start={callTime}&scrollToBottom=0">微信昵称</a>→ 该客户 1:1 聊天(带 callTime 定位)。 - 员工微信:
<a href="/cms/wechat/salewechat/message/wechat-window/1/{ownerWxid}/1">昵称</a>→ 账号级聊天窗。 - 员工信息:
<div class="link">姓名</div>→ 员工详情页。 - 通话信息 / 通话录音 / 通话时长:不可点(录音是播放按钮)。
结论:3 类可点单元格,目标 = 客户聊天 / 账号聊天窗 / 员工详情——全部我们已有对应物。
3. 前端(仅 yunkes/src/views/CallList.vue)
- call-type → chat-channel 映射:call 的
channel是通话类型(voice_call/wa_voice_call/phone_call),chat 路由用wechat/whatsapp。加 helper:function chatChannel(ch: CallChannel): 'wechat' | 'whatsapp' | null { return ch === 'voice_call' ? 'wechat' : ch === 'wa_voice_call' ? 'whatsapp' : null } - 读 owner/talker(经
readField,CallList 已用):- 语音:
owner = readField(row,'weChatId'),talker = readField(row,'talker')(fallbackrow.call_number)。 - WA:owner/talker 用 WA 字段(plan 核实具体键名,如
uid/talker或waId)。
- 语音:
- 3 个 onClick handler:
function viewCallCustomer(row: PhoneCall) { const ch = chatChannel(row.channel); const owner = cell(row,'weChatId') /* WA 另定 */; const talker = cell(row,'talker') if (!ch || !owner || !talker) return router.push({ name: 'chat', params: { channel: ch, ownerId: String(owner), talkerId: String(talker) } }) } function viewCallStaff(row: PhoneCall) { const ch = chatChannel(row.channel); const owner = cell(row,'weChatId') /* WA 另定 */ if (!ch || !owner) return router.push({ name: 'chat', params: { channel: ch, ownerId: String(owner) } }) } // 员工信息姓名 → openDrawer(row)(已有)不带
start/endquery:聊天窗默认近 2 周(step 5),近期通话在其内可见;简化。 - 模板:把对应单元格的昵称/姓名文本包成
<el-button link type="primary" size="small" @click.stop="...">(与 StaffList 数字单元格一致风格);缺失字段则显--(不可点)。三处:- 微信语音/wa语音
客户信息列的昵称行 →viewCallCustomer。 - 微信语音/wa语音
员工微信列的昵称行 →viewCallStaff。 - 微信语音/wa语音
员工信息列的姓名行 →openDrawer。
- 微信语音/wa语音
- 电话 tab 模板不动。
import { useRouter } from 'vue-router'+const router = useRouter()。
4. 关键决策(已与用户确认 2026-08-08)
- 范围 = 微信语音 + WA语音;电话不动(无 chat 映射)。
- 复用 Phase B(客户→1:1 chat)+ step 5(员工→账号级聊天窗)+ drawer(员工详情降级)——不新增导航逻辑/路由/组件。
- 不带日期窗 query:聊天窗默认近 2 周覆盖近期通话;简化。
- call-type→chat-channel 映射(voice_call→wechat, wa_voice_call→whatsapp)。
- WA owner/talker 字段 plan 核实 raw_json 键名(语音已确认 weChatId/talker)。
5. 文件清单
- 改
yunkes/src/views/CallList.vue(加useRouter、chatChannel、3 个 onClick、模板 6 处单元格包 link button)。仅此一文件;无后端、无路由、无新组件。
6. 验收
vue-tsc -bexit 0。- 浏览器实测(本地 smoke.db,无云客):
- 微信语音 tab:点客户昵称 →
/chat/wechat/{owner}/{talker}(侧栏+该会话);点员工微信昵称 →/chat/wechat/{owner}(账号级聊天窗+空态);点员工姓名 → drawer。 - WA语音 tab:同上,channel=whatsapp(若 smoke.db 有 WA 语音数据)。
- 电话 tab:单元格不可点,row-click→drawer 不变。
- 缺字段格显
--不可点;network 仅 localhost。
- 微信语音 tab:点客户昵称 →
7. 风险
- WA 字段键名:WA 语音 raw_json 的 owner uid / talker 键名 plan 核实(语音 weChatId/talker 已确认);缺失→该格不可点。
- smoke.db WA 语音数据:可能稀疏(Plan 4a 实跑 WA 593 条进库,应可测;若无则以语音为主验)。
- owner/talker 类型:raw_json 值可能非 string,onClick 里
String(...)强制 + 守卫。