做数据分析的人大概都有过这种经历:手里攥着一堆 SQL 查询,每个结论都要单独跑一遍、单独截个图、单独发一份文档,等到第二天业务方再问一句"昨天的数呢",又得从头来一遍。DataGear 这个平台最值钱的地方,就是把这些零散的查询固化成一个可复用、可交互、可交付的数据分析看板。这个系列写到第五篇,前面把数据源、数据集、图表这几块地基铺得差不多了,这一篇专门讲怎么把它们拼成一块真正能拿出去用的看板——不是那种"能显示图"的 Demo,而是业务方每天早上会自己点开看一眼的东西。如果你刚刚能把数据集查出结果、能画出单张图表,却卡在"怎么组合成一个页面",或者你已经做过几块看板但总觉得哪里别扭,下面的内容应该能帮你省掉不少来回折腾。
1. 动手之前先把看板当成一个产品来设计
1.1 四层模型决定了你的返工次数
DataGear 的结构是分层的,数据源管连接、数据集管取数、图表管呈现、看板管组合。很多人第一次做看板,习惯打开看板编辑器直接往里拖图,边拖边改,改到第七八个图的时候发现某个指标口径不对,得回去改数据集,改完发现图表里写死的中文标题和单位也得跟着改,改完图表又发现看板里几个图的高度对不齐。一轮下来两三个小时没了,产出的东西还不敢给人看。
我现在的习惯是反着来,先把四层在脑子里过一遍:这次要看的是哪几个业务问题,每个问题对应哪张表、哪个字段、什么过滤条件,这决定了数据集的数量;每张图要回答什么问题,决定了图表的类型和数量;这些图之间有没有主次关系,决定了看板的布局。这个思考过程大概二十分钟,但能省掉后面一两个小时的返工。
举个具体的例子。假设要做一块"订单运营看板",最容易犯的错是建一个大而全的数据集,把订单号、客户、金额、地区、品类、日期全查出来,然后让每张图自己去聚合。这种做法的后果是:数据集行数可能有几十万,浏览器端渲染时卡顿,而且每张图的聚合逻辑分散在图表配置里,口径根本没法统一。正确的拆法是按"图的粒度"建数据集——趋势图需要一个按天聚合的数据集,地区分布需要一个按省份聚合的数据集,品类排行需要一个按品类聚合的数据集。粒度不同,数据集就不同,这是最省心的分法。
提示:判断数据集是否该拆开的标准很简单——如果两张图需要的字段集合、聚合维度、过滤条件有明显差异,就拆。强行合在一起省下的那点建模时间,后面会以"改一处漏三处"的形式还回来。
1.2 指标口径先定,图后画
这一条是我吃过亏才记住的。之前做一块销售看板,"成交额"这个指标我在三个图里用了三种算法:趋势图用的是下单金额,地区图用的是支付金额,排行榜用的是扣除退款后的净额。三个数字放在同一个页面里,业务方第一眼就发现对不上,然后整块看板的可信度就没了。
所以现在我的流程是,动手建数据集之前,先在一张纸上(或者文档里)写清楚每个指标的定义:字段来源、过滤条件、是否含税、是否含运费、时间维度按哪个日期字段算。这块纸上的东西,最后会直接变成数据集里的 SQL 注释和看板的副标题说明。别嫌麻烦,写在页面上的口径说明,比事后在群里解释一百遍管用。
顺便说一句,看板里放一两条口径注释是有必要的。DataGear 的看板模板本质是 HTML,你想在标题旁边加一行灰色小字说明"数据口径:支付时间,含税",直接写个<span>加 CSS 就行,成本极低,收益极高。
1.3 布局先画草图,别在编辑器里试
看板布局我一般用 12 栅格来规划。一个典型的中层管理看板是这样分的:顶部一行放 3 到 4 个核心指标卡(KPI 数字),占满 12 格;第二行左边放趋势折线图占 8 格,右边放占比环形图占 4 格;第三行放两张明细表格各占 6 格。这个结构不是拍脑袋来的,它符合人看屏幕的眼动顺序——先看总数,再看走势,最后看细节。
草图画完之后,再决定每个区块的高度。这里有个很实际的问题:DataGear 的图表容器高度需要显式指定,不指定的话图表可能渲染出来是 0 高度或者被压扁。所以我习惯在模板里给每个图表容器写固定高度(比如height:320px),然后再用 CSS 的 flex 或者 grid 做外层自适应。固定高度加弹性宽度,是数据看板里最稳妥的组合,因为图表的宽高比变化太大会导致坐标轴标签重叠。
2. 数据集是整个看板的地基
2.1 参数化 SQL 怎么写才不返工
看板几乎一定需要参数:时间范围、地区、品类、部门。DataGear 的参数传递路径是"看板参数 → 图表参数 → 数据集参数",最后落到 SQL 里的占位符上。这套机制不难,难的是参数类型和空值处理。
我踩过最典型的一个坑是时间参数。数据集里写where order_date >= #{startDate},如果看板传过来的 startDate 是空字符串,这条 SQL 要么报错要么查不出数据。稳妥的写法是用带默认值的条件拼接,让空值等于"不过滤":
select date_trunc('day', order_time) as d, sum(pay_amount) as amount from orders where 1 = 1 and (#{startDate} is null or order_time >= #{startDate}) and (#{endDate} is null or order_time < #{endDate}) and (#{region} is null or region = #{region}) group by 1 order by 1这种(param is null or 条件)的写法看起来啰嗦,但它是唯一能让"参数为空时查全量、有值时查子集"稳定工作的方式。不同的数据库对参数类型的推断不一样,如果平台传过来的是字符串而字段是日期类型,可能还需要显式转换,这个在后面排查章节会细说。
另一个容易忽略的点是参数命名。看板参数、图表参数、数据集参数三层的名字最好保持一致,全用startDate这种驼峰,不要一层叫startDate、一层叫begin_date。DataGear 里参数是逐层往下传的,名字对不上就静默取不到值,页面不报错但数据是错的,这种 bug 排查起来最费时间。
2.2 聚合到底放在 SQL 里还是图表里
这个问题我被问过很多次。结论是:能在 SQL 里聚合的,就别丢给图表。
原因有两个。第一是数据量。图表渲染发生在浏览器端,如果数据集返回十万行明细让前端聚合,页面加载会明显变慢,尤其在低配办公电脑上。第二是一致性。图表的聚合配置散落在每个图里,改口径要一个个改;写在 SQL 里,改一个数据集就全改了。
当然也有例外。需要做钻取或者明细下钻的时候,数据集保留明细粒度是合理的,因为用户点击某个柱子之后要看具体订单。这种场景我的做法是建两个数据集:一个聚合的给主图用,一个明细的给下钻表格用,两者共享同一套过滤条件。多建一个数据集换来的是页面响应速度和口径清晰的平衡,很划算。
2.3 数据集调试别跳过这一步
DataGear 的数据集编辑页有预览功能,我建议每建一个数据集就完整跑一遍三种情况:参数全空、参数全填、只填一部分。全空的时候看行数是否合理(是不是把三年的数据都捞出来了),全填的时候看结果是否符合预期,部分填的时候看空值处理有没有漏洞。
还有一个小技巧:在数据集 SQL 里临时加上行数统计,比如select count(*) from (...)包一层,先确认数据量级,再决定要不要加时间默认值限制。我看过太多看板首屏加载五秒以上,最后定位下来是某个数据集默认查了两年全量数据。
3. 图表:把数据翻译成视觉
3.1 图表类型和数据字段的映射关系
选图表类型这件事,本质是问"我要让读者比较什么"。比较大小用柱状图,看趋势用折线图,看结构占比用环形图或饼图,看分布用散点图或箱线图,看排名用横向条形图,看地理分布用地图。这些是常识,但在实际项目里还是会出错,出错的重灾区是饼图。
一个饼图切超过 6 块,基本上就没人能分清了。如果品类有十几二十个,正确的做法是取 Top N 加一个"其他",或者干脆换成横向条形图。我做看板时有个硬规矩:环形图/饼图的分片不超过 6 个,超过就聚合。
另一个容易出错的地方是时间轴。如果数据集的日期是字符串格式而不是日期类型,折线图的 X 轴可能会被当成类目轴处理,导致间距不均。这种情况要么在 SQL 里做类型转换,要么在图表选项里指定 X 轴类型为 time。前者更稳。
3.2 图表选项 JSON 的常用配置
DataGear 的图表支持通过选项 JSON 做深度定制。这块配置看起来吓人,其实常用的就那么十几个键。我整理了一份自己最常改的清单:
{ "title": { "text": "近30天成交额趋势", "subtext": "口径:支付时间,含税" }, "grid": { "left": 60, "right": 30, "top": 70, "bottom": 50 }, "tooltip": { "trigger": "axis" }, "legend": { "show": false }, "xAxis": { "type": "category", "axisLabel": { "rotate": 0 } }, "yAxis": { "type": "value", "name": "万元" }, "series": [ { "type": "line", "smooth": true, "areaStyle": { "opacity": 0.15 }, "symbolSize": 6 } ] }这几个键解决的是最影响观感的问题:grid控制绘图区留白,不设的话 Y 轴标签容易被裁掉;tooltip.trigger设成 axis 让人能沿着时间轴对比;yAxis.name把单位写在轴上,比在标题后面加括号更清晰。
注意:选项 JSON 里不要写和数据集字段无关的东西。我见过有人在 series 里硬编码 data 数组,结果图表不跟随数据集变化,一切换参数就露馅。数据必须来自数据集,选项只管样式。
3.3 数值格式和可读性
金额动辄七八位数,直接显示 12345678 没人愿意看。DataGear 的图表支持数值格式化,我一般统一处理成万/亿单位,保留一位小数。这个处理最好在图表层面做,而不是在 SQL 里除以一万,因为原始数值留着的意义在于,用户 hover 看提示框的时候能看到精确值。
坐标轴刻度也是同理。如果纵轴全是 0、2000000、4000000 这种,读者的脑子要额外做一次除法。格式化之后变成 0、200、400(万元),阅读成本立刻降下来。
4. 看板组装:把散件拼成一块能用的面板
4.1 看板模板的最小骨架
DataGear 的看板最终是一个 HTML 模板,图表通过dg-chart属性挂载上去。最小可用的骨架大概长这样:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <style> body { margin: 0; background: #f5f6f8; font-family: "PingFang SC", "Microsoft YaHei", sans-serif; } .wrap { padding: 16px; } .row { display: flex; gap: 12px; margin-bottom: 12px; } .card { flex: 1; background: #fff; border-radius: 6px; padding: 8px 12px; box-sizing: border-box; } .kpi { display: flex; flex-direction: column; gap: 4px; } .kpi .label { font-size: 13px; color: #888; } .kpi .value { font-size: 26px; font-weight: 600; color: #222; } </style> </head> <body> <div class="wrap"> <div class="row"> <div class="card kpi"><span class="label">今日成交额</span><span class="value" id="kpi1">-</span></div> <div class="card kpi"><span class="label">今日订单数</span><span class="value" id="kpi2">-</span></div> <div class="card kpi"><span class="label">客单价</span><span class="value" id="kpi3">-</span></div> </div> <div class="row"> <div class="card" style="flex:2"><div dg-chart="成交额趋势" style="width:100%;height:320px;"></div></div> <div class="card" style="flex:1"><div dg-chart="地区分布" style="width:100%;height:320px;"></div></div> </div> </div> </body> </html>这里的关键是每个dg-chart的容器必须有明确高度。很多人第一次做看板,图挂上去之后一片空白,八成就是这个原因——容器高度是 auto,图表初始化时量到 0,画布就没了。
4.2 参数驱动的看板
看板参数的意义在于让一整页图跟着一个条件联动。比如顶部放一个时间选择器,下面所有图都跟着变。DataGear 里参数可以在看板设置中定义,然后在模板里用表达式取值,同时图表的数据集会引用同名参数。
我通常把参数分成两类:全局参数(时间范围、组织、地区)和图表私有参数(某些图特有的过滤)。全局参数放在顶部固定区域,私有参数如果不多就硬编码在图表的配置里,避免页面上参数控件太多吓到用户。
还有个细节:参数变化之后什么时候刷新。默认的交互是参数一改就重新请求所有图表,如果参数是个下拉框,用户每选一次就触发一轮请求,网络慢的时候体验很差。这时候可以考虑加一个"查询"按钮,把参数变化和刷新解耦。DataGear 的看板支持自定义按钮绑定刷新逻辑,模板里放个按钮写个事件就行。
4.3 布局自适应与滚动条
办公场景的屏幕尺寸跨度很大,笔记本 1366 宽,外接显示器 1920 甚至 2560。看板如果用绝对定位,在宽屏上会留一大片空白,在窄屏上又会溢出。我的做法是外层用 flex,卡片用flex:1按比例分,图表容器宽度 100%,只有高度固定。这样在 1366 上图表会窄一些但依然可读,在 2560 上图表会拉宽,看起来更大气。
需要注意一点:图表宽度变化时,内部的坐标轴标签可能会重叠。所以在看板做完之后,我习惯在浏览器里把窗口从宽拖到窄,观察一遍有没有文字叠字的情况。有的话就把 X 轴标签角度调成 30 度或者斜排,这个在图表选项里一行就能改。
5. 交互、联动与交付
5.1 图表的联动和钻取怎么做
单图看板的价值有限,真正好用的是"点一下某个地区,下面的明细图跟着变"这种联动。DataGear 的图表支持点击事件绑定,可以在模板里通过 JavaScript 监听图表的点击,拿到点击项的名称,再调用其他图表的刷新方法并传入新参数。
大致思路是:给主图绑定 click 事件,回调里拿到被点中的类目名(比如"华东"),然后把它塞进明细图对应的参数里,触发明细图重新加载。这个模式写一次可以复用,我在多个看板里都用同一段脚本。
// 主图点击后,把类目名传给明细图作为参数 dg.chart.on('click', function (params) { var name = params && params.name; if (!name) return; dg.chart.refresh('订单明细', { region: name }); });具体 API 名称不同版本可能有差异,实际写的时候对着当前版本的接口调一下即可。要点是参数名要对上数据集里的占位符,对不上的话明细图会查全量,看起来像是没联动。
5.2 定时刷新和全屏展示
运营看板经常挂在办公室里的大屏上,需要定时刷新。定时刷新有两种做法:一种是页面级定时器,每隔 N 分钟重新加载整页;另一种是逐个图表刷新。页面级刷新简单粗暴但会闪屏,图表级刷新体验更好但需要逐个维护。我一般用图表级,因为大屏上闪一下很显眼。
全屏展示这块,模板里加一个按钮调用浏览器全屏 API 就够了,配合隐藏掉页面上的编辑控件,看起来就是个独立的监控页。注意全屏状态下要重新计算图表尺寸,否则图表还是原来的宽度,右边会空一块。触发一次 resize 通常就能解决。
5.3 交付时该给什么
看板做完不是结束,交付才是。我交付一块看板通常会附带三样东西:页面本身、一页口径说明(每个指标怎么算的)、以及一份"常见疑问"(为什么这个数字和报表系统不一样)。
第三样东西最容易被忽略,但最省事。数据看板上线之后,一定会有人问"你这个成交额和我从后台导出的怎么差了三千"。如果提前在页面底部写清楚口径差异在哪,这类问题能减少一大半。
6. 常见问题与排查实录
下面这张表是我做过七八块看板之后攒下来的问题清单,基本覆盖了新手上手时 90% 的卡点。
| 现象 | 最可能的原因 | 排查动作 |
|---|---|---|
| 图表区域一片空白 | 容器没有显式高度 | 给dg-chart容器加height:300px之类 |
| 图表加载了但没数据 | 数据集参数名和图表参数名不一致 | 三层参数名逐一比对,注意大小写 |
| 切换参数后数据不变 | 图表缓存或未绑定参数 | 检查图表属性里的参数映射,必要时手动刷新 |
| 时间过滤结果偏少 | 参数是字符串,字段是日期类型 | 在 SQL 里做显式类型转换 |
| 页面首屏加载很慢 | 某个数据集默认查全量 | 给时间参数设默认范围,加索引 |
| 数字挤在一起看不清 | 未做数值格式化 | 图表选项里配置万/亿单位 |
| 宽屏上图表被拉变形 | 宽度用了固定值 | 改成百分比加 flex 布局 |
| 联动点了没反应 | 点击事件没绑上或参数名错 | 打开控制台看事件是否触发、参数是否正确 |
| 大屏定时刷新闪屏 | 用了整页刷新 | 改为逐图表刷新 |
| 导出图片缺内容 | 图表未渲染完成就导出 | 加延时或在渲染完成回调里触发 |
除开表格里的,还有两个值得单独说的问题。
第一个是跨域和静态资源。看板模板里如果引用了外部字体或者图标库,某些内网环境下加载会失败,表现是页面布局跑偏但数据正常。我的做法是尽量用系统字体,图标用内联 SVG,不依赖外部 CDN。
第二个是日期边界。order_time < #{endDate}和order_time <= #{endDate}差一天,如果 endDate 是2024-06-30,用<=在带时间的字段上其实只能查到当天零点。习惯上统一用"左闭右开",即>= startDate and < endDate + 1天,这样不管字段有没有时分秒都不会出错。
7. 几个踩过坑才明白的道理
做看板这件事,技术难度其实不高,难的是想清楚"谁在什么场景下看这块板"。我做过的返工最多的项目,不是因为 SQL 写错或者配置写错,而是因为一开始没问清楚使用场景——做给老板看的和执行层看的,完全是两个东西。老板要的是趋势和异常,一屏之内要有结论;执行层要的是明细和筛选,能自己往下钻。同一份数据,两种看板,硬塞进一块板里,两边都不满意。
另外一个体会是,看板是要养的。上线之后数据源可能改字段、业务口径可能调整、用户会提新的维度需求。所以我建议每块看板都留一页备注,记录数据集依赖了哪些表、哪些字段,改动的适合能快速评估影响范围。这个习惯看起来笨,但在我经手的项目里,它比任何花哨的可视化技巧都更值钱。
最后一个实用的小技巧:看板做完之后,自己连续用三天。每天早上打开看一眼,你会发现一堆设计时没意识到的问题,比如某个数字的字体太小、某个图表的颜色在真实屏幕上太浅、某个参数默认值让首屏加载慢了五秒。这些问题用户不一定说得出来,但用起来就是别扭。自己先用三天,能挡掉大部分返工。