# 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 未引用,可清理