# 文本编辑器功能设计文档 ## 1. 项目概述 ### 1.1 背景与目标 在 Axonix 平台中,添加类似豆包的富文本编辑器功能,服务于以下场景: 1. **mod-chat**:用户在聊天过程中,可将 AI 生成的文档内容直接打开进行编辑、保存 2. **工作流 + Word 模板**:工作流中上传指定 Word 模板,系统根据模板生成文档,用户可在编辑器中打开、修改、下载,下载后的文档格式与原模板一致 3. **oil-agent 跨平台**:同一套编辑器组件可在 oil-agent 平台通过免登录方式使用 ### 1.2 核心价值 - **无缝衔接工作流与编辑**:工作流生成的文档可直接在编辑器中打开,无需下载再用 Word 打开 - **格式保真**:基于 Word 模板生成的文档,下载后字体、标题样式、页眉页脚等与模板完全一致 - **跨平台复用**:一套编辑器组件同时服务 mod-chat 和 oil-agent ### 1.3 技术栈概览 | 层次 | 技术 | |------|------| | 前端框架 | React 18 + TypeScript + Vite + Ant Design | | 编辑器核心 | Slate.js | | 状态管理 | Zustand | | 后端框架 | FastAPI(Python 3.10+)| | ORM | SQLAlchemy 2.0 | | 数据库 | PostgreSQL | --- ## 2. 演进路线总览 ``` 阶段 0 打通基础链路(当前) mod-chat 中 AI 生成文档 → 编辑器打开 → 编辑 → 保存回 Chat 约 1.5 周 阶段 1 Word 模板 + 工作流文档编辑(近期目标) 上传 Word 模板 → 工作流生成文档 → 编辑器打开编辑 → 下载保持模板格式 约 2 周 阶段 2 oil-agent 跨平台集成 编辑器微前端化 → oil-agent 免登录接入 → 跨平台数据同步 约 2 周 阶段 3 编辑器增强 + 协同编辑 富文本增强、版本管理、实时多人协同 约 4 周 ``` --- ## 3. 阶段 0:打通基础链路 ### 3.1 目标 跑通 mod-chat 中"AI 生成文档 → 编辑器打开 → 编辑 → 保存回 Chat"的完整闭环。 ### 3.2 功能范围 1. **从 Chat 打开编辑器** - [ ] 识别聊天消息中的 Markdown 内容 - [ ] 点击消息右上角"编辑"按钮,弹出编辑器 Modal - [ ] 加载消息内容到编辑器 2. **基础编辑器** - [ ] Markdown 文本编辑(Slate.js) - [ ] 实时预览(分屏) - [ ] 防抖自动保存:小文件(< 200KB)防抖 3 秒直接推后端;大文件(≥ 200KB)写入 IndexedDB 缓存,手动保存时再同步 3. **保存并下载** - [ ] 点击"保存"按钮,弹出保存对话框 - [ ] 对话框中填写文件名,选择文件格式(阶段 0 仅支持 `.doc`) - [ ] 前端提交文件名、格式和文档内容到后端 - [ ] 后端生成文件,返回下载链接 - [ ] 前端根据下载链接自动触发浏览器下载 4. **文档 API** - [ ] 创建文档(POST /api/v1/documents) - [ ] 读取文档(GET /api/v1/documents/{id}) - [ ] 更新文档(PUT /api/v1/documents/{id}) - [ ] 导出下载(POST /api/v1/export/doc) ### 3.3 不做的事情 - ❌ Word 模板处理(阶段 1) - ❌ 保持模板格式的 DOCX 导出(阶段 1) - ❌ oil-agent 集成 / Token 机制(阶段 2) - ❌ 版本管理 / 协同编辑(阶段 3) - ❌ Redis / S3 等外部存储(文档内容直接存 DB text 字段,上限 200KB) ### 3.4 架构 > 详见 [技术架构文档 - 阶段 0](./text-editor-architecture.md#2-阶段-0基础链路当前) ### 3.5 核心交互流程 ``` 用户在 Chat 中查看 AI 生成的文档 ↓ 点击"编辑"按钮 ↓ POST /api/v1/documents 创建文档记录,获取 documentId ↓ 打开编辑器 Modal,加载文档内容 ↓ 用户编辑 → 计算变动块(标题 level + index)→ 写入本地缓存 ↓ 判断文档大小 ├── 小文件(< 200KB)→ 防抖 3s → PUT /api/v1/documents/{id}(局部块更新) └── 大文件(≥ 200KB)→ 仅写 IndexedDB,不自动推后端 ↓ 点击"保存"按钮 → 弹出保存对话框 ↓ 用户填写文件名,选择格式(.doc)→ 确认 ↓ POST /api/v1/export/doc 提交文件名、格式、文档内容 ↓ 后端生成 .doc 文件,返回下载链接 ↓ 前端自动触发浏览器下载 ``` ### 3.6 保存对话框交互 ``` ┌─────────────────────────────────┐ │ 保存文档 │ ├─────────────────────────────────┤ │ 文件名 [季度报告_2026Q2 ] │ │ 格式 [.doc ▼ ] │ │ (目前仅支持 .doc) │ ├─────────────────────────────────┤ │ [取消] [确认下载] │ └─────────────────────────────────┘ ``` ### 3.7 前端组件结构 ``` mod-chat/src/ components/ editor/ DocumentEditor.tsx // 编辑器 Modal 主组件 EditorToolbar.tsx // 工具栏(含保存按钮) EditorPreview.tsx // Markdown 分屏预览 SaveDialog.tsx // 保存对话框(文件名 + 格式选择) store/ editorStore.ts // 编辑器 Zustand store api/ documentApi.ts // 文档 API 封装 exportApi.ts // 导出下载 API 封装 ``` ### 3.8 后端文件结构 ``` backend/ app/ main.py // FastAPI 应用入口,注册路由 config.py // 环境变量配置(DB URL 等) core/ database.py // AsyncSession 连接池 dependencies.py // get_db 依赖注入 exceptions.py // 自定义异常 + 全局 handler models/ document.py // documents 表 ORM 模型 schemas/ document.py // DocumentCreate / Update / Response services/ document_service.py // CRUD 业务逻辑 export_service.py // .doc 文件生成 api/ v1/ documents.py // CRUD 路由(5 个端点) export.py // 导出下载路由(1 个端点) migrations/ versions/ 001_init.py // 建表迁移(Alembic) requirements.txt .env.example ``` ### 3.9 排期 | 任务 | 预计工时 | |------|---------| | 编辑器组件搭建(Slate.js 集成) | 3 天 | | 文档 CRUD API(FastAPI + PostgreSQL) | 2 天 | | 保存对话框 + 导出 .doc API | 2 天 | | Chat 集成(打开编辑器) | 1 天 | | 联调测试 | 1 天 | | **合计** | **约 1.5 周** | --- ## 4. 阶段 1:Word 模板 + 工作流文档编辑 ### 4.1 目标 支持工作流上传 Word 模板,工作流根据模板生成文档后,用户可在编辑器中打开、修改,并下载,**下载后的 DOCX 文件格式、字体、标题样式与原模板完全一致**。 ### 4.2 功能范围 1. **Word 模板管理** - [ ] 工作流上传 Word 模板(.docx) - [ ] 后端解析模板,提取样式表(标题级别、字体、页边距、页眉页脚等) - [ ] 模板与工作流关联存储 2. **工作流生成文档** - [ ] 工作流根据模板 + 业务数据生成文档内容,存入 documents 表 - [ ] 文档记录关联对应的模板 ID 3. **编辑器打开工作流文档** - [ ] 从工作流入口打开编辑器,加载生成的文档内容 - [ ] 编辑器渲染时应用模板样式(标题层级、字体显示) - [ ] 工具栏样式选项与模板中的样式对应 4. **下载(保持模板格式)** - [ ] 点击"下载",后端将编辑器内容 + 模板样式合并,生成 DOCX 文件 - [ ] 下载的 DOCX 字体、段落格式、页眉页脚与原模板一致 - [ ] 支持直接下载,不需要先上传到 CDN ### 4.3 不做的事情 - ❌ PDF 导出(可在阶段 3 补充) - ❌ oil-agent 集成(阶段 2) - ❌ 版本管理(阶段 3) ### 4.4 架构 > 详见 [技术架构文档 - 阶段 1](./text-editor-architecture.md#3-阶段-1word-模板--工作流文档编辑) **核心技术**: - `python-docx`:解析模板样式、生成最终 DOCX - `docxtpl`(可选):基于 Jinja2 模板语法填充变量,适合工作流场景 ### 4.5 Word 模板处理流程 ``` 上传模板阶段: 上传 .docx → 解析样式表(标题/正文/字体/间距)→ 存储元数据 + 原始文件 生成文档阶段: 工作流触发 → 基于模板填充内容 → 生成文档记录(内容存 DB) 编辑器打开阶段: 加载文档内容 → 查询关联模板样式 → 编辑器 UI 应用对应样式 下载阶段: 读取编辑器最新内容 → 加载原始模板文件 → 用 python-docx 将内容写入模板 → 返回 DOCX 文件流 ``` ### 4.6 数据模型新增 ```typescript // 模板表 interface DocumentTemplate { id: string; name: string; fileKey: string; // 模板文件存储路径 styles: { // 从模板解析出的样式元数据 headings: HeadingStyle[]; defaultFont: string; pageMargins: PageMargins; hasHeader: boolean; hasFooter: boolean; }; createdAt: number; } // documents 表新增字段 interface Document { // ...原有字段 templateId?: string; // 关联模板 ID(工作流文档必填) source: 'chat' | 'workflow'; } ``` ### 4.7 核心交互流程 ``` 工作流中上传 Word 模板 ↓ 后端解析模板样式,存储模板文件和元数据 ↓ 工作流根据业务数据 + 模板生成文档内容,POST /api/v1/documents(携带 templateId) ↓ 用户点击"在编辑器中打开" ↓ 编辑器加载文档内容,同时拉取模板样式,渲染时应用标题字体等样式 ↓ 用户修改内容 → 自动保存 ↓ 点击"下载" → POST /api/v1/export/docx ↓ 后端:读取最新文档内容 + 加载原始模板 → python-docx 合并 → 返回 DOCX 文件流 ↓ 浏览器下载,文件格式与模板一致 ``` ### 4.8 前端文件结构(新增) ``` mod-chat/src/ components/ editor/ TemplateStyleProvider.tsx // 加载模板样式并注入 CSS 变量(新增) EditorToolbar.tsx // 扩展:工具栏样式选项与模板标题级别对应 api/ templateApi.ts // 模板信息查询 API 封装(新增) ``` ### 4.9 后端文件结构(新增) ``` backend/app/ models/ document_template.py // document_templates 表 ORM 模型(新增) schemas/ template.py // TemplateCreate / TemplateResponse(新增) services/ template_service.py // 模板上传、样式解析、文件存储(新增) export_service.py // 扩展:新增 export_docx_with_template() api/ v1/ templates.py // 模板管理路由(新增,2 个端点) export.py // 扩展:新增 /export/docx 端点 utils/ docx_parser.py // python-docx 样式解析工具函数(新增) ``` ### 4.9 排期 | 任务 | 预计工时 | |------|---------| | 模板上传 + 样式解析 API | 2 天 | | 工作流文档生成 API | 1 天 | | 编辑器应用模板样式(前端) | 2 天 | | DOCX 下载导出(python-docx 合并) | 2 天 | | 联调测试 | 1 天 | | **合计** | **约 2 周** | --- ## 5. 阶段 2:oil-agent 跨平台集成 ### 5.1 目标 将编辑器组件微前端化,供 oil-agent 平台通过免登录 Token 接入,两个平台共用同一份文档数据。 ### 5.2 功能范围 1. **编辑器微前端化** - [ ] 打包为 Module Federation 远程模块 - [ ] 通过 Props 配置 API 端点、主题、权限 2. **免登录 Token 机制** - [ ] 生成临时访问 Token(JWT + Redis) - [ ] oil-agent 凭 Token 打开编辑器,访问指定文档 - [ ] Token 有效期管理(默认 1 小时) 3. **跨平台数据同步** - [ ] 编辑完成后通过 Webhook 通知 oil-agent - [ ] oil-agent 接收事件后拉取最新文档 ### 5.3 架构 > 详见 [技术架构文档 - 阶段 2](./text-editor-architecture.md#4-阶段-2oil-agent-跨平台集成) ### 5.4 核心交互流程 ``` oil-agent 请求编辑某文档 ↓ 调用后端 POST /api/v1/edit-sessions,生成一次性 Token ↓ 返回编辑器 URL(含 Token) ↓ oil-agent 打开新窗口,编辑器凭 Token 加载文档 ↓ 用户编辑保存 → 后端 Webhook 通知 oil-agent ↓ oil-agent 拉取最新文档内容 ``` ### 5.5 前端文件结构(新增) ``` mod-editor/src/ // 新建独立微前端包 components/ editor/ DocumentEditor.tsx // 从 mod-chat 迁移,支持 platform prop EditorToolbar.tsx EditorPreview.tsx SaveDialog.tsx transport/ EditorTransport.ts // SSE / WebSocket 通信适配层(新增) store/ editorStore.ts api/ documentApi.ts exportApi.ts editSessionApi.ts // Token 会话 API 封装(新增) vite.config.ts // Module Federation 打包配置(新增) ``` ### 5.6 后端文件结构(新增) ``` backend/app/ core/ cache.py // Redis 连接池(新增) models/ edit_session.py // edit_sessions 表 ORM 模型(新增) schemas/ edit_session.py // CreateEditSessionRequest / Response(新增) services/ token_service.py // JWT 生成/验证/撤销 + Redis 双重校验(新增) webhook_service.py // 触发 Webhook、签名、重试(新增) api/ v1/ edit_sessions.py // Token 会话路由(新增,3 个端点) webhooks.py // Webhook 配置路由(新增) ``` ### 5.6 排期 | 任务 | 预计工时 | |------|---------| | Module Federation 打包配置 | 1 天 | | Token 服务(JWT + Redis) | 2 天 | | 编辑会话 API | 1 天 | | Webhook 通知 | 1 天 | | oil-agent 接入联调 | 2 天 | | **合计** | **约 2 周** | --- ## 6. 阶段 3:编辑器增强 + 协同编辑 ### 6.1 目标 提升编辑器的编辑体验,并在有需要时支持多人实时协同。 ### 6.2 功能范围 1. **富文本增强** - [ ] 完整格式工具栏(H1-H6、粗体、斜体、列表、代码块、引用块、表格) - [ ] 快捷键(Ctrl+B / Ctrl+I 等) - [ ] 撤销/重做、字数统计、查找替换 - [ ] 图片上传插入 2. **PDF 导出** - [ ] 导出为 PDF(weasyprint) 3. **版本管理** - [ ] 文档版本历史列表 - [ ] 回滚到指定版本 - [ ] 版本对比(Diff) 4. **实时协同(可选,按需启用)** - [ ] 多人同时编辑(Yjs CRDT + WebSocket) - [ ] 实时光标显示、在线用户列表 - [ ] 冲突解决可视化 ### 6.3 架构 阶段 3 完整架构图见 [技术架构文档 - 阶段 3](./text-editor-architecture.md),新增依赖:Yjs、y-websocket、S3/MinIO(大文档迁移)、API Gateway。 ### 6.4 前端文件结构(新增) ``` mod-editor/src/ components/ editor/ CollabCursors.tsx // 协同光标渲染(在线用户实时位置)(新增) OnlineUserList.tsx // 在线用户列表展示(新增) toolbar/ FormatToolbar.tsx // 完整富文本工具栏(H1-H6、代码块、表格等)(新增) FindReplacePanel.tsx // 查找替换面板(新增) version/ VersionHistoryPanel.tsx // 版本历史列表(新增) VersionDiffView.tsx // 两版本对比 Diff 视图(新增) plugins/ withCollaboration.ts // Slate + Yjs 协同插件(新增) withHistory.ts // 撤销/重做扩展 withImages.ts // 图片上传插入插件(新增) api/ versionApi.ts // 版本历史 API 封装(新增) exportApi.ts // 扩展:新增 PDF 导出 ``` ### 6.5 后端文件结构(新增) ``` backend/app/ models/ document_version.py // document_versions 表 ORM 模型(新增) schemas/ version.py // VersionResponse / DiffResponse(新增) services/ version_service.py // 版本快照、回滚、Diff 逻辑(新增) collab_service.py // y-websocket 会话管理(新增) export_service.py // 扩展:新增 export_pdf() api/ v1/ versions.py // 版本管理路由(新增,3 个端点) export.py // 扩展:新增 /export/pdf 端点 collab.py // WebSocket 协同端点(新增) ``` ### 6.6 排期 | 任务 | 预计工时 | |------|---------| | 富文本工具栏增强 | 3 天 | | PDF 导出 | 2 天 | | 版本管理(DB 设计 + API + 前端) | 3 天 | | 实时协同(Yjs + WebSocket) | 5 天 | | **合计** | **约 3-4 周** | --- ## 7. 数据模型汇总 ### 7.1 documents 表 | 字段 | 类型 | 说明 | |------|------|------| | id | string | 文档唯一 ID | | title | string | 标题 | | content | text | 文档内容(Markdown / JSON),上限 200KB | | format | string | `markdown`(阶段 0/1);阶段 3 支持 `json` | | template_id | string | 关联模板 ID,工作流文档必填(阶段 1 引入) | | source | string | `chat` / `workflow` | | session_id | string | 关联聊天会话 ID(可选) | | created_by | string | 创建者 | | created_at | timestamp | | | updated_at | timestamp | | ### 7.2 document_templates 表(阶段 1 引入) | 字段 | 类型 | 说明 | |------|------|------| | id | string | 模板唯一 ID | | name | string | 模板名称 | | file_key | string | 模板文件存储路径 | | styles | jsonb | 解析出的样式元数据 | | created_at | timestamp | | ### 7.3 edit_sessions 表(阶段 2 引入) | 字段 | 类型 | 说明 | |------|------|------| | id | string | 会话 ID | | document_id | string | 文档 ID | | token_hash | string | Token 哈希(不存明文) | | platform | string | `mod-chat` / `oil-agent` | | status | string | `active` / `closed` | | expires_at | timestamp | | --- ## 8. API 汇总 | 接口 | 方法 | 引入阶段 | 说明 | |------|------|---------|------| | /api/v1/documents | POST | 阶段 0 | 创建文档 | | /api/v1/documents/{id} | GET | 阶段 0 | 获取文档 | | /api/v1/documents/{id} | PUT | 阶段 0 | 更新文档 | | /api/v1/documents/{id} | DELETE | 阶段 0 | 删除文档 | | /api/v1/export/doc | POST | 阶段 0 | 导出 .doc 并返回下载链接 | | /api/v1/templates | POST | 阶段 1 | 上传 Word 模板 | | /api/v1/templates/{id} | GET | 阶段 1 | 获取模板信息 | | /api/v1/export/docx | POST | 阶段 1 | 下载 DOCX(保持模板格式) | | /api/v1/edit-sessions | POST | 阶段 2 | 创建编辑会话,返回 Token | | /api/v1/edit-sessions/{id}/document | GET | 阶段 2 | 凭 Token 获取文档 | | /api/v1/edit-sessions/{id} | DELETE | 阶段 2 | 关闭会话 | | /api/v1/export/pdf | POST | 阶段 3 | 导出 PDF | | /api/v1/documents/{id}/versions | GET | 阶段 3 | 版本历史 | --- ## 9. 前端本地存储与同步策略 ### 9.1 大小文件判断与同步方向 以 **200KB** 为分界线: | 文档大小 | 自动保存行为 | |---------|------------| | **小文件(< 200KB)** | 防抖 3 秒后直接 PUT 到后端,本地不做额外缓存 | | **大文件(≥ 200KB)** | 写入前端 IndexedDB 缓存,不立即推后端;用户手动点"保存"时才同步到后端 | ### 9.2 存储分层 | 存储类型 | 用途 | 说明 | |---------|------|------| | **Cookies** | 小型临时状态 | 存储编辑器 UI 状态(当前 documentId、预览模式开关等),TTL 与会话一致 | | **localStorage** | 小文件草稿块缓存 | 按标题块存储变更内容,key 格式见 9.3,浏览器关闭后仍保留 | | **IndexedDB** | 大文件内容缓存 | 存储 ≥ 200KB 的文档内容,异步读写,不阻塞主线程 | ### 9.3 局部更新策略(按标题等级) 不管文档大小,每次编辑都按**标题等级**定位到具体块做局部更新,而不是全量覆盖整个文档。 **块的定义**:以标题节点(H1/H2/H3...)为分割点,每个标题及其下属正文构成一个块,用"第 N 个 K 级标题"来唯一定位。 ``` 文档结构示例: # 第一章 ← H1[0] ## 1.1 背景 ← H2[0] ## 1.2 目标 ← H2[1] # 第二章 ← H1[1] ## 2.1 方案 ← H2[2] 用户只修改了"1.2 目标"下的正文 ↓ 定位到块:{ level: 2, index: 1 } 即"第 2 个 H2 标题" ↓ 只更新该块,其余块不变 ``` **本地存储 key 格式**: ``` localStorage key: doc:{documentId}:h{level}:{index} 示例: doc:doc-abc123:h2:1 ``` **后端局部更新请求**(见 API 文档 2.3): ```json { "blocks": [ { "level": 2, "index": 1, "content": "更新后的内容..." } ] } ``` ### 9.4 自动保存完整流程 ``` 用户编辑内容 ↓ 计算变动块(level + index) ↓ 写入 localStorage(小文件)或 IndexedDB(大文件) ↓ 判断文档大小 ├── 小文件(< 200KB) │ 防抖 3 秒 → PUT /api/v1/documents/{id}(携带变动块) │ 后端更新成功 → 清除对应本地缓存块 │ 后端更新失败 → 保留缓存,下次打开时提示"有未同步的草稿" │ └── 大文件(≥ 200KB) 只写 IndexedDB,不自动推后端 用户手动点"保存" → 全量或按块同步到后端 ``` ### 9.5 打开编辑器时的恢复逻辑 ``` 打开编辑器,传入 documentId ↓ 检查 localStorage / IndexedDB 是否有本地缓存块 ├── 有缓存 → 对比本地 updatedAt 与服务端 updatedAt │ ├── 本地更新 → 提示"发现未同步的草稿,是否恢复?" │ │ 用户确认 → 合并本地块覆盖服务端内容 │ │ 用户拒绝 → 丢弃本地缓存,加载服务端内容 │ └── 服务端更新 → 直接加载服务端内容,清除本地缓存 └── 无缓存 → 直接从服务端加载文档内容 ``` --- ## 10. 风险与应对 | 风险 | 影响 | 应对措施 | |------|------|---------| | Word 模板样式复杂,解析不完整 | 下载格式与模板不一致 | 阶段 1 先支持标题/正文/字体基础样式,复杂样式(表格边框、页眉图片等)逐步覆盖 | | 编辑器富文本节点与 DOCX 样式映射困难 | 导出排版错乱 | 建立明确的节点类型 ↔ DOCX 样式映射表,作为开发规范 | | 大文档(> 200KB)阶段 0 无法存储 | 工作流生成超长文档时失败 | 阶段 0 强制限制,阶段 1 引入文件存储后解除 | | oil-agent Token 安全 | 数据泄露 | JWT + Redis 双重验证,Token 与文档 ID 绑定,默认 1 小时过期 | | 协同编辑并发冲突 | 数据不一致 | 引入成熟 CRDT 库(Yjs),阶段 3 单独评估 | --- ## 11. 附录 ### 相关文档 - [技术架构文档](./text-editor-architecture.md) - [后端 API 详细文档](./text-editor-backend-api.md) ### 参考资料 - [Slate.js 官方文档](https://docs.slatejs.org/) - [python-docx 文档](https://python-docx.readthedocs.io/) - [docxtpl 文档](https://docxtpl.readthedocs.io/) - [Yjs 协同编辑框架](https://yjs.dev/)(阶段 3) --- **文档版本**: v4.0 **最后更新**: 2026-06-11 **维护者**: Axonix 前端团队