docs(plan): CallList ④ 微信语音 单元格可点 实现计划

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
浅念
2026-08-08 16:15:50 +08:00
parent 3a30a41ca1
commit 18919f3401
@@ -0,0 +1,133 @@
# CallList 列内可点(④,微信语音)Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development or superpowers:executing-plans. Steps use checkbox (`- [ ]`).
**Goal:** CallList「微信语音」tab 的 客户信息/员工微信/员工信息 三类单元格可点,对齐云客(复用 Phase B 客户→1:1 chat + step 5 员工→账号级聊天窗 + drawer)。
**Architecture:** 纯前端,仅 `CallList.vue`。加 `useRouter` + `chatChannel` 映射 + `viewCallCustomer`/`viewCallStaff` 两个 handler + voice_call 模板 3 处单元格包 `<el-button link>`。WA语音/电话 tab 不动。
**Tech Stack:** Vue3+TS+Element Plus;门禁 `vue-tsc -b`。无后端、无路由、无新组件。
**Spec:** `docs/superpowers/specs/2026-08-08-yunke-calllist-cell-click-design.md`(已过审 + 已收窄至 voice_call)。
---
## 关键事实(plan 核实,codebase)
- `CallList.vue` voice_call 模板块(`<template v-if="callType === 'voice_call'">`)现有 6 列:通话信息 / 客户信息 / 员工微信 / 员工信息 / 通话录音 / 通话时长。
- **客户信息** 单元格:`<div>{{ cell(row, 'talkerNickName') || '--' }}</div>` + 子行 `call_number || cell(row,'talker')`。
- **员工微信** 单元格:`<div>{{ cell(row, 'userWeChatNickName') || '--' }}</div>` + 子行 `cell(row,'userWeChatAlias') || cell(row,'weChatId')`。
- **员工信息** 单元格:`<div>{{ row.user_id_name || '--' }}</div>` + 子行 `cell(row,'userPhone')`。
- `cell(row, camelKey)` 已存在(包装 `readField`)。`openDrawer(row)` 已存在。
- `PhoneCall`(`api/types.ts`):`channel`(通话类型 voice_call/wa_voice_call/phone_call)、`user_id`/`user_id_name`、`call_number`、`raw_json`。
- 路由 `/chat/:channel/:ownerId/:talkerId?`(name `'chat'`,talkerId 可选)—— account 级 `/chat/wechat/{owner}` 与 1:1 `/chat/wechat/{owner}/{talker}` 都可用(step 5/Phase B 已建)。
- `CALL_FIELD_LABELS`(callFields.ts)含 `weChatId`、`talker`——可经 `cell()` 读。
- 云客真机(2026-08-08):客户→`detail/message/{owner}/{talker}`、员工→`wechat-window/1/{owner}`、员工姓名→详情页。
## 文件清单
- 改 `yunkes/src/views/CallList.vue`(仅 voice_call 模板 3 处 + script 加 router/handlers)。**仅此一文件**。
---
## Task 1: CallList 微信语音 单元格可点
**Files:** Modify `yunkes/src/views/CallList.vue`(仅此一文件)。
- [ ] **Step 1: script 加 router + handlers**
`import { computed, onMounted, ref } from 'vue'` 行附近加 `useRouter`:
```typescript
import { useRouter } from 'vue-router'
```
`const router = useRouter()`(放 script 顶部状态区,如 `const drawerOpen = ref(false)` 之前)。
加 helper + handlers(放 `openDrawer` 附近):
```typescript
/** 通话类型 → 聊天渠道(voice_call→wechat, wa_voice_call→whatsapp, phone_call→无)。 */
function chatChannel(ch: CallChannel): 'wechat' | 'whatsapp' | null {
return ch === 'voice_call' ? 'wechat' : ch === 'wa_voice_call' ? 'whatsapp' : null
}
/** 客户昵称 → 该员工×客户 1:1 聊天(复用 Phase B 导航)。 */
function viewCallCustomer(row: PhoneCall): void {
const owner = cell(row, 'weChatId')
const talker = cell(row, 'talker')
if (!owner || !talker) return
router.push({
name: 'chat',
params: { channel: 'wechat', ownerId: String(owner), talkerId: String(talker) },
})
}
/** 员工微信昵称 → 账号级聊天窗(复用 step 5 导航)。 */
function viewCallStaff(row: PhoneCall): void {
const owner = cell(row, 'weChatId')
if (!owner) return
router.push({ name: 'chat', params: { channel: 'wechat', ownerId: String(owner) } })
}
```
> `CallChannel` 已在 import(`import type { CallChannel, PhoneCall }`)。`chatChannel` 本 task 只用于 voice_call,但保留 WA 映射供后续。
- [ ] **Step 2: voice_call 模板 3 处单元格包 link button**
`<template v-if="callType === 'voice_call'">` 内:
**客户信息** 列(约 line 170):
```vue
<el-button v-if="cell(row, 'talkerNickName')" link type="primary" size="small" class="yk-cell__main" @click.stop="viewCallCustomer(row)">{{ cell(row, 'talkerNickName') }}</el-button>
<span v-else class="yk-cell__main">--</span>
```
(替换原 `<div>{{ cell(row, 'talkerNickName') || '--' }}</div>`;子行 `call_number || talker` 不变。)
**员工微信** 列(约 line 177):
```vue
<el-button v-if="cell(row, 'userWeChatNickName')" link type="primary" size="small" class="yk-cell__main" @click.stop="viewCallStaff(row)">{{ cell(row, 'userWeChatNickName') }}</el-button>
<span v-else class="yk-cell__main">--</span>
```
(替换原 `<div>{{ cell(row, 'userWeChatNickName') || '--' }}</div>`;子行不变。)
**员工信息** 列(约 line 183):
```vue
<el-button v-if="row.user_id_name" link type="primary" size="small" class="yk-cell__main" @click.stop="openDrawer(row)">{{ row.user_id_name }}</el-button>
<span v-else class="yk-cell__main">--</span>
```
(替换原 `<div>{{ row.user_id_name || '--' }}</div>`;子行 userPhone 不变。)
> `@click.stop` 阻止冒泡到 row-click(避免同时开 drawer)。`class="yk-cell__main"` 让按钮文字继承主文本样式(13px);可在 `<style scoped>` 加 `.yk-cell__main { font-size: 13px; }`(若需要;el-button link 默认字号可能略偏)。
- [ ] **Step 3: vue-tsc**
```bash
cd /d/refactor/yunke/yunkes && npx vue-tsc -b
```
Expected: exit 0。
- [ ] **Step 4: 浏览器实测**
启动后端(smoke.db)+前端。`/calls` → 微信语音 tab:
- 点客户昵称 → `/chat/wechat/{owner}/{talker}`(侧栏+该会话+消息)。
- 点员工微信昵称 → `/chat/wechat/{owner}`(账号级聊天窗+空态或会话列表)。
- 点员工姓名 → drawer。
- 行点击空白处 → drawer(row-click 不受影响)。
- 缺字段格显 `--`(不可点);WA语音/电话 tab 不变。
- [ ] **Step 5: Commit**
```bash
git add yunkes/src/views/CallList.vue
git commit -m "feat(calls): CallList 微信语音 客户/员工/姓名 单元格可点(复用聊天导航,对齐云客)"
```
End with blank line + `Co-Authored-By: Claude <noreply@anthropic.com>`。
---
## 验收(spec §6)
- [ ] `vue-tsc -b` exit 0。
- [ ] 浏览器(本地 smoke.db,无云客):微信语音 三单元格可点且跳转正确;缺字段 `--` 不可点;WA语音/电话 不变;row-click→drawer 不变;network 仅 localhost。
## 风险
- **`weChatId`/`talker` 缺失**:守卫已加(`if (!owner || !talker) return`),该格显 `--` 不可点。
- **WA语音/电话**:本 plan 不动;WA 待云客真机核实其 WA 语音列表后另做(小补丁,复用同 handler + `chatChannel('wa_voice_call')→'whatsapp'`)。