# 合同起草功能实施方案 ## 功能概述 基于合同模板创建新合同,通过占位符替换和在线编辑实现合同起草。 --- ## 方案对比 ### 方案A:docxtemplater + 服务端生成 **技术栈**: - `docxtemplater` - DOCX 模板引擎 - 服务端处理(Node.js) - 生成新文档后上传到 MinIO **工作流程**: 1. 模板中预先定义占位符:`{甲方名称}`、`{合同金额}` 2. 用户填写表单 3. 服务端使用 docxtemplater 替换占位符 4. 生成新 DOCX 文件 5. 上传到 MinIO 6. 前端加载新文件用 Collabora 展示和编辑 **优点**: - ✅ 模板语法强大(支持循环、条件、表格等) - ✅ 替换精确可控 - ✅ 可以在服务端验证数据 - ✅ 支持复杂的模板逻辑 **缺点**: - ❌ 需要安装新依赖 - ❌ 需要开发服务端 API - ❌ 模板需要预先制作占位符 - ❌ 生成→上传→加载流程较长 - ❌ 无法实时预览替换效果 --- ### 方案B:CollaboraViewer 实时替换(推荐) **技术栈**: - 现有的 `CollaboraViewer` 组件 - `replaceTextInPage` / `unoReplaceAll` 功能 - Collabora Online 编辑模式 **工作流程**: 1. 模板中约定占位符格式:`{{甲方名称}}`、`{{合同金额}}` 2. 用户点击"起草合同",复制模板文件 3. 在 Collabora 中以编辑模式打开复制的文件 4. 用户填写右侧表单 5. 实时调用 `replaceTextInPage` 或 `unoReplaceAll` 替换占位符 6. 用户可以继续手动编辑文档 7. 保存生成的新合同 **优点**: - ✅ 无需新依赖,复用现有代码 - ✅ 实时预览替换效果 - ✅ 用户可以继续手动编辑 - ✅ 开发速度快 - ✅ 所见即所得 **缺点**: - ❌ 依赖 Collabora Online 服务 - ❌ 替换逻辑相对简单(基于查找替换) - ❌ 需要处理异步替换 --- ### 方案C:混合方案 **技术栈**: - 简单模板:使用 CollaboraViewer 实时替换 - 复杂模板:使用 docxtemplater 服务端生成 **工作流程**: 1. 模板元数据标记是否为"复杂模板" 2. 简单模板走方案B流程 3. 复杂模板走方案A流程 **优点**: - ✅ 灵活性最高 - ✅ 简单场景快速响应 - ✅ 复杂场景功能完整 **缺点**: - ❌ 开发成本高 - ❌ 维护两套逻辑 --- ## 推荐方案:方案B(CollaboraViewer 实时替换) ### 理由 1. **快速落地**:复用现有代码,开发周期短 2. **用户体验好**:所见即所得,实时预览 3. **灵活性高**:替换后用户可以继续编辑 4. **维护成本低**:不引入新依赖 ### 技术细节 #### 占位符约定 使用双花括号格式,便于识别和替换: ``` {{甲方名称}} {{甲方地址}} {{甲方法定代表人}} {{甲方联系电话}} {{乙方名称}} {{乙方地址}} {{乙方法定代表人}} {{乙方联系电话}} {{合同金额}} {{签订日期}} {{合同编号}} ``` #### 替换实现 使用 CollaboraViewer 的 `unoReplaceAll` 方法: ```typescript // 批量替换所有占位符 async function replacePlaceholders( collaboraRef: CollaboraViewerHandle, values: Record ) { for (const [key, value] of Object.entries(values)) { const placeholder = `{{${key}}}`; await collaboraRef.unoCommands.replaceAll(placeholder, value); } } ``` #### 文件复制策略 **选项1:服务端复制**(推荐) - API: `POST /api/contracts/draft` - 请求参数: `{ templateId: string }` - 返回: `{ newFileId: string, newFilePath: string }` - 服务端将模板文件复制为新文件 **选项2:客户端复制** - 下载模板文件 Blob - 重命名后重新上传 - 成本高,不推荐 --- ## 数据库设计 ### 新增表:`drafted_contracts` (起草的合同) ```sql CREATE TABLE drafted_contracts ( id SERIAL PRIMARY KEY, template_id INTEGER REFERENCES contract_templates(id), file_path TEXT NOT NULL, -- 起草后的文件路径 title TEXT NOT NULL, -- 合同标题 placeholder_values JSONB, -- 占位符填充值 status TEXT DEFAULT 'draft', -- draft | completed | archived created_by INTEGER REFERENCES auth.users(id), created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); ``` --- ## API 设计 ### 1. 创建起草合同 ``` POST /api/contracts/draft ``` **请求体**: ```json { "templateId": 123, "title": "智慧法务平台采购合同-草稿" } ``` **响应**: ```json { "id": 456, "filePath": "drafts/contract_456_20250104.docx", "title": "智慧法务平台采购合同-草稿", "templateId": 123 } ``` ### 2. 保存占位符值 ``` PUT /api/contracts/draft/:id/placeholders ``` **请求体**: ```json { "placeholders": { "甲方名称": "广东省烟草专卖局", "合同金额": "500000" } } ``` ### 3. 完成起草 ``` POST /api/contracts/draft/:id/complete ``` 将草稿标记为已完成,可选地移动到正式合同目录。 --- ## UI/UX 设计 ### 页面结构 ``` /contract-template/draft/:templateId ├── 左侧(60%):FilePreview(Collabora 编辑模式) └── 右侧(40%):占位符表单 ├── 基本信息 │ ├── 合同标题 │ └── 合同编号 ├── 甲方信息 │ ├── 甲方名称 │ ├── 甲方地址 │ ├── 法定代表人 │ └── 联系电话 ├── 乙方信息 │ ├── 乙方名称 │ ├── 乙方地址 │ ├── 法定代表人 │ └── 联系电话 ├── 合同条款 │ ├── 合同金额 │ ├── 签订日期 │ └── 其他自定义字段 └── 操作按钮 ├── 一键替换 ├── 保存草稿 └── 完成起草 ``` ### 交互流程 1. 用户在模板详情页点击"起草合同" 2. 系统复制模板文件,创建草稿记录 3. 跳转到起草页面 4. 左侧加载文档(Collabora 编辑模式) 5. 右侧显示占位符表单(从模板中提取或预定义) 6. 用户填写表单 7. 点击"一键替换"批量替换所有占位符 8. 用户可以继续手动编辑文档 9. 点击"保存草稿"保存当前状态 10. 点击"完成起草"将合同标记为完成 --- ## 占位符提取方案 ### 方案1:预定义字段(推荐) 在模板表中增加字段: ```sql ALTER TABLE contract_templates ADD COLUMN placeholder_schema JSONB; ``` 示例数据: ```json { "fields": [ { "key": "甲方名称", "label": "甲方名称", "type": "text", "required": true, "group": "甲方信息" }, { "key": "合同金额", "label": "合同金额(元)", "type": "number", "required": true, "group": "合同条款" }, { "key": "签订日期", "label": "签订日期", "type": "date", "required": true, "group": "合同条款" } ] } ``` ### 方案2:动态提取 从 DOCX 文件中提取所有 `{{xxx}}` 占位符: - 需要解析 DOCX 文件 - 服务端实现 - 成本较高 --- ## 技术风险与应对 ### 风险1:Collabora 替换性能 **风险**:多个占位符替换可能耗时较长 **应对**: - 使用 `unoReplaceAll` 而不是逐个替换 - 显示替换进度提示 - 考虑批量替换 API ### 风险2:并发编辑冲突 **风险**:用户同时编辑和替换可能冲突 **应对**: - 替换前提示用户 - 替换时禁用编辑 - 使用防抖避免频繁替换 ### 风险3:占位符未完全替换 **风险**:用户忘记填写某些字段 **应对**: - 完成前检查是否还有 `{{}}` 占位符 - 高亮显示未替换的占位符 - 必填字段验证 --- ## 扩展功能(可选) 1. **模板变量管理**:管理员可以配置模板的占位符字段 2. **历史记录**:记录每次替换的历史 3. **模板版本**:支持模板版本管理 4. **审批流程**:起草完成后进入审批流程 5. **电子签名**:集成电子签名功能 --- ## 总结 推荐使用 **方案B(CollaboraViewer 实时替换)**,理由: - 开发成本低,复用现有代码 - 用户体验好,所见即所得 - 灵活性高,支持后续手动编辑 - 维护成本低,不引入新依赖 如果未来需要支持复杂模板逻辑(循环、条件等),可以再引入 docxtemplater 作为补充。