9.9 KiB
9.9 KiB
Word合同模板配置说明
📋 问题原因
Word模板中的动态列表数据渲染失败,主要原因是:
- 没有配置
LoopRowTableRenderPolicy循环行渲染策略 - 数据格式不匹配(直接传JSONArray而不是List)
- 模板语法使用不正确
✅ 解决方案
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:使用绝对路径
/Users/mima0000/Desktop/合同.docx
3. 后端代码关键点
3.1 配置循环行策略
LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
Configure config = Configure.builder()
.bind("rows", policy) // 绑定rows标签
.build();
3.2 数据格式转换
// 错误方式 ❌
dataMap.put("rows", JSONArray.parseArray(materialJson));
// 正确方式 ✅
List<Map<String, Object>> rowsList = new ArrayList<>();
for (int i = 0; i < materialArray.size(); i++) {
JSONObject item = materialArray.getJSONObject(i);
Map<String, Object> rowData = new HashMap<>();
rowData.put("seq", i + 1);
rowData.put("materialName", item.getString("materialName"));
// ... 其他字段
rowsList.add(rowData);
}
dataMap.put("rows", rowsList);
3.3 使用配置渲染
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
解决:在代码中添加配置
Configure config = Configure.builder()
.bind("rows", policy)
.build();
2. 列表只显示一行
原因:模板中 {{#rows}} 和 {{/rows}} 没有单独占一行
解决:确保循环标签单独占表格的一行
3. 数据格式错误
原因:直接传JSONArray而不是List 解决:转换为List<Map<String, Object>>格式
4. 中文乱码
原因:文件名编码问题 解决:使用URLEncoder编码文件名
String fileName = java.net.URLEncoder.encode("合同.docx", "UTF-8");
🎉 测试步骤
-
准备模板文件
- 按照上述格式创建Word模板
- 保存为
contract_template.docx - 放到
src/main/resources/templates/目录
-
测试导出
- 访问:
GET /flow/contract/export?contractId=1 - 检查下载的Word文档
- 验证列表数据是否正确显示
- 访问:
-
验证数据
- 打开导出的Word文档
- 检查产品明细表是否有多行数据
- 检查所有字段是否正确填充
💡 最佳实践
-
模板管理
- 将模板文件放在resources目录
- 使用版本控制管理模板
- 为不同类型合同创建不同模板
-
错误处理
- 添加try-catch捕获异常
- 记录详细的错误日志
- 返回友好的错误提示
-
性能优化
- 缓存模板对象
- 使用流式处理大文件
- 异步处理导出任务
📚 参考资料
- 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