一、合并前后对比
合并前效果
合并后效果
二、如何合并
自定义一个内容相同合并的处理拦截器
import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.write.handler.CellWriteHandler; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; import com.alibaba.excel.write.metadata.holder.WriteTableHolder; import lombok.Data; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.util.CellRangeAddress; import java.util.List; @Data public class ExcelFillCellMergeStrategy implements CellWriteHandler { /** * 合并字段的下标,如第一到五列new int[]{0,1,2,3,4} */ private int[] mergeColumnIndex; /** * 从第几行开始合并,如果表头占两行,这个数字就是2 */ private int mergeRowIndex; public ExcelFillCellMergeStrategy() { } public ExcelFillCellMergeStrategy(int mergeRowIndex, int[] mergeColumnIndex) { this.mergeRowIndex = mergeRowIndex; this.mergeColumnIndex = mergeColumnIndex; } @Override public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer integer, Integer integer1, Boolean aBoolean) { } @Override public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer integer, Boolean aBoolean) { } @Override public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer integer, Boolean aBoolean) { } @Override public 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(); Cell preCell = cell.getSheet().getRow(curRowIndex - 1).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); } } } }
使用
.registerWriteHandler(new ExcelFillCellMergeStrategy(1,new int[]{0,1,2,3,4,5}))
public static <T> void writeTwo(HttpServletResponse response, String filename, String sheetName, Class<T> head, List<T> data) throws IOException { // 输出 Excel EasyExcel.write(response.getOutputStream(), head) .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 .registerWriteHandler(new ExcelFillCellMergeStrategy(1,new int[]{0,1,2,3,4,5})) // 基于 column 长度,自动适配。最大 255 宽度 .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) .sheet(sheetName).doWrite(data); // 设置 header 和 contentType。写在最后的原因是,避免报错时,响应 contentType 已经被修改了 response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8")); response.setContentType("application/vnd.ms-excel;charset=UTF-8"); }
三、其他代码
//获取数据 List<SystemPlanApply> list = applyService.export(reqVO); List<PlanExcelVO> excellist = list.stream().map(it -> { List<PlanExcelVO> res = new ArrayList<>(); //业务处理 return res; }).flatMap(it -> it.stream()).collect(Collectors.toList()); ExcelUtils.writeTwo(response, "工作日志.xls", "工作日志表", PlanExcelVO.class, excellist);
/** * 用户 Excel 导出 VO */ @Data @ApiModel("管理后台 - 日志导出") public class PlanExcelVO { /** * ID */ @ApiModelProperty(value = "ID") @ExcelProperty("ID") private Long id; /** * 计划年月(月计划、周计划) */ @ApiModelProperty(value = "计划年月(月计划、周计划)") @ExcelProperty("计划月") private Integer planYearMonth; @ExcelProperty("申请人") private String systemUserName; /** * 计划类型:1、年计划;2、月计划;3、周计划;4、日计划;5、年报;6、月报;7、周报;8、日报; */ @ApiModelProperty(value = "计划类型:1、年计划;2、月计划;3、周计划;4、日计划;5、年报;6、月报;7、周报;8、日报;") @ExcelProperty("报告类型") private String type; @ExcelProperty("提交时间") private Date createTime; /** * 编号 */ @ApiModelProperty(value = "编号") @ExcelProperty("编号") private Integer detailNo; /** * 优先级 */ @ApiModelProperty(value = "优先级") @ExcelProperty(value = "优先级", converter = DictConvert.class) @DictFormat(DictTypeConstants.PLAN_PRIORITY) //plan_priority private String priority; /** * 标题 */ @ApiModelProperty(value = "标题") @ExcelProperty("标题") private String title; }