286 lines
14 KiB
Markdown
286 lines
14 KiB
Markdown
# task-detail 页面数据来源排查
|
||
|
||
> 排查日期:2026-03-18
|
||
> 页面路径:pages/task-detail/task-detail
|
||
|
||
## 概览
|
||
|
||
| 分类 | 数量 | 说明 |
|
||
|------|------|------|
|
||
| Mock 数据 | 12 个字段 | 来自 `mock-data.ts` 的 `mockTaskDetails` + 页面内联 mock 备注 |
|
||
| 硬编码数据 | 18 个字段/数组 | 维客线索、话术参考、服务记录、服务汇总、手机号、储值等级等 |
|
||
| 已对接 API | 0 个接口 | 当前无任何真实 API 调用 |
|
||
| 前端计算/派生 | 7 个字段 | 关系等级、Banner 背景、AI 配色、时间标签等 |
|
||
| 路由参数 | 1 个 | `id`(任务 ID) |
|
||
| WXML 硬编码文案 | 6 处 | 建议文案、section 标题等 |
|
||
|
||
---
|
||
|
||
## 一、Mock 数据
|
||
|
||
### 1.1 来自 mock-data.ts(`mockTaskDetails`)
|
||
|
||
页面通过 `import { mockTaskDetails } from '../../utils/mock-data'` 引入,在 `loadData()` 中使用 `setTimeout` 模拟异步加载。
|
||
|
||
| 字段 | 类型 | 来源路径 | 联调替换 API |
|
||
|------|------|----------|-------------|
|
||
| `detail`(整体) | `TaskDetail` | `mockTaskDetails.find(t => t.id === id) \|\| mockTaskDetails[0]` | `GET /api/tasks/{id}` |
|
||
| `detail.id` | `string` | TaskDetail.id | 同上 |
|
||
| `detail.customerName` | `string` | TaskDetail.customerName | 同上 |
|
||
| `detail.customerAvatar` | `string` | TaskDetail.customerAvatar | 同上 |
|
||
| `detail.taskType` | `TaskType` | TaskDetail.taskType(`'callback' \| 'priority_recall' \| 'relationship' \| 'high_priority'`) | 同上 |
|
||
| `detail.taskTypeLabel` | `string` | TaskDetail.taskTypeLabel | 同上 |
|
||
| `detail.heartScore` | `number` | TaskDetail.heartScore | 同上 |
|
||
| `detail.status` | `'pending' \| 'completed' \| 'abandoned'` | TaskDetail.status | 同上 |
|
||
| `detail.aiAnalysis.summary` | `string` | TaskDetail.aiAnalysis.summary | `GET /api/tasks/{id}/ai-analysis` |
|
||
| `detail.aiAnalysis.suggestions` | `string[]` | TaskDetail.aiAnalysis.suggestions | 同上 |
|
||
| `detail.hobbies` | `string[]` | TaskDetail.hobbies | `GET /api/tasks/{id}` |
|
||
| `detail.deadline` | `string` | TaskDetail.deadline | 同上 |
|
||
|
||
**mock-data.ts 中 TaskDetail 扩展字段(已定义但页面未直接渲染):**
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `lastVisitDate` | `string?` | 最后到店日期,页面未使用 |
|
||
| `lastSpendAmount` | `number?` | 最后消费金额,页面未使用 |
|
||
| `callbackReason` | `string?` | 回访原因,页面未使用 |
|
||
| `daysAbsent` | `number?` | 缺席天数,页面未使用 |
|
||
| `spendTrend` | `'up' \| 'down' \| 'flat'?` | 消费趋势,页面未使用 |
|
||
| `churnRisk` | `'high' \| 'medium' \| 'low'?` | 流失风险,页面未使用 |
|
||
| `preferences` | `string[]?` | 偏好,页面未使用 |
|
||
| `consumptionHabits` | `string?` | 消费习惯,页面未使用 |
|
||
| `socialPreference` | `string?` | 社交偏好,页面未使用 |
|
||
|
||
### 1.2 页面内联 Mock 备注
|
||
|
||
`loadData()` 方法内部硬编码了 5 条 mock 备注(`mockNotes` 局部变量,L107-L113),覆盖了 `mockTaskDetails` 中的 `notes` 字段:
|
||
|
||
| 字段 | 当前值 | 联调替换 API |
|
||
|------|--------|-------------|
|
||
| `mockNotes[0]` | `'已通过微信联系王先生...'`,score=10 | `GET /api/tasks/{id}/notes` |
|
||
| `mockNotes[1]` | `'王先生最近出差较多...'`,score=7.5 | 同上 |
|
||
| `mockNotes[2]` | `'上次到店时推荐了会员续费活动...'`,score=6 | 同上 |
|
||
| `mockNotes[3]` | `'客户对今天的服务非常满意...'`,score=9.5 | 同上 |
|
||
| `mockNotes[4]` | `'完成高优先召回任务...'`,score=8 | 同上 |
|
||
|
||
---
|
||
|
||
## 二、硬编码数据
|
||
|
||
### 2.1 维客线索(`retentionClues`)
|
||
|
||
**风险:高** — 8 条完整的维客线索数据直接写死在 `data` 对象中(L68-L77),应从 API 获取。
|
||
|
||
| 索引 | tag | text(摘要) | source | 所在行 | 应改为 |
|
||
|------|-----|-------------|--------|--------|--------|
|
||
| 0 | 客户基础 | `'生日 3月15日 · VIP会员 · 注册2年'` | By:系统 | L69 | `GET /api/tasks/{id}/retention-clues` |
|
||
| 1 | 消费习惯 | `'常来夜场 · 月均4-5次'` | By:系统 | L70 | 同上 |
|
||
| 2 | 消费习惯 | `'高客单价'`(含 desc) | By:系统 | L71 | 同上 |
|
||
| 3 | 玩法偏好 | `'偏爱中式八球 · 斯诺克进阶中...'`(含 desc) | By:系统 | L72 | 同上 |
|
||
| 4 | 重要反馈 | `'上次提到想练斯诺克走位'`(含 desc) | By:小燕 | L73 | 同上 |
|
||
| 5 | 社交偏好 | `'喜欢带朋友来玩 · 社交型客户'`(含 desc) | By:系统 | L74 | 同上 |
|
||
| 6 | 消费习惯 | `'酒水消费占比高 · 偏好高端酒水'`(含 desc) | By:系统 | L75 | 同上 |
|
||
| 7 | 重要反馈 | `'上次提到想办生日派对'`(含 desc) | By:Lucy | L76 | 同上 |
|
||
|
||
> 注意:`mock-data.ts` 中已定义 `mockRetentionClues` 和 `RetentionClue` 接口,但页面未使用,而是自行硬编码了不同结构的数据。联调时应统一数据结构。
|
||
|
||
### 2.2 话术参考(`talkingPoints`)
|
||
|
||
**风险:高** — 5 条话术直接写死在 `data` 对象中(L80-L86),应由 AI 生成或从 API 获取。
|
||
|
||
| 索引 | 内容摘要 | 所在行 | 应改为 |
|
||
|------|---------|--------|--------|
|
||
| 0 | `'王哥您好,好久不见!最近店里新到了...'` | L81 | `GET /api/tasks/{id}/talking-points` 或 AI 生成接口 |
|
||
| 1 | `'王哥,最近忙吗?这周末我们有个...'` | L82 | 同上 |
|
||
| 2 | `'王哥好呀,上次您提到想练练...'` | L83 | 同上 |
|
||
| 3 | `'王哥,好久没见您了,您的老位置...'` | L84 | 同上 |
|
||
| 4 | `'王哥您好,我们这个月推出了...'` | L85 | 同上 |
|
||
|
||
### 2.3 近期服务记录(`serviceRecords`)
|
||
|
||
**风险:高** — 4 条服务记录直接写死在 `data` 对象中(L91-L96),应从 API 获取。
|
||
|
||
| 索引 | table | type | income | date | 所在行 | 应改为 |
|
||
|------|-------|------|--------|------|--------|--------|
|
||
| 0 | A12号台 | 基础课 | ¥200 | 2月7日 21:30 | L92 | `GET /api/tasks/{id}/service-records` |
|
||
| 1 | 3号台 | 基础课 | ¥160 | 2月1日 20:30 | L93 | 同上 |
|
||
| 2 | VIP1号房 | 包厢课 | ¥150 | 1月28日 19:00 | L94 | 同上 |
|
||
| 3 | (空) | 充值 | ¥80 | 1月15日 10:00 | L95 | 同上 |
|
||
|
||
### 2.4 服务汇总(`serviceSummary`)
|
||
|
||
**风险:高** — 汇总数据直接写死(L90),应由后端计算或前端根据服务记录列表聚合。
|
||
|
||
| 字段 | 当前值 | 所在行 | 应改为 |
|
||
|------|--------|--------|--------|
|
||
| `totalHours` | `6.0` | L90 | 后端返回或前端聚合 `serviceRecords` |
|
||
| `totalIncome` | `510` | L90 | 同上 |
|
||
| `avgIncome` | `170` | L90 | 同上 |
|
||
|
||
### 2.5 手机号
|
||
|
||
**风险:高** — 手机号硬编码在两处。
|
||
|
||
| 位置 | 当前值 | 所在行 | 应改为 |
|
||
|------|--------|--------|--------|
|
||
| WXML 中 `phone` 显示 | `'138****5678'`(脱敏)/ `'13812345678'`(明文) | wxml L38 | 从 `detail` 对象获取,API 返回脱敏版 |
|
||
| `onCopyPhone()` 方法 | `'13812345678'` | ts L175 | 调用 API 获取明文手机号 `GET /api/customers/{id}/phone` |
|
||
|
||
### 2.6 储值等级
|
||
|
||
**风险:中** — 直接写死在 `data` 中。
|
||
|
||
| 字段 | 当前值 | 所在行 | 应改为 |
|
||
|------|--------|--------|--------|
|
||
| `storageLevel` | `'非常多'` | ts L101 | 从 `detail` 或客户信息 API 获取 |
|
||
|
||
### 2.7 关系等级初始值
|
||
|
||
**风险:低** — 初始值会被 `updateRelationshipDisplay()` 覆盖,但仍属于硬编码。
|
||
|
||
| 字段 | 当前值 | 所在行 | 说明 |
|
||
|------|--------|--------|------|
|
||
| `relationLevel` | `'excellent'` | ts L104 | 初始值,`loadData` 后被覆盖 |
|
||
| `relationLevelText` | `'很好'` | ts L105 | 同上 |
|
||
| `relationColor` | `'#e91e63'` | ts L106 | 同上 |
|
||
|
||
### 2.8 任务建议文案
|
||
|
||
**风险:中** — WXML 中硬编码了建议引导文案。
|
||
|
||
| 位置 | 当前值 | 所在行 | 应改为 |
|
||
|------|--------|--------|--------|
|
||
| `suggestion-intro` | `'该客户已有 15 天未到店,存在流失风险。建议通过微信联系:'` | wxml L82 | 从 `detail.aiAnalysis` 获取或后端生成 |
|
||
|
||
---
|
||
|
||
## 三、已对接 API
|
||
|
||
**当前无任何真实 API 调用。**
|
||
|
||
`loadData()` 使用 `setTimeout` + `mockTaskDetails` 模拟异步请求(L128-L155)。所有数据操作(放弃任务、保存备注、删除备注)均为本地 `setData` 操作,未与后端通信。
|
||
|
||
涉及的伪操作:
|
||
|
||
| 操作 | 当前实现 | 需对接 API |
|
||
|------|---------|-----------|
|
||
| 加载任务详情 | `mockTaskDetails.find()` | `GET /api/tasks/{id}` |
|
||
| 放弃任务 | `setData({ 'detail.status': 'abandoned' })` | `POST /api/tasks/{id}/abandon` |
|
||
| 取消放弃 | `setData({ 'detail.status': 'pending' })` | `POST /api/tasks/{id}/cancel-abandon` |
|
||
| 保存备注 | `setData({ sortedNotes: [...] })` | `POST /api/tasks/{id}/notes` |
|
||
| 删除备注 | `filter` 本地数组 | `DELETE /api/tasks/{id}/notes/{noteId}` |
|
||
|
||
---
|
||
|
||
## 四、前端计算/派生数据
|
||
|
||
| 字段 | 计算逻辑 | 所在方法 | 依赖数据 |
|
||
|------|---------|---------|---------|
|
||
| `relationLevel` | `heartScore` 分段映射:>8.5→excellent, ≥6→good, ≥3.5→normal, <3.5→poor | `updateRelationshipDisplay()` L157 | `detail.heartScore` |
|
||
| `relationLevelText` | 同上分段映射为中文:很好/良好/一般/待发展 | 同上 | 同上 |
|
||
| `relationColor` | 同上分段映射为颜色值 | 同上 | 同上 |
|
||
| `bannerBgSvg` | 根据 `detail.taskType` 映射 SVG 路径 | `loadData()` L133-L142 | `detail.taskType` |
|
||
| `sortedNotes` | `mockNotes` → 附加 `timeLabel`(`formatRelativeTime`)→ `sortByTimestamp` 排序 | `loadData()` L116-L118 | mock 备注数据 |
|
||
| `aiColor` | 随机从 6 色中选取 | `onLoad()` L124 | 无(随机) |
|
||
| `copiedIndex` | 复制话术后设为当前索引,2 秒后重置为 -1 | `onCopySpeech()` L189 | 用户交互 |
|
||
| `pageState` | 加载状态机:loading → normal/empty/error | `loadData()` | 数据加载结果 |
|
||
| `phoneVisible` | 手机号显示/隐藏切换 | `onTogglePhone()` L173 | 用户交互 |
|
||
| `noteModalVisible` | 备注弹窗显隐 | `onAddNote()`/`onNoteConfirm()`/`onNoteCancel()` | 用户交互 |
|
||
| `abandonModalVisible` | 放弃弹窗显隐 | `onAbandon()`/`onAbandonConfirm()`/`onAbandonCancel()` | 用户交互 |
|
||
| `formatServiceDate()` 返回值 | ISO 日期 → `M月D日 HH:mm` 中文短格式 | 页面顶部函数 L55 | 服务记录日期字符串 |
|
||
|
||
---
|
||
|
||
## 五、路由参数
|
||
|
||
| 参数 | 来源 | 用途 | 所在行 |
|
||
|------|------|------|--------|
|
||
| `options.id` | `onLoad(options)` | 任务 ID,用于查找 mock 数据;缺失时回退空字符串 | ts L123 |
|
||
|
||
---
|
||
|
||
## 六、WXML 中的硬编码文案
|
||
|
||
| 文案 | 位置 | 类型 | 说明 |
|
||
|------|------|------|------|
|
||
| `'该客户已有 15 天未到店,存在流失风险。建议通过微信联系:'` | wxml L82 | 业务数据 | 应从 AI 分析结果获取 |
|
||
| `'💡 建议执行'` | wxml L79 | UI 文案 | 可保留 |
|
||
| `'💬 话术参考'` | wxml L89 | UI 文案 | 可保留 |
|
||
| `'60天内服务记录'` | wxml L121 | UI 文案 | "60天"为业务规则,可保留但需确认是否动态 |
|
||
| `'未找到任务信息'` / `'加载失败'` / `'加载中...'` | wxml L7-L15 | 状态文案 | 可保留 |
|
||
| `'暂无备注'` | wxml L117 | 空态文案 | 可保留 |
|
||
|
||
---
|
||
|
||
## 七、引用的工具函数
|
||
|
||
| 函数 | 来源文件 | 用途 |
|
||
|------|---------|------|
|
||
| `mockTaskDetails` | `utils/mock-data.ts` | Mock 任务详情数据 |
|
||
| `TaskDetail`, `Note` | `utils/mock-data.ts` | 类型定义 |
|
||
| `sortByTimestamp` | `utils/sort.ts` | 备注按时间降序排序 |
|
||
| `formatRelativeTime` | `utils/time.ts` | 备注时间 → 相对时间文案 |
|
||
| `formatMoney` | `utils/money.ts` | 金额格式化(已 import 但页面 TS 中未直接调用,WXML 使用 WXS 版) |
|
||
| `fmt.toFixed` | `utils/format.wxs` | WXML 中数字保留小数 |
|
||
| `fmt.money` | `utils/format.wxs` | WXML 中金额格式化 |
|
||
| `fmt.hours` | `utils/format.wxs` | WXML 中课时格式化 |
|
||
|
||
---
|
||
|
||
## 八、引用的自定义组件
|
||
|
||
| 组件 | 路径 | 用途 |
|
||
|------|------|------|
|
||
| `note-modal` | `/components/note-modal/note-modal` | 备注弹窗 |
|
||
| `abandon-modal` | `/components/abandon-modal/abandon-modal` | 放弃任务弹窗 |
|
||
| `star-rating` | `/components/star-rating/star-rating` | 星级评分展示 |
|
||
| `heart-icon` | `/components/heart-icon/heart-icon` | 爱心图标(关系等级) |
|
||
| `ai-inline-icon` | `/components/ai-inline-icon/ai-inline-icon` | AI 内联图标 |
|
||
| `ai-title-badge` | `/components/ai-title-badge/ai-title-badge` | AI 标题徽章 |
|
||
| `clue-card` | `/components/clue-card/clue-card` | 维客线索卡片 |
|
||
| `service-record-card` | `/components/service-record-card/service-record-card` | 服务记录卡片 |
|
||
| `dev-fab` | `/components/dev-fab/dev-fab` | 开发调试浮动按钮(JSON 注册但 WXML 未使用) |
|
||
|
||
---
|
||
|
||
## 九、调试面板数据
|
||
|
||
页面包含一个调试面板(`showDebugPanel`),用于开发阶段切换任务类型和关系数值。联调前应移除或隐藏。
|
||
|
||
| 字段 | 用途 | 所在行 |
|
||
|------|------|--------|
|
||
| `showDebugPanel` | 调试面板显隐 | ts L110 |
|
||
| `debugTaskType` | 调试用任务类型 | ts L111 |
|
||
| `debugHeartScore` | 调试用关系数值 | ts L112 |
|
||
| `debugShowExpandBtn` | 调试用展开按钮开关 | ts L113 |
|
||
|
||
---
|
||
|
||
## 十、联调 TODO
|
||
|
||
### 高优先级(数据完全依赖 Mock/硬编码)
|
||
|
||
- [ ] 替换 `mockTaskDetails` 为 `GET /api/tasks/{id}` 真实 API 调用
|
||
- [ ] 替换页面内联 mock 备注为 `GET /api/tasks/{id}/notes` API
|
||
- [ ] 替换硬编码 `retentionClues`(8 条维客线索)为 `GET /api/tasks/{id}/retention-clues` API
|
||
- [ ] 替换硬编码 `talkingPoints`(5 条话术)为 `GET /api/tasks/{id}/talking-points` 或 AI 生成接口
|
||
- [ ] 替换硬编码 `serviceRecords`(4 条服务记录)为 `GET /api/tasks/{id}/service-records?days=60` API
|
||
- [ ] 替换硬编码 `serviceSummary` 为后端返回或前端聚合计算
|
||
- [ ] 替换硬编码手机号 `'13812345678'` / `'138****5678'` 为 API 返回的客户手机号
|
||
- [ ] 替换硬编码 `storageLevel: '非常多'` 为客户信息 API 返回值
|
||
|
||
### 中优先级(操作类接口)
|
||
|
||
- [ ] `onAbandonConfirm` 对接 `POST /api/tasks/{id}/abandon`
|
||
- [ ] `cancelAbandon` 对接 `POST /api/tasks/{id}/cancel-abandon`
|
||
- [ ] `onNoteConfirm` 对接 `POST /api/tasks/{id}/notes`
|
||
- [ ] `onDeleteNote` 对接 `DELETE /api/tasks/{id}/notes/{noteId}`
|
||
|
||
### 低优先级(优化项)
|
||
|
||
- [ ] WXML 中 `suggestion-intro` 硬编码文案改为从 AI 分析结果动态获取
|
||
- [ ] 移除或条件隐藏调试面板(`showDebugPanel` 相关代码)
|
||
- [ ] 统一维客线索数据结构:页面硬编码的 `RetentionClue` 接口与 `mock-data.ts` 中定义的不一致
|
||
- [ ] `formatMoney` 已 import 但 TS 中未使用(WXML 用 WXS 版),可清理无用 import
|
||
- [ ] `dev-fab` 组件已在 JSON 注册但 WXML 未引用,可清理
|