|
@@ -0,0 +1,387 @@
|
|
|
|
|
+package com.hotent.util.wordexcelutils;/**
|
|
|
|
|
+ * @program: cbjs-mvue-master
|
|
|
|
|
+ * @description:
|
|
|
|
|
+ * @author: zhao yue yue
|
|
|
|
|
+ * @create: 2025-11-27 15:34
|
|
|
|
|
+ */
|
|
|
|
|
+
|
|
|
|
|
+import com.hotent.base.util.StringUtil;
|
|
|
|
|
+import lombok.Data;
|
|
|
|
|
+import lombok.Getter;
|
|
|
|
|
+import org.apache.poi.xwpf.usermodel.*;
|
|
|
|
|
+import java.util.*;
|
|
|
|
|
+import java.io.*;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ *@author: zhao yue yue
|
|
|
|
|
+ *@create: 2025-11-27 15:34
|
|
|
|
|
+ */
|
|
|
|
|
+public class CompleteTemplateProcessor {
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 完整处理模板:替换占位符 + 填充表格数据
|
|
|
|
|
+ */
|
|
|
|
|
+ public static void processTemplateComplete(XWPFDocument document,
|
|
|
|
|
+ String organizationName,
|
|
|
|
|
+ List<TableRowData> rowDataList,
|
|
|
|
|
+ String generateDate) {
|
|
|
|
|
+
|
|
|
|
|
+ // 1. 计算汇总信息
|
|
|
|
|
+ int totalPages = calculateTotalPages(rowDataList);
|
|
|
|
|
+ int totalDocuments = rowDataList.size();
|
|
|
|
|
+
|
|
|
|
|
+ System.out.println("开始处理模板...");
|
|
|
|
|
+ System.out.println("单位名称: " + organizationName);
|
|
|
|
|
+ System.out.println("数据行数: " + totalDocuments);
|
|
|
|
|
+ System.out.println("总页数: " + totalPages);
|
|
|
|
|
+ System.out.println("生成日期: " + generateDate);
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 替换文档中的占位符
|
|
|
|
|
+ //replaceDocumentPlaceholders(document, organizationName, totalDocuments, totalPages, generateDate);
|
|
|
|
|
+ // 替换映射
|
|
|
|
|
+ Map<String, String> replacements = new HashMap<>();
|
|
|
|
|
+ replacements.put("{价格主管部门或成本监审机构}", organizationName);
|
|
|
|
|
+ replacements.put("{材料件数}", String.valueOf(totalDocuments));
|
|
|
|
|
+ replacements.put("{材料页数}", String.valueOf(totalPages));
|
|
|
|
|
+ replacements.put("{登记表生成日期}", generateDate);
|
|
|
|
|
+ replacements.put("{序号}", "1");
|
|
|
|
|
+ replacements.put("{资料名称}", rowDataList.get(0).getDocumentName());
|
|
|
|
|
+ replacements.put("{页数}", String.valueOf(rowDataList.get(0).getPageCount()));
|
|
|
|
|
+ replacements.put("{备注}", rowDataList.get(0).getRemark());
|
|
|
|
|
+ SmartTemplateWriter.writeToTemplate(document,replacements);
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 处理表格数据
|
|
|
|
|
+ processTableData(document, rowDataList);
|
|
|
|
|
+
|
|
|
|
|
+ System.out.println("模板处理完成!");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 计算总页数
|
|
|
|
|
+ */
|
|
|
|
|
+ private static int calculateTotalPages(List<TableRowData> rowDataList) {
|
|
|
|
|
+ return rowDataList.stream().mapToInt(TableRowData::getPageCount).sum();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 替换文档中的占位符
|
|
|
|
|
+ */
|
|
|
|
|
+ private static void replaceDocumentPlaceholders(XWPFDocument document,
|
|
|
|
|
+ String organizationName,
|
|
|
|
|
+ int totalDocuments,
|
|
|
|
|
+ int totalPages,
|
|
|
|
|
+ String generateDate) {
|
|
|
|
|
+
|
|
|
|
|
+ // 替换映射
|
|
|
|
|
+ Map<String, String> replacements = new HashMap<>();
|
|
|
|
|
+ replacements.put("{价格主管部门或成本监审机构}", organizationName);
|
|
|
|
|
+ replacements.put("{材料件数}", String.valueOf(totalDocuments));
|
|
|
|
|
+ replacements.put("{材料页数}", String.valueOf(totalPages));
|
|
|
|
|
+ replacements.put("{登记表生成日期}", generateDate);
|
|
|
|
|
+
|
|
|
|
|
+ // 在文档中查找并替换
|
|
|
|
|
+ for (Map.Entry<String, String> entry : replacements.entrySet()) {
|
|
|
|
|
+ boolean found = replaceTextInDocument(document, entry.getKey(), entry.getValue());
|
|
|
|
|
+ if (!found) {
|
|
|
|
|
+ System.out.println("警告: 未找到占位符 " + entry.getKey());
|
|
|
|
|
+ } else {
|
|
|
|
|
+ System.out.println("成功替换: " + entry.getKey() + " → " + entry.getValue());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 处理表格数据
|
|
|
|
|
+ */
|
|
|
|
|
+ private static void processTableData(XWPFDocument document, List<TableRowData> rowDataList) {
|
|
|
|
|
+ List<XWPFTable> tables = document.getTables();
|
|
|
|
|
+ if (tables.isEmpty()) {
|
|
|
|
|
+ System.err.println("错误: 未找到表格");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ XWPFTable table = tables.get(0);
|
|
|
|
|
+ System.out.println("找到表格,行数: " + table.getRows().size());
|
|
|
|
|
+
|
|
|
|
|
+ // 找到模板行(包含占位符的行)
|
|
|
|
|
+ //int templateRowIndex = findTemplateRow(table);
|
|
|
|
|
+ /* if (templateRowIndex == -1) {
|
|
|
|
|
+ System.err.println("错误: 未找到包含占位符的模板行");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }*/
|
|
|
|
|
+
|
|
|
|
|
+ System.out.println("模板行索引: " + 1);
|
|
|
|
|
+
|
|
|
|
|
+ // 处理表格数据
|
|
|
|
|
+ processTableRows(table, 1, rowDataList);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 找到包含占位符的模板行
|
|
|
|
|
+ */
|
|
|
|
|
+ private static int findTemplateRow(XWPFTable table) {
|
|
|
|
|
+ for (int i = 0; i < table.getRows().size(); i++) {
|
|
|
|
|
+ XWPFTableRow row = table.getRows().get(i);
|
|
|
|
|
+ if (containsTablePlaceholders(row)) {
|
|
|
|
|
+ return i;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return -1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 检查行是否包含表格占位符
|
|
|
|
|
+ */
|
|
|
|
|
+ private static boolean containsTablePlaceholders(XWPFTableRow row) {
|
|
|
|
|
+ String[] placeholders = {"{序号}", "{资料名称}", "{页数}", "{备注}"};
|
|
|
|
|
+ String rowText = getRowText(row);
|
|
|
|
|
+
|
|
|
|
|
+ if (rowText == null) return false;
|
|
|
|
|
+
|
|
|
|
|
+ for (String placeholder : placeholders) {
|
|
|
|
|
+ if (rowText.contains(placeholder)) {
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 处理表格行数据
|
|
|
|
|
+ */
|
|
|
|
|
+ private static void processTableRows(XWPFTable table, int templateRowIndex, List<TableRowData> rowDataList) {
|
|
|
|
|
+ XWPFTableRow templateRow = table.getRows().get(templateRowIndex);
|
|
|
|
|
+
|
|
|
|
|
+ // 1. 首先替换模板行(第一行数据)
|
|
|
|
|
+ /* if (rowDataList.size() > 0) {
|
|
|
|
|
+ replaceTemplateRow(templateRow, rowDataList.get(0), 1);
|
|
|
|
|
+ System.out.println("替换模板行完成");
|
|
|
|
|
+ }*/
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 添加额外的数据行
|
|
|
|
|
+ for (int i = 1; i < rowDataList.size(); i++) {
|
|
|
|
|
+ addDataRow(table, templateRow, rowDataList.get(i), i + 1, templateRowIndex + i);
|
|
|
|
|
+ System.out.println("添加数据行: " + (i + 1));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 清理多余的空行(保留汇总行)
|
|
|
|
|
+ cleanupEmptyRows(table, templateRowIndex + rowDataList.size());
|
|
|
|
|
+
|
|
|
|
|
+ System.out.println("表格处理完成,最终行数: " + table.getRows().size());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 替换模板行
|
|
|
|
|
+ */
|
|
|
|
|
+ private static void replaceTemplateRow(XWPFTableRow row, TableRowData data, int sequence) {
|
|
|
|
|
+ List<XWPFTableCell> cells = row.getTableCells();
|
|
|
|
|
+ if (cells.size() < 4) {
|
|
|
|
|
+ System.err.println("错误: 表格列数不足");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 替换占位符
|
|
|
|
|
+ //replaceInCell(cells.get(0), "[{序号}]", String.valueOf(sequence));
|
|
|
|
|
+ // replaceInCell(cells.get(1), "[{资料名称}]", data.getDocumentName());
|
|
|
|
|
+ // replaceInCell(cells.get(2), "[{页数}]", data.getPageCount() > 0 ? String.valueOf(data.getPageCount()) : "");
|
|
|
|
|
+ // replaceInCell(cells.get(3), "[{备注}]", data.getRemark() != null ? data.getRemark() : "");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 添加数据行
|
|
|
|
|
+ */
|
|
|
|
|
+ private static void addDataRow(XWPFTable table, XWPFTableRow templateRow, TableRowData data, int sequence, int insertIndex) {
|
|
|
|
|
+ // 创建新行
|
|
|
|
|
+ XWPFTableRow newRow = table.insertNewTableRow(insertIndex);
|
|
|
|
|
+
|
|
|
|
|
+ // 复制模板行的单元格结构
|
|
|
|
|
+ for (int i = 0; i < templateRow.getTableCells().size(); i++) {
|
|
|
|
|
+ XWPFTableCell newCell = newRow.addNewTableCell();
|
|
|
|
|
+ // 复制单元格样式(简化)
|
|
|
|
|
+ copyCellStyle(templateRow.getTableCells().get(i), newCell);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 填充数据
|
|
|
|
|
+ List<XWPFTableCell> cells = newRow.getTableCells();
|
|
|
|
|
+ setCellText(cells.get(0), String.valueOf(sequence));
|
|
|
|
|
+ setCellText(cells.get(1), data.getDocumentName());
|
|
|
|
|
+ setCellText(cells.get(2), data.getPageCount() > 0 ? String.valueOf(data.getPageCount()) : "");
|
|
|
|
|
+ setCellText(cells.get(3), data.getRemark() != null ? data.getRemark() : "");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 清理多余的空行
|
|
|
|
|
+ */
|
|
|
|
|
+ private static void cleanupEmptyRows(XWPFTable table, int startIndex) {
|
|
|
|
|
+ // 找到汇总行
|
|
|
|
|
+ int summaryRowIndex = findSummaryRow(table);
|
|
|
|
|
+ if (summaryRowIndex == -1) return;
|
|
|
|
|
+
|
|
|
|
|
+ // 清理汇总行之前的空行
|
|
|
|
|
+ for (int i = summaryRowIndex - 1; i >= startIndex; i--) {
|
|
|
|
|
+ if (i < table.getRows().size() && isRowEmpty(table.getRows().get(i))) {
|
|
|
|
|
+ table.removeRow(i);
|
|
|
|
|
+ System.out.println("清理空行: " + i);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 找到汇总行
|
|
|
|
|
+ */
|
|
|
|
|
+ private static int findSummaryRow(XWPFTable table) {
|
|
|
|
|
+ for (int i = 0; i < table.getRows().size(); i++) {
|
|
|
|
|
+ XWPFTableRow row = table.getRows().get(i);
|
|
|
|
|
+ String rowText = getRowText(row);
|
|
|
|
|
+ if (rowText != null && rowText.contains("以上材料共")) {
|
|
|
|
|
+ return i;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return -1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ========== 工具方法 ==========
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 替换文档中的文本
|
|
|
|
|
+ */
|
|
|
|
|
+ private static boolean replaceTextInDocument(XWPFDocument document, String findText, String replaceText) {
|
|
|
|
|
+ boolean found = false;
|
|
|
|
|
+
|
|
|
|
|
+ // 在段落中查找
|
|
|
|
|
+ for (XWPFParagraph paragraph : document.getParagraphs()) {
|
|
|
|
|
+ if (replaceInParagraph(paragraph, findText, replaceText)) {
|
|
|
|
|
+ found = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 在表格中查找
|
|
|
|
|
+ for (XWPFTable table : document.getTables()) {
|
|
|
|
|
+ for (XWPFTableRow row : table.getRows()) {
|
|
|
|
|
+ for (XWPFTableCell cell : row.getTableCells()) {
|
|
|
|
|
+ for (XWPFParagraph paragraph : cell.getParagraphs()) {
|
|
|
|
|
+ if (replaceInParagraph(paragraph, findText, replaceText)) {
|
|
|
|
|
+ found = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return found;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static boolean replaceInParagraph(XWPFParagraph paragraph, String findText, String replaceText) {
|
|
|
|
|
+ boolean found = false;
|
|
|
|
|
+ for (XWPFRun run : paragraph.getRuns()) {
|
|
|
|
|
+ String text = run.getText(0);
|
|
|
|
|
+ if (text != null && text.contains(findText)) {
|
|
|
|
|
+ run.setText(text.replace(findText, replaceText), 0);
|
|
|
|
|
+ found = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return found;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 替换单元格中的文本
|
|
|
|
|
+ */
|
|
|
|
|
+ private static void replaceInCell(XWPFTableCell cell, String findText, String replaceText) {
|
|
|
|
|
+ for (XWPFParagraph paragraph : cell.getParagraphs()) {
|
|
|
|
|
+ for (XWPFRun run : paragraph.getRuns()) {
|
|
|
|
|
+ String text = run.getText(0);
|
|
|
|
|
+ if (text != null && text.contains(findText)) {
|
|
|
|
|
+ run.setText(text.replace(findText, replaceText), 0);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 设置单元格文本
|
|
|
|
|
+ */
|
|
|
|
|
+ private static void setCellText(XWPFTableCell cell, String text) {
|
|
|
|
|
+ // 清除现有内容
|
|
|
|
|
+ for (int i = cell.getParagraphs().size() - 1; i >= 0; i--) {
|
|
|
|
|
+ cell.removeParagraph(i);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 添加新内容
|
|
|
|
|
+ XWPFParagraph paragraph = cell.addParagraph();
|
|
|
|
|
+ XWPFRun run = paragraph.createRun();
|
|
|
|
|
+ run.setText(text);
|
|
|
|
|
+ paragraph.setAlignment(ParagraphAlignment.CENTER);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 复制单元格样式
|
|
|
|
|
+ */
|
|
|
|
|
+ private static void copyCellStyle(XWPFTableCell source, XWPFTableCell target) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ target.setColor(source.getColor());
|
|
|
|
|
+ if (StringUtil.isNotEmpty(String.valueOf(source.getWidth()) )) {
|
|
|
|
|
+ target.setWidth(String.valueOf(source.getWidth()));
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ // 忽略样式复制错误
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取行文本
|
|
|
|
|
+ */
|
|
|
|
|
+ private static String getRowText(XWPFTableRow row) {
|
|
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
|
|
+ for (XWPFTableCell cell : row.getTableCells()) {
|
|
|
|
|
+ String cellText = getCellText(cell);
|
|
|
|
|
+ if (cellText != null) {
|
|
|
|
|
+ sb.append(cellText);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return sb.toString();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取单元格文本
|
|
|
|
|
+ */
|
|
|
|
|
+ private static String getCellText(XWPFTableCell cell) {
|
|
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
|
|
+ for (XWPFParagraph paragraph : cell.getParagraphs()) {
|
|
|
|
|
+ if (paragraph.getText() != null) {
|
|
|
|
|
+ sb.append(paragraph.getText());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return sb.toString();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 检查行是否为空
|
|
|
|
|
+ */
|
|
|
|
|
+ private static boolean isRowEmpty(XWPFTableRow row) {
|
|
|
|
|
+ for (XWPFTableCell cell : row.getTableCells()) {
|
|
|
|
|
+ String text = getCellText(cell);
|
|
|
|
|
+ if (text != null && !text.trim().isEmpty()) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 表格行数据类
|
|
|
|
|
+ */
|
|
|
|
|
+ @Getter
|
|
|
|
|
+ @Data
|
|
|
|
|
+ public static class TableRowData {
|
|
|
|
|
+ private String documentName;
|
|
|
|
|
+ private int pageCount;
|
|
|
|
|
+ private String remark;
|
|
|
|
|
+
|
|
|
|
|
+ public TableRowData() {
|
|
|
|
|
+ this.documentName = documentName;
|
|
|
|
|
+ this.pageCount = pageCount;
|
|
|
|
|
+ this.remark = remark;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+}
|