5.3 KiB
5.3 KiB
实现方案 — 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
// 修改前
{ 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
// 在 formatTimeDisplay 函数开头增加一行兼容兜底
const formatTimeDisplay = (timeStr: string, fmt?: string): string => {
if (!timeStr || !fmt) return timeStr || '';
if (fmt === '24h') fmt = 'HH:mm'; // 兼容旧脏数据
// ... 后续代码不变
};
3. TemplateManage.tsx
3.1 清理旧脏数据(初始化时)
// 修改前
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 过滤:
<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 编辑卡片点击后自动滚动对齐
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 |
scrollIntoView 在极端短容器中频繁触发 |
使用 block: 'nearest' 而非 'start',减少不必要的滚动;50ms 延迟确保 DOM 已撑开 |
回滚策略
types.ts的修改可直接回退两个字段的timeFormat字符串。ReportEditor.tsx的修改仅增加一行,删除即可。TemplateManage.tsx的修改均为增量逻辑,回滚时移除条件块即可。