144 lines
4.1 KiB
Markdown
144 lines
4.1 KiB
Markdown
# 占位符提取功能修复总结
|
|
|
|
## 问题描述
|
|
|
|
在测试占位符提取功能时,发现使用 `docxtemplater` 库的 `getFullText()` 方法会因为 Word 文档中的格式化(粗体、斜体等)导致占位符标签被分割成多个 XML 元素,从而引发编译错误。
|
|
|
|
### 原始错误
|
|
|
|
```
|
|
Error: Duplicate open tag, expected one open tag
|
|
Error: Duplicate close tag, expected one close tag
|
|
```
|
|
|
|
这是因为 Word 文档中的 `{{占位符}}` 可能被格式化标签分割,例如:
|
|
- `{{地市名称}}` 可能在 XML 中被分割为 `{{地市` 和 `名称}}`
|
|
- 这导致 docxtemplater 在编译时检测到重复的开/闭标签
|
|
|
|
## 解决方案
|
|
|
|
### 核心思路
|
|
|
|
不使用 `docxtemplater` 进行编译,而是直接读取 `word/document.xml` 文件,移除 XML 标签后提取占位符。
|
|
|
|
### 实现步骤
|
|
|
|
1. **使用 PizZip 解压 docx 文件**
|
|
```typescript
|
|
const zip = new PizZip(content);
|
|
```
|
|
|
|
2. **直接读取 document.xml**
|
|
```typescript
|
|
const documentXml = zip.file('word/document.xml');
|
|
const xmlContent = documentXml.asText();
|
|
```
|
|
|
|
3. **移除所有 XML 标签,只保留纯文本**
|
|
```typescript
|
|
const fullText = xmlContent.replace(/<[^>]+>/g, '');
|
|
```
|
|
|
|
4. **使用正则表达式提取占位符**
|
|
```typescript
|
|
const placeholderRegex = /\{\{([^}]+)\}\}/g;
|
|
const matches = fullText.matchAll(placeholderRegex);
|
|
```
|
|
|
|
### 优势
|
|
|
|
✅ **避免格式化问题** - 不受 Word 文档格式化的影响
|
|
✅ **更简单** - 不需要创建 docxtemplater 实例
|
|
✅ **更快** - 跳过编译步骤,直接提取
|
|
✅ **更可靠** - 适用于任何格式化的 docx 文件
|
|
|
|
## 修改的文件
|
|
|
|
### 1. `app/api/contracts/docx-parser.server.ts`
|
|
|
|
**修改前**:使用 docxtemplater.getFullText()
|
|
```typescript
|
|
const doc = new Docxtemplater(zip, {
|
|
paragraphLoop: true,
|
|
linebreaks: true,
|
|
});
|
|
const fullText = doc.getFullText();
|
|
```
|
|
|
|
**修改后**:直接读取 XML
|
|
```typescript
|
|
const documentXml = zip.file('word/document.xml');
|
|
const xmlContent = documentXml.asText();
|
|
const fullText = xmlContent.replace(/<[^>]+>/g, '');
|
|
```
|
|
|
|
### 2. `scripts/test-docx-parser.cjs`
|
|
|
|
- 重命名:`test-docx-parser.js` → `test-docx-parser.cjs`(适配 ES 模块项目)
|
|
- 移除了 `docxtemplater` 依赖
|
|
- 使用相同的 XML 读取方法
|
|
|
|
### 3. `docs/docxtemplater-placeholder-extraction.md`
|
|
|
|
更新文档以反映新的实现方式:
|
|
- 技术栈:移除 docxtemplater,强调使用 pizzip + 正则表达式
|
|
- 工作流程:更新为直接读取 XML 的流程
|
|
- 代码示例:更新为新的实现
|
|
|
|
## 测试结果
|
|
|
|
运行 `node scripts/test-docx-parser.cjs` 成功提取了 18 个占位符:
|
|
|
|
```
|
|
✅ 文件读取成功, 大小: 18.95 KB
|
|
✅ PizZip 解压成功
|
|
✅ document.xml 读取成功
|
|
✅ XML 内容长度: 33378
|
|
✅ 提取纯文本成功
|
|
文本长度: 5047
|
|
|
|
找到 18 个占位符:
|
|
1. {{地市名称}}
|
|
2. {{合同名称}}
|
|
3. {{合同编号}}
|
|
4. {{项目编号}}
|
|
5. {{市名}}
|
|
6. {{区名}}
|
|
7. {{年}}
|
|
8. {{月}}
|
|
9. {{日}}
|
|
10. {{有效期限}}
|
|
11. {{甲方名称}}
|
|
12. {{甲方注册地址}}
|
|
13. {{甲方法定代表人}}
|
|
14. {{甲方联系电话}}
|
|
15. {{乙方名称}}
|
|
16. {{乙方注册地址}}
|
|
17. {{乙方法定代表人}}
|
|
18. {{乙方联系电话}}
|
|
```
|
|
|
|
### 智能分类
|
|
|
|
系统自动推测了字段类型和分组:
|
|
|
|
- **基本信息**:地市名称、合同名称、合同编号等
|
|
- **甲方信息**:甲方名称、甲方注册地址(textarea)、甲方法定代表人、甲方联系电话
|
|
- **乙方信息**:乙方名称、乙方注册地址(textarea)、乙方法定代表人、乙方联系电话
|
|
|
|
## 类型检查
|
|
|
|
运行 `npm run typecheck` 确认没有引入新的类型错误。所有显示的错误都是代码库中的既有问题,与本次修改无关。
|
|
|
|
## 总结
|
|
|
|
✅ **问题已解决** - 占位符提取功能正常工作
|
|
✅ **测试通过** - 成功从测试文档中提取 18 个占位符
|
|
✅ **类型安全** - 没有引入新的 TypeScript 错误
|
|
✅ **文档更新** - 文档已更新以反映新的实现方式
|
|
|
|
功能已经可以集成到实际应用中使用。下一步可以:
|
|
1. 启动开发服务器测试完整流程
|
|
2. 验证占位符表单渲染
|
|
3. 测试一键替换功能
|