原創:轉載需注明原創地址 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; } }
代碼注釋也是十分詳盡的, 可以直接看代碼了.