再次完成

This commit is contained in:
Sjshi763
2026-01-28 23:10:56 +08:00
parent 1f75682dea
commit 92cf0bc9ef
5 changed files with 134 additions and 8 deletions

View File

@@ -88,11 +88,16 @@
"toolsCount": "tools",
"skillsCount": "skills",
"contextMenu": {
"moveTo": "Move to..."
"moveTo": "Move to...",
"export": "Export"
},
"messages": {
"moveSuccess": "Persona moved successfully",
"moveError": "Failed to move persona"
"moveError": "Failed to move persona",
"exportSuccess": "Exported successfully",
"exportError": "Export failed",
"importSuccess": "Imported successfully",
"importError": "Import failed"
}
},
"folder": {

View File

@@ -88,11 +88,16 @@
"toolsCount": "个工具",
"skillsCount": "个 Skills",
"contextMenu": {
"moveTo": "移动到..."
"moveTo": "移动到...",
"export": "导出"
},
"messages": {
"moveSuccess": "人格移动成功",
"moveError": "移动人格失败"
"moveError": "移动人格失败",
"exportSuccess": "导出成功",
"exportError": "导出失败",
"importSuccess": "导入成功",
"importError": "导入失败"
}
},
"folder": {

View File

@@ -330,5 +330,29 @@ export const usePersonaStore = defineStore({
};
return findNode(this.folderTree);
},
/**
* 导入人格数据
*/
async importPersona(data: Partial<Persona>): Promise<Persona> {
const response = await axios.post('/api/persona/create', {
persona_id: data.persona_id,
system_prompt: data.system_prompt,
begin_dialogs: data.begin_dialogs,
tools: data.tools,
skills: data.skills,
folder_id: data.folder_id ?? this.currentFolderId,
sort_order: data.sort_order ?? 0,
});
if (response.data.status !== 'ok') {
throw new Error(response.data.message || '导入人格失败');
}
// 刷新当前文件夹内容
await this.refreshCurrentFolder();
return response.data.data.persona;
},
}
});

View File

@@ -14,12 +14,18 @@
</template>
<v-list-item-title>{{ tm('buttons.edit') }}</v-list-item-title>
</v-list-item>
<v-list-item @click.stop="$emit('move')">
<v-list-item @click.stop="$emit('move')">
<template v-slot:prepend>
<v-icon size="small">mdi-folder-move</v-icon>
</template>
<v-list-item-title>{{ tm('persona.contextMenu.moveTo') }}</v-list-item-title>
</v-list-item>
<v-list-item @click.stop="$emit('export')">
<template v-slot:prepend>
<v-icon size="small">mdi-download</v-icon>
</template>
<v-list-item-title>{{ tm('persona.contextMenu.export') }}</v-list-item-title>
</v-list-item>
<v-divider class="my-1" />
<v-list-item @click.stop="$emit('delete')" class="text-error">
<template v-slot:prepend>
@@ -96,7 +102,7 @@ export default defineComponent({
required: true
}
},
emits: ['view', 'edit', 'move', 'delete'],
emits: ['view', 'edit', 'move', 'export', 'delete'],
setup() {
const { tm } = useModuleI18n('features/persona');
return { tm };

View File

@@ -36,6 +36,10 @@
rounded="lg">
{{ tm('folder.createButton') }}
</v-btn>
<v-btn variant="outlined" prepend-icon="mdi-upload" @click="handleImportPersona"
rounded="lg">
{{ tm('buttons.import') }}
</v-btn>
</div>
</div>
@@ -80,7 +84,7 @@
xl="3">
<PersonaCard :persona="persona" @view="viewPersona(persona)"
@edit="editPersona(persona)" @move="openMovePersonaDialog(persona)"
@delete="confirmDeletePersona(persona)" />
@export="handleExportPersona(persona)" @delete="confirmDeletePersona(persona)" />
</v-col>
</v-row>
</div>
@@ -384,7 +388,7 @@ export default defineComponent({
await this.initialize();
},
methods: {
...mapActions(usePersonaStore, ['loadFolderTree', 'navigateToFolder', 'updateFolder', 'deleteFolder', 'deletePersona', 'refreshCurrentFolder', 'movePersonaToFolder']),
...mapActions(usePersonaStore, ['loadFolderTree', 'navigateToFolder', 'updateFolder', 'deleteFolder', 'deletePersona', 'refreshCurrentFolder', 'movePersonaToFolder', 'importPersona']),
async initialize() {
await Promise.all([
@@ -444,6 +448,88 @@ export default defineComponent({
}
},
// 导出人格数据
async handleExportPersona(persona: Persona) {
try {
console.log('开始导出人格:', persona.persona_id);
// 直接使用前端已有的 persona 数据
const personaData = persona;
console.log('导出成功,人格数据:', personaData);
// 清理文件名中的特殊字符
const safeFileName = personaData.persona_id.replace(/[\/\\:*?"<>|]/g, '_');
// 创建 JSON 文件并下载
const jsonStr = JSON.stringify(personaData, null, 2);
const blob = new Blob([jsonStr], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `${safeFileName}.json`;
document.body.appendChild(link);
link.click();
// 清理
document.body.removeChild(link);
URL.revokeObjectURL(url);
this.showSuccess(this.tm('persona.messages.exportSuccess'));
} catch (error: any) {
console.error('导出人格失败:', error);
// 构建详细的错误消息
let errorMessage = this.tm('persona.messages.exportError');
if (error.message) {
errorMessage += `: ${error.message}`;
}
this.showError(errorMessage);
}
},
// 导入人格数据
async handleImportPersona() {
const input = document.createElement('input');
input.type = 'file';
input.accept = 'application/json';
input.onchange = async (event: Event) => {
const target = event.target as HTMLInputElement;
const file = target.files?.[0];
if (!file) return;
try {
const text = await file.text();
const data = JSON.parse(text);
// 白名单过滤
const whitelist = ['persona_id', 'system_prompt', 'begin_dialogs', 'tools', 'skills', 'folder_id'];
const filteredData: Partial<Persona> = {};
for (const key of whitelist) {
if (key in data) {
filteredData[key as keyof Persona] = data[key];
}
}
// 验证必需字段
if (!filteredData.persona_id || !filteredData.system_prompt) {
throw new Error('导入数据缺少必需字段');
}
// 执行导入
await (this as any).importPersona(filteredData);
this.showSuccess(this.tm('persona.messages.importSuccess'));
} catch (error: any) {
this.showError(error.message || this.tm('persona.messages.importError'));
}
};
input.click();
},
// 文件夹操作
openRenameFolderDialog(folder: Folder) {
this.renameFolderData = { folder, name: folder.name };