Files
Mdeical_Sur_Report/工程分析/需求分析-2026-04-17-13-32-07.md
admin b155dd42d6 fix(TemplateManage): Ctrl+Z undo and smart-field insertion layout
- Intercept Ctrl+Z/Y keyboard shortcuts in keydown listener and route
to custom undoStack/redoStack to fix undo inconsistency.
- Replace execCommand('insertHTML') with precise Range.insertNode()
in insertSmartField to prevent <span> escaping out of <p> when
preceded by <br>.
2026-04-17 13:39:16 +08:00

74 lines
3.6 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.
# 需求分析 — 2026-04-17-13-32-07
## 用户反馈
1. **Ctrl+Z 快捷键无法撤销对 `smart-field-wrapper` 的删除**,但点击工具栏的撤销按钮可以正常撤销。
2. **插入 `smart-field-wrapper` 时仍会分成两行**。用户怀疑是原本文本结构的问题,并提供了出现问题的 HTML 片段:
```html
<p style="font-family: SimSun;">
<strong>手术日期:</strong><br>
</p>
<span class="smart-field-wrapper" ...>...</span>
```
用户补充:把字段插到下面的内容中(即上述 HTML 中已有字段的位置)则没有问题。
---
## 问题 1Ctrl+Z 快捷键撤销失效
### 现状
- `TemplateManage.tsx` 已实现了自定义的 `undoStack` / `redoStack` 以及 `handleUndo()` / `handleRedo()`。
- 工具栏的撤销/重做按钮调用的是自定义函数,能够正确恢复 `innerHTML` 历史快照。
- 键盘按下 `Ctrl+Z` 时,浏览器会触发**原生 `undo`**`document.execCommand('undo')`),它与自定义历史栈完全脱节,因此:
- 若原生栈为空(或已耗尽),则没有任何反应;
- 若原生栈有记录,可能恢复出意料之外的状态。
### 根因
- 当前只在编辑器上拦截了 `Backspace` / `Delete` 键(用于防止误删整段),**没有拦截 `Ctrl+Z` / `Ctrl+Y` 快捷键**。
### 需求
- 在 `TemplateManage.tsx` 的编辑器 `keydown` 事件中,拦截以下快捷键并路由到自定义 Undo/Redo 逻辑:
- `Ctrl+Z` / `Cmd+Z` → `handleUndo()`
- `Ctrl+Shift+Z` / `Cmd+Shift+Z` → `handleRedo()`
- `Ctrl+Y` / `Cmd+Y` → `handleRedo()`
- 拦截后调用 `e.preventDefault()` 阻止浏览器原生行为。
---
## 问题 2插入 smart-field-wrapper 分成两行
### 现状
- `insertSmartField()` 使用 `document.execCommand('insertHTML', false, html)` 插入字段。
- 当光标位于一个以 `<br>` 结尾的 `<p>` 标签末尾时WebKit/Blink 内核的 `insertHTML` 会把 `<span>` 插到 `<p>` **外部**,导致字段独自占据新行。
### 根因
- 用户提供的 HTML 明确显示了这一现象:`<span>` 跑到了 `</p>` 之后。
- `execCommand('insertHTML')` 对块级元素边界(尤其是末尾存在 `<br>` 时)的自动修正行为不可控。
### 需求
- 将 `insertSmartField()` 的插入方式从 `execCommand('insertHTML')` 替换为**精确的 `Range.insertNode()` 手动插入**
1. `restoreSelection()` 恢复光标;
2. 获取当前 `Selection` 的 `Range`
3. `range.deleteContents()`(对 collapsed 光标无实际删除);
4. 将 HTML 字符串转为 `DocumentFragment`
5. `range.insertNode(fragment)` 精确插入到 Range 位置;
6. 把光标移动到插入内容的末尾(最后一个节点之后),保持编辑连贯性。
- 该方式不依赖浏览器的 `execCommand` 自动修正,可避免 `<span>` 被抛到 `<p>` 外部。
---
## 影响范围
- **仅 `src/pages/TemplateManage.tsx`** 需要修改:
- `insertSmartField()` 函数(替换插入逻辑)。
- `keydown` 事件监听 `useEffect`(增加快捷键拦截)。
- 其他页面(`ReportEditor.tsx`、`ReportManage.tsx` 等)不受影响。
---
## 验收标准
1. 在 `TemplateManage` 编辑器中删除一个 `smart-field-wrapper`(通过点击 × 或 Backspace/Delete立即按 `Ctrl+Z`,字段能够完整恢复;按 `Ctrl+Y`(或 `Ctrl+Shift+Z`)能够重做删除。
2. 在 `<p>` 标签末尾(尤其是存在 `<br>` 的情况下)插入 `smart-field-wrapper`,字段与段落保持在同一行,不再被拆成两行。
3. `npm run lint` 通过,无 TypeScript 编译错误。