# 实现方案 —— 2026-04-18-16-55-47 ## 方案目标 实现 report-editor 中正文与侧边栏的点击联动、字段动态排序、以及默认模板的手术图片表格替换。 ## 需求 1:点击 field-value 联动右侧基本信息 ### 实现步骤 1. **修改 `handleEditorClick`**:在 `ReportEditor.tsx` 的 `handleEditorClick` 函数中,增加对 `.field-value` 的点击捕获。 - 通过 `e.target.closest('.field-value')` 获取被点击的 field-value 元素。 - 读取其 `data-bind` 属性值(如 `patientName`)。 2. **切换 Tab**:调用 `setActiveTab('info')` 将右侧面板切回「基本信息」。 3. **聚焦与滚动**: - 为右侧表单中的每个输入组件增加 `id={\`input-\${field.key}\`}`。 - 使用 `setTimeout` 等待 React DOM 渲染完成后,通过 `document.getElementById(\`input-\${bindKey}\`)` 获取对应元素。 - 调用 `scrollIntoView({ behavior: 'smooth', block: 'center' })` 并 `focus()`。 ## 需求 2:右侧基本信息字段按正文出现顺序动态排序 ### 实现步骤 1. **提取正文字段顺序**: - 使用 `contentRef.current`(当前编辑器 HTML 字符串)或 `editorRef.current?.innerHTML`。 - 对 `formFields.filter(f => f.visibleInForm)` 中的每个非置顶字段,计算 `data-bind="${field.key}"` 在 HTML 中的首次出现位置(`indexOf`)。 2. **排序策略**: - **置顶组**:`const topKeys = ['patientName', 'hospitalId', 'title'];`,按此固定顺序排列。 - **正文组**:非置顶字段,按 `indexOf` 升序排列(越早出现越靠前)。 - **末尾组**:正文中未出现的字段(`indexOf === -1`),统一排在最后,保持原有相对顺序。 3. **渲染表单**:将排序后的字段数组直接用于右侧表单 `.map()` 渲染。 ### 性能优化 - 使用 `useMemo` 缓存排序结果,仅在 `formFields` 或编辑器内容变化时重新计算。 - 排序逻辑放在 `useMemo` 中,避免每次渲染重复计算。 ## 需求 3:替换默认手术图片说明表格 ### 实现步骤 1. 定位 `src/utils/defaultContent.ts` 中的 `defaultReportContent`。 2. 找到 `` 注释所在的 `` 区域。 3. 替换为用户提供的 HTML 代码: - 2 行 × 3 列布局 - 每格包含 `.image-placeholder`(表格内模式:`
` 块级容器,`width:100%; height:100%; max-width:200px; max-height:200px;`) - 每格底部含图注(图A~图F) - 保留 `data-placeholder="true"` 和 `contenteditable="false"` 4. 清理复制时产生的冗余内联样式(如 `background-image: initial` 等),保留功能必需的样式。 ## 涉及文件及修改点 | 文件 | 修改点 | |------|--------| | `src/pages/ReportEditor.tsx` | `handleEditorClick` 增加 field-value 点击捕获;表单渲染增加 `id`;右侧字段排序逻辑 | | `src/utils/defaultContent.ts` | 替换手术图片说明表格 HTML | ## 风险与注意事项 1. `contentRef.current` 在组件首次挂载前可能为空,排序逻辑需做空值保护。 2. `setActiveTab` 后 DOM 切换有短暂延迟,`scrollIntoView` 需包裹在 `setTimeout` 中。 3. 默认模板替换后,需验证新建报告时表格渲染是否正常、占位符点击事件是否生效。 4. 置顶字段的 `key` 名称需与 `DEFAULT_FORM_FIELDS` 中严格一致。