修复导入导出问题
This commit is contained in:
+10
-10
@@ -66,16 +66,6 @@ function switchView(view: 'editor' | 'manager' | 'presets') {
|
|||||||
</svg>
|
</svg>
|
||||||
<span>编辑器</span>
|
<span>编辑器</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
|
||||||
class="nav-btn"
|
|
||||||
:class="{ active: currentView === 'manager' }"
|
|
||||||
@click="switchView('manager')"
|
|
||||||
>
|
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M3 3h18v18H3zM9 9h6v6H9z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
|
||||||
<span>词库管理</span>
|
|
||||||
</button>
|
|
||||||
<button
|
<button
|
||||||
class="nav-btn"
|
class="nav-btn"
|
||||||
:class="{ active: currentView === 'presets' }"
|
:class="{ active: currentView === 'presets' }"
|
||||||
@@ -88,6 +78,16 @@ function switchView(view: 'editor' | 'manager' | 'presets') {
|
|||||||
</svg>
|
</svg>
|
||||||
<span>预设管理</span>
|
<span>预设管理</span>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
class="nav-btn"
|
||||||
|
:class="{ active: currentView === 'manager' }"
|
||||||
|
@click="switchView('manager')"
|
||||||
|
>
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M3 3h18v18H3zM9 9h6v6H9z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
<span>词库管理</span>
|
||||||
|
</button>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
|
|||||||
@@ -275,21 +275,22 @@ function formatDate(dateStr: string) {
|
|||||||
|
|
||||||
// 导入导出
|
// 导入导出
|
||||||
function exportPresets() {
|
function exportPresets() {
|
||||||
const data = {
|
try {
|
||||||
folders: store.presetFolders || [],
|
const jsonData = store.exportPresetsToJson();
|
||||||
presets: store.extendedPresets || [],
|
const blob = new Blob([jsonData], { type: 'application/json' });
|
||||||
exportedAt: new Date().toISOString()
|
const url = URL.createObjectURL(blob);
|
||||||
};
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
|
a.download = `presets-${new Date().toISOString().split('T')[0]}.json`;
|
||||||
const url = URL.createObjectURL(blob);
|
document.body.appendChild(a);
|
||||||
const a = document.createElement('a');
|
a.click();
|
||||||
a.href = url;
|
document.body.removeChild(a);
|
||||||
a.download = `presets-${new Date().toISOString().split('T')[0]}.json`;
|
URL.revokeObjectURL(url);
|
||||||
a.click();
|
|
||||||
URL.revokeObjectURL(url);
|
showNotification('预设已导出', 'success');
|
||||||
|
} catch (error) {
|
||||||
showNotification('预设已导出', 'success');
|
showNotification('导出失败', 'error');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function importPresets(event: Event) {
|
function importPresets(event: Event) {
|
||||||
@@ -299,18 +300,22 @@ function importPresets(event: Event) {
|
|||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = (e) => {
|
reader.onload = (e) => {
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(e.target?.result as string);
|
const jsonData = e.target?.result as string;
|
||||||
if (data.folders && data.presets) {
|
const success = store.importPresetsFromJson(jsonData);
|
||||||
store.importExtendedPresets(data);
|
|
||||||
|
if (success) {
|
||||||
showNotification('预设导入成功', 'success');
|
showNotification('预设导入成功', 'success');
|
||||||
} else {
|
} else {
|
||||||
showNotification('导入文件格式不正确', 'error');
|
showNotification('导入文件格式不正确或不是预设文件', 'error');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showNotification('导入失败,请检查文件格式', 'error');
|
showNotification('导入失败:文件格式错误', 'error');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
reader.readAsText(file);
|
reader.readAsText(file);
|
||||||
|
|
||||||
|
// 清空文件输入
|
||||||
|
(event.target as HTMLInputElement).value = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ function resetDefault() {
|
|||||||
</div>
|
</div>
|
||||||
<div class="pm-right">
|
<div class="pm-right">
|
||||||
<button class="pm-btn" @click="exportAll">导出 JSON</button>
|
<button class="pm-btn" @click="exportAll">导出 JSON</button>
|
||||||
<span class="pm-tip">导出 JSON 会同时包含你的预设</span>
|
<span class="pm-tip">导出 JSON 仅包含词库(不包含预设)</span>
|
||||||
<label class="pm-import pm-btn">导入 JSON
|
<label class="pm-import pm-btn">导入 JSON
|
||||||
<input type="file" accept="application/json" @change="importAll" />
|
<input type="file" accept="application/json" @change="importAll" />
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
+132
-17
@@ -141,45 +141,48 @@ export const usePromptStore = defineStore('promptStore', {
|
|||||||
if (!bundle) {
|
if (!bundle) {
|
||||||
if (!baseline) baseline = await loadInitialDataset();
|
if (!baseline) baseline = await loadInitialDataset();
|
||||||
this.dataset = deepClone(baseline!);
|
this.dataset = deepClone(baseline!);
|
||||||
this.presets = [];
|
// 重置时不清空预设数据,只重置词库
|
||||||
this.extendedPresets = [];
|
|
||||||
this.presetFolders = [];
|
|
||||||
} else {
|
} else {
|
||||||
// 确保兼容性
|
// 词库导入:只处理词库相关数据,不影响预设
|
||||||
bundle = this.ensureCompatibility(bundle);
|
|
||||||
|
|
||||||
if (bundle.dataset) {
|
if (bundle.dataset) {
|
||||||
this.dataset = bundle.dataset;
|
this.dataset = bundle.dataset;
|
||||||
} else if (bundle.customDiff) {
|
} else if (bundle.customDiff) {
|
||||||
if (!baseline) baseline = await loadInitialDataset();
|
if (!baseline) baseline = await loadInitialDataset();
|
||||||
this.dataset = this.applyDiff(deepClone(baseline!), bundle.customDiff);
|
this.dataset = this.applyDiff(deepClone(baseline!), bundle.customDiff);
|
||||||
}
|
}
|
||||||
this.presets = bundle.presets || [];
|
|
||||||
// 导入扩展预设数据
|
// 兼容旧版本:如果导入的是包含预设的旧格式,提示用户使用预设导入功能
|
||||||
this.extendedPresets = bundle.extendedPresets || [];
|
if (bundle.presets || bundle.extendedPresets || bundle.presetFolders) {
|
||||||
this.presetFolders = bundle.presetFolders || [];
|
console.warn('检测到预设数据,请使用预设管理页面的导入功能来导入预设数据');
|
||||||
if (bundle.presetManagement) {
|
|
||||||
this.presetManagement = bundle.presetManagement;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.selectedCategoryIndex = 0;
|
this.selectedCategoryIndex = 0;
|
||||||
this.selectedGroupIndex = 0;
|
this.selectedGroupIndex = 0;
|
||||||
// 确保扩展预设管理已初始化
|
|
||||||
this.initializeExtendedPresets();
|
|
||||||
this.save();
|
this.save();
|
||||||
},
|
},
|
||||||
exportToJson(): string {
|
exportToJson(): string {
|
||||||
// 导出仅包含自定义差异(不包含公共词库)
|
// 词库导出:仅包含自定义差异(不包含公共词库和预设数据)
|
||||||
const diff = this.buildDiff(baseline!, this.dataset!);
|
const diff = this.buildDiff(baseline!, this.dataset!);
|
||||||
const bundle: ExportBundle = {
|
const bundle: ExportBundle = {
|
||||||
version: 1,
|
version: 1,
|
||||||
savedAt: new Date().toISOString(),
|
savedAt: new Date().toISOString(),
|
||||||
customDiff: diff,
|
customDiff: diff,
|
||||||
presets: deepClone(this.presets),
|
// 词库导出不包含预设数据
|
||||||
// 导出扩展预设数据
|
};
|
||||||
|
return JSON.stringify(bundle, null, 2);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 预设导出:仅导出预设相关数据
|
||||||
|
exportPresetsToJson(): string {
|
||||||
|
const bundle = {
|
||||||
|
version: 1,
|
||||||
|
type: 'presets',
|
||||||
|
savedAt: new Date().toISOString(),
|
||||||
extendedPresets: deepClone(this.extendedPresets),
|
extendedPresets: deepClone(this.extendedPresets),
|
||||||
presetFolders: deepClone(this.presetFolders),
|
presetFolders: deepClone(this.presetFolders),
|
||||||
presetManagement: deepClone(this.presetManagement),
|
presetManagement: deepClone(this.presetManagement),
|
||||||
|
// 保留旧预设以兼容
|
||||||
|
presets: deepClone(this.presets),
|
||||||
};
|
};
|
||||||
return JSON.stringify(bundle, null, 2);
|
return JSON.stringify(bundle, null, 2);
|
||||||
},
|
},
|
||||||
@@ -811,6 +814,118 @@ export const usePromptStore = defineStore('promptStore', {
|
|||||||
this.save();
|
this.save();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 预设导入:专门处理预设导入文件
|
||||||
|
importPresetsFromJson(jsonData: string): boolean {
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(jsonData);
|
||||||
|
|
||||||
|
// 仅处理预设相关的导入文件
|
||||||
|
if (!(data.type === 'presets' || data.extendedPresets || data.presetFolders || data.presets)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1) 构建旧ID到新ID的映射,按名称合并已存在的文件夹
|
||||||
|
const idMap = new Map<string, string>();
|
||||||
|
const existingByName = new Map((this.presetFolders || []).map((f) => [f.name, f]));
|
||||||
|
|
||||||
|
const foldersToCreate: any[] = [];
|
||||||
|
const incomingFolders: any[] = Array.isArray(data.presetFolders) ? data.presetFolders : [];
|
||||||
|
|
||||||
|
// 先确定每个旧ID对应的新ID(复用同名文件夹,否则生成新ID)
|
||||||
|
for (const folder of incomingFolders) {
|
||||||
|
const sameName = existingByName.get(folder.name);
|
||||||
|
if (sameName) {
|
||||||
|
idMap.set(folder.id, sameName.id);
|
||||||
|
// 更新同名文件夹的基础信息(不改ID)
|
||||||
|
Object.assign(sameName, {
|
||||||
|
description: folder.description ?? sameName.description,
|
||||||
|
color: folder.color ?? sameName.color,
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const newId = `folder_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
||||||
|
idMap.set(folder.id, newId);
|
||||||
|
foldersToCreate.push({ ...folder, id: newId });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建不存在的文件夹,修正其父子关系(父ID按映射转换)
|
||||||
|
for (const folder of foldersToCreate) {
|
||||||
|
const created = {
|
||||||
|
id: folder.id,
|
||||||
|
name: folder.name,
|
||||||
|
description: folder.description ?? undefined,
|
||||||
|
color: folder.color ?? '#6366f1',
|
||||||
|
parentId: folder.parentId ? idMap.get(folder.parentId) : undefined,
|
||||||
|
createdAt: folder.createdAt || new Date().toISOString(),
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
} as PresetFolder;
|
||||||
|
this.presetFolders.push(created);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) 导入扩展预设,修正其 folderId 指向到新ID
|
||||||
|
const incomingPresets: any[] = Array.isArray(data.extendedPresets) ? data.extendedPresets : [];
|
||||||
|
for (const preset of incomingPresets) {
|
||||||
|
// 如果已存在同名同类型,则更新;否则创建新预设
|
||||||
|
const existing = (this.extendedPresets || []).find((p) => p.name === preset.name && p.type === preset.type);
|
||||||
|
const mappedFolderId = preset.folderId ? idMap.get(preset.folderId) : undefined;
|
||||||
|
|
||||||
|
if (existing) {
|
||||||
|
Object.assign(existing, {
|
||||||
|
content: preset.content,
|
||||||
|
description: preset.description ?? existing.description,
|
||||||
|
tags: Array.isArray(preset.tags) ? preset.tags : existing.tags,
|
||||||
|
folderId: mappedFolderId ?? existing.folderId,
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.extendedPresets.push({
|
||||||
|
id: `preset_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
|
||||||
|
name: preset.name,
|
||||||
|
type: preset.type,
|
||||||
|
content: preset.content,
|
||||||
|
description: preset.description ?? undefined,
|
||||||
|
tags: Array.isArray(preset.tags) ? preset.tags : undefined,
|
||||||
|
folderId: mappedFolderId,
|
||||||
|
createdAt: preset.createdAt || new Date().toISOString(),
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
} as ExtendedPreset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) 合并预设管理配置(不覆盖现有设置),无需变更 defaultFolder
|
||||||
|
if (data.presetManagement) {
|
||||||
|
this.presetManagement = {
|
||||||
|
...this.presetManagement,
|
||||||
|
folders: [...(this.presetManagement?.folders || []), ...(data.presetManagement.folders || [])],
|
||||||
|
presets: [...(this.presetManagement?.presets || []), ...(data.presetManagement.presets || [])],
|
||||||
|
settings: { ...(this.presetManagement?.settings || {}) },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4) 兼容旧预设格式(旧格式只含 presets: [{ name, text }...])
|
||||||
|
if (Array.isArray(data.presets)) {
|
||||||
|
for (const oldPreset of data.presets) {
|
||||||
|
this.extendedPresets.push({
|
||||||
|
id: `preset_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
|
||||||
|
name: oldPreset.name,
|
||||||
|
type: 'positive' as PresetType,
|
||||||
|
content: oldPreset.text,
|
||||||
|
description: '从旧格式导入',
|
||||||
|
createdAt: oldPreset.updatedAt || new Date().toISOString(),
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
} as ExtendedPreset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.save();
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('预设导入失败:', error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// 兼容性:从旧预设迁移到新预设系统
|
// 兼容性:从旧预设迁移到新预设系统
|
||||||
migrateOldPresets() {
|
migrateOldPresets() {
|
||||||
if (this.presets.length === 0) return; // 没有旧预设需要迁移
|
if (this.presets.length === 0) return; // 没有旧预设需要迁移
|
||||||
|
|||||||
Reference in New Issue
Block a user