树型结构递归导出excel,合并父级单元格
最近遇到一个需求,需要将树型结构列表数据导致至excel表格中,并且要合并父级数据的单元格,类似下图
通过递归算法真实导出来之后,发现子级节点会有空行,空行原因是父级写在了上一行,子级写在下一行,来呈现一个树结构,类似下图
因此需要调整递归算法逻辑,判断是叶子节点时,才创建新的一行,否则不创建新行,获取到的数据写在同一行,单元格合并逻辑是遍历到叶子节点列表最后一个时,则合并上一级父节点单元格
完整代码如下
在这里插入代码片
```public void exportWorkCodeExcel(HttpServletResponse response) throws IOException {
List<WhProject> permissionDataList = whProjectService.getProjectPermissionDataList().stream().sorted(Comparator.comparing(WhProject::getLevel).reversed()).collect(Collectors.toList());
Integer maxLevel = permissionDataList.stream().findFirst().get().getLevel();
List<ProjectTreeVo> projectTreeVos = buildTreeUseStream(permissionDataList, 0L);
String[] CHINESE_DIGITS = {"二", "三", "四", "五", "六", "七", "八", "九"};
String workCodeHeadTitle = "%s级工时编码";
String nameHeadTitle = "%s级工时编码名称";
//创建工作薄
XSSFWorkbook wb = new XSSFWorkbook();
//创建工作表
XSSFSheet sheet = wb.createSheet("工时编码");
//设置样式
XSSFCellStyle style = wb.createCellStyle();
//水平对齐
style.setAlignment(HorizontalAlignment.CENTER);
//垂直对齐
style.setVerticalAlignment(VerticalAlignment.CENTER);
XSSFFont font = wb.createFont();
font.setBold(true);
style.setFont(font);
//表头
//冻结表头
sheet.createFreezePane(0, 1);
XSSFRow sheet1row1 = sheet.createRow((short) 0);
sheet1row1.setHeight((short) 480);
//写入表头
int circle = 0;
for(int i = 0,j = 1; i < (maxLevel-1); i++,j = j + 1) {
String columnCode = String.format(workCodeHeadTitle,CHINESE_DIGITS[i]);
String columnName = String.format(nameHeadTitle, CHINESE_DIGITS[i]);
//列
XSSFCell cellCode = sheet1row1.createCell(i + circle);
XSSFCell cellName = sheet1row1.createCell(j + circle);
cellCode.setCellValue(columnCode);
cellCode.setCellStyle(style);
cellName.setCellValue(columnName);
cellName.setCellStyle(style);
circle = circle + 1;
}
//数据开始行
int rowIndex = 1;
for (ProjectTreeVo projectTreeVo : projectTreeVos) {
rowIndex = writeExcel(sheet, projectTreeVo, rowIndex, 0);
}
response.setContentType("application/x-download");
response.setCharacterEncoding("UTF-8");
String outFileName = URLEncoder.encode("工时编码.xlsx", "UTF-8");
//修复中文文件名输出乱码
response.setHeader("Content-disposition", "attachment;filename=" + outFileName + ";" + "filename*=utf-8''" + outFileName);
// 写入输出流
OutputStream outputStream = response.getOutputStream();
wb.write(outputStream);
outputStream.flush();
outputStream.close();
wb.close();
}
/**
* 写数据到excel中
* @param sheet 表格
* @param projectTreeVo 当前节点
* @param rowIndex 行号
* @param colIndex 列号
*/
private int writeExcel(XSSFSheet sheet, ProjectTreeVo projectTreeVo, int rowIndex, int colIndex) {
int startRow = rowIndex;
Row row = sheet.getRow(rowIndex);
if(Objects.isNull(row)){
row = sheet.createRow(rowIndex);
row.setHeight((short) 380);
}
Cell cell1 = row.createCell(colIndex);
cell1.setCellValue(projectTreeVo.getWorkHourCode());
//设置单元格宽度
sheet.setColumnWidth(colIndex, 256 * 25);
colIndex = colIndex + 1;
Cell cell2 = row.createCell(colIndex);
cell2.setCellValue(projectTreeVo.getName());
//自适应单元格宽度
//sheet.autoSizeColumn(colIndex);
sheet.setColumnWidth(colIndex, 256 * 25);
for (ProjectTreeVo child : projectTreeVo.getChildren()) {
rowIndex = writeExcel(sheet, child, rowIndex, colIndex + 1);
}
int endRow = rowIndex - 1;
if (startRow < endRow) { // 如果有子节点,则合并父级单元格
sheet.addMergedRegion(new CellRangeAddress(startRow, endRow, colIndex, colIndex));
sheet.addMergedRegion(new CellRangeAddress(startRow, endRow, colIndex-1, colIndex-1));
}
if(projectTreeVo.getIsLeaf() == 1){
//遇到最子级时,+1创建新的一行
rowIndex++;
}
return rowIndex;
}