5.4 KiB
5.4 KiB
实现方案 —— 2026-04-18-20-03-44
方案目标
实现模板的导入/导出迁移能力,统一默认模板 Logo 的交互行为。
需求 1:模板导出功能
修改文件
src/pages/TemplateManage.tsx
修改内容
在模板列表的每个模板行操作列中增加「导出」按钮(使用 Download 图标)。点击时:
const handleExportTemplate = (template: Template) => {
const exportData = {
version: '1.0',
type: 'surclaw_template_package',
title: template.title,
description: template.description,
content: template.content,
fields: template.fields || []
};
const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `模板导出-${template.title}.json`;
a.click();
URL.revokeObjectURL(url);
};
需求 2:模板导入功能
修改文件
src/pages/TemplateManage.tsx
修改内容
-
新增状态:
const [importedContent, setImportedContent] = useState<{content: string, fields: FormField[]} | null>(null); const fileInputRef = useRef<HTMLInputElement>(null); -
新增导入处理函数:
const handleImportFile = (e: React.ChangeEvent<HTMLInputElement>) => { const file = e.target.files?.[0]; if (!file) return; const reader = new FileReader(); reader.onload = (event) => { try { const json = JSON.parse(event.target?.result as string); if (json.type !== 'surclaw_template_package') { alert('无效的模板包文件'); return; } setNewTemplateTitle(json.title || ''); setNewTemplateDescription(json.description || ''); setImportedContent({ content: json.content || '', fields: Array.isArray(json.fields) ? json.fields : [] }); } catch { alert('文件解析失败,请检查 JSON 格式'); } }; reader.readAsText(file); }; -
修改创建逻辑:在
handleCreateTemplate中,如果有importedContent,优先使用导入的内容和字段:const newTemplate: Template = { id: 'tmpl_' + Date.now(), title: newTemplateTitle, description: newTemplateDescription, content: importedContent?.content || `<div style="font-size:12pt;line-height:1.5;"><p>请输入模板内容...</p></div>`, fields: importedContent?.fields || [], createdAt: new Date().toISOString() }; -
UI 调整:在新增模板 Modal 中标题下方加入导入区域:
<div className="flex items-center gap-3 mb-4 p-3 bg-slate-50 rounded-xl border border-dashed border-slate-200"> <div className="text-xs text-text-muted flex-1">已有模板文件?点击右侧图标导入</div> <button onClick={() => fileInputRef.current?.click()} className="w-8 h-8 bg-accent text-white rounded-lg flex items-center justify-center hover:bg-blue-700 transition-colors shadow-sm" > <Upload size={16} /> </button> <input ref={fileInputRef} type="file" accept=".json" className="hidden" onChange={handleImportFile} /> </div> -
关闭 Modal 时重置:
setImportedContent(null)
需求 3:Logo 替换为可交互占位符
修改文件
src/utils/defaultContent.ts
修改内容
将默认模板顶部的 Logo HTML 替换为标准 image-placeholder:
<span class="image-placeholder" data-placeholder="true" contenteditable="false" data-mode="manual" style="display:inline-block;text-align:center;width:65px;height:65px;line-height:65px;border:1px dashed #cbd5e1;background:#f8fafc;vertical-align:middle;margin:0 4px;cursor:pointer;position:relative;">
<span class="delete-btn" contenteditable="false">×</span>
<span class="placeholder-text" style="color:#94a3b8;font-size:11px;pointer-events:none;position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);display:block;width:100%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;">LOGO</span>
</span>
关键点:
class="image-placeholder":触发编辑器中的占位符交互逻辑data-mode="manual":标记为静态图片占位,不支持自动帧插入position:relative+position:absolute居中:确保提示文字绝对居中delete-btn:支持点击右上方的「×」删除
涉及文件及修改点
| 文件 | 修改点 |
|---|---|
src/pages/TemplateManage.tsx |
新增 handleExportTemplate;新增 importedContent 状态和 handleImportFile;修改 handleCreateTemplate 使用导入数据;新增模板 Modal 中增加导入 UI;模板列表操作列增加导出按钮 |
src/utils/defaultContent.ts |
顶部 Logo 替换为标准 image-placeholder |
风险与注意事项
- 导入的 JSON 中
fields数组需要与FormField类型结构兼容。由于 JSON 导入的是纯数据,直接赋值给template.fields即可(TypeScript 编译时类型校验通过)。 - 导出文件名中包含模板标题,需注意标题中的特殊字符可能影响文件名(但浏览器通常会自动处理)。
- Logo 占位符替换后,原有「西安交通大学第一附属医院」的样式应保持不变,仅替换 Logo 部分。
- 新增模板弹窗关闭时,需同步重置
importedContent为null,避免影响下一次创建。