Co-Authored-By: Claude <noreply@anthropic.com>
5.8 KiB
导出会话聊天为 HTML(③)Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: superpowers:subagent-driven-development or superpowers:executing-plans. Steps use checkbox (
- [ ]).
Goal: ChatTimeline 聊天窗加「导出」按钮(触发已有 /api/export/chat-html)+ 修 downloadBlob 文件名丢失 bug(惠及 3 处导出按钮)。
Architecture: 纯前端 2 文件。client.ts 的 downloadBlob 改用裸 axios(不经解包拦截器)保 Content-Disposition → 正确文件名;ChatTimeline.vue 头部加「导出」el-button → downloadBlob('/export/chat-html', {channel,owner,talker|room,start,end}) 用当前 dateRange。后端 export.py 不动;CustomerList/GroupList 已有导出按钮不动。
Tech Stack: Vue3+TS+Element Plus(yunkes/);门禁 vue-tsc -b。无后端。
Spec: docs/superpowers/specs/2026-08-08-yunke-export-chat-html-design.md(已过审 + 已纠正)。
关键事实(plan 核实)
- 后端
GET /api/export/chat-html(export.py)已就绪:参数 channel/owner_account_id/talker_id?/room_id?/start?/end?,返回text/html,Content-Disposition: attachment; filename="chat-{channel}-{owner}-{peer}.html"。不改。 client.ts现状:http = axios.create({baseURL:'/api'})+ 响应拦截器(r)=>r.data(解包 body,丢头)。downloadBlob(46-66)用http.get(url,{params,responseType:'blob'})→res已是 Blob(头丢)→ 文件名硬编码'chat-export.html'。- CustomerList(:201
downloadBlob('/export/chat-html', params)+ 时间 modal)、GroupList(:166)已有导出按钮(wechat tab)。不动。 - ChatTimeline(step 5/Task4 后):
defineProps<{channel,ownerId,talkerId?,roomId?}>、channelTyped(44)、isGroup(45)、winStart/winEnd(103-104)、hasActive(56)、ElMessage已 import、.yk-chat__filters区已存在(含日期选择器/搜索)。缺「导出」按钮。 axios已在 client.ts import。
文件清单
- 改
yunkes/src/api/client.ts(downloadBlob文件名修复)。 - 改
yunkes/src/views/ChatTimeline.vue(头部「导出」按钮 +exportCurrent)。 - 无后端、无其它文件。
Task 1: downloadBlob 文件名修复 + ChatTimeline 导出按钮
Files: yunkes/src/api/client.ts、yunkes/src/views/ChatTimeline.vue。
- Step 1:
client.ts—downloadBlob改裸 axios 保头
把现有 downloadBlob(46-66)替换为:
/** 以 blob 下载(HTML 导出等附件)。从 Content-Disposition 取文件名(绕开 http 的 (r)=>r.data 解包拦截器)。 */
export async function downloadBlob(
url: string,
params?: Record<string, unknown>,
): Promise<string> {
// 裸 axios(补 /api 前缀)拿全响应(含 headers),不经 http 拦截器
const res = await axios.get(`/api${url}`, { params, responseType: 'blob' })
const cd = (res.headers['content-disposition'] as string) || ''
const m = /filename="?([^";]+)"?/i.exec(cd)
const filename = m ? m[1] : 'chat-export.html'
const objUrl = URL.createObjectURL(res.data as Blob)
const a = document.createElement('a')
a.href = objUrl
a.download = filename
document.body.appendChild(a)
try {
a.click()
} finally {
document.body.removeChild(a)
URL.revokeObjectURL(objUrl)
}
return filename
}
url入参仍是相对/export/chat-html(与 CustomerList/GroupList 现有调用一致);函数内补/api。错误处理:裸 axios 抛原始错,调用方 try/catch 兜底(CustomerList 现状亦如此)。
- Step 2:
ChatTimeline.vue— import + exportCurrent + 按钮
import(ElMessage 已在;加 downloadBlob):
import { downloadBlob } from '@/api/client'
若
@/api已 re-exportdownloadBlob则从@/api导;否则从@/api/client。plan 核实(grepexport.*downloadBlobin api/index.ts)。
加 handler(放 onSelectSession/loadInitial 附近):
async function exportCurrent(): Promise<void> {
if (!hasActive.value) return
try {
await downloadBlob('/export/chat-html', {
channel: channelTyped.value,
owner_account_id: props.ownerId,
talker_id: isGroup.value ? undefined : props.talkerId,
room_id: isGroup.value ? props.roomId : undefined,
start: winStart.value,
end: winEnd.value,
})
} catch (e) {
ElMessage.error(`导出失败:${(e as Error).message}`)
}
}
模板 .yk-chat__filters 区加(在日期选择器/搜索旁):
<el-button v-if="hasActive" size="small" type="primary" plain @click="exportCurrent">导出</el-button>
- Step 3: vue-tsc
cd /d/refactor/yunke/yunkes && npx vue-tsc -b
Expected: exit 0。
- Step 4: 浏览器实测
启动后端(smoke.db)+前端。/chat/wechat/{owner}/{talker}(有消息的会话)→「导出」→ 下载文件名 chat-wechat-{owner}-{talker}.html(非 chat-export.html);打开含该 dateRange 内消息气泡。改 dateRange 再导出 → 内容随窗变。
(回归)/customers → 操作列「导出HTML聊天记录」→ 文件名也正确(chat-...html)。
- Step 5: Commit
git add yunkes/src/api/client.ts yunkes/src/views/ChatTimeline.vue
git commit -m "feat(chat): ChatTimeline 导出按钮 + 修复 downloadBlob 文件名丢失(Content-Disposition)"
End with blank line + Co-Authored-By: Claude <noreply@anthropic.com>。
验收(spec §6)
vue-tsc -bexit 0。- 浏览器(本地 smoke.db):ChatTimeline「导出」→ 文件名正确 + 内容随 dateRange;CustomerList/GroupList 导出文件名也修复(回归);network 仅
/api/export/chat-html。
风险
- downloadBlob import 路径:
@/api是否 re-exportdownloadBlob?plan grep 核实;否则从@/api/client。 - 裸 axios 错误:无拦截器包装,调用方 try/catch + ElMessage(已加)。
- filename 正则:后端双引号包裹(export.py 确认);fallback
'chat-export.html'。