一般情況:
Excel導出一般都是一行一行的記錄輸出
、
這是Controller代碼:

標題行的設置:
標題行會設置獲取的結果集的字段名,數據會自動根據設置的名稱匹配裝填

特殊的需求:
如頁面的效果,附加的三個字段存在多個記錄,需要合並和之前的主記錄拼接處理:

右側的數據是根據這個接口提供的:

頁面有用循環回調來實現
但是導出功能,開發干脆就沒寫這部分了???
所以BA要我把這個部分給弄出來
合並原理:
關於POI框架和用友提供的封裝方法都沒有這樣關於記錄行合並的操作:
現有的方式都是對標題行進行合並設置的
這是一段原框架中的Main方法Demo案例:
public static void main(String[] args) { NormalExcelExport excel = new NormalExcelExport(); SXSSFWorkbook wb = excel.createExcelWork(); List<List<ExcelCol>> colColList = new LinkedList(); List<ExcelCol> exColList = new LinkedList(); exColList.add(new ExcelCol("TEST1", "測試1", 2, 1)); exColList.add(new ExcelCol("TEST2", "測試2", 2, 1)); exColList.add(new ExcelCol("TEST3", "測試3", 1, 3)); colColList.add(exColList); List<ExcelCol> exColList1 = new LinkedList(); exColList1.add(new ExcelCol("TEST1", "測試1")); exColList1.add(new ExcelCol("TEST2", "測試2")); exColList1.add(new ExcelCol("TEST3", "測試3")); colColList.add(exColList1); List<Map> datas = new LinkedList(); Map map1 = new HashMap(); map1.put("TEST1", "1"); map1.put("TEST2", "2"); map1.put("TEST3", "3"); datas.add(map1); String[] fieldNames = new String[]{"TEST1", "TEST2", "TEST3"}; excel.addSheetMerge(wb, colColList, fieldNames, datas, "第1頁"); try { excel.saveLocal(wb, "F:/", "test1.xlsx"); } catch (Exception var10) { var10.printStackTrace(); } }
文件效果:

每一次添加之后該方法會將迭代器撥動至下一個基本單元行
colColList.add(exColList);

方法執行之后,迭代器換行至這個位置:

這時就是程序理解的第二行:

添加完第二行記錄之后,迭代器切換至第三行:

所以到這里我們就明白合並操作的原理了
這是我寫的一個簡單的案例:
private static void demo2() { NormalExcelExport excel = new NormalExcelExport(); SXSSFWorkbook wb = excel.createExcelWork(); // 總表格 List<List<ExcelCol>> colColList = new LinkedList(); // 標題行 List<ExcelCol> exColList = new LinkedList(); exColList.add(new ExcelCol("TEST1", "標題1")); exColList.add(new ExcelCol("TEST2", "標題2")); exColList.add(new ExcelCol("TEST3", "標題3")); // 要合並的行標題 exColList.add(new ExcelCol("TEST4", "標題4")); exColList.add(new ExcelCol("TEST5", "標題5")); exColList.add(new ExcelCol("TEST6", "標題6")); colColList.add(exColList); // 單次合並實現 List<ExcelCol> leftSideRow = new LinkedList(); // 左側單元格 實現行合並 leftSideRow.add(new ExcelCol("TEST1", "AAA", 2, 1)); leftSideRow.add(new ExcelCol("TEST2", "BBB", 2, 1)); leftSideRow.add(new ExcelCol("TEST3", "CCC", 2, 1)); leftSideRow.add(new ExcelCol("TEST4", "41")); leftSideRow.add(new ExcelCol("TEST5", "42")); leftSideRow.add(new ExcelCol("TEST6", "43")); colColList.add(leftSideRow); leftSideRow = new LinkedList<>(); leftSideRow.add(new ExcelCol("TEST4", "51")); leftSideRow.add(new ExcelCol("TEST5", "52")); leftSideRow.add(new ExcelCol("TEST6", "53")); colColList.add(leftSideRow); List<ExcelCol> smapleRow = new LinkedList(); smapleRow.add(new ExcelCol("TEST1", "AAA3", 3, 1)); smapleRow.add(new ExcelCol("TEST2", "AAA4", 3, 1)); smapleRow.add(new ExcelCol("TEST3", "AAA5", 3, 1)); smapleRow.add(new ExcelCol("TEST4", "AAA6")); smapleRow.add(new ExcelCol("TEST5", "AAA7")); smapleRow.add(new ExcelCol("TEST6", "AAA8")); colColList.add(smapleRow); // 換行時注意 前3個合並了3行,下一行會是 AA5開始 smapleRow = new LinkedList<>(); smapleRow.add(new ExcelCol("TEST4", "AAA26")); smapleRow.add(new ExcelCol("TEST5", "AAA27")); smapleRow.add(new ExcelCol("TEST6", "AAA28")); colColList.add(smapleRow); smapleRow = new LinkedList<>(); smapleRow.add(new ExcelCol("TEST4", "AAA26")); smapleRow.add(new ExcelCol("TEST5", "AAA27")); smapleRow.add(new ExcelCol("TEST6", "AAA28")); colColList.add(smapleRow); excel.addSheetMerge(wb, colColList, null, null, "第1頁"); try { excel.saveLocal(wb, "D:/", "test1.xlsx"); } catch (Exception var10) { var10.printStackTrace(); } }

回到業務需求:
/** * * @param queryParam * @return * @throws Exception */ @RequestMapping(value = "/exportData2", method = RequestMethod.GET) @ResponseBody public Map<String, Object> SSPInvoiceSumReportExport2(@RequestParam Map<String, String> queryParam) throws Exception { ElemBean condition = new ElemBean(queryParam); List<List<ExcelCol>> colColList = new LinkedList(); // 設置標題行 List<ExcelCol> titleColList = new LinkedList<ExcelCol>(); titleColList.add(new ExcelCol("asc_code","維修站代碼")); titleColList.add(new ExcelCol("SAP_CODE","SAP碼")); titleColList.add(new ExcelCol("asc_name","維修站名稱")); titleColList.add(new ExcelCol("invoice_no","發票號")); titleColList.add(new ExcelCol("no_tax_amount","不含稅金額")); titleColList.add(new ExcelCol("invoice_amount","含稅金額")); titleColList.add(new ExcelCol("tax_amount","稅費")); titleColList.add(new ExcelCol("audit_status","狀態", ExcelDataType.DICT)); titleColList.add(new ExcelCol("invoice_date","發票日期",ExcelDataType.DATEYYYYMMDD)); titleColList.add(new ExcelCol("created_at","提報日期",ExcelDataType.DATEYYYYMMDD)); titleColList.add(new ExcelCol("sap_code1","沖收入代碼")); titleColList.add(new ExcelCol("product_price","合同金額")); titleColList.add(new ExcelCol("no_tax_income_amount","收入金額(不含稅)")); colColList.add(titleColList); // 需要getInvoiceQuery的查詢條件 設置 limit 9999999 // PageInfoDto pageInfoDto = service.getInvoiceQuery(new ElemBean(queryParam)); List<Object> params = new LinkedList<Object>(); String sql = service.getSspInvoiceSumReportExpSql(condition, params); List<Map> pageInfoDto = DcsDaoUtil.findAll(sql, params); // 左側的主結果集 Map<String,String> distMap = new LinkedHashMap<>(); distMap.put("59701001","已提交"); distMap.put("59701002","已審核"); distMap.put("59701003","已駁回"); distMap.put("59701004","已核銷"); for (int i = 0; i < pageInfoDto.size(); i++) { // 當前行 Map currentRow = pageInfoDto.get(i); String invoice_no = StringUtils.isNullOrEmpty(currentRow.get("invoice_no")) ? "" : currentRow.get("invoice_no").toString(); // 發票單號為空的情況 List<Map> invoiceDetail = new LinkedList<>(); // 右側的 if(!"".equals(invoice_no) && invoice_no != null){ invoiceDetail = service.getInvoiceDetail(invoice_no, null); // 右側結果集 } titleColList = new LinkedList<>(); titleColList.add(new ExcelCol("asc_code", StringUtils.isNullOrEmpty(currentRow.get("asc_code")) ? "" : currentRow.get("asc_code").toString(),invoiceDetail.size() == 0 ? 1 : invoiceDetail.size() , 1)); titleColList.add(new ExcelCol("SAP_CODE", StringUtils.isNullOrEmpty(currentRow.get("SAP_CODE")) ? "" : currentRow.get("SAP_CODE").toString(),invoiceDetail.size() == 0 ? 1 : invoiceDetail.size() , 1)); titleColList.add(new ExcelCol("asc_name", StringUtils.isNullOrEmpty(currentRow.get("asc_name")) ? "" : currentRow.get("asc_name").toString(),invoiceDetail.size() == 0 ? 1 : invoiceDetail.size() , 1)); titleColList.add(new ExcelCol("invoice_no", StringUtils.isNullOrEmpty(currentRow.get("invoice_no")) ? "" : currentRow.get("invoice_no").toString(),invoiceDetail.size() == 0 ? 1 : invoiceDetail.size() , 1)); titleColList.add(new ExcelCol("no_tax_amount", StringUtils.isNullOrEmpty(currentRow.get("no_tax_amount")) ? "" : currentRow.get("no_tax_amount").toString(),invoiceDetail.size() == 0 ? 1 : invoiceDetail.size() , 1)); titleColList.add(new ExcelCol("invoice_amount", StringUtils.isNullOrEmpty(currentRow.get("invoice_amount")) ? "" : currentRow.get("invoice_amount").toString(),invoiceDetail.size() == 0 ? 1 : invoiceDetail.size() , 1)); titleColList.add(new ExcelCol("tax_amount", StringUtils.isNullOrEmpty(currentRow.get("tax_amount")) ? "" : currentRow.get("tax_amount").toString(),invoiceDetail.size() == 0 ? 1 : invoiceDetail.size() , 1)); titleColList.add(new ExcelCol("audit_status", StringUtils.isNullOrEmpty(currentRow.get("audit_status")) ? "" : distMap.get( currentRow.get("audit_status").toString()),invoiceDetail.size() == 0 ? 1 : invoiceDetail.size() , 1)); titleColList.add(new ExcelCol("invoice_date", StringUtils.isNullOrEmpty(currentRow.get("invoice_date")) ? "" : currentRow.get("invoice_date").toString().substring(0,10),invoiceDetail.size() == 0 ? 1 : invoiceDetail.size() , 1)); titleColList.add(new ExcelCol("created_at", StringUtils.isNullOrEmpty(currentRow.get("created_at")) ? "" : currentRow.get("created_at").toString().substring(0,10),invoiceDetail.size() == 0 ? 1 : invoiceDetail.size() , 1)); for(int j = 0 ; j < invoiceDetail.size() ; j ++){ if(j == 0){ // 第一行的的時候就需要和前面的內容 並列為一行 titleColList.add(new ExcelCol("sap_code1",StringUtils.isNullOrEmpty(invoiceDetail.get(j).get("sap_code1")) ? "" : invoiceDetail.get(j).get("sap_code1").toString())); titleColList.add(new ExcelCol("product_price",StringUtils.isNullOrEmpty(invoiceDetail.get(j).get("product_price")) ? "" : invoiceDetail.get(j).get("product_price").toString())); titleColList.add(new ExcelCol("no_tax_income_amount",StringUtils.isNullOrEmpty(invoiceDetail.get(j).get("no_tax_income_amount")) ? "" : invoiceDetail.get(j).get("no_tax_income_amount").toString())); colColList.add(titleColList); }else{ // 后面下推的行記錄就是新的一行了 titleColList = new LinkedList<>(); titleColList.add(new ExcelCol("sap_code1",StringUtils.isNullOrEmpty(invoiceDetail.get(j).get("sap_code1")) ? "" : invoiceDetail.get(j).get("sap_code1").toString())); titleColList.add(new ExcelCol("product_price",StringUtils.isNullOrEmpty(invoiceDetail.get(j).get("product_price")) ? "" : invoiceDetail.get(j).get("product_price").toString())); titleColList.add(new ExcelCol("no_tax_income_amount",StringUtils.isNullOrEmpty(invoiceDetail.get(j).get("no_tax_income_amount")) ? "" : invoiceDetail.get(j).get("no_tax_income_amount").toString())); colColList.add(titleColList); } } if(invoiceDetail.size() == 0){ // 還存在右邊結果集查不到結果的情況,這一行也要保留為空記錄 titleColList.add(new ExcelCol("sap_code1","")); titleColList.add(new ExcelCol("product_price","")); titleColList.add(new ExcelCol("no_tax_income_amount","")); colColList.add(titleColList); } } // for local tested , write by dzz 2021年5月20日18:28:41 // NormalExcelExport excel = new NormalExcelExport(); // // SXSSFWorkbook wb = excel.createExcelWork(); // // excel.addSheetMerge(wb, colColList, null, null, "第1頁"); // excel.saveLocal(wb, "D:/", "test1.xlsx"); Map<String, Object> maps = jmcExc.addSheetMerge(colColList, null, " select * from (select 1 + 1 ) a where 1 = 2 ", null, "SSP發票匯總報表.xlsx", "SSP發票匯總報表", null); return maps; }
最后的調用方法:
Map<String, Object> maps = jmcExc.addSheetMerge(colColList, null, " select * from (select 1 + 1 ) a where 1 = 2 ", null, "SSP發票匯總報表.xlsx", "SSP發票匯總報表", null);
SQL參數時方法中有一個數據裝填操作,如果SQL沒有記錄就不會裝數據執行:
無記錄的SQL:
" select * from (select 1 + 1 ) a where 1 = 2 "
要求的字段名為空,SQL參數空
最后直接把這個【標題結果集】丟進去,就實現了頁面的那種效果
