2026-04-18-23-39-35 - 四项修复:下划线默认、PDF文件名、间距缩紧、表单逆向联动
This commit is contained in:
@@ -55,6 +55,24 @@ export default function ReportEditor() {
|
|||||||
|
|
||||||
const [activeTab, setActiveTab] = useState<'info' | 'video'>('info');
|
const [activeTab, setActiveTab] = useState<'info' | 'video'>('info');
|
||||||
const [activeFieldKey, setActiveFieldKey] = useState<string | null>(null);
|
const [activeFieldKey, setActiveFieldKey] = useState<string | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!editorRef.current) return;
|
||||||
|
const allFields = editorRef.current.querySelectorAll('.field-value');
|
||||||
|
allFields.forEach(el => {
|
||||||
|
(el as HTMLElement).style.backgroundColor = '#f8fafc';
|
||||||
|
(el as HTMLElement).style.boxShadow = 'none';
|
||||||
|
});
|
||||||
|
if (activeFieldKey) {
|
||||||
|
const targetEl = editorRef.current.querySelector(`.field-value[data-bind="${activeFieldKey}"]`) as HTMLElement;
|
||||||
|
if (targetEl) {
|
||||||
|
targetEl.style.backgroundColor = '#eff6ff';
|
||||||
|
targetEl.style.boxShadow = '0 0 0 2px #3b82f6';
|
||||||
|
targetEl.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [activeFieldKey]);
|
||||||
|
|
||||||
const [multiSelectOptions, setMultiSelectOptions] = useState<Record<string, string[]>>({
|
const [multiSelectOptions, setMultiSelectOptions] = useState<Record<string, string[]>>({
|
||||||
surgeon: ['张医生', '李医生', '王医生'],
|
surgeon: ['张医生', '李医生', '王医生'],
|
||||||
assistant: ['赵医生', '钱医生', '孙医生'],
|
assistant: ['赵医生', '钱医生', '孙医生'],
|
||||||
@@ -1525,7 +1543,7 @@ export default function ReportEditor() {
|
|||||||
if (field.type === 'text' || field.type === 'date') {
|
if (field.type === 'text' || field.type === 'date') {
|
||||||
const inputType = field.type === 'date' ? 'date' : 'text';
|
const inputType = field.type === 'date' ? 'date' : 'text';
|
||||||
return (
|
return (
|
||||||
<div key={field.key} id={`input-${field.key}`} className={`${field.category === '填空' && formFields.filter(f2 => f2.visibleInForm && f2.type === 'text' && f2.isSystemLocked).length > 1 && (field.key === 'patientName' || field.key === 'hospitalId') ? 'flex-1 space-y-1' : 'space-y-1'} p-2 -mx-2 rounded-xl transition-all duration-300 ${activeFieldKey === field.key ? 'bg-blue-50 ring-1 ring-accent shadow-sm' : ''}`}>
|
<div key={field.key} id={`input-${field.key}`} onClick={() => setActiveFieldKey(field.key)} className={`${field.category === '填空' && formFields.filter(f2 => f2.visibleInForm && f2.type === 'text' && f2.isSystemLocked).length > 1 && (field.key === 'patientName' || field.key === 'hospitalId') ? 'flex-1 space-y-1' : 'space-y-1'} p-2 -mx-2 rounded-xl transition-all duration-300 ${activeFieldKey === field.key ? 'bg-blue-50 ring-1 ring-accent shadow-sm' : ''}`}>
|
||||||
<label className="block text-xs font-bold text-text-main">
|
<label className="block text-xs font-bold text-text-main">
|
||||||
{field.label} {isRequired && <span className="text-red-500">*</span>}
|
{field.label} {isRequired && <span className="text-red-500">*</span>}
|
||||||
</label>
|
</label>
|
||||||
@@ -1545,7 +1563,7 @@ export default function ReportEditor() {
|
|||||||
const isOpen = openDropdown === field.key;
|
const isOpen = openDropdown === field.key;
|
||||||
const opts = field.options || (field.key === 'anesthesiaType' ? anesthesiaOptions : []);
|
const opts = field.options || (field.key === 'anesthesiaType' ? anesthesiaOptions : []);
|
||||||
return (
|
return (
|
||||||
<div key={field.key} id={`input-${field.key}`} className={`space-y-1 select-dropdown-root relative p-2 -mx-2 rounded-xl transition-all duration-300 ${activeFieldKey === field.key ? 'bg-blue-50 ring-1 ring-accent shadow-sm' : ''}`}>
|
<div key={field.key} id={`input-${field.key}`} onClick={() => setActiveFieldKey(field.key)} className={`space-y-1 select-dropdown-root relative p-2 -mx-2 rounded-xl transition-all duration-300 ${activeFieldKey === field.key ? 'bg-blue-50 ring-1 ring-accent shadow-sm' : ''}`}>
|
||||||
<label className="block text-xs font-bold text-text-main">{field.label}</label>
|
<label className="block text-xs font-bold text-text-main">{field.label}</label>
|
||||||
<div
|
<div
|
||||||
className="w-full px-3 py-2 border border-border rounded-lg bg-white flex items-center min-h-[42px] cursor-text"
|
className="w-full px-3 py-2 border border-border rounded-lg bg-white flex items-center min-h-[42px] cursor-text"
|
||||||
@@ -1654,7 +1672,7 @@ export default function ReportEditor() {
|
|||||||
const currentInputText = multiInputText[field.key] !== undefined ? multiInputText[field.key] : displayText;
|
const currentInputText = multiInputText[field.key] !== undefined ? multiInputText[field.key] : displayText;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={field.key} id={`input-${field.key}`} className={`space-y-1 select-dropdown-root relative p-2 -mx-2 rounded-xl transition-all duration-300 ${activeFieldKey === field.key ? 'bg-blue-50 ring-1 ring-accent shadow-sm' : ''}`}>
|
<div key={field.key} id={`input-${field.key}`} onClick={() => setActiveFieldKey(field.key)} className={`space-y-1 select-dropdown-root relative p-2 -mx-2 rounded-xl transition-all duration-300 ${activeFieldKey === field.key ? 'bg-blue-50 ring-1 ring-accent shadow-sm' : ''}`}>
|
||||||
<label className="block text-xs font-bold text-text-main">{field.label}(可多选)</label>
|
<label className="block text-xs font-bold text-text-main">{field.label}(可多选)</label>
|
||||||
<div
|
<div
|
||||||
className="w-full px-3 py-2 border border-border rounded-lg bg-white flex flex-wrap gap-1 items-center min-h-[42px] cursor-text"
|
className="w-full px-3 py-2 border border-border rounded-lg bg-white flex flex-wrap gap-1 items-center min-h-[42px] cursor-text"
|
||||||
@@ -1735,7 +1753,7 @@ export default function ReportEditor() {
|
|||||||
const { h: h12, isPM } = from24h(h24val);
|
const { h: h12, isPM } = from24h(h24val);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={field.key} id={`input-${field.key}`} className={`space-y-1 p-2 -mx-2 rounded-xl transition-all duration-300 ${activeFieldKey === field.key ? 'bg-blue-50 ring-1 ring-accent shadow-sm' : ''}`}>
|
<div key={field.key} id={`input-${field.key}`} onClick={() => setActiveFieldKey(field.key)} className={`space-y-1 p-2 -mx-2 rounded-xl transition-all duration-300 ${activeFieldKey === field.key ? 'bg-blue-50 ring-1 ring-accent shadow-sm' : ''}`}>
|
||||||
<label className="block text-xs font-bold text-text-main">{field.label}</label>
|
<label className="block text-xs font-bold text-text-main">{field.label}</label>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<select
|
<select
|
||||||
@@ -1792,7 +1810,7 @@ export default function ReportEditor() {
|
|||||||
const { h: h12g, isPM: isPMg } = from24h(h24);
|
const { h: h12g, isPM: isPMg } = from24h(h24);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={field.key} id={`input-${field.key}`} className="space-y-1">
|
<div key={field.key} id={`input-${field.key}`} onClick={() => setActiveFieldKey(field.key)} className={`space-y-1 p-2 -mx-2 rounded-xl transition-all duration-300 ${activeFieldKey === field.key ? 'bg-blue-50 ring-1 ring-accent shadow-sm' : ''}`}>
|
||||||
<label className="block text-xs font-bold text-text-main">{field.label}</label>
|
<label className="block text-xs font-bold text-text-main">{field.label}</label>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<select
|
<select
|
||||||
|
|||||||
@@ -121,8 +121,8 @@ export interface FormField {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const DEFAULT_FORM_FIELDS: FormField[] = [
|
export const DEFAULT_FORM_FIELDS: FormField[] = [
|
||||||
{ key: 'patientName', label: '患者姓名', category: '填空', type: 'text', visibleInForm: true, isSystemLocked: true, hasUnderline: true },
|
{ key: 'patientName', label: '患者姓名', category: '填空', type: 'text', visibleInForm: true, isSystemLocked: true, hasUnderline: false },
|
||||||
{ key: 'hospitalId', label: '住院号', category: '填空', type: 'text', visibleInForm: true, isSystemLocked: true, hasUnderline: true },
|
{ key: 'hospitalId', label: '住院号', category: '填空', type: 'text', visibleInForm: true, isSystemLocked: true, hasUnderline: false },
|
||||||
{ key: 'title', label: '手术名称', category: '填空', type: 'text', visibleInForm: true, isSystemLocked: false },
|
{ key: 'title', label: '手术名称', category: '填空', type: 'text', visibleInForm: true, isSystemLocked: false },
|
||||||
{ key: 'patientGender', label: '患者性别', category: '单选', type: 'single_select', visibleInForm: true, isSystemLocked: false, options: ['男', '女'] },
|
{ key: 'patientGender', label: '患者性别', category: '单选', type: 'single_select', visibleInForm: true, isSystemLocked: false, options: ['男', '女'] },
|
||||||
{ key: 'patientAge', label: '患者年龄', category: '填空', type: 'text', visibleInForm: true, isSystemLocked: false },
|
{ key: 'patientAge', label: '患者年龄', category: '填空', type: 'text', visibleInForm: true, isSystemLocked: false },
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
const smartField = (key: string) => {
|
const smartField = (key: string) => {
|
||||||
return `<span class="smart-field-wrapper" contenteditable="false" style="white-space:nowrap;position:relative;"><span class="field-value no-underline" data-bind="${key}" contenteditable="true" style="min-width:32px;padding:0 4px;margin:0 2px;border:1px solid #cbd5e1;border-radius:2px;display:inline-block;background:#f8fafc;color:#0f172a;line-height:inherit;font-size:inherit;vertical-align:baseline;box-sizing:border-box;outline:none;"> </span><span class="delete-btn" contenteditable="false">×</span></span>​`;
|
return `<span class="smart-field-wrapper" contenteditable="false" style="white-space:nowrap;position:relative;"><span class="field-value no-underline" data-bind="${key}" contenteditable="true" style="min-width:24px;padding:0 2px;margin:0;border:1px solid #cbd5e1;border-radius:2px;display:inline-block;background:#f8fafc;color:#0f172a;line-height:inherit;font-size:inherit;vertical-align:baseline;box-sizing:border-box;outline:none;text-align:center;"> </span><span class="delete-btn" contenteditable="false">×</span></span>​`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const defaultReportContent = `
|
export const defaultReportContent = `
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export const printDocument = (htmlContent: string, docTitle: string = '图文报
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
|
<title>${docTitle}</title>
|
||||||
<style>
|
<style>
|
||||||
@page { size: A4; margin: 15mm 10mm; }
|
@page { size: A4; margin: 15mm 10mm; }
|
||||||
* { box-sizing: border-box; }
|
* { box-sizing: border-box; }
|
||||||
@@ -36,12 +37,12 @@ export const printDocument = (htmlContent: string, docTitle: string = '图文报
|
|||||||
.delete-btn { display: none !important; }
|
.delete-btn { display: none !important; }
|
||||||
.image-placeholder:not(.has-image) { display: none !important; }
|
.image-placeholder:not(.has-image) { display: none !important; }
|
||||||
.template-info-section { position: relative; margin-bottom: 16px; }
|
.template-info-section { position: relative; margin-bottom: 16px; }
|
||||||
.smart-field-wrapper { display: inline-flex; align-items: baseline; margin: 0 2px; vertical-align: baseline; }
|
.smart-field-wrapper { display: inline-flex; align-items: baseline; margin: 0; vertical-align: baseline; }
|
||||||
.smart-field-wrapper .field-label { color: #64748b; user-select: none; }
|
.smart-field-wrapper .field-label { color: #64748b; user-select: none; }
|
||||||
.smart-field-wrapper .field-value { min-width: 32px; padding: 0 4px; margin: 0 2px; border: 1px solid #cbd5e1; border-radius: 2px; display: inline-block; background: #f8fafc; color: #0f172a; line-height: inherit; font-size: inherit; vertical-align: baseline; box-sizing: border-box; outline: none; }
|
.smart-field-wrapper .field-value { min-width: 24px; padding: 0 2px; margin: 0; border: 1px solid #cbd5e1; border-radius: 2px; display: inline-block; background: #f8fafc; color: #0f172a; line-height: inherit; font-size: inherit; vertical-align: baseline; box-sizing: border-box; outline: none; text-align: center; }
|
||||||
.report-signature-img { max-width: 120px; max-height: 40px; width: auto; height: auto; object-fit: contain; vertical-align: middle; display: inline-block; }
|
.report-signature-img { max-width: 120px; max-height: 40px; width: auto; height: auto; object-fit: contain; vertical-align: middle; display: inline-block; }
|
||||||
@media print {
|
@media print {
|
||||||
.smart-field-wrapper .field-value { border: none !important; border-bottom: 1px solid #000 !important; border-radius: 0 !important; background: transparent !important; padding: 0 2px 1px 2px !important; }
|
.smart-field-wrapper .field-value { border: none !important; border-bottom: 1px solid #000 !important; border-radius: 0 !important; background: transparent !important; padding: 0 2px 0px 2px !important; }
|
||||||
.smart-field-wrapper .field-value.no-underline { border-bottom: none !important; }
|
.smart-field-wrapper .field-value.no-underline { border-bottom: none !important; }
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
108
工程分析/实现方案-2026-04-18-23-39-35.md
Normal file
108
工程分析/实现方案-2026-04-18-23-39-35.md
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
# 实现方案 —— 2026-04-18-23-39-35
|
||||||
|
|
||||||
|
## 方案目标
|
||||||
|
修复下划线功能、统一导出文件名、缩紧输入框间距、实现表单逆向联动。
|
||||||
|
|
||||||
|
## 需求 1:修复下划线勾选状态异常及打印失效
|
||||||
|
|
||||||
|
### 修改文件 1:`src/types.ts`
|
||||||
|
在 `DEFAULT_FORM_FIELDS` 数组中,为所有字段显式设置 `hasUnderline: false`(如果当前为 `true` 或未指定)。
|
||||||
|
|
||||||
|
### 修改文件 2:`src/pages/TemplateManage.tsx`
|
||||||
|
在编辑字段的回显逻辑中:
|
||||||
|
```ts
|
||||||
|
setEditFieldHasUnderline(field.hasUnderline === true);
|
||||||
|
```
|
||||||
|
确保 `undefined` 时默认不勾选。
|
||||||
|
|
||||||
|
### 修改文件 3:`src/utils/print.ts`
|
||||||
|
恢复默认显示下划线的白名单机制:
|
||||||
|
```css
|
||||||
|
@media print {
|
||||||
|
.smart-field-wrapper .field-value {
|
||||||
|
border: none !important;
|
||||||
|
border-bottom: 1px solid #000 !important;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
background: transparent !important;
|
||||||
|
padding: 0 2px 0px 2px !important;
|
||||||
|
}
|
||||||
|
.smart-field-wrapper .field-value.no-underline {
|
||||||
|
border-bottom: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 需求 2:统一 PDF 和 JSON 导出文件名
|
||||||
|
|
||||||
|
### 修改文件:`src/utils/print.ts`
|
||||||
|
确保 `printDocument` 中:
|
||||||
|
1. 保存原始 `document.title`
|
||||||
|
2. 设置 `document.title = docTitle`
|
||||||
|
3. iframe HTML 中也写入 `<title>${docTitle}</title>`
|
||||||
|
4. 打印完成后恢复 `document.title`
|
||||||
|
|
||||||
|
同时检查 `ReportEditor.tsx` 和 `ReportManage.tsx` 中调用 `printDocument` 时传入的 `docTitle` 是否与 JSON 文件名一致。
|
||||||
|
|
||||||
|
## 需求 3:缩紧 field-value 内文字间距
|
||||||
|
|
||||||
|
### 修改文件 1:`src/utils/defaultContent.ts`
|
||||||
|
```ts
|
||||||
|
// padding:0 4px → padding:0 2px
|
||||||
|
// margin:0 2px → margin:0
|
||||||
|
// min-width:32px → min-width:24px
|
||||||
|
// 增加 text-align:center 让文字居中
|
||||||
|
```
|
||||||
|
|
||||||
|
### 修改文件 2:`src/utils/print.ts`
|
||||||
|
同步修改打印样式中的 `.field-value`:
|
||||||
|
```css
|
||||||
|
.smart-field-wrapper .field-value {
|
||||||
|
min-width: 24px;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 需求 4:ReportEditor 表单逆向联动
|
||||||
|
|
||||||
|
### 修改文件:`src/pages/ReportEditor.tsx`
|
||||||
|
|
||||||
|
1. **新增 useEffect 监听 activeFieldKey**:
|
||||||
|
```ts
|
||||||
|
useEffect(() => {
|
||||||
|
if (!editorRef.current) return;
|
||||||
|
const allFields = editorRef.current.querySelectorAll('.field-value');
|
||||||
|
allFields.forEach(el => {
|
||||||
|
(el as HTMLElement).style.backgroundColor = '#f8fafc';
|
||||||
|
(el as HTMLElement).style.boxShadow = 'none';
|
||||||
|
});
|
||||||
|
if (activeFieldKey) {
|
||||||
|
const targetEl = editorRef.current.querySelector(`.field-value[data-bind="${activeFieldKey}"]`) as HTMLElement;
|
||||||
|
if (targetEl) {
|
||||||
|
targetEl.style.backgroundColor = '#eff6ff';
|
||||||
|
targetEl.style.boxShadow = '0 0 0 2px #3b82f6';
|
||||||
|
targetEl.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [activeFieldKey]);
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **右侧表单添加 onFocus/onClick**:
|
||||||
|
在右侧表单字段容器的 `onClick` 中增加 `setActiveFieldKey(field.key)`,在 input/select 的 `onFocus` 中也增加 `setActiveFieldKey(field.key)`。
|
||||||
|
|
||||||
|
## 涉及文件及修改点
|
||||||
|
| 文件 | 修改点 |
|
||||||
|
|------|--------|
|
||||||
|
| `src/types.ts` | DEFAULT_FORM_FIELDS 中 hasUnderline 设为 false |
|
||||||
|
| `src/pages/TemplateManage.tsx` | 编辑字段回显逻辑 |
|
||||||
|
| `src/utils/print.ts` | 打印下划线白名单机制;document.title 设置;field-value 间距 |
|
||||||
|
| `src/utils/defaultContent.ts` | smartField padding/margin 缩小;text-align:center |
|
||||||
|
| `src/pages/ReportEditor.tsx` | activeFieldKey useEffect 高亮滚动;表单 onFocus 联动 |
|
||||||
|
| `src/pages/ReportManage.tsx` | 检查导出文件名一致性 |
|
||||||
|
|
||||||
|
## 风险与注意事项
|
||||||
|
1. `DEFAULT_FORM_FIELDS` 修改后,现有用户的 localStorage 中已保存的字段配置不会自动更新,需要手动编辑或清除 `formFieldsConfig` 才能看到效果。
|
||||||
|
2. `activeFieldKey` 的 useEffect 直接操作 DOM style,需要确保在组件卸载或切换 tab 时清除高亮。
|
||||||
|
3. 缩小 padding/margin 后,需要验证在表格单元格(td)内的显示是否正常。
|
||||||
|
4. 打印样式中 `.field-value.no-underline` 的优先级必须高于基础 `.field-value` 规则。
|
||||||
64
工程分析/测试方案-2026-04-18-23-39-35.md
Normal file
64
工程分析/测试方案-2026-04-18-23-39-35.md
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
# 测试方案 —— 2026-04-18-23-39-35
|
||||||
|
|
||||||
|
## 测试目标
|
||||||
|
验证下划线修复、文件名统一、间距缩紧和双向联动的正确性。
|
||||||
|
|
||||||
|
## 测试用例
|
||||||
|
|
||||||
|
### TC-1:基础字段默认不勾选下划线
|
||||||
|
**前置条件**:进入模板管理 → 字段管理。
|
||||||
|
**步骤**:
|
||||||
|
1. 点击「患者姓名」或「住院号」的编辑按钮。
|
||||||
|
**预期结果**:「打印时显示下划线」复选框默认未勾选。
|
||||||
|
|
||||||
|
### TC-2:勾选下划线后打印生效
|
||||||
|
**前置条件**:某个字段已勾选「打印时显示下划线」。
|
||||||
|
**步骤**:
|
||||||
|
1. 在编辑器中插入该字段。
|
||||||
|
2. 点击打印预览。
|
||||||
|
**预期结果**:该字段显示下划线,且下划线紧贴文字底部。
|
||||||
|
|
||||||
|
### TC-3:未勾选下划线打印不显示
|
||||||
|
**前置条件**:某个字段未勾选下划线。
|
||||||
|
**步骤**:
|
||||||
|
1. 在编辑器中插入该字段。
|
||||||
|
2. 点击打印预览。
|
||||||
|
**预期结果**:该字段不显示下划线。
|
||||||
|
|
||||||
|
### TC-4:PDF 与 JSON 文件名一致
|
||||||
|
**前置条件**:报告已填写完整信息。
|
||||||
|
**步骤**:
|
||||||
|
1. 分别点击「导出 PDF」和「导出 JSON」。
|
||||||
|
**预期结果**:两个文件的文件名前缀完全一致(如 `图文报告-腹腔镜胆囊切除术报告-未知-无号-2026-04-18T23-28`)。
|
||||||
|
|
||||||
|
### TC-5:field-value 间距缩紧
|
||||||
|
**前置条件**:模板中有 field-value 字段。
|
||||||
|
**步骤**:
|
||||||
|
1. 观察字段框内文字与边框的距离。
|
||||||
|
2. 打印预览中观察间距。
|
||||||
|
**预期结果**:文字紧贴边框,左右无明显空白。
|
||||||
|
|
||||||
|
### TC-6:表单逆向联动
|
||||||
|
**前置条件**:ReportEditor 已加载默认模板。
|
||||||
|
**步骤**:
|
||||||
|
1. 点击右侧「基本信息」中「手术名称」输入框。
|
||||||
|
2. 观察中间模板区域。
|
||||||
|
**预期结果**:
|
||||||
|
- 中间模板中「手术名称」字段高亮显示(蓝色背景 + 蓝色描边)。
|
||||||
|
- 页面平滑滚动到该字段位置(视野中央)。
|
||||||
|
|
||||||
|
### TC-7:正向联动仍正常
|
||||||
|
**前置条件**:ReportEditor 已加载默认模板。
|
||||||
|
**步骤**:
|
||||||
|
1. 点击中间模板中的「患者姓名」字段。
|
||||||
|
**预期结果**:
|
||||||
|
- 右侧表单中高亮「患者姓名」输入框。
|
||||||
|
- 右侧滚动到该输入框位置。
|
||||||
|
|
||||||
|
## 回归测试
|
||||||
|
- 确保字段插入、编辑、删除功能正常。
|
||||||
|
- 确保打印样式正常,所有字段类型显示正确。
|
||||||
|
- 确保视频分析、图片占位符功能正常。
|
||||||
|
|
||||||
|
## 测试通过标准
|
||||||
|
所有用例均通过,无控制台报错,下划线逻辑正确,双向联动流畅。
|
||||||
34
工程分析/需求分析-2026-04-18-23-39-35.md
Normal file
34
工程分析/需求分析-2026-04-18-23-39-35.md
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# 需求分析 —— 2026-04-18-23-39-35
|
||||||
|
|
||||||
|
## 需求来源
|
||||||
|
用户在实际使用中发现下划线功能异常、导出文件名不一致、输入框间距过大、以及表单缺乏逆向联动等问题。
|
||||||
|
|
||||||
|
## 需求概述
|
||||||
|
|
||||||
|
### 需求 1:修复下划线勾选状态异常及打印失效
|
||||||
|
1. **默认勾选未取消**:`DEFAULT_FORM_FIELDS` 中的基础字段(如患者姓名、住院号)默认 `hasUnderline` 仍为 `true` 或未指定,导致编辑弹窗中仍显示为勾选状态。
|
||||||
|
2. **打印失效**:`print.ts` 中 `@media print` 的样式逻辑有问题,导致无论是否勾选「打印时显示下划线」,打印时都不显示下划线。
|
||||||
|
3. **下划线紧贴文字**:用户希望勾选后的下划线紧贴文字底部。
|
||||||
|
|
||||||
|
### 需求 2:统一 PDF 和 JSON 导出文件名
|
||||||
|
当前 PDF 导出文件名与 JSON 不一致(缺少时间后缀或格式不同),希望两者完全一致。
|
||||||
|
|
||||||
|
### 需求 3:缩紧 field-value 内文字间距
|
||||||
|
`.field-value` 当前有 `padding:0 4px; margin:0 2px`,导致框内文字偏右,打印时左右间距过大。希望缩小 padding 和 margin。
|
||||||
|
|
||||||
|
### 需求 4:ReportEditor 表单逆向联动
|
||||||
|
当前实现了「点击中间模板字段 → 右侧表单高亮滚动」,但反向逻辑缺失:点击右侧表单输入框时,中间模板内的对应 `.field-value` 不会高亮,也不会滚动到对应位置。
|
||||||
|
|
||||||
|
## 涉及文件
|
||||||
|
- `src/types.ts`(需求 1)
|
||||||
|
- `src/pages/TemplateManage.tsx`(需求 1)
|
||||||
|
- `src/utils/print.ts`(需求 1、2、3)
|
||||||
|
- `src/utils/defaultContent.ts`(需求 3)
|
||||||
|
- `src/pages/ReportEditor.tsx`(需求 2、4)
|
||||||
|
- `src/pages/ReportManage.tsx`(需求 2)
|
||||||
|
|
||||||
|
## 需求影响范围
|
||||||
|
- 字段默认配置数据
|
||||||
|
- 打印样式逻辑
|
||||||
|
- 输入框内边距/外边距
|
||||||
|
- 编辑器双向联动交互
|
||||||
Reference in New Issue
Block a user