easyExcel+POI 資料橫向平鋪匯出
業務開發中經常會遇到匯出excel的情況,我們大都會藉助第三方工具如 Apache POI或 alibaba/easyexcel
easy/excel 已經提供了模板填充excel的方法,但是如果遇到如下所示的複雜的橫向平鋪資料,簡單的模板填充則不能達到效果
表格中的輪數資料也是不確定的,其資料結構如下所示
[
{
"brand": "brand",
"buzzRound": 2,
"cost": 2.91,
"explain": "3",
"materialCode": "60010300000001",
"materialName": "裁紙器",
"planQuantity": 1.0,
"price": 3.0,
"remark": "",
"specification": "B41",
"tax": 0.09,
"tenderId": "b44464dbf0649ba59eba1b7387fec69a",
"totalPrice": 3.0,
"unit": "個"
},
{
"brand": "brand",
"buzzRound": 2,
"cost": 1.96,
"explain": "2",
"invoiceType": "增值稅普通發票",
"materialName": "長尾夾",
"planQuantity": 1.0,
"price": 2.0,
"remark": "",
"specification": "32mm",
"tax": 0.04,
"taxRate": 2,
"tenderId": "b44464dbf0649ba59eba1b7387fec69a",
"totalPrice": 2.0,
"unit": "盒"
},
{
"brand": "brand",
"buzzRound": 2,
"cost": 0.99,
"explain": "1",
"invoiceType": "增值稅專用發票",
"materialCode": "60010300000003",
"materialName": "垃圾桶",
"planQuantity": 1.0,
"price": 1.0,
"remark": "",
"specification": "35*35*45cm",
"tax": 0.01,
"taxRate": 1,
"tenderId": "b44464dbf0649ba59eba1b7387fec69a",
"totalPrice": 1.0,
"unit": "個"
},
{
"brand": "brand",
"buzzRound": 1,
"cost": 2079.21,
"explain": "備註",
"invoiceType": "增值稅普通發票",
"materialCode": "60010300000001",
"materialName": "裁紙器",
"planQuantity": 1.0,
"price": 2100.0,
"remark": "",
"specification": "B41",
"tax": 20.79,
"taxRate": 1,
"tenderId": "b44464dbf0649ba59eba1b7387fec69a",
"totalPrice": 2100.0,
"unit": "個"
},
{
"brand": "brand",
"buzzRound": 1,
"cost": 2079.21,
"explain": "備註",
"invoiceType": "增值稅普通發票",
"materialCode": "60010300000002",
"materialName": "長尾夾",
"planQuantity": 1.0,
"price": 2100.0,
"remark": "",
"specification": "32mm",
"tax": 20.79,
"taxRate": 1,
"tenderId": "b44464dbf0649ba59eba1b7387fec69a",
"totalPrice": 2100.0,
"unit": "盒"
},
{
"brand": "brand",
"buzzRound": 1,
"cost": 2079.21,
"explain": "備註",
"invoiceType": "增值稅普通發票",
"materialCode": "60010300000003",
"materialName": "垃圾桶",
"planQuantity": 1.0,
"price": 2100.0,
"remark": "",
"specification": "35*35*45cm",
"tax": 20.79,
"taxRate": 1,
"tenderId": "b44464dbf0649ba59eba1b7387fec69a",
"totalPrice": 2100.0,
"unit": "個"
}
]
這裡我們採用的方法是:首先拆分excel模板,使用POI操作excel複製區域性模板,最後使用easyexcel的多列表組合填充填充,模板如下所示
序號 | 物料編碼 | 物料名稱 | 規格型號 | 品牌/廠家 | 計劃備註 | 單位 | 數量 | 第#輪 | ||||||
發票型別 | 含稅單價(元) | 含稅總價(元) | 稅率(%) | 總稅金(元) | 不含稅總價(元) | 備註 | ||||||||
{data0.materialCode} | {data0.materialName} | {data0.specification} | {data0.brand} | {data0.remark} | {data0.unit} | {data0.planQuantity} | {data#.invoiceType} | {data#.price} | {data#.totalPrice} | {data#.taxRate} | {data#.tax} | {data#.cost} | {data#.explain} |
使用POI根據輪數複製data#處模板,並修改模板的資料,修改後的模板如下:
序號 | 物料編碼 | 物料名稱 | 規格型號 | 品牌/廠家 | 計劃備註 | 單位 | 數量 | 第2輪 | 第1輪 | ||||||||||||
型別 | 含稅單價(元) | 含稅總價(元) | 稅率(%) | 總稅金(元) | 不含稅總價(元) | 報價備註 | 型別 | 含稅單價(元) | 含稅總價(元) | 稅率(%) | 總稅金(元) | 不含稅總價(元) | 備註 | ||||||||
{data0.materialCode} | {data0.materialName} | {data0.specification} | {data0.brand} | {data0.remark} | {data0.unit} | {data0.planQuantity} | {data2.invoiceType} | {data2.price} | {data2.totalPrice } | {data2.taxRate} | {data2.tax} | {data2.cost} | {data2.explain} | {data1.invoiceType} | {data1.price} | {data1.totalPrice } | {data1.taxRate} | {data1.tax} | {data1.cost} | {data1.explain} |
核心程式碼如下
try {
String templateFileName = "C:\\Users\\user\\Desktop\\template.xlsx";
File fi = new File(templateFileName);
XSSFWorkbook wb = new XSSFWorkbook(new FileInputStream(fi));
Sheet sheet = wb.getSheetAt(0);
//起始列
int startColumn = 8;
//每輪列數
int step = 8;
//輪數
int round = 3;
//輪次
for (int i = 1; i < round; i++) {
//行
for (int j = 0; j < 3; j++) {
//列
for (int k = 0; k < step; k++) {
copyCell(sheet, j, startColumn + k, sheet, j, (i * startColumn) + step + k);
}
if (j == 0) {
continue;
}
}
}
//更新模板數字
for (int i = 1; i <= round; i++) {
//行
for (int j = 0; j < 3; j++) {
if (j == 0) {
Cell cell = getCell(sheet, j, startColumn + ((i - 1) * step));
cell.setCellValue(cell.getStringCellValue().replace("#", String.valueOf(round - i + 1)));
//合併單元格
sheet.addMergedRegion(new CellRangeAddress(
j, //first row (0-based)
j, //last row (0-based)
startColumn + ((i - 1) * step), //first column (0-based)
startColumn + ((i - 1) * step) + (step - 1) //last column (0-based)
));
}
if (j == 2) {
//列
for (int k = 0; k < step; k++) {
Cell cell = getCell(sheet, j, startColumn + ((i - 1) * step) + k);
cell.setCellValue(cell.getStringCellValue().replace("#", String.valueOf(round - i + 1)));
}
}
}
}
FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\user\\Desktop\\" + System.currentTimeMillis() + ".xlsx");
wb.write(fileOutputStream);
wb.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 從(s1,r1,c1)單元格複製內容到(s2,r2,c2)
*/
public static void copyCell(Sheet s1, int r1, int c1, Sheet s2, int r2, int c2) {
Cell cell = getCell(s2, r2, c2);
Object obj = getObj(s1, r1, c1);
if (null == obj) {
//為空不處理
} else if (obj instanceof String) {
cell.setCellValue((String) obj);
} else if (obj instanceof Date) {
cell.setCellValue((Date) obj);
} else if (obj instanceof Double) {
cell.setCellValue((double) obj);
} else {
System.out.println("未處理型別:" + obj.getClass());
}
}
private static Cell getCell(Sheet sheet, int r, int c) {
Row row = sheet.getRow(r);
if (row == null) {
row = sheet.createRow(r);
}
Cell cell = row.getCell(c);
if (cell == null) {
cell = row.createCell(c);
}
return cell;
}
private static Object getObj(Sheet sheet, int r, int c) {
Cell cell = getCell(sheet, r, c);
if (cell.getCellTypeEnum() == CellType.NUMERIC) {
if (HSSFDateUtil.isCellDateFormatted(cell)) {
return cell.getDateCellValue();
} else {
return cell.getNumericCellValue();
}
} else if (cell.getCellTypeEnum() == CellType.STRING) {
return cell.getStringCellValue();
} else if (cell.getCellTypeEnum() == CellType.BLANK) {
return null;
} else if (cell.getCellTypeEnum() == CellType.FORMULA) {
if (cell.getCachedFormulaResultTypeEnum() == CellType.NUMERIC) {
return cell.getNumericCellValue();
} else {
return cell.getStringCellValue();
}
} else {
System.out.println("(" + r + "," + c + ")未處理型別:" + cell.getCellTypeEnum());
}
return "";
}
以上使用POI工具將資料模板製作成功,還需要調整資料結構使其對應模板中的data#資料,調整後的資料結構如下:其中key標識輪數 ,陣列則為需要渲染的模板資料, key=0 表示基本資訊
{
"0": [
{
"brand": "brand",
"materialCode": "60010300000001",
"materialName": "裁紙器",
"planQuantity": 1.0,
"remark": "",
"specification": "B41",
"unit": "個"
},
{
"brand": "brand",
"materialCode": "60010300000002",
"materialName": "長尾夾",
"planQuantity": 1.0,
"remark": "",
"specification": "32mm",
"unit": "盒"
},
{
"brand": "brand",
"materialCode": "60010300000003",
"materialName": "垃圾桶",
"planQuantity": 1.0,
"remark": "",
"specification": "35*35*45cm",
"unit": "個"
}
],
"1": [
{
"buzzRound": 1,
"cost": 2079.21,
"explain": "報價備註",
"invoiceType": "增值稅普通發票",
"price": 2100.0,
"tax": 20.79,
"taxRate": 1,
"totalPrice": 2100.0
},
{
"buzzRound": 1,
"cost": 2079.21,
"explain": "報價備註",
"invoiceType": "增值稅普通發票",
"price": 2100.0,
"tax": 20.79,
"taxRate": 1,
"totalPrice": 2100.0
},
{
"buzzRound": 1,
"cost": 2079.21,
"explain": "報價備註",
"invoiceType": "增值稅普通發票",
"price": 2100.0,
"tax": 20.79,
"taxRate": 1,
"totalPrice": 2100.0
}
],
"2": [
{
"buzzRound": 2,
"cost": 2.91,
"explain": "3",
"invoiceType": "增值稅專用發票",
"price": 3.0,
"tax": 0.09,
"taxRate": 3,
"totalPrice": 3.0
},
{
"buzzRound": 2,
"cost": 1.96,
"explain": "2",
"invoiceType": "增值稅普通發票",
"price": 2.0,
"tax": 0.04,
"taxRate": 2,
"totalPrice": 2.0
},
{
"buzzRound": 2,
"cost": 0.99,
"explain": "1",
"invoiceType": "增值稅專用發票",
"price": 1.0,
"tax": 0.01,
"taxRate": 1,
"totalPrice": 1.0
}
]
}
最後根據easyexcel的 :多列表組合填充填充 填充資料 核心程式碼如下:
ExcelWriter excelWriter = EasyExcel.write(byteArrayOutputStream).withTemplate(inputStream).build();
WriteSheet writeSheet = EasyExcel.writerSheet().build();
bidMaterialHistory.forEach((k, v) -> {
excelWriter.fill(new FillWrapper("data" + k, v), writeSheet);
});
excelWriter.finish();