feat: 统计分析(卡片/漏斗/趋势/分布/推送健康,ECharts)

- admin 统计分析页(views/stats/overview): 指标卡四张(口径文案沿用
  仪表盘)/ 漏斗横向条形(有序单色明度阶梯,标注 数量·占上一级%)/
  按日趋势双线(窗口内无数据日期前端补零)/ 意向环形(分类三槽固定
  指派,色随实体不随筛选)/ 来源·站点·国家 Top10 横向条形(名义类目
  统一单色)/ 推送健康表(四状态计数 + duplicated 子集行)
- 筛选一行作用全部图表: 时间档(今天/近7天/近30天/自定义区间,自定义
  不完整不发请求并提示)+ 租户切换(超管含"全部"合并选项,停用租户
  标注;租户管理员固定自己)
- charts.ts 纯函数层(选项构建 + 双模式主题档),配色经
  validate_palette.js 明暗双模式校验(分类/漏斗 ordinal 全过;
  意向饼语义红绿组 CVD 硬伤弃用改分类槽)
- statsRange.ts 扩展自定义区间解析(既有仪表盘导出不动);
  新增 charts/resolveStatsRange 单测 19 例(vitest 120→139)
- 修复 merge_stats distributions 尾随逗号 1 元组 bug(合并端点曾把
  distributions 序列化成 JSON 数组,单租户路径不受累)+ 回归钉子;
  后端 595 测试全过

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-10-13 22:33:55 +08:00
parent 481243b958
commit 230eb2d86c
7 changed files with 1193 additions and 11 deletions
+29
View File
@@ -39,3 +39,32 @@ export function rangeToParams(
to: toDateKey(now)
}
}
// ---- 自定义区间扩展(Phase 3 Task 8 统计分析页) ----------------------------------
/** 时间筛选状态:快捷档 + 自定义(统计分析页;仪表盘只用快捷档) */
export type StatsRangeFilter = StatsQuickRange | 'custom'
/** 筛选档位清单(顺序即切换器展示序;自定义档配合日期区间选择器) */
export const STATS_RANGE_OPTIONS: Array<{ value: StatsRangeFilter; label: string }> = [
...STATS_QUICK_RANGES.map(({ value, label }) => ({ value, label })),
{ value: 'custom', label: '自定义' }
]
/**
* 筛选状态 → {from, to} 查询参数(custom 需完整且起≤止的区间)
*
* 返回 null = 区间不完整/非法(自定义只选了一半、起>止)——调用方不发请求,
* 等 ElDatePicker 补全;快速档永不为 null。
*/
export function resolveStatsRange(
filter: StatsRangeFilter,
custom: [string, string] | null | undefined,
now: Date = new Date()
): { from: string; to: string } | null {
if (filter !== 'custom') return rangeToParams(filter, now)
if (!custom || !custom[0] || !custom[1]) return null
const [from, to] = custom
if (from > to) return null // ISO 日期串字典序即时间序
return { from, to }
}