# Word合同模板配置说明 ## 📋 问题原因 Word模板中的动态列表数据渲染失败,主要原因是: 1. 没有配置 `LoopRowTableRenderPolicy` 循环行渲染策略 2. 数据格式不匹配(直接传JSONArray而不是List) 3. 模板语法使用不正确 ## ✅ 解决方案 ### 1. Word模板正确语法 在Word模板的表格中,需要使用以下格式: ``` ┌─────────┬──────────┬──────────┬──────┬──────┬──────────┬──────────┬──────┐ │ 序号 │ 产品名称 │ 型号 │ 数量 │ 单位 │ 单价(元) │ 总价(元) │ 备注 │ ├─────────┼──────────┼──────────┼──────┼──────┼──────────┼──────────┼──────┤ │{{#rows}}│ │ │ │ │ │ │ │ ├─────────┼──────────┼──────────┼──────┼──────┼──────────┼──────────┼──────┤ │{{seq}} │{{materialName}}│{{materialSpec}}│{{qty}}│{{unitName}}│{{salePrice}}│{{amount}}│{{remark}}│ ├─────────┼──────────┼──────────┼──────┼──────┼──────────┼──────────┼──────┤ │{{/rows}}│ │ │ │ │ │ │ │ └─────────┴──────────┴──────────┴──────┴──────┴──────────┴──────────┴──────┘ ``` **重要说明**: - `{{#rows}}` 必须单独占一行(表格的一行) - 数据行包含所有字段:`{{seq}}`, `{{materialName}}` 等 - `{{/rows}}` 必须单独占一行(表格的一行) - 这三行构成一个完整的循环结构 ### 2. 模板文件位置 将模板文件放在以下位置之一: **方式1:放在resources目录(推荐)** ``` src/main/resources/templates/contract_template.docx ``` **方式2:使用绝对路径** ```java /Users/mima0000/Desktop/合同.docx ``` ### 3. 后端代码关键点 #### 3.1 配置循环行策略 ```java LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy(); Configure config = Configure.builder() .bind("rows", policy) // 绑定rows标签 .build(); ``` #### 3.2 数据格式转换 ```java // 错误方式 ❌ dataMap.put("rows", JSONArray.parseArray(materialJson)); // 正确方式 ✅ List> rowsList = new ArrayList<>(); for (int i = 0; i < materialArray.size(); i++) { JSONObject item = materialArray.getJSONObject(i); Map rowData = new HashMap<>(); rowData.put("seq", i + 1); rowData.put("materialName", item.getString("materialName")); // ... 其他字段 rowsList.add(rowData); } dataMap.put("rows", rowsList); ``` #### 3.3 使用配置渲染 ```java XWPFTemplate template = XWPFTemplate.compile(templateStream, config) .render(dataMap); ``` ## 🎯 完整的Word模板示例 ### 模板结构 ``` 服务合同 需方:{{clientName}} 合同编号:{{contractCode}} 供方:上海诺力智能科技有限公司 签订时间:{{effectiveDate}} 一、产品明细单 ┌─────────┬──────────┬──────────┬──────┬──────┬──────────┬──────────┬──────┐ │ 序号 │ 产品名称 │ 型号 │ 数量 │ 单位 │ 单价(元) │ 总价(元) │ 备注 │ ├─────────┼──────────┼──────────┼──────┼──────┼──────────┼──────────┼──────┤ │{{#rows}}│ │ │ │ │ │ │ │ ├─────────┼──────────┼──────────┼──────┼──────┼──────────┼──────────┼──────┤ │{{seq}} │{{materialName}}│{{materialSpec}}│{{qty}}│{{unitName}}│{{salePrice}}│{{amount}}│{{remark}}│ ├─────────┼──────────┼──────────┼──────┼──────┼──────────┼──────────┼──────┤ │{{/rows}}│ │ │ │ │ │ │ │ ├─────────┴──────────┴──────────┴──────┴──────┴──────────┼──────────┼──────┤ │ 共计: │{{totalPrice}}│ │ └──────────────────────────────────────────────┴──────────┴──────┘ 二、质量要求:{{qc}} 三、交货时间、地点:货期:{{delivery}},交货地:{{place}} 四、运输方式:{{transport}} 五、包装标准:{{packaging}} 六、结算方式:{{pay}},付款方式:{{payment}} 七、违约责任:{{breach}} 八、解决合同纠纷的方式:{{solve_dispute}} 九、其它约定事项:{{supplement}} ┌──────────────────────────────┬──────────────────────────────┐ │ 需方: │ 供方: │ ├──────────────────────────────┼──────────────────────────────┤ │单位名称:{{clientName}} │单位名称:上海诺力智能科技有限公司│ │地址:{{clientAdd}} │地址:上海青浦区徐泾镇... │ │委托代理人:{{juridicalPerson}}│委托代理人: │ │电话:{{clientTel}} │电话: │ │传真:{{clientFax}} │传真: │ │开户银行:{{clientBank}} │开户银行:招商银行虹桥支行 │ │帐号:{{clientCard}} │帐号:12191702501091 │ └──────────────────────────────┴──────────────────────────────┘ ``` ## 📝 字段说明 ### 基本信息字段 | 字段名 | 说明 | 示例 | |--------|------|------| | clientName | 客户名称 | XX公司 | | contractCode | 合同编号 | HT20250101 | | effectiveDate | 生效日期 | 2025年01月01日 | | totalPrice | 总价 | 100000.00 | ### 列表字段(rows) | 字段名 | 说明 | 示例 | |--------|------|------| | seq | 序号 | 1, 2, 3... | | materialName | 产品名称 | 叉车 | | materialSpec | 型号 | CPD15 | | qty | 数量 | 10 | | unitName | 单位 | 台 | | salePrice | 单价 | 50000.00 | | amount | 总价 | 500000.00 | | remark | 备注 | 含税 | ### 合同条款字段 | 字段名 | 说明 | |--------|------| | qc | 质量要求 | | delivery | 交货时间 | | place | 交货地点 | | transport | 运输方式 | | packaging | 包装标准 | | pay | 结算方式 | | payment | 付款方式 | | breach | 违约责任 | | solve_dispute | 解决纠纷方式 | | supplement | 其它约定 | ### 客户信息字段 | 字段名 | 说明 | |--------|------| | clientAdd | 客户地址 | | juridicalPerson | 法人代表 | | clientTel | 客户电话 | | clientFax | 客户传真 | | clientBank | 客户开户行 | | clientCard | 客户账号 | ## 🔧 常见问题 ### 1. 列表数据不显示 **原因**:没有配置 `LoopRowTableRenderPolicy` **解决**:在代码中添加配置 ```java Configure config = Configure.builder() .bind("rows", policy) .build(); ``` ### 2. 列表只显示一行 **原因**:模板中 `{{#rows}}` 和 `{{/rows}}` 没有单独占一行 **解决**:确保循环标签单独占表格的一行 ### 3. 数据格式错误 **原因**:直接传JSONArray而不是List **解决**:转换为List>格式 ### 4. 中文乱码 **原因**:文件名编码问题 **解决**:使用URLEncoder编码文件名 ```java String fileName = java.net.URLEncoder.encode("合同.docx", "UTF-8"); ``` ## 🎉 测试步骤 1. **准备模板文件** - 按照上述格式创建Word模板 - 保存为 `contract_template.docx` - 放到 `src/main/resources/templates/` 目录 2. **测试导出** - 访问:`GET /flow/contract/export?contractId=1` - 检查下载的Word文档 - 验证列表数据是否正确显示 3. **验证数据** - 打开导出的Word文档 - 检查产品明细表是否有多行数据 - 检查所有字段是否正确填充 ## 💡 最佳实践 1. **模板管理** - 将模板文件放在resources目录 - 使用版本控制管理模板 - 为不同类型合同创建不同模板 2. **错误处理** - 添加try-catch捕获异常 - 记录详细的错误日志 - 返回友好的错误提示 3. **性能优化** - 缓存模板对象 - 使用流式处理大文件 - 异步处理导出任务 ## 📚 参考资料 - poi-tl官方文档:http://deepoove.com/poi-tl/ - 循环行表格:http://deepoove.com/poi-tl/#_loop-row-table - GitHub示例:https://github.com/Sayi/poi-tl --- **更新时间**: 2026-01-30 **版本**: v1.0