dashboard-builder v4.3
从Excel/CSV数据文件生成单文件交互式HTML看板,支持全局多维筛选 + 手动数据刷新按钮。唯一外部依赖:Chart.js 4.x CDN。
执行流程
Phase 1: 数据读取与分类
- 读取用户提供的数据文件(Excel/CSV)
- 自动分类所有列:
- 日期列 → 含日期/时间信息,作为趋势分析时间轴
- 数值列 → 含数值,候选核心指标
- 分类列 → 含有限离散值,候选维度
- 向用户展示分类结果并确认
Phase 2: 指标体系构建
核心指标(3-5个):从数值列选取最具业务含义的原始聚合值。按日期聚合(求和/平均/计数)。
- 销售场景:销售额、订单量、利润、客户数
- 财务场景:收入、成本、毛利、净利润
- HR场景:在职人数、入职数、离职数、薪酬总额
计算指标(5-8个):基于核心指标派生,必须覆盖以下类别:
- 环比增长率:(当期-上期)/上期(每个核心指标配一个环比)
- 同比增长率:需多年数据,否则跳过
- 比率指标:利润率、客单价、人效等(根据业务场景推断2-3个)
维度(2-4个):从分类列中选取
- 优先选择值域≤15的分类列,排除ID/编号等无意义列
漏斗链路:给出最全的漏斗转化链路
Phase 3: 参数确认
在开始分析前,快速向用户确认以下参数
- 核心指标 — 给出选项让用户自己选择确认
- 计算指标 — 给出选项让用户自己选择确认
- 分析维度 — 给出选项让用户自己选择确认
- 漏斗链路 — 给出选项让用户自己选择确认
- 全局筛选维度 — 从分析维度中选择尽量全面的维度作为顶部全局筛选器;这些筛选器控制整个看板的数据范围,选择"全部"时该维度不筛选
- 数据刷新 — 确认数据源路径(原始Excel/CSV文件的绝对路径,用于生成伴生
data.json)
Phase 4: HTML生成
生成两个交付物:
dashboard.html— 主看板文件,内嵌全部CSS/JS,初始数据以JS对象嵌入dashboard_data.json— 伴生数据文件,包含完整DATA对象(raw/daily/city/category/funnel/dimValues/generatedAt),供HTML刷新使用
⚠️ DATA嵌入铁律:先用Python json.dumps(data, ensure_ascii=False, separators=(',', ':')) 生成紧凑JSON字符串,再整体替换HTML中 const DATA = ...; 块。禁止手动逐行拼接JSON——多行嵌套对象极易出现括号不匹配(如daily对象后多余})导致JS语法错误。
⚠️ data.json 生成铁律:同样用 json.dumps(data, ensure_ascii=False) 生成,写入独立文件。data 对象必须包含 generatedAt 字段(ISO 8601时间戳,如 "2026-06-30T09:35:48"),供HTML端判断数据是否有更新。
⚠️ 全局筛选数据要求:DATA.raw 必须嵌入所有原始明细行(对象数组),供全局筛选后重新聚合。DATA.dimValues 存储各筛选维度的distinct值列表(如 { city: [...], category: [...] })。DATA.daily/DATA.city/DATA.category/DATA.funnel 在Python阶段基于全量数据预聚合,同时作为初始状态(全选"全部"时的展示数据);全局筛选变更后,由前端JS基于 DATA.raw 重新聚合生成 WORKING_DATA。
布局结构
header.global-filter-bar(44px高,全宽,粘性顶部)
│ └── 筛选器组(左)+ 刷新按钮组(右)
└── body → flex布局
├── aside.sidebar(280px,可折叠)
│ ├── 配色方案 → 13套色块选择器(点击切换)
│ ├── 核心指标 → 复选框列表(默认全选)
│ ├── 计算指标 → 复选框列表(默认全选)
│ └── 重点维度 → 复选框列表(默认全选)
└── main.dashboard → 12板块纵向排列
全局筛选栏
位于看板最顶部(body之上),全宽横向排列,粘性定位(position: sticky; top: 0; z-index: 100),包含各维度的下拉筛选器。左右分区:左侧为筛选器组,右侧为刷新控件组。
结构:
- 每个筛选维度一个
<select>下拉框,选项 =["全部"]+ 该维度在DATA.dimValues中的所有distinct值 - 所有筛选器横向排列,
gap: 12px - 筛选器右侧放置"重置"按钮(点击恢复所有筛选为"全部")
- 刷新按钮组(右对齐):手动刷新按钮 + 「恢复默认」按钮 + 刷新状态指示灯
- 筛选器标签(维度名)前置,与下拉框同行展示
样式:
- 高度44px,背景色 =
var(--bg-primary),底部1px solid var(--bg-card)分隔线 - 下拉框暗色主题风格:
bg = var(--bg-card),color = var(--text-primary),border: 1px solid rgba(255,255,255,0.1),border-radius: 4px padding: 8px 16px,字体 Calibri 11pt- 筛选标签:字体 Calibri 10pt,
color = var(--text-secondary),margin-right: 6px - "重置"按钮:
bg = transparent,border: 1px solid var(--accent-1),color = var(--accent-1),border-radius: 4px,cursor: pointer, hover时bg = var(--accent-1),color = #fff - "刷新"按钮:与"重置"按钮同一套样式(
border: 1px solid var(--accent-2),color = var(--accent-2)),hover时bg = var(--accent-2),color = #fff;按钮内显示刷新图标文字("↻ 刷新数据"),刷新进行中时显示旋转动画 + "刷新中…" - 刷新状态指示灯:8px圆点,位于刷新按钮左侧。绿色(
var(--accent-2))= 数据最新/空闲;黄色(#F39C12)= 刷新进行中;红色(var(--accent-3))= 上次刷新失败 - 「恢复默认」按钮:位于刷新按钮右侧,初始
display: none。样式:border: 1px solid var(--accent-3); color: var(--accent-3),hover 时bg = var(--accent-3); color = #fff。点击后清除 localStorage 并location.reload() - 筛选器组与刷新按钮组之间用
margin-left: auto分隔,确保刷新控件固定在右侧
行为:
- 任何维度筛选变更 → 即时重新计算全看板数据 → 刷新所有12板块(无需Apply按钮)
- 多维度筛选:AND逻辑(同时满足所有选定筛选条件)
- 选择"全部" → 该维度不过滤(等效于移除该维度筛选)
- 刷新流程:
filterRawData(filters)→reaggregateAll(filteredRows)→refreshAllPanels(workingData) reaggregateAll()重新生成WORKING_DATA.daily、WORKING_DATA.city、WORKING_DATA.category、WORKING_DATA.funnelrefreshAllPanels()更新所有卡片数值 + destroyAllCharts + recreateAllCharts + 刷新表格 + 重写结论文本
数据刷新机制(v4.2 更新:协议感知 + File System Access API)
看板仅支持手动刷新,initRefresh() 根据浏览器协议和能力自动选择最佳方式:
| 协议 | 浏览器 | 刷新方式 | 说明 |
|---|---|---|---|
| file:// | Chrome/Edge 86+ | File System Access API | showOpenFilePicker() 获取句柄存 IndexedDB,后续直接读取无需重复选择 |
| file:// | Safari/Firefox | <input type="file"> | 每次需重新选择文件 |
| http:// | 任意 | fetch('/api/data') | 比较 generatedAt 时间戳,相同则跳过;失败则提示 |
核心流程:用户点「↻ 刷新数据」→ 状态灯变黄 → 按协议读取数据 → applyNewData(fresh):
applyNewData():
DATA = fresh(let 声明,允许重新赋值)
buildFilters() → resetFilters()
全量刷新所有12板块(卡片+图表+表格+结论)
状态灯 → 绿色
显示「恢复默认」按钮(display: inline-block)
保存到 localStorage(key: 'dashboard_data',value: JSON.stringify(DATA))
文件解析(FSA / 文件上传共用):parseExcelFile(file) 用 SheetJS (XLSX.read) 解析 Excel → 标准化行数据(日期→YYYY-MM-DD)→ 构建 {raw, daily, city, category, funnel, dimValues, generatedAt} → 调用 applyNewData()。
可选服务器:dashboard_server.py 提供 GET /api/data 端点,实时 pd.read_excel() 返回最新数据。启动方式:python dashboard_server.py 9090。
localStorage 数据持久化(v4.3 新增)
上传文件后的数据在浏览器内存中生效,但刷新页面会丢失(HTML 中的内嵌数据不可自修改)。通过 localStorage 持久化解决:
applyNewData() 中:聚合 + 刷新完成后,执行 localStorage.setItem('dashboard_data', JSON.stringify(DATA)) 保存完整 DATA 对象。
init() 初始化时:页面加载后先检查 localStorage:
var saved = null;
try { saved = localStorage.getItem('dashboard_data'); } catch(e) {}
if (saved) {
try {
var parsed = JSON.parse(saved);
if (parsed && parsed.raw && Array.isArray(parsed.raw) && parsed.raw.length > 0) {
DATA = parsed;
document.getElementById('restoreDefault').style.display = 'inline-block';
}
} catch(e) { console.warn('localStorage data corrupt, using embedded data'); }
}
有持久化数据时用持久化数据,没有则用内嵌初始数据。
「恢复默认」按钮:位于刷新按钮右侧,初始 display: none,localStorage 有数据时显示。样式:border: 1px solid var(--accent-3); color: var(--accent-3)(红色边框,与重置/刷新区分)。点击后:
function restoreDefaultData() {
try { localStorage.removeItem('dashboard_data'); } catch(e) {}
location.reload();
}
13大板块
基于用户确认的核心指标、计算指标、分析维度,按照如下方式展示。所有板块均受全局筛选控制:
| # | 板块 | 图表类型 | 交互方式 | 全局筛选响应 | 数据刷新响应 | |---|------|----------|----------|-------------|-------------| | 1 | 核心指标卡片 | 数值卡片(32px高)+环比色标 | 左侧勾选切换显隐 | 数值基于筛选后数据重新计算 | 数值基于新DATA重新计算 | | 2 | 计算指标卡片 | 数值卡片(32px高)+环比色标 | 左侧勾选切换显隐 | 数值基于筛选后数据重新计算 | 数值基于新DATA重新计算 | | 3 | 核心指标趋势 | 折线图(渐变填充) | 下拉框选指标 | 折线数据基于筛选后daily重绘 | 折线数据基于新DATA.daily重绘 | | 4 | 比率指标趋势 | 折线图(渐变填充) | 下拉框选指标 | 折线数据基于筛选后daily重绘 | 折线数据基于新DATA.daily重绘 | | 5 | 同环比指标趋势 | 柱形图(正向绿色,负向红色) | 下拉框选指标 | 折线数据基于筛选后daily重绘 | 折线数据基于新DATA.daily重绘 | | 6 | 核心指标分维度 | 柱状图(渐变填充) | 2个下拉框分别选核心指标、分析维度 | 柱状图数据基于筛选后维度聚合重绘 | 柱状图数据基于新DATA维度聚合重绘 | | 7 | 计算指标分维度 | 柱状图(渐变填充) | 2个下拉框分别选核心指标、分析维度 | 柱状图数据基于筛选后维度聚合重绘 | 柱状图数据基于新DATA维度聚合重绘 | | 8 | 漏斗分析 | 横向递减柱状图 | 无漏斗数据→留空+提示文字 | 各阶段计数基于筛选后数据重算 | 各阶段计数基于新DATA.funnel重算 | | 9 | 漏斗分析 | 各步骤转化的格式化表格(各步数量级、转化率) | 无漏斗数据→留空+提示文字 | 各阶段计数基于筛选后数据重算 | 各阶段计数基于新DATA.funnel重算 | | 10 | 城市排名 | 条形图(渐变填充) | 1个下拉框,用来筛选指标(核心指标+计算指标) | 排名基于筛选后数据重算 | 排名基于新DATA重算 | | 11 | 品类排名 | 条形图(渐变填充) | 1个下拉框,用来筛选指标(核心指标+计算指标) | 排名基于筛选后数据重算 | 排名基于新DATA重算 | | 12 | 多维交叉分析 | 格式化表格 | 3个下拉框分别选指标(核心指标+计算指标)、横向维度、纵向维度 | 表格数据基于筛选后数据重算 | 表格数据基于新DATA重算 | | 13 | 文字分析结论 | 格式化文本 | 基于数据自动生成 | 结论基于筛选后数据重新生成 | 结论基于新DATA重新生成 |
6、7并列展示;8、9并列展示;10、11并列展示
视觉规范
字体与尺寸:
- 全篇使用统一字体Calibri
- 字号:
- 图表轴标签10pt、不显示图例
- 图表标题:11.5pt
- 卡片内容高度32px,图表容器高度220px,宽度均100%自适应
- 表格字号:10pt,对齐方式为靠左
- 结论页标题:11.5pt、结论页内容:10pt
- 删除所有背景网格线(grid display:false)
- 图表横坐标轴的文字都横排显示,如果太拥挤就扩大两个文本之间的间隔数量
Chart.js图表必选项:
折线图: pointRadius=4, pointHoverRadius=6, tension=0.3, fill=true, borderWidth=3, 渐变backgroundColor
柱状图: borderRadius=6, barPercentage=0.7, 渐变backgroundColor
条形图: borderRadius=6, barPercentage=0.7, 渐变backgroundColor
环形图: cutout='60%', 数据标签在外圈, 渐变backgroundColor
渐变创建: ctx=createLinearGradient(0,0,0,220), addColorStop(0,主色0.8透明), addColorStop(1,主色0.5透明)
卡片三层结构(⚠️ 强制规范):
-
结构: ┌─ .kpi-card (.bc1~.bc8) ──────────────┐ │ .kpi-label 图标+名称 9pt 灰色 │ │ .kpi-value 主数值 18pt 白色粗体 │ │ .kpi-meta 辅助信息 8.5pt 暗色 │ └──── 4px 左边框颜色条 ─────────────────┘
-
第二层 .kpi-value 计算规则: | 卡片类型 | 第二层显示 | 说明 | |----------|-----------|------| | 核心指标 | ¥34.4万/2896 | 取最近一天的对应指标之和 | | 计算指标-isMom 类 | 环比 ▲ 3.2% | 计算该指标最近一天的实际值 | | 计算指标-比率类 | 25% | 计算该指标最近一天的实际值|
-
第三层 .kpi-meta 内容规则(根据业务语义差异化): | 卡片类型 | 第三层显示 | 说明 | |----------|-----------|------| | 核心指标 | 环比 ▲ 3.2% | 取 daily 最后一行对应的 _mom 字段 | | 计算指标-isMom 类 | 最近一天 | 主数值已是环比%,第三层只标注数据来源 | | 计算指标-比率类 |环比 ▲ 3.2% | 利润率/客单价/转化率/复购率 |
-
⚠️ 环比色标分场景规则(卡片 vs 图表,方向相反): | 场景 | 涨(正增长) | 跌(负增长) | 逻辑 | |------|:----------:|:----------:|------| | 卡片环比文字 | 绿色 ▲ | 红色 ▼ | 增长=好事=绿,下跌=警报=红 | | 同环比趋势图 | 绿色柱 | 红色柱 | 增长=好事=绿,下跌=警报=红 |
⚠️ 维度数据KPI过滤铁律:DATA.city/DATA.category 中没有环比类指标(如 sales_mom, orders_mom 等),它们只存在于 DATA.daily。初始化维度图表下拉和排名下拉时,必须用 k in DATA.city 过滤,否则 ds[calcKpi] 取到 undefined → data.map() 崩溃 → 后续漏斗/交叉表/排名/结论全部挂掉。数据刷新后重新赋值 DATA 对象时,同样必须重新执行此过滤逻辑。
表格规范
- 找出该行的最大值和最小值,最大值 → 🔴 红色加粗(ct-high,--accent-2),最小值 → 🟢 绿色(ct-low,--accent-2)
美化要素:
- 每个图表标题前加一个适合的小图标
- 卡片:左侧4px色条+渐变背景+box-shadow:0 4px 15px rgba(0,0,0,0.3)
- 每个卡片分配一个不同的颜色
- ⚠️ 核心指标卡片与计算指标卡片样式必须完全一致(同一套CSS class,禁止两套不同样式)
- 计算指标卡片沿用
.kpi-card样式,仅左边框颜色和底部提示文字颜色不同
- 图表容器:圆角8px+阴影+微渐变边框,注:卡片不要容器
- 每个柱形图、条形图采用同一种色系,不要彩色的,柱子不要边框
- 折线点标记:白色描边1.5px+主题色填充
- 板块间间距20px,板块标题带图标
- 第9大板块的漏斗转化表格:配色联动,每行左边框 + 渐变底色与漏斗图同色(蓝→绿→红→橙);且文字表格平铺满整个区域,文字靠左对其
- 第8大板块的漏斗图:图表居中,标签只保留指标名称且放到最左侧,转化率及每步的数值去掉
- 状态指示灯:
border-radius: 50%,带box-shadow: 0 0 6px同色发光效果
13套配色方案(CSS变量驱动)
切换方案时更新:root的CSS变量,同时destroy所有chart并用新配色recreate。
| 方案名 | --bg-primary | --bg-card | --accent-1 | --accent-2 | --accent-3 | |--------|-------------|-----------|------------|------------|------------| | dark_tech★ | #1A252F | #2C3E50 | #3498DB | #27AE60 | #E74C3C | | business_blue | #0D1B2A | #1B2838 | #3A86FF | #4895EF | #4CC9F0 | | dark_purple | #1A1A2E | #16213E | #7B2CBF | #9D4EDD | #C77DFF | | dark_forest | #1B2A1B | #2D4A2D | #2D6A4F | #40916C | #52B788 | | warm_copper | #2C1810 | #3E2723 | #BF6900 | #D4880F | #E8A838 | | slate_modern | #0F172A | #1E293B | #38BDF8 | #818CF8 | #F472B6 | | ocean_dream | #0C1B33 | #132A47 | #00A8FF | #00D2FF | #77E4D4 | | sunset_embers | #1E0A1F | #2D122C | #FF6B6B | #FF9F43 | #FECA57 | | teal_mystic | #0A1F1A | #12332A | #20B2AA | #48C9B0 | #7DDFC2 | | crimson_royal | #1E1018 | #2D1A24 | #E63946 | #D90429 | #FFADAD | | midnight_lavender | #10101A | #1C1C2E | #B980F0 | #8B5CF6 | #C4B5FD | | coffee_night | #1E1512 | #2C211D | #D97A3C | #E69A59 | #F4B97A | | aurora_green | #0A1C12 | #0F2B1A | #00C49A | #4ADE80 | #BBF7D0 |
多指标/多维度的图表配色:在accent-1/2/3基础上生成渐变色系(同色系不同透明度/亮度)。
交互逻辑
- 左侧任何变更 → 右侧看板即时刷新(无需Apply按钮)
- Chart实例管理:全局Map存储,切换时先chart.destroy()再new Chart()
- 配色切换 → 更新CSS变量 + destroyAll + recreateAll
- 勾选切换 → 对应板块显隐 + 图表destroy/recreate
- 指标下拉切换 → 仅更新对应图表数据
- 全局筛选变更 →
filterRawData()→reaggregateAll()→refreshAllPanels()(⚠️ 全量刷新,不可遗漏任何板块) - 数据刷新(手动)→ 检测 generatedAt 变化 → 替换 DATA → 重置筛选 →
refreshAllPanels()(⚠️ 全量刷新,与筛选变更相同管线)
Phase 5: ⛔ 强制自检(不可跳过,必须在对话中逐项输出结果)
⛔ 本节是硬关卡。Phase 4 完成后,必须逐项输出检查结果(
PASS或FAIL + 原因),全部 PASS 后才能进入 Phase 6 交付。严禁跳过任何一项。 如果某项 FAIL,立即修复后重新运行全部自检。
自检输出格式:编号 + 项目 + [PASS] / [FAIL: 原因],每项一行。
功能类(⛔ 最高优先级,先查这些)
- [ ] F1: SheetJS CDN 已加载 — HTML 中
<script src="...xlsx.full.min.js">存在 - [ ] F2: 隐藏 file input 已添加 —
<input type="file" id="hiddenFileInput" accept=".xlsx,.xls,.csv" style="display:none">存在 - [ ] F3:
handleRefresh()已实现且非空壳 — 函数体内包含实际的 Excel 解析逻辑,不能是DATA.raw = DATA.raw或setTimeout(()=>{},500)这类 no-op - [ ] F4: FSA 分支存在 —
typeof window.showOpenFilePicker === 'function'检测 +showOpenFilePicker()调用 - [ ] F5: file input 兜底分支存在 — FSA 不可用时触发
hiddenFileInput.click() - [ ] F6:
parseExcelFile(file)已实现 — 使用XLSX.read()+XLSX.utils.sheet_to_json(),中文列名→英文映射 - [ ] F7:
applyNewData(rows)已实现 — 替换 DATA.raw → 重建 dimValues → 重建筛选下拉框 → resetFilters → refreshAll - [ ] F8:
rebuildFilterDropdowns()已实现 — 刷新后筛选下拉框选项随新数据更新 - [ ] F9:
loadWD()使用过滤后的数据 — 调用filterRaw(DATA.raw)而非 直接DATA.raw - [ ] F10: 全局筛选变更后所有13板块更新 — onFilterChange → refreshAll → 12个板块全部刷新
- [ ] F11: 多维度筛选为 AND 逻辑 — filterRaw 中
(f.city==='all'||r.city===f.city) && (f.cat==='all'||r.category===f.cat) - [ ] F12: "重置"按钮生效 — 恢复所有筛选为"全部"并触发 refreshAll
- [ ] F13:
let DATA声明(非const DATA)— 允许刷新后重新赋值 DATA.raw - [ ] F14: 刷新按钮状态管理 — 刷新中 disabled,完成后恢复
- [ ] F15: 状态指示灯三态 — 绿(空闲)/黄(刷新中,
#F39C12)/红(失败,var(--accent-3)) - [ ] F16: localStorage 持久化 —
applyNewData()保存 DATA 到 localStorage;init()中优先读取 localStorage;「恢复默认」按钮存在且功能正常
视觉类(功能通过后再查)
- [ ] V1: 所有图表无网格线
- [ ] V2: 折线图 pointRadius≥4,白色描边
- [ ] V3: 图表容器高度统一220px
- [ ] V4: 配色与选定方案一致
- [ ] V5: 渐变填充生效(非纯色块,addColorStop(1) 透明度≥50%)
- [ ] V6: 图表标题 11.5pt,轴标签 10pt,时间轴 9pt
- [ ] V7: 卡片样式统一(kpi-card class,仅 border-left-color 不同)
- [ ] V8: 环比涨跌红涨绿跌(A股惯例)
- [ ] V9: 漏斗图+漏斗表严格递减,标签左对齐
- [ ] V10: 13套配色全部包含 tp/ts 字段(文本色)
健壮性
- [ ] R1: DATA JSON 语法有效(用 Node.js
new Function(script)验证,禁止跳过) - [ ] R2: 维度下拉和排名下拉不含环比类 KPI(
k in DATA.city过滤) - [ ] R3: 无漏斗数据时留空+提示而非崩溃
- [ ] R4: 刷新不影响当前配色方案选择
- [ ] R5: 刷新不影响侧边栏勾选状态
- [ ] R6: 上传数据后刷新页面 → localStorage 恢复 → 「恢复默认」按钮显示 → 数据与上传后一致
Phase 4 执行顺序(⛔ 强制,防偷懒)
HTML 代码编写必须按以下顺序,先功能→后视觉,严禁先调渐变再补刷新:
- 骨架:HTML 结构(header bar + sidebar + 12 panel 容器)
- 数据注入:Python json.dumps → 嵌入
__DATA_JSON__占位符 - 核心功能:全局筛选(filterRaw + onFilterChange + resetFilters)+ 数据刷新(handleRefresh + parseExcelFile + applyNewData + localStorage 持久化 + restoreDefaultData + SheetJS CDN + hidden file input + 「恢复默认」按钮)
- 12板块渲染:卡片→折线→柱状→漏斗→排名→交叉表→结论
- 视觉美化:渐变参数→图表样式→卡片样式→漏斗样式→表格条件格式
- 配色系统:13套 THEME + applyTheme + CSS变量联动
- Phase 5 全量自检:31项逐条 PASS/FAIL,FAIL 必须修复后重新全检
⛔ 防偷懒铁律:如果第3步(核心功能)未完成,禁止进入第5步(视觉美化)。
handleRefresh()写成空壳就去做渐变调色 = 违规。
Phase 6: 原 Phase 5 质量检查清单(详细参考)
以下为旧版完整清单,保留供详细对照,但执行时以 Phase 5 强制自检为准。
- [ ] 所有图表无网格线
- [ ] 折线图pointRadius≥4,有白色描边
- [ ] 图表容器高度统一220px
- [ ] 配色与选定方案一致(非默认蓝色)
- [ ] 图例不与坐标轴重叠(legend position:'top')
- [ ] 渐变填充生效(非纯色块)
- [ ] 左侧→右侧联动正常
- [ ] chart先destroy再recreate(无canvas复用报错)
- [ ] 无漏斗数据时留空+提示
- [ ] DATA JSON语法有效(用Node.js
new vm.Script(script)验证,禁止跳过) - [ ] 数值格式化:大数用万/亿,百分比1位小数
- [ ] 维度下拉和排名下拉仅包含
DATA.city/DATA.category中存在的KPI(不含环比类) - [ ] 全局筛选初始值均为"全部",全看板展示完整数据
- [ ] 全局筛选变更后所有12板块数据联动更新(卡片、图表、表格、结论无一遗漏)
- [ ] 多维度筛选为AND逻辑,选择"全部"时该维度不参与过滤
- [ ] 筛选后图表先destroy再recreate(无canvas残留或复用报错)
- [ ] 筛选后
DATA.raw不受影响(每次筛选均从原始全量数据重新过滤+聚合) - [ ] "重置"按钮一键恢复所有筛选为"全部"并刷新全看板
- [ ]
let DATA声明(非const DATA),允许刷新后重新赋值 - [ ] FSA 检测生效(Chrome/Edge:
typeof showOpenFilePicker==='function',使用handleRefreshWithFSA) - [ ] FSA 兜底生效(Safari/Firefox:回退到传统
<input type="file">文件选择框) - [ ] API 刷新生效(
handleRefresh(),http://协议,服务器运行时) - [ ] 文件上传刷新生效(
parseExcelFile(),所有浏览器,SheetJS 解析 Excel) - [ ]
applyNewData()正确执行:DATA 赋值 → buildFilters() → resetFilters() → 全量刷新12板块 - [ ] 刷新按钮状态管理:刷新中 disabled,完成后恢复
- [ ] 状态指示灯:黄色(刷新中)→ 绿色(成功)/ 红色(失败)
- [ ] 无自动轮询(已移除
setInterval和倒计时) - [ ]
generatedAt变化检测:API 刷新时比较时间戳,相同则跳过 - [ ] 文件 Banner 显示(file:// 协议下显示,提示用户选择 Excel 文件)
- [ ] 刷新后所有12板块数据正确更新(卡片数值、图表、表格、结论)
- [ ] 刷新后全局筛选器重置为"全部"
- [ ] 刷新不影响配色方案选择(当前选中的配色在刷新后保持不变)
- [ ] 刷新不影响侧边栏勾选状态(核心指标/计算指标/维度的选中状态保持)
Phase 7: 交付
把下面两个链接都交付给我:
- 交付本地版本的看板链接
- 把这个链接部署成可以对外分享的新链接
微信扫一扫