# 实现方案 — 2026-04-17-09-36-07 ## 根因分析 1. **多余空格**:`TemplateManage.tsx` 的 `insertSmartField` 函数在 HTML 字符串末尾追加了 ` `,这是导致字段后跟随大量空白的主要原因。 2. **异常换行**:`inline-block` 元素默认会在边界处根据容器宽度自动换行;`contenteditable="false"` 节点在行尾时,浏览器可能将其视为独立的渲染单元进行换行。 3. **Backspace 误删整行**:当光标位于 `contenteditable="false"` 的内联元素之后时,Webkit/Blink 内核的默认行为无法正确删除该节点,而是向上寻找到父级 `

` 并将其删除。这是 `contentEditable` 的经典 Bug。 4. **默认模板未预置**:`defaultContent.ts` 中的第一行仍使用红色纯文本占位符,没有使用 `smartField()` 函数生成智能控件。 ## 修改文件清单 | 文件 | 修改类型 | 说明 | |------|---------|------| | `src/pages/TemplateManage.tsx` | 修改 | 优化 `insertSmartField` HTML(移除 ` `、压缩为一行);增加 `keydown` 事件拦截,保护 `.smart-field-wrapper` 不被 Backspace 误删 | | `src/utils/defaultContent.ts` | 修改 | 将默认模板第一行的红色占位符替换为预置的智能字段控件 | | `src/index.css` | 修改(可选) | 给 `.smart-field-wrapper` 增加 `white-space: nowrap` | --- ## 具体代码变更 ### 变更 1:`src/pages/TemplateManage.tsx` — 优化插入 HTML **当前代码(约第 159-173 行):** ```tsx const insertSmartField = (field: FormField) => { editorRef.current?.focus(); const html = `   `; document.execCommand('insertHTML', false, html); editorRef.current?.focus(); }; ``` **修改为:** ```tsx const insertSmartField = (field: FormField) => { editorRef.current?.focus(); const html = ``; document.execCommand('insertHTML', false, html); editorRef.current?.focus(); }; ``` **改动点**: - 移除末尾的 ` `。 - 将多行模板字符串压缩为一行,消除源码换行被渲染为空格的问题。 - 在 `field-value` 的内联样式中增加 `white-space: nowrap;`。 ### 变更 2:`src/pages/TemplateManage.tsx` — 拦截 Backspace/Delete 防止误删整行 在现有的 `useEffect`(用于监听编辑器 click 事件)附近,新增一个 `useEffect` 监听 `keydown`: ```tsx useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key !== 'Backspace' && e.key !== 'Delete') return; const sel = window.getSelection(); if (!sel || sel.rangeCount === 0) return; const range = sel.getRangeAt(0); if (!range.collapsed) return; const container = range.startContainer; const offset = range.startOffset; // Find the node immediately before the cursor let prevNode: Node | null = null; if (container.nodeType === Node.TEXT_NODE) { if (offset === 0) { prevNode = container.previousSibling; } } else if (container.nodeType === Node.ELEMENT_NODE) { prevNode = (container as Element).childNodes[offset - 1] || null; } if (!prevNode) return; // If the previous node is our smart field wrapper, remove it manually const fieldWrapper = prevNode.nodeType === Node.ELEMENT_NODE ? (prevNode as Element).closest('.smart-field-wrapper') : prevNode.parentElement?.closest('.smart-field-wrapper'); if (fieldWrapper && editorRef.current?.contains(fieldWrapper)) { e.preventDefault(); e.stopPropagation(); fieldWrapper.remove(); } }; const editor = editorRef.current; if (editor) { editor.addEventListener('keydown', handleKeyDown, true); } return () => { if (editor) { editor.removeEventListener('keydown', handleKeyDown, true); } }; }, [currentTemplateId]); ``` > 注意:此逻辑与 `ReportEditor.tsx` 中保护 `.image-placeholder` 不被误删的 `handleKeyDown` 思路一致。 ### 变更 3:`src/utils/defaultContent.ts` — 默认模板预置字段控件 **当前第一行(约第 15-23 行):** ```html

姓名:${smartField('patientName')} 性别:${smartField('patientGender')} 年龄:${smartField('patientAge')} 科别:${smartField('department')} 床号:${smartField('bedNumber')} 住院号:${smartField('hospitalId')}

``` 这部分已经在上一版中被替换为 `smartField()`,但需要确认是否末尾有空格问题。由于 `smartField()` 函数本身返回的 HTML 不带 ` `(且是压缩的一行),`defaultContent.ts` 中的这段代码本身没有问题。 **但**:如果之前的 `smartField()` 定义末尾带有 ` `,则需要一并修正。当前 `defaultContent.ts` 中的 `smartField` 定义已经在上一版中被修正为压缩的一行且不带 ` `,所以默认模板本身已经符合要求。 **确认结果**:`defaultContent.ts` 中的第一行在上一版(`2026-04-17-00-13-09`)中已经替换为 `smartField('patientName')` 等智能控件。**本次只需确保 `smartField` 辅助函数的定义与变更 1 保持一致(移除 ` `、压缩为一行、增加 `white-space: nowrap`)即可。** ### 变更 4:`src/index.css` — 增加 `white-space: nowrap` 在 `.smart-field-wrapper` 的样式中增加: ```css .smart-field-wrapper { display: inline-flex; align-items: center; margin: 0 2px; vertical-align: text-bottom; white-space: nowrap; } ``` > 由于 `field-value` 已经通过内联样式设置了 `white-space: nowrap`,给外层 `.smart-field-wrapper` 增加此属性可作为双重保险。 ## 风险点 | 风险 | 级别 | 应对措施 | |------|------|---------| | 移除 ` ` 后,字段与前/后文本之间没有间隔,显得拥挤 | 低 | `margin: 0 2px` 已经提供了 2px 的左右间距,视觉上足够紧凑 | | `keydown` 拦截可能影响编辑器其他正常删除操作 | 低 | 拦截逻辑严格限定在光标前一个节点为 `.smart-field-wrapper` 时才生效,其他情况正常放行 | | 老模板中已插入的字段仍带有 ` ` | 低 | 老模板中的字段只是带有一个额外的空格,不影响功能;用户可手动删除重插 | ## 回滚策略 本次修改范围极小,仅调整 `insertSmartField` 的 HTML 输出和增加一个 `keydown` 事件监听。如出现异常,可直接 `git revert` 回滚。 --- **⚠️ 请审核以上方案,确认无误后回复「确认」或提出修改意见,我将继续编写测试方案。**