data, Configure config) throws IOException {
+ try (InputStream templateStream = getTemplateStream(templatePath)) {
+ return XWPFTemplate.compile(templateStream, config).render(data);
+ }
+ }
+
/**
* 重置响应头,返回输出流
*
@@ -122,6 +137,42 @@ public final class WordTemplateUtil {
return response.getOutputStream();
}
+ /**
+ * 渲染模板并输出到 HttpServletResponse(支持自定义 Configure 配置)
+ *
+ * 适用于需要自定义渲染策略的场景,例如使用 DynamicTableRenderPolicy 动态生成表格。
+ *
+ * @param templatePath classpath 下的模板路径(含文件名),如 "templates/wms_shipping_bill.docx"
+ * @param fileName 下载文件名(不含后缀),如 "发货单_X123",方法内部自动补全 .docx 后缀
+ * @param data 模板数据 Map,业务自行约定占位符字段
+ * @param config poi-tl 配置对象,可包含自定义 RenderPolicy(如 DynamicTableRenderPolicy)
+ * @param response HttpServletResponse,由 Spring MVC 自动注入
+ */
+ public static void renderToResponse(String templatePath, String fileName, Map data,
+ Configure config, HttpServletResponse response) {
+ Objects.requireNonNull(response, "HttpServletResponse must not be null");
+ Objects.requireNonNull(config, "Configure must not be null");
+ if (StringUtils.isBlank(templatePath)) {
+ throw new ServiceException("Word 模板路径不能为空");
+ }
+
+ String safeFileName = StringUtils.isBlank(fileName) ? "export" : fileName.trim();
+ Map safeData = (data == null ? Collections.emptyMap() : data);
+
+ log.info("Word模板导出开始(自定义Configure), templatePath={}, fileName={}, dataKeys={}",
+ templatePath, safeFileName, safeData.keySet());
+
+ try (XWPFTemplate template = buildTemplate(templatePath, safeData, config);
+ OutputStream os = resetResponse(safeFileName, response)) {
+ template.write(os);
+ os.flush();
+ log.info("Word模板导出成功, fileName={}", safeFileName);
+ } catch (IOException e) {
+ log.error("Word 模板导出失败, templatePath={}, fileName={}", templatePath, safeFileName, e);
+ throw new ServiceException("Word 模板导出失败,请联系管理员").setDetailMessage(e.getMessage());
+ }
+ }
+
/**
* 渲染模板并输出到指定 OutputStream(不负责关闭 out)
* 适用于非 HTTP 场景,例如:
diff --git a/ruoyi-modules/ruoyi-wms/src/main/java/org/dromara/wms/controller/WmsShippingBillController.java b/ruoyi-modules/ruoyi-wms/src/main/java/org/dromara/wms/controller/WmsShippingBillController.java
index 2667ae5d..ebc08281 100644
--- a/ruoyi-modules/ruoyi-wms/src/main/java/org/dromara/wms/controller/WmsShippingBillController.java
+++ b/ruoyi-modules/ruoyi-wms/src/main/java/org/dromara/wms/controller/WmsShippingBillController.java
@@ -1,6 +1,7 @@
package org.dromara.wms.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
+import com.deepoove.poi.config.Configure;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
@@ -19,6 +20,7 @@ import org.dromara.common.word.util.WordTemplateUtil;
import org.dromara.wms.domain.bo.WmsShippingBillBo;
import org.dromara.wms.domain.vo.WmsShippingBillVo;
import org.dromara.wms.service.IWmsShippingBillService;
+import org.dromara.wms.word.WmsShippingDetailTablePolicy;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@@ -140,13 +142,17 @@ public class WmsShippingBillController extends BaseController {
public void exportWord(@NotNull(message = "发货单ID不能为空")
@PathVariable("shippingBillId") Long shippingBillId,
HttpServletResponse response) {
- // 组装模板数据
+ // 组装模板数据(包含主表字段和 List 明细列表)
Map data = wmsShippingBillService.buildWordExportData(shippingBillId);
// 生成文件名(发货单号)
WmsShippingBillVo vo = wmsShippingBillService.queryById(shippingBillId);
String fileName = "发货单_" + (vo != null && vo.getShippingCode() != null ? vo.getShippingCode() : shippingBillId);
- // 渲染并输出Word文档(模板路径为classpath根目录)
- WordTemplateUtil.renderToResponse("发货单模板.docx", fileName, data, response);
+ // 配置自定义表格渲染策略,绑定 detailsTable 占位符到 WmsShippingDetailTablePolicy
+ Configure config = Configure.builder()
+ .bind("detailsTable", new WmsShippingDetailTablePolicy())
+ .build();
+ // 渲染并输出Word文档(使用自定义 Configure)
+ WordTemplateUtil.renderToResponse("发货单模板.docx", fileName, data, config, response);
}
}
diff --git a/ruoyi-modules/ruoyi-wms/src/main/java/org/dromara/wms/service/impl/WmsShippingBillServiceImpl.java b/ruoyi-modules/ruoyi-wms/src/main/java/org/dromara/wms/service/impl/WmsShippingBillServiceImpl.java
index 7b7a3dc1..ba57d569 100644
--- a/ruoyi-modules/ruoyi-wms/src/main/java/org/dromara/wms/service/impl/WmsShippingBillServiceImpl.java
+++ b/ruoyi-modules/ruoyi-wms/src/main/java/org/dromara/wms/service/impl/WmsShippingBillServiceImpl.java
@@ -6,6 +6,8 @@ import cn.hutool.core.map.MapUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.deepoove.poi.data.RowRenderData;
+import com.deepoove.poi.data.Rows;
import com.github.yulichang.toolkit.JoinWrappers;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import lombok.RequiredArgsConstructor;
@@ -382,30 +384,27 @@ public class WmsShippingBillServiceImpl implements IWmsShippingBillService {
// 发货方联系电话
data.put("contactNumber", StringUtils.blankToDefault(vo.getContactNumber(), ""));
- // === 明细列表 ===
- // 对应 Word 模板中的 {{#details}}...{{/details}} 循环表格:
- // - seq → {{seq}} (行号)
- // - materialName → {{materialName}}(物料名称)
- // - quantity → {{quantity}} (发货数量)
- // - unit → {{unit}} (计量单位)
- // - remark → {{remark}} (备注)
- // 如需在模板中添加单价、金额、合计行等字段,可在此处扩展 row 中的字段,并在 data 中追加 totalQuantity、totalAmount 等汇总字段。
- List