EasyExcel导出自定义列,合并列,合并行,动态表格表头

全部代码已经粘贴本文,直接复制到自己路径下,
各位CRUD的大神们把引用路径改一下即可
package com.springboot.cli.controller;
import com.springboot.cli.domain.TestDynamicModel;
import com.springboot.cli.handler.ExcelRowMergeHandler;
import com.springboot.cli.handler.ExcelColumnMergeHandler;
import com.springboot.cli.utils.EasyExcelUtils;

详细的工具类可以参照使用案例进行模仿。
主要是解决一些EasyExcel原生功能的缺陷,不过EasyExcel可以注入事件处理器,来扩展更复杂的操作,

EasyExcel工具类

  1. exportExcelInclude:按指定顺序/默认顺序导出
  2. writeDynamicExcel: 动态列导出(支持行列合并)

动态表格行合并列合并样式
EasyExcel导出自定义列,合并列,合并行,动态表格表头_第1张图片

Excel事件处理器:(代码注释中有详细使用的描述)
主要是做一些特殊的操作(行列合并,列顺序排序)

  1. ExcelColumnMergeHandler
  2. ExcelRowMergeHandler
  3. SortRowWriteHandler

POM依赖

       <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>fastjsonartifactId>
            <version>1.2.75version>
        dependency>
        <dependency>
            <groupId>cn.hutoolgroupId>
            <artifactId>hutool-allartifactId>
            <version>5.8.0.M2version>
        dependency>
        
            
            
            
        

        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>easyexcelartifactId>
            <version>2.2.10version>
        dependency>
        <dependency>
            <groupId>com.google.guavagroupId>
            <artifactId>guavaartifactId>
            <version>27.0.1-jreversion>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-validationartifactId>
        dependency>

使用案例教学

package com.springboot.cli.controller;


import com.springboot.cli.domain.TestDynamicModel;
import com.springboot.cli.handler.ExcelRowMergeHandler;
import com.springboot.cli.handler.ExcelColumnMergeHandler;
import com.springboot.cli.utils.EasyExcelUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;


/**
 * 动态表头测试案例
 *
 */
@Slf4j
@RestController
@RequestMapping("/TestDynamicHeadWriteController")
public class TestDynamicHeadWriteController {

    /**
     * 动态头,实时生成头写入
     * 思路是这样子的,先创建List头格式的sheet仅仅写入头,然后通过table 不写入头的方式 去写入数据
     *
     * 表格样式:
     *
     * 合计	合计	日期	日期	日期	日期	日期	日期	日期	日期
     * 名称	分数	日期	日期	日期	日期	日期	日期	日期	日期
     * 二梦	10	0	1	2	3	4	5	6	7
     * 二梦	10	0	1	2	3	4	5	6	7
     * 小计	小计	0	1	2	3	4	5	6	7
     * 黑妮	10	0	1	2	3	4	5	6	7
     * 黑妮	10	0	1	2	3	4	5	6	7
     * 黑妮	10	0	1	2	3	4	5	6	7
     * 小计	小计	0	1	2	3	4	5	6	7
     * 老王	10	0	1	2	3	4	5	6	7
     * 老王	10	0	1	2	3	4	5	6	7
     * 老王	10	0	1	2	3	4	5	6	7
     * 老王	10	0	1	2	3	4	5	6	7
     * 小计	小计	0	1	2	3	4	5	6	7
     *
     */
    @PostMapping("dynamicHeadWrite")
    public void dynamicHeadWrite(HttpServletResponse response) throws IOException {
        /*如果需要本地目录生成:改为“EasyExcel.write(fileName)”就可以了。*/
        String fileName = "C:\\Users\\Administrator\\Downloads\\" + "fileExcel" + System.currentTimeMillis() + ".xlsx";

        String name = "webExcel"+System.currentTimeMillis();

        /*web下载*/
        /*定义编码,格式*/
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        String exportFileName = URLEncoder.encode(name, "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + exportFileName + ".xlsx");
        response.setHeader("filename", exportFileName + ".xlsx");
        response.setHeader("Access-Control-Expose-Headers", "filename");

        /*生成测试数据*/
        List<TestDynamicModel> data = dataTemp();
        /*数据转换为List>*/
        List<List<Object>> dataList = convertToListListObj(data);
        /*通过自定义处理行合并*/
        ExcelRowMergeHandler colMerge = getColMerge(dataList);


        /*写入表格*/
        //EasyExcelUtils.writeDynamicExcel(response, head(),dataList);

        /*
         * 写入表格
         * 如果需要自定义行合并,自定义列合并
         * */
        EasyExcelUtils.writeDynamicExcel(response,head(), dataList,new ExcelColumnMergeHandler(1, new int[]{0,1}),colMerge);
    }


    /**
     * 通过自定义处理行合并
     *
     * @param list 加油卡消费汇总数据
     * @return 行合并处理器
     */
    private ExcelRowMergeHandler getColMerge(List<List<Object>> list) {
        /*创建列合并工具类对象*/
        ExcelRowMergeHandler mergePrevCol = new ExcelRowMergeHandler();
        /*动态合并*/
        for (int i = 0 ; i < list.size() ; i++ ){
            List<Object> objects = list.get(i);
            if ("小计".equals(objects.get(0).toString())) {
                /*从i+2 行操作 在第0列合并2列*/
                mergePrevCol.add(i + 2, 0, 1);
            }
        }
        return mergePrevCol;
    }

    /**
     * 数据转换为List>
     * TestDynamicModel.dataList是动态数据的部分,数量不确定
     *
     * @param data 数据库查询出的数据
     * @return
     */
    private List<List<Object>> convertToListListObj(List<TestDynamicModel> data) {
        List<List<Object>> rootList = new ArrayList<>();
        for (TestDynamicModel datum : data) {
            List<Object> rowData = new ArrayList<>();
            rowData.add(datum.getName());
            rowData.add(datum.getScore());
            for (Integer dataTemp : datum.getDataList()) {
                rowData.add(dataTemp);
            }
            rootList.add(rowData);
        }

        return rootList;
    }


    /**
     * 生成测试数据
     * @return
     */
    private List<TestDynamicModel> dataTemp() {
        List<Integer> dataList = new ArrayList<>();
        for (int i = 0; i < 8; i++) {
            dataList.add(i);
        }
        TestDynamicModel dynamicModel = new TestDynamicModel()
                .setName("二梦")
                .setScore("10")
                .setDataList(dataList)
                ;
        TestDynamicModel dynamicModel2 = new TestDynamicModel()
                .setName("黑妮")
                .setScore("10")
                .setDataList(dataList)
                ;
        TestDynamicModel dynamicModel3 = new TestDynamicModel()
                .setName("老王")
                .setScore("10")
                .setDataList(dataList)
                ;
        TestDynamicModel dynamicModelTotal = new TestDynamicModel()
                .setName("小计")
                .setScore("小计")
                .setDataList(dataList)
                ;
        List<TestDynamicModel> list = new ArrayList<>();
        list.add(dynamicModel);
        list.add(dynamicModel);
        list.add(dynamicModelTotal);
        list.add(dynamicModel2);
        list.add(dynamicModel2);
        list.add(dynamicModel2);
        list.add(dynamicModelTotal);
        list.add(dynamicModel3);
        list.add(dynamicModel3);
        list.add(dynamicModel3);
        list.add(dynamicModel3);
        list.add(dynamicModelTotal);
        return list;
    }


    /**
     * 测试表头
     *
     * 每一个list中代表一列表头,横向相同时自动合并
     *
     * @return
     */
    private List<List<String>> head() {
        List<List<String>> list = new ArrayList<List<String>>();
        List<String> head0 = new ArrayList<String>();
        head0.add("合计");
        head0.add("名称"  );
        List<String> head1 = new ArrayList<String>();
        head1.add("合计");
        head1.add("分数"  );
        List<String> head2 = new ArrayList<String>();
        head2.add("日期"  );
        head2.add("日期"  );
        List<String> head3 = new ArrayList<String>();
        List<String> head4 = new ArrayList<String>();
        List<String> head5 = new ArrayList<String>();
        List<String> head6 = new ArrayList<String>();
        List<String> head7 = new ArrayList<String>();
        List<String> head8 = new ArrayList<String>();
        head3 .add("日期"  );
        head4 .add("日期"  );
        head5 .add("日期"  );
        head6 .add("日期"  );
        head7 .add("日期"  );
        head8 .add("日期"  );
        head3 .add("日期"  );
        head4 .add("日期"  );
        head5 .add("日期"  );
        head6 .add("日期"  );
        head7 .add("日期"  );
        head8 .add("日期"  );


        list.add(head0);
        list.add(head1);
        list.add(head2);
        list.add(head2);
        list.add(head3);
        list.add(head4);
        list.add(head5);
        list.add(head6);
        list.add(head7);
        list.add(head8);
        return list;
    }
}

实体类

package com.springboot.cli.domain;

import lombok.Data;
import lombok.experimental.Accessors;

import java.util.List;


@Data
@Accessors(chain = true)
public class TestDynamicModel {
    String name;
    String  score;
    /*动态数据的部分,数量不确定*/
    List<Integer> dataList;
}

EasyExcel工具类

package com.springboot.cli.utils;


import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.springboot.cli.handler.ExcelColumnMergeHandler;
import com.springboot.cli.handler.ExcelRowMergeHandler;
import com.springboot.cli.handler.SortRowWriteHandler;
import org.springframework.util.CollectionUtils;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * EasyExcel工具类
 */
public class EasyExcelUtils {


    /**
     * 注入的具有排序功能的handle
     */
    private static final SortRowWriteHandler SORT_ROW_WRITE_HANDLER = new SortRowWriteHandler();

    /**
     * 导出excel-按指定顺序
     *
     * @param       类(必须是小写,并遵守阿里开发规范)
     * @param list     列表数据
     * @param fileName 文件名称
     * @param useDefaultStyle   是否使用默认样式
     * @param response 响应
     */
    public static <T> Map<String, Object> exportExcelInclude(List<T> list, String fileName,Boolean useDefaultStyle, HttpServletResponse response) throws IOException {
        Map<String, Object> resultMap = new HashMap<>();
        if (list.size() == 0) {
            resultMap.put("msg", "数据长度为空");
            resultMap.put("result", -1);
            return resultMap;
        }

        Class<?> clazz = list.get(0).getClass();

        /*定义编码,格式*/
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        String exportFileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + exportFileName + ".xlsx");
        response.setHeader("filename", exportFileName + ".xlsx");
        response.setHeader("Access-Control-Expose-Headers", "filename");
        /**
         * .head(head(headNameList))
         * 改行代码不可以用ExcelWriterSheetBuilder类型的变量单独写,否则会包错:表头长度与字段列表长度不符
         */
        /*导出表格*/
        ExcelWriterSheetBuilder excelWriterSheetBuilder = EasyExcel.write(response.getOutputStream(), clazz)
                .useDefaultStyle(useDefaultStyle)
                .sheet(fileName);
        excelWriterSheetBuilder.doWrite(list);

        resultMap.put("msg", "excel export success");
        resultMap.put("result", 0);
        return resultMap;
    }


    /**
     * 导出excel-默认顺序
     *
     * @param       类(必须是小写,并遵守阿里开发规范)
     * @param list     列表数据
     * @param fileName 文件名称
     * @param useDefaultStyle   是否使用默认样式
     * @param response 响应
     * @param headNameList
     * @param columnList 设置列字段(必须是小写)
     */
    public static <T> Map<String, Object> exportExcelInclude(List<T> list, String fileName,Boolean useDefaultStyle, HttpServletResponse response, List<String> columnList, List<String> headNameList) throws IOException {
        Map<String, Object> resultMap = new HashMap<>();
        if (list.size() == 0) {
            resultMap.put("msg", "数据长度为空");
            resultMap.put("result", -1);
            return resultMap;
        }

        if (CollectionUtils.isEmpty(headNameList)) {
            /*设置表头*/
            resultMap.put("msg", "表头长度为空");
            resultMap.put("result", -2);
            return resultMap;
        }

        if (CollectionUtils.isEmpty(columnList)) {
            /*设置列字段*/
            resultMap.put("msg", "列字段长度为空");
            resultMap.put("result", -3);
            return resultMap;
        }

        Class<?> clazz = list.get(0).getClass();

        /*定义编码,格式*/
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        String exportFileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + exportFileName + ".xlsx");
        response.setHeader("filename", exportFileName + ".xlsx");
        response.setHeader("Access-Control-Expose-Headers", "filename");
        /**
         * .head(head(headNameList))
         * 改行代码不可以用ExcelWriterSheetBuilder类型的变量单独写,否则会包错:表头长度与字段列表长度不符
         */
        /*导出表格*/
        ExcelWriterSheetBuilder excelWriterSheetBuilder = EasyExcel.write(response.getOutputStream(), clazz)
                .head(head(headNameList))
                /*设置列字段(会按顺序)*/
                .includeColumnFiledNames(columnList)
                /*注入的具有排序功能的handle*/
                .registerWriteHandler(SORT_ROW_WRITE_HANDLER)
                .useDefaultStyle(useDefaultStyle)
                .sheet(fileName);
        excelWriterSheetBuilder.doWrite(list);

        resultMap.put("msg", "excel export success");
        resultMap.put("result", 0);
        return resultMap;
    }


    /**
     * 支持动态头,行列合并导出
     *
     * @param response web响应
     * @param headList 动态头
     * @param dataList 数据列表
     * @param columnMergeHandler 注入列合并
     * @param rowMergeHandler 注入行合并
     * @throws IOException
     */
    public static Map<String, Object> writeDynamicExcel(HttpServletResponse response, @NotEmpty List<List<String>> headList, @NotEmpty List<List<Object>> dataList, @NotNull ExcelColumnMergeHandler columnMergeHandler,@NotNull ExcelRowMergeHandler rowMergeHandler) throws IOException {
        Map<String, Object> resultMap = new HashMap<>();
        if (dataList.size() == 0) {
            resultMap.put("msg", "数据长度为空");
            resultMap.put("result", -1);
            return resultMap;
        }

        if (CollectionUtils.isEmpty(headList)) {
            /*设置表头*/
            resultMap.put("msg", "表头长度为空");
            resultMap.put("result", -2);
            return resultMap;
        }

        if (columnMergeHandler == null || rowMergeHandler == null) {
            /*设置列字段*/
            resultMap.put("msg", "行列处理器为空");
            resultMap.put("result", -3);
            return resultMap;
        }

        EasyExcel.write(response.getOutputStream())
                // 设置动态头
                .head(headList)
                .sheet("模板")
                /*注入列合并*/
                .registerWriteHandler(columnMergeHandler)
                /*注入行合并*/
                .registerWriteHandler(rowMergeHandler)
                /*传需要写入的数据,类型List>*/
                .doWrite(dataList);

        resultMap.put("msg", "excel export success");
        resultMap.put("result", 0);
        return resultMap;
    }


    /**
     * 支持动态头导出
     *
     * @param response web响应
     * @param headList 动态头
     * @param dataList 数据列表
     * @throws IOException
     */
    public static void writeDynamicExcel(HttpServletResponse response,List<List<String>> headList, List<List<Object>> dataList) throws IOException {
        EasyExcel.write(response.getOutputStream())
                // 设置动态头
                .head(headList)
                .sheet("模板")
                /*传需要写入的数据,类型List>*/
                .doWrite(dataList);
    }


    /**
     * 设置Excel头
     *
     * @param headList  Excel头信息
     * @return
     */
    public static List<List<String>> head(List<String> headList) {
        List<List<String>> list = new ArrayList<>();
        for (String value : headList) {
            List<String> head = new ArrayList<>();
            head.add(value);
            list.add(head);
        }
        return list;
    }

    /**
     * Excel头对应的字段转换小写
     *
     * @param ColumnListTemp Excel字段
     * @return
     */
    public static List<String> castLowerCase(List<String> ColumnListTemp) {
        List<String> ColumnList = new ArrayList<>();
        for (String name : ColumnListTemp) {
            ColumnList.add(StrUtil.lowerFirst(name));
        }
        return ColumnList;
    }

}

列合并处理器

package com.springboot.cli.handler;


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 ExcelColumnMergeHandler implements CellWriteHandler {
    /**
     * 合并字段的下标,如new int[]{0,1,2,3,4,10},0-4合并,10合并
     */
    private int[] mergeColumnIndex;
    /**
     * 从第几行开始合并,如果表头占两行,这个数字就是2
     */
    private int mergeRowIndex;

    public ExcelColumnMergeHandler() {
    }

    public ExcelColumnMergeHandler(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);
            }
        }
    }
}

行合并处理器


package com.springboot.cli.handler;


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

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

/**
 * 行合并处理器
 *
 */
public class ExcelRowMergeHandler implements CellWriteHandler {
    private static final String KEY ="%s-%s";
    //所有的合并信息都存在了这个map里面
    Map<String, Integer> mergeInfo = new HashMap<>();

    public ExcelRowMergeHandler() {
    }
    public ExcelRowMergeHandler(Map<String, Integer> mergeInfo) {
        this.mergeInfo = mergeInfo;
    }

    @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();

        Integer num = mergeInfo.get(String.format(KEY, curRowIndex, curColIndex));
        if(null != num){
            // 合并最后一行 ,列
            mergeWithPrevCol(writeSheetHolder, cell, curRowIndex, curColIndex,num);
        }
    }

    public void mergeWithPrevCol(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex, int num) {
        Sheet sheet = writeSheetHolder.getSheet();
        CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex, curRowIndex, curColIndex, curColIndex + num);
        sheet.addMergedRegion(cellRangeAddress);
    }


    /**
     * 添加合并行
     *      比如参数:(3,0,2)
     *      从第三行的地0列开始向右合并2格
     *      这样,第三行的地0列到第2列就会合并
     *
     * 注意:
     *      合并不能冲突,如果合并到已经被合并的地方会报一个POI错误
     *      (java.lang.IllegalStateException: Cannot add merged region B3:B6 to sheet because it overlaps with an existing merged region (A6:C6).)
     *
     * @param curRowIndex 当前行: 需要合并的哪一行(一般用数据List的“index”+“表头的行数”,表头站的行数是1就+1,是2就+2)
     * @param curColIndex 合并开始列:从第几行开始合并
     * @param num 合并列的数量:从第curColIndex行开始,向右合并多少格
     */
    public void add (int curRowIndex,  int curColIndex , int num){
        mergeInfo.put(String.format(KEY, curRowIndex, curColIndex),num);
    }

}

列排序处理器

package com.springboot.cli.handler;


import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.write.handler.RowWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import com.alibaba.excel.write.property.ExcelWriteHeadProperty;
import org.apache.poi.ss.usermodel.Row;

import java.util.Collection;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 列排序处理器
 *
 * @author wf
 */
public class SortRowWriteHandler implements RowWriteHandler {

    @Override
    public void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Integer rowIndex, Integer relativeRowIndex, Boolean isHead) {
        // 获取传入的包含的列(字段的名字list),将headMap的索引按照传入的列的顺序重新放入,即可实现排序
        ExcelWriteHeadProperty excelWriteHeadProperty = writeSheetHolder.getExcelWriteHeadProperty();
        //行头部排序map
        Map<Integer, Head> headMap = excelWriteHeadProperty.getHeadMap();
        //行内容排序map
        Map<Integer, ExcelContentProperty> contentPropertyMap = excelWriteHeadProperty.getContentPropertyMap();
        Collection<String> includeColumnFieldNames = writeSheetHolder.getIncludeColumnFiledNames();
        // 将headMap中的字段名字对应Head
        Map<String, Head> fieldNameHead = headMap.values().stream().collect(Collectors.toMap(Head::getFieldName, head -> head));
        Map<String, ExcelContentProperty> fieldNameColumnFiled = contentPropertyMap.values().stream().collect(Collectors.toMap(e -> e.getHead().getFieldName(), e -> e));
        int index = 0;
        for (String includeColumnFieldName : includeColumnFieldNames) {
            if (isHead) {
                // 按照includeColumnFieldNames中的顺序取出head重新覆盖
                Head head = fieldNameHead.get(includeColumnFieldName);
                if (head == null) {
                    continue;
                }
                headMap.put(index, head);
            } else {
                // 按照includeColumnFieldNames中的顺序取出fieldNameColumnFiled重新覆盖
                ExcelContentProperty contentProperty = fieldNameColumnFiled.get(includeColumnFieldName);
                if (contentProperty == null) {
                    continue;
                }
                contentPropertyMap.put(index,contentProperty);
            }
            index++;
        }
    }

    @Override
    public void afterRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer relativeRowIndex, Boolean isHead) {

    }

    @Override
    public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer relativeRowIndex, Boolean isHead) {

    }

}


你可能感兴趣的:(EasyExcel,按列顺序排序导出,合并列,合并行,动态表格,java,spring,开发语言)