119 lines
4.0 KiB
Markdown
119 lines
4.0 KiB
Markdown
# 实现方案 — 2026-04-16-18-51-06
|
||
|
||
## 根因分析
|
||
|
||
`ReportEditor.tsx` 中使用了 `stateRef.current` 作为「草稿自动保存」的数据来源。组件卸载时,`saveDraftToStorage()` 会将 `stateRef.current` 写入 `localStorage`。
|
||
|
||
但在页面初始化(`useEffect` 和 `useLayoutEffect`)从已保存报告或 draft 恢复数据时,仅通过 `setState` 更新了 React state,**没有同步更新 `stateRef.current` 中的 `videos` 和 `capturedFrames` 字段**。
|
||
|
||
这导致:
|
||
1. 用户首次进入 `/report-editor` 时,数据从 localStorage 正确恢复;
|
||
2. 用户离开页面时,`stateRef.current` 仍保存着初始的空数组;
|
||
3. 组件卸载触发的 `saveDraftToStorage()` 用空数组覆盖了 draft;
|
||
4. 用户再次返回 `/report-editor` 时,系统优先读取被覆盖后的 draft,导致视频分析数据全部丢失。
|
||
|
||
## 修改文件清单
|
||
|
||
| 文件 | 修改类型 | 说明 |
|
||
|------|---------|------|
|
||
| `src/pages/ReportEditor.tsx` | 修改 | 在 4 处数据恢复逻辑后追加 `stateRef.current` 同步赋值 |
|
||
|
||
## 具体代码变更
|
||
|
||
### 修改点 1:初始化 useEffect — 从 draft 恢复已有报告(约第 128 行后)
|
||
|
||
在已有代码:
|
||
```tsx
|
||
setLoadedTemplateId(draft.loadedTemplateId || '');
|
||
stateRef.current = { ...stateRef.current, loadedTemplateId: draft.loadedTemplateId || '' };
|
||
```
|
||
|
||
**追加同步**:
|
||
```tsx
|
||
stateRef.current = {
|
||
...stateRef.current,
|
||
reportData: draft.reportData,
|
||
videos: draft.videos,
|
||
capturedFrames: draft.capturedFrames,
|
||
loadedTemplateId: draft.loadedTemplateId || ''
|
||
};
|
||
```
|
||
|
||
### 修改点 2:初始化 useEffect — 从已保存报告(found)恢复(约第 146 行后)
|
||
|
||
在设置完 `contentLoadedRef.current = true;` 之后,**追加同步**:
|
||
```tsx
|
||
stateRef.current = {
|
||
...stateRef.current,
|
||
reportData: found,
|
||
videos: found.videos || [],
|
||
capturedFrames: found.capturedFrames || []
|
||
};
|
||
```
|
||
|
||
### 修改点 3:初始化 useEffect — 从 draft 恢复新建报告(约第 176 行后)
|
||
|
||
与修改点 1 类似,在 `setLoadedTemplateId(draft.loadedTemplateId || '');` 之后,**追加同步**:
|
||
```tsx
|
||
stateRef.current = {
|
||
...stateRef.current,
|
||
reportData: draft.reportData,
|
||
videos: draft.videos,
|
||
capturedFrames: draft.capturedFrames,
|
||
loadedTemplateId: draft.loadedTemplateId || ''
|
||
};
|
||
```
|
||
|
||
### 修改点 4:useLayoutEffect 安全网 — 从 draft 恢复已有报告(约第 677 行后)
|
||
|
||
在 `setLoadedTemplateId(draft.loadedTemplateId || '');` 之后,**追加同步**:
|
||
```tsx
|
||
stateRef.current = {
|
||
...stateRef.current,
|
||
reportData: draft.reportData,
|
||
videos: draft.videos,
|
||
capturedFrames: draft.capturedFrames,
|
||
loadedTemplateId: draft.loadedTemplateId || ''
|
||
};
|
||
```
|
||
|
||
### 修改点 5:useLayoutEffect 安全网 — 从已保存报告(found)恢复(约第 692 行后)
|
||
|
||
在 `contentLoadedRef.current = true;` 之后,**追加同步**:
|
||
```tsx
|
||
stateRef.current = {
|
||
...stateRef.current,
|
||
reportData: found,
|
||
videos: found.videos || [],
|
||
capturedFrames: found.capturedFrames || []
|
||
};
|
||
```
|
||
|
||
### 修改点 6:useLayoutEffect 安全网 — 从 draft 恢复新建报告(约第 701 行后)
|
||
|
||
在 `setLoadedTemplateId(draft.loadedTemplateId || '');` 之后,**追加同步**:
|
||
```tsx
|
||
stateRef.current = {
|
||
...stateRef.current,
|
||
reportData: draft.reportData,
|
||
videos: draft.videos,
|
||
capturedFrames: draft.capturedFrames,
|
||
loadedTemplateId: draft.loadedTemplateId || ''
|
||
};
|
||
```
|
||
|
||
## 风险点
|
||
|
||
| 风险 | 级别 | 应对措施 |
|
||
|------|------|---------|
|
||
| `stateRef` 仍可能在其他未覆盖路径中不同步 | 低 | 已检查所有数据恢复入口(init effect + layout effect),后续若新增恢复逻辑需保持同步习惯 |
|
||
| `found.videos` / `found.capturedFrames` 为 undefined | 低 | 代码中使用 `|| []` 做防御性处理 |
|
||
|
||
## 回滚策略
|
||
|
||
本次修改仅增加 `stateRef.current` 的同步赋值语句,不涉及删除或重构现有逻辑。如出现异常,可直接 `git revert` 回滚。
|
||
|
||
---
|
||
|
||
**⚠️ 请审核以上方案,确认无误后回复「确认」或提出修改意见,我将进入测试方案编写阶段。**
|