feat: add 4 post-op fields to default template with auto-merge for existing configs

- Add postOpCondition, specimenDescription, pathologySent, frozenPathology
  to Report type and DEFAULT_FORM_FIELDS
- Replace static placeholders in defaultContent.ts with smartField() calls
- Auto-merge missing fields from DEFAULT_FORM_FIELDS into existing
  formFieldsConfig in ReportEditor and TemplateManage
This commit is contained in:
2026-04-17 10:11:48 +08:00
parent 38ff67a6a8
commit 9e75e86454
8 changed files with 221 additions and 6 deletions

View File

@@ -110,8 +110,14 @@ export default function ReportEditor() {
if (savedAnesthesia) setAnesthesiaOptions(savedAnesthesia);
const savedFields = storage.get<FormField[]>('formFieldsConfig', []);
let mergedFields = savedFields;
if (savedFields.length > 0) {
setFormFields(savedFields);
const missing = DEFAULT_FORM_FIELDS.filter(def => !savedFields.some(f => f.key === def.key));
if (missing.length > 0) {
mergedFields = [...savedFields, ...missing];
storage.set('formFieldsConfig', mergedFields);
}
setFormFields(mergedFields);
} else {
setFormFields(DEFAULT_FORM_FIELDS);
storage.set('formFieldsConfig', DEFAULT_FORM_FIELDS);

View File

@@ -41,8 +41,14 @@ export default function TemplateManage() {
setCurrentUser(user);
const savedFields = storage.get<FormField[]>('formFieldsConfig', []);
let mergedFields = savedFields;
if (savedFields.length > 0) {
setFormFields(savedFields);
const missing = DEFAULT_FORM_FIELDS.filter(def => !savedFields.some(f => f.key === def.key));
if (missing.length > 0) {
mergedFields = [...savedFields, ...missing];
storage.set('formFieldsConfig', mergedFields);
}
setFormFields(mergedFields);
} else {
setFormFields(DEFAULT_FORM_FIELDS);
storage.set('formFieldsConfig', DEFAULT_FORM_FIELDS);

View File

@@ -30,6 +30,10 @@ export interface Report {
assistant?: string[];
anesthesiologist?: string[];
anesthesiaType?: string;
postOpCondition?: string;
specimenDescription?: string;
pathologySent?: string;
frozenPathology?: string;
reportNote?: string;
content: string;
videos?: {
@@ -129,4 +133,8 @@ export const DEFAULT_FORM_FIELDS: FormField[] = [
{ key: 'assistant', label: '助手', category: '多选', type: 'multi_select', visibleInForm: true, isSystemLocked: false, options: ['赵医生', '钱医生', '孙医生'] },
{ key: 'anesthesiologist', label: '麻醉师', category: '多选', type: 'multi_select', visibleInForm: true, isSystemLocked: false, options: ['周医生', '吴医生', '郑医生'] },
{ key: 'anesthesiaType', label: '麻醉方式', category: '单选', type: 'single_select', visibleInForm: true, isSystemLocked: false, options: ['全麻', '局麻', '腰麻', '硬膜外麻醉', '静脉麻醉', '吸入麻醉'] },
{ key: 'postOpCondition', label: '手术后情况', category: '单选', type: 'single_select', visibleInForm: true, isSystemLocked: false, options: ['患者麻醉恢复后安返病房'] },
{ key: 'specimenDescription', label: '切除标本描述', category: '单选', type: 'single_select', visibleInForm: true, isSystemLocked: false, options: [] },
{ key: 'pathologySent', label: '是否送检病理', category: '单选', type: 'single_select', visibleInForm: true, isSystemLocked: false, options: ['是', '否'] },
{ key: 'frozenPathology', label: '冰冻病理结果', category: '填空', type: 'text', visibleInForm: true, isSystemLocked: false },
];

View File

@@ -132,19 +132,19 @@ export const defaultReportContent = `
<div class="template-info-section">
<p style="font-family: SimSun;">
<strong>手术后情况</strong>患者麻醉恢复后安返病房
<strong>手术后情况</strong>${smartField('postOpCondition')}
</p>
<p style="font-family: SimSun;">
<strong>切除标本描述</strong><span style="color: #bdbdbd;">切除标本描述</span>
<strong>切除标本描述</strong>${smartField('specimenDescription')}
</p>
<p style="font-family: SimSun;">
<strong>是否送病理检查</strong>
<strong>是否送病理检查</strong>${smartField('pathologySent')}
</p>
<p style="font-family: SimSun;">
<strong>冰冻病理结果</strong><span style="color: #bdbdbd;">冰冻病理结果</span>
<strong>冰冻病理结果</strong>${smartField('frozenPathology')}
</p>
<p style="font-family: SimSun;">

View File

@@ -0,0 +1,73 @@
# 实现方案 — 模板新增术后字段2026-04-17-09-57-08
## 一、修改文件清单
1. `src/types.ts` — 扩展 `Report` 类型 + 追加 `DEFAULT_FORM_FIELDS`
2. `src/utils/defaultContent.ts` — 替换默认模板中的 4 处占位文本为 `smartField()`
3. `src/pages/ReportEditor.tsx` — 增加 `formFieldsConfig` 自动补全/升级逻辑
4. `src/pages/TemplateManage.tsx` — 增加同样的自动补全/升级逻辑
## 二、详细改动
### 2.1 `src/types.ts`
#### A. `Report` 接口追加字段
```ts
postOpCondition?: string;
specimenDescription?: string;
pathologySent?: string;
frozenPathology?: string;
```
#### B. `DEFAULT_FORM_FIELDS` 追加 4 条
```ts
{ key: 'postOpCondition', label: '手术后情况', category: '单选', type: 'single_select', visibleInForm: true, isSystemLocked: false, options: ['患者麻醉恢复后安返病房'] },
{ key: 'specimenDescription', label: '切除标本描述', category: '单选', type: 'single_select', visibleInForm: true, isSystemLocked: false, options: [] },
{ key: 'pathologySent', label: '是否送检病理', category: '单选', type: 'single_select', visibleInForm: true, isSystemLocked: false, options: ['是', '否'] },
{ key: 'frozenPathology', label: '冰冻病理结果', category: '填空', type: 'text', visibleInForm: true, isSystemLocked: false },
```
### 2.2 `src/utils/defaultContent.ts`
将模板底部 `template-info-section` 中的 4 处文本替换:
- `患者麻醉恢复后安返病房``${smartField('postOpCondition')}`
- `<span style="color: #bdbdbd;">切除标本描述</span>``${smartField('specimenDescription')}`
- `是``${smartField('pathologySent')}`
- `<span style="color: #bdbdbd;">冰冻病理结果</span>``${smartField('frozenPathology')}`
### 2.3 `src/pages/ReportEditor.tsx`
在初始化 `useEffect` 中,读取 `formFieldsConfig` 后增加**字段合并逻辑**
```ts
const savedFields = storage.get<FormField[]>('formFieldsConfig', []);
let mergedFields = savedFields;
if (savedFields.length > 0) {
const missing = DEFAULT_FORM_FIELDS.filter(def => !savedFields.some(f => f.key === def.key));
if (missing.length > 0) {
mergedFields = [...savedFields, ...missing];
storage.set('formFieldsConfig', mergedFields);
}
setFormFields(mergedFields);
} else {
setFormFields(DEFAULT_FORM_FIELDS);
storage.set('formFieldsConfig', DEFAULT_FORM_FIELDS);
}
```
### 2.4 `src/pages/TemplateManage.tsx`
在同样的初始化位置执行完全相同的合并逻辑,保证两个页面中的字段配置一致性。
## 三、风险与回滚
- **风险**:修改 `DEFAULT_FORM_FIELDS` 和默认模板后,新用户直接生效;老用户通过合并逻辑无痛升级。
- **回滚**:如出现问题,可将 `DEFAULT_FORM_FIELDS` 恢复为 14 条,并回滚 `defaultContent.ts` 中的 4 处替换。
## 四、验证计划
1. 清空浏览器 localStorage 后刷新,确认新建报告时模板底部 4 处为可编辑方框。
2. 在右侧表单中操作 4 个新控件,观察模板方框实时同步。
3. 在模板方框中直接输入,观察右侧表单实时同步。
4. 预先写入旧版 `formFieldsConfig`14 条),刷新页面,确认自动补全为 18 条。

View File

@@ -0,0 +1,48 @@
# 测试方案 — 模板新增术后字段2026-04-17-09-57-08
## 一、编译检查
- 执行 `npm run lint``tsc --noEmit`),确保全量 TypeScript 无编译错误。
## 二、功能验证步骤
### 测试 1新用户首次使用默认模板加载
1. 浏览器 DevTools → Application → Local Storage → 清空本站所有 key。
2. 刷新页面,用 `admin / 123456` 登录。
3. 进入【模板管理】→ 确认默认模板底部 4 处文本已变为可编辑的智能方框:
- 手术后情况
- 切除标本描述
- 是否送检病理
- 冰冻病理结果
4. 点击【新建报告】→ 确认右侧【基本信息】表单出现:
- 手术后情况(下拉单选,默认选项 1 条)
- 切除标本描述(下拉单选,暂无选项,显示"暂无选项"
- 是否送检病理(下拉单选,选项:是 / 否)
- 冰冻病理结果(文本输入框)
5. 在表单中修改"是否送检病理"为"否",观察编辑器中对应方框实时变为"否"。
6. 在编辑器中直接点击"冰冻病理结果"方框并输入"良性",观察右侧表单实时变为"良性"。
### 测试 2老用户字段升级自动补全
1. DevTools Console 执行:
```js
localStorage.setItem('formFieldsConfig', JSON.stringify([
{ key: 'patientName', label: '患者姓名', category: '填空', type: 'text', visibleInForm: true, isSystemLocked: true },
{ key: 'hospitalId', label: '住院号', category: '填空', type: 'text', visibleInForm: true, isSystemLocked: true }
]));
```
2. 刷新页面,进入【新建报告】。
3. 确认右侧表单中除了"患者姓名"、"住院号"外,还自动出现了本次新增的 4 个字段以及其他默认字段(共 18 条)。
4. DevTools 中查看 `formFieldsConfig`,确认 key 列表包含 `postOpCondition`、`specimenDescription`、`pathologySent`、`frozenPathology`。
### 测试 3持久化验证
1. 在【新建报告】中填写 4 个新字段的值。
2. 切换到【报告管理】再返回【新建报告】。
3. 确认 4 个新字段的值在表单和编辑器中均被正确恢复。
## 三、预期结果
- `npm run lint` 0 错误。
- 新建报告时模板底部 4 处为智能方框,右侧表单出现对应控件。
- 表单 ↔ 编辑器双向同步正常。
- 老用户自动补全字段成功,无数据丢失。
- 路由切换后4 个新字段的值能被自动保存/恢复。

View File

@@ -345,6 +345,35 @@ if ((settings.autoInsertDelay || 0) > 0) {
---
## 记录 15默认模板新增术后 4 个字段并自动补全老用户配置
**A. 具体问题**
需要在默认手术报告模板中新增 4 个字段(手术后情况、切除标本描述、是否送检病理、冰冻病理结果),使其与右侧【基本信息】动态表单双向联动。同时,老用户浏览器 localStorage 中已存在的 `formFieldsConfig` 不包含这 4 个字段,直接发版会导致老用户看不到新控件。
**B. 产生问题原因**
1. `DEFAULT_FORM_FIELDS` 和 `defaultContent.ts` 是代码层面的默认值,但 `ReportEditor.tsx` 和 `TemplateManage.tsx` 的初始化逻辑是"若 localStorage 中已存在 `formFieldsConfig`,则直接使用,不再回退到代码默认值"。
2. 因此新增字段只对新用户localStorage 为空)生效,老用户的字段列表会永远停留在旧版本。
**C. 解决问题方案**
1. 在 `src/types.ts` 的 `Report` 接口中追加 4 个新字段;在 `DEFAULT_FORM_FIELDS` 末尾追加 4 条配置(含 `options`)。
2. 在 `src/utils/defaultContent.ts` 中,将模板底部对应 4 处静态文本/占位色 `<span>` 替换为 `${smartField('key')}`。
3. 在 `ReportEditor.tsx` 和 `TemplateManage.tsx` 的初始化 `useEffect` 中,读取 `formFieldsConfig` 后增加**缺失字段自动补全逻辑**
```ts
const missing = DEFAULT_FORM_FIELDS.filter(def => !savedFields.some(f => f.key === def.key));
if (missing.length > 0) {
mergedFields = [...savedFields, ...missing];
storage.set('formFieldsConfig', mergedFields);
}
```
这样老用户首次进入页面时,缺失字段会自动追加到列表末尾,同时保留用户已有的自定义字段和显隐设置。
**D. 后续如何避免问题**
- 任何依赖 `localStorage` 持久化配置的"字段/菜单/权限"列表,在后续版本新增默认值时,都应提供**自动合并/升级逻辑**,而不是简单依赖"有值则跳过"。
- 合并策略推荐:以代码层 `DEFAULT_xxx` 为基准,检测缺失 key 并追加,保留用户已有的自定义项和状态,实现无痛升级。
- 对于模板 HTML 的变更,若旧模板已存入 `templates` localStorage则默认模板只影响新建报告若需要更新已有模板需额外提供模板升级脚本或管理员手动维护机制。
---
## 记录 14智能字段插入间距修复与 Backspace 防误删
**A. 具体问题**

View File

@@ -0,0 +1,45 @@
# 需求分析 — 模板新增术后字段2026-04-17-09-57-08
## 一、需求来源
用户要求在默认手术报告模板中进一步插入 4 个新字段,并使其与右侧【基本信息】动态表单联动。
## 二、具体字段清单
| 序号 | 字段名称 | 字段 key | 类型 | 分类 | 选项 | 表单显隐 |
|------|----------|----------|------|------|------|----------|
| 1 | 手术后情况 | `postOpCondition` | 单选 | 单选 | `患者麻醉恢复后安返病房` | 显示 |
| 2 | 切除标本描述 | `specimenDescription` | 单选 | 单选 | 暂无(空数组) | 显示 |
| 3 | 是否送检病理 | `pathologySent` | 单选 | 单选 | `是`, `否` | 显示 |
| 4 | 冰冻病理结果 | `frozenPathology` | 填空 | text | — | 显示 |
## 三、影响范围分析
1. **`src/types.ts`**
- 需要在 `Report` 接口中新增 4 个字段定义,保持类型安全。
- 需要在 `DEFAULT_FORM_FIELDS` 数组末尾追加 4 条 `FormField` 配置。
2. **`src/utils/defaultContent.ts`**
- 默认模板 HTML 中,当前"手术后情况"、"切除标本描述"、"是否送病理检查"、"冰冻病理结果"均为纯文本或占位色 `<span>`
- 需要将这 4 处替换为 `${smartField('key')}` 调用,使其具备双向绑定能力。
3. **`src/pages/ReportEditor.tsx`**
- 右侧表单已经是"配置驱动渲染"(遍历 `formFieldsConfig``type` switch-case新增字段后无需修改 JSX。
- 表单 → 编辑器的同步 `useEffect` 使用 `(reportData as any)[fieldKey]` 读取,对新字段自动生效。
- 编辑器 → 表单的 `handleEditorInput` 通过 `data-bind` 属性反射,同样自动生效。
4. **`src/pages/TemplateManage.tsx`**
- 字段库侧边栏读取 `formFieldsConfig`,新增配置后自动显示在【插入字段】列表中。
- 无需额外修改。
## 四、兼容性说明
- 旧用户 localStorage 中已存在 `formFieldsConfig`,如果其中不包含新字段,则首次进入 `ReportEditor`/`TemplateManage` 时只会显示旧字段。
- **策略**:由于系统当前逻辑是"若 `formFieldsConfig` 有值则直接使用,不再回退到 `DEFAULT_FORM_FIELDS`",因此需要额外提供一个**字段合并/升级逻辑**,在初始化时检测缺失字段并自动补入,确保老用户也能看到这 4 个新字段。
## 五、验收标准
- [ ] 新建报告时,默认模板中"手术后情况"等 4 处显示为可编辑的智能方框。
- [ ] 右侧【基本信息】表单中出现对应的 4 个输入控件3 个单选下拉 + 1 个文本框)。
- [ ] 表单修改值后,模板中的方框实时同步;在方框中直接输入后,表单也实时同步。
- [ ] 老用户(已有 `formFieldsConfig`)首次进入页面时,自动补全这 4 个缺失字段。