1、先看效果图
2、表头自适应类
根据自己的业务需求来写,下面只是个例子
public class RwhzCustemhandler extends AbstractColumnWidthStyleStrategy { private static final int MAX_COLUMN_WIDTH = 255; //the maximum column width in Excel is 255 characters public RwhzCustemhandler() { } @Override protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { if (isHead) { int columnWidth = cell.getStringCellValue().getBytes().length; if (columnWidth > MAX_COLUMN_WIDTH) { columnWidth = MAX_COLUMN_WIDTH; } else { if (cell.getColumnIndex() == 1) { columnWidth = columnWidth + 10; } else { columnWidth = columnWidth + 3; } } writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 256); } } }
3、根据某些列来合并相同内容的行单元格类
public class RwhzMergeCell implements CellWriteHandler { //需要哪些列的行进行合并 private int[] mergeColumnIndex; //从哪一行开始,表头如果是只有一行,数据从第二行开始,即索引1开始 private int mergeRowIndex; public RwhzMergeCell() { } public RwhzMergeCell(int mergeRowIndex, int[] mergeColumnIndex) { this.mergeRowIndex = mergeRowIndex; this.mergeColumnIndex = mergeColumnIndex; } @Override public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) { } @Override public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { } @Override public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { } @Override public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { //当前行 int curRowIndex = cell.getRowIndex(); //当前列 int curColIndex = cell.getColumnIndex(); if (curRowIndex > mergeRowIndex) { for (int columnIndex : mergeColumnIndex) { if (curColIndex == columnIndex) { //符合条件,调用自定义合并函数 mergeWithPrevRow(writeSheetHolder, cell, curRowIndex, curColIndex); break; } } } } private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) { //获取当前行当前列的单元格的值,可能是String类型,也可能是数值 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 (preData.equals(curData)) { 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); } } } }
4、API接口方法
@GetMapping("/exportExcel") public void exportExcel(@RequestParam(value = "ztId") int ztId, @RequestParam(value = "rwszId") int rwszId, @RequestParam(value = "bmId", required = false, defaultValue = "") Integer bmId, @RequestParam(value = "qjszId") int qjszId, @RequestParam(value = "sbzt", required = false, defaultValue = "") Boolean sbzt, @RequestParam(value = "wsjbxs") boolean wsjbxs, @RequestParam(value = "wcqk") boolean wcqk, HttpServletResponse response) throws IOException { //获取内容集合 List<RwhzView> rwhzViews = getRwhzViews(ztId, rwszId, bmId, qjszId, sbzt, wsjbxs, wcqk); //获取表头集合 List<RwhzHead> heads = setRwhzHeads(rwszId); response.setHeader("Content-Disposition", "attachment; filename=rwhz.xls"); // 响应类型,编码 response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); //因为我业务的表头是不固定的,而且顺序也是按业务规则排序的,可以随缘改变表头的位置,我要合并的列是“工作内容”表头对应的数据,一下是找到“工作内容”所处的位置index int index = 0; for (int i = 0; i < heads.size(); i++) { if ("工作内容".equals(heads.get(i).getXmmc())) { index = i; break; } } //因为表头需要额外加上一列序号、任务、期间,那么此时工作内容的位置应该往后移3,工作内容相同的需要也相同,序号也需要合并相同的 int[] mergeColumnIndex = {0, index + 3}; //自定义获取需要导出的数据,根据自己的业务需求来 List<List<Object>> exportData = getExportData(heads, rwhzViews, index); EasyExcel.write(response.getOutputStream()) .registerWriteHandler(new RwhzCustemhandler()) .registerWriteHandler(new RwhzMergeCell(1, mergeColumnIndex)) .sheet("任务汇总导出模板").head(getExcelHeader(heads)).doWrite(exportData); } private List<List<String>> getExcelHeader(List<RwhzHead> heads) {//...}
5、重要的是那两个类
不确定表头主要是head(List<List<String>>).doWrite( List<List<Object>>)
(1)对于head的List<List<String>>
外面的List的每一个元素List<String>代表每一列表头,里面的List的每一个元素String代表每一行的表头,比如
List<List<String>>heads=new ArrayList<>(); List<String> list1 = Arrays.asList("AAA", "aaa"); List<String> list2 = Arrays.asList("AAA", "bbb"); List<String> list3 = Arrays.asList("AAA", "ccc"); heads.add(list1 ); heads.add(list2 ); heads.add(list3 );
形成的表头如下图所示
图片画的不够标准,大概是那样。
(2)对于数据的List<List<Object>>
外面的List的每一个元素List<Object>代表一行数据,里面List的每一个元素Object代表当前行每一列的数据。
合并单元格参考网址:https://blog.csdn.net/weixin_50067580/article/details/111559637