2026-04-18-00-43-19 - 默认模板占位符补充data-mode属性与图片来源隔离对齐

This commit is contained in:
2026-04-18 00:46:05 +08:00
parent 1278f7282f
commit 0df27cbc73
5 changed files with 168 additions and 8 deletions

View File

@@ -3,7 +3,7 @@ const smartField = (key: string) => `<span class="smart-field-wrapper" contented
export const defaultReportContent = ` export const defaultReportContent = `
<!-- 医院Logo --> <!-- 医院Logo -->
<p style="text-align: center; margin-bottom: 16px;" contenteditable="false"> <p style="text-align: center; margin-bottom: 16px;" contenteditable="false">
<span class="image-placeholder" data-placeholder="true" contenteditable="false" style="display:inline-flex;align-items:center;justify-content:center;width:65px;height:65px;border:1px dashed #cbd5e1;background:#f8fafc;vertical-align:middle;margin:0 auto;cursor:pointer;"> <span class="image-placeholder" data-placeholder="true" contenteditable="false" data-mode="manual" style="display:inline-flex;align-items:center;justify-content:center;width:65px;height:65px;border:1px dashed #cbd5e1;background:#f8fafc;vertical-align:middle;margin:0 auto;cursor:pointer;">
<span class="delete-btn" contenteditable="false">×</span> <span class="delete-btn" contenteditable="false">×</span>
<span class="placeholder-text" style="color:#94a3b8;font-size:11px;pointer-events:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;">插入图片</span> <span class="placeholder-text" style="color:#94a3b8;font-size:11px;pointer-events:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;">插入图片</span>
</span> </span>
@@ -87,21 +87,21 @@ export const defaultReportContent = `
<table style="width: 100%; border-collapse: collapse; margin: 20px 0; table-layout: fixed;"> <table style="width: 100%; border-collapse: collapse; margin: 20px 0; table-layout: fixed;">
<tbody><tr> <tbody><tr>
<td style="width: 33%; text-align: center; padding: 10px; vertical-align: top; border: 1px solid #e2e8f0;"> <td style="width: 33%; text-align: center; padding: 10px; vertical-align: top; border: 1px solid #e2e8f0;">
<span class="image-placeholder" data-placeholder="true" contenteditable="false" style="display:inline-flex;align-items:center;justify-content:center;width:100%;height:150px;border:1px dashed #cbd5e1;background:#f8fafc;vertical-align:middle;cursor:pointer;"> <span class="image-placeholder" data-placeholder="true" contenteditable="false" data-mode="frame" style="display:inline-flex;align-items:center;justify-content:center;width:100%;height:150px;border:1px dashed #cbd5e1;background:#f8fafc;vertical-align:middle;cursor:pointer;">
<span class="delete-btn" contenteditable="false">×</span> <span class="delete-btn" contenteditable="false">×</span>
<span class="placeholder-text" style="color:#94a3b8;font-size:11px;pointer-events:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;">插入/点击放置图片</span> <span class="placeholder-text" style="color:#94a3b8;font-size:11px;pointer-events:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;">插入/点击放置图片</span>
</span> </span>
<p style="color: #64748b; font-size: 13px; margin: 0;">图A 腹腔镜探查</p> <p style="color: #64748b; font-size: 13px; margin: 0;">图A 腹腔镜探查</p>
</td> </td>
<td style="width: 33%; text-align: center; padding: 10px; vertical-align: top; border: 1px solid #e2e8f0;"> <td style="width: 33%; text-align: center; padding: 10px; vertical-align: top; border: 1px solid #e2e8f0;">
<span class="image-placeholder" data-placeholder="true" contenteditable="false" style="display:inline-flex;align-items:center;justify-content:center;width:100%;height:150px;border:1px dashed #cbd5e1;background:#f8fafc;vertical-align:middle;cursor:pointer;"> <span class="image-placeholder" data-placeholder="true" contenteditable="false" data-mode="frame" style="display:inline-flex;align-items:center;justify-content:center;width:100%;height:150px;border:1px dashed #cbd5e1;background:#f8fafc;vertical-align:middle;cursor:pointer;">
<span class="delete-btn" contenteditable="false">×</span> <span class="delete-btn" contenteditable="false">×</span>
<span class="placeholder-text" style="color:#94a3b8;font-size:11px;pointer-events:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;">插入/点击放置图片</span> <span class="placeholder-text" style="color:#94a3b8;font-size:11px;pointer-events:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;">插入/点击放置图片</span>
</span> </span>
<p style="color: #64748b; font-size: 13px; margin: 0;">图B 胆囊管夹闭与离断</p> <p style="color: #64748b; font-size: 13px; margin: 0;">图B 胆囊管夹闭与离断</p>
</td> </td>
<td style="width: 33%; text-align: center; padding: 10px; vertical-align: top; border: 1px solid #e2e8f0;"> <td style="width: 33%; text-align: center; padding: 10px; vertical-align: top; border: 1px solid #e2e8f0;">
<span class="image-placeholder" data-placeholder="true" contenteditable="false" style="display:inline-flex;align-items:center;justify-content:center;width:100%;height:150px;border:1px dashed #cbd5e1;background:#f8fafc;vertical-align:middle;cursor:pointer;"> <span class="image-placeholder" data-placeholder="true" contenteditable="false" data-mode="frame" style="display:inline-flex;align-items:center;justify-content:center;width:100%;height:150px;border:1px dashed #cbd5e1;background:#f8fafc;vertical-align:middle;cursor:pointer;">
<span class="delete-btn" contenteditable="false">×</span> <span class="delete-btn" contenteditable="false">×</span>
<span class="placeholder-text" style="color:#94a3b8;font-size:11px;pointer-events:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;">插入/点击放置图片</span> <span class="placeholder-text" style="color:#94a3b8;font-size:11px;pointer-events:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;">插入/点击放置图片</span>
</span> </span>
@@ -110,21 +110,21 @@ export const defaultReportContent = `
</tr> </tr>
<tr> <tr>
<td style="width: 33%; text-align: center; padding: 10px; vertical-align: top; border: 1px solid #e2e8f0;"> <td style="width: 33%; text-align: center; padding: 10px; vertical-align: top; border: 1px solid #e2e8f0;">
<span class="image-placeholder" data-placeholder="true" contenteditable="false" style="display:inline-flex;align-items:center;justify-content:center;width:100%;height:150px;border:1px dashed #cbd5e1;background:#f8fafc;vertical-align:middle;cursor:pointer;"> <span class="image-placeholder" data-placeholder="true" contenteditable="false" data-mode="frame" style="display:inline-flex;align-items:center;justify-content:center;width:100%;height:150px;border:1px dashed #cbd5e1;background:#f8fafc;vertical-align:middle;cursor:pointer;">
<span class="delete-btn" contenteditable="false">×</span> <span class="delete-btn" contenteditable="false">×</span>
<span class="placeholder-text" style="color:#94a3b8;font-size:11px;pointer-events:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;">插入/点击放置图片</span> <span class="placeholder-text" style="color:#94a3b8;font-size:11px;pointer-events:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;">插入/点击放置图片</span>
</span> </span>
<p style="color: #64748b; font-size: 13px; margin: 0;">图D 胆囊剥离与床面止血</p> <p style="color: #64748b; font-size: 13px; margin: 0;">图D 胆囊剥离与床面止血</p>
</td> </td>
<td style="width: 33%; text-align: center; padding: 10px; vertical-align: top; border: 1px solid #e2e8f0;"> <td style="width: 33%; text-align: center; padding: 10px; vertical-align: top; border: 1px solid #e2e8f0;">
<span class="image-placeholder" data-placeholder="true" contenteditable="false" style="display:inline-flex;align-items:center;justify-content:center;width:100%;height:150px;border:1px dashed #cbd5e1;background:#f8fafc;vertical-align:middle;cursor:pointer;"> <span class="image-placeholder" data-placeholder="true" contenteditable="false" data-mode="frame" style="display:inline-flex;align-items:center;justify-content:center;width:100%;height:150px;border:1px dashed #cbd5e1;background:#f8fafc;vertical-align:middle;cursor:pointer;">
<span class="delete-btn" contenteditable="false">×</span> <span class="delete-btn" contenteditable="false">×</span>
<span class="placeholder-text" style="color:#94a3b8;font-size:11px;pointer-events:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;">插入/点击放置图片</span> <span class="placeholder-text" style="color:#94a3b8;font-size:11px;pointer-events:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;">插入/点击放置图片</span>
</span> </span>
<p style="color: #64748b; font-size: 13px; margin: 0;">图E 胆囊取出与钛夹确认</p> <p style="color: #64748b; font-size: 13px; margin: 0;">图E 胆囊取出与钛夹确认</p>
</td> </td>
<td style="width: 33%; text-align: center; padding: 10px; vertical-align: top; border: 1px solid #e2e8f0;"> <td style="width: 33%; text-align: center; padding: 10px; vertical-align: top; border: 1px solid #e2e8f0;">
<span class="image-placeholder" data-placeholder="true" contenteditable="false" style="display:inline-flex;align-items:center;justify-content:center;width:100%;height:150px;border:1px dashed #cbd5e1;background:#f8fafc;vertical-align:middle;cursor:pointer;"> <span class="image-placeholder" data-placeholder="true" contenteditable="false" data-mode="frame" style="display:inline-flex;align-items:center;justify-content:center;width:100%;height:150px;border:1px dashed #cbd5e1;background:#f8fafc;vertical-align:middle;cursor:pointer;">
<span class="delete-btn" contenteditable="false">×</span> <span class="delete-btn" contenteditable="false">×</span>
<span class="placeholder-text" style="color:#94a3b8;font-size:11px;pointer-events:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;">插入/点击放置图片</span> <span class="placeholder-text" style="color:#94a3b8;font-size:11px;pointer-events:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;">插入/点击放置图片</span>
</span> </span>
@@ -151,7 +151,7 @@ export const defaultReportContent = `
</p> </p>
<p style="font-family: SimSun;"> <p style="font-family: SimSun;">
手术者签名:<span class="image-placeholder" data-placeholder="true" contenteditable="false" style="display:inline-flex;align-items:center;justify-content:center;width:200px;height:40px;border:1px dashed #cbd5e1;background:#f8fafc;vertical-align:middle;margin:0 4px;cursor:pointer;"><span class="delete-btn" contenteditable="false">×</span><span class="placeholder-text" style="color:#94a3b8;font-size:11px;pointer-events:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;">插入图片</span></span> 手术者签名:<span class="image-placeholder" data-placeholder="true" contenteditable="false" data-mode="manual" style="display:inline-flex;align-items:center;justify-content:center;width:200px;height:40px;border:1px dashed #cbd5e1;background:#f8fafc;vertical-align:middle;margin:0 4px;cursor:pointer;"><span class="delete-btn" contenteditable="false">×</span><span class="placeholder-text" style="color:#94a3b8;font-size:11px;pointer-events:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;">插入/点击放置图片</span></span>
</p> </p>
<p style="text-align: right; font-family: SimSun;"> <p style="text-align: right; font-family: SimSun;">

View File

@@ -0,0 +1,74 @@
# 实现方案 — 2026-04-18-00-43-19
## 根因分析
此前对「插入图片占位符」进行了弹窗改造,生成的占位符 HTML 新增了 `data-mode="frame|manual"` 属性,用于区分手术影像占位(允许拖拽/自动插入关键帧)和静态图片占位(仅允许点击上传/签名/素材)。
`defaultContent.ts` 中的默认模板仍使用旧版 `image-placeholder` 结构,**缺少 `data-mode` 属性**。这导致:
- 默认模板中的签名、Logo 等静态占位符在新建报告时,可被关键帧拖拽误填充。
- `autoCaptureFrames``insertFrameToPlaceholder` 等逻辑通过 `:not([data-mode="manual"])` 选择器过滤时,无该属性的占位符会被错误地当作手术影像占位处理。
## 修改文件清单
- `src/utils/defaultContent.ts`
## 具体代码变更
### 一、医院 Logo 占位符line 6
原结构:
```html
<span class="image-placeholder" data-placeholder="true" contenteditable="false" style="display:inline-flex;align-items:center;justify-content:center;width:65px;height:65px;border:1px dashed #cbd5e1;background:#f8fafc;vertical-align:middle;margin:0 auto;cursor:pointer;">
<span class="delete-btn" contenteditable="false">×</span>
<span class="placeholder-text" style="color:#94a3b8;font-size:11px;pointer-events:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;">插入图片</span>
</span>
```
新结构(仅添加 `data-mode="manual"`,宽高及布局不变):
```html
<span class="image-placeholder" data-placeholder="true" contenteditable="false" data-mode="manual" style="display:inline-flex;align-items:center;justify-content:center;width:65px;height:65px;border:1px dashed #cbd5e1;background:#f8fafc;vertical-align:middle;margin:0 auto;cursor:pointer;">
<span class="delete-btn" contenteditable="false">×</span>
<span class="placeholder-text" style="color:#94a3b8;font-size:11px;pointer-events:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;">插入图片</span>
</span>
```
### 二、表格内术中影像占位符lines 90/97/104/113/120/127共 6 处)
原结构:
```html
<span class="image-placeholder" data-placeholder="true" contenteditable="false" style="display:inline-flex;align-items:center;justify-content:center;width:100%;height:150px;border:1px dashed #cbd5e1;background:#f8fafc;vertical-align:middle;cursor:pointer;">
<span class="delete-btn" contenteditable="false">×</span>
<span class="placeholder-text" style="color:#94a3b8;font-size:11px;pointer-events:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;">插入/点击放置图片</span>
</span>
```
新结构(仅添加 `data-mode="frame"`,宽高及布局不变):
```html
<span class="image-placeholder" data-placeholder="true" contenteditable="false" data-mode="frame" style="display:inline-flex;align-items:center;justify-content:center;width:100%;height:150px;border:1px dashed #cbd5e1;background:#f8fafc;vertical-align:middle;cursor:pointer;">
<span class="delete-btn" contenteditable="false">×</span>
<span class="placeholder-text" style="color:#94a3b8;font-size:11px;pointer-events:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;">插入/点击放置图片</span>
</span>
```
### 三、手术者签名占位符line 154
原结构:
```html
<span class="image-placeholder" data-placeholder="true" contenteditable="false" style="display:inline-flex;align-items:center;justify-content:center;width:200px;height:40px;border:1px dashed #cbd5e1;background:#f8fafc;vertical-align:middle;margin:0 4px;cursor:pointer;"><span class="delete-btn" contenteditable="false">×</span><span class="placeholder-text" style="color:#94a3b8;font-size:11px;pointer-events:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;">插入图片</span></span>
```
新结构(添加 `data-mode="manual"`,并将提示文本改为「插入/点击放置图片」,因为 width=200px ≥ 80px
```html
<span class="image-placeholder" data-placeholder="true" contenteditable="false" data-mode="manual" style="display:inline-flex;align-items:center;justify-content:center;width:200px;height:40px;border:1px dashed #cbd5e1;background:#f8fafc;vertical-align:middle;margin:0 4px;cursor:pointer;"><span class="delete-btn" contenteditable="false">×</span><span class="placeholder-text" style="color:#94a3b8;font-size:11px;pointer-events:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;">插入/点击放置图片</span></span>
```
## 风险点与应对措施
| 风险 | 应对措施 |
|------|---------|
| 修改默认模板后,新建报告的布局发生偏移 | 仅添加 `data-mode` 属性并修改文本,保持 `style``width/height/margin/display` 等所有布局属性绝对不变。 |
| 默认模板中占位符是 `<span>`,而新弹窗在表格内生成 `<div>` | 用户明确要求「只保存当前框的大小不变」,因此不改动标签类型,保持 `<span>` 以避免表格布局被破坏。 |
## 回滚策略
- 仅修改单个文件 `src/utils/defaultContent.ts`,回滚时直接还原该文件即可。

View File

@@ -0,0 +1,32 @@
# 测试方案 — 2026-04-18-00-43-19
## 测试目标
验证默认模板 `defaultContent.ts` 中的全部 `.image-placeholder` 已正确添加 `data-mode` 属性,且尺寸、布局与原有模板保持一致。
## 测试环境
- 本地开发服务器:`npm run dev`(端口 3000
- 浏览器Chrome / Edge
- 测试账号:`admin` / `123456`
## 测试用例
| 编号 | 场景 | 操作步骤 | 预期结果 |
|------|------|---------|---------|
| TC-01 | 默认模板 Logo 占位符 | 1. 登录后新建报告(不选择任何模板,加载默认模板)。<br>2. 查看编辑器顶部的医院 Logo 占位符。 | 占位符尺寸仍为 65×65pxDOM 中可见 `data-mode="manual"`;从右侧视频分析面板拖拽关键帧到 Logo 占位符时,弹出提示「此处为静态图片占位符...」并拒绝插入。 |
| TC-02 | 默认模板签名占位符 | 1. 新建报告,滚动到底部「手术者签名」处。<br>2. 查看占位符 DOM。 | 占位符尺寸仍为 200×40pxDOM 中可见 `data-mode="manual"`;提示文本为「插入/点击放置图片」;拖拽关键帧到签名区域时被拦截。 |
| TC-03 | 默认模板表格内影像占位符 | 1. 新建报告,查看「手术图片说明表格」中的 6 个占位符。<br>2. 检查 DOM。 | 每个占位符尺寸仍为 100%×150pxDOM 中可见 `data-mode="frame"`;从右侧拖拽关键帧到表格占位符时,可正常插入。 |
| TC-04 | 自动帧插入过滤 | 1. 新建报告,确保表格内和签名/Logo 占位符均为空。<br>2. 上传视频并开启「自动帧插入」。<br>3. 观察自动插入行为。 | 自动插入的关键帧只会填充表格内 `data-mode="frame"` 的占位符;不会填充 `data-mode="manual"` 的 Logo 和签名占位符。 |
| TC-05 | 布局无偏移 | 1. 对比修改前后的默认模板预览效果(或打印预览)。 | 所有占位符的位置、大小、边框、背景色与修改前完全一致,无可见差异。 |
## 验收标准
- [ ] TC-01 ~ TC-03默认模板中 8 个占位符均已正确添加 `data-mode`,尺寸未改变。
- [ ] TC-04自动帧插入和拖拽逻辑对 `manual` / `frame` 的隔离生效。
- [ ] TC-05视觉和排版与修改前完全一致。
- [ ] `npm run lint` 无 TypeScript 编译错误。
## 测试方式
手工验证。通过浏览器 DevTools 检查 DOM 属性,并通过拖拽/自动插入验证隔离逻辑。

View File

@@ -920,3 +920,25 @@ if ((settings.autoInsertDelay || 0) > 0) {
- 当同一填充逻辑存在多个入口(点击上传、拖拽、自动插入)时,务必确保所有入口的后续处理完全一致,避免某一路径遗漏样式清除。 - 当同一填充逻辑存在多个入口(点击上传、拖拽、自动插入)时,务必确保所有入口的后续处理完全一致,避免某一路径遗漏样式清除。
- 原生 `prompt`/`confirm`/`alert` 在现代 Web 应用中应尽量避免使用,优先采用自定义 Modal 组件,以获得一致的视觉体验和更灵活的控制能力。 - 原生 `prompt`/`confirm`/`alert` 在现代 Web 应用中应尽量避免使用,优先采用自定义 Modal 组件,以获得一致的视觉体验和更灵活的控制能力。
- 当系统中存在"自动填充"机制时,应考虑为被填充的容器增加分类标记(如 `data-mode`),并在自动填充逻辑中通过选择器过滤,防止无关区域被污染。 - 当系统中存在"自动填充"机制时,应考虑为被填充的容器增加分类标记(如 `data-mode`),并在自动填充逻辑中通过选择器过滤,防止无关区域被污染。
---
## 记录 30默认模板中 image-placeholder 缺少 data-mode 导致来源隔离失效
**A. 具体问题**
默认模板 `defaultContent.ts` 中的 8 个 `.image-placeholder`(医院 Logo、6 个表格内术中影像、手术者签名)使用的是旧版 HTML 结构,缺少 `data-mode="frame|manual"` 属性。新建报告加载默认模板后,签名和 Logo 区域可被关键帧拖拽误填充;自动帧插入时也会将术中截图插入签名位置。
**B. 产生问题原因**
此前对「插入图片占位符」进行弹窗改造时,仅在运行时插入逻辑中新增了 `data-mode` 属性,但未同步回刷默认模板 `defaultContent.ts`。导致默认模板产出的占位符与新插入的占位符结构不一致,图片来源隔离机制在默认模板场景下完全失效。
**C. 解决问题方案**
在 `defaultContent.ts` 中对 8 个占位符做最小化修补:
1. 医院 Logo65×65和手术者签名200×40添加 `data-mode="manual"`,标记为静态图片占位。
2. 表格内 6 个术中影像占位符100%×150添加 `data-mode="frame"`,标记为手术影像占位。
3. 签名占位符宽度 200px ≥ 80px按新弹窗规则将提示文本从「插入图片」更新为「插入/点击放置图片」。
4. 所有占位符的 `width/height/margin/display` 等布局属性绝对保持不变。
**D. 后续如何避免问题**
- 当为 `image-placeholder` 引入新的核心属性(如 `data-mode`、`data-allow-source`)时,必须同步检索 `defaultContent.ts` 和任何预置模板文件,确保静态模板中的占位符结构与运行时插入逻辑保持一致。
- 默认模板修改后,应通过「新建报告 → 检查 DOM」快速验证所有占位符是否携带了最新属性。

View File

@@ -0,0 +1,32 @@
# 需求分析 — 2026-04-18-00-43-19
## 原始需求摘要
用户反馈默认模板里的 `class="image-placeholder"` 有问题,要求将默认模板中全部 `image-placeholder` 替换为「按动插入图片占位符之后的状态」,且**只保留当前框的大小不变**。
## 需求拆解
### 功能点
- **F1**:分析默认模板 `defaultContent.ts` 中所有 `.image-placeholder` 的当前结构与新弹窗插入逻辑生成结构的差异。
- **F2**:为默认模板中所有 `.image-placeholder` 补充 `data-mode` 属性,使其与新的图片来源隔离机制兼容:
- 医院 Logo、手术者签名 → `data-mode="manual"`(静态图片占位,仅支持点击插入,禁止拖入关键帧)
- 表格内术中影像占位符 → `data-mode="frame"`(手术影像占位,支持拖拽/自动关键帧插入)
- **F3**更新签名占位符的提示文本使其符合新弹窗的宽度阈值规则width ≥ 80 时显示「插入/点击放置图片」)。
- **F4**:保持所有占位符的现有 `width``height` 及外围布局标签类型、margin、容器结构绝对不变。
### 非功能点
- 向后兼容:默认模板仅影响新建报告,已有报告不受影响。
- 最小侵入:仅修改 `defaultContent.ts`,不动任何 TSX/JS 逻辑。
- `npm run lint` 零错误。
## 影响范围
| 模块 | 影响程度 | 说明 |
|------|---------|------|
| `src/utils/defaultContent.ts` | 高 | 修改 8 个 `image-placeholder` 的 HTML 结构,补充 `data-mode` 及文本。 |
## 待确认问题
无。用户已明确无需人工二次确认。