FastExcel 合并单元格(相当的行数据,进行合并)

news/2024/12/16 15:47:57/文章来源:https://www.cnblogs.com/vipsoft/p/18610336

目录
  • 需求
  • 思路
  • 实现
    • Excel导出单元格全量合并策略
    • 日期格式转换
    • 接口代码
    • Service
    • DTO

使用FastExcel数据导出:官网: https://idev.cn/fastexcel/zh-CN

需求

信用代码、填报人,唯一时,将:信用代码、单位名称、填报人,进行 row 合并,并垂直居中对齐
image

思路

这边不需要做列合并,所以采用了 RowWriteHandler
思路,

  • 指定唯一值,根据某个或多个单元格确定相当的数据行(代码中的 ExcelCellMergeStrategy. uniqueCol)
  • 判断当前行的唯一列的数据和上一行是否相等,如果相等继续,要合并的行数 mergeCount + 1
  • 如果当前行和上一行不相等,说明前面的数据需要做合并处理了。同时将当前行做为下一次待合并的起始行

实现

Excel导出单元格全量合并策略

package com.vipsoft.handler;import cn.idev.excel.write.handler.RowWriteHandler;
import cn.idev.excel.write.metadata.holder.WriteSheetHolder;
import cn.idev.excel.write.metadata.holder.WriteTableHolder;import org.apache.poi.ss.usermodel.*;import org.apache.poi.ss.util.CellRangeAddress;import java.util.ArrayList;
import java.util.List;/*** Excel导出单元格全量合并策略*/
public class ExcelCellMergeStrategy implements RowWriteHandler {private int mergeRowIndex;//从哪一行开始合并private List<Integer> mergeColumnIndex = new ArrayList<>();//excel合并的列private int[] uniqueCol;//合并的唯一标识,根据指定的列,确定数据是否相同private int totalRow;//总行数private int lastRow;private int firstCol;private int lastCol;private int firstRow;private int mergeCount = 1;/*** @param mergeRowIndex* @param mergeColIndex 支持范围如:0-3,6,9* @param uniqueCol     唯一标识,1列或多列 数据组成唯一值* @param totalRow      总行数(从0开始):List.size -1  + 跳过的表头*/public ExcelCellMergeStrategy(int mergeRowIndex, Object[] mergeColIndex, int[] uniqueCol, int totalRow) {this.mergeRowIndex = mergeRowIndex;for (Object item : mergeColIndex) {if (item.toString().contains("-")) {String[] spCol = item.toString().split("-");int start = Integer.parseInt(spCol[0]);int end = Integer.parseInt(spCol[1]);for (int i = start; i <= end; i++) {mergeColumnIndex.add(i);}} else {int colIndex = Integer.parseInt(item.toString());mergeColumnIndex.add(colIndex);}}this.uniqueCol = uniqueCol;this.totalRow = totalRow;}@Overridepublic void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Integer rowIndex, Integer relativeRowIndex, Boolean isHead) {}@Overridepublic void afterRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer relativeRowIndex, Boolean isHead) {}@Overridepublic void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer relativeRowIndex, Boolean isHead) {//当前行int curRowIndex = row.getRowNum();//每一行的最大列数short lastCellNum = row.getLastCellNum();//当前行为开始合并行时,标记if (curRowIndex == mergeRowIndex) {//赋初值 第一行firstRow = curRowIndex;}//开始合并位置if (curRowIndex > mergeRowIndex && !row.getCell(0).getStringCellValue().equals("")) {for (int i = 0; i < lastCellNum; i++) {if (mergeColumnIndex.contains(i)) {//当前行号 当前行对象 合并的标识位mergeWithPrevAnyRow(writeSheetHolder.getSheet(), curRowIndex, row, uniqueCol);break;//已经进入到合并单元格操作里面了,执行一次就行}}}}public void mergeWithPrevAnyRow(Sheet sheet, int curRowIndex, Row row, int[] uniqueCol) {Object currentData = "";Object preData = "";for (int col : uniqueCol) {currentData = currentData + row.getCell(col).getStringCellValue();Row preRow = row.getSheet().getRow(curRowIndex - 1);preData = preData + preRow.getCell(col).getStringCellValue();}//判断是否合并单元格boolean curEqualsPre = currentData.equals(preData);//判断前一个和后一个相同 并且 标识位相同if (curEqualsPre) {lastRow = curRowIndex;mergeCount++;}//excel过程中合并if (!curEqualsPre && mergeCount > 1) {mergeSheet(firstRow, lastRow, mergeColumnIndex, sheet);mergeCount = 1;}//excel结尾处合并if (mergeCount > 1 && totalRow == curRowIndex) {mergeSheet(firstRow, lastRow, mergeColumnIndex, sheet);mergeCount = 1;}//重置下一个要合并的行if (!curEqualsPre) {firstRow = curRowIndex;}}private void mergeSheet(int firstRow, int lastRow, List<Integer> mergeColumnIndex, Sheet sheet) {for (int colNum : mergeColumnIndex) {firstCol = colNum;lastCol = colNum;CellRangeAddress cellRangeAddress = new CellRangeAddress(firstRow, lastRow, firstCol, lastCol);sheet.addMergedRegion(cellRangeAddress);// 设置合并后的单元格样式为垂直居中CellStyle style = sheet.getWorkbook().createCellStyle();style.setVerticalAlignment(VerticalAlignment.CENTER);//style.setAlignment(HorizontalAlignment.CENTER);Cell mergedCell = sheet.getRow(firstRow).getCell(colNum, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);mergedCell.setCellStyle(style);}}
}

日期格式转换

EasyExcel => FastExcel ,导入支持多种时间格式

package com.vipsoft.base.util;import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;import cn.idev.excel.converters.Converter;
import cn.idev.excel.enums.CellDataTypeEnum;
import cn.idev.excel.metadata.GlobalConfiguration;
import cn.idev.excel.metadata.data.ReadCellData;
import cn.idev.excel.metadata.data.WriteCellData;
import cn.idev.excel.metadata.property.ExcelContentProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** 日期格式转换器*/
public class ExcelDateConverter implements Converter<Date> {private static final Logger log = LoggerFactory.getLogger(ExcelDateConverter.class);// 定义所有要尝试的日期格式SimpleDateFormat[] formats = {new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"),new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"),new SimpleDateFormat("yyyy/MM/dd"),new SimpleDateFormat("yyyy-MM-dd"),new SimpleDateFormat("yyyy-MM"),new SimpleDateFormat("yyyy/MM"),new SimpleDateFormat("yyyyMMdd")};@Overridepublic Class<Date> supportJavaTypeKey() {return Date.class;}@Overridepublic CellDataTypeEnum supportExcelTypeKey() {return CellDataTypeEnum.STRING;}@Overridepublic Date convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty,GlobalConfiguration globalConfiguration) throws Exception {String cellValue = "";if (cellData.getType().equals(CellDataTypeEnum.NUMBER)) {long cellIntValue = cellData.getNumberValue().longValue();if (cellIntValue > 19900100) {try {// 1. 第一种解析,传入的是数字形式的日期,形如yyyyMMddSimpleDateFormat originalFormat = new SimpleDateFormat("yyyyMMdd");return originalFormat.parse(String.valueOf(cellIntValue));} catch (Exception e) {log.warn("exception when parse numerical time with format yyyyMMdd");cellValue=String.valueOf(cellIntValue);}}// 2. 第二种解析, excel是从1900年开始计算,最终通过计算与1900年间隔的天数计算目标日期LocalDate localDate = LocalDate.of(1900, 1, 1);//excel 有些奇怪的bug, 导致日期数差2localDate = localDate.plusDays(cellIntValue - 2);// 转换为ZonedDateTime(如果需要时区信息)ZonedDateTime zonedDateTime = localDate.atStartOfDay(ZoneId.systemDefault());return Date.from(zonedDateTime.toInstant());} else if (cellData.getType().equals(CellDataTypeEnum.STRING)) {// 3. 第三种解析Date date = null;cellValue = cellData.getStringValue();for (SimpleDateFormat format : formats) {try {date = format.parse(cellValue);if (date != null) {// 这一步是将日期格式化为Java期望的格式return date;}} catch (Exception e) {// 如果有异常,捕捉异常后继续解析//log.error(e.getMessage(), e);}}}// 没转成功,抛出异常throw new UnsupportedOperationException("The current operation is not supported by the current converter." + cellValue);}@Overridepublic WriteCellData<?> convertToExcelData(Date value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");String dateValue = sdf.format(value);return new WriteCellData<>(dateValue);}
} 

接口代码

导出代码

package com.vipsoft.api.controller;import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.util.Map;/*** 企业信息*/
@RestController
@RequestMapping("/detail")
public class CooperationDetailController extends BaseController {/*** 企业信息* * @return*/@PostMapping("/export")public void exportInfo(HttpServletRequest request, HttpServletResponse response, @RequestBody Map<String, Object> param) {try {Page page = buildPage(param, CooperationInfo.class);QueryWrapper<SysOrganization> queryWrapper = buildQueryWrapper(SysOrganization.class, param);            cooperationDetailService.exportInfo(response, queryWrapper);} catch (Exception ex) {logger.error(ex.getMessage(), ex);}}
}

Service

@Service
public class SysOrganizationServiceImpl extends ServiceImpl<SysOrganizationMapper, SysOrganization> implements ISysOrganizationService {@Overridepublic void exportInfo(HttpServletResponse response, QueryWrapper<SysOrganization> queryWrapper) {String templateFileName = "";try {templateFileName = cuworConfig.getFilePath() + "/template/企业导出模板.xlsx";response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setCharacterEncoding("utf-8");// 这里URLEncoder.encode可以防止中文乱码 当然和 FastExcel 没有关系String fileName = URLEncoder.encode("企业数据", "UTF-8").replaceAll("\\+", "%20");response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");//获取要导出的数据 DTOList<SysOrganizationExcelDTO> dataList = data(queryWrapper);int mergeRowIndex = 2;                      // 从那一行开始合并  -- 跳过表头int[] uniqueCol = {0, 7};                  //根据指定的列,确定相同的数据Object[] mergeColIndex = {"0-1", 6, 7};    //需要合并的列int totalRow = dataList.size() - 1 + mergeRowIndex;// 这里需要设置不关闭流ExcelCellMergeStrategy excelCellMergeStrategy = new ExcelCellMergeStrategy(mergeRowIndex, mergeColIndex, uniqueCol, totalRow);FastExcel.write(response.getOutputStream(), SysOrganizationExcelDTO.class).needHead(false).withTemplate(templateFileName).autoCloseStream(Boolean.FALSE).registerWriteHandler(excelCellMergeStrategy) //合并单元格.sheet("企业数据").doWrite(dataList);} catch (Exception e) {// 重置responseresponse.reset();response.setContentType("application/json");response.setCharacterEncoding("utf-8");//异常时,向前端抛出 JSON ApiResult result = new ApiResult(6001, "下载文件失败 " + templateFileName + " " + e.getMessage());try {response.getWriter().println(PojoUtil.pojoToJson(result));} catch (IOException ex) {logger.error(ex.getMessage(), ex);throw new CustomException(ex.getMessage());}}}/*** 获得要到出的数据*/private List<SysOrganizationExcelDTO> data(QueryWrapper<SysOrganization>  queryWrapper) {IPage list = this.page(new Page(1, 10000), queryWrapper);List<SysOrganizationExcelDTO> result = new ArrayList<>();for (Object obj : list.getRecords()) {if (obj instanceof SysOrganization) {SysOrganization item = (SysOrganization) obj;SysOrganizationExcelDTO info = new SysOrganizationExcelDTO(); BeanUtils.copyProperties(item, info); //组装数据result.add(info);}}return result;}
}  

DTO

package com.vipsoft.base.dto;import cn.idev.excel.annotation.ExcelIgnore;
import cn.idev.excel.annotation.ExcelProperty;
import cn.idev.excel.annotation.format.DateTimeFormat;
import com.vipsoft.base.util.ExcelDateConverter;import java.io.Serializable;
import java.util.Date;/*** Excel 导出使用*/
public class SysOrganizationExcelDTO implements Serializable {/*** 统一社会信用代码*///@ExcelProperty(value = "统一社会信用代码")@ExcelProperty(index = 0)private String unifiedSocialCode;/*** 机构名称*/@ExcelProperty(index = 1)private String orgName; /*** 岗位大类名称*/@ExcelProperty(index = 2)private String jobBigName;/*** 岗位中类名称*/@ExcelProperty(index = 3)private String jobMiddleName;/*** 岗位小类名称*/@ExcelProperty(index = 4)private String jobSmallName;/*** 岗位数量*/@ExcelProperty(index = 5)private Integer jobQty;/*** 填报日期**/@ExcelProperty(index = 6, converter = ExcelDateConverter.class)private Date inputDate;/*** 填报人*/@ExcelProperty(index = 7)private String inputUser;......省略get set}

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

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

相关文章

街面环卫算法视频分析服务器浅析智能视频监控在智慧城市的应用与趋向

在数字化浪潮的推动下,智慧城市的建设已成为全球范围内城市发展的重要趋势。智慧城市不仅仅是技术的集合,它更是一个系统工程,涉及到城市管理的各个方面,旨在通过高科技手段提升城市的运行效率和居民的生活质量。 其中,智能视频监控技术作为智慧城市建设的关键组成部分,正…

嘻嘻嘻嘻嘻测试下

凡你能说的,你说清楚。凡你不能说的,留给沉默!

算法网关视频分析网关无线视频监控技术如何以智能化手段提升抗干扰与数据安全效果

在当今这个信息化快速发展的时代,无线技术的应用已经渗透到我们生活的方方面面,尤其是在视频监控领域,无线传输技术正以其独特的优势,改变着传统的监控系统部署方式。本文将探讨无线视频监控业务的发展情况、面临的挑战以及如何通过技术手段提高系统的抗干扰能力和数据安全…

打破局限!如何在项目管理中运用鱼骨图分析法

一、鱼骨图分析法在项目管理中的重要性简述在项目管理的漫长旅程中,我们常常会遭遇到各种各样棘手的问题,这些问题就像隐藏在暗处的礁石,随时可能让项目的 “船只” 偏离航线,甚至搁浅。小到团队成员之间沟通不畅,导致工作衔接出现缝隙;大到项目进度严重延误,成本超出预…

使用wsimport命令生成webService客户端代码

wsimport 是 JDK 自带的一个工具,可以根据 WSDL 文件生成 Java 类。 1.进入JDK/bin目录,从地址栏进入cmd 2.执行如下命令:wsimport -keep -s D:\tmp -p com.cn.phone -verbose http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?wsdl-keep:是否生成java源文件 -s:指…

实验6 模板类、文件I/O和异常处理

1.实验任务1 运行代码: Complex.h:#pragma once#include <iostream> #include <stdexcept>// 声明 //////////////////////////////////////////////////// // 复数模板类声明 template<typename T> class Complex { public:Complex(T r = 0, T i = 0);Comp…

人员乘坐皮带识别智慧矿山一体机:矿山达到智能化最终要求需要哪些AI算法及关键因素?

在数字化转型的大潮中,非煤矿山行业正站在智能化升级的风口浪尖。随着人工智能、大数据、物联网等技术的飞速发展,矿山智能化已成为提升行业竞争力、保障作业安全、优化资源利用的关键路径。 本文将深入探讨实现矿山智能化所需的AI算法及其应用,并分析在构建智能化矿山生态系…

容器与虚拟机的区别与关系

什么是虚拟机技术? 传统的虚拟机技术(VMware)将一组硬件虚拟化,在其上安装并运行一个完整的操作系统,然后在该系统上运行所需的应用进程;虚拟机就像一台主机。 什么是容器? 容器是一个应用层面的抽象,用于将代码和依赖资源打包在一起。本文分享自天翼云开发者社区《容器…

数理统计中的分位数

目录上分位数和下分位数的定义下分位数的直观理解上分位数的直观理解常用分布中的分位数正态分布卡方分布 上分位数和下分位数的定义 设连续型随机变量 \(X\) 的分布函数为 \(F(x)\),概率密度函数为 \(f(x)\),则:对于任意正数 \(\alpha(0<\alpha<1)\),称满足条件\[F(…

SWD下载口的端口状态

1、关于SWD SWD是MCU下载程序和调试的端口,分为四线制和五线制 四线制:VCC GND SWDIO SWCKL 五线制:VCC GND SWDIO SWCLK RESET 四线制没有留出RESET引脚,没有RESET引脚,下载完程序,会自动运行。SWDIO是双向数据线,实现数据的输入和输出。SWCLK是 时钟信号线。 2、SWD电…

工作睡觉监测识别摄像机

工作睡觉监测识别摄像机是一种用于监测员工是否在工作时间内偷懒、打瞌睡或者进行其他不合规行为的智能监控设备。这种摄像机利用先进的人工智能和图像识别技术,能够实时监测员工的工作状态,发现异常行为并采取相应的措施。工作睡觉监测识别摄像机是一种用于监测员工是否在工…

FMC子卡设计方案:202-基于TI DSP TMS320C6678、Xilinx K7 FPGA XC7K325T的高速数据处理核心板

基于TI DSP TMS320C6678、Xilinx K7 FPGA XC7K325T的高速数据处理核心板一、板卡概述 该DSP+FPGA高速信号采集处理板由我公司自主研发,包含一片TI DSP TMS320C6678和一片Xilinx FPGA K7 XC72K325T-1ffg900。包含1个千兆网口,1个FMC HPC接口。可搭配使用AD FMC子卡、图像FMC子…