diff --git a/dashboard/src/components/shared/PersonaForm.vue b/dashboard/src/components/shared/PersonaForm.vue index 48f1a0d0e..bfd5caf1b 100644 --- a/dashboard/src/components/shared/PersonaForm.vue +++ b/dashboard/src/components/shared/PersonaForm.vue @@ -378,16 +378,26 @@ export default { } this.saving = true; - try { - const url = this.editingPersona ? '/api/persona/update' : '/api/persona/create'; - const response = await axios.post(url, this.personaForm); - - if (response.data.status === 'ok') { - this.$emit('saved', response.data.message || this.tm('messages.saveSuccess')); - this.closeDialog(); - } else { - this.$emit('error', response.data.message || this.tm('messages.saveError')); + try { + const url = this.editingPersona ? '/api/persona/update' : '/api/persona/create'; + + // 白名单过滤字段 + const allowedFields = ['persona_id', 'system_prompt', 'begin_dialogs', 'tools']; + const filteredData = {}; + allowedFields.forEach(field => { + if (this.personaForm.hasOwnProperty(field)) { + filteredData[field] = this.personaForm[field]; } + }); + + const response = await axios.post(url, filteredData); + + if (response.data.status === 'ok') { + this.$emit('saved', response.data.message || this.tm('messages.saveSuccess')); + this.closeDialog(); + } else { + this.$emit('error', response.data.message || this.tm('messages.saveError')); + } } catch (error) { this.$emit('error', error.response?.data?.message || this.tm('messages.saveError')); } @@ -533,4 +543,4 @@ export default { .v-virtual-scroll { padding-bottom: 16px; } - \ No newline at end of file + diff --git a/dashboard/src/i18n/locales/en-US/features/persona.json b/dashboard/src/i18n/locales/en-US/features/persona.json index 94708ee56..9ce06dcc8 100644 --- a/dashboard/src/i18n/locales/en-US/features/persona.json +++ b/dashboard/src/i18n/locales/en-US/features/persona.json @@ -7,6 +7,8 @@ "createFirst": "Create First Persona", "edit": "Edit", "delete": "Delete", + "export": "Export JSON", + "import": "Import", "cancel": "Cancel", "save": "Save", "addDialogPair": "Add Dialog Pair" diff --git a/dashboard/src/i18n/locales/zh-CN/features/persona.json b/dashboard/src/i18n/locales/zh-CN/features/persona.json index 15121df41..ee8f3ee1a 100644 --- a/dashboard/src/i18n/locales/zh-CN/features/persona.json +++ b/dashboard/src/i18n/locales/zh-CN/features/persona.json @@ -7,6 +7,8 @@ "createFirst": "创建第一个人格", "edit": "编辑", "delete": "删除", + "export": "导出 JSON", + "import": "导入", "cancel": "取消", "save": "保存", "addDialogPair": "添加对话对" diff --git a/dashboard/src/views/PersonaPage.vue b/dashboard/src/views/PersonaPage.vue index cffeeb549..b23ae7e12 100644 --- a/dashboard/src/views/PersonaPage.vue +++ b/dashboard/src/views/PersonaPage.vue @@ -11,11 +11,16 @@ {{ tm('page.description') }}

-
+
+ + {{ tm('buttons.import') || '导入' }} + {{ tm('buttons.create') }} +
@@ -40,6 +45,12 @@ {{ tm('buttons.edit') }} + + + mdi-content-copy + {{ tm('buttons.export') || '导出 JSON' }} + + mdi-delete @@ -270,6 +281,105 @@ export default { this.message = message; this.messageType = 'error'; this.showMessage = true; + }, + + async downloadPersonaJson(persona) { + try { + // 创建清洁副本,排除系统字段 + const cleanPersona = { + persona_id: persona.persona_id, + system_prompt: persona.system_prompt, + begin_dialogs: persona.begin_dialogs, + tools: persona.tools + }; + + // 格式化 JSON + const jsonString = JSON.stringify(cleanPersona, null, 4); + + // 创建 Blob 对象 + const blob = new Blob([jsonString], { type: 'application/json' }); + + // 创建下载链接 + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = `${persona.persona_id}.json`; + + // 触发下载 + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + + // 清理 URL 对象 + URL.revokeObjectURL(url); + + // 显示成功消息 + this.showSuccess(this.tm('messages.downloadSuccess') || 'JSON 文件已下载'); + } catch (error) { + // 显示错误消息 + this.showError(error.message || this.tm('messages.downloadError') || '下载 JSON 文件失败'); + } + }, + + triggerImport() { + this.$refs.importInput.click(); + }, + + async handleImport(event) { + const file = event.target.files[0]; + if (!file) return; + + try { + const text = await file.text(); + const parsedData = JSON.parse(text); + + console.log("Parsed Data:", parsedData); + + // 验证必需字段 + if (!parsedData.persona_id || !parsedData.system_prompt) { + this.showError('人格 JSON 缺少必需字段喵!'); + event.target.value = ''; + return; + } + + // 检查重复 ID + const id = parsedData.persona_id; + const exists = this.personas.some(persona => persona.persona_id === id); + if (exists) { + this.showError('人格 ID [' + id + '] 已存在喵!'); + event.target.value = ''; + return; + } + + // 白名单过滤字段 + const allowedFields = ['persona_id', 'system_prompt', 'begin_dialogs', 'tools']; + const filteredData = {}; + allowedFields.forEach(field => { + if (parsedData.hasOwnProperty(field)) { + filteredData[field] = parsedData[field]; + } + }); + + // 调用 API 保存(使用正确的端点) + const response = await axios.post('/api/persona/create', filteredData); + + if (response.data.status === 'ok') { + this.showSuccess(response.data.message || '导入成功喵!'); + await this.loadPersonas(); + } else { + this.showError(response.data.message || '导入失败喵!'); + } + } catch (error) { + console.error("Import Error:", error); + if (error instanceof SyntaxError) { + this.showError('JSON 格式错误喵!' + error.message); + } else { + this.showError('导入失败喵!' + (error.response?.data?.message || error.message)); + } + } + + // 清理文件输入 + event.target.value = ''; } } }