112 lines
5.3 KiB
Markdown
112 lines
5.3 KiB
Markdown
# 实现方案 — 2026-04-17-23-12-52
|
||
|
||
## 根因分析
|
||
|
||
### 问题1:格式选项缺失与旧值残留
|
||
- `types.ts` 的 `DEFAULT_FORM_FIELDS` 中,`startTime` 和 `endTime` 的 `timeFormat` 仍被硬编码为 `'24h'`(历史遗留)。
|
||
- `TemplateManage.tsx` 中的 `defaultFormats` 虽然已包含 `HH:mm` 和 `hh:mm A`,但 `customTimeFormats` 的 datalist 未按字段类型过滤,导致 date/time 格式混在一起显示,用户体验混乱。
|
||
- 用户的 `localStorage` 中 `customTimeFormats` 可能还残留旧值 `'24h'`、`'12h'`。
|
||
|
||
### 问题2:报告编辑器显示 "24h"
|
||
- `formatTimeDisplay('14:30', '24h')` 中,格式字符串 `'24h'` 不包含 `HH`/`hh`/`mm`/`A` 等任何替换 token,函数直接原样返回 `'24h'`。
|
||
- 这是 F1 根因的直接后果:`formFieldsConfig` 中的 `timeFormat = '24h'` 被传入格式化函数。
|
||
|
||
### 问题3:输入框点击失效
|
||
- `TemplateManage.tsx` 字段管理列表位于一个 `overflow-y-auto` 的滚动容器中。
|
||
- 点击字段卡片后,卡片内部展开编辑表单,高度瞬间增加。如果卡片原本位于可视区域底部,展开后的输入框可能刚好处于容器边缘之外。
|
||
- 浏览器的 hit-testing 在布局突变时可能无法正确将点击事件路由到新出现的输入框上,导致需要手动滚动后才能点击。
|
||
|
||
## 修改文件清单
|
||
|
||
| 文件 | 修改内容 |
|
||
|------|---------|
|
||
| `src/types.ts` | `DEFAULT_FORM_FIELDS` 中 `startTime`/`endTime` 的 `timeFormat` 从 `'24h'` 改为 `'HH:mm'` |
|
||
| `src/pages/ReportEditor.tsx` | `formatTimeDisplay` 开头增加 `if (fmt === '24h') fmt = 'HH:mm';` 兼容兜底 |
|
||
| `src/pages/TemplateManage.tsx` | ① `defaultFormats` 初始化时过滤掉旧脏数据 `'24h'`/`'12h'`;② datalist 渲染时按字段类型(date/time)过滤选项;③ 字段编辑卡片 `onClick` 中增加 `scrollIntoView` |
|
||
|
||
## 具体代码变更
|
||
|
||
### 1. types.ts
|
||
|
||
```ts
|
||
// 修改前
|
||
{ key: 'startTime', label: '手术开始时间', category: '时间', type: 'time', visibleInForm: true, isSystemLocked: true, timeFormat: '24h', timeDefault: 'specific' },
|
||
{ key: 'endTime', label: '手术终止时间', category: '时间', type: 'time', visibleInForm: true, isSystemLocked: true, timeFormat: '24h', timeDefault: 'specific' },
|
||
|
||
// 修改后
|
||
{ key: 'startTime', label: '手术开始时间', category: '时间', type: 'time', visibleInForm: true, isSystemLocked: true, timeFormat: 'HH:mm', timeDefault: 'specific' },
|
||
{ key: 'endTime', label: '手术终止时间', category: '时间', type: 'time', visibleInForm: true, isSystemLocked: true, timeFormat: 'HH:mm', timeDefault: 'specific' },
|
||
```
|
||
|
||
### 2. ReportEditor.tsx
|
||
|
||
```ts
|
||
// 在 formatTimeDisplay 函数开头增加一行兼容兜底
|
||
const formatTimeDisplay = (timeStr: string, fmt?: string): string => {
|
||
if (!timeStr || !fmt) return timeStr || '';
|
||
if (fmt === '24h') fmt = 'HH:mm'; // 兼容旧脏数据
|
||
// ... 后续代码不变
|
||
};
|
||
```
|
||
|
||
### 3. TemplateManage.tsx
|
||
|
||
**3.1 清理旧脏数据(初始化时)**
|
||
```ts
|
||
// 修改前
|
||
const savedFormats = storage.get<string[]>('customTimeFormats', []);
|
||
const defaultFormats = ['YYYY-MM-DD', 'YYYY年MM月DD日', 'MM-DD', 'MM月DD日', 'HH:mm', 'hh:mm A'];
|
||
setCustomTimeFormats(Array.from(new Set([...defaultFormats, ...savedFormats])));
|
||
|
||
// 修改后
|
||
const savedFormats = storage.get<string[]>('customTimeFormats', []);
|
||
const defaultFormats = ['YYYY-MM-DD', 'YYYY年MM月DD日', 'MM-DD', 'MM月DD日', 'HH:mm', 'hh:mm A'];
|
||
// 过滤掉历史遗留的无效旧格式
|
||
const cleanedSaved = savedFormats.filter(f => f !== '24h' && f !== '12h');
|
||
setCustomTimeFormats(Array.from(new Set([...defaultFormats, ...cleanedSaved])));
|
||
```
|
||
|
||
**3.2 按字段类型过滤 datalist**
|
||
在编辑字段和新增字段的 format `<datalist>` 渲染处,增加按 `field.type` / `newFieldForm.type` 过滤:
|
||
```tsx
|
||
<datalist id={`edit-format-list-${field.key}`}>
|
||
{customTimeFormats
|
||
.filter(fmt => {
|
||
const isDateFormat = /YYYY|MM|DD/.test(fmt);
|
||
const isTimeFormat = /HH|hh|mm|A/.test(fmt);
|
||
if (field.type === 'date') return isDateFormat;
|
||
if (field.type === 'time') return isTimeFormat;
|
||
return true;
|
||
})
|
||
.map(fmt => <option key={fmt} value={fmt} />)}
|
||
</datalist>
|
||
```
|
||
新增字段的 datalist 同理。
|
||
|
||
**3.3 编辑卡片点击后自动滚动对齐**
|
||
```tsx
|
||
onClick={(e) => {
|
||
setEditingFieldKey(field.key);
|
||
// ... 其他 setState
|
||
|
||
const target = e.currentTarget;
|
||
setTimeout(() => {
|
||
target.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||
}, 50);
|
||
}}
|
||
```
|
||
|
||
## 风险点与应对措施
|
||
|
||
| 风险 | 应对措施 |
|
||
|------|---------|
|
||
| 清理 `customTimeFormats` 中的 `'24h'`/`'12h'` 可能误删用户真正想保留的自定义格式 | 仅过滤精确匹配 `'24h'` 和 `'12h'` 两个字符串,不影响其他自定义格式 |
|
||
| 按类型过滤 datalist 可能误过滤 | 使用正则 `/YYYY|MM|DD/` 识别日期格式,`/HH|hh|mm|A/` 识别时间格式;若格式同时满足两者(理论上不应发生)则同时显示 |
|
||
| `scrollIntoView` 在极端短容器中频繁触发 | 使用 `block: 'nearest'` 而非 `'start'`,减少不必要的滚动;50ms 延迟确保 DOM 已撑开 |
|
||
|
||
## 回滚策略
|
||
|
||
- `types.ts` 的修改可直接回退两个字段的 `timeFormat` 字符串。
|
||
- `ReportEditor.tsx` 的修改仅增加一行,删除即可。
|
||
- `TemplateManage.tsx` 的修改均为增量逻辑,回滚时移除条件块即可。
|