diff --git a/src/pages/ReportEditor.tsx b/src/pages/ReportEditor.tsx index 8973d99..21b683f 100644 --- a/src/pages/ReportEditor.tsx +++ b/src/pages/ReportEditor.tsx @@ -54,6 +54,7 @@ export default function ReportEditor() { const prevVideoCountRef = useRef(0); const [activeTab, setActiveTab] = useState<'info' | 'video'>('info'); + const [activeFieldKey, setActiveFieldKey] = useState(null); const [multiSelectOptions, setMultiSelectOptions] = useState>({ surgeon: ['张医生', '李医生', '王医生'], assistant: ['赵医生', '钱医生', '孙医生'], @@ -378,13 +379,14 @@ export default function ReportEditor() { const targetEl = node as HTMLElement | null; if (!targetEl) return; - // Handle click on field-value: switch to info tab and focus corresponding input + // Handle click on field-value: switch to info tab, highlight and focus corresponding input const fieldValue = targetEl.closest('.field-value') as HTMLElement | null; if (fieldValue) { const bindKey = fieldValue.getAttribute('data-bind'); if (bindKey) { setActiveTab('info'); stateRef.current = { ...stateRef.current, activeTab: 'info' }; + setActiveFieldKey(bindKey); setTimeout(() => { const inputEl = document.getElementById(`input-${bindKey}`); if (inputEl) { @@ -489,11 +491,14 @@ export default function ReportEditor() { const fillPlaceholderSrc = (placeholder: HTMLElement, src: string) => { placeholder.innerHTML = ` × - + `; placeholder.classList.add('has-image'); placeholder.style.border = 'none'; placeholder.style.background = 'transparent'; + placeholder.style.height = 'auto'; + placeholder.style.width = 'auto'; + placeholder.style.lineHeight = 'normal'; if (editorRef.current) contentRef.current = editorRef.current.innerHTML; saveDraftToStorage(); }; @@ -664,6 +669,9 @@ export default function ReportEditor() { emptyPlaceholder.classList.add('has-image'); emptyPlaceholder.style.border = 'none'; emptyPlaceholder.style.background = 'transparent'; + emptyPlaceholder.style.height = 'auto'; + emptyPlaceholder.style.width = 'auto'; + emptyPlaceholder.style.lineHeight = 'normal'; contentRef.current = editorRef.current.innerHTML; saveDraftToStorage(); } @@ -690,11 +698,14 @@ export default function ReportEditor() { const fillPlaceholder = (placeholder: HTMLElement, frame: CapturedFrame) => { placeholder.innerHTML = ` × - + `; placeholder.classList.add('has-image'); placeholder.style.border = 'none'; placeholder.style.background = 'transparent'; + placeholder.style.height = 'auto'; + placeholder.style.width = 'auto'; + placeholder.style.lineHeight = 'normal'; if (editorRef.current) contentRef.current = editorRef.current.innerHTML; saveDraftToStorage(); }; @@ -1453,7 +1464,7 @@ export default function ReportEditor() { if (field.type === 'text' || field.type === 'date') { const inputType = field.type === 'date' ? 'date' : 'text'; return ( -
f2.visibleInForm && f2.type === 'text' && f2.isSystemLocked).length > 1 && (field.key === 'patientName' || field.key === 'hospitalId') ? 'flex-1 space-y-1' : 'space-y-1'}> +
f2.visibleInForm && f2.type === 'text' && f2.isSystemLocked).length > 1 && (field.key === 'patientName' || field.key === 'hospitalId') ? 'flex-1 space-y-1' : 'space-y-1'} p-2 -mx-2 rounded-xl transition-all duration-300 ${activeFieldKey === field.key ? 'bg-blue-50 ring-1 ring-accent shadow-sm' : ''}`}> @@ -1473,7 +1484,7 @@ export default function ReportEditor() { const isOpen = openDropdown === field.key; const opts = field.options || (field.key === 'anesthesiaType' ? anesthesiaOptions : []); return ( -
+
+
+
- {videos.length > 0 && ( -
+
+ {videos.map((v, i) => (
{currentVideoIndex !== -1 && ( -
+
-
+
关键帧摘取 + ``` + +## 需求 4:视频模块间距紧凑化 + +### 修改文件 +`src/pages/ReportEditor.tsx` + +### 修改内容 +1. 最外层容器从 `space-y-4` 改为 `space-y-2`。 +2. 视频播放器与控制按钮之间从 `space-y-4` 改为 `space-y-2`。 +3. 控制按钮区域(播放/暂停/进度条等)的 `gap` 或 `margin` 适当缩减。 +4. 「关键帧摘取」标题区域的 `padding-top` 缩减,可增加 `border-t` 作为视觉分隔。 + +## 需求 5:签名与日期之间增加空行 + +### 修改文件 +`src/utils/defaultContent.ts` + +### 修改内容 +在「手术者签名」`

` 和「撰写时间」`

` 之间插入: +```html +

 

+``` + +## 需求 6:图片占位符填充后高度自适应 + +### 修改文件 +`src/pages/ReportEditor.tsx` 和 `src/pages/TemplateManage.tsx` + +### 修改内容 +在所有填充图片的逻辑中(`fillPlaceholderSrc`、`handleDrop`、`autoCaptureFrames` 等),在 `placeholder.classList.add('has-image')` 之后,增加: +```ts +placeholder.style.height = 'auto'; +placeholder.style.width = 'auto'; +placeholder.style.lineHeight = 'normal'; +``` + +## 涉及文件及修改点 +| 文件 | 修改点 | +|------|--------| +| `src/utils/defaultContent.ts` | `smartField()` 注入 `.no-underline`;签名与日期之间插入空行 | +| `src/pages/ReportEditor.tsx` | `activeFieldKey` 状态 + 高亮样式;视频上传按钮整合;视频面板间距缩减;占位符自适应样式 | +| `src/pages/TemplateManage.tsx` | 占位符自适应样式 | + +## 风险与注意事项 +1. `smartField()` 中硬编码的 6 个 key 需与 `DEFAULT_FORM_FIELDS` 严格一致。 +2. `activeFieldKey` 高亮样式使用 `transition-all duration-300`,需确保不会与现有样式冲突。 +3. 视频上传按钮移入缩略图列表后,需确保 `videoInputRef` 的点击触发逻辑不受影响。 +4. 占位符 `height: auto` 后,需验证图片在表格内(table cell)和正文中的显示是否正常。 diff --git a/工程分析/测试方案-2026-04-18-19-08-43.md b/工程分析/测试方案-2026-04-18-19-08-43.md new file mode 100644 index 0000000..fff91ab --- /dev/null +++ b/工程分析/测试方案-2026-04-18-19-08-43.md @@ -0,0 +1,64 @@ +# 测试方案 —— 2026-04-18-19-08-43 + +## 测试目标 +验证六项需求修改的正确性和稳定性。 + +## 测试用例 + +### TC-1:基础信息字段打印无下划线 +**前置条件**:新建报告,默认模板已加载。 +**步骤**: +1. 点击「打印预览」或「下载 PDF」。 +2. 检查「姓名、性别、年龄、科别、床号、住院号」区域。 +**预期结果**:这 6 个字段不显示下划线,其他字段(如手术名称、诊断等)正常显示下划线。 + +### TC-2:点击 field-value 联动高亮并居中滚动 +**前置条件**:编辑器已加载默认模板,右侧基本信息 Tab 可见。 +**步骤**: +1. 点击正文中任意 `class="field-value"`(如「手术名称」)。 +2. 观察右侧对应输入框。 +**预期结果**: +- 对应输入框出现蓝色背景高亮(`bg-blue-50 ring-1 ring-accent`)。 +- 页面平滑滚动,使该输入框位于可视区域中部。 +- 输入框获得焦点。 + +### TC-3:视频上传按钮整合进缩略图列表 +**前置条件**:已上传至少一个视频。 +**步骤**: +1. 切换到「视频分析」Tab。 +2. 观察视频缩略图列表。 +**预期结果**: +- 列表第一个位置是一个缩小版「上传视频」按钮,尺寸与视频卡片一致(约 `w-24`)。 +- 点击该按钮能正常打开文件选择器。 +- 原本独立的「点击上传手术视频」大按钮已消失。 + +### TC-4:视频模块间距紧凑化 +**前置条件**:视频分析面板展开,有视频和关键帧。 +**步骤**: +1. 观察缩略图列表与播放器之间的间距。 +2. 观察播放器与控制按钮之间的间距。 +3. 观察控制按钮与「关键帧摘取」标题之间的间距。 +**预期结果**:各项间距明显缩小,下方关键帧列表获得更多展示空间。 + +### TC-5:签名与日期之间增加空行 +**前置条件**:默认模板已加载。 +**步骤**: +1. 滚动到模板底部,查看「手术者签名」与「撰写时间」之间。 +**预期结果**:两者之间有一个空行(约一行高度的空白)。 + +### TC-6:图片占位符填充后高度自适应 +**前置条件**:模板中有空图片占位符,有较小的图片(高度 < 200px)。 +**步骤**: +1. 将图片插入占位符(通过上传、拖拽或自动摘取)。 +2. 观察占位符区域。 +**预期结果**: +- 占位符高度随图片实际尺寸自适应,不再保留 200px 固定高度。 +- 图片下方不会出现大片空白。 + +## 回归测试 +- 确保打印功能(PDF 导出)正常工作。 +- 确保视频播放、关键帧摘取、拖拽插入功能正常。 +- 确保 `template-manage` 中的图片占位符同样支持高度自适应。 + +## 测试通过标准 +所有用例均通过,无控制台报错,打印样式正常。 diff --git a/工程分析/需求分析-2026-04-18-19-08-43.md b/工程分析/需求分析-2026-04-18-19-08-43.md new file mode 100644 index 0000000..6a27bc6 --- /dev/null +++ b/工程分析/需求分析-2026-04-18-19-08-43.md @@ -0,0 +1,41 @@ +# 需求分析 —— 2026-04-18-19-08-43 + +## 需求来源 +用户基于实际使用体验,提出六项界面交互和排版优化需求。 + +## 需求概述 + +### 需求 1:基础信息字段默认无下划线 +默认模板中的「姓名、性别、年龄、科别、床号、住院号」6 个基础信息字段,在打印时默认不显示下划线。通过在 `smartField()` 函数中根据 key 自动注入 `.no-underline` 类实现。 + +### 需求 2:点击 field-value 联动高亮并居中滚动 +在 `report-editor` 中点击正文 `class="field-value"` 时: +- 右侧基本信息对应输入框高亮显示蓝色背景(类似 `template-manage` 中的字段高亮效果) +- 自动滚动到屏幕可视区域的 1/3~2/3 位置(使用 `block: 'center'`) + +### 需求 3:视频上传按钮整合进缩略图列表 +将 `report-editor` 右侧「视频分析」Tab 中原本独立占据一行的「上传视频」大按钮,缩小并移入水平滚动的视频缩略图列表首位(`flex gap-2 overflow-x-auto`),尺寸与视频卡片保持一致(约 `w-24 h-[68px]`),节省垂直空间。 + +### 需求 4:视频模块间距紧凑化 +缩减「视频分析」面板中以下间距: +- 视频缩略图列表与下方视频播放器之间的距离 +- 视频播放器与播放控制按钮之间的距离 +- 播放控制按钮与「关键帧摘取」标题之间的距离 +为下方关键帧列表腾出更多展示空间。 + +### 需求 5:签名与日期之间增加空行 +在 `defaultContent.ts` 中,「手术者签名」行与「撰写时间」行之间插入一个空段落 `

 

`,使排版更美观。 + +### 需求 6:图片占位符填充后高度自适应 +当图片占位符被填充图片后,若图片实际高度小于占位符预设高度(如 200px),占位符仍保留固定高度导致下方出现大片空白。需在填充图片后,将占位符的 `height`、`width`、`line-height` 重置为 `auto` / `normal`,让高度由图片实际尺寸决定。 + +## 涉及文件 +- `src/utils/defaultContent.ts`(需求 1、5) +- `src/pages/ReportEditor.tsx`(需求 2、3、4、6) +- `src/pages/TemplateManage.tsx`(需求 6) + +## 需求影响范围 +- 默认模板打印样式 +- 编辑器交互体验 +- 视频分析面板布局 +- 图片占位符自适应行为