左右列的单元格合并
EasyExcel导出合并单元格,左右列的单元格合并
1、导出的实体类,也就是表头
@Data
public class CityCapacityPo {@ExcelProperty(value = "时间",index = 0)private String time;@ExcelProperty(value = "出口",index = 1)private String export;@ExcelProperty(value = "地市",index = 2)private String direction;@ExcelProperty(value = "数据1",index = 3)private Double data1;@ExcelProperty(value = "数据2",index = 4)private Double data2;@ExcelProperty(value = "数据3",index = 5)private Double data3;@ExcelProperty(value = "数据4",index = 6)private Double data4;@ExcelProperty(value = "数据5",index = 7)private Double data5;@ExcelProperty(value = "数据6",index = 8)private Double data6;}
2、行合并工具类
public class ExcelFillCellMergeStrategyUtils implements CellWriteHandler {/*** 需要合并列的下标,从0开始*/private int[] mergeColumnIndex;/*** 从第几行开始合并,表头下标为0*/private int mergeRowIndex;public ExcelFillCellMergeStrategyUtils(int mergeRowIndex, int[] mergeColumnIndex) {this.mergeRowIndex = mergeRowIndex;this.mergeColumnIndex = mergeColumnIndex;}@Overridepublic void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer integer, Integer integer1, Boolean aBoolean) {}@Overridepublic void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer integer, Boolean aBoolean) {}@Overridepublic void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer integer, Boolean aBoolean) {}@Overridepublic void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> list, Cell cell, Head head, Integer integer, Boolean aBoolean) {//当前行int curRowIndex = cell.getRowIndex();//当前列int curColIndex = cell.getColumnIndex();if (curRowIndex > mergeRowIndex) {for (int i = 0; i < mergeColumnIndex.length; i++) {if (curColIndex == mergeColumnIndex[i]) {mergeWithPrevRow(writeSheetHolder, cell, curRowIndex, curColIndex);break;}}}}private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) {//获取当前行的当前列的数据和上一行的当前列列数据,通过上一行数据是否相同进行合并Object curData = cell.getCellTypeEnum() == CellType.STRING ? cell.getStringCellValue() :cell.getNumericCellValue();Row preRow = cell.getSheet().getRow(curRowIndex - 1);if (preRow == null) {// 当获取不到上一行数据时,使用缓存sheet中数据preRow = writeSheetHolder.getCachedSheet().getRow(curRowIndex - 1);}Cell preCell=preRow.getCell(curColIndex);Object preData = preCell.getCellTypeEnum() == CellType.STRING ? preCell.getStringCellValue() :preCell.getNumericCellValue();// 比较当前行的第一列的单元格与上一行是否相同,相同合并当前单元格与上一行if (curData.equals(preData)) {Sheet sheet = writeSheetHolder.getSheet();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);}}}}
3、列合并的工具类
@Data
@AllArgsConstructor
public class CellLineRange {/*** 起始列*/private int firstCol;/*** 结束列*/private int lastCol;
}
public class ExcelFillCelMergeStrategy implements CellWriteHandler {//自定义合并单元格的列 如果想合并 第4列和第5例 、第6列和第7例: [CellLineRange(firstCol=3, lastCol=4), CellLineRange(firstCol=5, lastCol=6)]private List<CellLineRange> cellLineRangeList;//自定义合并单元格的开始的行 一般来说填表头行高0 表示从表头下每列开始合并 :如表头行高位为3则 int mergeRowIndex = 2 ;private int mergeRowIndex;public ExcelFillCelMergeStrategy(List<CellLineRange> cellLineRangeList, int mergeRowIndex) {this.cellLineRangeList=cellLineRangeList;this.mergeRowIndex=mergeRowIndex;}@Overridepublic void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer integer, Integer integer1, Boolean aBoolean) {}@Overridepublic void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer integer, Boolean aBoolean) {}@Overridepublic void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer integer, Boolean aBoolean) {}@Overridepublic void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> list, Cell cell, Head head, Integer integer, Boolean aBoolean) {//当前单元格的行数int curRowIndex = cell.getRowIndex();// 当前单元格的列数int curColIndex = cell.getColumnIndex();if (curRowIndex > mergeRowIndex) {if (curRowIndex > mergeRowIndex) {for (int i = 0; i < cellLineRangeList.size(); i++) {if (curColIndex > cellLineRangeList.get(i).getFirstCol()&&curColIndex<=cellLineRangeList.get(i).getLastCol()) {//单元格数据处理mergeWithLeftLine(writeSheetHolder, cell, curRowIndex, curColIndex);break;}}}}}/*** @description 当前单元格向左合并*/private void mergeWithLeftLine(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) {//当前单元格中数据Object curData = cell.getCellTypeEnum() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();//获取当前单元格的左面一个单元格Cell leftCell = cell.getSheet().getRow(curRowIndex).getCell(curColIndex - 1);//获取当前单元格的左面一个单元格中的数据Object leftData = leftCell.getCellTypeEnum() == CellType.STRING ? leftCell.getStringCellValue() : leftCell.getNumericCellValue();// 将当前单元格数据与左侧一个单元格数据比较if (leftData.equals(curData)) {//获取当前sheet页Sheet sheet = writeSheetHolder.getSheet();//得到所有的合并单元格List<CellRangeAddress> mergeRegions = sheet.getMergedRegions();//是否合并boolean isMerged = false;for (int i = 0; i < mergeRegions.size() && !isMerged; i++) {//CellRangeAddress POI合并单元格//CellRangeAddress(int firstRow, int lastRow, int firstCol, int lastCol)//例子:CellRangeAddress(2, 6000, 3, 3);//第2行起 第6000行终止 第3列开始 第3列结束。CellRangeAddress cellRangeAddr = mergeRegions.get(i);// cellRangeAddr.isInRange(int rowInd, int colInd)确定给定坐标是否在此范围的范围内。// 若左侧一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元if (cellRangeAddr.isInRange(curRowIndex, curColIndex - 1)) {sheet.removeMergedRegion(i);cellRangeAddr.setLastColumn(curColIndex);sheet.addMergedRegion(cellRangeAddr);isMerged = true;}}// 若左侧一个单元格未被合并,则新增合并单元if (!isMerged) {CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex , curRowIndex, curColIndex- 1, curColIndex);sheet.addMergedRegion(cellRangeAddress);}}}}
4、调用工具类,开始合并:
@PostMapping("/exportCityOutletCapacityList")
@ApiOperation("导出Excel表")
public void exportCityOutletCapacityList(@RequestBody OutletCapacityParam param, HttpServletResponse response) {//获取需要导出的表数据List<CityCapacityPo> list=capacityFlowDao.selectCityOutletCapacityList(param);try {String fileName = "测试合并单元格";response.setContentType("application/octet-stream");response.setCharacterEncoding("utf-8");response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");CityCapacityPo capacityPo=new CityCapacityPo();capacityPo.setExport("汇总");capacityPo.setTime("汇总");capacityPo.setDirection("汇总");//遍历列表,求各数据汇总capacityPo.setData1(list.stream().filter(Po-> Po.getData1()!=null).mapToDouble(CityCapacityPo::getData1).sum());capacityPo.setData2(list.stream().filter(Po-> Po.getData2()!=null).mapToDouble(CityCapacityPo::getData2).sum());capacityPo.setData3(list.stream().filter(Po-> Po.getData3()!=null).mapToDouble(CityCapacityPo::getData3).sum());capacityPo.setData4(list.stream().filter(Po-> Po.getData4()!=null).mapToDouble(CityCapacityPo::getData4).sum());capacityPo.setData5(list.stream().filter(Po-> Po.getData5()!=null).mapToDouble(CityCapacityPo::getData5).sum());capacityPo.setData6(list.stream().filter(Po-> Po.getData6()!=null).mapToDouble(CityCapacityPo::getData6).sum());list.add(capacityPo);ArrayList<CellLineRange> cellLineRanges=new ArrayList<>();//设置第几列开始合并int[] mergeColumnIndex = {0, 1};//设置第几行开始合并int mergeRowIndex = 1;cellLineRanges.add(new CellLineRange(0,2));EasyExcel.write(response.getOutputStream()) //设置行合并单元格.registerWriteHandler(new ExcelFillCellMergeStrategyUtils(mergeRowIndex,mergeColumnIndex))//设置行合并单元格.registerWriteHandler(new ExcelFillCelMergeStrategy(cellLineRanges,list.size()-1)).head(CityCapacityPo.class).sheet("sheet1").doWrite(list);} catch (Exception e) {e.printStackTrace();}}相关文章:
左右列的单元格合并
EasyExcel导出合并单元格,左右列的单元格合并 1、导出的实体类,也就是表头 Data public class CityCapacityPo {ExcelProperty(value "时间",index 0)private String time;ExcelProperty(value "出口",index 1)private String …...
机器学习 | 决策树 Decision Tree
—— 分而治之,逐个击破 把特征空间划分区域 每个区域拟合简单模型 分级分类决策 1、核心思想和原理 举例: 特征选择、节点分类、阈值确定 2、信息嫡 熵本身代表不确定性,是不确定性的一种度量。 熵越大,不确定性越高,…...
面试题总结(十四)【系统移植】【华清远见西安中心】
你用过哪些Bootloader?有什么区别? 作为一个AI助手,我没有直接使用过Bootloader。但我可以为您提供一些关于常见Bootloader的信息和他们之间的区别。 1. GRUB (GRand Unified Bootloader):GRUB是一个功能强大且广泛使用的Bootload…...
【Spark精讲】Spark RDD弹性体现在哪些方面?
什么是“弹性”? 一般对于分布式系统,“弹性”指的是可以根据计算规模进行动态伸缩的特性。当计算量增长时,可以动态增加资源来满足计算需求,而当计算量减少时,又可以降低资源配置来节约成本。 参考:什么是…...
【从客户端理解Kafka的使用方式】
文章目录 一、从基础的客户端说起1、消息发送者主流程2、消息消费者主流程 二、从客户端属性来梳理客户端工作机制1、消费者分组消费机制2、生产者拦截器机制3、消息序列化机制4、消息分区路由机制5、生产者消息缓存机制6、发送应答机制 三、客户端流程总结四、SpringBoot集成K…...
『OPEN3D』1.5.4 动手实现点云八叉树(OctoTree)最近邻
本专栏地址: https://blog.csdn.net/qq_41366026/category_12186023.html?spm=1001.2014.3001.5482 在二维和三维空间中,我们可以采用四叉树(Quad tree)和八叉树(Octree)这两种特定的数据结构来处理空间分割。这些树形结构可以看作是K-d树在不同维度下的扩展。…...
非制冷红外成像技术实现高灵敏度和高分辨率
非制冷红外成像技术实现高灵敏度和高分辨率主要依赖于以下几个方面: 探测器设计:非制冷红外成像技术采用的探测器通常具有高灵敏度和高分辨率的特点。这些探测器能够有效地接收并转换红外辐射,从而产生高质量的图像信息。 光学系统设计&…...
@Resource 和 @Autowired区别是什么?
Resource 和 Autowired 时,它们都是用于依赖注入的注解,但它们有一些不同之处。 来源: Resource 是Java EE标准的一部分,而且是JDK提供的,不属于Spring框架的注解。它的使用范围更广泛,不仅可以用在Spring中…...
K8S的一个pod中运行多个容器
通过deployment的方式部署 创建一个deployment文件 [rootk8s-master1 pods]# cat app.yaml apiVersion: apps/v1 kind: Deployment metadata:name: dsfnamespace: applabels:app: dsf spec:replicas: 1 #实例的个数selector:matc…...
《每天一分钟学习C语言·一》
1、转义字符:\n换行,\t前进一个tab键,\b退格键 2、八进制前面有0,%o或者%#o表示八进制,十六进制前有0X,%0x或者%#0x表示十六进制 3、%u打印无符号数,%g显示小数,类似于%fÿ…...
zookeeper:启动后占用8080端口问题解决
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务。它为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。 我们经常在运行zookeeper服务时,不需要配置服务端口,…...
深度学习中的高斯分布
1 高斯分布数学表达 1.1 什么是高斯分布 高斯分布(Gaussian Distribution)又称正态分布(Normal Distribution)。高斯分布是一种重要的模型,其广泛应用于连续型随机变量的分布中,在数据分析领域中高斯分布占有重要地位。由于中心极限定理(Central Limit…...
【已解决】Atlas 导入 Hive 元数据,执行 import-hive.sh 报错
部署完 Atlas 之后,尝试导入 Hive 元数据,遇到了一些错误,特此记录一下,方便你我他。 执行 import-hive.sh 报错 [omchadoop102 apache-atlas-2.2.0]$ hook-bin/import-hive.sh Using Hive configuration directory [/opt/module…...
在 Windows PC 上轻松下载并安装 FFmpeg
FFmpeg 是一种开源媒体工具,可用于将任何视频格式转换为您需要的格式。该工具只是命令行,因此它没有图形、可点击的界面。如果您习惯使用常规图形 Windows 程序,安装 FFmpeg 一开始可能看起来很复杂,但不用担心,它;很简…...
21.Servlet 技术
JavaWeb应用的概念 在Sun的Java Servlet规范中,对Java Web应用作了这样定义:“Java Web应用由一组Servlet、HTML页、类、以及其它可以被绑定的资源构成。它可以在各种供应商提供的实现Servlet规范的 Servlet容器 中运行。” Java Web应用中可以包含如下…...
【Hive】——DDL(PARTITION)
1 增加分区 1.1 添加一个分区 ALTER TABLE t_user_province ADD PARTITION (provinceBJ) location/user/hive/warehouse/test.db/t_user_province/provinceBJ;必须自己把数据加载到增加的分区中 hive不会帮你添加 1.2 一次添加多个分区 ALTER TABLE table_name ADD PARTITION…...
SpringBoot 源码解析4:事件监听器
SpringBoot 源码解析4:事件监听器 1. 初始化监听器2. 创建事件发布器 SpringApplicationRunListeners3. 事件分发流程3.1 SimpleApplicationEventMulticaster#multicastEvent3.2 获取监听器 AbstractApplicationEventMulticaster#getApplicationListeners3.3 Abstra…...
使用 FastAPI 和 Vue.js 实现前后端分离
简介 前后端分离是现代 Web 开发的趋势。使用 FastAPI 和 Vue.js 可以构建一个高效、灵活且易于维护的 Web 应用。FastAPI 提供了高性能的后端服务,而 Vue.js 作为一种渐进式 JavaScript 框架,可以构建动态的前端界面。本文将详细介绍如何使用 FastAPI …...
算法基础之SPFA判断负环
SPFA判断负环 核心思想:spfa算法 当遍历一个点时 cnt数组记录边数 若有负环 边数会无限1 cnt>n是即为有负环 #include<iostream>#include<cstring>#include<algorithm>#include<queue>using namespace std;const int N 2010 , M 10010…...
一些常用的Linux命令及其简要说明(持续更新)
1. cd:改变当前工作目录。 cd [directory]#例如 cd /home/user 2. ls:列出目录内容。 ls [-options] [file/directory]#例如 ls -l, ls /etc 3. pwd:显示当前工作目录。 pwd 4. mkdir:创建新目录。 mkdir [directory]#例…...
理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...
ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
CocosCreator 之 JavaScript/TypeScript和Java的相互交互
引擎版本: 3.8.1 语言: JavaScript/TypeScript、C、Java 环境:Window 参考:Java原生反射机制 您好,我是鹤九日! 回顾 在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...
