現在有個業務場景,需要動態導出單據費用。由於不同的業務單位配置的費用名稱不同,所有表頭需要動態拼接。
表頭還會一級表頭合併,如下圖所示:
表頭動態加載,同時對應的行數據也要根據表頭加載。
使用阿里的easyExcel。
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.Sheet;
import com.alibaba.excel.metadata.Table;
import com.alibaba.excel.support.ExcelTypeEnum;
public BaseResultVo exportFeeDetail(BillFeeDTO dto, HttpServletResponse response) throws Exception {
Long companyId = 1L;
/**表單**/
Sheet sheet = new Sheet(1,0);
sheet.setSheetName("第一個Sheet");
/**創建一個表格**/
Table table = new Table(1);
List<List<String>> headList = Lists.newArrayList();
List<String> headTitle0 = Lists.newArrayList(),
headTitle11 = Lists.newArrayList();
headTitle0.add("託運單號");
headList.add(headTitle0);
headTitle1.add("託運單狀態");
headList.add(headTitle1);
/**動態加載費用表頭**/
List<FeeItemVO> feeItemVOList = feeItemApi.listFeeItemByType(companyId, "運輸配送類");
feeItemVOList.stream().forEach(feeItemVO -> {
List<String> headTitleN = Lists.newArrayList();
headTitleN.add("營收");
headTitleN.add(feeItemVO.getFeeName());
headList.add(headTitleN);
});
headTitle9.add("運輸/配送成本");
headTitle9.add("承運商");
headList.add(headTitle9);
feeItemVOList.stream().forEach(feeItemVO -> {
List<String> headTitleN = Lists.newArrayList();
headTitleN.add("運輸/配送成本");
headTitleN.add(feeItemVO.getFeeName());
headList.add(headTitleN);
});
headTitle10.add("毛利");
headList.add(headTitle10);
headTitle11.add("毛利率");
headList.add(headTitle11);
table.setHead(headList);
/** 所有數據行集合 **/
List<List<Object>> list = Lists.newArrayList();
BaseResultVo resultVo = list(dto, null);
PageInfo<Map<String, Object>> mapPageInfo = (PageInfo<Map<String, Object>>)resultVo.getData();
if (null != mapPageInfo || !CollectionUtils.isEmpty(mapPageInfo.getList())) {
List<Map<String, Object>> mapList = mapPageInfo.getList();
for (int i = 0; i < mapList.size(); i++) {
/**第 n 行的數據**/
List<Object> row = Lists.newArrayList();
row.add(mapList.get(i).get("billNo"));
row.add(mapList.get(i).get("status"));
/**根據費用名稱 找費用*/
for (FeeItemVO feeItemVO : feeItemVOList) {
row.add(mapList.get(i).get(BillFeeEnums.FeeTypeEnum.revenue.getId()+ feeItemVO.getFeeName()));
}
list.add(row);
}
}
/**彙總合計行*/
List<Object> row = Lists.newArrayList();
dto.setTitleType(BillFeeEnums.TitleTypeEnum.detail.getId());
BaseResultVo totalFeeResultVo = listTotalFee(dto);
Map<String, Object> totalFeeMap = (Map<String, Object>)totalFeeResultVo.getData();
row.add("合計");
row.add("");
/**單據營收: 提貨費:80.00,幹線費:50.00,配送費:100.00**/
for (FeeItemVO feeItemVO : feeItemVOList) {
row.add(totalFeeMap.get(BillFeeEnums.FeeTypeEnum.revenue.getId()+ feeItemVO.getFeeName()));
}
//todo 該行的其他數據
list.add(row);
String fileName = ExcelUtils.generatorFileName("fee_detail");
ExcelWriter excelWriter =new ExcelWriter(getOutputStream(fileName, response), ExcelTypeEnum.XLSX);
excelWriter.write1(list,sheet,table);
/**表頭合併、毛利、毛利率合併**/
excelWriter.merge(0,1,0,0);
excelWriter.merge(0,1,1,1);
int length = 7 + 3 * feeItemVOList.size() + 2 + 1;
excelWriter.merge(0,1,length,length);
/**毛利報表 彙總行合併**/
int lastRow = 1 + list.size();
excelWriter.merge(lastRow,lastRow,0,7);
/** 釋放資源**/
excelWriter.finish();
System.out.println("ok");
return null;
}
生成excel文件名:
public static String generatorFileName(String title) throws UnsupportedEncodingException {
return URLEncoder.encode(String.format("%s%s", title,
DateTimeFormatter.ofPattern("yyyyMMdd").format(LocalDateTime.now())), "UTF-8");
}
HttpServletResponse設置:
private static OutputStream getOutputStream(String fileName, HttpServletResponse response) throws Exception {
try {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".xlsx");
response.setHeader("Pragma", "public");
response.setHeader("Cache-Control", "no-store");
response.addHeader("Cache-Control", "max-age=0");
return response.getOutputStream();
} catch (IOException e) {
throw new Exception("導出excel表格失敗!", e);
}
}
Controller層:
@RequestMapping(value = "/exportFeeDetail",method = RequestMethod.GET)
public BaseResultVo exportFeeDetail(@RequestParam(name = "no", required = false) String no,
@RequestParam(name = "customerId", required = false) String customerId,
@RequestParam(name = "statusList", required = false) String statusList,
HttpServletResponse response) throws Exception {
BillFeeDTO dto = new BillFeeDTO();
dto.setNo(no);
if (Strings.isNotBlank(customerId)) {
dto.setCustomerId(Long.parseLong(customerId));
}
if (StringUtil.isNotBlank(statusList)) {
String[] statusArray = statusList.split("-");
if (null != statusArray && statusArray.length > 0) {
dto.setStatusList(Arrays.stream(statusArray).map(o->Integer.parseInt(o)).collect(Collectors.toList()));
}
}
return billFeeService.exportFeeDetail(dto, response);
}
注意:
1.Controller層,get請求。
2.請求參數,如果不是必填,需要加入@RequestParam(name = "no", required = false) String no,required = false限制。
3.狀態列表,不能 @RequestParam(name = "statusList", required = false) List<Integer> statusList,前端轉義錯誤。
要使用:@RequestParam(name = "statusList", required = false) String statusList;可以與前端約定值使用"-"分隔。
4.用postman測試導出excel,選擇“send and download”。
5.合併單元格,需要數清楚第幾列,然後使用
merge(int firstRow, int lastRow, int firstCol, int lastCol) 。
excelWriter.merge(0,1,col,col),把第col列的,第一行和第二行合併。
6.二級表頭:
List<List<String>> headList = Lists.newArrayList();
List<String> headTitle8 = Lists.newArrayList();
headTitle8.add("提貨成本");
headTitle8.add("承運商");
headList.add(headTitle8);