DataGear数据分析看板实战:数据集、图表与交互组装
做数据分析的人大概都有过这种经历手里攥着一堆 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 charsetUTF-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 classwrap div classrow div classcard kpispan classlabel今日成交额/spanspan classvalue idkpi1-/span/div div classcard kpispan classlabel今日订单数/spanspan classvalue idkpi2-/span/div div classcard kpispan classlabel客单价/spanspan classvalue idkpi3-/span/div /div div classrow div classcard styleflex:2div dg-chart成交额趋势 stylewidth:100%;height:320px;/div/div div classcard styleflex:1div dg-chart地区分布 stylewidth: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 写错或者配置写错而是因为一开始没问清楚使用场景——做给老板看的和执行层看的完全是两个东西。老板要的是趋势和异常一屏之内要有结论执行层要的是明细和筛选能自己往下钻。同一份数据两种看板硬塞进一块板里两边都不满意。另外一个体会是看板是要养的。上线之后数据源可能改字段、业务口径可能调整、用户会提新的维度需求。所以我建议每块看板都留一页备注记录数据集依赖了哪些表、哪些字段改动的适合能快速评估影响范围。这个习惯看起来笨但在我经手的项目里它比任何花哨的可视化技巧都更值钱。最后一个实用的小技巧看板做完之后自己连续用三天。每天早上打开看一眼你会发现一堆设计时没意识到的问题比如某个数字的字体太小、某个图表的颜色在真实屏幕上太浅、某个参数默认值让首屏加载慢了五秒。这些问题用户不一定说得出来但用起来就是别扭。自己先用三天能挡掉大部分返工。