Files
Mdeical_Sur_Report/工程分析/经验记录.md
2026-04-16 17:55:13 +08:00

113 lines
9.1 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.
# 经验记录
---
## 记录 1report-editor 新建报告时显示空白模板
**A. 具体问题**
超级管理员进入 `/report-editor`(新建报告)时,编辑区域为纯白色空白,顶部模板选择器显示"无",但 system-settings 中已配置了默认模板。
**B. 产生问题原因**
1. `ReportEditor.tsx` 在组件卸载如页面切换时会自动将当前编辑器内容保存为草稿draft。即使用户未输入任何内容保存的 `content` 也是空字符串 `""`
2. 初始化 effect 中判断草稿是否有效的条件仅使用了 `typeof draft.content === 'string'`,空字符串满足该条件,导致编辑器被填充为空白 HTML并将 `contentLoadedRef.current` 设为 `true`
3. 由于 `contentLoadedRef.current` 已被置为 `true`,后续加载 `settings.defaultTemplate` 的默认模板分支被完全跳过,从而永远显示空白。
4. 此外,草稿中未保存 `loadedTemplateId`,即使内容非空时恢复草稿,模板选择器也会因缺少状态而显示"无"。
**C. 解决问题方案**
1.`saveDraftToStorage` 中将当前 `loadedTemplateId` 一并存入 draft。
2. 将四处草稿恢复的判断条件从 `typeof draft.content === 'string'` 收紧为 `typeof draft.content === 'string' && draft.content.trim().length > 0`,使空白草稿不再拦截默认模板加载。
3. 恢复草稿时同步执行 `setLoadedTemplateId(draft.loadedTemplateId || '')`,确保模板选择器名称正确。
**D. 后续如何避免问题**
- 在前端使用 contentEditable 的自动保存机制时,保存和恢复草稿都应增加对空/仅空白内容的过滤。
- 若草稿与某个业务状态(如当前模板 ID强关联应确保两者一并持久化和恢复避免状态不一致。
- 对兜底初始化逻辑(如默认模板加载)增加更严格的防护,防止被无效中间状态提前截断。
---
## 记录 2关键帧一键插入占位符功能实现
**A. 具体问题**
用户希望视频分析面板中的关键帧截图除了拖拽插入外,还能通过点击 "插入" 按钮一键自动填充到编辑器中第一个空置的 `image-placeholder`
**B. 产生问题原因**
原先仅支持拖拽方式将关键帧放入占位符。当关键帧数量多或占位符位置较远时,操作不便。且 `handleDrop` 中的填充逻辑未抽离,无法被其他交互方式复用。
**C. 解决问题方案**
1.`handleDrop` 中的 HTML 填充逻辑抽离为 `fillPlaceholder(placeholder, frame)` 公共函数。
2. 新增 `insertFrameToPlaceholder(frame)` 函数:通过 `editorRef.current.querySelector('.image-placeholder:not(.has-image)')` 查找第一个空置占位符,找到则调用 `fillPlaceholder`,未找到则 `alert('没有可插入图片的空位')`
3. 在关键帧卡片底部的 `timeFormatted` 与 "可拖拽" 之间新增 "插入" 按钮,使用 `opacity-0 group-hover:opacity-100 transition-opacity` 与 "可拖拽" 保持一致的显隐行为,并通过 `e.stopPropagation()` 避免触发卡片的视频跳转 `onClick`
**D. 后续如何避免问题**
- 当同一交互效果(如填充占位符)需要支持多种触发方式(拖拽、按钮点击、快捷键等)时,应将核心逻辑抽离为独立函数,避免重复代码。
- 在可点击子元素上务必注意事件冒泡控制,防止触发父级不必要的副作用(如此处的视频跳转)。
- UI 提示文字(如 "插入"、"可拖拽")的显隐样式应尽量保持一致,减少用户认知成本。
---
## 记录 3关键帧 "插入" 按钮位置与样式优化
**A. 具体问题**
用户对已实现的 "插入" 按钮位置和样式提出优化:希望按钮位于图片中央、做成实体按钮样式、颜色与 "可拖拽" 的蓝色有明显区分。
**B. 产生问题原因**
初次实现时将 "插入" 按钮放在了卡片底部文字区域,采用纯文字链接样式(`text-accent`),视觉上不够醒目,且与 "可拖拽" 提示颜色重叠,辨识度低。
**C. 解决问题方案**
1. 将 "插入" 按钮从底部文字行移到图片层的 `<div className="relative">` 容器内,使用 `absolute inset-0 m-auto w-fit h-fit` 实现水平和垂直居中。
2. 将按钮样式改为实体胶囊按钮:`px-3 py-1.5 bg-emerald-500 text-white rounded-full shadow-md`hover 时加深为 `bg-emerald-600`
3. 底部文字区域只保留 `timeFormatted` 和 "可拖拽" 提示,"插入" 按钮不再与它们并列。
**D. 后续如何避免问题**
- 对于图片卡片上的核心操作按钮,优先考虑覆盖在图片中央或显著位置,比在底部小字中放置链接更符合用户直觉。
- 同一卡片上的多个 hover 提示元素应保持显隐动画一致(`opacity-0 group-hover:opacity-100 transition-opacity`),但颜色上要有区分,避免用户混淆不同功能。
- 使用 `absolute inset-0 m-auto w-fit h-fit` 是一种在 Tailwind 中不依赖 flex/grid 的居中技巧,适合在 `relative` 容器内居中不定宽高的元素。
---
## 记录 4关键帧 "插入" 按钮位置微调(从图片中央移回底部)
**A. 具体问题**
用户反馈将 "插入" 按钮放在图片正中央会遮挡图片内容,希望移回卡片底部,但仍保留实体按钮样式和蓝色。
**B. 产生问题原因**
按钮以 `absolute` 层覆盖在图片中央时,确实会遮挡部分图片内容,对于医学影像类截图可能影响用户预览。
**C. 解决问题方案**
1. 将 "插入" 按钮从图片层的 absolute 覆盖层移回卡片底部的文字行,放置在 `timeFormatted` 与 "可拖拽" 之间。
2. 按钮颜色恢复为蓝色(`bg-accent text-white`),与 "可拖拽" 蓝色保持一致,视觉上统一。
3. 保留实体胶囊按钮样式:`px-2 py-0.5 rounded-full shadow-sm`,不再是纯文字链接。
4. 显隐行为仍通过 `opacity-0 group-hover:opacity-100 transition-opacity` 与 "可拖拽" 同步。
**D. 后续如何避免问题**
- 对于图片/截图类卡片上的操作按钮,应优先考虑不遮挡核心图片内容的区域(如底部、角落),避免影响预览。
- 在 UI 微调过程中,可以通过小步迭代快速验证用户意图,减少一次性大改导致的方向偏差。
- 实体按钮比纯文字链接具有更高的可点击性和辨识度,在微小空间中也能提供良好的交互体验。
---
## 记录 5自动抽帧丢失、模板选择器变"无"与自动帧插入开关
**A. 具体问题**
用户反馈三个关联问题:
1. 从其他页面返回 `/report-editor` 后,自动抽帧的图片全部丢失。
2. 编辑报告后切换界面再返回,"当前模板"显示为"无"。
3. 希望 system-settings 增加"开启自动帧插入"开关,让自动抽帧直接填充占位符。
**B. 产生问题原因**
1. **自动抽帧丢失**`autoCaptureFrames``captureFrame``removeVideo``handleVideoUpload` 中调用 `setCapturedFrames`/`setVideos` 后立即调用 `saveDraftToStorage`,但 `stateRef.current` 中的 `capturedFrames``videos` 仍是旧值React 的 setState 是异步的)。因此组件卸载前保存的 draft 里缺少新生成的自动帧。
2. **模板选择器变"无"**`saveDraftToStorage` 使用 `useCallback` 捕获了旧的 `loadedTemplateId`stale closure。编辑报告时 `loadedTemplateId` 未通过 `stateRef.current` 同步,导致 draft 中的 `loadedTemplateId` 为空字符串,恢复后模板选择器显示"无"。
3. **自动帧插入需求**:用户希望减少手动拖拽操作,提升效率。
**C. 解决问题方案**
1. **修复状态同步**:将 `loadedTemplateId` 纳入 `stateRef.current`,并在 `saveDraftToStorage` 调用前先强制同步 `stateRef.current = { reportData, videos, capturedFrames, activeTab, loadedTemplateId }`。同时,在所有修改 `videos``capturedFrames` 的函数(`handleVideoUpload``removeVideo``captureFrame``autoCaptureFrames`)中,手动将最新状态同步到 `stateRef.current` 后再 `saveDraftToStorage`
2. **新增自动帧插入开关**
- `SystemSettings` 类型增加 `autoInsertFrames?: boolean`
- `SystemSettings.tsx` 增加 checkbox 开关,并在每个 `framePositions` 百分比旁显示绿色 `Check` 对勾。
- `ReportEditor.tsx``autoCaptureFrames` 中,每生成一帧后若开关开启,则查找第一个 `.image-placeholder:not(.has-image)` 并填充图片;若找不到则静默跳过,不弹窗。循环结束后统一更新 `contentRef.current` 并保存草稿。
**D. 后续如何避免问题**
- 当多个状态需要同时持久化到 localStorage如通过 draft 机制),应避免依赖异步 setState 后的自动同步。在保存前手动将最新计算值同步到 ref 或直接在保存函数中传入最新值,是更可靠的做法。
- `useCallback` 的依赖数组虽然能触发重新创建函数,但函数体内部若同时读取 state 和 ref仍可能出现 stale closure。将可变状态统一收敛到 ref 中,并在保存前强制刷新 ref可有效避免此类问题。
- 新增业务开关时,类型定义、设置页面、消费逻辑三个点必须同步修改,并在 UI 上给予用户明确的视觉反馈(如此次的绿色对勾标记)。