maven依赖

 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.0.5</version>
</dependency>

数据结构与element表格数据结构类似
多级表头使用children渲染
合并行或列通过cols和rows控制

关键代码

try (OutputStream out = new FileOutputStream(file)) {EasyExcel.write(out).head(excelHead(tableVO.getTableHead())).sheet("Sheet1").registerWriteHandler(new MyMergeStrategy(tableVO))//自定义合并单元格.registerWriteHandler(new CustomCellWriteUtil())//表头宽度自适应.doWrite(excelData(tableVO));
} catch (IOException e) {e.printStackTrace();
}
/**
* 创建表头,这里会自动合并表头** @return*/
private List<List<String>> excelHead(List<TableHeadVO> headVOS) {boolean needMerge = false;for (TableHeadVO headVO : headVOS) {if (CollectionUtils.isNotEmpty(headVO.getChildren())) {needMerge = true;}}List<List<String>> headList = new ArrayList<>();for (TableHeadVO headVO : headVOS) {if (CollectionUtils.isNotEmpty(headVO.getChildren())) {headVO.getChildren().forEach(child -> {List<String> head = new ArrayList<>();head.add(headVO.getLabel());head.add(child.getLabel());headList.add(head);});} else {List<String> head = new ArrayList<>();head.add(headVO.getLabel());if (needMerge) {head.add(headVO.getLabel());}headList.add(head);}}return headList;
}

创建数据

/*** 创建数据** @return*/
private List<List<Object>> excelData(TableVO tableVO) {int maxExcel = 32767;List<List<Object>> list = new ArrayList<>();if (Objects.isNull(tableVO) || CollectionUtils.isEmpty(tableVO.getTableData())) {return list;}List<TableHeadVO> heads = tableVO.getTableHead();for (Map<String, Object> map : tableVO.getTableData()) {List<Object> data = new ArrayList<>();for (TableHeadVO head : heads) {if (CollectionUtils.isNotEmpty(head.getChildren())) {head.getChildren().forEach(child -> {Object value = map.get(child.getProp());if ((value + "").length() >= maxExcel) {data.add((value + "").substring(0, 255) + "字符过长");} else {data.add(value);}});} else {Object value = map.get(head.getProp());if ((value + "").length() >= maxExcel) {data.add((value + "").substring(0, 255) + "字符过长");} else {data.add(value);}}}list.add(data);}return list;
}

表格数据

import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.List;
import java.util.Map;@NoArgsConstructor
@AllArgsConstructor
@Data
public class TableVO {@ApiModelProperty("表头信息")private List<TableHeadVO> tableHead;@ApiModelProperty("表格数据信息")private List<Map<String, Object>> tableData;@ApiModelProperty("总数据")private long total;@ApiModelProperty("总页数")private long totalPage;@ApiModelProperty("当前页:从1开始")private long pageIndex;@ApiModelProperty("页大小")private long pageSize;public TableVO(List<TableHeadVO> tableHead, List<Map<String, Object>> tableData) {this.tableHead = tableHead;this.tableData = tableData;}public static TableVO of(PageResult result) {TableVO tableVO = new TableVO();tableVO.setPageIndex(result.getPageIndex());tableVO.setPageSize(result.getPageSize());tableVO.setTotal(result.getTotalCount());tableVO.setTotalPage(result.getTotalPage());return tableVO;}
}

表头对象


import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.List;@Builder
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("表头")
@Data
public class TableHeadVO {@ApiModelProperty("是否合并")private boolean merge = true;@ApiModelProperty("表格的字段")private String prop;@ApiModelProperty("表格字段名称")private String label;@ApiModelProperty("字段类型:input,textarea,number,radio,checkbox,time,date,rate,color,select,switch,slider,text,link,imgupload,fileupload,table,grid,report,divider")private String type;@ApiModelProperty("是否为导出excel字段")private Boolean excel;@ApiModelProperty("宽度")private String width;@ApiModelProperty("后台自定义")private Boolean custom = false;@ApiModelProperty("子表单")private List<TableHeadVO> children;public TableHeadVO(String prop, String label) {this.prop = prop;this.label = label;this.type = "input";this.excel = true;this.width = "auto";}
}

最效果

数据合并


import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.merge.AbstractMergeStrategy;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;import java.util.ArrayList;
import java.util.List;
import java.util.Map;@Slf4j
public class MyMergeStrategy extends AbstractMergeStrategy {//合并坐标集合private List<CellRangeAddress> cellRangeAddresses;public MyMergeStrategy(TableVO tableVO) {List<CellRangeAddress> list = new ArrayList<>();// 判断是否需要合并,需要合并的再构造合并的地址boolean needMerge = false;for (TableHeadVO headVO : tableVO.getTableHead()) {if (CollectionUtils.isNotEmpty(headVO.getChildren())) {needMerge = true;}}if (needMerge) {// 重新定义headList<TableHeadVO> heads = new ArrayList<>();for (TableHeadVO headVO : tableVO.getTableHead()) {if (CollectionUtils.isNotEmpty(headVO.getChildren())) {for (TableHeadVO child : headVO.getChildren()) {heads.add(child);}} else {heads.add(headVO);}}if (CollectionUtils.isNotEmpty(tableVO.getTableData())) {for (int i = 0; i < tableVO.getTableData().size(); i++) {Map<String, Object> data = tableVO.getTableData().get(i);System.out.println("第" + i + "行数据" + data);for (int j = 0; j < heads.size(); j++) {TableHeadVO head = heads.get(j);int cols = (int) data.getOrDefault("cols", 1);int rows = (int) data.getOrDefault("rows", 1);// 判断有没有孩子,有孩子的不用合并,没孩子的都要合并,根据所占列数合并if (rows > 1 && head.isMerge()) {System.out.print(head.getLabel());log.info("{}, {} , {}, {}", i + 1 + 1, i + 1 + rows, j, j);// row 行,col列// int firstRow, int lastRow, int firstCol, int lastColCellRangeAddress item = new CellRangeAddress(i + 1 + 1, i + 1 + rows, j, j);list.add(item);}}System.out.println();}}}this.cellRangeAddresses = list;}/*** 合并操作:对每个单元格执行!!!** @param sheet            sheet对象* @param cell             当前单元格* @param head             表头对象* @param relativeRowIndex 相关行索引*/@Overrideprotected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {/** 合并单元格** 由于merge()方法会在写每个单元格(cell)时执行,因此需要保证合并策略只被添加一次。否则如果每个单元格都添加一次* 合并策略,则会造成重复合并。例如合并A2:A3,当cell为A2时,合并A2:A3,但是当cell为A3时,又要合并A2:A3,而此时* 的A2已经是之前的A2和A3合并后的结果了。* 由于此处的表头占了两行,因此数据单元格是从(2, 0)开始的,所以就对这个单元格(cell.getRowIndex() == 2 && cell.getColumnIndex() == 0)* 添加一次合并策略就可以了。如果表头只有一行,则判断条件改为「cell.getRowIndex() == 1 && cell.getColumnIndex() == 0」就可以了。*/if (cell.getRowIndex() == 2 && cell.getColumnIndex() == 0) {for (CellRangeAddress item : cellRangeAddresses) {sheet.addMergedRegion(item);}}/** 如果不作判断,可以使用addMergedRegionUnsafe()方法,* 这样生成的Excel文件可以打开,只是打开时会提示内容有问题,修复后可以打开*/// for (CellRangeAddress item : cellRangeAddresses) {//     sheet.addMergedRegionUnsafe(item);// }}
}

列宽自适应


import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.CellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import org.apache.poi.ss.usermodel.Cell;import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** excel自适应列宽 拉过去就能直接套用自动适应列宽*/
public class CustomCellWriteUtil extends AbstractColumnWidthStyleStrategy {private Map<Integer, Map<Integer, Integer>> CACHE = new HashMap(8);public CustomCellWriteUtil() {}protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {boolean needSetWidth = isHead;if (needSetWidth) {Map<Integer, Integer> maxColumnWidthMap = CACHE.get(writeSheetHolder.getSheetNo());if (maxColumnWidthMap == null || maxColumnWidthMap.isEmpty()) {maxColumnWidthMap = new HashMap(16);CACHE.put(writeSheetHolder.getSheetNo(), maxColumnWidthMap);}Integer columnWidth = this.dataLength(cellDataList, cell, true);if (columnWidth >= 0) {if (columnWidth > 255) {columnWidth = 255;}Integer maxColumnWidth = (Integer) ((Map) maxColumnWidthMap).get(cell.getColumnIndex());if (maxColumnWidth == null || columnWidth > maxColumnWidth) {((Map) maxColumnWidthMap).put(cell.getColumnIndex(), columnWidth);writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), (int) ((columnWidth + 0.72) * 256));}}}}private Integer dataLength(List<WriteCellData<?>> cellDataList, Cell cell, Boolean isHead) {if (isHead) {return cell.getStringCellValue().getBytes().length;} else {CellData cellData = cellDataList.get(0);CellDataTypeEnum type = cellData.getType();if (type == null) {return -1;} else {switch (type) {case STRING:return cellData.getStringValue().getBytes().length;case BOOLEAN:return cellData.getBooleanValue().toString().getBytes().length;case NUMBER:return cellData.getNumberValue().toString().getBytes().length;default:return -1;}}}}
}

参考文献

easyexcel(十一):easyexcel动态表头,表头合并
EasyExcel 动态表头 + 数据单元格合并
Easyexcel 动态导出多行表头(非注解)

EasyExcel导出excel合并表头和数据相关推荐

  1. easyexcel读取excel合并单元格数据

    普通的excel列表,easyexcel读取是没有什么问题的.但是,如果有合并单元格,那么它读取的时候,能获取数据,但是数据是不完整的.如下所示的单元格数据: 我们通过简单的异步读取,最后查看数据内容 ...

  2. 【Easyexcel】使用easyexcel导出带有固定表头的excel代码,并且有合并单元格操作

    以下是一个使用EasyExcel导出带有固定表头并且带有合并单元格的Excel代码示例: //创建excel对象 ExcelWriter writer = EasyExcel.write(" ...

  3. EasyExcel导入导出excel 复杂表头 表头校验 数据校验

    目录 EasyExcel特点 一.导入excel案例 二.读取excel的相关技术点 1.读取excel的方式 2.读取sheet数量 3.指定从第几行开始读数据 三.导出excel 1.前端发起请求 ...

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

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

  5. easyexcel导出excel,大数据量100万以内分页查询zip格式导出

    easyexcel导出excel,大数据量100万以内分页查询zip格式导出 准备工作 整体思路 controller层 service层 mapper层 VO 表结构 测试 备注 easyExcel ...

  6. EasyExcel导出自定义合并单元格文件

    目标 需要使用阿里的EasyExcel库来导出excel,并要自定义合并单元格. 思路 这里自定义的CellWriteHandler需要将数据进行如下处理: 1.Excel每一行数据必须对应一个对象: ...

  7. java多表头导出excel表格_【每日一点】1. Java如何实现导出Excel单表头或多表头

    一.背景 在后台项目中,经常会遇到将呈现的内容导出到Excel的需求,通过都是导出单个表头的Excel文件,如果存在级联关系的情况下,也就需要导出多表头的场景.今天这篇文章就是分享导出Excel单表头 ...

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

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

  9. Java 使用EasyExcel导出excel文件

    Java 使用EasyExcel导出excel文件 一.引入pom依赖 二.导出实体 三. 生成excelController 四.效果 一.引入pom依赖 <dependency>< ...

最新文章

  1. SLAM的开源以及在移动端AR的适用分析
  2. JavaScript面向对象及原型 及setTimeout
  3. matplotlib xticks 基于 旋转_数据可视化之 matplotlib 绘图篇
  4. 【Spring学习】spring提供的三种定时任务
  5. 昆山立讯电子工程师_教会徒弟饿死师傅?立讯精密会不会成为第二个富士康
  6. [CTO札记]从Cloud Computing看战略决策:想做、能做与可做 -
  7. HttpUtility.UrlEncode 方法
  8. 百度回应“宕机”;微信 5 年内出 VR 版?腾讯破解谷歌漏洞 | 极客头条
  9. Linux 中的 【 TOP 】 命令,查看CUP的使用率
  10. 将图片转化为txt文本显示
  11. centos mysql5.7主从同步配置_centos 7 配置 mysql 5.7 主从复制
  12. TiledMap简单使用
  13. 自制ArduinoUno R3开发板,烧写bootloader
  14. 怎么用云便签实现家里的电脑和办公室电脑的数据共享?
  15. 【VBA研究】关于工作表单元格复制粘贴的语句
  16. TP路由器的ip映射配置
  17. (EKL)elasticsearch
  18. java实现导出内容不固定的word文档
  19. 计算机英语versatile意思,英语单词versatile是什么意思,英文单词查询versatile,在线单词versatile翻译...
  20. JavaSE详细总结——万字纯手码

热门文章

  1. 只需5分钟看完这篇 HTTPS,去阿里面试和面试官扯皮就没问题了!
  2. LinkedHashMap、LinkedHashSet、LinkedList哪个最适合当作Stack使用?
  3. python闭包修改全局变量_Python 闭包 自由变量
  4. 微信小程序如何在浏览器运行
  5. PHP 三联截骨,髋臼三联截骨术可否改善年轻成年DDH患者的远期预后
  6. C++提高编程(3/3)
  7. 家长如何使用pdfFactory Pro虚拟打印机制作试卷
  8. 【Python实战】2022年中国富豪榜出炉,首富竟是他......教你一键采集榜单并做可视化效果图(今天是拉仇恨的一天鸭~)
  9. 快解析的ERP远程管理解决方案
  10. 手机端H5页面上调试打印console的方法