左右列的单元格合并
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]#例…...
SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...
用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...
【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论
路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中(图1): mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...
Python网页自动化Selenium中文文档
1. 安装 1.1. 安装 Selenium Python bindings 提供了一个简单的API,让你使用Selenium WebDriver来编写功能/校验测试。 通过Selenium Python的API,你可以非常直观的使用Selenium WebDriver的所有功能。 Selenium Python bindings 使用非常简洁方便的A…...
