使用EasyExcel 导出复杂的合并单元格

引入pom.xml

java 复制代码
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.1.1</version>
</dependency>
java 复制代码
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.merge.AbstractMergeStrategy;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

import java.util.HashMap;
import java.util.Map;

public class CustomMergeStrategy extends AbstractMergeStrategy {
    private final int[] mergeColumns;  // 需要合并的列索引
    private final int startRow;        // 开始合并的行号
    private Map<Integer, MergeRange> lastValueMap = new HashMap<>();

    public CustomMergeStrategy(int[] mergeColumns, int startRow) {
        this.mergeColumns = mergeColumns;
        this.startRow = startRow;
    }

    @Override
    protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
        int currentRow = cell.getRowIndex();
        if (currentRow <= startRow) return;

        for (int col : mergeColumns) {
            if (cell.getColumnIndex() == col) {
                String currentValue = cell.getStringCellValue();
                MergeRange lastRange = lastValueMap.get(col);

                if (lastRange != null && lastRange.value.equals(currentValue)) {
                    // 合并到上一个范围
                    sheet.addMergedRegion(new CellRangeAddress(
                            lastRange.startRow, currentRow, col, col
                    ));
                    lastValueMap.put(col, new MergeRange(
                            lastRange.startRow, currentValue
                    ));
                } else {
                    // 新值范围开始
                    lastValueMap.put(col, new MergeRange(
                            currentRow, currentValue
                    ));
                }
                break;
            }
        }
    }

    private static class MergeRange {
        int startRow;
        String value;

        MergeRange(int startRow, String value) {
            this.startRow = startRow;
            this.value = value;
        }
    }
}

完成列头合并

复制代码
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 org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

public class ComplexHeadStrategy implements CellWriteHandler {

    public void afterCellDispose(WriteSheetHolder writeSheetHolder,
                                 WriteTableHolder writeTableHolder, Cell cell, Head head, Integer integer, Boolean aBoolean) {

        if (head.getColumnIndex() == 0 || head.getColumnIndex() == 4) { // 第一列和第五列
            Sheet sheet = writeSheetHolder.getSheet();
            if (cell.getRowIndex() == 0) { // 首行
                // 合并"引导车误差分析"标题
                sheet.addMergedRegion(new CellRangeAddress(
                        0, 0, 0, 1 // 合并前两列
                ));
                sheet.addMergedRegion(new CellRangeAddress(
                        0, 0, 4, 5 // 合并最后两列
                ));
            }
        }
    }
}

Demo实现

复制代码
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import lombok.Data;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;

import java.util.ArrayList;
import java.util.List;

public class MergeCellExportDemo {
    public static void main(String[] args) {
        List<FlightData> dataList = new ArrayList<>();
        dataList.add(new FlightData("0.12", "0.5", "B-1234", "A01", "0.08", "0.3"));
        dataList.add(new FlightData("0.15", "0.6", "B-5678", "B02", "0.09", "0.4"));

        // 自定义合并策略(合并机号列)
        CustomMergeStrategy mergeStrategy = new CustomMergeStrategy(
                new int[]{2, 3}, 1 // 合并第3、4列(机号/机位)
        );


        // 设置表头样式
        WriteCellStyle headStyle = new WriteCellStyle();
        headStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        WriteCellStyle contentStyle = new WriteCellStyle();
        contentStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        HorizontalCellStyleStrategy styleStrategy =
                new HorizontalCellStyleStrategy(headStyle, contentStyle);

        EasyExcel.write("flight_report.xlsx")
                .head(FlightData.class)
                .registerWriteHandler(new ComplexHeadStrategy()) // 复杂表头处理
                .registerWriteHandler(mergeStrategy)
                .registerWriteHandler(styleStrategy)
                .sheet("航班数据")
                .doWrite(dataList);
    }

    // 数据模型类(对应图片中的列)
    @Data
    static class FlightData {
        @ExcelProperty({"引导车开始", "开始_分析误差"})
        private String startAnalysisError;

        @ExcelProperty({"引导车开始", "开始_终端误差(s)"})
        private String startTerminalError;

        @ExcelProperty("机号")
        private String flightNumber;

        @ExcelProperty("机位")
        private String gate;

        @ExcelProperty({"引导车结束", "结束_分析误差"})
        private String endAnalysisError;

        @ExcelProperty({"引导车结束", "结束_终端误差(s)"})
        private String endTerminalError;


        public FlightData(String startAnalysisError, String startTerminalError, String flightNumber,
                          String gate, String endAnalysisError, String endTerminalError) {
            this.startAnalysisError=startAnalysisError;
            this.startTerminalError=startTerminalError;
            this.flightNumber=flightNumber;
            this.gate=gate;
            this.endAnalysisError=endAnalysisError;
            this.endTerminalError=endTerminalError;
        }


    }
}
相关推荐
圆山猫11 分钟前
[Virtualization](四):Linux KVM/RISC-V 的 vCPU 运行路径
java·linux·risc-v
城管不管1 小时前
ReAct、Plan-and-Execute、Reflection 三大智能 Agent 范式核心区别
java·人工智能·算法·spring·ai·动态规划
IT小白杨1 小时前
从环境制备到自动化工作流:多账号运营的工程化架构拆解
java·经验分享·自动化·安全架构·指纹浏览器
豆瓣鸡1 小时前
算法日记 - Day3
java·开发语言·算法
hPw0eKIqD2 小时前
使用二次封装的Excel COM 组件操作Excel\WPS ET中的区域、行和列
excel·wps
萧瑟余晖2 小时前
Java深入解析篇九之NIO详解
java·网络·nio
The Chosen One9852 小时前
高进度算法模板速记(待完善)
java·前端·算法
极光代码工作室4 小时前
基于SpringBoot的课程预约系统
java·springboot·web开发·后端开发
Leighteen5 小时前
`try-finally` 里的 `return`:为什么 `finally` 会悄悄改掉返回值、吞掉异常
java·开发语言
名字还没想好☜5 小时前
Go 的 time.After 在 select 循环里内存泄漏:定时器堆积原理与 timer.Reset 正确姿势
java·数据库·golang·go·goroutine