Files
Mdeical_Sur_Report/工程分析/实现方案-2026-04-16-19-06-18.md

133 lines
5.5 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.
# 实现方案 — 2026-04-16-19-06-18
## 根因分析
### 问题 1路由切换后所有内容丢失
`ReportEditor.tsx` 的初始化 `useEffect` 在从 draft 恢复数据时,将 `stateRef.current` 的同步代码放在了 `if (editorRef.current && draft.content.trim().length > 0)` 条件块的**内部**。
这导致当以下任一情况发生时,`stateRef.current` 不会被同步:
- `editorRef.current` 在 useEffect 执行时尚未挂载(组件首次渲染时常见);
- `draft.content` 为空字符串或仅包含空白(新建报告时常见)。
`stateRef.current` 保持为初始值(空的 `reportData`、空的 `videos`、空的 `capturedFrames`)时,用户离开页面触发的 `saveDraftToStorage()` 会**用这些空值覆盖 localStorage 中的 draft**。再次返回 `/report-editor` 时,系统读取了这个被清空的 draft导致所有内容全部丢失。
### 问题 2自动帧插入 UI 批量刷新
`autoCaptureFrames` 是一个 `async` 函数,内部通过 `for` 循环逐帧处理。循环中每次摘取到新帧后都会调用 `setCapturedFrames(accumulatedFrames)`,但由于 React 18 的**自动批处理**机制在异步函数中连续调用的状态更新会被合并DOM 重渲染被推迟到整个循环结束后才执行一次。因此用户看不到逐帧实时摘取的过程,只有等全部帧处理完后,关键帧列表和 placeholder 中的图片才会一次性批量出现。
## 修改文件清单
| 文件 | 修改类型 | 说明 |
|------|---------|------|
| `src/pages/ReportEditor.tsx` | 修改 | 移动 stateRef 同步位置 + 引入 `flushSync` |
## 具体代码变更
### 变更 1useEffect — draft 恢复已有报告(约第 123 行区域)
**当前代码(有问题的结构):**
```tsx
if (editorRef.current && typeof draft.content === 'string' && draft.content.trim().length > 0) {
editorRef.current.innerHTML = draft.content;
// ...
stateRef.current = { ...stateRef.current, reportData: draft.reportData, ... };
}
```
**修改为:**
`stateRef.current` 的赋值提取到 `if (editorRef.current && ...)` 的**外部**,紧接在 `setCapturedFrames` 之后:
```tsx
if (draft.activeTab) setActiveTab(draft.activeTab);
stateRef.current = {
...stateRef.current,
reportData: draft.reportData,
videos: draft.videos,
capturedFrames: draft.capturedFrames,
loadedTemplateId: draft.loadedTemplateId || ''
};
if (editorRef.current && typeof draft.content === 'string' && draft.content.trim().length > 0) {
editorRef.current.innerHTML = draft.content;
contentRef.current = draft.content;
contentLoadedRef.current = true;
setLoadedTemplateId(draft.loadedTemplateId || '');
setTimeout(() => updatePageHeight(), 0);
}
```
### 变更 2useEffect — found 恢复已有报告(约第 141 行区域)
**修改为:**
`stateRef.current` 的赋值提取到 `if (editorRef.current)` 的**外部**,紧接在 `setReportData(found)` 之后:
```tsx
setReportData(found);
stateRef.current = {
...stateRef.current,
reportData: found,
videos: found.videos || [],
capturedFrames: found.capturedFrames || []
};
if (editorRef.current) {
// ... 恢复 editor content ...
}
```
同时顺手清理重复赋值 `contentRef.current = found.content;`(第 149-150 行重复)。
### 变更 3useEffect — draft 恢复新建报告(约第 184 行区域)
**修改为:**
与变更 1 类似,将 `stateRef.current` 的赋值提取到 `if (editorRef.current && ...)` 的**外部**
```tsx
if (draft.activeTab) setActiveTab(draft.activeTab);
stateRef.current = {
...stateRef.current,
reportData: draft.reportData,
videos: draft.videos,
capturedFrames: draft.capturedFrames,
loadedTemplateId: draft.loadedTemplateId || ''
};
if (editorRef.current && typeof draft.content === 'string' && draft.content.trim().length > 0) {
// ... 恢复 editor content ...
}
```
### 变更 4autoCaptureFrames 引入 flushSync约第 519-521 行区域)
在文件顶部增加导入:
```tsx
import { flushSync } from 'react-dom';
```
`autoCaptureFrames` 的 for 循环中,每次更新 `capturedFrames` 时使用 `flushSync` 强制同步渲染:
**当前代码:**
```tsx
setCapturedFrames(accumulatedFrames);
stateRef.current = { ...stateRef.current, capturedFrames: accumulatedFrames };
```
**修改为:**
```tsx
flushSync(() => {
setCapturedFrames(accumulatedFrames);
});
stateRef.current = { ...stateRef.current, capturedFrames: accumulatedFrames };
```
这样每一帧被摘取后都会立即触发 DOM 更新,用户可以在右侧「视频分析」面板中实时看到关键帧逐张出现,同时自动插入逻辑也能按配置延迟后逐张填充 placeholder。
## 风险点
| 风险 | 级别 | 应对措施 |
|------|------|---------|
| `flushSync` 在循环中可能导致轻微渲染卡顿 | 低 | 关键帧数量通常仅 10~20 张,现代浏览器完全可以承受;已在 async 函数中使用,不会阻塞视频 seek 事件 |
| 移动 `stateRef` 同步位置可能与其他逻辑产生时序影响 | 低 | 仅将同步从 `if` 内部移到外部,逻辑语义完全一致,只是确保在所有路径下都被执行 |
| `found` 分支中 `contentRef.current` 重复赋值 | 极低 | 顺手清理,不影响行为 |
## 回滚策略
本次修改仅调整代码执行顺序并引入一个 React 标准 API`flushSync`),未改变数据结构或接口。如出现异常,可直接 `git revert` 回滚。
---
**⚠️ 请审核以上方案,确认无误后回复「确认」或提出修改意见,我将进入测试方案编写阶段。**