# 占位符提取功能修复总结 ## 问题描述 在测试占位符提取功能时,发现使用 `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. 测试一键替换功能