目录
子模版test_nodedatadetail.jrxml相关操作
java代码
NodeData.java
@Data
public class NodeDataInfo {
String createTime;
String nodeNo;
Double tempVal;
}
TestDataInfo.java
@Data
public class TestDataInfo {
//如果有多个交叉表、table,可以创建多个list,供不同的交叉表、table使用
private List<NodeDataInfo> nodeDataList;
private List<TestDataDetailInfo> nodeDetailList;
private List<NodeStatisticsInfo> nodeStaList;
}
/**
* 主数据源注入属性
*/
public List<TestDataInfo> getTestDataInfo() {
List<TestDataInfo> list = new ArrayList<>();
TestDataInfo info = new TestDataInfo();
info.setNodeDataList(getTestDataList("230109552"));
//多个list时,在此处一起全部赋值
list.add(info);
return list;
}
private List<TestDataDetailInfo> getTestDataDetailList(String projectNo) {
List<TestDataDetailInfo> list = new ArrayList<>();
List<TestDevice> deviceList = deviceService.getDeviceNoByProjectNo(projectNo);
if (CollectionUtils.isNotEmpty(deviceList)) {
for (int i = 0; i < deviceList.size(); i++) {
TestDevice testDevice = deviceList.get(i);
TestDataDetailInfo detailInfo = new TestDataDetailInfo();
detailInfo.setOrderNo(i + 1);
detailInfo.setNodeNo(testDevice.getDeviceNo().toString());
detailInfo.setNodeName(testDevice.getDeviceName());
detailInfo.setJobId(testDevice.getJobId());
detailInfo.setDeviceElectricity(testDevice.getDeviceElectricity() + "%");
list.add(detailInfo);
}
}
List<TestDataDetailInfo> collect = list.stream().sorted(Comparator.comparing(TestDataDetailInfo::getJobId)).collect(Collectors.toList());
return collect;
}
@GetMapping("/Test")
public void Test(HttpServletResponse response) throws Exception {
try {
String jasperPath = "classpath:/jaspers/Test/Test.jasper";
OutputStream os = response.getOutputStream();
response.setContentType("application/pdf; charset=utf-8");
response.setDateHeader("Expires", 0);
// 加载主模板
JasperReport jasperReport = (JasperReport) JRLoader.loadObject(resourceLoader.getResource(jasperPath).getInputStream());
// 获取主模板的路径
String subPath = resourceLoader.getResource("classpath:/jaspers/Test/").getURL().toString();
// 构造数据源
List<TestDataInfo> TestDataInfoList = getTestDataInfo();
JRBeanCollectionDataSource dataFactory = new JRBeanCollectionDataSource(TestDataInfoList);
HashMap<String, Object> hashMap = new HashMap<>(16);
TestProject project = projectService.getById("1612287936136671234");
// 这里map中的key就是你模板中变量的名称 一一对应
hashMap.put("subPath", subPath);
hashMap.put("dataFactory", dataFactory);
JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, hashMap, new JREmptyDataSource());
// 页面预览pdf
JasperExportManager.exportReportToPdfStream(jasperPrint, os);
} catch (Exception e) {
e.printStackTrace();
}
}
jasper文件在springboot项目中的存放位置
JasperReport相关操作:
在test.jrxml中先创建好子报表中使用的数据参数
1、在主模板test.jrxml中的Parameters中创建subPath,用于给找到子报表文件及子报表通过路径找文件。
2、在主模板test.jrxml中的Parameters中创建dataFactory,class为net.sf.jasperreports.engine.data.JRBeanCollectionDataSource。
新建子模板:
右击test文件夹 -> new -> jasper report -> 选择一个Blank A4即可 表示空模板,我们自己填充内容 -> 命名随便我这里叫test_nodedatadetail.jrxml
在主模板test.jrxml中的Content空白处右键添加一个book part,如下图
设置下模板的路径,否则放到项目中会提示找不到模板。路径设置为:$P{subPath}+"test_nodedatadetail.jasper"
**设置子模版数据源:** 数据源使用上面创建的dataFactory参数
设置其他非数据源传递参数,子模版可直接从主模板的paramters中读取数据,前提是需要事先配置对应起来,如下图。主模板中的subPath传递给子模版的subPath使用(注意此时尚未在子模版创建subPath,后续需要在子模版的paramters中创建上subPath。dataFactory通过数据源传递过去了,所以不需要在子模板的paramters中创建dataFatory参数)。
子模版test_nodedatadetail.jrxml相关操作
1、在子模板test_nodedatadetail.jrxml中的Parameters中创建subPath。
2、在Fields中创建Field参数,此处创建的是针对交叉表使用的数据源参数创建。同样右键Fields->Create Field->Name:nodeDataList->class:java.util.List。
3、创建数据集合:
右键子模版,然后选择Create Dataset。
在新创建的dataset中的Fields中创建要展示数据的字段createTime、nodeNo、tempVal,都是String。
4、创建交叉表
在TIBCO工具的右上角的Palette中找到Crosstab,并把它拖到子模版的Detail中去。
看右上角,表示你横向列头显示的内容,我们选择nodeNo,选择后点击>或双击nodeNo都可以,会发现nodeNo移动到了后面的区域,默认Order表示升序方式;Total Position表示总计的位置,我们不需要总计所以选择none;然后点击next下一步:
这一次表示纵向的列头,显示的是时间,所以我们选择createTime,双击即可,不需要总计,total position设置为none,同第二步;点击next下一步:
表示主体内容,选择人数tempVal,这里的calculation表示计算方式,我们只需要展示人数,所以我们不计算,选择No calculation,表示不计算只显示数据,点击next下一步:
创建完成后,调整下大小,双击进入交叉表设置
创建Header后可以将Text Field拖到马赛克的位置,不创建无法拖进去
**(注意坑)** 可能存在比如,框内数据很长,给的框框空间不够咋办? jasperreport提供了自适用高度,当长度不够的时候,会自动拉伸高度,达到换行的效果。选中gender,grade,value同时设置属性
包括Stretch Type 自动拉伸;
Detail Overflows 超过一页的时候会自动填写列头;
Text Adjust 自适用行高;
Blank when null 当你的交叉表中存在null值的时候,会自动以空白处理,当然有些时候,可能你的表格会用“–”表示,主动赋予值也是可以的,反正这个打钩没坏处,不然你的值会以null显示在表格中,很难看!
**(注意坑)** 交叉表取消默认排序规则
这个功能也很重要,比如你的数据在list中是一个顺序,但是交叉表默认给你的是按照某个字段的升序排列的,例如你的数据插入顺序是张三李四,但是交叉表默认升序给你变成了李四张三,所以我们就想要按照默认list的顺序进行展示,怎么办?可以设置取消交叉表的默认升序排序,改为默认不排序!
在交叉表页面选择Crosstab展开,选中你的列头属性,右侧可以看到有个order,默认是升序Ascending,改成none就ok了,表示不排序。createTime和nodeNo可以都改动一下。
**注意点:** 你改完了之后,横向和纵向都会取消排序,如果你的横向是一些月份啥的,纵向比如是一些年份啥的,这种的就要求你在进行数据处理的时候需要按照月份或者年份顺序进行赋值,不然顺序肯定会混;所以需不需要取消排序效果也是根据实际情况判断的哦,这里我们都改一下默认不排序,给大家展示效果。
交叉表的数据源注入
交叉表需要一个数据源用于展示(其实就是一个数据对象),交叉表的注入对象格式是固定的,将对象包装成模板可以识别的模型就可以了,这里注入的变量其实就需要用到它的父级模板中dataFactory的nodeDataList 变量了,达到传递的效果, 因为变量默认是list类型,所以我们需要转换成交叉表识别的格式:**new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($F{nodeDataList})** 。其中的nodeDataList是传过来的dataFactory中的List\<VevoDataInfo\>,VevoDataInfo里面的属性private List\<NodeDataInfo\> nodeDataList;。
如果是主模板中使用table表格,使用数据源时可直接从主模板的paramters中获取,如再hashmap.put("nodeDataList", list);。dataset的格式变为:new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($<font color="#ff0000">P</font>{nodeDataList}) 。
点击detail中的交叉表,右侧选择dataset注入数据集,选择Use a JRDatasource expression
最终结果:
欢迎交流:Q:335381879 W:18660409166