Files
leaudit-platform-frontend/docs/contract-drafting-implementation-checklist.md
T
2025-12-05 00:09:32 +08:00

549 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 合同起草功能实施清单
## 阶段一:数据库设计(1天)
### 1.1 创建数据库表
- [ ] 创建 `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' CHECK (status IN ('draft', 'completed', 'archived')),
created_by INTEGER REFERENCES auth.users(id),
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_drafted_contracts_template_id ON drafted_contracts(template_id);
CREATE INDEX idx_drafted_contracts_created_by ON drafted_contracts(created_by);
CREATE INDEX idx_drafted_contracts_status ON drafted_contracts(status);
```
### 1.2 扩展模板表
- [ ] 为 `contract_templates` 表添加占位符配置字段
```sql
ALTER TABLE contract_templates
ADD COLUMN placeholder_schema JSONB;
-- 示例数据
UPDATE contract_templates
SET placeholder_schema = '{
"fields": [
{"key": "甲方名称", "label": "甲方名称", "type": "text", "required": true, "group": "甲方信息"},
{"key": "甲方地址", "label": "甲方地址", "type": "text", "required": true, "group": "甲方信息"},
{"key": "甲方法定代表人", "label": "法定代表人", "type": "text", "required": true, "group": "甲方信息"},
{"key": "甲方联系电话", "label": "联系电话", "type": "tel", "required": true, "group": "甲方信息"},
{"key": "乙方名称", "label": "乙方名称", "type": "text", "required": true, "group": "乙方信息"},
{"key": "乙方地址", "label": "乙方地址", "type": "text", "required": true, "group": "乙方信息"},
{"key": "乙方法定代表人", "label": "法定代表人", "type": "text", "required": true, "group": "乙方信息"},
{"key": "乙方联系电话", "label": "联系电话", "type": "tel", "required": true, "group": "乙方信息"},
{"key": "合同金额", "label": "合同金额(元)", "type": "number", "required": true, "group": "合同条款"},
{"key": "签订日期", "label": "签订日期", "type": "date", "required": true, "group": "合同条款"},
{"key": "合同编号", "label": "合同编号", "type": "text", "required": false, "group": "基本信息"}
]
}'::jsonb
WHERE id = 1;
```
---
## 阶段二:后端 API 开发(2-3天)
### 2.1 文件复制服务
- [ ] 创建 `app/api/contracts/draft-service.server.ts`
- [ ] 实现 `copyTemplateFile` 函数
- 从 MinIO 下载模板文件
- 重命名文件(添加时间戳、用户ID)
- 上传到草稿目录
- 返回新文件路径
### 2.2 创建起草合同 API
- [ ] 创建路由 `app/routes/api.contracts.draft.tsx`
- [ ] POST action:创建起草记录
```typescript
export async function action({ request }: ActionFunctionArgs) {
const { templateId, title } = await request.json();
// 1. 获取模板信息
// 2. 复制模板文件
// 3. 创建 drafted_contracts 记录
// 4. 返回新记录 ID 和文件路径
}
```
### 2.3 保存占位符值 API
- [ ] 创建路由 `app/routes/api.contracts.draft.$id.placeholders.tsx`
- [ ] PUT action:更新占位符值
```typescript
export async function action({ request, params }: ActionFunctionArgs) {
const { placeholders } = await request.json();
// 更新 drafted_contracts 表的 placeholder_values 字段
}
```
### 2.4 完成起草 API
- [ ] 创建路由 `app/routes/api.contracts.draft.$id.complete.tsx`
- [ ] POST action:标记为已完成
```typescript
export async function action({ request, params }: ActionFunctionArgs) {
// 1. 检查是否还有未替换的占位符
// 2. 更新状态为 'completed'
// 3. 可选:移动文件到正式合同目录
}
```
### 2.5 查询草稿列表 API
- [ ] 扩展 `app/routes/api.contracts.drafts.tsx`
- [ ] GET loader:查询当前用户的草稿列表
- [ ] 支持分页和筛选
---
## 阶段三:前端组件开发(3-4天)
### 3.1 模板详情页增加按钮
- [ ] 修改 `app/routes/contract-template.detail.$id.tsx`
- [ ] 在操作按钮区域添加"起草合同"按钮
```tsx
<button
className="detail-btn primary bg-primary text-white px-6 py-3 rounded-lg"
onClick={handleStartDraft}
>
<i className="ri-edit-line"></i>
起草合同
</button>
```
- [ ] 实现 `handleStartDraft` 函数
- 调用创建草稿 API
- 导航到起草页面
### 3.2 创建起草页面路由
- [ ] 创建 `app/routes/contract-draft.$draftId.tsx`
- [ ] Loader:加载草稿信息和模板信息
```typescript
export async function loader({ params, request }: LoaderFunctionArgs) {
const draftId = params.draftId!;
// 1. 获取草稿记录
// 2. 获取模板信息(占位符配置)
// 3. 返回数据
return { draft, template };
}
```
### 3.3 创建占位符表单组件
- [ ] 创建 `app/components/contracts/PlaceholderForm.tsx`
- [ ] Props 定义
```typescript
interface PlaceholderFormProps {
schema: PlaceholderSchema;
values: Record<string, string>;
onChange: (values: Record<string, string>) => void;
onBatchReplace: () => void;
onSaveDraft: () => void;
onComplete: () => void;
isReplacing: boolean;
isSaving: boolean;
}
```
- [ ] 渲染表单字段
- 根据 `schema.fields` 动态生成表单
- 按 `group` 分组显示
- 支持不同字段类型(text, number, date, tel
- [ ] 表单验证
- 必填字段验证
- 数据格式验证
- [ ] 操作按钮
- "一键替换"按钮
- "保存草稿"按钮
- "完成起草"按钮
### 3.4 创建起草页面布局
- [ ] 实现 `app/routes/contract-draft.$draftId.tsx` 页面组件
- [ ] 布局结构
```tsx
<div className="flex h-screen">
{/* 左侧:文档预览(60% */}
<div className="w-[60%] border-r">
<FilePreview
fileContent={{
path: draft.file_path,
title: draft.title,
// ...
}}
isTemplate={false} // 编辑模式
/>
</div>
{/* 右侧:占位符表单(40%) */}
<div className="w-[40%] overflow-y-auto p-6">
<PlaceholderForm
schema={template.placeholder_schema}
values={draft.placeholder_values}
onChange={handleValuesChange}
onBatchReplace={handleBatchReplace}
onSaveDraft={handleSaveDraft}
onComplete={handleComplete}
isReplacing={isReplacing}
isSaving={isSaving}
/>
</div>
</div>
```
### 3.5 实现替换逻辑
- [ ] 在起草页面实现 `handleBatchReplace` 函数
```typescript
const handleBatchReplace = async () => {
setIsReplacing(true);
try {
// 获取 CollaboraViewer 引用
const collaboraRef = filePreviewRef.current?.collaboraViewerRef;
if (!collaboraRef?.isReady) {
toastService.warning('文档尚未加载完成');
return;
}
// 批量替换所有占位符
for (const [key, value] of Object.entries(placeholderValues)) {
if (value) { // 只替换有值的字段
const placeholder = `{{${key}}}`;
await collaboraRef.unoCommands.replaceAll(placeholder, value);
}
}
toastService.success('占位符替换完成');
} catch (error) {
console.error('替换失败:', error);
toastService.error('替换失败');
} finally {
setIsReplacing(false);
}
};
```
- [ ] 实现 `handleSaveDraft` 函数
```typescript
const handleSaveDraft = async () => {
setIsSaving(true);
try {
await fetch(`/api/contracts/draft/${draftId}/placeholders`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ placeholders: placeholderValues })
});
toastService.success('草稿已保存');
} catch (error) {
toastService.error('保存失败');
} finally {
setIsSaving(false);
}
};
```
- [ ] 实现 `handleComplete` 函数
```typescript
const handleComplete = async () => {
// 1. 检查必填字段
const missingFields = schema.fields
.filter(f => f.required && !placeholderValues[f.key])
.map(f => f.label);
if (missingFields.length > 0) {
toastService.error(`请填写必填字段:${missingFields.join('、')}`);
return;
}
// 2. 检查是否还有未替换的占位符
// (可选:调用 API 检查文档内容)
// 3. 标记为完成
await fetch(`/api/contracts/draft/${draftId}/complete`, {
method: 'POST'
});
toastService.success('起草完成');
navigate('/contracts/drafts'); // 跳转到草稿列表
};
```
### 3.6 FilePreview 组件增强
- [ ] 修改 `app/components/reviews/FilePreview.tsx`
- [ ] 导出 `collaboraViewerRef` 引用
```typescript
export interface FilePreviewHandle {
collaboraViewerRef: RefObject<CollaboraViewerHandle>;
}
export const FilePreview = forwardRef<FilePreviewHandle, FilePreviewProps>(
function FilePreview(props, ref) {
const collaboraViewerRef = useRef<CollaboraViewerHandle>(null);
useImperativeHandle(ref, () => ({
collaboraViewerRef
}));
// ...
}
);
```
---
## 阶段四:CollaboraViewer 替换功能完善(1-2天)
### 4.1 检查现有替换功能
- [ ] 查看 `app/components/collabora/lib/` 中的替换相关方法
- `unoReplaceAll` - 全局替换
- `unoReplaceCurrent` - 替换当前匹配
- `replaceTextInPage` - 页面定点替换
### 4.2 增强 replaceAll 方法
- [ ] 修改 `app/components/collabora/hooks.ts`
- [ ] 确保 `replaceAll` 方法可用
```typescript
const replaceAll = useCallback(async (searchText: string, replaceText: string) => {
if (!iframeRef.current?.contentWindow) {
throw new Error('iframe 不可用');
}
await unoReplaceAll(iframeRef.current.contentWindow, searchText, replaceText);
}, [iframeRef]);
return useMemo(
() => ({
scrollToTop,
replaceAll // 确保导出
}),
[scrollToTop, replaceAll]
);
```
### 4.3 添加批量替换方法
- [ ] 在 `CollaboraViewer.tsx` 中添加批量替换方法
```typescript
useImperativeHandle(ref, () => ({
isReady: documentReady,
unoCommands: {
scrollToTop,
replaceAll
},
// 新增批量替换方法
batchReplace: async (replacements: Array<{ search: string; replace: string }>) => {
for (const { search, replace } of replacements) {
await replaceAll(search, replace);
// 添加延迟避免 Collabora 响应不过来
await new Promise(resolve => setTimeout(resolve, 100));
}
},
clearAllHighlights: async () => {
await clearHighlights(iframeWindowRef.current);
}
}));
```
---
## 阶段五:样式和体验优化(1-2天)
### 5.1 占位符表单样式
- [ ] 创建 `app/styles/components/placeholder-form.css`
- 表单分组样式
- 字段输入框样式
- 按钮样式
- 响应式布局
### 5.2 起草页面样式
- [ ] 创建 `app/styles/pages/contract-draft.css`
- 左右分栏布局
- 固定高度和滚动
- 响应式设计
### 5.3 交互优化
- [ ] 添加加载状态指示
- 文档加载中
- 替换进行中
- 保存中
- [ ] 添加进度提示
- "正在替换占位符... (3/10)"
- 替换完成提示
- [ ] 添加确认对话框
- 完成起草前确认
- 离开页面前提示未保存
### 5.4 错误处理
- [ ] 网络错误提示
- [ ] 文件加载失败处理
- [ ] 替换失败回滚
---
## 阶段六:草稿管理功能(1-2天)
### 6.1 草稿列表页面
- [ ] 创建 `app/routes/contracts.drafts.tsx`
- [ ] 显示用户的所有草稿
- [ ] 支持筛选(模板、状态、日期)
- [ ] 支持搜索
- [ ] 操作按钮
- 继续编辑
- 删除草稿
- 查看详情
### 6.2 草稿卡片组件
- [ ] 创建 `app/components/contracts/DraftCard.tsx`
- 显示草稿信息
- 标题
- 基于的模板
- 创建时间
- 状态
- 操作按钮
---
## 阶段七:测试和优化(2-3天)
### 7.1 功能测试
- [ ] 创建草稿流程测试
- [ ] 占位符替换测试
- 单个替换
- 批量替换
- 部分替换
- 重复替换
- [ ] 保存草稿测试
- [ ] 完成起草测试
- [ ] 边界情况测试
- 占位符不存在
- 替换值为空
- 特殊字符处理
### 7.2 性能测试
- [ ] 大文档替换性能
- [ ] 多占位符替换时间
- [ ] 文件复制速度
### 7.3 用户体验测试
- [ ] 操作流畅性
- [ ] 错误提示清晰度
- [ ] 响应式布局适配
---
## 阶段八:文档和部署(1天)
### 8.1 开发文档
- [ ] 编写功能使用说明
- [ ] 编写 API 文档
- [ ] 编写占位符配置说明
### 8.2 用户手册
- [ ] 如何创建模板(添加占位符)
- [ ] 如何起草合同
- [ ] 常见问题解答
### 8.3 部署准备
- [ ] 数据库迁移脚本
- [ ] 环境变量配置
- [ ] 测试环境部署
- [ ] 生产环境部署
---
## 预计总工期
- **阶段一**:数据库设计 - 1天
- **阶段二**:后端 API 开发 - 2-3天
- **阶段三**:前端组件开发 - 3-4天
- **阶段四**CollaboraViewer 完善 - 1-2天
- **阶段五**:样式优化 - 1-2天
- **阶段六**:草稿管理 - 1-2天
- **阶段七**:测试优化 - 2-3天
- **阶段八**:文档部署 - 1天
**总计:12-18天**
---
## 依赖关系
```
阶段一(数据库) → 阶段二(后端 API) → 阶段三(前端开发)
阶段四(Collabora 完善)
阶段五(样式优化) + 阶段六(草稿管理)
阶段七(测试) → 阶段八(部署)
```
---
## 关键里程碑
1. **里程碑1**:数据库和 API 完成(第3天)
2. **里程碑2**:基本起草功能完成(第7天)
3. **里程碑3**:完整功能开发完成(第12天)
4. **里程碑4**:测试和部署完成(第18天)
---
## 风险评估
| 风险 | 等级 | 应对措施 |
|------|------|----------|
| CollaboraViewer 替换性能不足 | 中 | 提前测试,准备降级方案(使用 docxtemplater |
| 文件复制失败 | 低 | 添加重试机制,记录详细日志 |
| 占位符格式不统一 | 中 | 制定严格的占位符规范,提供模板检查工具 |
| 用户数据丢失 | 中 | 实现自动保存,添加草稿历史记录 |
---
## 可选增强功能(后续迭代)
- [ ] 模板变量智能推荐
- [ ] 历史数据自动填充
- [ ] 合同模板市场
- [ ] 审批流程集成
- [ ] 电子签名集成
- [ ] 导出为 PDF
- [ ] 版本控制和对比