easyExcel 填充写时,动态合并单元格

1、定义合并单元格策略获取方法
/** * @description: 获取第二个表格的合并策略* @param secondTablelist  * @return: java.util.List<com.alibaba.excel.write.merge.OnceAbsoluteMergeStrategy> * @author 30864* @date: 2024/8/24 20:27*/ public static List<OnceAbsoluteMergeStrategy> getSecondMergeStrategy(List<ProjectContractExpenditureStatisticDto> secondTablelist) {int baseRowIndex = 3;//合并开始行int startRowIndex = 0;//合并结束行int endRowIndex = 0;//合并开始列int colStartIndex = 0;//合并结束列int colEndIndex = 0;List<OnceAbsoluteMergeStrategy> strategyList = new ArrayList<>();//       取出string列的数据List<String> contractNoList = secondTablelist.stream().map(ProjectContractExpenditureStatisticDto::getContractNo).collect(Collectors.toList());//       前后添加两个不计入合并的元素 , 类似链表中哨兵,简化索引越界的判断contractNoList.add(0, "开始节点不计入合并");contractNoList.add(contractNoList.size(), "结束节点不计入合并");List<Integer> mergeColIndexList = Arrays.asList(0,1,2,3,4,5,6,13,14,15);for (int index = 1; index < contractNoList.size() - 1; index++) {// 当前的元素和数组中前一个元素是否相等boolean equalsPrevious = Objects.equals(contractNoList.get(index), contractNoList.get(index - 1));// 当前的元素和数组中后一个元素是否相等boolean equalsAfter = Objects.equals(contractNoList.get(index), contractNoList.get(index + 1));
//            如果找到一个元素和不等于前面,但是等于后面,则这个元素的索引就是一个OnceAbsoluteMergeStrategy的开始行if (!equalsPrevious && equalsAfter) {startRowIndex = index;//            如果找到一个元素和等于前面,但是不等于后面,则这个元素的索引就是一个OnceAbsoluteMergeStrategy的结束结束行} else if (equalsPrevious && !equalsAfter) {endRowIndex = index;// 无合同号,不进行合并if (StringUtils.equals(secondTablelist.get(startRowIndex).getContractName(), Constansts.NOT_HAS_CONTRACT_CONTENT) ){continue;}
//              创建单个合并策略,并添加到策略列表里面去for (int colIndex : mergeColIndexList) {OnceAbsoluteMergeStrategy strategy = new OnceAbsoluteMergeStrategy(baseRowIndex + startRowIndex, baseRowIndex + endRowIndex, colIndex, colIndex);strategyList.add(strategy);}}}return strategyList;}

解释: 需求:根据合同编码判断是否要合并,相同合同合并单元格

1、根据待写入数据记录的集合list, 获取合同编号集合

2、取当前记录及其前一条记录,判断当前记录的合同号  = 前一条记录合同号 ?

如果不相等,则当然前记录就是待合并单元格的起始行

3、取当前记录及其后一条记录,判断当前记录的合同号 = 后一条记录合同号 ? 

如果不相等,则当前记录就是待合并单元格的结束行

4、起始结尾行号确定了,需要确定起始结尾的列号,由于每列内容不一样,所以合同属性涉及的列号,都要一一合并,(这里涉及的列号 List<Integer> mergeColIndexList=Arrays.asList(0,1,2,3,4,5,6,13,14,15);)所以这里把涉及的列号定义了一个集合,然后其中的每一列都按照 起始行、结束行去合并。

2、写入时将合并策略注册到ExcelWriterSheetBuilder
/*** @param project* @description: 导出项目* @return: void* @author 30864* @date: 2024/8/1 9:05*/@Overridepublic void export(Project project, HttpServletResponse response) throws IOException {Project dbProject = projectMapper.selectProjectByNo(project.getProjectNo());String fileName = String.format("%s【%s】报告", dbProject.getProjectName(), dbProject.getProjectNo()) + System.currentTimeMillis() + ".xlsx";response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setCharacterEncoding("utf-8");// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);OutputStream os = response.getOutputStream();String templateFileName = "template/template-project-report-new.xlsx";InputStream templateFileIos = getClass().getClassLoader().getResourceAsStream(templateFileName);if (null == templateFileIos) {throw new ServiceException("找不到模板文件");}try (ExcelWriter excelWriter = EasyExcel.write(os).withTemplate(templateFileIos).build()) {ExcelWriterSheetBuilder excelWriterSheetBuilder1 =   EasyExcel.writerSheet("项目执行情况-汇总");ExcelWriterSheetBuilder excelWriterSheetBuilder2 =   EasyExcel.writerSheet("项目执行情况-明细");ExcelWriterSheetBuilder excelWriterSheetBuilder3 =   EasyExcel.writerSheet("项目执行情况-审定数据");StatisticFirstTableDataDto firstTableDataDto = firstTableData(project);StatisticSecondTableDataDto secondTableDataDto = secondTableData(project);StatisticThirdTableDataDto thirdTableDataDto = thirdTableData(project);// 第二个表格添加合并单元格策略List<OnceAbsoluteMergeStrategy> secondMergeStrategyList = getSecondMergeStrategy(secondTableDataDto.getExpenditureList());for (OnceAbsoluteMergeStrategy secondMergeStrategy : secondMergeStrategyList ) {excelWriterSheetBuilder2.registerWriteHandler(secondMergeStrategy);}// 第三个表格添加合并单元格策略List<OnceAbsoluteMergeStrategy> thirdMergeStrategyList = getThirdMergeStrategy(thirdTableDataDto.getAuditList());for (OnceAbsoluteMergeStrategy thirdMergeStrategy : thirdMergeStrategyList ) {excelWriterSheetBuilder3.registerWriteHandler(thirdMergeStrategy);}WriteSheet writeSheet1 = excelWriterSheetBuilder1.build();WriteSheet writeSheet2 = excelWriterSheetBuilder2.build();WriteSheet writeSheet3 = excelWriterSheetBuilder3.build();/** 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。* 默认 是false,会直接使用下一行,如果没有则创建。 forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用 */// 如果有多个list 模板上必须有{前缀.} 这里的前缀就是 totalData,warnList 然后多个list必须用 FillWrapper包裹// HORIZONTAL 横向填充 , VERTICAL 纵向填充// 第一个表格合同维度列表统计FillConfig contractListConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).direction(WriteDirectionEnum.VERTICAL).build();excelWriter.fill(new FillWrapper("firstTableData", firstTableDataDto.getContractList()), contractListConfig, writeSheet1);// 第二个表格支出维度,列表统计FillConfig secordTableConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).direction(WriteDirectionEnum.VERTICAL).build();excelWriter.fill(new FillWrapper("secondTableData", secondTableDataDto.getExpenditureList()), secordTableConfig, writeSheet2);// 第三个表格概算类型维度,列表统计FillConfig thirdTableConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).direction(WriteDirectionEnum.VERTICAL).build();excelWriter.fill(new FillWrapper("thirdTableData", thirdTableDataDto.getAuditList()), thirdTableConfig, writeSheet3);// 填充其他非列表数据Map<String, Object> data = new HashMap<>();// 项目名称data.put("projectName", dbProject.getProjectName());data.put("projectNo", dbProject.getProjectNo());// 第一个表格总计data.put("firstTableTotal_totalPayMoney", firstTableDataDto.getTotalPayMoney());data.put("firstTableTotal_totalUnpaidAmount", firstTableDataDto.getTotalUnpaidAmount());// 第二个表格总计data.put("secondTableTotal_totalPayMoney", secondTableDataDto.getTotalPayMoney());data.put("secondTableTotal_unpaidAmount", secondTableDataDto.getUnpaidAmount());// 第三个表格总计data.put("thirdTableTotal_totalPayMoney", thirdTableDataDto.getTotalPayMoney());data.put("thirdTableTotal_unpaidAmount", thirdTableDataDto.getUnpaidAmount());data.put("thirdTableTotal_estimateAmount", thirdTableDataDto.getEstimateAmount());excelWriter.fill(data, writeSheet1);excelWriter.fill(data, writeSheet2);excelWriter.fill(data, writeSheet3);excelWriter.finish();}}

解释: 这里涉及三个sheet的写入,只需关注 excelWriterSheetBuilder2 即可

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/52371.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

柔版印刷版市场前景:预计2030年全球市场规模将达到20.9亿美元

一、当前市场状况 目前&#xff0c;柔版印刷版市场呈现出较为稳定的发展态势。随着全球经济的逐步复苏&#xff0c;包装印刷等领域对柔版印刷版的需求持续增长。柔版印刷版具有环保、高效、印刷质量高等特点&#xff0c;在食品包装、标签印刷等行业中得到广泛应用。 全球前四…

FlinkCEP - Flink的复杂事件处理详解

目录 Pattern API 单独模式&#xff08;Individual Patterns&#xff09; 量词&#xff08;Quantifiers&#xff09; 条件&#xff08;Conditions&#xff09; 迭代条件 简单条件 组合条件 停止条件 组合模式&#xff08;Combining Patterns&#xff09; 模式组&…

网上商城|基于SprinBoot+vue的分布式架构网上商城系统(源码+数据库+文档)

分布式架构网上商城系统 目录 基于SprinBootvue的分布式架构网上商城系统 一、前言 二、系统设计 三、系统功能设计 5.1系统功能模块 5.2管理员功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍…

微信小程序flex-grow无效

在微信小程序中使用 Flexbox 布局时&#xff0c;如果遇到 flex-grow 属性无效的情况&#xff0c;可能是由几个不同的原因导致的。以下是一些可能的原因和解决方案&#xff1a; 1. 检查 Flex 容器 确保你的父元素&#xff08;即 Flex 容器&#xff09;已经正确设置了 display:…

每期一个小窍门 golang中 CGO_ENABLED 含义

转载 CGO_ENABLED是一个环境变量&#xff0c;用于控制Go编译器是否启用CGO&#xff08;C语言调用Go函数&#xff09;功能。当CGO_ENABLED0时&#xff0c;表示禁用CGO功能。 编译时使用CGO_ENABLED0会导致编译速度较慢的原因有以下几点&#xff1a; 编译器需要额外的时间来检…

时间继电器和定时器

一、概述 1.时间继电器是可以在设定的定时周期内或周期后闭合或断开触点的元器件。 2.时间继电器上可设定的定时周期数量有限&#xff0c;多为一个或两个。定时时长从0.02s至300h(根据产品型号范围不同)。 3.定时器可以理解为一台钟表&#xff0c;它在某个时间点上闭合(断开…

PostgreSQL11 | 事务处理与并发控制

PostgreSQL11 | 事务处理与并发控制 本文章代码已在pgsql11.22版本上运行且通过&#xff0c;展示页由pgAdmin8.4版本提供&#xff0c;本文章第一次采用md文档&#xff0c;效果比csdn官方富文本编辑器好用&#xff0c;以后的文章都将采用md文档 事务管理简介 事物是pgsql中的…

三种相机模型总结(针孔、鱼眼、全景)

相机标定 文章目录 相机标定前言 前言 我们最常见的投影模型Perspective Projection Model描述的就是针孔相机的成像原理。从上面的图根据相似三角形可以得出 参考链接 https://zhuanlan.zhihu.com/p/540969207 相机标定之张正友标定法数学原理详解&#xff08;含python源码&a…

上线eleme项目

&#xff08;一&#xff09;搭建主从从数据库 主服务器master 首先下载mysql57安装包&#xff0c;然后解压 复制改目录到/usr/local底下并且改个名字 cp -r mysql-5.7.44-linux-glibc2.12-x86_64 /usr/local/mysql 删掉/etc/my.cnf 这个会影响mysql57的启动 rm -rf /etc…

解读vue3源码-响应式篇3 effect副作用函数

提示&#xff1a;看到我 请让我滚去学习 文章目录 前言effect问题拓展分支切换与 cleanup嵌套的 effect 与 effect 栈解决在副作用函数中同时读取和操作同一属性时无限循环 effect函数实现computed-api 实现图解在这里插入图片描述 总结 前言 什么是副作用函数&#xff1f; 在…

SCYC 56901传感器SCYC 56901模块面价

SCYC 56901传感器SCYC 56901模块面价 SCYC 56901传感器SCYC 56901模块面价 SCYC 56901传感器SCYC 56901模块面价 SCYC 56901传感器SCYC 56901模块引脚线 SCYC 56901传感器SCYC 56901模块说明书 SCYC 56901传感器SCYC 56901模块电路图 SCYC 56901温度传感器是早开发&#…

iPhone 手机使用技巧:iPhone 数据恢复软件

无论是由于意外删除、系统崩溃还是软件更新&#xff0c;丢失 iPhone 上的数据都是一场噩梦。从珍贵的照片到重要的工作文件&#xff0c;这种损失可能会让人感到毁灭性。值得庆幸的是&#xff0c;几个 iPhone 数据恢复软件选项可以帮助您找回丢失的文件。这些工具提供不同的功能…

神经网络——非线性激活

1 非线性激活 1.1 几种常见的非线性激活&#xff1a; ReLU (Rectified Linear Unit)线性整流函数 Sigmoid 1.2代码实战&#xff1a; 1.2.1 ReLU import torch from torch import nn from torch.nn import ReLUinputtorch.tensor([[1,-0.5],[-1,3]])inputtorch.reshape(…

刷题DAY18

排序 题目&#xff1a;输入10个大小不同的整数&#xff0c;将它们从小到大排序后输出&#xff0c;并给出现每个元素在原来序列中的位置 输入&#xff1a;输入数据有一行&#xff0c;包含10个整数&#xff0c;用空格分开 输出&#xff1a;输出数据有两行&#xff0c;第一行为…

【计算机网络】名词解释--网络专有名词详解

在网络通信中&#xff0c;有许多专业术语和概念&#xff0c;它们共同构成了网络通信的基础。以下是一些常见的网络术语及其定义和相互之间的关系&#xff1a; 一、网络基础 1.1 电路交换&#xff1a;电路交换是一种在数据传输前建立专用通信路径的通信方式。在通信开始前&…

HTML+CSS+JavaScript制作动态七夕表白网页(含音乐+自定义文字)

源码介绍 这篇博客就享下前端代码如何实现HTMLCSSJavaScript制作七夕表白网页(含音乐自定义文字)。记事本打开源码文件可以进行内容文字之类的修改&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器里面&#xff0c;重定向这个界面 源码效果 源码下载…

Django后端架构开发:缓存机制,接口缓存、文件缓存、数据库缓存与Memcached缓存

深入探讨Django后端架构中的缓存机制&#xff1a;接口缓存、文件缓存、数据库缓存与Memcached缓存 目录 &#x1f31f; 缓存接口数据的实现✨ Django文件缓存的应用⚡ 关系型数据库缓存的策略&#x1f4a0; Memcached缓存的配置与优化 &#x1f31f; 缓存接口数据的实现 在D…

前端开发工程师面试整理-ES6+的新特性

ES6(ECMAScript 2015)及后续版本引入了许多新特性,极大地增强了JavaScript的功能和开发体验。以下是一些主要的新特性: 变量声明 1. let 和 const: ● let 声明块作用域变量。 ● const 声明常量,值不能重新赋值。 ● 示例:

从行为面试问题(behavioral questions)看中美程序员差异。

中美程序员在职场中的工作状态和职能、福利等有很大区别&#xff0c;从面试中的BQ轮就可见一斑。 中美程序员的面试轮差异&#xff1f; 国内的面试轮在不同公司间差异很大&#xff0c;但总体的问题类型包含笔试面试&#xff08;算法题、概念题、项目深挖、职业目标、职场文化…

混合现实UI优化:利用物理环境的直接交互

随着虚拟现实(VR)和混合现实(MR)技术的发展,用户界面(UI)的设计变得越来越重要,尤其是在需要适应多种物理环境的情况下。本文将介绍一种名为 InteractionAdapt 的用户界面优化方法,它专为VR环境中的工作空间适配而设计,能够有效利用物理环境,为用户提供更加灵活和个…