131 lines
5.4 KiB
Markdown
131 lines
5.4 KiB
Markdown
# 实现方案 —— 2026-04-18-20-03-44
|
||
|
||
## 方案目标
|
||
实现模板的导入/导出迁移能力,统一默认模板 Logo 的交互行为。
|
||
|
||
## 需求 1:模板导出功能
|
||
|
||
### 修改文件
|
||
`src/pages/TemplateManage.tsx`
|
||
|
||
### 修改内容
|
||
在模板列表的每个模板行操作列中增加「导出」按钮(使用 Download 图标)。点击时:
|
||
```ts
|
||
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`
|
||
|
||
### 修改内容
|
||
1. **新增状态**:
|
||
```ts
|
||
const [importedContent, setImportedContent] = useState<{content: string, fields: FormField[]} | null>(null);
|
||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||
```
|
||
|
||
2. **新增导入处理函数**:
|
||
```ts
|
||
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);
|
||
};
|
||
```
|
||
|
||
3. **修改创建逻辑**:在 `handleCreateTemplate` 中,如果有 `importedContent`,优先使用导入的内容和字段:
|
||
```ts
|
||
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()
|
||
};
|
||
```
|
||
|
||
4. **UI 调整**:在新增模板 Modal 中标题下方加入导入区域:
|
||
```tsx
|
||
<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>
|
||
```
|
||
|
||
5. **关闭 Modal 时重置**:`setImportedContent(null)`
|
||
|
||
## 需求 3:Logo 替换为可交互占位符
|
||
|
||
### 修改文件
|
||
`src/utils/defaultContent.ts`
|
||
|
||
### 修改内容
|
||
将默认模板顶部的 Logo HTML 替换为标准 `image-placeholder`:
|
||
```html
|
||
<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` |
|
||
|
||
## 风险与注意事项
|
||
1. 导入的 JSON 中 `fields` 数组需要与 `FormField` 类型结构兼容。由于 JSON 导入的是纯数据,直接赋值给 `template.fields` 即可(TypeScript 编译时类型校验通过)。
|
||
2. 导出文件名中包含模板标题,需注意标题中的特殊字符可能影响文件名(但浏览器通常会自动处理)。
|
||
3. Logo 占位符替换后,原有「西安交通大学第一附属医院」的样式应保持不变,仅替换 Logo 部分。
|
||
4. 新增模板弹窗关闭时,需同步重置 `importedContent` 为 `null`,避免影响下一次创建。
|