Files
yunke/docs/superpowers/specs/2026-08-08-yunke-export-chat-html-design.md
T

101 lines
6.2 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.
# 导出会话聊天为 HTML(③,前端接线)设计
> 日期:2026-08-08|分支:`feat/yunke-data-mirror`|路线图 ③
> 前置(已就绪):后端 `GET /api/export/chat-html`(`app/api/routers/export.py`)已实现、已注册、有测试——按 (channel, owner_account_id, talker_id|room_id, start?, end?) 拉消息 → 渲染**自包含 HTML 气泡页**(is_send 左右、带时间/文本)→ `text/html` attachment 下载。
> 数据/交互:全本地(运行时不碰云客)。云客「导出聊天记录」**2026-08-08 真机核实**:异步任务 → 下载中心(`POST /wechatweb/msg/exportChatData2Html`,参数 startdate/enddate/wechatId/friendId)。
> 用户决策(2026-08-08):**按我们的内部逻辑做**——不复刻云客的异步下载中心;HTML 渲染沿用已有 export.py(即"我们的逻辑")。
## 1. 目标与范围
**目标**:在 3 处加「导出」入口,触发已有的 `/api/export/chat-html`,让用户把一段会话(员工×客户/群,可带日期窗)下载成 HTML 文件。
**在本 plan(纯前端,后端不动)**:
1. **ChatTimeline 头部「导出」按钮**:用当前会话(channel/owner/talkerId 或 roomId)+ 当前 dateRange(start/end)→ 下载该窗内聊天 HTML。仅在 `hasActive` 时可用。
2. **CustomerList 操作列「导出HTML」**(在「查看聊天记录」旁):该 owner×friend_id,**全量消息**(列表无日期选择器)。
3. **GroupList 操作列「导出HTML」**:该 owner×room_id,全量消息。
**机制(我们的内部逻辑)**:直接浏览器下载——构造 `/api/export/chat-html?...` URL,`window.open(url, '_blank')` 触发后端返回的 attachment 下载。**同步、无下载中心**(本地小工具;后端 attachment `Content-Disposition` 强制下载)。
**不在本 plan**:
- 云客的异步任务 / 下载中心(不需要)。
- 导出多个会话 / 批量导出(云客也是单个会话导出)。
- HTML 排版改版(沿用 export.py 现有渲染)。
- 媒体字节内嵌(HTML 里媒体保持链接,不内嵌;export.py 现状)。
## 2. 后端契约(已就绪,引用)
`GET /api/export/chat-html`:
- query:`channel`(wechat/whatsapp)、`owner_account_id`(必填)、`talker_id?`、`room_id?`(二选一)、`start?`、`end?`(ms-epoch,可省=全量)。
- 返回:`text/html; charset=utf-8`,`Content-Disposition: attachment; filename="chat-...html"`。
- 逻辑:DM 限定 `room_id` 空;按 timestamp asc;渲染气泡(is_send 右、否则左)。
- 本 plan **不改后端**。
## 3. 前端
### 3.1 共用 URL 构造(`yunkes/src/api/index.ts`)
```typescript
/** 构造 /api/export/chat-html 下载 URL(浏览器直接下载,不走 getJson)。 */
export function exportChatUrl(params: {
channel: Channel
owner_account_id: string
talker_id?: string
room_id?: string
start?: number
end?: number
}): string {
const q = clean({
channel: params.channel,
owner_account_id: params.owner_account_id,
talker_id: params.talker_id,
room_id: params.room_id,
start: params.start,
end: params.end,
})
const usp = new URLSearchParams(q as Record<string, string>)
return `/api/export/chat-html?${usp.toString()}`
}
```
> 触发下载:`window.open(exportChatUrl({...}), '_blank')`(attachment 强制下载;本地 viewer 无 api_token,window.open 可达)。
### 3.2 ChatTimeline.vue
- 头部 `.yk-chat__filters` 区(step 5 已建,含日期选择器/搜索)加「导出」`el-button`:`v-if="hasActive"`,`@click` → `window.open(exportChatUrl({channel: channelTyped, owner_account_id: props.ownerId, talker_id: props.talkerId, room_id: props.roomId, start: winStart, end: winEnd}))`。
- 即「导出当前会话在当前日期窗内的聊天」。
### 3.3 CustomerList.vue(操作列)
- 在「查看聊天记录」按钮旁加 `<el-button link>导出HTML</el-button>`(wechat + WA 两 tab 的操作列)→ `window.open(exportChatUrl({channel: row.channel, owner_account_id: row.owner_account_id, talker_id: row.friend_id}))`(全量,无 start/end)。
- plan 核实两 tab 操作列结构(Plan 2 已建「查看聊天记录」→ chat)。
### 3.4 GroupList.vue(操作列)
- 在「查看聊天记录」旁加「导出HTML」→ `exportChatUrl({channel: row.channel, owner_account_id: row.owner_account_id, room_id: row.room_id})`(全量)。
## 4. 关键决策(已与用户确认 2026-08-08)
1. **后端已就绪**(export.py),③ = 纯前端 3 处按钮接线。
2. **同步直接下载**(`window.open` + attachment),不做云客异步下载中心。
3. **HTML 渲染沿用 export.py**(我们的内部逻辑,不改)。
4. **ChatTimeline 导出带 dateRange**;CustomerList/GroupList 导出**全量**(列表无日期选择器)。
5. 媒体保持链接(export.py 现状)。
## 5. 文件清单
- 改 `yunkes/src/api/index.ts`(`exportChatUrl` helper)。
- 改 `yunkes/src/views/ChatTimeline.vue`(头部「导出」按钮)。
- 改 `yunkes/src/views/CustomerList.vue`(操作列「导出HTML」)。
- 改 `yunkes/src/views/GroupList.vue`(操作列「导出HTML」)。
- **无后端改动**;export.py 不动。
## 6. 验收
- `vue-tsc -b` exit 0。
- **浏览器实测**(本地 smoke.db,无云客):
- ChatTimeline:进一个会话 → 「导出」→ 下载 `chat-wechat-{owner}-{talker}.html`,打开含该窗内消息气泡(员工右/客户左、时间、文本);改 dateRange 再导出 → 内容随窗变。
- CustomerList/GroupList:操作列「导出HTML」→ 下载该 owner×friend/room 的全量聊天 HTML。
- network:仅打 `/api/export/chat-html`(无云客)。
## 7. 风险
- **window.open 被拦截**:用户手势触发(button click)一般不拦;若拦可降级为隐藏 `<a download>` + `click()`。
- **api_token 鉴权**:本地 viewer 默认无 token(require_auth no-op)→ window.open 可达;若启用 token 需改 fetch+blob(本 plan 不涉及,记录)。
- **大对话导出耗时**:同步生成;超大对话可能慢,可接受(本地工具);后续必要时再异步。
- **CustomerList/GroupList 操作列结构**:plan 核实两 tab(wechat/WA)操作列「查看聊天记录」位置,照旁加按钮。