转自: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; } }
代码注释也是十分详尽的, 可以直接看代码了.