android应用
This commit is contained in:
@@ -92,7 +92,7 @@
|
||||
:node-types="customNodeTypes"
|
||||
:connection-line-style="{ stroke: '#409eff', strokeWidth: 2.5, strokeDasharray: '5,5' }"
|
||||
:default-edge-options="{
|
||||
type: 'smoothstep',
|
||||
type: 'bezier',
|
||||
animated: true,
|
||||
selectable: true,
|
||||
deletable: true,
|
||||
@@ -100,7 +100,7 @@
|
||||
style: { stroke: '#409eff', strokeWidth: 2.5 },
|
||||
markerEnd: { type: 'arrowclosed', color: '#409eff', width: 20, height: 20 }
|
||||
}"
|
||||
:connection-line-type="'smoothstep'"
|
||||
:connection-line-type="'bezier'"
|
||||
:edges-focusable="true"
|
||||
:nodes-focusable="true"
|
||||
:delete-key-code="'Delete'"
|
||||
@@ -1902,7 +1902,7 @@ const onConnect = (connection: Connection) => {
|
||||
target: connection.target,
|
||||
sourceHandle: connection.sourceHandle || undefined,
|
||||
targetHandle: connection.targetHandle || undefined,
|
||||
type: 'smoothstep', // 使用平滑步进曲线(类似 Dify)
|
||||
type: 'bezier', // 使用贝塞尔曲线(平滑曲线)
|
||||
animated: true,
|
||||
selectable: true,
|
||||
deletable: true,
|
||||
@@ -2700,7 +2700,7 @@ watch(
|
||||
selectable: true,
|
||||
deletable: true,
|
||||
focusable: true,
|
||||
type: edge.type || 'smoothstep',
|
||||
type: edge.type || 'bezier',
|
||||
animated: true,
|
||||
style: {
|
||||
stroke: '#409eff',
|
||||
@@ -2955,7 +2955,7 @@ onMounted(async () => {
|
||||
selectable: true,
|
||||
deletable: true,
|
||||
focusable: true,
|
||||
type: edge.type || 'smoothstep',
|
||||
type: edge.type || 'bezier',
|
||||
animated: true,
|
||||
style: {
|
||||
stroke: '#409eff',
|
||||
@@ -3006,7 +3006,7 @@ onMounted(async () => {
|
||||
target: edge.target,
|
||||
sourceHandle: edge.sourceHandle,
|
||||
targetHandle: edge.targetHandle,
|
||||
type: edge.type || 'smoothstep',
|
||||
type: edge.type || 'bezier',
|
||||
animated: true,
|
||||
selectable: true,
|
||||
deletable: true,
|
||||
|
||||
@@ -166,6 +166,61 @@ export const useAgentStore = defineStore('agent', () => {
|
||||
currentAgent.value = agent
|
||||
}
|
||||
|
||||
// 导出Agent
|
||||
const exportAgent = async (agentId: string) => {
|
||||
try {
|
||||
const response = await api.get(`/api/v1/agents/${agentId}/export`)
|
||||
|
||||
if (!response.data) {
|
||||
throw new Error('导出数据为空')
|
||||
}
|
||||
|
||||
// 创建下载链接
|
||||
const dataStr = JSON.stringify(response.data, null, 2)
|
||||
const dataBlob = new Blob([dataStr], { type: 'application/json;charset=utf-8' })
|
||||
const url = URL.createObjectURL(dataBlob)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
|
||||
// 处理文件名:移除特殊字符,避免下载失败
|
||||
const agentName = (response.data.name || 'agent')
|
||||
.replace(/[<>:"/\\|?*]/g, '_') // 移除Windows不允许的字符
|
||||
.replace(/\s+/g, '_') // 空格替换为下划线
|
||||
.substring(0, 50) // 限制长度
|
||||
|
||||
link.download = `${agentName}_${Date.now()}.json`
|
||||
link.style.display = 'none'
|
||||
document.body.appendChild(link)
|
||||
|
||||
// 触发下载
|
||||
link.click()
|
||||
|
||||
// 清理
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(link)
|
||||
URL.revokeObjectURL(url)
|
||||
}, 100)
|
||||
|
||||
return response.data
|
||||
} catch (error: any) {
|
||||
console.error('导出Agent失败', error)
|
||||
const errorMessage = error.response?.data?.detail || error.message || '导出失败'
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
}
|
||||
|
||||
// 导入Agent
|
||||
const importAgent = async (agentData: any) => {
|
||||
loading.value = true
|
||||
try {
|
||||
const response = await api.post('/api/v1/agents/import', agentData)
|
||||
agents.value.unshift(response.data) // 添加到列表开头
|
||||
return response.data
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
agents,
|
||||
currentAgent,
|
||||
@@ -178,6 +233,8 @@ export const useAgentStore = defineStore('agent', () => {
|
||||
deployAgent,
|
||||
stopAgent,
|
||||
duplicateAgent,
|
||||
setCurrentAgent
|
||||
setCurrentAgent,
|
||||
exportAgent,
|
||||
importAgent
|
||||
}
|
||||
})
|
||||
|
||||
@@ -5,10 +5,16 @@
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<h2>Agent管理</h2>
|
||||
<el-button type="primary" @click="handleCreate">
|
||||
<el-icon><Plus /></el-icon>
|
||||
创建Agent
|
||||
</el-button>
|
||||
<div>
|
||||
<el-button @click="handleImport">
|
||||
<el-icon><Upload /></el-icon>
|
||||
导入Agent
|
||||
</el-button>
|
||||
<el-button type="primary" @click="handleCreate" style="margin-left: 10px">
|
||||
<el-icon><Plus /></el-icon>
|
||||
创建Agent
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -70,12 +76,21 @@
|
||||
{{ formatDate(row.created_at) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="350" fixed="right">
|
||||
<el-table-column label="操作" width="480" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" @click="handleEdit(row)">
|
||||
<el-icon><Edit /></el-icon>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="row.status === 'published' || row.status === 'running'"
|
||||
link
|
||||
type="primary"
|
||||
@click="handleUse(row)"
|
||||
>
|
||||
<el-icon><ChatDotRound /></el-icon>
|
||||
使用
|
||||
</el-button>
|
||||
<el-button link type="primary" @click="handleDesign(row)">
|
||||
<el-icon><Setting /></el-icon>
|
||||
设计
|
||||
@@ -84,6 +99,10 @@
|
||||
<el-icon><CopyDocument /></el-icon>
|
||||
复制
|
||||
</el-button>
|
||||
<el-button link type="success" @click="handleExport(row)">
|
||||
<el-icon><Download /></el-icon>
|
||||
导出
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="row.status === 'draft' || row.status === 'stopped'"
|
||||
link
|
||||
@@ -164,6 +183,63 @@
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 导入Agent对话框 -->
|
||||
<el-dialog
|
||||
v-model="importDialogVisible"
|
||||
title="导入Agent"
|
||||
width="600px"
|
||||
@close="handleImportDialogClose"
|
||||
>
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
:auto-upload="false"
|
||||
:on-change="handleFileChange"
|
||||
:file-list="fileList"
|
||||
accept=".json"
|
||||
drag
|
||||
>
|
||||
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||||
<div class="el-upload__text">
|
||||
将JSON文件拖到此处,或<em>点击上传</em>
|
||||
</div>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">
|
||||
只能上传JSON格式的Agent配置文件
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
|
||||
<div v-if="importFileContent" style="margin-top: 20px;">
|
||||
<el-alert
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
>
|
||||
<template #title>
|
||||
<div>
|
||||
<div><strong>Agent名称:</strong> {{ importFileContent.name || '未命名' }}</div>
|
||||
<div v-if="importFileContent.description" style="margin-top: 5px;">
|
||||
<strong>描述:</strong> {{ importFileContent.description }}
|
||||
</div>
|
||||
<div style="margin-top: 5px;">
|
||||
<strong>节点数:</strong> {{ importFileContent.workflow_config?.nodes?.length || 0 }}
|
||||
<span style="margin-left: 20px;">
|
||||
<strong>连接数:</strong> {{ importFileContent.workflow_config?.edges?.length || 0 }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-alert>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="importDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleConfirmImport" :loading="importing" :disabled="!importFileContent">
|
||||
确认导入
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</MainLayout>
|
||||
</template>
|
||||
@@ -182,7 +258,11 @@ import {
|
||||
Setting,
|
||||
VideoPlay,
|
||||
VideoPause,
|
||||
CopyDocument
|
||||
CopyDocument,
|
||||
Upload,
|
||||
Download,
|
||||
UploadFilled,
|
||||
ChatDotRound
|
||||
} from '@element-plus/icons-vue'
|
||||
import { useAgentStore } from '@/stores/agent'
|
||||
import type { Agent } from '@/stores/agent'
|
||||
@@ -225,6 +305,13 @@ const form = ref({
|
||||
})
|
||||
const currentAgentId = ref<string | null>(null)
|
||||
|
||||
// 导入相关
|
||||
const importDialogVisible = ref(false)
|
||||
const fileList = ref<any[]>([])
|
||||
const importFileContent = ref<any>(null)
|
||||
const importing = ref(false)
|
||||
const uploadRef = ref()
|
||||
|
||||
// 表单验证规则
|
||||
const rules = {
|
||||
name: [
|
||||
@@ -331,6 +418,14 @@ const handleEdit = (agent: Agent) => {
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 使用Agent(跳转到设计器,那里有聊天界面)
|
||||
const handleUse = (agent: Agent) => {
|
||||
router.push({
|
||||
name: 'AgentDesigner',
|
||||
params: { id: agent.id }
|
||||
})
|
||||
}
|
||||
|
||||
// 设计
|
||||
const handleDesign = (agent: Agent) => {
|
||||
router.push({
|
||||
@@ -427,6 +522,75 @@ const handleDelete = async (agent: Agent) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 导出Agent
|
||||
const handleExport = async (agent: Agent) => {
|
||||
try {
|
||||
await agentStore.exportAgent(agent.id)
|
||||
ElMessage.success('Agent导出成功')
|
||||
} catch (error: any) {
|
||||
console.error('导出失败详情:', error)
|
||||
const errorMessage = error.message || error.response?.data?.detail || '导出失败,请查看控制台获取详细信息'
|
||||
ElMessage.error(errorMessage)
|
||||
}
|
||||
}
|
||||
|
||||
// 显示导入对话框
|
||||
const handleImport = () => {
|
||||
importDialogVisible.value = true
|
||||
fileList.value = []
|
||||
importFileContent.value = null
|
||||
}
|
||||
|
||||
// 文件选择
|
||||
const handleFileChange = (file: any) => {
|
||||
const reader = new FileReader()
|
||||
reader.onload = (e) => {
|
||||
try {
|
||||
const content = JSON.parse(e.target?.result as string)
|
||||
importFileContent.value = content
|
||||
} catch (error) {
|
||||
ElMessage.error('文件格式错误,请上传有效的JSON文件')
|
||||
fileList.value = []
|
||||
importFileContent.value = null
|
||||
}
|
||||
}
|
||||
reader.readAsText(file.raw)
|
||||
}
|
||||
|
||||
// 关闭导入对话框
|
||||
const handleImportDialogClose = () => {
|
||||
fileList.value = []
|
||||
importFileContent.value = null
|
||||
if (uploadRef.value) {
|
||||
uploadRef.value.clearFiles()
|
||||
}
|
||||
}
|
||||
|
||||
// 确认导入
|
||||
const handleConfirmImport = async () => {
|
||||
if (!importFileContent.value) {
|
||||
ElMessage.warning('请先选择要导入的文件')
|
||||
return
|
||||
}
|
||||
|
||||
importing.value = true
|
||||
try {
|
||||
const agent = await agentStore.importAgent(importFileContent.value)
|
||||
importDialogVisible.value = false
|
||||
ElMessage.success('Agent导入成功')
|
||||
await loadAgents()
|
||||
// 跳转到Agent设计器
|
||||
router.push({
|
||||
name: 'AgentDesigner',
|
||||
params: { id: agent.id }
|
||||
})
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.response?.data?.detail || '导入Agent失败')
|
||||
} finally {
|
||||
importing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const handleSubmit = async () => {
|
||||
if (!formRef.value) return
|
||||
|
||||
Reference in New Issue
Block a user