用开源报表工具SpringReport做车间大屏+Excel 导出,我踩过的一些坑,基于最初版本的设计开发

关键词:SpringReport、数据大屏、Excel 报表、双 Y 轴、下钻传参、免登录分享 适合读者:正在用或准备用 SpringReport 做报表/大屏的后端、数据、实施同学。


写在前面

最近用 SpringReport 给产线做了一套「可动率分析」:5 个大屏页面 + 5 个 Excel 导出模板,要求很硬--- ---

  • 不允许改源代码;
  • 不允许直接连数据库(一切走平台接口);
  • 页面要能逐级下钻,最后跳到 MES 系统;
  • Excel 要能正式打印导出发给客户。

两周下来,我对这套平台的判断可以压成一句话:

界面能做出"像样的画面",但要做"精确控制",必须落到组件 JSON;两者都改不了的,就得靠 URL 传参绕。

下面把踩过的坑、验证过的写法、以及平台目前的能力边界一次讲清,能帮你少走几天弯路。


一、先建立一张"能力边界表"

SpringReport 的交付物有三类:Excel 展示报表 (单元格)、数据大屏 (自由布局 + VChart)、数据集(SQL/API)。

上手之前建议先明确:某需求属于哪一层?

需求 界面可配 需要改组件 JSON 只能改源码
换图表类型、颜色、图例、标签位置 ✅
布局、层级、背景、边框 ✅
数据集 + 参数(字符串/数值/日期/下拉) ✅
图表下钻(带参跳转) ✅
双 Y 轴 ✅
轴刻度颜色、隐藏轴标题 ✅
饼图标签引导线 / 防重叠 ✅
tooltip 配色、参考线 ✅
下钻带业务字段名(对接外部系统) ✅ + 数据集别名
页面内改日期 → 数据刷新 ✅
下钻在当前页打开(不新标签) ✅
文本链接带动态参数 ✅

这张表决定了你后面 80% 的决策。先查表,再动手,能省掉大量试错。


二、坑 1:双 Y 轴,面板里真的没有

做柏拉图(柱状 + 累计占比折线)时,你会遇到:柱子数值几百,累计占比 0~100,折线被压成一条直线。

面板里能加轴、能改标题,但没有任何地方能把"某个系列"绑到"某根轴" 。

答案在 VChart 的 axes[].seriesIndex,只能改组件 JSON:

json 复制代码
{
  "axes": [
    { "orient": "bottom" },
    { "orient": "left",  "seriesIndex": [0], "min": 0, "max": 250 },
    { "orient": "right", "seriesIndex": [1], "min": 0, "max": 100 }
  ]
}

两个细节:

  1. seriesIndex:[0] 是柱子,[1] 是折线;右轴记得 min:0,max:100,否则刻度会被数据带着跑;
  2. series[].axes 是无效写法,别被某些文档带偏。

三、坑 2:坐标轴刻度一白一彩,改不了颜色

左右轴默认配色不同,放在深色大屏上很花。面板只能改字体大小/标题,颜色要写 JSON:

json 复制代码
"axes": [{
  "orient": "left",
  "label": { "visible": true, "style": { "fill": "#FFFFFF", "fontSize": 13 } },
  "title": { "visible": false }
}]

顺手把 title.visible 关掉,画面会干净很多。


四、坑 3:饼图标签挤成一坨

数据倾斜时(比如最大项占 95%,其余五项各占 1%),标签全堆在同一角度。

先开外置 + 引导线:

json 复制代码
"label": {
  "visible": true,
  "position": "outside",
  "line": { "visible": true, "style": { "lineWidth": 1, "stroke": "#9BB7E0" } },
  "style": { "fontSize": 13, "fill": "#FFFFFF" }
},
"labelLayout": { "hideOverlap": true, "align": "labelLine" }

但要说实话 :hideOverlap 在"95% + 多个 1%"这种极端分布下压不住。工程上的正解是------别让饼图承担精度:标签只标大项,明细交给右侧表格。


五、坑 4:下钻能点,但参数带不过去

大屏下钻的机制很简单:

ini 复制代码
url = drillLink + '&' + 数据行里的字段名 + '=' + 值;

关键点:参数只能取"数据集返回的列"。 你想让 URL 出现 lineCode=AS0020,数据集里就得有 lineCode 这一列。

于是我把业务字段做成别名列,一步到位:

sql 复制代码
SELECT
  DATE_FORMAT(r.work_day,'%Y-%m-%d')  AS 工作日,
  r.line_code                         AS 产线,
  r.availability                      AS 可动率,
  r.line_code                         AS lineCode,   -- 下钻用
  #{start_time}                       AS startTime,  -- 下钻用
  concat(#{start_time}, ' 08:30:00')  AS beginTime,
  concat(DATE_ADD(#{end_time}, INTERVAL 1 DAY), ' 08:29:59') AS endTime
FROM daily_rate r

然后在组件里配:

json 复制代码
"isDrill": true,
"drillLink": "http://host/mes/pro/defectRecord?",   // 注意只写到 ?
"drillParam": "lineCode,beginTime,endTime"

三个血泪教训:

  1. drillLink 千万不要写查询串 。我一开始写成 ?lineId=4&badTypeMode=${bad_type_mode},结果 URL 变成 badTypeMode=%24%7Bbad_type_mode%7D&badTypeMode=1 ------ 占位符原样发出,还和真实参数重复;
  2. 目标页要有同名参数才接得住;
  3. 折线图只有圆点能点 ,线身不可点,把 point.style.size 放大到 14 会好点很多。

六、坑 5:日期控件能选,但数据不变

这是最容易被业务当成 bug 的一点:抽屉里选好日期 → 点查询 → 请求确实重发了,但参数还是空。

原因链:控件值只写进控件临时状态,没回写 component.params;而取数是 Object.assign(组件参数, URL参数, 表单参数)------组件这一层从来没拿到新值。

结论:日期区间只能靠 URL 传。

ini 复制代码
.../screenView?tplId=xxx&sendRequest=1&start_time=2026-10-16&end_time=2026-10-26

配套做法:把默认区间写进入口链接,SQL 里再兜一层:

sql 复制代码
CASE WHEN #{start_time} = '' OR #{start_time} IS NULL
     THEN DATE_SUB(CURDATE(), INTERVAL 6 DAY)
     ELSE #{start_time} END

七、坑 6:#{} 和 ${} 的引号规则不一样

这个坑能让 SQL 直接报语法错,且报错信息很不友好。

写法 替换方式 要不要加引号
#{x} 预编译,值自带引号 ❌ 不加('#{x}' → ''值'')
${x} 字符串拼接 ✅ 字符串要加、数字不加
表名后缀 只能 ${} qt_over_station_${line_id}

还有个隐藏坑:通过接口建数据集时,SQL 里用 ${} 会被当模板表达式解析 ,直接 模版解析错误:Encountered ...。所以接口建库一律用 #{}。


八、坑 7:点查询后,抽屉不关

现象:数据刷新了,但筛选抽屉赖着不走。

翻源码(screenView.js)找到真相:

kotlin 复制代码
refreshComponentData() {
  for (let i = 0; i < this.components.length; i++) {
    const el = this.components[i];
    if (el.type != 'picture') {
      this.$refs['draggable'].$refs[el.id][0].initData(...);  // ← 任一组件取不到 ref 就抛错
    }
  }
  this.closeSearch();   // ← 只有全部跑完才会执行
}

只要有一个组件拿不到 ref,循环中断,closeSearch() 永远不执行。 实践中元凶是表单类组件 (formsDate/formsButton 在查看模式下不渲染),以及非规范的自定义组件 id。

解法:页面里不要留 forms 组件;自定义组件 id 统一改成 32 位十六进制。


九、坑 8:表格永远"暂无数据"

同一个数据集,饼图有数,表格空白。原因:表格组件要自己声明参数,不声明就传空参数给 SQL。

记住三件事:

  1. 表格参数与图表同名同类型逐个补齐;
  2. tableColumn 里的 key 必须等于数据集列名;
  3. 列宽用百分比,合计 100。

十、坑 9:Excel 报表建不出来(50001)

调 reportTpl/insert 一直 50001,日志里是空指针。翻后端发现:

css 复制代码
for (int i = 0; i < model.getDataSource().size(); i++) { ... }   // dataSource 为 null → NPE

建 Excel 模板必须带 dataSource 数组:

bash 复制代码
{ "tplCode": "exp_xxx", "tplName": "导出-xxx", "tplType": 2,
  "reportType": <目录真实id>, "merchantNo": "SR00000000", "isExample": 2,
  "dataSource": ["<datasourceId>"] }

另外:tplType 必须 2,reportType 要填目录 id (不是字符串 "1")。


十一、坑 10:大屏导出的 Excel,参数带不过去

这是架构级限制:文本组件的 href 是静态字符串。

xml 复制代码
<!-- textComponent.vue -->
<a :href="component.href" target="_blank">

写 href="...?lineId=${line_id}" 不会替换,只会编码成 %24%7Bline_id%7D 发出去。

目前三条路:

方案 可行性 代价
静态 href(写死默认参数) 能用 不跟随筛选
图表下钻当"导出按钮" 能带参 每页多一个组件 + 一次取数
外层 iframe 外壳页拼 URL 能带参、还能同页跳转 需同源部署一个 html

我最后选了外壳页,顺手解决了"下钻总开新标签"的问题:

javascript 复制代码
// 同源 iframe 内接管跳转
view.contentWindow.open = function (url) { view.src = String(url); return null; };
document.querySelectorAll('a[target="_blank"]').forEach(a => a.target = '_self');

十二、顺带提给产品的 6 个需求

调研完最大的感受:这些不是使用姿势问题,是产品缺口。按性价比排序:

优先级 需求 解决什么
P1 下钻「打开方式」:新标签 / 当前页 / 父 iframe 多级下钻开一堆标签
P1 参数控件值回写请求(表单组件可注册为筛选项) 页面内筛选形同虚设
P1 刷新函数 ref 判空 + try/catch + 完成后关抽屉 点了没反应 / 抽屉不关
P2 文本/按钮 href 支持参数占位 导出、外链不能带当前筛选
P2 图表面板加「轴-系列绑定」+ 轴刻度色 不用再改 JSON
P2 折线下钻支持线身热区 点击成功率

十三、总结

  1. SpringReport 是能交付的:不改源码、不碰数据库,也做出了 5 页大屏 + 5 个 Excel 模板 + MES 跳转;
  2. 但要有"JSON 能力" :双轴、轴样式、标签策略、外部系统参数名,全都靠改组件 JSON 实现;
  3. 交互层要绕:日期靠 URL、同页跳转靠 iframe 外壳、导出靠拼参;
  4. 配置规范比技巧重要 :#{} 不加引号、组件 id 用 32 位十六进制、表格必声明参数、日期不填默认值------这四条能避免你 70% 的疑难杂症。

最后给一句判断标准,省得你反复试:

界面能配颜色,就别写 JSON;能写 JSON,就别改源码;改源码都解决不了的,用"外壳页 + URL 传参"绕过。


如果这篇对你有帮助,欢迎点赞收藏;也欢迎在评论区聊聊你踩过的 SpringReport 坑。

相关推荐
QuZhengRong3 小时前
【Luck‑Report】 AI 智能报表助手 + RAG 知识库 + 报表引擎 V2.0.9 更新
github·agent·报表·开源项目·数据可视化
MetWeave6 小时前
风的三十年:一个机场的风在变小吗:10,958 天的账(《风不是一个数》系列第 6 篇)
前端·数据可视化
AllData公司负责人1 天前
【产品便捷使用一】AIIData 数据中台:企业基础版核心能力支持,一站式全链路数据处理实操指南
大数据·前端·数据治理·数据可视化·数据开发·数据服务·数据接入
wangweilong3342 天前
把一部古典小说建模成知识图谱:红楼梦图鉴的数据、本体和单文件 HTML
数据可视化
Blockbuater_drug2 天前
《经典机器学习实战》第 2 课:回归入门——环境、工具与数据准备
线性回归·pandas·scikit-learn·数据可视化·数据清洗·最小二乘法·conda环境
阿琳a_2 天前
b-map-viewer:面向 Vue 3 的地图可视化 SDK,从接入到展示再到交互分析
前端·javascript·vue.js·gis·数据可视化·地图
MetWeave3 天前
一朵玫瑰,装下一年的风(《风不是一个数》系列第 5 篇
前端·数据可视化
桑年4 天前
四、Pandas数据处理
人工智能·python·线性代数·pandas·数据可视化·数据处理
QianhangQianping4 天前
WebSocket实时推送+ HTTP 定时轮询:一块大屏两套取数怎么不打架
前端·websocket·网络协议·http·低代码·架构·数据可视化