Files
Neo-ZQYY/docs/miniprogram-dev/api-audit/board-customer.md

14 KiB
Raw Blame History

board-customer 页面数据来源排查

排查日期2026-03-18 页面路径:pages/board-customer/board-customer 引用文件:board-customer.ts / .wxml / .wxss / .json 外部依赖:utils/ai-color-manager.tsinitPageAiColor,已导入但未调用)

概览

分类 数量 说明
A. Mock 数据 1 组3 条记录,~30 个字段/条) 页面内联 MOCK_CUSTOMERS 常量,未引用 utils/mock-data.ts
B. 硬编码数据 5 处 维度选项、项目选项、Tab 列表、列表标题、加载延迟
C. 已对接 API 0 无任何 wx.request / API 调用
D. 前端计算/派生 3 处 dimType 映射、滚动隐藏筛选栏、空/错误态判断
E. 路由参数 0 onLoad 无参数读取
F. WXML 硬编码文案 12 处 Tab 文案、列表标题、网格标签、状态提示等

一、Mock 数据

1.1 MOCK_CUSTOMERS(页面内联,第 72178 行)

页面顶部定义的 const MOCK_CUSTOMERS: CustomerItem[],包含 3 条客户记录(王先生 / 李女士 / 张先生),在 loadData() 中通过 setTimeout(400ms) 模拟异步加载。

未引用 utils/mock-data.ts:该文件中有 mockCustomers: CustomerCard[],但 board-customer 页面使用了自己定义的 CustomerItem 接口(字段更丰富,支持 8 维度卡片)。

字段清单(每条 CustomerItem

字段 示例值(王先生) 数据类型 联调时对应 API 字段
id 'u1' string 客户 IDmember_id
name '王先生' string 客户姓名(dim_member.nickname,注意 DQ-6 断档)
initial '王' string 前端从 name 截取首字
avatarCls 'avatar--amber' string 前端根据规则分配头像色
idealDays 7 number 理想到店间隔天数DWS 计算)
elapsedDays 15 number 距上次到店已过天数DWS 计算)
overdueDays 8 number 超期天数 = elapsedDays - idealDays
visits30d 3 number 近 30 天到店次数
balance '¥2,680' string 当前余额(储值卡)
recallIndex '9.2' string 召回指数DWS 综合评分)
potentialTags [{text:'高频',theme:'primary'}] array 消费潜力标签(后端计算)
spend30d '¥4,200' string 近 30 天消费金额
avgVisits '6.2次' string 月均到店次数
avgSpend '¥680' string 次均消费金额
lastVisit '3天前' string 最近到店(相对时间)
monthlyConsume '¥3,500' string 月均消耗金额
availableMonths '约0.8个月' string 余额可用月数 = balance / monthlyConsume
lastRecharge '2月15日' string 最后充值日期
rechargeAmount '¥5,000' string 最后充值金额
recharges60d '2次' string 近 60 天充值次数
currentBalance '¥2,680' string 当前余额(与 balance 重复)
spend60d '¥8,400' string 近 60 天消费金额
visits60d '12' string 近 60 天到店次数
highSpendTag true boolean 是否高消费标签
avgInterval '5.0天' string 平均到店间隔
intimacy '92' string 专一度指数
topCoachName '小燕' string 最亲密助教姓名
topCoachHeart 9.2 number 最亲密助教关系指数
topCoachScore '9.2' string 同上(字符串版)
coachDetails 2 条明细 array 助教服务明细表
weeklyVisits 8 周数据 array 8 周到店频率(迷你柱状图)
coachName '小燕' string 主助教姓名
coachRatio '78%' string 主助教服务占比
visitFreq '6.2次/月' string 到店频率
daysAgo 3 number 距上次到店天数
assistants 2 条 array 助教列表(底部行)

coachDetails 子字段

字段 示例值 说明
name '小燕' 助教姓名
cls 'assistant--assignee' 样式类(跟/弃/普通)
heartScore 9.2 关系指数
badge '跟' 跟/弃标签
avgDuration '2.3h' 次均服务时长
serviceCount '14' 服务次数
coachSpend '¥4,200' 助教消费金额
relationIdx 9.2 关系指数(数值版)

assistants 子字段

字段 示例值 说明
name '小燕' 助教姓名
cls 'assistant--assignee' 样式类
heartScore 9.2 关系指数
badge '跟' 跟/弃标签(可选)
badgeCls 'assistant-badge--follow' badge 样式类(可选)

二、硬编码数据

2.1 DIMENSION_OPTIONS(维度下拉选项)

风险 字段 当前值 应从何处获取
🟡 DIMENSION_OPTIONS 8 个维度选项recall/potential/balance/recharge/recent/spend60/freq60/loyal 前端枚举,可保留硬编码;若后端支持动态维度则需 API
const DIMENSION_OPTIONS = [
  { value: 'recall', text: '最应召回' },
  { value: 'potential', text: '最大消费潜力' },
  { value: 'balance', text: '最高余额' },
  { value: 'recharge', text: '最近充值' },
  { value: 'recent', text: '最近到店' },
  { value: 'spend60', text: '最高消费 近60天' },
  { value: 'freq60', text: '最频繁 近60天' },
  { value: 'loyal', text: '最专一 近60天' },
]

2.2 PROJECT_OPTIONS(项目筛选选项)

风险 字段 当前值 应从何处获取
🟡 PROJECT_OPTIONS 5 个项目选项all/chinese/snooker/mahjong/karaoke 前端枚举,可保留;若门店项目可配置则需 API
const PROJECT_OPTIONS = [
  { value: 'all', text: '全部' },
  { value: 'chinese', text: '🎱 中式/追分' },
  { value: 'snooker', text: '斯诺克' },
  { value: 'mahjong', text: '🀄 麻将/棋牌' },
  { value: 'karaoke', text: '🎤 团建/K歌' },
]

2.3 DIMENSION_TO_DIM 映射表

风险 字段 说明
🟢 DIMENSION_TO_DIM 纯前端映射,维度 value → DimType无需替换

2.4 loadData() 中的 setTimeout(400ms)

风险 位置 当前值 说明
🔴 loadData() setTimeout(() => {...}, 400) 模拟网络延迟,联调时必须替换为真实 wx.request

2.5 onPullDownRefresh() 中的 setTimeout(500ms)

风险 位置 当前值 说明
🟡 onPullDownRefresh() setTimeout(() => wx.stopPullDownRefresh(), 500) 联调时应在 API 回调中调用 wx.stopPullDownRefresh()

三、已对接 API

无。 页面中没有任何 wx.requestwx.cloud.callFunction 或封装的 API 调用。

loadData() 当前实现:

loadData() {
  this.setData({ pageState: 'loading' })
  setTimeout(() => {
    const data = MOCK_CUSTOMERS  // ← 直接引用内联 mock
    if (!data || data.length === 0) {
      this.setData({ pageState: 'empty' })
      return
    }
    this.setData({
      allCustomers: data,
      customers: data,
      totalCount: data.length,
      pageState: 'normal',
    })
  }, 400)
}

四、前端计算/派生数据

字段/逻辑 位置 说明
dimType onDimensionChange() DIMENSION_TO_DIM[selectedDimension] 映射得出,控制 WXML 卡片模板切换
filterBarVisible onPageScroll() 滚动方向 + 累积距离判断,纯 UI 交互逻辑
pageState loadData() 根据数据加载结果设置 'loading' / 'empty' / 'normal' / 'error'
totalCount loadData() data.length,由返回数据长度计算
overdueDays > 7 WXML 控制超期标签颜色danger / warn纯前端判断
item.highSpendTag WXML 控制是否显示"高消费"标签,值来自 mock 数据
柱状图 opacity WXML 0.2 + wIdx * 0.057,渐变透明度,纯 UI 计算
cd.relationIdx >= 8 WXML 控制关系指数颜色primary / gray纯前端判断

五、路由参数

无。 onLoad() 不接收任何参数,直接调用 loadData()

页面作为导航目标时的入口:

  • board-finance.tswx.navigateTo({ url: '/pages/board-customer/board-customer' })
  • board-coach.tswx.navigateTo({ url: '/pages/board-customer/board-customer' })
  • dev-tools.ts → 开发工具页面列表

六、WXML 中的硬编码文案

位置 硬编码文案 风险 说明
加载态 加载中... 🟢 通用 UI 文案
空状态 暂无客户数据 🟢 通用 UI 文案
错误态 加载失败 🟢 通用 UI 文案
错误态按钮 点击重试 🟢 通用 UI 文案
Tab 栏 财务 / 客户 / 助教 🟢 固定导航文案
列表标题 客户列表 🟢 固定标题
列表副标题 · 前100名 🟡 若后端支持分页/动态 TopN需改为动态值
列表计数 共{{totalCount}}名客户 🟢 动态绑定
召回维度标签 理想 / 已过 / 超期 / 🟢 固定维度标签
网格标签 30天消费 / 月均到店 / 余额 / 次均消费 / 月均消耗 / 可用 / 最后充值 / 充值 / 60天充值 / 当前余额 / 近60天消费 / 到店次数 🟢 固定维度标签
频率维度 60天到店 / / 8周前 / 本周 🟢 固定维度标签
专一维度表头 助教 / 次均时长 / 服务次数 / 助教消费 / 关系指数 🟢 固定表头
助教标签 助教: / / 🟢 固定 UI 文案
最近到店 天前到店 / 理想间隔 / 60天到店 / 次均消费 🟢 固定维度标签
频率中间行 平均间隔 / 60天消费 🟢 固定维度标签
召回中间行 30天到店 / 余额 / 召回指数 🟢 固定维度标签
最近到店右上角 天前到店 🟢 固定维度标签

七、引用组件清单

组件 路径 数据来源 说明
filter-dropdown /components/filter-dropdown/ 父组件传入 options / value 纯展示组件,无自有数据
heart-icon /components/heart-icon/ 父组件传入 score 纯展示组件,根据分数渲染心形图标
ai-float-button /components/ai-float-button/ 自有逻辑 AI 悬浮按钮,独立组件
board-tab-bar /custom-tab-bar/index active="board" 底部导航栏,当前 mock 3 按钮
dev-fab /components/dev-fab/ 开发调试按钮,wx:if="{{false}}" 已隐藏
t-loading / t-empty / t-icon / t-tag TDesign 第三方 UI 组件

八、联调 TODO

🔴 P0 — 必须替换

# 任务 当前状态 目标
1 替换 MOCK_CUSTOMERS 为 API 调用 页面内联 3 条 mock 数据 调用后端客户看板 API传入 dimension + project 参数
2 实现 loadData() 真实请求 setTimeout(400ms) 模拟 wx.request → 后端 API处理 loading/error/empty 三态
3 下拉刷新对接 setTimeout(500ms) 后停止 API 回调中 wx.stopPullDownRefresh()
4 维度切换重新请求 onDimensionChange 仅切换 dimType 切换维度后重新调用 API不同维度返回不同排序/字段)
5 项目筛选重新请求 onProjectChange 仅更新 selectedProject 切换项目后重新调用 API按项目类型过滤

🟡 P1 — 建议优化

# 任务 说明
6 分页/滚动加载 当前一次性加载全部,"前 100 名"硬编码;若数据量大需分页
7 initPageAiColor 调用 已导入但未在 onLoad 中调用,需补充
8 金额字段格式化 Mock 中金额为预格式化字符串('¥2,680'),联调后需前端格式化
9 相对时间计算 lastVisit: '3天前'daysAgo: 3 等,联调后需从时间戳计算
10 balancecurrentBalance 去重 Mock 中两字段值相同API 设计时应统一

🟢 P2 — 可保留

# 项目 说明
11 DIMENSION_OPTIONS 前端枚举,维度列表固定,可保留硬编码
12 PROJECT_OPTIONS 前端枚举,若门店项目固定可保留
13 DIMENSION_TO_DIM 映射 纯前端逻辑,保留
14 WXML 固定文案 维度标签、表头等,保留

九、待确认 API 设计

联调前需与后端确认以下接口设计:

GET /api/v1/board/customers
  Query:
    dimension: string    — 排序维度recall/potential/balance/...
    project: string      — 项目筛选all/chinese/snooker/...
    limit: number        — 返回条数(默认 100
    site_id: string      — 门店 ID从登录态获取
  Response:
    total: number
    items: CustomerItem[]

关键字段口径注意

  • 金额字段(balancespend30dspend60d 等):后端返回 numeric(2) 原始数值,前端负责 ¥ 前缀 + 千分位格式化
  • 会员姓名:通过 member_id JOIN dim_member.nicknameDQ-6 断档,禁止直接用 member_name
  • 储值卡余额DWS 层 balance_pay(总额)= recharge_card_pay + gift_card_pay
  • 消费金额:使用 items_sum 口径,禁止直接使用 consume_money(三种历史口径混合)