springBoot 整合 poi 导出带有复杂表格(合并表格)的word文件
1.Maven依赖见上一篇文章
直接贴源码如下:
package com.mingx.pms.web.system.file; import cn.hutool.core.date.DateUtil; import com.mingx.pms.constant.SystemInfo; import com.mingx.pms.entities.workplan.plan.vo.WorkPlanDetailExportVO; import com.mingx.pms.entities.workplan.plan.vo.WorkPlanDetailQueryRO; import com.mingx.pms.entities.workplan.plan.vo.WorkPlanDetailVO; import com.mingx.pms.entities.workplan.report.constant.ReportTypeDict; import com.mingx.pms.entities.workplan.report.vo.*; import com.mingx.pms.entity.PageResult; import com.mingx.pms.entity.Paginator; import com.mingx.pms.exception.CustomException; import com.mingx.pms.tools.BillCodeUtil; import com.mingx.pms.workplan.service.plan.IWorkPlanDetailService; import com.mingx.pms.workplan.service.report.IWorkReportService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.apache.poi.xwpf.usermodel.*; import org.camunda.feel.syntaxtree.In; import org.openxmlformats.schemas.wordprocessingml.x2006.main.*; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.lang.reflect.Field; import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; /** * @Description: 生产计划报告模块导出 * @Author: gch * @Date: 2023/8/18 10:34 */ @Api(value = "WorkPlanReportExportProvider", tags = "导出-生产计划报告模块") @Controller public class WorkPlanReportExportProvider extends BaseExportProvider{ @Resource private IWorkReportService workReportService; @PostMapping("/workPlanReport/export") @ApiOperation(value = "导出生产计划报告Word", notes = "导出生产计划报告Word", produces = "application/octet-stream") public void exportWord(@RequestBody GenerateDetailRO generateDetailRO, HttpServletResponse response) throws IOException { String reportTypeDesc; String descByType; if (ReportTypeDict.MONTH.getCode().equals(generateDetailRO.getReportType())) { reportTypeDesc = ReportTypeDict.MONTH.getDesc(); descByType = "本月"; } else if (ReportTypeDict.WEEK.getCode().equals(generateDetailRO.getReportType())) { reportTypeDesc = ReportTypeDict.WEEK.getDesc(); descByType = "本周"; } else { throw new CustomException("报告类型错误", WorkPlanReportExportProvider.class.toString()); } if (Objects.isNull(generateDetailRO.getReportId())) { throw new CustomException("报告id必填", WorkPlanReportExportProvider.class.toString()); } WorkReportDetailVO workReportDetailVO = workReportService.generateDetailInfo(generateDetailRO); // 创建一个新的Word文档 XWPFDocument document = new XWPFDocument(); String formatStart = DateUtil.format(workReportDetailVO.getReportStartTime(), SystemInfo.DATE_CHINESE); String formatEnd = DateUtil.format(workReportDetailVO.getReportEndTime(), SystemInfo.DAY_CHINESE); String title = workReportDetailVO.getProjectDepartName() + "生产作业" + reportTypeDesc + "(" + formatStart + "-" + formatEnd + " )"; addParagraphCenter(document, title); // 添加段落 addParagraph(document, "一、电网运行总体情况:"); addContent(document,"1、电网安全情况:"); if (workReportDetailVO.getSafeSituationTextList().isEmpty()) { addContent(document, ""); } else { for (String content : workReportDetailVO.getSafeSituationTextList()) { addContent(document, content); } } addContent(document,"2、电网运行情况:"); if (workReportDetailVO.getRunSituationTextList().isEmpty()) { addContent(document, ""); } else { for (String content : workReportDetailVO.getRunSituationTextList()) { addContent(document, content); } } addParagraph(document, "二、违章情况"); addContent(document, "1、本周发生恶性违章" + workReportDetailVO.getMalignancyBreakRuleNum() + "条,严重违章 " + workReportDetailVO.getSeriousBreakRuleNum() + " 条,一般违章 " + workReportDetailVO.getNormalBreakRuleNum() + " 条。"); addParagraph(document, "三、作业计划执行情况"); addContent(document, "1、月作业计划中本周(本月)应完成 " + workReportDetailVO.getShouldFinish() + " 条,实际完成 " + workReportDetailVO.getRealFinish() + " 条"); // 添加表格 List<String> headers = Arrays.asList("序号", "作业计划内容", "时间", "未执行原因"); // 对象集合 转换为表格识别的 列表类型 List<List<String>> data = new ArrayList<>(); Integer order = 1; for (WorkPlanStatisticTableVO unExecutePlan : workReportDetailVO.getUnExecutePlans()) { List<String> attributeValues = getAttributeValues(unExecutePlan); // 在列表最前面添加序号 attributeValues.add(0, order.toString()); order++; data.add(attributeValues); } addTable(document, headers, data); addContent(document, "2、" + descByType + "新增其他作业计划 " + workReportDetailVO.getAddWorkPlanNum() + " 条(月计划中未计划的)"); // 添加表格 List<String> headers2 = Arrays.asList("序号", "作业计划内容", "时间"); List<List<String>> data2 = new ArrayList<>(); Integer order2 = 1; for (WorkPlanStatisticTableVO vo : workReportDetailVO.getAddPlans()) { List<String> attributeValues = getAttributeValues(vo); attributeValues.add(0, order2.toString()); order2++; data.add(attributeValues); } addTable(document, headers2, data2); if (ReportTypeDict.WEEK.getCode().equals(generateDetailRO.getReportType())) { addContent(document, "3、下周新增其他作业计划 " + workReportDetailVO.getNextWeekAddWorkPlanNum() + " 条(月计划中未计划的)"); // 添加表格 List<String> headers3 = Arrays.asList("序号", "作业计划内容", "时间"); List<List<String>> data3 = new ArrayList<>(); Integer order3 = 1; for (WorkPlanStatisticTableVO vo : workReportDetailVO.getNextWeekAddPlans()) { List<String> attributeValues = getAttributeValues(vo); attributeValues.add(0, order3.toString()); order3++; data.add(attributeValues); } addTable(document, headers3, data3); } addParagraph(document, "四、停电情况"); addContent(document, "1、" + descByType + "主网停电 " + workReportDetailVO.getMainNetPowerCutNum() + " 次。"); addContent(document, "2、" + descByType + "公司计划停电应执行 " + workReportDetailVO.getCompanyPlanNum() + " 次,实际执行 " + workReportDetailVO.getRealExecutePowerCutNum() + " 次,临时停电" + workReportDetailVO.getTempPowerCutNum() + "次,故障停电 " + workReportDetailVO.getBreakdownPowerCutNum() + " 次。重复停电 " + workReportDetailVO.getRepeatPowerCutNum() + " 次"); addContent(document,descByType + "公司计划停电未执行情况"); // 添加表格 List<String> headers4 = Arrays.asList("序号", "项目部", "变电站/线路名称", "计划停电时间", "未执行原因"); List<List<String>> data4 = new ArrayList<>(); Integer order4 = 1; for (PowerCutUnExecuteStatisticTableVO vo : workReportDetailVO.getPowerCutUnExecuteInfos()) { List<String> attributeValues = getAttributeValues(vo); attributeValues.add(0, order4.toString()); order4++; data.add(attributeValues); } addTable(document, headers4, data4); addContent(document, descByType + "临时停电情况"); // 添加表格 List<String> headers5 = Arrays.asList("序号", "项目部", "变电站/线路名称", "停电时间", "送电时间", "停电时长(h)", "损失电量(kWh)", "停电具体原因", "处理经过"); List<List<String>> data5 = new ArrayList<>(); Integer order5 = 1; for (PowerCutStatisticTableVO vo : workReportDetailVO.getTempPowerCutInfos()) { List<String> attributeValues = getAttributeValues(vo); attributeValues.add(0, order5.toString()); order5++; data.add(attributeValues); } addTable(document, headers5, data5); addContent(document, descByType + "故障停电情况"); // 添加表格 List<String> headers6 = Arrays.asList("序号", "项目部", "变电站/线路名称", "停电时间", "送电时间", "停电时长(h)", "损失电量(kWh)", "停电具体原因", "处理经过"); List<List<String>> data6 = new ArrayList<>(); Integer order6 = 1; for (PowerCutStatisticTableVO vo : workReportDetailVO.getBreakdownPowerCutInfos()) { List<String> attributeValues = getAttributeValues(vo); attributeValues.add(0, order6.toString()); order6++; data.add(attributeValues); } addTable(document, headers6, data6); addParagraph(document, "五、隐患及缺陷消除情况"); // 添加表格 List<List<String>> data7 = new ArrayList<>(); Integer order7 = 1; for (DefectHandleStatisticTableVO vo : workReportDetailVO.getDefectHandleInfos()) { List<String> attributeValues = getAttributeValues(vo); attributeValues.add(0, order7.toString()); order7++; data.add(attributeValues); } addMergeTable(document, data7); addContent(document,"严重及以上缺陷未消除情况说明:"); if (workReportDetailVO.getSeriousDefectUneliminatedTextList().isEmpty()) { addContent(document, ""); } else { for (String content : workReportDetailVO.getRunSituationTextList()) { addContent(document, content); } } // 这里是动态标题和内容 for (OtherDetailVO otherDetailVO : workReportDetailVO.getOtherDetailInfoList()) { addParagraph(document, otherDetailVO.getTitle()); for (String content : otherDetailVO.getContents()) { addContent(document, content); } } // 设置响应头信息 response.setHeader("Content-Disposition", "attachment; filename=" + title + ".docx"); response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document"); // 将文档写入输出流 document.write(response.getOutputStream()); document.close(); } /** * @author: gch * @Description: 添加基础文本内容 * @Date: 2023/8/19 12:12 * @Param: * @return: */ private void addContent(XWPFDocument document, String content) { document.createParagraph().createRun().setText(content); } /** * @author: gch * @Description: 添加段落 * @Date: 2023/8/19 12:11 * @Param: * @return: */ private void addParagraph(XWPFDocument document, String s) { XWPFParagraph paragraph1 = document.createParagraph(); paragraph1.setAlignment(ParagraphAlignment.LEFT); XWPFRun run1 = paragraph1.createRun(); run1.setBold(true); run1.setFontSize(16); // 设置标题字体大小 run1.setText(s); } /** * @author: gch * @Description: 添加段落 居中 * @Date: 2023/8/19 12:11 * @Param: * @return: */ private void addParagraphCenter(XWPFDocument document, String s) { XWPFParagraph paragraph1 = document.createParagraph(); paragraph1.setAlignment(ParagraphAlignment.CENTER); XWPFRun run1 = paragraph1.createRun(); run1.setBold(true); run1.setFontSize(16); // 设置标题字体大小 run1.setText(s); } /** * @author: gch * @Description: 添加动态表格 * @Date: 2023/8/19 12:11 * @Param: * @return: */ private void addTable(XWPFDocument document, List<String> headers, List<List<String>> data) { Integer rowNum = 2; if (!Objects.isNull(data) && !data.isEmpty()) { rowNum = data.size() + 1; } // 添加表格 XWPFTable table = document.createTable(rowNum, headers.size()); // 创建一个3行3列的表格 // 创建表格后给每个单元格设置样式 for (int row = 0; row < rowNum; row++) { for (int col = 0; col < headers.size(); col++) { XWPFTableCell cell = table.getRow(row).getCell(col); cell.setWidth("1000"); addCellCenterStyle(cell); } } // 设置表头 for (int col = 0; col < headers.size(); col++) { XWPFTableCell cell = table.getRow(0).getCell(col); cell.setText(headers.get(col)); } // 设置示例数据 for (int row = 0; row < data.size(); row++) { for (int col = 0; col < headers.size(); col++) { XWPFTableCell cell = table.getRow(row + 1).getCell(col); cell.setText(data.get(row).get(col)); } } } /** * @author: gch * @Description: 添加动态表格 * @Date: 2023/8/19 12:11 * @Param: * @return: */ private void addMergeTable(XWPFDocument document, List<List<String>> data) { Integer rowNum = 4; if (!Objects.isNull(data) && !data.isEmpty()) { rowNum = data.size() + 3; } // 添加表格 XWPFTable table = document.createTable(rowNum, 10); // 创建表格后给每个单元格设置样式 for (int row = 0; row < rowNum; row++) { for (int col = 0; col < 10; col++) { XWPFTableCell cell = table.getRow(row).getCell(col); cell.setWidth("1000"); addCellCenterStyle(cell); } } mergeCellsHorizontal(table, 0, 1, 6); mergeCellsHorizontal(table, 0, 7, 9); mergeCellsHorizontal(table, 1, 1, 2); mergeCellsHorizontal(table, 1, 3, 4); mergeCellsHorizontal(table, 1, 5, 6); mergeCellsVertically(table, 0, 0, 2); mergeCellsVertically(table, 7, 1, 2); mergeCellsVertically(table, 8, 1, 2); mergeCellsVertically(table, 9, 1, 2); //手动设置表头 addTableContext(table, 0, 0, "项目部"); addTableContext(table, 0, 1, "本周发现及处理情况"); addTableContext(table, 0, 7, "累计未消除缺陷"); addTableContext(table, 1, 1, "危急缺陷"); addTableContext(table, 1, 3, "重大缺陷"); addTableContext(table, 1, 5, "一般缺陷"); addTableContext(table, 1, 7, "危急缺陷"); addTableContext(table, 1, 8, "重大缺陷"); addTableContext(table, 1, 9, "一般缺陷"); addTableContext(table, 2, 1, "发现"); addTableContext(table, 2, 2, "消除"); addTableContext(table, 2, 3, "发现"); addTableContext(table, 2, 4, "消除"); addTableContext(table, 2, 5, "发现"); addTableContext(table, 2, 6, "消除"); // 设置示例数据 for (int row = 0; row < data.size(); row++) { for (int col = 0; col < 10; col++) { XWPFTableCell cell = table.getRow(row + 3).getCell(col); cell.setText(data.get(row).get(col)); } } } private static void addTableContext(XWPFTable table, int row, int col, String text) { XWPFTableCell cell = table.getRow(row).getCell(col); cell.setText(text); } /** * @author: gch * @Description: 添加水平居中韩国样式 * @Date: 2023/8/19 12:11 * @Param: * @return: */ private void addCellCenterStyle(XWPFTableCell cell) { CTTc ctTc = cell.getCTTc(); //获取 CTP CTP ctP = (ctTc.sizeOfPArray() == 0) ? ctTc.addNewP() : ctTc.getPArray(0); //获取段落 XWPFParagraph par = cell.getParagraph(ctP); //设置水平居中 par.setAlignment(ParagraphAlignment.CENTER); // 垂直居中 cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER); } /** * @Description: 跨列合并 * table要合并单元格的表格 * row要合并哪一行的单元格 * fromCell开始合并的单元格 * toCell合并到哪一个单元格 */ public static void mergeCellsHorizontal(XWPFTable table, int row, int fromCell, int toCell) { for (int cellIndex = fromCell; cellIndex <= toCell; cellIndex++) { XWPFTableCell cell = table.getRow(row).getCell(cellIndex); if ( cellIndex == fromCell ) { // The first merged cell is set with RESTART merge value cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART); } else { // Cells which join (merge) the first one, are set with CONTINUE cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE); } } } /** * @Description: 跨行合并 * table要合并单元格的表格 * col要合并哪一列的单元格 * fromRow从哪一行开始合并单元格 * toRow合并到哪一个行 */ public void mergeCellsVertically(XWPFTable table, int col, int fromRow, int toRow) { for (int rowIndex = fromRow; rowIndex <= toRow; rowIndex++) { XWPFTableCell cell = table.getRow(rowIndex).getCell(col); if ( rowIndex == fromRow ) { // The first merged cell is set with RESTART merge value cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART); } else { // Cells which join (merge) the first one, are set with CONTINUE cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE); } } } /** * @Description: 获取对象属性值并转换成字符串列表 * @Author: gch * @Date: 2023/8/23 10:54 */ public static List<String> getAttributeValues(Object obj) { Class<?> objClass = obj.getClass(); List<String> valuesList = new ArrayList<>(); // 获取对象定义的所有属性 Field[] fields = objClass.getDeclaredFields(); // 遍历属性并获取属性值 for (Field field : fields) { field.setAccessible(true); // 设置私有属性可访问 try { Object value = field.get(obj); String valueStr = (value != null) ? value.toString() : ""; valuesList.add(valueStr); } catch (IllegalAccessException e) { e.printStackTrace(); } } return valuesList; } }
运行结果如下:
⎛⎝官萧何⎠⎞一只快乐的爪哇程序猿;邮箱:1570608034@qq.com