[文档] 更新经验记录:时间字段联动修复

This commit is contained in:
2026-04-17 22:51:42 +08:00
parent 8ea0a9a69e
commit 8e7079e6a9

View File

@@ -472,3 +472,112 @@ if ((settings.autoInsertDelay || 0) > 0) {
- 12h/24h 转换要覆盖所有边界情况12AM→00、12PM→12、1PM→13建议用独立纯函数`to24h`/`from24h`)集中处理,避免在 JSX 中内联复杂计算。
- 自动填充当前时间必须增加「仅当值为空时触发」的保护,防止编辑已有报告时覆盖用户数据。
---
## 记录 16时间字段增强——自定义格式、固定时间默认值、系统锁定标签
**A. 具体问题**
用户提出 4 个改进需求:
1. 默认模板底部「撰写时间」文字前缀与 smartField 占位符重复,需删除前缀仅保留占位符;
2. 多选类和时间类字段在 TemplateManage 字段管理中仍可修改名称,应锁定为系统字段;
3. 「手动选择」文案歧义,应改为「固定时间」;
4. 时间格式应从固定下拉选项改为支持自定义格式输入(类似单选新增选项策略),并支持为「固定时间」设置默认值。
**B. 产生问题原因**
1. `defaultContent.ts` 中底部 HTML 写死了 `撰写时间:${smartField('reportDate')}`,导致编辑器中显示重复文字。
2. `DEFAULT_FORM_FIELDS` 中 `surgeryDate`、`startTime`、`endTime`、`surgeon` 等字段的 `isSystemLocked` 为 `false`,字段库允许修改 label。
3. 早期实现时默认将时间默认值策略命名为「手动选择」,语义不够精确。
4. 日期/时间格式仅通过固定 `<select>` 提供预设选项(如 `YYYY-MM-DD`、`24h`),无法覆盖用户自定义需求(如 `YYYY/MM/DD`、`hh:mm A` 等)。
5. 当默认值策略为「固定时间」时,系统无法自动填充用户指定的固定值到报告表单中。
**C. 解决问题方案**
1. **删除前缀**`defaultContent.ts` 中将底部 HTML 从 `撰写时间:${smartField('reportDate')}` 改为仅 `${smartField('reportDate')}`。
2. **系统锁定**`types.ts` 中 `DEFAULT_FORM_FIELDS` 的 `surgeryDate`、`startTime`、`endTime`、`reportDate`、`surgeon`、`assistant`、`anesthesiologist` 全部改为 `isSystemLocked: true`。
3. **文案修改**`TemplateManage.tsx` 中所有「手动选择」改为「固定时间」。
4. **自定义格式输入**
- `types.ts` 的 `FormField` 增加 `fixedTimeValue?: string`。
- `TemplateManage.tsx` 的时间格式 UI 改为「下拉 + 自定义输入」双模式:
- `formatInputMode: 'select' | 'custom'`,默认 `select`。
- 选择「自定义」时显示 `<input>`,用户可自由输入格式字符串;回车后将输入值加入候选列表并设为当前值。
- 预设候选包含常用格式:`YYYY-MM-DD`、`YYYY年MM月DD日`、`YYYY/MM/DD`、`24h`、`12h`、`hh:mm A`、`HH:mm`。
- 通用化显示函数:
```ts
const formatDateDisplay = (isoDate: string, fmt?: string): string => {
if (!isoDate || !fmt) return isoDate || '';
const [y, m, d] = isoDate.split('-');
return fmt.replace(/YYYY/g, y || '').replace(/MM/g, m || '').replace(/DD/g, d || '');
};
const formatTimeDisplay = (timeStr: string, fmt?: string): string => {
if (!timeStr || !fmt) return timeStr || '';
const [h24str, mstr] = timeStr.split(':');
const h24 = parseInt(h24str) || 0;
const isPM = h24 >= 12;
let h12 = h24 % 12; if (h12 === 0) h12 = 12;
return fmt.replace(/HH/g, String(h24).padStart(2, '0'))
.replace(/mm/g, mstr || '00')
.replace(/hh/g, String(h12).padStart(2, '0'))
.replace(/A/g, isPM ? '下午' : '上午');
};
```
5. **通用化反向解析**:新增 `parseDateFromFormat` / `parseTimeFromFormat`,从格式化文本中通过数字正则提取原始值,确保用户在编辑器中直接编辑格式化后的 smart field 后能正确回存。
6. **固定时间默认值自动填充**`ReportEditor.tsx` 的自动填充 `useEffect` 中增加 `timeDefault === 'specific'` 分支,若字段配置了 `fixedTimeValue` 且当前值为空,则自动填入固定值。
**D. 后续如何避免问题**
- 自定义格式输入必须同时提供「正向格式化」和「反向解析」函数,否则编辑器双向同步会断裂。
- 使用占位符替换(如 `fmt.replace(/YYYY/g, y)`)实现通用格式化时,要确保所有可能的 token 都覆盖到,且替换顺序不会相互干扰。
- 当某个字段被标记为 `isSystemLocked: true` 后,需在 UI 层面同时禁用 label 输入框,否则用户会困惑「为何修改无效」。
- 时间/日期字段的默认值策略文案应直接体现业务含义(如「固定时间」「当前时间」),避免使用技术词汇(如「手动选择」)。
- 对于 `startTime`/`endTime` 这类拆分存储(`startHour`+`startMinute`)的遗留字段,在通用化处理时需保留特殊分支,避免破坏现有数据结构。
---
## 记录 17时间字段联动修复——默认格式、固定时间自动填充、12/24h 动态切换
**A. 具体问题**
用户发现 3 个时间字段配置与报告编辑器的联动断层:
1. 模板管理中新建日期字段时默认格式为 `YYYY-MM-DD`,缺少中文格式 `YYYY年MM月DD日`;新建时间字段时默认格式为不可解析的 `'24h'`。
2. 在模板管理中将时间字段设为「固定时间」并填写固定值后,进入报告编辑器新建报告时,该固定值未自动填充到表单中。
3. 在模板管理中将 `startTime` 格式改为 `hh:mm A`12小时制报告编辑器中的手术开始时间表单仍显示为 24 小时制下拉框,未联动切换。
**B. 产生问题原因**
1. **默认格式错误**`TemplateManage.tsx` 中 `newFieldForm.type` 的 `onChange` 将时间字段默认值硬编码为 `'24h'`,而实际通用格式化函数 `formatTimeDisplay` 使用的是 `HH`、`hh`、`mm`、`A` 等 token `'24h'` 无法被正确解析。
2. **固定时间未注入**`ReportEditor.tsx` 初始 `reportData` 和切换模板时的 `nextReportData` 中,`surgeryDate` 被强制赋值为 `new Date().toISOString().split('T')[0]`,导致后续「仅当值为空时才填充固定时间」的判断被跳过(因为已有值了)。切换模板时也未遍历 `formFields` 读取字段的 `timeDefault`/`fixedTimeValue` 配置来注入默认值。
3. **12h 判断写死**`ReportEditor.tsx` 中 `const is12h = field.timeFormat === '12h';` 仅匹配精确的 `'12h'` 字符串。当用户在模板管理中选择了 `hh:mm A` 或自定义了其他包含 `hh`/`A` 的格式时,判断失败,表单始终渲染为 24 小时制。
**C. 解决问题方案**
1. **修正默认格式**
- `TemplateManage.tsx` 中新建字段的默认格式改为:
```ts
setNewFieldTimeFormat(t === 'date' ? 'YYYY年MM月DD日' : 'HH:mm');
```
- 重置表单时的默认值同步修正。
2. **注入固定时间默认值**
- `ReportEditor.tsx` 初始 `reportData` 中 `surgeryDate` 从 `new Date()` 改为空字符串 `''`。
- 切换模板的 `useEffect` 中,在构建 `nextReportData` 后增加遍历 `formFields` 的逻辑:
```ts
formFields.forEach(field => {
if (field.category === '时间') {
if (field.timeDefault === 'specific' && field.fixedTimeValue) {
// 按 field.type 和 field.key 注入固定值
} else if (field.timeDefault === 'current') {
// 注入当前系统时间
}
}
});
if (!nextReportData.surgeryDate) {
nextReportData.surgeryDate = new Date().toISOString().split('T')[0];
}
```
3. **通用化 12h 判断**
- `ReportEditor.tsx` 中:
```ts
const is12h = field.timeFormat ? (field.timeFormat.includes('hh') || field.timeFormat.includes('A')) : false;
```
- 这样无论格式是 `12h`、`hh:mm A`、`hh:mm` 还是用户自定义的 `hh时mm分 A`,只要包含 `hh` 或 `A` 就自动切换为 12 小时制表单。
**D. 后续如何避免问题**
- 时间/日期格式的默认值必须与通用格式化函数的 token 体系保持一致,不能使用简写别名(如 `'24h'`、`'12h'`)作为存储值,除非格式化函数也能识别这些别名。
- 当字段配置了「固定默认值」或「自动填充当前值」时,必须在所有「创建新数据」的入口(初始 state、切换模板、重置表单等中显式遍历字段配置并注入不能依赖单个 `useEffect` 来兜底——因为 `useEffect` 的触发条件可能与数据创建时机不一致。
- 对于「格式→UI 形态」的联动判断,应使用**包含性判断**`includes`)而非**精确匹配**,以兼容用户自定义格式。如果判断逻辑较为复杂,建议抽离为独立工具函数(如 `is12HourFormat(fmt: string): boolean`)。
- 当某个字段在初始化时被赋予了「看似合理的默认值」(如 `surgeryDate: new Date()`),必须评估这是否会拦截后续基于字段配置的自动填充逻辑。若会拦截,应改为空值并在最后做兜底赋值。