mirror of
https://github.com/AstrBotDevs/AstrBot
synced 2026-07-15 17:30:13 +08:00
再次完成
This commit is contained in:
@@ -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": {
|
||||
|
||||
@@ -88,11 +88,16 @@
|
||||
"toolsCount": "个工具",
|
||||
"skillsCount": "个 Skills",
|
||||
"contextMenu": {
|
||||
"moveTo": "移动到..."
|
||||
"moveTo": "移动到...",
|
||||
"export": "导出"
|
||||
},
|
||||
"messages": {
|
||||
"moveSuccess": "人格移动成功",
|
||||
"moveError": "移动人格失败"
|
||||
"moveError": "移动人格失败",
|
||||
"exportSuccess": "导出成功",
|
||||
"exportError": "导出失败",
|
||||
"importSuccess": "导入成功",
|
||||
"importError": "导入失败"
|
||||
}
|
||||
},
|
||||
"folder": {
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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 };
|
||||
|
||||
Reference in New Issue
Block a user