FineReport
报表设计
二维码

页码 / 页眉页脚
可通过 FineReport 提供的 API 自己制作页码的显示

也可以在页脚中设置

两者的不同在于页脚的内容始终在页面的最下面,而自己制作的页码是紧跟在打印内容后面的,也就是说后者有可能打印的一张纸的中间(打印内容过少的时候)
纸型与布局尺寸
行高最小设置 3.1 毫米,再缩小行号就看不到了,列宽最小 3.2 毫米。
制作第一个报表

单元格字体缩小、换行

设置小数位数

日期时间相关
仅显示日期 
打印当前日期时间: 
合计

合并单元格

字段拼接静态信息

数据集字段拼接静态文本

显示传入参数

FORMAT 格式化日期

每页重复显示行
类似下图,也可以设置每页重复显示结尾行,类似页眉和页脚的功能

内容换行显示
单据明细数据多个字段在竖向上显示为一行内容,如果在 FineReport 中通过多行拼接的方式不好实现可以在 mysql 中通过 concat('a',char(10),'b') 在 sql 中拼接多个字段设定换行来实现 
案例见 xms_account 的报价单的打印
A4分栏重复打印
一张A4上重复打印一小块区域,看旭纸业项目8个包装的TOTO小标签 - TOTO_tiny8.cpt,路径是 D:\xzy\WebReport\WEB-INF\reportlets\tags
可选显示汇总数据
染厂项目中发货单打印模板使用了本方法,由于数量列是可变的 - 选择性显示:纸管、空加、匹数、公斤 当是前两者时不显示表格的汇总数据,是后两者时则需要显示汇总数量。

卡片分栏
实现下面效果 
的功能叫做 卡片分栏 正常数据一行行显示是纵向延展的,所以要在

注意数据源要保证有 6 * 4 =24行数据(6行,每行4列)。该功能的官方介绍见https://help.fanruan.com/finereport/doc-view-354.html
父子表分组换页
看下面数据结构,向 data.reportlets 数组中填充不同模板的数据则可实现一次打印预览中显示多个不同模板 数组中的每个 printTemplate 可根据数量手动控制换页。 首次应用在天之然成品货单单据中通过右键菜单一次打印预览显示多有缸号的细码单(一个缸号一张细码单) 文件 D:\software\tzr\pademis_ssm\src\main\webapp\web\MainPages\pages_CPGL\CP_sendOut_v8.js 的方法 printAllXMDdo
let printConfig = {
url: "ReportServer",
isPopUp: false,
data: {
reportlets: [// 将多个不同模板组合在一个数组中表示一次预览多页效果,可不同模板
printTemplate = {
reportlet: xmdUrl,
vatNum: vatNum,
billCode: billCode,
page: j * rowCount1VatNo
}
]
}
};
FR.doURLPDFPrint(printConfig);
官网关于分组换页的介绍在 https://help.fanruan.com/finereport/doc-view-333.html
填充空白行
下图是使用 FineReport 自带的功能实现填充空白行,该方法有问题(版本9、10都有,以后的版本没有使用过,不知道是否还存在该问题),如果第一列被设置了合并单元格并且数据确实出现了合并单元格的情况,此时被合并的多行被记做一行,会导致原本计算好的一页打印多少行不再准确。

所以可以在数据库层面在 sql 中填充空白数据行来达到每页打印指定数量的行,例如下面
# MYSQL
DROP PROCEDURE IF EXISTS `usp_autoFillFineReport`;
delimiter $
# 下面的 `root`@`%`表示允许任意机器上通过用户root使用本存储过程
CREATE DEFINER=`root`@`%` PROCEDURE `usp_autoFillFineReport`(IN mainId INT)
BEGIN
# 最终打印数据的行数
set @row_count = (select count(*) as counts from shipping_inspect_dtl where parent_id = mainId);
SET @remainder = @row_count % 15; # 每页打印15行数据
SET @rows_to_add = IF(@remainder < 0, 0, 15 - @remainder);# 需要填充的空白行的数量
# 手动制作了行号 rowNum
SET @sql = CONCAT('select @r:= @r + 1 AS rowNum,p.* from (select id,inspect01 from shipping_inspect_dtl where parent_id=', mainId,'
union all select * from (select null as id,null as inspect01 from mysql.help_topic limit ',@rows_to_add,') as aa) p,( SELECT @r:= 0 ) b');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END;$
在 FineReport 中只要制作普通的纵向报表即可,不需要使用 FineReport 提供的填充空白行功能。
分页最后一行被截断

安装与部署
两种打印预览方式
url传参
将参数拼接在 url 后面,代码如下
const url = `ReportServer?reportlet=cp_statement_summary_1.cpt&clientId=${clientId}&start=${start}&end=${end}&accountName=${accountName}`
window.open(url);
预览效果如下,这种方式可以导出为 excel 文件

如果拼接的 url 后面还有格式参数则也是直接预览不可导出
const url = `ReportServer?reportlet=cp_statement_summary_1.cpt&clientId=${clientId}&start=${start}&end=${end}&accountName=${accountName}&format=pdf`
FR调用打印方式
下面方式可以将多个报表一次性打印出来 - reportlets 是数组
let reportlets = [];
let reportlet = 'cp_statement_summary_2.cpt';
let printTemplate = {};
printTemplate.id = id;
printTemplate.remarks_id_list = checked.length > 0 ? checked.join(',') : 0;
printTemplate.current_remarks_id_list = localChecked.length > 0 ? localChecked.join(',') : 0;
printTemplate.reportlet = reportlet;
reportlets.push(printTemplate);
let config = {
url: "ReportServer",
isPopUp: false,
data: {
reportlets: reportlets
}
};
FR.doURLPDFPrint(config);
打印预览的效果如下,这种方式无法导出 excel 文件

引用时的相对路径和绝对路径
闵成金铭驰
2025年10月28日 欣铭晟、闵成、金铭驰,相继使用商贸系统,源码来自 xzy 系统,前后端单体项目,采用外置报表服务器的方式打印,引用报表 js 文件和打印的方法如下
<!--
项目访问地址:https://comm.barrel.fit/jmccomm,在前端 html 中如下导入报表 js 文件
报表服务器决策系统地址:https://comm.barrel.fit/barrelr/ReportServer
下面方式默认浏览器会自动拼接协议+域名:https://comm.barrel.fit
-->
<script src="/barrelr/ReportServer?op=emb&resource=finereport.js"></script>
<!-- js中打印如下 -->
// 当前页面全局变量,报表服务器路径+数据库名称
const reportServer = "/barrelr/ReportServer?dbname=fabric_trading_xdf";
// 打印报表
var reportlets=[];
const data = {reportlet: cdata.data + '.cpt',billCode:billCode, databaseName: fineReportDbName, groupType: 0};
reportlets.push(data);
FR.doURLPDFPrint({
url:reportServer,
isPopUp: false,
data: {
reportlets:reportlets
}
});
最早的引用方式
html 文件的绝对路径是:D:\barrel\frontend\main_gt\html\JCZL\jc_barrel.html 前端项目打开该基础资料的URL是:http://xzyerpserver/barrel/# 前端项目的主页文件在 D:\barrel\frontend 下 - 即前端项目的所有文件在该目录下,main_get 是一级目录 在该 html 中引入 Finereport 的 js 文件,使用相对路径是:
<script type="text/javascript" src="../../../../barrelr/ReportServer?op=emb&resource=finereport.js"></script>
使用绝对路径方式则是:
<script type="text/javascript" src="/barrelr/ReportServer?op=emb&resource=finereport.js"></script>
由上可知,绝对路径是从 URL 的域名开始,即从 http://xzyerpserver 向后拼接 相对路径则是从 html 所在目录出发,一直要回退到前端项目的根目录下
springboot 项目整合
见本站文章 https://www.yuque.com/chanchaw/pkb/cevvwq
安装程序与部署
从180的百度网盘:安装程序-> 软件开发 -> Java 相关 下载“FineReport9.0不限项目名不限点数”,同时旁边有个文件保存了使用设计器需要用到的激活码(f7d8b945-732d44d2c-87e3-cee441db2168) 报表服务是独立 jar (张柯的系统常用方法)的配置文件如下

要访问 FineReport 的后台管理页面的地址是:http://127.0.0.1:8083/xzyr/ReportServer
设置服务器连接
初次打开报表设计器,要设置服务器连接,目的是设置好连接到MYSQL的配置,按照下图的步骤设置,下面的用户名和密码填写登录MYSQL的用户名和密码

数据决策系统账号密码
目录:D:\Java\finereport9.0\WebReport\WEB-INF\resources 下的privilege.xml,将其删除然后启动JAVA后端项目,进入决策系统:http://localhost:7075/gsms/ReportServer 系统会要求填写账号密码并且重新创建该文件。
使用maven管理依赖包
由于远端仓库和中样仓库都没有 FineReport 的相关 jar 包,需要自己将本地的10个 jar 使用 maven 命令加入本地仓库供项目使用
详情
先通过命令:mvn -v 检测环境变量是否设置成功了(必须先做上面的环境变量注册才能使用 mvn 命令),之后通过命令(将本地 FineReport Jar 纳入本地仓库):
call mvn install:install-file -Dfile=D:\softWare\FineReport_9.0\WebReport\WEB-INF\lib\fr-core-9.0.jar -DgroupId=com.fr -DartifactId=fr-core -Dversion=9.0 -Dpackaging=jar
call mvn install:install-file -Dfile=D:\softWare\FineReport_9.0\WebReport\WEB-INF\lib\fr-report-9.0.jar -DgroupId=com.fr -DartifactId=fr-report -Dversion=9.0 -Dpackaging=jar
call mvn install:install-file -Dfile=D:\softWare\FineReport_9.0\WebReport\WEB-INF\lib\fr-third-9.0.jar -DgroupId=com.fr -DartifactId=fr-third -Dversion=9.0 -Dpackaging=jar
call mvn install:install-file -Dfile=D:\softWare\FineReport_9.0\WebReport\WEB-INF\lib\fr-performance-9.0.jar -DgroupId=com.fr -DartifactId=fr-performance -Dversion=9.0 -Dpackaging=jar
call mvn install:install-file -Dfile=D:\softWare\FineReport_9.0\WebReport\WEB-INF\lib\fr-platform-9.0.jar -DgroupId=com.fr -DartifactId=fr-platform -Dversion=9.0 -Dpackaging=jar
call mvn install:install-file -Dfile=D:\softWare\FineReport_9.0\WebReport\WEB-INF\lib\fr-chart-9.0.jar -DgroupId=com.fr -DartifactId=fr-chart -Dversion=9.0 -Dpackaging=jar
call mvn install:install-file -Dfile=D:\softWare\FineReport_9.0\WebReport\WEB-INF\lib\fr-datasource-9.0.jar -DgroupId=com.fr -DartifactId=fr-datasource -Dversion=9.0 -Dpackaging=jar
call mvn install:install-file -Dfile=D:\softWare\FineReport_9.0\WebReport\WEB-INF\lib\fr-designer-chart-9.0.jar -DgroupId=com.fr -DartifactId=fr-designer-chart -Dversion=9.0 -Dpackaging=jar
call mvn install:install-file -Dfile=D:\softWare\FineReport_9.0\WebReport\WEB-INF\lib\fr-designer-core-9.0.jar -DgroupId=com.fr -DartifactId=fr-designer-core -Dversion=9.0 -Dpackaging=jar
call mvn install:install-file -Dfile=D:\softWare\FineReport_9.0\WebReport\WEB-INF\lib\fr-designer-report-9.0.jar -DgroupId=com.fr -DartifactId=fr-designer-report -Dversion=9.0 -Dpackaging=jar
将本地 jar 导入 maven 库中,可以看到 -Dfile 后面是 jar 文件所在的绝对路径
中文乱码
打印预览中文字体
CentOS7 中默认是英文字体,并且没有 FineReport 中用到的中文字体,要先设置 CentOS7 系统为中文语言再拷贝中文字体

解决方案见 CentOS7使用中文字体
报表文件名乱码
如下图,如果报表文件名中有中文导致乱码,则需要使用 cjkEncode
let reportlet = 'cp_statement_summary_2.cpt';
printTemplate.accountName = encodeURIComponent(FR.cjkEncode(accountName || ''));
// 报表文件名或者数据内容的变量都使用该函数转换中文后再使用
使用案例见染厂 web 项目 \src\main\webapp\web\MainPages\pages_CPGL\CP_statement_summary.js 中的 btn_print
数据内容乱码
报表中部分单元格内中文可以正常,部分不行,原因在于服务器是 CentOS,并且设置了“单行显示(调整字体)”

一行显示不下时会自动缩小字体,这种情况下中文会显示为乱码,不要缩小则不会乱码
忘记后台决策系统密码
项目地址是 182.xxx.xxx.227:7072/white 那么对应的 FineReport 服务的地址是 182.xxx.xxx.227:7072/white/ReportServer,如果忘记登录密码,到下图中的路径中将文件 privilege.xml 删除,然后重启项目(重启地址 182.xxx.xxx.227:7072/white 的 jar 服务),就可以重新设置登录密码了
动态数据源
打印报表时传入数据库名称、登录用户名、密码等,多用于多租户类型的系统中,看下面文档 https://help.fanruan.com/finereport/doc-view-983.html
JS 调用打印
reportlets 要求是个数组,可以一次打印N多报表。其中对象的结构第一个参数 reportlet 是固定名称,之后的 SDate 和 EDate 是报表用到的参数,要求和模板文件中参数的大小写一致。
function btn_print() {
// $.messager.show({title: '提示', msg: '暂无打印模板'});
var reportlets = [
{
reportlet: 'getdyeingyield.cpt',
SDate:$('#start').datetimebox('getValue'),
EDate:$('#end').datetimebox('getValue')
}
];
//设置打印参数
var config = {
url: "ReportServer",
isPopUp: false,
data: {
reportlets: reportlets
}
};
//启动打印页
FR.doURLPDFPrint(config);
}
直接打印预览
使用 nginx 为后端 SpringBoot 项目做反向代理后前端打印报表页面点击“打印”后控制台出现下图的错误,引起该问题的前端代码是:
var strurl = "https://www.jzy.world/whiteyc/ReportServer?reportlet=white/yc/printfhd_1.cpt&billID="+m_FHID + "&format=pdf";
window.open(strurl);

网上找了不少帖子没有解决该问题,最后还是制作了直接打印预览,其中用到了 FineReport 的运行时 js (后端项目运行起来后存在服务器内存中的 js 文件),前端 jquery 项目引用的方法
<script type="text/javascript" src="https://www.jzy.world/whiteyc/ReportServer?op=emb&resource=finereport.js"></script>
执行打印的 js 代码如下
function printfhd() {
var beUrl = "https://www.jzy.world/whiteyc/ReportServer";
FR.doURLPDFPrint({url:beUrl,isPopUp:false,
data: {
reportlets: [{
billID:m_FHID,// 传递给报表的参数
// 如果报表文件在
//reportlet: 'white/yc/printfhd_1.cpt',
reportlet: 'printfhd.cpt',
}]}
})
}

多个后端项目共用一个FineReport目录
在制作织造企业管理系统时由于打算做多租户系统,本来打算多个后端项目共用一个 FineReport 目录,后来在登录决策系统时从第二个项目开始就登录不上(账号密码对的),后来每个后端项目对应一个目录就可以运行起来,例如后端项目 whitebr 对应 /usr/local/whiterpt/br (该目录下放 /usr/local/finereport/WebReport 下的目录和文件)另外一个项目 whiteyc 对应 /usr/local/whiterpt/yc
独立报表 jar 的 nginx 配置
概述
旭纸业项目和织造厂项目都采用了多 jar 包的方式部署,业务系统一个 jar ,FineReport 制作的报表一个 jar ,该 jar 只是提供运行 FineReport 必要的依赖,仍然需要将其他依赖的文件夹拷贝到指定的相对路径
白坯系统在云端的部署
云服务器有2T的数据盘,挂载在 /data/extdisk 目录下,所以将项目都方法在该目录下,为白坯系统创建的目录是 whiteaccount 目录,如下图:

文件 white-account-0.0.1.jar 是主业务系统,下面的 white-account-report.jar 是提供报表服务的 jar 包。目录 finereport 的文件结构如下

接下来看 nginx.conf 的配置,由于做了 http 自动跳转 https ,所以看下图的80端口设置有 rewrite ,不会影响报表的使用

在本端口下要创建报表系统的名称映射,如下图

注意要保证 jar 的项目名称也是 whiteaccountr ,不可为空!!! 另外还要设置 443 端口的域名映射

可以看到上面还为主业务系统也做了映射 whiteaccount
动态切换数据源
多租户模式的软件在打印报表时需要动态切换数据库(要求数据结构一致,否则可能出错),只要调整前端代码即可(前后分离项目中后端项目不需要修改代码),按照下面步骤:
- 通过 http://192.168.0.208/barrelr/ReportServer 进入报表服务设置页面,修改 url 连接,数据库名称不要硬编码,需要用到变量占位符,看下图。

即 url 写作:jdbc:mysql://192.168.0.208:3306/${dbname}?userUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useLegacyDatetimeCode=false&allowMultiQueries=true
- 前端打印的请求要在 url 后面接变量名称 dbname - 注意不可放在报表参数对象中,只能方法请求的 url 上
function print_detail() {
var reportlets=[];
const rptObj = {reportlet: barrelReportName,iid:34};
reportlets.push(rtpObj)
FR.doURLPDFPrint({
url:'../../../../barrelr/ReportServer?dbname=barrel002',
isPopUp: false,
data: {
reportlets:reportlets
}
});
update_detail_print(reportlets);
}
看上面代码行号6,最后的 dbname=barrel002 表示数据来自名称为 barrel002 的数据库,注意不要将参数 rptObj 写作下面的代码,这样是没用的。
const rptObj = {reportlet: barrelReportName,iid:34,dbname:'barrel002'};
同样的可以设置 IP 地址也是变动的,做法一样。本功能点的官网链接是: https://help.fanruan.com/finereport9.0/# FineReport 9.0 版本
错误与注意点
导出报错
使用 FineReport 导出数据时,浏览器打开了新的标签页,但是显示文件目录不存在,要检查目录 /tmp/poifiles 目录是否存在,创建后要赋予权限
chmod 777 -R /tmp/poifiles/
后台管理页面空白页
类似旭纸业、showa 的报表后台服务,启动后访问 http://localhost/showar/ReportServer 显示空白页面,查看浏览器控制台没有报错提示,可以到目录 D:\projs\xzy\report\WebReport\WEB-INF\logs 查看日志,注意要在浏览器端访问管理页面并显示为空白页后再来查看日志,会看到如下报错
17:12:34 http-nio-8083-exec-3 ERROR [root] Could not initialize class com.fr.license.function.VT4FR
java.lang.NoClassDefFoundError: Could not initialize class com.fr.license.function.VT4FR
at com.fr.web.core.ReportDispatcher.dealWithoutOp(Unknown Source)
at com.fr.web.core.ReportDispatcher.dealWeblet(Unknown Source)
at com.fr.web.core.ReportDispatcher.dealWithRequest(Unknown Source)
at com.fr.web.BaseServlet.doGet(Unknown Source)
搜索 deepseek 和通义千问都没有解决,后来发现是开发电脑切换为 jdk11 了,恢复为 jdk8 后可正常运行。和什么许可证都没有关系。
打印预览显示空白
按照下面步骤检查排除故障
- 检查前端代码中调用打印的模板文件的路径和文件名是否正确
- 测试同项目内其他打印是否正常,如正常说明是局部问题
- 检查打印模板中的
sql数据源,如果使用了存储过程确保服务器中存在并是最新,还要注意存储过程头上对于用户和主机的声明,注意如果是父子模式的打印模板要检查所有模板中的数据源
前端传递数据报表接受的不对
保证前端传递时对象的属性要和报表中参数的名称大小写完全一致
预览页面点击打印无效
在 FineReport 预览页面点击“打印”无效,如下图

查看控制台报错:

原因是 chrome 默认阻止在 iframe 中做显示新页面,更换 firefox 可以正常打印 在拉链项目中后台 springboot 项目内部集成了 FineReport 同时使用 nginx 做反向代理,整个项目是前后分离的模式。此种情况下调整 nginx 的配置文件,在 location 中添加行号8,9两行代码即可解决
location /zipperdye {
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://127.0.0.1:7075/zipperdye;
proxy_redirect http://127.0.0.1:7075/zipperdye /zipperdye;
proxy_cookie_path /zipperdye /zipperdye;
proxy_hide_header X-Frame-Options;
add_header X-Frame-Options "ALLOWALL";
}
解决该问题的文章是:https://discourse.metabase.com/t/cant-change-x-frame-options-nginx-reverse-proxy/14701
单张纸重复内容出现空白数据

Session timeout - 502 Bad Gateway

部署 CentOS 的两个问题
- 报表文件 (cpt文件)不可使用中文名称
- 使用相对路径,不可使用绝对路径: 在 windows 系统中可以通过下面两种方式引入 FineReport 的 JS 文件
<!-- 从当前 html 出发的相对路径 -->
<script type="text/javascript" src="../../../../barrelr/ReportServer?op=emb&resource=finereport.js"></script>
<!--
下面的方式表示从 url 的当前域名开始拼接,比如现在是:
http://101.34.89.88/barrel/#
下面的方法表示:
http://101.34.89.88/barrelr/ReportServer?op=emb&resource=finereport.js
-->
<script type="text/javascript" src="/barrelr/ReportServer?op=emb&resource=finereport.js"></script>
但是第二种形式不可用于 CentOS 中,该形式表示绝对路径,会导致找不到 FR 的 JS 文件
400 错误

导出异常
导出提示找不到文件,登录后台决策系统时提示日志超过500M,将报表目录下的 log 目录中的日志清空即可。管理后台中可以设置自动清理功能

