转自:https://www.cnblogs.com/fanerwei222/p/12029411.html

某种偶然的机会遇到了这个插件, 听说很牛X, 我之前也不知道, 不过还是耍了一下子, 遂记录下来.

官方的demo官网都有,传送门 : https://alibaba-easyexcel.github.io/index.html

当然, 官网的demo只是简单的演示, 如果你要实现的表格内容比较复杂, 那么需要自己去定义你的数据类, 并且自己去实现, 遂记录下我实现的一个不简单也不复杂的表格.

首先看实现的效果图: 比较粗糙, 不过一般的操作都有

 

 

由于我没有设置样式, 所以看起来还是比较丑的, 不过没关系, 功能实现了, 页面后面再说嘛.

直接上pom配置:

复制代码
<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>2.0.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.8</version>
    <scope>provided</scope>
</dependency>
复制代码

这两个是必须要的, lombok主要是方便, 我用的EasyExcel版本是2.0.5, 应该可以换成其他的也没事.

两个数数据类和一个主类直接贴出来看代码吧.

复制代码
package com.domaven.orange.easyExcel;

import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import lombok.Data;

import java.util.Date;
import java.util.List;

/**
 * TODO
 * 示范数据实体
 * {} 括起来的代表着层级标题
 */
@Data
@ContentRowHeight(20)
@HeadRowHeight(20)
@ColumnWidth(25)
public class DemoData {

    @ExcelProperty({"2019年商品房预售许可证发证统计报表", "序号"})
    private String order;
    @ExcelProperty({"2019年商品房预售许可证发证统计报表", "企业名称"})
    private String tenantName;
    @ExcelProperty({"2019年商品房预售许可证发证统计报表", "项目名称"})
    private String projectName;
    @ExcelProperty({"2019年商品房预售许可证发证统计报表", "幢号"})
    private Integer houseOrder;
    @ExcelProperty({"2019年商品房预售许可证发证统计报表", "层数"})
    private Integer flowsNums;
    @ExcelProperty({"2019年商品房预售许可证发证统计报表", "规划用途"})
    private String regularUse;
    @ExcelProperty({"2019年商品房预售许可证发证统计报表", "住宅", "住宅套数"})
    private Integer houseNums;
    @ExcelProperty({"2019年商品房预售许可证发证统计报表", "住宅", "住宅面积"})
    private Integer houseArea;
    @ExcelProperty({"2019年商品房预售许可证发证统计报表", "非住宅", "非住宅套数"})
    private Integer noHouseNums;
    @ExcelProperty({"2019年商品房预售许可证发证统计报表", "非住宅", "非住宅面积"})
    private Integer noHouseArea;
    @ExcelProperty({"2019年商品房预售许可证发证统计报表", "总建筑面积"})
    private Integer totalHouseArea;
    @ExcelProperty({"2019年商品房预售许可证发证统计报表", "许可证号"})
    private Integer admitNums;
    @ExcelProperty({"2019年商品房预售许可证发证统计报表", "发证日期"})
    private Date subIdDate;
    /**
     * 小计
     */
    @ExcelIgnore
    private List<TotalData> minTotal;
    /**
     * 总计
     */
    @ExcelIgnore
    private List<TotalData> allTotal;
}
复制代码
复制代码
package com.domaven.orange.easyExcel;

import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;

import java.util.Date;

/**
 * TODO
 * 统计数据实体
 */
@Data
public class TotalData {

    @ExcelIgnore
    private String order;
    @ExcelProperty({"2019年商品房预售许可证发证统计报表", "企业名称"})
    private String tenantName;

    @ExcelProperty({"2019年商品房预售许可证发证统计报表", "幢号"})
    private Integer houseOrder;
    @ExcelProperty({"2019年商品房预售许可证发证统计报表", "层数"})
    private Integer flowsNums;
    @ExcelProperty({"2019年商品房预售许可证发证统计报表", "规划用途"})
    private String regularUse;
    @ExcelProperty({"2019年商品房预售许可证发证统计报表", "住宅", "住宅套数"})
    private Integer houseNums;
    @ExcelProperty({"2019年商品房预售许可证发证统计报表", "住宅", "住宅面积"})
    private Integer houseArea;
    @ExcelProperty({"2019年商品房预售许可证发证统计报表", "非住宅", "非住宅套数"})
    private Integer noHouseNums;
    @ExcelProperty({"2019年商品房预售许可证发证统计报表", "非住宅", "非住宅面积"})
    private Integer noHouseArea;
    @ExcelProperty({"2019年商品房预售许可证发证统计报表", "总建筑面积"})
    private Integer totalHouseArea;
    @ExcelProperty({"2019年商品房预售许可证发证统计报表", "许可证号"})
    private Integer admitNums;
    @ExcelProperty({"2019年商品房预售许可证发证统计报表", "发证日期"})
    private Date subIdDate;

    public TotalData setTenantName(String tenantName) {
        this.tenantName = tenantName;
        return this;
    }

    public TotalData setOrder(String order) {
        this.order = order;
        return this;
    }
}
复制代码
复制代码
package com.domaven.orange.easyExcel;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.merge.LoopMergeStrategy;
import com.alibaba.excel.write.merge.OnceAbsoluteMergeStrategy;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.springframework.beans.BeanUtils;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * TODO
 * 使用EasyExcel按照自己想要的数据格式生成excel
 */
public class MainEasyExcel {

    /**
     * 分批写入数据
     * @param args
     */
    public static void main(String[] args) {
        /**
         * 文件保存地址
         */
        String fileName = "d://tmp/good.xlsx";

        /**
         * 弄一个写建造器
         */
        ExcelWriterBuilder builder = EasyExcel.write(fileName, DemoData.class);
        /**
         * 装点数据
         */
        List<DemoData> list = data();
        /**
         * 将数据按企业名称进行分组
         */
        Map<String, List<DemoData>> map = list.stream().collect(Collectors.groupingBy(DemoData::getTenantName));

        /**
         * 给除了"小计""总计"等行外的所有有效数据标序号
         */
        final int[] mapOrder = {1};
        map.values().forEach(
                value ->
                    value.forEach(
                            v -> {
                                v.setOrder(String.valueOf(mapOrder[0]));
                                mapOrder[0]++;
                            }
                    )
        );
        List<TotalData> allTotalDataList = new ArrayList<>();
        /**
         * 是否处理总计行标记
         */
        boolean isDepressAllTotal = false;
        /**
         * 从第四行开始合并(下标为3)
         */
        int mergeStartWithRow = 3;
        /**
         * 从第二列开始合并(下标为1)
         */
        int mergeStartWithColumn = 1;
        /**
         * 合并单元格需要用到的参数分别如下
         * firstRowIndex : 起始行索引
         * lastRowIndex : 结束行索引
         * firstColumnIndex : 起始列索引
         * lastColumnIndex : 结束列索引
         */
        int firstRowIndex = 0, lastRowIndex = 0, firstColumnIndex = 0, lastColumnIndex = 0;
        /**
         * 填充数据数量计数器
         */
        int count = 0;
        for (Map.Entry entry : map.entrySet()) {
            /**
             * 设置要合并的单元格的位置
             */
            firstRowIndex = mergeStartWithRow + count;
            lastRowIndex = ((List<DemoData>) entry.getValue()).size() + firstRowIndex - 1;
            firstColumnIndex = mergeStartWithColumn;
            lastColumnIndex = mergeStartWithColumn;
            /**
             * 合并公司名称行
             */
            builder.registerWriteHandler(getOnceMerge(firstRowIndex, lastRowIndex, firstColumnIndex, lastColumnIndex));
            /**
             * 合并小计行
             */
            builder.registerWriteHandler(getOnceMerge(lastRowIndex + 1, lastRowIndex + 1, 0, 1));
            count = count + ((List<DemoData>) entry.getValue()).size() + 1;
        }

        /**
         * 合并总计行
         * 加1是小计那一行
         * 加2是总计那一行
         */
        builder.registerWriteHandler(getOnceMerge(lastRowIndex + 2, lastRowIndex + 2, 0, 1));
        /**
         * 写器
         */
        ExcelWriter writer = builder.build();
        /**
         * sheet页
         */
        WriteSheet sheet = EasyExcel.writerSheet("测试").build();
        for (Map.Entry entry : map.entrySet()) {
            /**
             * 分批写入数据
             */
            writer.write((List<DemoData>) entry.getValue(), sheet);
            /**
             * 写入小计行数据
             */
            List<TotalData> totalDataList = new ArrayList<>();
            totalDataList.add(((List<DemoData>) (entry.getValue())).get(0).getMinTotal().get(0).setTenantName("小计").setOrder("小计"));
            writer.write(totalDataList, sheet);

            /**
             * 设置总计行数据
             */
            if (!isDepressAllTotal) {
                allTotalDataList.add(((List<DemoData>) (entry.getValue())).get(0).getAllTotal().get(0).setTenantName("总计").setOrder("总计"));
                isDepressAllTotal = true;
            }
        }
        /**
         * 写入总计行数据
         */
        writer.write(allTotalDataList, sheet);
        writer.finish();

    }

    /**
     * 测试方法, 直接写入方式
     * @param args
     */
    public static void mainDirect(String[] args) {
        String fileName = "d://tmp/good.xlsx";
        List<DemoData> list = data();
        Map<String, List<DemoData>> map = list.stream().collect(Collectors.groupingBy(DemoData::getTenantName));
        EasyExcel.write(fileName, DemoData.class).sheet("数据模板").doWrite(data());
    }

    /**
     * 按照需要的数据格式构建数据
     * @return
     */
    private static List<DemoData> data() {
        List<DemoData> list = new ArrayList<DemoData>();

        for (int i = 0; i < 10; i++) {
            DemoData data = new DemoData();
            data.setOrder(String.valueOf(i));
            if (i % 2 == 0) {
                data.setTenantName("北京科技");
            } else if (i % 3 == 0) {
                data.setTenantName("南京公司");
            } else {
                data.setTenantName("天津包子铺");
            }
            data.setProjectName("打铁项目");
            data.setHouseOrder(123);
            data.setFlowsNums(8);
            data.setRegularUse("没用");
            data.setHouseNums(2);
            data.setHouseArea(120);
            data.setNoHouseNums(4);
            data.setNoHouseArea(400);
            data.setTotalHouseArea(900);
            data.setAdmitNums(8888888);
            data.setAdmitNums(10);
            data.setSubIdDate(new Date());
            /**
             * 特殊的小计和总计数据
             */
            List<TotalData> totalDataList = new ArrayList<>();
            TotalData totalData = new TotalData();
            BeanUtils.copyProperties(data, totalData);
            totalDataList.add(totalData);
            data.setMinTotal(totalDataList);
            data.setAllTotal(totalDataList);
            list.add(data);
        }
        return list;
    }

    /**
     * 获取样式策略
     * 阿里官方模板
     * @return
     */
    public static HorizontalCellStyleStrategy getStyle() {
        /**
         * 头的策略
         */
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        /**
         * 背景设置为红色
         */
        headWriteCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex());
        WriteFont headWriteFont = new WriteFont();
        headWriteFont.setFontHeightInPoints((short) 20);
        headWriteCellStyle.setWriteFont(headWriteFont);
        /**
         * 内容的策略
         */
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        /**
         * 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定
         */
        contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
        /**
         * 背景绿色
         */
        contentWriteCellStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex());
        WriteFont contentWriteFont = new WriteFont();
        /**
         * 字体大小
         */
        contentWriteFont.setFontHeightInPoints((short) 20);
        contentWriteCellStyle.setWriteFont(contentWriteFont);
        /**
         * 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
         */
        HorizontalCellStyleStrategy horizontalCellStyleStrategy =
                new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);

        return horizontalCellStyleStrategy;
        /**
         * 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
         * EasyExcel.write(fileName, DemoData.class).registerWriteHandler(horizontalCellStyleStrategy).sheet("模板").doWrite(data());
         */
    }

    /**
     * 获取合并策略
     * 固定的合并策略,官方模板
     * @return
     */
    public static LoopMergeStrategy getLoopMergeStrategy() {
        /**
         * 每隔2行会合并 把eachColumn 设置成 3 也就是我们数据的长度,所以就第一列会合并。当然其他合并策略也可以自己写
         */
        LoopMergeStrategy loopMergeStrategy = new LoopMergeStrategy(2, 1);
        return loopMergeStrategy;
    }

    /**
     * 合并单元格
     * 根据需要指定位置的单元格进行合并
     * @param firstRowIndex 起始行索引
     * @param lastRowIndex 结束行索引
     * @param firstColumnIndex 起始列索引
     * @param lastColumnIndex 结束列索引
     * @return
     */
    public static OnceAbsoluteMergeStrategy getOnceMerge(int firstRowIndex, int lastRowIndex, int firstColumnIndex, int lastColumnIndex) {
        OnceAbsoluteMergeStrategy once = new OnceAbsoluteMergeStrategy(firstRowIndex, lastRowIndex, firstColumnIndex, lastColumnIndex);
        return once;
    }

}
复制代码

代码注释也是十分详尽的, 可以直接看代码了.

posted on 2020-09-22 18:32  Sharpest  阅读(537)  评论(0编辑  收藏  举报