|
@ -129,33 +129,40 @@ public class WordReportExporter { |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private static void addScoreTable(XWPFDocument document, ReportData reportData) { |
|
|
private static void addScoreTable(XWPFDocument document, ReportData reportData) { |
|
|
List<CheckItem> items = reportData.getCheckItems(); |
|
|
List<CheckItem> items = reportData.getCheckItems(); |
|
|
if (items == null || items.isEmpty()) return; |
|
|
if (items == null || items.isEmpty()) return; |
|
|
|
|
|
|
|
|
// ✅ 1. 创建表格
|
|
|
// ✅ 1. 创建表格
|
|
|
XWPFTable table = document.createTable(); |
|
|
XWPFTable table = document.createTable(); |
|
|
|
|
|
|
|
|
// ✅ 2. 设置表格宽度
|
|
|
// ✅ 2. 设置表格总宽度 (9000 TWIP ≈ 页面宽度)
|
|
|
CTTblPr tblPr = table.getCTTbl().getTblPr(); |
|
|
CTTblPr tblPr = table.getCTTbl().getTblPr(); |
|
|
CTTblWidth tblWidth = tblPr.addNewTblW(); |
|
|
CTTblWidth tblWidth = tblPr.addNewTblW(); |
|
|
tblWidth.setW(BigInteger.valueOf(9000)); |
|
|
tblWidth.setW(BigInteger.valueOf(9000)); |
|
|
tblWidth.setType(STTblWidth.DXA); |
|
|
tblWidth.setType(STTblWidth.DXA); |
|
|
|
|
|
|
|
|
// ✅ 3. 强制第一行有 5 列(必须!否则 createRow 后 getCell 越界)
|
|
|
// ✅ 3. 强制第一行有 5 列
|
|
|
XWPFTableRow header = table.getRow(0); |
|
|
XWPFTableRow header = table.getRow(0); |
|
|
for (int i = 1; i < 5; i++) { |
|
|
for (int i = 1; i < 5; i++) { |
|
|
header.addNewTableCell(); |
|
|
header.addNewTableCell(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// ✅ 4. 设置表头文本和样式(不要提前操作 TcPr)
|
|
|
// ✅ 4. 设置表头文本和样式
|
|
|
setCellTextAndStyle(header.getCell(0), "检查项目", true); |
|
|
setCellTextAndStyle(header.getCell(0), "检查项目", true); |
|
|
setCellTextAndStyle(header.getCell(1), "检查要点", true); |
|
|
setCellTextAndStyle(header.getCell(1), "检查要点", true); |
|
|
setCellTextAndStyle(header.getCell(2), "单项分值", true); |
|
|
setCellTextAndStyle(header.getCell(2), "单项分值", true); |
|
|
setCellTextAndStyle(header.getCell(3), "得分", true); |
|
|
setCellTextAndStyle(header.getCell(3), "得分", true); |
|
|
setCellTextAndStyle(header.getCell(4), "评分说明", true); |
|
|
setCellTextAndStyle(header.getCell(4), "评分说明", true); |
|
|
|
|
|
|
|
|
// ✅ 5. 分组数据
|
|
|
// ✅ 5. 设置列宽(关键:20% / 35% / 10% / 10% / 25%)
|
|
|
|
|
|
setCellWidth(header.getCell(0), 1500); // 检查项目
|
|
|
|
|
|
setCellWidth(header.getCell(1), 2800); // 检查要点
|
|
|
|
|
|
setCellWidth(header.getCell(2), 950); // 单项分值(窄)
|
|
|
|
|
|
setCellWidth(header.getCell(3), 950); // 得分(窄)
|
|
|
|
|
|
setCellWidth(header.getCell(4), 2200); // 评分说明
|
|
|
|
|
|
|
|
|
|
|
|
// ✅ 6. 分组数据
|
|
|
Map<String, List<CheckItem>> grouped = items.stream() |
|
|
Map<String, List<CheckItem>> grouped = items.stream() |
|
|
.sorted(Comparator.comparing(CheckItem::getCheckProject)) |
|
|
.sorted(Comparator.comparing(CheckItem::getCheckProject)) |
|
|
.collect(Collectors.groupingBy( |
|
|
.collect(Collectors.groupingBy( |
|
@ -172,29 +179,28 @@ public class WordReportExporter { |
|
|
|
|
|
|
|
|
for (int i = 0; i < groupItems.size(); i++) { |
|
|
for (int i = 0; i < groupItems.size(); i++) { |
|
|
CheckItem item = groupItems.get(i); |
|
|
CheckItem item = groupItems.get(i); |
|
|
XWPFTableRow row = table.createRow(); // ✅ 自动 5 列
|
|
|
XWPFTableRow row = table.createRow(); |
|
|
|
|
|
|
|
|
// ✅ 先设置文本和样式
|
|
|
// 设置内容
|
|
|
setCellTextAndStyle(row.getCell(1), item.formatCheckItem(), false); |
|
|
setCellTextAndStyle(row.getCell(1), item.formatCheckItem(), false); |
|
|
setCellTextAndStyle(row.getCell(2), String.valueOf(item.getItemScore()), false); |
|
|
setCellTextAndStyle(row.getCell(2), String.valueOf(item.getItemScore()), false); |
|
|
setCellTextAndStyle(row.getCell(3), String.valueOf(item.getObtainedScore()), false); |
|
|
setCellTextAndStyle(row.getCell(3), String.valueOf(item.getObtainedScore()), false); |
|
|
setCellTextAndStyle(row.getCell(4), item.formatScoringExplanation(), false); |
|
|
setCellTextAndStyle(row.getCell(4), item.formatScoringExplanation(), false); |
|
|
|
|
|
|
|
|
// ✅ 累加得分
|
|
|
// ✅ 累加总分
|
|
|
totalScore += item.getObtainedScore(); |
|
|
totalScore += item.getObtainedScore(); |
|
|
|
|
|
|
|
|
// ✅ 再设置第0列的合并
|
|
|
// 设置第0列(检查项目)及垂直合并
|
|
|
XWPFTableCell cell = row.getCell(0); |
|
|
XWPFTableCell cell = row.getCell(0); |
|
|
setCellTextAndStyle(cell, i == 0 ? project : "", i == 0); // 只有第一行显示项目名
|
|
|
setCellTextAndStyle(cell, i == 0 ? project : "", i == 0); |
|
|
|
|
|
|
|
|
// ✅ 最后设置垂直合并
|
|
|
CTTcPr tcPr = cell.getCTTc().addNewTcPr(); |
|
|
CTTcPr tcPr = cell.getCTTc().addNewTcPr(); // ✅ 这里可以 addNew,因为 setCellTextAndStyle 已完成
|
|
|
|
|
|
CTVMerge merge = tcPr.addNewVMerge(); |
|
|
CTVMerge merge = tcPr.addNewVMerge(); |
|
|
merge.setVal(i == 0 ? STMerge.RESTART : STMerge.CONTINUE); |
|
|
merge.setVal(i == 0 ? STMerge.RESTART : STMerge.CONTINUE); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// ✅ 6. 总分行
|
|
|
// ✅ 7. 总分行
|
|
|
XWPFTableRow totalRow = table.createRow(); |
|
|
XWPFTableRow totalRow = table.createRow(); |
|
|
XWPFTableCell labelCell = totalRow.getCell(0); |
|
|
XWPFTableCell labelCell = totalRow.getCell(0); |
|
|
setCellTextAndStyle(labelCell, "总分", true); |
|
|
setCellTextAndStyle(labelCell, "总分", true); |
|
@ -202,11 +208,11 @@ public class WordReportExporter { |
|
|
XWPFTableCell scoreCell = totalRow.getCell(1); |
|
|
XWPFTableCell scoreCell = totalRow.getCell(1); |
|
|
setCellTextAndStyle(scoreCell, String.valueOf(totalScore), true); |
|
|
setCellTextAndStyle(scoreCell, String.valueOf(totalScore), true); |
|
|
|
|
|
|
|
|
// ✅ 合并第1~4列(共4列)
|
|
|
// 合并第1~4列(共4列)
|
|
|
CTTcPr tcPr = scoreCell.getCTTc().addNewTcPr(); |
|
|
CTTcPr tcPr = scoreCell.getCTTc().addNewTcPr(); |
|
|
tcPr.addNewGridSpan().setVal(BigInteger.valueOf(4)); |
|
|
tcPr.addNewGridSpan().setVal(BigInteger.valueOf(4)); |
|
|
|
|
|
|
|
|
// ✅ 清空后面单元格(可选)
|
|
|
// 清空后面单元格(避免显示多余文本)
|
|
|
for (int i = 2; i <= 4; i++) { |
|
|
for (int i = 2; i <= 4; i++) { |
|
|
XWPFTableCell cell = totalRow.getCell(i); |
|
|
XWPFTableCell cell = totalRow.getCell(i); |
|
|
if (cell != null) { |
|
|
if (cell != null) { |
|
@ -215,29 +221,57 @@ public class WordReportExporter { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private static void setCellTextAndStyle(XWPFTableCell cell, String text, boolean isHeader) { |
|
|
// ✅ 辅助方法:设置单元格宽度
|
|
|
// 1. 先设置文本(✅ 支持 \n 换行)
|
|
|
private static void setCellWidth(XWPFTableCell cell, int width) { |
|
|
cell.setText(text); |
|
|
|
|
|
|
|
|
|
|
|
// 2. 获取或创建单元格属性,设置宽度
|
|
|
|
|
|
CTTc ctTc = cell.getCTTc(); |
|
|
CTTc ctTc = cell.getCTTc(); |
|
|
CTTcPr tcPr = ctTc.isSetTcPr() ? ctTc.getTcPr() : ctTc.addNewTcPr(); |
|
|
CTTcPr tcPr = ctTc.isSetTcPr() ? ctTc.getTcPr() : ctTc.addNewTcPr(); |
|
|
CTTblWidth tcW = tcPr.getTcW() != null ? tcPr.getTcW() : tcPr.addNewTcW(); |
|
|
CTTblWidth tcW = tcPr.isSetTcW() ? tcPr.getTcW() : tcPr.addNewTcW(); |
|
|
tcW.setW(BigInteger.valueOf(2000)); |
|
|
tcW.setW(BigInteger.valueOf(width)); |
|
|
tcW.setType(STTblWidth.DXA); // 推荐设置单位
|
|
|
tcW.setType(STTblWidth.DXA); |
|
|
|
|
|
} |
|
|
// 3. 遍历所有段落和 Run,设置样式
|
|
|
private static void setCellTextAndStyle(XWPFTableCell cell, String text, boolean isHeader) { |
|
|
for (XWPFParagraph p : cell.getParagraphs()) { |
|
|
cell.removeParagraph(0); |
|
|
p.setSpacingAfter(50); // 段后间距
|
|
|
XWPFParagraph p = cell.addParagraph(); |
|
|
for (XWPFRun r : p.getRuns()) { |
|
|
p.setAlignment(isHeader ? ParagraphAlignment.CENTER : ParagraphAlignment.LEFT); |
|
|
r.setFontFamily("宋体"); |
|
|
p.setSpacingAfter(0); |
|
|
r.setFontSize(10); |
|
|
p.setSpacingBefore(0); |
|
|
if (isHeader) { |
|
|
|
|
|
r.setBold(true); |
|
|
String[] lines = (text == null ? "" : text).split("\n", -1); // 保留末尾空行
|
|
|
} |
|
|
|
|
|
|
|
|
for (int i = 0; i < lines.length; i++) { |
|
|
|
|
|
if (i > 0) { |
|
|
|
|
|
p.createRun().addCarriageReturn(); |
|
|
} |
|
|
} |
|
|
|
|
|
XWPFRun run = p.createRun(); |
|
|
|
|
|
run.setText(lines[i]); |
|
|
|
|
|
run.setBold(isHeader); |
|
|
|
|
|
run.setFontSize(10); |
|
|
|
|
|
run.setFontFamily("等线"); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
// private static void setCellTextAndStyle(XWPFTableCell cell, String text, boolean isHeader) {
|
|
|
|
|
|
// // 1. 先设置文本(✅ 支持 \n 换行)
|
|
|
|
|
|
// cell.setText(text);
|
|
|
|
|
|
//
|
|
|
|
|
|
// // 2. 获取或创建单元格属性,设置宽度
|
|
|
|
|
|
// CTTc ctTc = cell.getCTTc();
|
|
|
|
|
|
// CTTcPr tcPr = ctTc.isSetTcPr() ? ctTc.getTcPr() : ctTc.addNewTcPr();
|
|
|
|
|
|
// CTTblWidth tcW = tcPr.getTcW() != null ? tcPr.getTcW() : tcPr.addNewTcW();
|
|
|
|
|
|
// tcW.setW(BigInteger.valueOf(2000));
|
|
|
|
|
|
// tcW.setType(STTblWidth.DXA); // 推荐设置单位
|
|
|
|
|
|
//
|
|
|
|
|
|
// // 3. 遍历所有段落和 Run,设置样式
|
|
|
|
|
|
// for (XWPFParagraph p : cell.getParagraphs()) {
|
|
|
|
|
|
// p.setSpacingAfter(50); // 段后间距
|
|
|
|
|
|
// for (XWPFRun r : p.getRuns()) {
|
|
|
|
|
|
// r.setFontFamily("宋体");
|
|
|
|
|
|
// r.setFontSize(10);
|
|
|
|
|
|
// if (isHeader) {
|
|
|
|
|
|
// r.setBold(true);
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
private static void addTotalAndRemarks(XWPFDocument document, ReportData reportData) { |
|
|
private static void addTotalAndRemarks(XWPFDocument document, ReportData reportData) { |
|
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); |
|
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); |
|
|
|
|
|
|
|
|