- Add signature?: string to User type and 'signature' to FieldType - Add surgeonSignature field to DEFAULT_FORM_FIELDS (category: 图片) - UserManage: add canvas-based image compression (max 500px) and signature upload UI - TemplateManage: add hover highlight on field buttons via direct DOM style manipulation - TemplateManage: add '图片' category to field library for surgeonSignature insertion - ReportEditor: auto-fill surgeonSignature with currentUser.signature image or placeholder text - index.css & print.ts: add .report-signature-img styling (height 2.4em, vertical-align middle) - Update experience record (#18)
5.4 KiB
5.4 KiB
实现方案 — 字段悬浮高亮、电子签上传与手术者签名联动(2026-04-17-11-34-24)
一、修改文件清单
src/types.ts— 扩展User/FieldType/DEFAULT_FORM_FIELDSsrc/pages/UserManage.tsx— 电子签上传组件 + 前端压缩逻辑src/pages/TemplateManage.tsx— 悬浮高亮 + 图片分类 + 手术者签名插入src/pages/ReportEditor.tsx—surgeonSignature特殊同步逻辑src/index.css— 签名图片排版样式 + 打印样式src/utils/print.ts— 打印样式中增加签名图片规则
二、详细改动
2.1 src/types.ts
User接口追加signature?: string。FieldType扩展为'text' | 'single_select' | 'multi_select' | 'time' | 'date' | 'signature'。DEFAULT_FORM_FIELDS末尾追加:{ key: 'surgeonSignature', label: '手术者签名', category: '图片', type: 'signature', visibleInForm: false, isSystemLocked: true }
2.2 src/pages/UserManage.tsx
A. 前端压缩工具函数
const compressImage = (file: File, maxSize: number = 500): Promise<string> => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = (e) => {
const img = new Image();
img.src = e.target?.result as string;
img.onload = () => {
const canvas = document.createElement('canvas');
let { width, height } = img;
if (width > height && width > maxSize) {
height = Math.round((height * maxSize) / width);
width = maxSize;
} else if (height > maxSize) {
width = Math.round((width * maxSize) / height);
height = maxSize;
}
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
if (ctx) {
ctx.fillStyle = '#FFFFFF';
ctx.fillRect(0, 0, width, height);
ctx.drawImage(img, 0, 0, width, height);
}
resolve(canvas.toDataURL('image/jpeg', 0.8));
};
img.onerror = reject;
};
reader.onerror = reject;
});
};
B. 上传组件与保存逻辑
- 在模态框表单中("状态"选择器下方或底部按钮上方)增加一个区块:
- 标签:"电子签名"
- 若
formData.signature有值,显示压缩后的预览图(高度限制 64px)。 - "上传签名" 按钮(
type="button"),触发隐藏的<input type="file" accept="image/*">。 - "清除签名" 按钮(有值时显示)。
handleSubmit中保存signature字段到用户对象。- 编辑当前登录用户时,同步更新
storage.set('currentUser', currentCached),确保 ReportEditor 能立即读取到最新签名。
2.3 src/pages/TemplateManage.tsx
A. 悬浮高亮
在字段库按钮上增加 onMouseEnter 和 onMouseLeave:
const highlightField = (key: string, active: boolean) => {
if (!editorRef.current) return;
const el = editorRef.current.querySelector(`[data-bind="${key}"]`) as HTMLElement | null;
if (!el) return;
if (active) {
el.style.transition = 'all 0.2s';
el.style.boxShadow = '0 0 0 2px #3b82f6';
el.style.backgroundColor = '#e0f2fe';
} else {
el.style.boxShadow = '';
el.style.backgroundColor = '';
}
};
B. 图片分类与手术者签名
- 插入字段分类数组从
['填空', '单选', '多选', '时间']改为['填空', '单选', '多选', '时间', '图片']。 surgeonSignature字段会自动出现在"图片"分类下,按钮点击逻辑复用insertSmartField(已支持唯一性校验)。
2.4 src/pages/ReportEditor.tsx
在"Sync form state -> rich text field values"的 useEffect 中,对 fieldKey === 'surgeonSignature' 做特殊分支:
if (fieldKey === 'surgeonSignature') {
const signatureData = currentUser?.signature;
if (signatureData) {
const imgHtml = `<img src="${signatureData}" class="report-signature-img" alt="签名" draggable="false" />`;
if (el.innerHTML !== imgHtml) {
el.innerHTML = imgHtml;
el.style.border = 'none';
el.style.backgroundColor = 'transparent';
}
} else {
if (el.innerText !== '【请上传电子签】') {
el.innerText = '【请上传电子签】';
el.style.border = '';
el.style.backgroundColor = '';
}
}
return; // 跳过常规文本同步
}
2.5 src/index.css
增加签名图片样式:
.report-signature-img {
height: 2.4em;
width: auto;
vertical-align: middle;
display: inline-block;
margin: -0.3em 0;
}
在 @media print 中同步增加:
@media print {
.report-signature-img {
height: 2.4em !important;
width: auto !important;
vertical-align: middle !important;
display: inline-block !important;
margin: -0.3em 0 !important;
}
}
2.6 src/utils/print.ts
在打印 iframe 的 <style> 标签内,.smart-field-wrapper 规则之后追加:
.report-signature-img {
height: 2.4em;
width: auto;
vertical-align: middle;
display: inline-block;
margin: -0.3em 0;
}
三、风险与回滚
- 风险:
localStorage容量有限,压缩后的签名图片通常在 10~50KB,单用户存储安全。 - 风险:旧用户的
User对象没有signature字段,读取时为undefined,代码中已通过可选链和默认值处理。 - 回滚:如出现问题,可回退 6 个文件的修改。