# 实现方案 — 撤销栈修复、字段删除交互优化与签名字段闭环(2026-04-17-12-34-56) ## 一、修改文件清单 1. `src/pages/TemplateManage.tsx` — 删除逻辑改用 `execCommand('delete')`;插入 HTML 增加零宽空格防换行 2. `src/types.ts` — 修改 `surgeonSignature` 显隐属性;新增 `isSigned` 字段 3. `src/pages/ReportEditor.tsx` — 初始值增加 `isSigned`;签名同步逻辑重构;完成报告签名校验 4. `src/index.css` — 签名图片尺寸约束 5. `src/utils/print.ts` — 打印样式同步签名尺寸约束 ## 二、详细改动 ### 2.1 `src/pages/TemplateManage.tsx` #### A. 点击红 X 删除改用 `execCommand('delete')` ```ts if (smartField && targetEl.closest('.delete-btn')) { e.stopPropagation(); e.preventDefault(); const sel = window.getSelection(); const range = document.createRange(); range.selectNode(smartField); sel?.removeAllRanges(); sel?.addRange(range); document.execCommand('delete'); saveTemplateContent(); return; } ``` #### B. 键盘 Backspace/Delete 改用 `execCommand('delete')` 在 `handleKeyDown` 中,当定位到 `smart-field-wrapper` 目标后: ```ts if (target) { e.preventDefault(); const sel = window.getSelection(); const range = document.createRange(); range.selectNode(target); sel?.removeAllRanges(); sel?.addRange(range); document.execCommand('delete'); saveTemplateContent(); } ``` #### C. 插入 HTML 防换行 在 `insertSmartField` 的 HTML 字符串末尾增加 `​`(零宽空格),作为行内锚点,防止浏览器将字段挤到新行: ```html ...​ ``` ### 2.2 `src/types.ts` - 将 `surgeonSignature` 改为: ```ts { key: 'surgeonSignature', label: '手术者签名', category: '图片', type: 'signature', visibleInForm: true, isSystemLocked: false } ``` - 在 `DEFAULT_FORM_FIELDS` 末尾追加(放在 `surgeonSignature` 之前或之后均可): ```ts { key: 'isSigned', label: '手术者签名确认', category: '单选', type: 'single_select', visibleInForm: true, isSystemLocked: false, options: ['已签字', '未签字'] }, ``` ### 2.3 `src/pages/ReportEditor.tsx` #### A. 初始 `reportData` 增加 `isSigned` ```ts const [reportData, setReportData] = useState>({ // ... 其他字段 isSigned: '未签字', // ... }); ``` #### B. 签名同步逻辑重构 将 `surgeonSignature` 的特殊处理从 `useEffect` 移到更前面的位置,逻辑改为: ```ts if (fieldKey === 'surgeonSignature') { const isSigned = (reportData as any).isSigned === '已签字'; const signatureData = currentUser?.signature; if (isSigned && signatureData) { const imgHtml = `签名`; if (el.innerHTML !== imgHtml) { el.innerHTML = imgHtml; el.style.border = 'none'; el.style.backgroundColor = 'transparent'; } } else { const placeholder = isSigned ? '【请上传电子签】' : '【未签字】'; if (el.innerText !== placeholder) { el.innerText = placeholder; el.style.border = ''; el.style.backgroundColor = ''; } } return; } ``` #### C. 完成报告签名校验 在 `saveReport` 的 `status === 'completed'` 分支中,在现有患者信息校验之后追加: ```ts const hasSignatureField = editorRef.current?.querySelector('[data-bind="surgeonSignature"]'); if (hasSignatureField) { const isSigned = reportData.isSigned === '已签字'; const hasSignatureImage = !!currentUser?.signature; if (!isSigned) { const proceed = window.confirm('提示:模板中包含【手术者签名】字段,但您在基本信息中未选择"已签字"。是否继续完成报告?'); if (!proceed) return; } else if (!hasSignatureImage) { const proceed = window.confirm('提示:您选择了"已签字",但您的账号尚未上传电子签名图片。报告中将不显示签名图片,是否继续完成?'); if (!proceed) return; } } ``` ### 2.4 `src/index.css` 修改 `.report-signature-img`: ```css .report-signature-img { max-width: 120px; max-height: 40px; width: auto; height: auto; object-fit: contain; vertical-align: middle; display: inline-block; } ``` 在 `@media print` 中同步: ```css @media print { .report-signature-img { max-width: 120px !important; max-height: 40px !important; width: auto !important; height: auto !important; object-fit: contain !important; vertical-align: middle !important; display: inline-block !important; } } ``` ### 2.5 `src/utils/print.ts` 在 iframe 的 `