提交 aef3ad69 authored 作者: 张伊明's avatar 张伊明

feat 重构方案

上级 208093f8
流水线 #585 已通过 于阶段
in 1 分 48 秒
# 智能翻译文档对比页前端重构实施方案
## 方案进度概述
- 当前阶段:重构方案定稿
- 目标:将现有全量渲染的文档对比页重构为页级懒加载架构
- 面向对象:大模型执行与前端开发协作
- 约束:原有接口不能改,只能基于新增接口重构
## 1. 需求理解
当前页面的核心问题不是单纯“慢”,而是:
1. 整包数据过大
2. 页和块一次性全量挂载
3. 富文本、图片、表格混合渲染
4. 目录与滚动状态依赖大量 DOM
因此前端重构的目标不是“微调样式”,而是从渲染架构上切换到更适合大文档的方式。
## 2. 最终结论
> 以“页”为最小渲染单位,采用“页级懒加载 + 视口内挂载 + 页外轻量壳 + 页内二级懒加载”的混合方案。
这是当前场景下最稳、最容易落地、也最符合文档阅读习惯的方案。
## 3. 重构目标
### 3.1 功能目标
- 支持超长文档平稳展示
- 支持目录跳转
- 支持页码同步
- 支持图片、表格、标题、正文正常展示
- 支持当前页高亮与章节高亮
### 3.2 性能目标
- 首屏加载更快
- 滚动更稳定
- 大文档不掉帧
- 降低一次性 DOM 规模
### 3.3 可维护性目标
- 页、块、节点职责分离
- 渲染逻辑可分层扩展
- 后续方便接搜索、高亮、差异对比
## 4. 重构原则
1. 页是最小渲染单位
2. 目录只负责导航,不参与重渲染
3. 可见页才挂载真实内容
4. 页外只保留占位壳
5. 图片、表格、富文本做二级懒加载
6. 原始完整结果保留,但不直接驱动 UI 全量渲染
## 5. 页面结构设计
### 5.1 页面容器
文件建议:`src/views/intelligentTranslation/documentCompare.vue`
职责:
- 请求文档摘要、目录、页索引、页内容
- 管理当前页、当前章节、加载状态
- 维护滚动容器
- 协调页面跳转和状态同步
### 5.2 页列表组件
文件建议:`PageList.vue`
职责:
- 遍历页元信息
- 判断页是否进入视口附近
- 决定挂载真实内容还是仅显示页壳
- 控制页缓存策略
### 5.3 页内容组件
文件建议:`PageBlock.vue`
职责:
- 渲染单页内部节点
-`semantic_type` 分发渲染子组件
- 处理 absolute 与 flow 两种布局模式
### 5.4 节点子组件
建议拆分:
- `HeadingNode.vue`
- `TextNode.vue`
- `ImageNode.vue`
- `TableNode.vue`
- `RichHtmlNode.vue`
职责:
- 各类节点独立渲染
- 减少单组件复杂度
- 控制 `v-html` 使用范围
### 5.5 目录树组件
文件建议:`ArticleNavTree.vue`
职责:
- 渲染标题目录
- 点击跳页
- 当前章节高亮
## 6. 状态设计
建议状态如下:
```js
const rawResult = ref(null)
const summary = ref(null)
const pageIndexList = ref([])
const outlineTree = ref([])
const pageBlockMap = ref(new Map())
const visiblePageSet = ref(new Set())
const mountedPageCache = ref(new Map())
const currentPageId = ref('')
const currentNodeKey = ref('')
const compareScrollRef = ref(null)
```
### 状态含义
- `rawResult`:原始完整结果,兼容旧逻辑
- `summary`:文档摘要数据
- `pageIndexList`:页级索引列表
- `outlineTree`:目录树
- `pageBlockMap`:页到节点的映射
- `visiblePageSet`:当前需要挂载的页集合
- `mountedPageCache`:已挂载页缓存
- `currentPageId`:当前激活页
- `currentNodeKey`:当前目录高亮节点
- `compareScrollRef`:滚动容器引用
## 7. 数据获取顺序
### 7.1 初始化顺序
1. 请求 `document-meta`
2. 解析出摘要、页索引、目录树
3. 请求首屏页内容 `document-page`
4. 根据滚动位置继续按需请求其他页
### 7.2 页内按需顺序
当某页进入视口附近:
1. 请求该页完整内容
2. 挂载页真实 DOM
### 7.3 目录跳转顺序
点击目录节点时:
1. 根据 `page_idx` 找目标页
2. 若该页未挂载,先请求页内容
3. 滚动到目标页
4. 更新当前页和章节高亮
## 8. 渲染策略
### 8.1 首屏策略
首屏只挂载:
- 当前页
- 当前页前 1 页
- 当前页后 2 页
其他页只渲染页壳,不渲染真实内容。
### 8.2 视口内挂载策略
使用 `IntersectionObserver` 或滚动监听判断页面是否进入视口附近:
- 进入可视区:挂载真实内容
- 离开较远:保留占位或卸载
### 8.3 页内二级懒加载策略
页内对重资源进一步控制:
- 图片 `loading="lazy"`
- 表格默认只渲染摘要或首屏片段
- 富文本仅对必要节点使用 `v-html`
### 8.4 页外壳策略
未挂载页仍保留:
- 页码
- 页面尺寸占位
- 可选的轻量标题摘要
这样可以保证滚动长度和定位稳定。
## 9. 现有代码改造重点
### 9.1 不再全量 `v-for` 渲染所有页内容
当前逻辑需要从“所有页一次性渲染”改成“页壳 + 条件挂载”。
### 9.2 控制 `v-html` 范围
只在确实需要富文本还原的节点使用 `v-html`,且尽量局部化。
### 9.3 目录与页级状态绑定
当前页、章节高亮、跳转定位统一使用页级索引,不再依赖海量 block DOM 计算。
### 9.4 图片和表格独立组件化
图片和表格不要和普通文本混成一个大渲染函数,必须独立拆组件。
## 10. 推荐组件结构
```text
documentCompare.vue
├── ArticleNavTree.vue
├── PageList.vue
│ ├── PageShell.vue
│ └── PageBlock.vue
│ ├── HeadingNode.vue
│ ├── TextNode.vue
│ ├── ImageNode.vue
│ ├── TableNode.vue
│ └── RichHtmlNode.vue
└── ComparePanel.vue
```
## 11. 实施步骤
### 第 1 步:接入新接口,但不改 UI
目标:先把数据链路切到新接口,保留现有页面结构。
验收:
- 能拿到摘要、页索引、目录
- 能按页拿到内容
### 第 2 步:改成页壳 + 条件挂载
目标:只挂载视口附近页。
验收:
- 非可见页不再创建真实 DOM
- 首屏明显变快
### 第 3 步:拆分页内节点组件
目标:降低单文件复杂度。
验收:
- 标题、正文、图片、表格分组件渲染
### 第 4 步:改造目录跳转和滚动同步
目标:按页同步当前章节。
验收:
- 点击目录可准确跳转
- 滚动时高亮正确
### 第 5 步:增加页内二级懒加载
目标:处理大图、大表和富文本。
验收:
- 长文档滚动更流畅
- 图片/表格不会拖慢主线程
## 12. 风险与注意事项
- 不能直接把整包 JSON 当唯一 UI 数据源长期使用
- 需要保证 `page_idx``node_id` 稳定唯一
- `bbox_norm` 优先使用,缺失时再由 `bbox` 换算
- `reading_order` 必须稳定,否则页内顺序会错乱
- `style_spans` 的字符区间必须与文本严格对齐
## 13. 验收标准
### 功能验收
- 文档正常渲染
- 目录可跳转
- 当前页可同步
- 图片、表格、标题、正文正常展示
### 性能验收
- 首屏快于原方案
- 长文档滚动不卡顿
- 不再出现明显“截断感”
### 结构验收
- 组件职责清晰
- 渲染链路分层明确
- 后续可继续拓展搜索和对比能力
## 14. 结论
前端最终采用:
> 页级懒加载渲染架构。
即:
- 页作为最小渲染单位
- 可见页才挂载真实内容
- 页外只保留轻量壳
- 目录和滚动按页同步
- 图片、表格、富文本做二级懒加载
这是当前场景下最稳妥、最可落地、最利于后续扩展的方案。
# documentCompare 块级响应式渲染改造清单(面向大模型)
## 目标约束
- 单页渲染容器宽度 = 中英文对比区域的一半(即单列宽度)
- 页面宽高比固定为 `21:29.7`(A4 纵向)
- 按 JSON 的 `bbox_norm` 做块级响应式定位渲染
- 块内文本按现有 `style_spans` 做样式还原(非字符几何定位)
## 执行范围
- 目标文件:`src/views/intelligentTranslation/documentCompare.vue`
- 数据源:`src/views/intelligentTranslation/merged_ans.json`
## 一、页面尺寸与比例改造
1. 在英文列与中文列内引入页面画布容器:
- 外层:`page-canvas-wrapper`
- 内层:`page-canvas`
2. `page-canvas` 宽度取当前列可用宽度(100%),高度通过比例自动推导:
- 推荐:`aspect-ratio: 21 / 29.7;`
3. 每个 `page-block` 改为 `position: relative` 的定位容器。
4. 页面中的节点块改为 `position: absolute`,由 `bbox_norm` 映射位置和尺寸。
## 二、数据结构补齐(块级定位)
`buildNormalizedBlocks` 中为每个节点补齐:
- `bboxNorm`: `layout.bbox_norm`
- `bbox`: `layout.bbox`
- `readingOrder`: `layout.reading_order`
- `pageIdx`: `node.page_idx`
- `blockType`: `mapSemanticTypeToNodeType(...)` 的返回值
- `imagePath`: `content_payload.img_path || content_payload.preview_img_path || ''`
新增校验函数:
- `isValidBboxNorm([l,t,r,b])`
- `l >= 0`
- `t >= 0`
- `r <= 1`
- `b <= 1`
- `r > l`
- `b > t`
降级策略:
-`bbox_norm` 无效但 `bbox` 有值,可按 `pages[pageIdx].width/height` 转换为归一化坐标。
- 若仍不可用,节点回退为流式渲染模式。
## 三、块级响应式定位算法
新增方法 `getBlockStyle(item)`,返回绝对定位样式:
- `left: ${l * 100}%`
- `top: ${t * 100}%`
- `width: ${(r - l) * 100}%`
- `height: ${(b - t) * 100}%`
- `position: 'absolute'`
- `overflow: 'hidden'`
说明:
- `[l,t,r,b]` 来自 `item.bboxNorm`
- 同一页节点渲染前按 `readingOrder` 升序排序
## 四、英文区块渲染规则
英文列渲染优先级:
1. `table`:渲染 `tableHtml`
2. `image`:渲染 `<img :src="getEnglishImageSrc(item)" @error="onEnglishImageError(item)" />`
3. 其它文本:渲染 `renderBlockHtml(item.text, item.styleSpans)`
要求:
- 图片链接不可访问时自动降级占位图
- 图片样式:`object-fit: contain; width: 100%; height: 100%`
## 五、中文区块渲染规则
中文列沿用同样的定位方式(`getBlockStyle(item)`):
1. `table``translatedTableHtml || tableHtml`
2. `image`:可先复用英文图片或占位图
3. 文本:`translatedText || text`
## 六、响应式要求
1. 容器宽度变化时无需重算像素,百分比定位自动生效。
2. 同一页的中英文画布高度保持一致。
3. 窄屏可切换为上下布局,但 `page-canvas` 比例保持 `21:29.7`
4. 滚动同步保持页级逻辑,不引入复杂块级联动。
## 七、样式改造点(SCSS)
新增/调整:
- `.page-canvas { position: relative; width: 100%; aspect-ratio: 21 / 29.7; }`
- `.page-node { position: absolute; box-sizing: border-box; }`
- `.page-node__content { width: 100%; height: 100%; overflow: hidden; }`
- `.page-node--image img { width: 100%; height: 100%; object-fit: contain; }`
注意:
- 旧的流式双栏样式保留为 fallback(可配置开关控制)。
- 避免父容器错误 `overflow` 导致块裁剪。
## 八、稳定性与调试开关
建议增加常量:
- `ENABLE_ABSOLUTE_LAYOUT = true`
- `ENABLE_DEBUG_BOX = false`
行为:
- `ENABLE_DEBUG_BOX = true` 时,为每个节点显示边框/类型标签,便于核对布局。
- 页级有效 `bbox_norm` 命中率低于阈值(如 70%)时,该页回退流式渲染。
## 九、验收标准
1. 单页比例恒定为 `21:29.7`
2. 缩放窗口后块位置保持稳定,不出现明显漂移。
3. 英文列图片正常显示,失效链接显示占位图。
4. 左侧导航点击可定位到对应内容。
5. 不新增 lint 报错。
## 十、建议实施顺序
1. 先改英文列为绝对定位并验证图片、标题位置。
2. 再同步改造中文列。
3. 最后补充 fallback 与调试开关。
4. 完成后执行 lint 与页面联调验证。
# 智能翻译模块接口接入开发实施方案
## 方案进度概述
- 当前阶段:**阶段 5(异常处理与收尾)已完成**
- 下一阶段:**联调验收与真实数据回归测试**
- 当前阻塞项:**暂无**
- 实施原则:**每完成一步,经用户验收确认后,先更新本文档中的进度概述与阶段状态,再开始下一步**
---
## 1. 需求理解
智能翻译模块需由当前的本地 Mock/占位逻辑,切换为真实接口驱动的文档翻译流程,核心链路如下:
1.`index.vue` 页面上传 PDF 文件并点击“翻译”。
2. 调用提交接口,获取任务 ID。
3. 跳转到 `documentCompare.vue` 页面后,根据任务 ID 轮询状态接口。
4. 任务未完成时展示等待界面,并每秒轮询一次。
5. 任务完成后关闭等待界面,调用结果接口获取翻译数据。
6. 将结果数据渲染到页面中:
- 英文部分展示接口返回的原始内容或结构化内容
- 译文部分先置空,后续可扩展人工编辑或 AI 补全
---
## 2. 接口链路方案
### 2.1 提交翻译任务接口
- 方法:`POST`
- 地址:`http://172.19.21.16:8019/tools/api/v1/doc-detail-pdf/submit`
- Header:
- `Client-ID: test_client`
- `X-API-Key: test_key`
- Body:
- 文件字段名:`files`
#### 预期返回
```json
{
"code": 0,
"message": "submit success",
"data": {
"task_id": "12418428891750955941",
"filename": "test.pdf",
"file_path": "/app/Datas/uploads/12418428891750955941/test.pdf",
"task_ids": [
"12418428891750955941"
]
}
}
```
#### 处理逻辑
- 用户在 `index.vue` 上传 PDF 后点击“AI翻译”
- 前端将文件组装为 `FormData`
- 调用提交接口
- 提取返回的 `task_id`
- 携带 `task_id` 跳转到 `documentCompare.vue`
---
### 2.2 查询任务状态接口
- 方法:`GET`
- 地址:`http://172.19.21.16:8019/tools/api/v1/doc-detail-pdf/status/{taskId}`
- Header:
- 同上
#### 预期返回
```json
{
"code": 0,
"message": "status success",
"data": {
"tool_id": "doc-detail-pdf",
"task_id": "660469818111596",
"status": "succeeded",
"progress": 0,
"file_path": "/app/Datas/uploads/12418428891750955941/test.pdf",
"time_finish": "1776680370634331224"
}
}
```
#### 处理逻辑
- `documentCompare.vue` 页面根据 `taskId` 启动轮询
- 轮询频率:**1 秒一次**
- 只要接口返回不是 `status success`,就保持等待界面
- 当返回 `status success` 时,停止轮询并进入结果获取阶段
---
### 2.3 获取翻译结果接口
- 方法:`GET`
- 地址:`http://172.19.21.16:8019/tools/api/v1/doc-detail-pdf/result/{taskId}`
- Header:
- 同上
#### 预期返回
- 返回结构与 `merged_ans.json` 类似
- 该数据用于渲染文档翻译结果
#### 处理逻辑
- 状态成功后立即请求结果接口
- 将返回数据映射为页面可渲染结构
- 英文部分展示接口返回内容
- 译文部分默认置空
---
## 3. 页面级实施方案
### 3.1 `index.vue`
#### 目标
将当前页面的“上传 PDF + 点击翻译”改为真实提交接口调用。
#### 实施内容
1. 保留 PDF 上传能力。
2. 校验文件类型,仅允许 PDF。
3. 点击“AI翻译”时:
- 检查是否已选择文件
- 组装 `FormData`
- 调用提交接口
4. 提交成功后:
- 提取 `task_id`
- 跳转到 `documentCompare.vue`
- 通过路由参数传递 `task_id`
#### 验收标准
- 选择 PDF 后点击翻译可发起真实请求
- 成功后能拿到 `task_id`
- 页面能正确跳转到对比页
---
### 3.2 `documentCompare.vue`
#### 目标
实现任务轮询、等待态展示、结果拉取和结果渲染。
#### 实施内容
1. 页面进入时读取 `taskId`
2. 展示等待界面。
3. 每秒轮询状态接口。
4. 当状态完成时:
- 关闭等待界面
- 请求结果接口
5. 将结果渲染到页面中。
#### 验收标准
- 未完成任务时持续等待且不闪退
- 完成后自动进入结果展示
- 不重复发起多余轮询
- 离开页面时轮询能正确停止
---
## 4. 数据渲染规则
### 4.1 英文部分
- 使用结果接口返回的数据直接渲染
- 保持文档结构尽量原样展示
- 如果接口返回的是分段/页结构,则按结构组织渲染
### 4.2 译文部分
- 默认置空
- 可显示空态提示,如“暂无译文”
- 为后续人工修订或二次翻译预留输入区域
### 4.3 文档结构适配
由于结果数据来源于 `merged_ans.json` 类似结构,建议增加一层适配处理:
- 原始接口字段
- 页面展示字段
- 空值兜底字段
- 顺序结构字段
---
## 5. 关键技术点
### 5.1 文件上传
- 使用 `FormData`
- 字段名必须为 `files`
- 仅允许 PDF
### 5.2 轮询控制
- 建议使用递归 `setTimeout`,更易控制停止与重试
- 轮询周期:1 秒
- 进入结果阶段后立即停止轮询
### 5.3 生命周期处理
- 组件卸载时清理轮询
- 路由切换时避免重复定时器
- 请求失败时展示错误状态并允许重试
### 5.4 状态管理
建议维护以下状态:
- `taskId`
- `loading`
- `polling`
- `status`
- `resultData`
- `errorMessage`
---
## 6. 分步实施计划
### 阶段 1:接口封装
- 封装提交接口
- 封装状态查询接口
- 封装结果查询接口
- 统一请求头配置
**验收标准**
- 三个接口可独立调通
- 返回结构可正确解析
---
### 阶段 2:`index.vue` 提交链路改造
- 替换 Mock 翻译逻辑
- 点击翻译时调用提交接口
- 成功后跳转并传递 `taskId`
**验收标准**
- 上传 PDF 后可成功创建任务
- 能跳转到对比页
---
### 阶段 3:`documentCompare.vue` 轮询等待
- 实现进入页自动轮询
- 未完成时展示等待界面
- 完成后停止轮询
**验收标准**
- 任务未完成时能持续等待
- 任务完成后自动进入下一步
---
### 阶段 4:结果获取与渲染
- 请求结果接口
- 将返回数据映射到页面结构
- 英文区渲染原始内容
- 译文区默认空
**验收标准**
- 页面能展示真实翻译结果
- 数据结构与 UI 对应正确
---
### 阶段 5:异常处理与收尾
- 上传失败提示
- 状态轮询失败处理
- 结果接口异常处理
- 页面卸载清理定时器
**验收标准**
- 网络异常时不崩溃
- 错误信息可见且可恢复
---
## 7. 风险与待确认项
### 风险
1. 接口返回的结果结构可能与 `merged_ans.json` 存在差异,需要做适配层。
2. `status success` 的判断条件需要严格确认,避免误判。
3. 结果接口是否支持大文件完整返回,需要确认前端展示性能。
4. `documentCompare.vue` 的 UI 结构可能需要根据结果数据做一定调整。
### 待确认项
1. 路由参数传递方式:`query` 还是 `params`
2. 结果页是否需要支持多任务历史记录
3. 英文/译文是否需要可编辑
4. 翻译完成后是否需要自动保存到本地或后端
---
## 8. 进度更新规则
1. 每完成一个阶段,先进行自检。
2. 自检通过后,向用户汇报当前阶段结果。
3. 等待用户验收确认。
4. 验收通过后,更新本文档中的:
- 方案进度概述
- 当前阶段状态
- 已完成事项
- 下一步计划
5. 再开始下一阶段。
---
## 9. 交付物
- 本开发实施方案文档
- 接口封装代码
- `index.vue` 提交链路改造
- `documentCompare.vue` 轮询与结果渲染
- 异常处理与清理逻辑
---
## 10. 本轮执行结果(2026-04-21)
- 已完成:阶段 1(提交/状态/结果接口封装),新增 `src/api/intelligentTranslation/index.js`
- 已完成:阶段 2(提交链路改造),`index.vue` 已接入真实提交接口并携带 `taskId` 跳转
- 已完成:阶段 3(轮询等待),`documentCompare.vue` 进入页面后按 1 秒轮询状态并支持停止
- 已完成:阶段 4(结果渲染),状态成功后自动请求结果接口并渲染英文侧,译文侧默认空
- 已完成:阶段 5(异常与收尾),缺少任务 ID、状态查询失败、页面卸载均已处理
......@@ -122,6 +122,11 @@ export default defineConfig({
target: 'http://8.140.26.4:10029/',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/intelligent-api/, '')
},
'/doc-detail-pdf-api': {
target: 'http://172.19.21.16:8019/',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/doc-detail-pdf-api/, '/tools/api/v1/doc-detail-pdf')
}
}
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论