使用easyexcel动态合并列导出

前言

项目中遇到需要导出excel,并且动态合并指定列,设计表头样式的需求。项目框架一开始用的是easypoi,它对于导出格式简单的excel和word都非常友好,但是对于复杂的excel格式,就显得非常的局限了。所以后面决定使用easyexcel来解决这类问题,下面就是开发中的一个例子。

提示:以下是本篇文章正文内容,下面案例可供参考

一、实现流程

1.引入依赖

代码如下:

    <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.2.6</version>
            <exclusions>
                <exclusion>
                    <artifactId>poi</artifactId>
                    <groupId>org.apache.poi</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>poi-ooxml</artifactId>
                    <groupId>org.apache.poi</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>poi-ooxml-schemas</artifactId>
                    <groupId>org.apache.poi</groupId>
                </exclusion>
            </exclusions>
        </dependency>

2.代码实现

1.创建实体类,设计表头样式

@Data
@ApiModel(value = "测试导出DTO")
@HeadStyle(horizontalAlignment = HorizontalAlignment.CENTER)
@HeadFontStyle(bold = true ,color = -1)

public class ExcelMultipurposeDTO {

    @ColumnWidth(value = 20)
    @ExcelProperty(value = {"项目","项目"} ,index = 0)
    private String project;


    @ExcelIgnore
    private String site;

    @ColumnWidth(value = 25)
    @ExcelProperty(value = {"网点","网点"} ,index = 1)
    private String stationName;

    @ColumnWidth(value = 15)
    @ExcelProperty(value = {"本期发生数","业务笔数"} ,index = 2)
    private Integer currentTransCount;

    @ColumnWidth(value = 15)
    @ExcelProperty(value = {"本期发生数","发生额"} ,index = 3)
    private BigDecimal currentTransAmount;

    @ColumnWidth(value = 15)
    @ExcelProperty(value = {"本期发生数","结算服务费"} ,index = 4)
    private BigDecimal currentServiceFee;

    @ColumnWidth(value = 15)
    @ExcelProperty(value = {"本年发生数","业务笔数"} ,index = 5)
    private Integer yearTransCount;

    @ColumnWidth(value = 15)
    @ExcelProperty(value = {"本年发生数","发生额"} ,index = 6)
    private BigDecimal yearTransAmount;

    @ColumnWidth(value = 15)
    @ExcelProperty(value = {"本年发生数","结算服务费"} ,index = 7)
    private BigDecimal yearServiceFee;

}

其中,@HeadStyle@HeadFontStyle是设置表头居中,字体加粗和颜色。@ColumnWidth 是设置列宽,@ExcelProperty 里面有valueindex属性,value 是列名,index 是列的顺序。

2.核心代码实现

		List<ExcelMultipurposeDTO> resultList = xxxDao.getList(); //获取需要导出的数据
        try {
//            设置第几列合并
            int[] mergeColumnIndex = {0}; //这边我需要指定合并第一列,所以赋值0,如果需要合并多列,直接逗号分隔:int[] mergeColumnIndex = {0,1,2}
//            需要从第几行开始合并
            int mergeRowIndex = 1;
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            //  设置文件名称
            String fileName = URLEncoder.encode("测试导出", "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
            //  sheet名称
            EasyExcel.write(response.getOutputStream(), ExcelMultipurposeDTO.class)
//                    excel版本
                    .excelType(ExcelTypeEnum.XLSX)
//                    是否自动关流
                    .autoCloseStream(Boolean.TRUE)
                    .registerWriteHandler(new ExcelFillCellMergeStrategy(mergeRowIndex,mergeColumnIndex)).
                    sheet("测试导出").doWrite(resultList);
        } catch (Exception e) {
            e.printStackTrace();
        }
	package com.umasoft.modules.webapps.reportmanage.service.impl;
	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.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;
	
	/**
	 * @author
	 * @date
	 */
	public class ExcelFillCellMergeStrategy implements CellWriteHandler {
	
	    private int[] mergeColumnIndex;
	    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 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 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);
	            }
	        }
	    }
	}

代码逻辑大概是:如果第一列的内容一样,那就把这些列合并形成一对多的展现形式。

二、实现效果

使用easyexcel动态合并列导出_第1张图片
代码中指定合并第一列,所以是excel将“项目”列进行了合并,当然了,源数据的格式需要自己组装,下面附上源数据格式,仅供参考:

[
    {
        "site":"代理业务",
        "project":"代理业务"
    },
    {
        "site":"地铁3号线林场",
        "yearTransAmount":0,
        "yearTransCount":0,
        "currentServiceFee":0,
        "project":"超市代理网点",
        "currentTransAmount":0,
        "stationName":"地铁3号线林场",
        "yearServiceFee":0,
        "currentTransCount":0
    },
    {
        "site":"鸡鸣寺",
        "yearTransAmount":0,
        "yearTransCount":0,
        "currentServiceFee":0,
        "project":"超市代理网点",
        "currentTransAmount":0,
        "stationName":"鸡鸣寺",
        "yearServiceFee":0,
        "currentTransCount":0
    },
    {
        "site":"智汇APP微信充值结算",
        "yearTransAmount":0,
        "yearTransCount":0,
        "currentServiceFee":0,
        "project":"超市代理网点",
        "currentTransAmount":0,
        "yearServiceFee":0,
        "currentTransCount":0
    },
    {
        "yearTransAmount":0,
        "yearTransCount":0,
        "currentServiceFee":0,
        "project":"超市代理网点",
        "currentTransAmount":0,
        "stationName":"小计",
        "yearServiceFee":0,
        "currentTransCount":0
    },
    {
        "site":"仙林中心",
        "yearTransAmount":0,
        "yearTransCount":0,
        "currentServiceFee":0,
        "project":"主卡代理充值",
        "currentTransAmount":0,
        "stationName":"仙林中心",
        "yearServiceFee":0,
        "currentTransCount":0
    },
    {
        "site":"S8宁天线卸甲甸",
        "yearTransAmount":0,
        "yearTransCount":0,
        "currentServiceFee":0,
        "project":"主卡代理充值",
        "currentTransAmount":0,
        "stationName":"S8宁天线卸甲甸",
        "yearServiceFee":0,
        "currentTransCount":0
    },
    {
        "site":"江苏东方航空国际旅业公司燕归楼商务酒店",
        "yearTransAmount":3,
        "yearTransCount":1,
        "currentServiceFee":0,
        "project":"主卡代理充值",
        "currentTransAmount":0,
        "stationName":"东航燕归楼商务酒店",
        "yearServiceFee":0,
        "currentTransCount":0
    },
    {
        "yearTransAmount":0,
        "yearTransCount":0,
        "currentServiceFee":0,
        "project":"主卡代理充值",
        "currentTransAmount":3,
        "stationName":"小计",
        "yearServiceFee":0,
        "currentTransCount":1
    },
    {
        "site":"智汇APP支付宝充值结算",
        "yearTransAmount":40689.86,
        "yearTransCount":779,
        "currentServiceFee":0,
        "project":"其他代理网点",
        "currentTransAmount":66849.86,
        "stationName":"金尧标超       ",
        "yearServiceFee":0,
        "currentTransCount":878
    },
    {
        "site":"智汇APP支付宝充值结算",
        "yearTransAmount":0,
        "yearTransCount":0,
        "currentServiceFee":0,
        "project":"其他代理网点",
        "currentTransAmount":66849.86,
        "stationName":"金尧标超       ",
        "yearServiceFee":0,
        "currentTransCount":878
    },
    {
        "site":"智汇APP支付宝充值结算",
        "yearTransAmount":26160,
        "yearTransCount":99,
        "currentServiceFee":0,
        "project":"其他代理网点",
        "currentTransAmount":66849.86,
        "stationName":"金尧生活超市",
        "yearServiceFee":0,
        "currentTransCount":878
    },
    {
        "site":"智汇APP支付宝充值结算",
        "yearTransAmount":0,
        "yearTransCount":0,
        "currentServiceFee":0,
        "project":"其他代理网点",
        "currentTransAmount":66849.86,
        "stationName":"金尧生活超市",
        "yearServiceFee":0,
        "currentTransCount":878
    },
    {
        "site":"智汇APP支付宝充值结算",
        "yearTransAmount":0,
        "yearTransCount":0,
        "currentServiceFee":0,
        "project":"其他代理网点",
        "currentTransAmount":66849.86,
        "stationName":"金尧生活超市",
        "yearServiceFee":0,
        "currentTransCount":878
    },
    {
        "site":"智汇APP支付宝充值结算",
        "yearTransAmount":0,
        "yearTransCount":0,
        "currentServiceFee":0,
        "project":"其他代理网点",
        "currentTransAmount":66849.86,
        "stationName":"金尧标超       ",
        "yearServiceFee":0,
        "currentTransCount":878
    },
    {
        "site":"智汇APP支付宝充值结算",
        "yearTransAmount":0,
        "yearTransCount":0,
        "currentServiceFee":0,
        "project":"其他代理网点",
        "currentTransAmount":66849.86,
        "stationName":"金尧标超       ",
        "yearServiceFee":0,
        "currentTransCount":878
    },
    {
        "site":"智汇APP支付宝充值结算",
        "yearTransAmount":0,
        "yearTransCount":0,
        "currentServiceFee":0,
        "project":"其他代理网点",
        "currentTransAmount":66849.86,
        "stationName":"金尧生活超市",
        "yearServiceFee":0,
        "currentTransCount":878
    },
    {
        "site":"智汇APP支付宝充值结算",
        "yearTransAmount":26160,
        "yearTransCount":99,
        "currentServiceFee":0,
        "project":"其他代理网点",
        "currentTransAmount":66849.86,
        "stationName":"金尧标超       ",
        "yearServiceFee":0,
        "currentTransCount":878
    },
    {
        "site":"智汇APP支付宝充值结算",
        "yearTransAmount":0,
        "yearTransCount":0,
        "currentServiceFee":0,
        "project":"其他代理网点",
        "currentTransAmount":66849.86,
        "stationName":"金尧生活超市",
        "yearServiceFee":0,
        "currentTransCount":878
    },
    {
        "site":"智汇APP支付宝充值结算",
        "yearTransAmount":0,
        "yearTransCount":0,
        "currentServiceFee":0,
        "project":"其他代理网点",
        "currentTransAmount":66849.86,
        "stationName":"金尧标超       ",
        "yearServiceFee":0,
        "currentTransCount":878
    },
    {
        "site":"智汇APP支付宝充值结算",
        "yearTransAmount":40689.86,
        "yearTransCount":779,
        "currentServiceFee":0,
        "project":"其他代理网点",
        "currentTransAmount":66849.86,
        "stationName":"金尧生活超市",
        "yearServiceFee":0,
        "currentTransCount":878
    },
    {
        "site":"智汇APP支付宝充值结算",
        "yearTransAmount":0,
        "yearTransCount":0,
        "currentServiceFee":0,
        "project":"其他代理网点",
        "currentTransAmount":66849.86,
        "stationName":"金尧标超       ",
        "yearServiceFee":0,
        "currentTransCount":878
    },
    {
        "site":"智汇APP支付宝充值结算",
        "yearTransAmount":0,
        "yearTransCount":0,
        "currentServiceFee":0,
        "project":"其他代理网点",
        "currentTransAmount":66849.86,
        "stationName":"金尧生活超市",
        "yearServiceFee":0,
        "currentTransCount":878
    },
    {
        "site":"1号线南京站北广场",
        "yearTransAmount":0,
        "yearTransCount":0,
        "currentServiceFee":0,
        "project":"其他代理网点",
        "currentTransAmount":0,
        "stationName":"1号线南京站北广场",
        "yearServiceFee":0,
        "currentTransCount":0
    },
    {
        "site":"地铁3号线星火路",
        "yearTransAmount":0,
        "yearTransCount":0,
        "currentServiceFee":0,
        "project":"其他代理网点",
        "currentTransAmount":0,
        "stationName":"地铁3号线星火路",
        "yearServiceFee":0,
        "currentTransCount":0
    },
    {
        "yearTransAmount":133699.72,
        "yearTransCount":1756,
        "currentServiceFee":0,
        "project":"其他代理网点",
        "currentTransAmount":133699.72,
        "stationName":"小计",
        "yearServiceFee":0,
        "currentTransCount":1756
    },
    {
        "site":"自助业务",
        "project":"自助业务"
    },
    {
        "site":"扬子六合",
        "yearTransAmount":5.5,
        "yearTransCount":2,
        "currentServiceFee":0,
        "project":"银行渠道",
        "currentTransAmount":0,
        "yearServiceFee":0,
        "currentTransCount":0
    },
    {
        "site":"地铁4号线云南路",
        "yearTransAmount":0,
        "yearTransCount":0,
        "currentServiceFee":0,
        "project":"银行渠道",
        "currentTransAmount":0,
        "stationName":"地铁4号线云南路",
        "yearServiceFee":0,
        "currentTransCount":0
    },
    {
        "currentServiceFee":0,
        "project":"银行渠道",
        "currentTransAmount":5.5,
        "stationName":"小计",
        "currentTransCount":2
    },
    {
        "site":"代收代付",
        "project":"代收代付"
    },
    {
        "site":"自有人工业务",
        "project":"自有人工业务"
    },
    {
        "site":"地铁1号线软件大道",
        "yearTransAmount":0,
        "yearTransCount":0,
        "currentServiceFee":0,
        "project":"紫金卡充值",
        "currentTransAmount":0,
        "stationName":"地铁1号线软件大道",
        "yearServiceFee":0,
        "currentTransCount":0
    },
    {
        "yearTransAmount":0,
        "yearTransCount":0,
        "currentServiceFee":0,
        "project":"紫金卡充值",
        "currentTransAmount":0,
        "stationName":"小计",
        "yearServiceFee":0,
        "currentTransCount":0
    },
    {
        "site":"邻仕家江东南路店",
        "yearTransAmount":22.8,
        "yearTransCount":1,
        "currentServiceFee":0,
        "project":"吉利卡充值",
        "currentTransAmount":0,
        "yearServiceFee":0,
        "currentTransCount":0
    },
    {
        "site":"S8宁天线龙池",
        "yearTransAmount":0,
        "yearTransCount":0,
        "currentServiceFee":0,
        "project":"吉利卡充值",
        "currentTransAmount":0,
        "stationName":"S8宁天线龙池",
        "yearServiceFee":0,
        "currentTransCount":0
    },
    {
        "yearTransAmount":0,
        "yearTransCount":0,
        "currentServiceFee":0,
        "project":"吉利卡充值",
        "currentTransAmount":22.8,
        "stationName":"小计",
        "yearServiceFee":0,
        "currentTransCount":1
    },
    {
        "site":"线上业务",
        "project":"线上业务"
    },
    {
        "site":"扬子六合",
        "yearTransAmount":5.5,
        "yearTransCount":2,
        "currentServiceFee":0,
        "project":"银行渠道",
        "currentTransAmount":0,
        "yearServiceFee":0,
        "currentTransCount":0
    },
    {
        "site":"地铁4号线云南路",
        "yearTransAmount":0,
        "yearTransCount":0,
        "currentServiceFee":0,
        "project":"银行渠道",
        "currentTransAmount":0,
        "stationName":"地铁4号线云南路",
        "yearServiceFee":0,
        "currentTransCount":0
    },
    {
        "currentServiceFee":0,
        "project":"银行渠道",
        "currentTransAmount":5.5,
        "stationName":"小计",
        "currentTransCount":2
    },
    {
        "site":"退值业务",
        "project":"退值业务"
    }
]

三、总结

相对于easypoi而言,这样的方式实现复杂表头和动态合并列就简单多了,只需要将源数据的格式处理好,直接调用封装好的方法就可以。希望对大家有帮助,感谢阅读!

你可能感兴趣的:(easyexcel,poi,java)