EasyExcel复杂Excel导出

article/2025/8/7 5:53:09

效果图展示

在这里插入图片描述

1、引入依赖

<!-- easyExcel -->
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>4.0.2</version>
</dependency>

2、实体类

import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;/*** @Author: Harris* @Date: 2025/5/29* @Description:**/
@Data
public class AbroadDeptCommBo {@ApiModelProperty(value = "单位名称")@ExcelProperty(value = {"单位名称", "单位名称", "单位名称"})private String unitName;@ApiModelProperty(value = "线路类型-国际")@ExcelProperty(value = {"通信情况", "线路情况", "线路类型"})private String lineTypeInternational;@ApiModelProperty(value = "速率-国际")@ExcelProperty(value = {"通信情况", "线路情况", "速率"})private String rateInternational;@ApiModelProperty(value = "数量-国际")@ExcelProperty(value = {"通信情况", "线路情况", "数量"})private String quantityInternational;@ApiModelProperty(value = "备份方式-国际")@ExcelProperty(value = {"通信情况", "线路情况", "备份方式"})private String backupTypeInternational;@ApiModelProperty(value = "数量-国际")@ExcelProperty(value = {"通信情况", "站数", "站数"})private String cmacastReceiveNumInternational;@ApiModelProperty(value = "线路类型-国内")@ExcelProperty(value = {"通信情况", "线路情况", "线路类型"})private String lineTypeDomestic;@ApiModelProperty(value = "速率-国内")@ExcelProperty(value = {"通信情况", "线路情况", "速率"})private String rateDomestic;@ApiModelProperty(value = "数量-国内")@ExcelProperty(value = {"通信情况", "线路情况", "数量"})private String quantityDomestic;@ApiModelProperty(value = "备份方式-国内")@ExcelProperty(value = {"通信情况", "线路情况", "备份方式"})private String backupTypeDomestic;@ApiModelProperty(value = "站数量-国内")@ExcelProperty(value = {"通信情况", "站数", "站数"})private String cmacastReceiveNumDomestic;
}

3、excel 生成

@Testpublic void exportExcel() {String fileName = "./data/tmp/out.xlsx";WriteCellStyle writeCellStyle = getWriteCellStyle();//头策略使用默认WriteCellStyle headWriteCellStyle = new WriteCellStyle();List<AbroadDeptCommBo> dataList = new ArrayList<>(10);dataList.add(initData("中心"));dataList.add(initData("中心"));dataList.add(initData("中心"));dataList.add(initData("集团"));dataList.add(initData("集团"));dataList.add(initData("集团"));try (ExcelWriter build = EasyExcel.write(fileName).build()) {WriteSheet sheet0 = EasyExcel.writerSheet(0, "sheet0").head(AbroadDeptCommBo.class)//设置拦截器或自定义样式.registerWriteHandler(new HorizontalCellStyleStrategy(headWriteCellStyle, writeCellStyle)).registerWriteHandler(new ExcelMergeHandler(3, new int[]{0})).useDefaultStyle(true).build();build.write(dataList, sheet0);WriteSheet sheet1 = EasyExcel.writerSheet(1, "sheet1").head(AbroadDeptCommBo.class)//设置拦截器或自定义样式.registerWriteHandler(new HorizontalCellStyleStrategy(headWriteCellStyle, writeCellStyle)).registerWriteHandler(new ExcelMergeHandler(3, new int[]{0})).useDefaultStyle(true).build();build.write(dataList, sheet1);build.finish();} catch (Exception e) {// TODO catch block}}/*** 单元格样式设置* * @return WriteCellStyle*/private static WriteCellStyle getWriteCellStyle() {WriteCellStyle writeCellStyle = new WriteCellStyle();writeCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);writeCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);writeCellStyle.setBorderLeft(BorderStyle.THIN);writeCellStyle.setBorderTop(BorderStyle.THIN);writeCellStyle.setBorderRight(BorderStyle.THIN);writeCellStyle.setBorderBottom(BorderStyle.THIN);//设置 自动换行writeCellStyle.setWrapped(true);// 字体策略WriteFont contentWriteFont = new WriteFont();// 字体大小contentWriteFont.setFontHeightInPoints((short) 12);writeCellStyle.setWriteFont(contentWriteFont);return writeCellStyle;}/*** 初始化数据* * @param unitName 单位名称* @return AbroadDeptCommBo*/private static AbroadDeptCommBo initData(String unitName) {AbroadDeptCommBo abroadDeptCommBo = new AbroadDeptCommBo();abroadDeptCommBo.setUnitName(unitName);abroadDeptCommBo.setLineTypeInternational("1");abroadDeptCommBo.setRateInternational("1");abroadDeptCommBo.setQuantityInternational("1");abroadDeptCommBo.setBackupTypeInternational("1");abroadDeptCommBo.setCmacastReceiveNumInternational("1");abroadDeptCommBo.setLineTypeDomestic("1");abroadDeptCommBo.setRateDomestic("1");abroadDeptCommBo.setQuantityDomestic("1");abroadDeptCommBo.setBackupTypeDomestic("1");abroadDeptCommBo.setCmacastReceiveNumDomestic("1");return abroadDeptCommBo;}

合并单元格拦截器(纵向合并)


import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;import java.util.List;/*** @Author: Harris* @Date: 2025/5/30* @Description:**/
public class ExcelMergeHandler implements CellWriteHandler {// 要合并的列索引数组private final int[] mergeColumnIndex;// 合并开始的行索引private final int mergeRowIndex;/*** 构造函数** @param mergeRowIndex     合并开始的行索引* @param mergeColumnIndex  要合并的列索引数组*/public ExcelMergeHandler(int mergeRowIndex, int[] mergeColumnIndex) {this.mergeRowIndex = mergeRowIndex;this.mergeColumnIndex = mergeColumnIndex;}@Overridepublic void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {// 单元格创建前的处理(这里不需要处理)}@Overridepublic void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {// 单元格创建后的处理(这里不需要处理)}@Overridepublic void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {// 当前行索引int curRowIndex = cell.getRowIndex();// 当前列索引int curColIndex = cell.getColumnIndex();// 如果当前行大于合并开始行且当前列在需要合并的列中if (curRowIndex > mergeRowIndex && isMergeColumn(curColIndex)) {// 进行合并操作mergeWithPrevRow(writeSheetHolder, cell, curRowIndex, curColIndex);}}/*** 检查当前列是否在需要合并的列中** @param curColIndex 当前列索引* @return 如果是需要合并的列返回true,否则返回false*/private boolean isMergeColumn(int curColIndex) {for (int columnIndex : mergeColumnIndex) {if (curColIndex == columnIndex) {return true;}}return false;}/*** 当前单元格向上合并** @param writeSheetHolder 当前工作表持有者* @param cell             当前单元格* @param curRowIndex      当前行索引* @param curColIndex      当前列索引*/private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) {// 获取当前单元格的数据Object curData = getCellData(cell);// 获取前一个单元格的数据Cell preCell = cell.getSheet().getRow(curRowIndex - 1).getCell(curColIndex);Object preData = getCellData(preCell);// 判断当前单元格和前一个单元格的数据以及主键是否相同if (curData.equals(preData) && isSamePrimaryKey(cell, curRowIndex)) {// 获取工作表Sheet sheet = writeSheetHolder.getSheet();// 合并单元格mergeCells(sheet, curRowIndex, curColIndex);}}/*** 获取单元格的数据** @param cell 单元格* @return 单元格数据*/private Object getCellData(Cell cell) {return cell.getCellType() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();}/*** 判断当前单元格和前一个单元格的主键是否相同** @param cell         当前单元格* @param curRowIndex  当前行索引* @return 如果主键相同返回true,否则返回false*/private boolean isSamePrimaryKey(Cell cell, int curRowIndex) {String currentPrimaryKey = cell.getRow().getCell(0).getStringCellValue();String previousPrimaryKey = cell.getSheet().getRow(curRowIndex - 1).getCell(0).getStringCellValue();return currentPrimaryKey.equals(previousPrimaryKey);}/*** 合并单元格** @param sheet        工作表* @param curRowIndex  当前行索引* @param curColIndex  当前列索引*/private void mergeCells(Sheet sheet, int curRowIndex, int curColIndex) {// 获取已合并的区域List<CellRangeAddress> mergeRegions = sheet.getMergedRegions();boolean isMerged = false;// 检查前一个单元格是否已经被合并for (int i = 0; i < mergeRegions.size() && !isMerged; i++) {CellRangeAddress cellRangeAddr = mergeRegions.get(i);if (cellRangeAddr.isInRange(curRowIndex - 1, curColIndex)) {sheet.removeMergedRegion(i);cellRangeAddr.setLastRow(curRowIndex);sheet.addMergedRegion(cellRangeAddr);isMerged = true;}}// 如果前一个单元格未被合并,则新增合并区域if (!isMerged) {CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex - 1, curRowIndex, curColIndex, curColIndex);sheet.addMergedRegion(cellRangeAddress);}}
}

最主要的方法其实就是下面这段代码,可以通过这段合并任意单元格

CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex - 1, curRowIndex, curColIndex, curColIndex);
sheet.addMergedRegion(cellRangeAddress);

参考文档:
https://www.cnblogs.com/better-farther-world2099/articles/16106085.html
https://blog.csdn.net/ManGooo0/article/details/128094925


http://www.hkcw.cn/article/CHYYCRCoMa.shtml

相关文章

云服务器系统日志占满磁盘怎么办?

云服务器系统日志占满磁盘是常见问题&#xff0c;长期积累大量日志文件会导致磁盘空间不足&#xff0c;影响服务器稳定运行。下面是几种常用的清理和管理日志的方法&#xff1a; 解决系统日志占满磁盘的步骤 1. 查看磁盘使用情况和日志目录大小 df -h # 查看磁盘空间使用情况 …

vueflow

自定义节点&#xff0c;自定义线&#xff0c;具体细节还未完善&#xff0c;实现效果&#xff1a; 1.安装vueflow 2.目录如下 3. index.vue <script setup> import { ref } from vue import { VueFlow, useVueFlow } from vue-flow/core import { Background } from vue-…

2022 RoboCom 世界机器人开发者大赛-本科组(省赛)解题报告 | 珂学家

前言 题解 2022 RoboCom 世界机器人开发者大赛-本科组&#xff08;省赛&#xff09;。 感觉T5是最简单的&#xff0c;其他都不好做。 RC-u5 树与二分图 分值: 30分 思路: 容斥原理 树天然就是二分图&#xff0c;按深度d归类(偶数深度为S1&#xff0c;奇数深度为S2)&#x…

framework之慕课大巴

8.Android启动课程大纲_哔哩哔哩_bilibili

redis未授权(CVE-2022-0543)

概述 Redis 默认绑定在 0.0.0.0:6379&#xff0c;在未配置防火墙或访问控制的情况下会将服务暴露在公网上。若未设置访问密码&#xff08;默认通常为空&#xff09;&#xff0c;攻击者可直接未授权访问 Redis。利用 Redis 提供的 CONFIG 命令&#xff0c;攻击者可修改配置并将…

帕金森常见情况解读

一、身体出现的异常节奏​ 帕金森会让身体原本协调的 “舞步” 出现错乱。它是一种影响身体行动能力的状况&#xff0c;随着时间推进&#xff0c;就像老旧的时钟&#xff0c;齿轮转动不再顺畅&#xff0c;使得身体各个部位的配合逐渐失衡&#xff0c;打乱日常行动的节奏。​ …

[正点原子]ESP32S3 RGB屏幕移植LVGL

ESP32S3 RGB屏幕移植lvgl 简介准备工作移植过程创建文件加修改配置修改适配文件main函数lvgl的图形化配置 着重要注意的屏幕驱动的问题 效果展示 简介 最近入手了 正点原子ESP32开发版准备学习LVGL&#xff0c;该板子支持RGB屏幕RGB565&#xff0c;之前买Linux开发板的时候有一…

移动安全Android——客户端静态安全

一、反编译保护 测试工具 Jadx GitHub - skylot/jadx: Dex to Java decompiler PKID [下载]PKID-APP查壳工具-Android安全-看雪-安全社区|安全招聘|kanxue.com 测试流程 &#xff08;1&#xff09;通过Jadx对客户端APK文件进行反编译&#xff0c;观察是否进行代码混淆 &…

支持功能安全ASIL-B的矩阵管理芯片IS32LT3365,助力ADB大灯系统轻松实现功能安全等级

随着自动驾驶技术的快速发展&#xff0c;汽车前灯智能化也越来越高。自适应远光灯 (ADB) 作为一种智能照明系统&#xff0c;在提升驾驶安全性和舒适性方面发挥着重要作用。ADB 系统通过摄像头和传感器获取前方道路信息&#xff0c;例如来车的位置、距离和速度&#xff0c;并根据…

Python训练营打卡Day40

DAY 40 训练和测试的规范写法 知识点回顾&#xff1a; 1.彩色和灰度图片测试和训练的规范写法&#xff1a;封装在函数中 2.展平操作&#xff1a;除第一个维度batchsize外全部展平 3.dropout操作&#xff1a;训练阶段随机丢弃神经元&#xff0c;测试阶段eval模式关闭dropout 作…

晨控CK-FR03与汇川H5U系列PLC配置MODBUS TCP通讯连接操作手册

晨控CK-FR03与汇川H5U系列PLC配置MODBUS TCP通讯连接操作手册 CK-FR03-TCP是一款基于射频识别技术的高频RFID标签读卡器&#xff0c;读卡器工作频率为13.56MHZ&#xff0c;支持对I-CODE 2、I-CODE SLI等符合ISO15693国际标准协议格式标签的读取。 读卡器同时支持标准工业通讯协…

二叉树实验

引言 在数据结构的学习过程中&#xff0c;二叉树作为一种典型的非线性结构&#xff0c;其构造和操作方式具有高度的层次性和结构性。而递归正是处理这种结构最自然的方式之一。关于二叉树的基本结构如下图所示&#xff1a; 递归的本质是函数调用自身的过程&#xff0c;这恰好…

【空间光学系统与集成微纳光子学系统简介】

空间光学系统 空间光学系统指用于太空探测、遥感、通信等领域的光学仪器&#xff0c;通常具备高分辨率、轻量化、抗辐射等特性。主要应用包括&#xff1a; 天文观测&#xff1a;如哈勃望远镜、詹姆斯韦伯太空望远镜&#xff08;JWST&#xff09;&#xff0c;利用大口径主镜收集…

开发一款IIS自动检测修复工具

目录 实现的功能 技术栈与依赖 DLL 实现细节 变量 初始化操作 自定义cpu阈值 检测IIS应用程序池 获取自定义阈值 获取某个应用程序池的占用率 获取性能计数器实例名 Kill 并重新启动应用池 写入日志到 Log 目录&#xff0c;并显示在文本框中 实际运行效果 此工具可…

网络编程4-epoll

select底层原理 fd_set底层使用位图标记每个文件标识符有没有被使用&#xff0c;位图在c语言里靠数组实现。 select 流程 在用户态空间里&#xff08;栈、堆、数据段&#xff09;申请一个fd_set将fd_set从用户态拷贝到内核态&#xff08;在后面操作系统轮询会使用到&#xff09…

SOC-ESP32S3部分:19-ADC模数转换

飞书文档https://x509p6c8to.feishu.cn/wiki/XycAwmO6Niitdtka1RAcclYfnvf ESP32-S3 集成了两个 12 位 SAR ADC&#xff0c;共支持 20 个模拟通道输入。 SAR ADC 管脚通过 IO MUX 与 GPIO1 ~ GPIO20、RTC_GPIO1 ~ RTC_GPIO20、触摸传感器接口、UART 接口、SPI 接口、以及 USB…

默克微生物培养基选择指南

微生物学研究需要能在实验室提供各种不同种类的细菌、酵母或病毒。诸如发酵、蛋白质和疫苗生产的大规模过程需要大量处于生理活性状态的细菌。因此&#xff0c;针对各种应用需要有能提供适当的生化环境并保持微生物所有特征的合适的营养培养基。 任何微生物培养基都应包括营养…

移动安全Android——解决APP抓包证书无效问题

问题 通过Burpsuite和ProxyPin进行代理抓包Android APP的时候发现虽然已经正确添加了用户证书&#xff0c;但是还是会出现SSL握手错误&#xff0c;证书无效问题。这是因为Android 7 以上版本APP默认不信任用户证书&#xff0c;只信任系统证书&#xff0c;所以需要将用户证书移动…

【数据库】数据库恢复技术

数据库恢复技术 实现恢复的核心是使用冗余&#xff0c;也就是根据冗余数据重建不正确数据。 事务 事务是一个数据库操作序列&#xff0c;是一个不可分割的工作单位&#xff0c;是恢复和并发的基本单位。 在关系数据库中&#xff0c;一个事务是一条或多条SQL语句&#xff0c…

【学习笔记】深度学习-梯度概念

一、定义 梯度向量不仅表示函数变化的速度&#xff0c;还表示函数增长最快的方向 二、【问】为什么说它表示方向&#xff1f; 三、【问】那在深度学习梯度下降的时候&#xff0c;还要判断梯度是正是负来更新参数吗&#xff1f; 假设某个参数是 w&#xff0c;损失函数对它的…