244 lines
9.9 KiB
Markdown
244 lines
9.9 KiB
Markdown
# Word合同模板配置说明
|
||
|
||
## 📋 问题原因
|
||
|
||
Word模板中的动态列表数据渲染失败,主要原因是:
|
||
1. 没有配置 `LoopRowTableRenderPolicy` 循环行渲染策略
|
||
2. 数据格式不匹配(直接传JSONArray而不是List<Map>)
|
||
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<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 使用配置渲染
|
||
```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<Map>
|
||
**解决**:转换为List<Map<String, Object>>格式
|
||
|
||
### 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
|