目标

需要使用阿里的EasyExcel库来导出excel,并要自定义合并单元格。

思路


这里自定义的CellWriteHandler需要将数据进行如下处理:

  • 1.Excel每一行数据必须对应一个对象;
  • 2.每一个对象必须有ID字段。
    这里使用EasyExcel的数据数组,必须达到这两个条件。合并单元格的判断逻辑如下:
    判断当前行的对象ID与上一行的对象ID相等,且当前单元格对象的字段值与上一行单元格对象的字段值相等。才能够进行单元格合并。

步骤

ExportKbdAreaRuleItemVO.java

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentStyle;
import com.alibaba.excel.enums.poi.HorizontalAlignmentEnum;
import com.alibaba.excel.enums.poi.VerticalAlignmentEnum;
import io.swagger.annotations.ApiModelProperty;
import lombok.*;import java.util.Date;/*** KBD区域规则 (hq bu sd) 列表回参对象**/
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
@EqualsAndHashCode
@ColumnWidth(value = 28)
@ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.CENTER, verticalAlignment = VerticalAlignmentEnum.CENTER)
public class ExportKbdAreaRuleItemVO {@ExcelProperty(value = "id", index = 0)private String id;/** 组织架构 —— BU */@ExcelProperty(value = "BU", index = 1)private String bu;/** 组织架构 —— Region */@ApiModelProperty(value = "组织架构二级")@ExcelProperty(value = "Region", index = 2)private String region;/** 组织架构 —— Area */@ApiModelProperty(value = "组织架构三级")@ExcelProperty(value = "Area", index = 3)private String area;/** 组织架构 —— Territory */@ApiModelProperty(value = "组织架构四级")@ExcelProperty(value = "Territory", index = 4)private String territory;/** 渠道业态name*/@ApiModelProperty(value = "渠道业态name")@ExcelProperty(value = "渠道业态", index = 5)private String channelBusinessLevelName;/** 进行中的规则  */@ApiModelProperty(value = "进行中的规则")@ExcelProperty(value = "进行中规则", index = 6)private String doingName;/** 规则开始时间 */@ApiModelProperty(value = "规则开始时间")@ExcelProperty(value = "规则开始时间", index = 7)private Date ruleStartTime;/** 规则结束时间 */@ApiModelProperty(value = "规则结束时间")@ExcelProperty(value = "规则结束时间", index = 8)private Date ruleEndTime;/** 其他规则  */@ApiModelProperty(value = "其他规则")@ExcelProperty(value = "其他规则", index = 9)private String otherName;/** 失效规则 */@ApiModelProperty(value = "失效规则")@ExcelProperty(value = "上一条规则信息", index = 10)private String endChannelRuleName;/** 失效日期 */@ApiModelProperty(value = "失效日期")@ExcelProperty(value = "上一条规则失效时间", index = 11)private Date endTime;}

ExcelFillCellMergeStrategy.java

这里就是处理核心类。

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.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;import java.util.List;public class ExcelFillCellMergeStrategy implements CellWriteHandler {// 需要从第几行开始合并,0表示第1行private final int mergeRowIndex;// 合并的哪些列,比如为4时,当前行id和上一行id相同则合并前五列private final int mergeColumnRegion;private final List<Integer> ignoreColumn;public ExcelFillCellMergeStrategy(int mergeRowIndex, int mergeColumnRegion, List<Integer> ignoreColumn) {this.mergeRowIndex = mergeRowIndex;this.mergeColumnRegion = mergeColumnRegion;this.ignoreColumn = ignoreColumn;}@Overridepublic void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {CellWriteHandler.super.afterCellCreate(writeSheetHolder, writeTableHolder, cell, head, relativeRowIndex, isHead);// 隐藏id列writeSheetHolder.getSheet().setColumnHidden(0, true);}@Overridepublic void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {CellWriteHandler.super.afterCellDispose(writeSheetHolder, writeTableHolder, cellDataList, cell, head, relativeRowIndex, isHead);//当前行int curRowIndex = cell.getRowIndex();//当前列int curColIndex = cell.getColumnIndex();if (!ignoreColumn.contains(curColIndex) && curRowIndex > mergeRowIndex) {for (int i = 0; i < mergeColumnRegion; i++) {if (curColIndex <= mergeColumnRegion) {mergeWithPreviousRow(writeSheetHolder, cell, curRowIndex, curColIndex);break;}}}}/*** 当前单元格向上合并:当前行的id和上一行的id相同则合并前面(mergeColumnRegion+1)列** @param cell             当前单元格* @param curRowIndex      当前行* @param curColIndex      当前列*/private void mergeWithPreviousRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) {// 当前行的第一个CellCell curFirstCell = cell.getSheet().getRow(curRowIndex).getCell(0);Object curFirstData = curFirstCell.getCellType() == CellType.STRING ? curFirstCell.getStringCellValue() : curFirstCell.getNumericCellValue();// 上一行的第一个CellCell preFirstCell = cell.getSheet().getRow(curRowIndex - 1).getCell(0);Object preFirstData = preFirstCell.getCellType() == CellType.STRING ? preFirstCell.getStringCellValue() : preFirstCell.getNumericCellValue();// 当前cellObject data = cell.getCellType() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();// 上面的CellCell upCell = cell.getSheet().getRow(curRowIndex - 1).getCell(curColIndex);Object upData = upCell.getCellType() == CellType.STRING ? upCell.getStringCellValue() : upCell.getNumericCellValue();// 当前行的id和上一行的id相同则合并前面(mergeColumnRegion+1)列 且上一行值相同if (curFirstData.equals(preFirstData) && data.equals(upData)) {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);}}}
}

使用片段

@Overridepublic void kbdAreaRuleListToExcel(HttpServletResponse response, List<KbdAreaRuleListVO> kbdAreaRuleListVOList) {try {List<ExportKbdAreaRuleItemVO> dataList = new ArrayList<>();int size = kbdAreaRuleListVOList.size();for (int i = 0; i < size; i++) {KbdAreaRuleListVO data = kbdAreaRuleListVOList.get(i);List<KbdAreaRuleListVO.BasicDTO> doingList = data.getDoingList();int doingListSize;if (CollectionUtils.isEmpty(doingList)){doingListSize = 0;} else {doingListSize = doingList.size();}List<KbdAreaRuleListVO.BasicDTO> otherList = data.getOtherList();List<KbdAreaRuleListVO.BasicTime> timeList = data.getTimeList();int timeListSize;if (CollectionUtils.isEmpty(timeList)){timeListSize = 0;} else {timeListSize = timeList.size();}int sizeMax = Math.max(doingListSize, timeListSize);if (sizeMax > 0){// 这里需要重复填充对象for (int j = 0; j < sizeMax; j++) {ExportKbdAreaRuleItemVO exportKbdAreaRuleItemVO = ExportKbdAreaRuleItemVO.builder().id(String.valueOf(i)).bu(data.getOrgLevel1Name()).region(data.getOrgLevel2Name()).area(data.getOrgLevel3Name()).territory(data.getOrgLevel4Name()).channelBusinessLevelName(data.getChannelBusinessLevelName()).endTime(data.getEndTime()).endChannelRuleName(data.getEndChannelRuleName()).otherName(CollectionUtils.isEmpty(otherList) ? "" : otherList.stream().map(KbdAreaRuleListVO.BasicDTO::getName).collect(Collectors.joining(","))).build();if (j < doingListSize){KbdAreaRuleListVO.BasicDTO doing = doingList.get(j);exportKbdAreaRuleItemVO.setDoingName(doing.getName());}if (j < timeListSize){KbdAreaRuleListVO.BasicTime time = timeList.get(j);exportKbdAreaRuleItemVO.setRuleStartTime(time.getStartTime());exportKbdAreaRuleItemVO.setRuleEndTime(time.getEnDTime());}dataList.add(exportKbdAreaRuleItemVO);}} else {ExportKbdAreaRuleItemVO exportKbdAreaRuleItemVO = ExportKbdAreaRuleItemVO.builder().id(String.valueOf(i)).bu(data.getOrgLevel1Name()).region(data.getOrgLevel2Name()).area(data.getOrgLevel3Name()).territory(data.getOrgLevel4Name()).channelBusinessLevelName(data.getChannelBusinessLevelName()).endTime(data.getEndTime()).endChannelRuleName(data.getEndChannelRuleName()).otherName(CollectionUtils.isEmpty(otherList) ? "" : otherList.stream().map(KbdAreaRuleListVO.BasicDTO::getName).collect(Collectors.joining(","))).build();dataList.add(exportKbdAreaRuleItemVO);}}// 上面就是数据的前处理response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");response.setCharacterEncoding("utf-8");// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系String fileName = URLEncoder.encode("数据.xlsx", "UTF-8");response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);// 这里是设置这些列 不用合并的列List<Integer> ignoreColumn = new ArrayList<>();ignoreColumn.add(6);ignoreColumn.add(7);ignoreColumn.add(8);ignoreColumn.add(10);ignoreColumn.add(11);// 这里需要设置不关闭流EasyExcelFactory.write(response.getOutputStream(), ExportKbdAreaRuleItemVO.class).autoCloseStream(Boolean.TRUE).sheet("数据").registerWriteHandler(new ExcelFillCellMergeStrategy(1, 11, ignoreColumn)).doWrite(dataList);} catch (Exception e) {// 重置responselog.error("exportList导出异常:{}", e);}}

总结

这里EasyExcel,使用在java代码里进行Excel样式设定,虽然也有模板方式,但这里就不介绍了。

参考:

  • EasyExcel导出自定义合并单元格的策略
  • easyexcel中的常用注解
  • 写Excel

EasyExcel导出自定义合并单元格文件相关推荐

  1. EasyExcel导出动态合并单元格

    CellRangeAddress(起始行,终止行,起始列,终止列) public class LoopMergeStrategyQuestion extends AbstractRowWriteHan ...

  2. Web中的EasyExcel导出Excel(不创建对象且自定义合并单元格策略)

    Web中的EasyExcel导出Excel(不创建对象且自定义合并单元格策略) 适用于多张表(只查单表数据就用创建对象那种方法) Controller @RequestMapping(value = ...

  3. easyexcel 列头合并_2020-05-19:EasyExcel自定义合并单元格

    public static void writeExcelWithHeadAndData(OutputStream outputStream, List> head, List> body ...

  4. easyExcel中合并单元格文件读取实现方案

    1.需求场景描述 2.问题分析与实现方案 1.需求场景描述 现在有个业务需要按照指定模板上传选择题,并进行入表处理,使用easyExcel进行文件上传并读取数据,其中涉及合并单元数据读取问题,这里简单 ...

  5. EasyExcel 实现批量合并单元格(支持自定义)

    目录 1 Maven配置文件 2 MergeCellModel 3 CustomMergeCellHandler 4 调试代码 5 调试结果 注: 1 Maven配置文件 <!--hutool工 ...

  6. springboot项目导出excel 合并单元格表格

    springboot项目导出excel 合并单元格表格 导出效果 业务controller 业务数据 业务实体类 注解MyExcel.java 注解 MyExcels 导出工具类MyExcelUtil ...

  7. hutool导出excel大数据_Hutool excel导出并合并单元格

    一.Hutool介绍 Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以"甜甜的&q ...

  8. easypoi 模板导出兼容合并单元格功能

    最新在使用easypoi,使用注解导出和模板导出的方式,现在主要就模板导出合并单元格一些坑和解决方法. 首先我建议刚接触的同学看这篇文章,很详细,功能也比较全面,比较感谢这篇文章的原创作者,网站1:e ...

  9. easyexcel导出excel自定义合并单元格【动态表头和动态数据均可以自由合并】

    网上合并单元格的博客还是很多的,大家自行舍取吧.本文主要讲解固定与不固定的表头和内容如何合并 参考官网 https://easyexcel.opensource.alibaba.com/docs/cu ...

最新文章

  1. pta 插入排序还是堆排序
  2. EF Core数据库Provider一览
  3. 如何使用mklink命令DIY自己的Windows Live Writer——安装、保存博文到D盘
  4. Xamarin ios 教程 Xamarin跨平台开发 C#苹果应用开发
  5. 京东618:六年历程步步为营,京东商城的安全保卫战
  6. OC-NSString从文件中读取字符串
  7. bochs上网镜像怎么上网_【干货科普】上网慢!经常掉线!怎么办?
  8. IOS简单的登陆界面
  9. 【Python】Python Mako模板使用
  10. Spring Boot笔记-自定义配置项默认值设置
  11. C++高级教程之多线程
  12. 2022年版中国石油焦市场运行现状调研与前景动态分析报告
  13. 水星路由器登录界面找不到服务器,新版水星(Mercurey)路由器后台登陆界面打不开解决方法...
  14. 空气质量等级c语言编程,字节跳动2018秋招编程题——空气质量
  15. VMware 搭建私有云
  16. endata 电影票房响应数据破解
  17. 【JavaScript】浏览器
  18. Linux安装tomcat,配置环境变量
  19. php emoji表情,PHP处理字符中的emoji表情
  20. 数据库事务的四大特性:ACID

热门文章

  1. 《MySQL实战45讲》——学习笔记31 “误删数据的解决方案(删行/删表/删库/删实例)“
  2. asp.net 是什么?
  3. android 10系统下载地址,Android 10正式版
  4. java-net-php-python-jsp网络工程师在线测试系统计算机毕业设计程序
  5. 搜索量过低百度和谷歌竞价账户分别是怎么处理的
  6. 开机总是进行磁盘检查
  7. 一个菜鸟管理的学习和思考(二)
  8. 如何在手机查看电脑html
  9. 我把几个理论糅合了一下
  10. 【分布式 论文】之 1. MapReduce——Simplified Data Processing on Large Clusters