Browse Source

word模板导出demov3

aqm-ops-supervision-platform
gjh 2 weeks ago
parent
commit
27f168f99c
  1. 20
      ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/CheckItem.java
  2. 5
      ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/ReportData.java
  3. 146
      ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/WordReportExporter.java

20
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/CheckItem.java

@ -22,6 +22,7 @@ public class CheckItem {
*/
private String checkProject;
/**
* 具体检查子项描述
* 示例值"站房清洁与规范管理"
@ -29,6 +30,9 @@ public class CheckItem {
*/
private String checkItem;
// TODO 需要设置集合
private List<String> checkItemBasis;
/**
* 该项满分分数
* 示例值2
@ -79,4 +83,20 @@ public class CheckItem {
}
return sb.toString().trim();
}
/**
* 格式化检查要点用于导出 Word
* 将检查要点拼接成带换行符的字符串
*
* @return 格式化后的完整说明文本
*/
public String formatCheckItem() {
StringBuilder sb = new StringBuilder();
if (this.checkItemBasis != null) {
for (String basis : this.checkItemBasis) {
sb.append(basis).append("\n");
}
}
return sb.toString().trim();
}
}

5
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/ReportData.java

@ -19,11 +19,6 @@ public class ReportData {
/** 站点名称,如:浦东新区张江空气质量监测站 */
private String siteName;
/** 所属区或管委会,如:浦东新区 */
//private String district;
/** 所属街道、乡或镇,如:张江镇 */
//private String street;
/** 运维单位,如:上海环保科技有限公司 */
private String operationUnit;

146
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/WordReportExporter.java

@ -2,8 +2,7 @@ package org.dromara.demo.domain;
import cn.hutool.json.JSONUtil;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblWidth;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTblWidth;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import java.io.File;
import java.io.FileOutputStream;
@ -134,81 +133,121 @@ public class WordReportExporter {
List<CheckItem> items = reportData.getCheckItems();
if (items == null || items.isEmpty()) return;
// ✅ 1. 创建表格
XWPFTable table = document.createTable();
CTTblWidth width = table.getCTTbl().getTblPr().addNewTblW();
width.setW(BigInteger.valueOf(9000));
width.setType(STTblWidth.DXA);
// 表头
// ✅ 2. 设置表格宽度
CTTblPr tblPr = table.getCTTbl().getTblPr();
CTTblWidth tblWidth = tblPr.addNewTblW();
tblWidth.setW(BigInteger.valueOf(9000));
tblWidth.setType(STTblWidth.DXA);
// ✅ 3. 强制第一行有 5 列(必须!否则 createRow 后 getCell 越界)
XWPFTableRow header = table.getRow(0);
for (int i = 1; i < 5; i++) {
header.addNewTableCell();
}
// ✅ 4. 设置表头文本和样式(不要提前操作 TcPr)
setCellTextAndStyle(header.getCell(0), "检查项目", true);
header.addNewTableCell();
setCellTextAndStyle(header.getCell(1), "检查要点", true);
header.addNewTableCell();
setCellTextAndStyle(header.getCell(2), "单项分值", true);
header.addNewTableCell();
setCellTextAndStyle(header.getCell(3), "得分", true);
header.addNewTableCell();
setCellTextAndStyle(header.getCell(4), "评分说明", true);
// 按 checkProject 分组,并按项目编号排序
// ✅ 5. 分组数据
Map<String, List<CheckItem>> grouped = items.stream()
.sorted(Comparator.comparing(CheckItem::getCheckProject))
.collect(Collectors.groupingBy(
CheckItem::getCheckProject, // 分组依据
TreeMap::new, // ✅ 用 TreeMap 保持 key 有序
Collectors.toList() // 每组是一个 List
CheckItem::getCheckProject,
TreeMap::new,
Collectors.toList()
));
int totalScore = 0;
for (Map.Entry<String, List<CheckItem>> entry : grouped.entrySet()) {
List<CheckItem> groupItems = entry.getValue();
String project = entry.getKey();
for (int i = 0; i < groupItems.size(); i++) {
CheckItem item = groupItems.get(i);
XWPFTableRow row = table.createRow();
if (i == 0) {
XWPFTableCell cell = row.getCell(0);
cell.getCTTc().addNewTcPr().addNewVMerge().setVal(org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge.RESTART);
setCellTextAndStyle(cell, project, false);
} else {
XWPFTableCell cell = row.getCell(0);
cell.getCTTc().addNewTcPr().addNewVMerge().setVal(org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge.CONTINUE);
cell.setText("");
}
XWPFTableRow row = table.createRow(); // ✅ 自动 5 列
setCellTextAndStyle(row.getCell(1), item.getCheckItem(), false);
// ✅ 先设置文本和样式
setCellTextAndStyle(row.getCell(1), item.formatCheckItem(), false);
setCellTextAndStyle(row.getCell(2), String.valueOf(item.getItemScore()), false);
setCellTextAndStyle(row.getCell(3), String.valueOf(item.getObtainedScore()), false);
setCellTextAndStyle(row.getCell(4), item.formatScoringExplanation(), false);
// ✅ 累加得分
totalScore += item.getObtainedScore();
// ✅ 再设置第0列的合并
XWPFTableCell cell = row.getCell(0);
setCellTextAndStyle(cell, i == 0 ? project : "", i == 0); // 只有第一行显示项目名
// ✅ 最后设置垂直合并
CTTcPr tcPr = cell.getCTTc().addNewTcPr(); // ✅ 这里可以 addNew,因为 setCellTextAndStyle 已完成
CTVMerge merge = tcPr.addNewVMerge();
merge.setVal(i == 0 ? STMerge.RESTART : STMerge.CONTINUE);
}
}
// ✅ 6. 总分行
XWPFTableRow totalRow = table.createRow();
XWPFTableCell labelCell = totalRow.getCell(0);
setCellTextAndStyle(labelCell, "总分", true);
XWPFTableCell scoreCell = totalRow.getCell(1);
setCellTextAndStyle(scoreCell, String.valueOf(totalScore), true);
// ✅ 合并第1~4列(共4列)
CTTcPr tcPr = scoreCell.getCTTc().addNewTcPr();
tcPr.addNewGridSpan().setVal(BigInteger.valueOf(4));
// ✅ 清空后面单元格(可选)
for (int i = 2; i <= 4; i++) {
XWPFTableCell cell = totalRow.getCell(i);
if (cell != null) {
cell.setText("");
}
}
}
private static void setCellTextAndStyle(XWPFTableCell cell, String text, boolean isHeader) {
cell.getCTTc().addNewTcPr().addNewTcW().setW(BigInteger.valueOf(1800));
XWPFParagraph p = cell.getParagraphs().get(0);
p.setSpacingAfter(50);
XWPFRun r = p.createRun();
r.setText(text);
r.setFontFamily("宋体");
r.setFontSize(10);
if (isHeader) {
r.setBold(true);
// 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) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 1. 总分
XWPFParagraph totalPara = document.createParagraph();
/* XWPFParagraph totalPara = document.createParagraph();
XWPFRun totalRun = totalPara.createRun();
totalRun.setBold(false);
totalRun.setText("总分:" + reportData.getTotalScore());
totalRun.setFontFamily("宋体");
totalRun.setFontSize(12);
totalRun.setFontSize(12);*/
// 2. 备注
XWPFParagraph remarksPara = document.createParagraph();
@ -236,12 +275,9 @@ public class WordReportExporter {
report.setReportTitle("数据购买监理服务现场技术核查评分表");
report.setSiteName("临安区龙岗镇站");
// report.setDistrict("西湖区");
// report.setStreet("龙翔桥");
report.setOperationUnit("国研信息");
report.setCheckTime(LocalDateTime.now());
report.setCheckPersonnel("张三、李四");
//report.setTotalScore(95);
report.setRemarks("注1:《环境空气环境空气气态污染物(SO2、NO2、O3、CO)连续自动监测系统技术要求及检测方法HJ654-2013》\n" +
"*标项一包含的项目,标项二不包含。\n" +
"04质量控制效果中相关质控因子有缺项的,分值按比例分摊。\n");
@ -256,6 +292,12 @@ public class WordReportExporter {
"2)物品摆放不整齐;"
));
item1.setDeductionRule("一项不合格扣0.5分,扣完为止(评分规则)");
item1.setCheckItemBasis(List.of(
"1)站房环境脏,有明显灰尘和异味;",
"2)物品摆放不整齐;"
));
CheckItem item2 = new CheckItem();
item2.setCheckProject("01站房环境保障情况(5分)");
@ -264,6 +306,11 @@ public class WordReportExporter {
item2.setObtainedScore(3);
item2.setScoringBasis(List.of("1)仪器运行正常;"));
item2.setDeductionRule("发现异常未处理扣1分");
item2.setCheckItemBasis(List.of(
"动态校准仪质量流量控制器(MFC)单点流量测试(要求相对误差≤±2%):",
"零气MFC流量: L/min,标准流量计测值: L/min,相对误差: %",
"标气MFC流量: ml/min,标准流量计测值: ml/min,相对误差: %"
));
CheckItem item3 = new CheckItem();
item3.setCheckProject("02采样系统维护效果(10分)");
@ -272,19 +319,16 @@ public class WordReportExporter {
item3.setObtainedScore(3);
item3.setScoringBasis(List.of("1)仪器运行正常;"));
item3.setDeductionRule("发现异常未处理扣1分");
item3.setCheckItemBasis(List.of(
"动态校准仪质量流量控制器(MFC)单点流量测试(要求相对误差≤±2%):",
"零气MFC流量: L/min,标准流量计测值: L/min,相对误差: %",
"标气MFC流量: ml/min,标准流量计测值: ml/min,相对误差: %"
));
report.setCheckItems(List.of(item1, item2, item3));
// 🔢 动态计算总分
int totalScore = report.getCheckItems().stream()
.mapToInt(CheckItem::getObtainedScore)
.sum();
int maxScore = report.getCheckItems().stream()
.mapToInt(CheckItem::getItemScore)
.sum();
// 添加到集合
report.setCheckItems(List.of(item1, item2, item3));
// ✅ 设置为格式化字符串(推荐)
report.setTotalScore(totalScore);
// ✅ 默认导出到桌面
WordReportExporter.export(report);

Loading…
Cancel
Save