feat(agent): 支持自定义API配置并优化UI交互

- 为agent.json添加apiUrl、apiKey、apiModel字段支持
- 更新API接口类型定义,支持传递自定义API配置
- 优化AgentRepoList组件UI样式和交互效果
- 增强JSON文件上传校验逻辑,支持API配置验证
- 改进任务结果页面布局和视觉呈现
- 添加任务过程查看抽屉功能
- 实现执行按钮动态样式和悬停效果
- 优化节点连接线渲染逻辑和性能
This commit is contained in:
zhaoweijie
2025-12-15 20:46:54 +08:00
parent 6392301833
commit 77530c49f8
25 changed files with 2040 additions and 306 deletions

View File

@@ -1,4 +1,5 @@
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { ElNotification } from 'element-plus'
import { pick } from 'lodash'
@@ -7,7 +8,6 @@ import api from '@/api/index.ts'
import SvgIcon from '@/components/SvgIcon/index.vue'
import { agentMapDuty } from '@/layout/components/config.ts'
import { type Agent, useAgentsStore } from '@/stores'
import { onMounted } from 'vue'
import { readConfig } from '@/utils/readJson.ts'
import AgentRepoList from './AgentRepoList.vue'
@@ -19,7 +19,7 @@ onMounted(async () => {
const res = await readConfig<Agent[]>('agent.json')
agentsStore.setAgents(res)
}
await api.setAgents(agentsStore.agents.map((item) => pick(item, ['Name', 'Profile'])))
await api.setAgents(agentsStore.agents.map(item => pick(item, ['Name', 'Profile'])))
})
// 上传agent文件
@@ -37,50 +37,150 @@ const handleFileSelect = (event: Event) => {
}
}
const readFileContent = async (file: File) => {
const reader = new FileReader()
reader.onload = async (e) => {
if (!e.target?.result) {
return
}
try {
const json = JSON.parse(e.target.result?.toString?.() ?? '{}')
// 处理 JSON 数据
if (Array.isArray(json)) {
const isValid = json.every(
(item) =>
typeof item.Name === 'string' &&
typeof item.Icon === 'string' &&
typeof item.Profile === 'string',
)
if (isValid) {
// 处理有效的 JSON 数据
agentsStore.setAgents(
json.map((item) => ({
Name: item.Name,
Icon: item.Icon.replace(/\.png$/, ''),
Profile: item.Profile,
Classification: item.Classification,
})),
)
await api.setAgents(json.map((item) => pick(item, ['Name', 'Profile'])))
} else {
ElNotification.error({
title: '错误',
message: 'JSON 格式错误',
})
}
} else {
console.error('JSON is not an array')
ElNotification.error({
title: '错误',
message: 'JSON 格式错误',
})
// 在validateApiConfig函数中添加更详细的日志
const validateApiConfig = (agent: any) => {
const hasApiUrl = 'apiUrl' in agent
const hasApiKey = 'apiKey' in agent
const hasApiModel = 'apiModel' in agent
// 三个字段必须同时存在或同时不存在
if (hasApiUrl !== hasApiKey || hasApiKey !== hasApiModel) {
console.error('❌ API配置不完整:', {
agentName: agent.Name,
missingFields: {
apiUrl: !hasApiUrl,
apiKey: !hasApiKey,
apiModel: !hasApiModel
}
} catch (e) {
console.error(e)
})
return false
}
if (hasApiUrl && hasApiKey && hasApiModel) {
console.log('✅ API配置完整将使用自定义API配置:', {
apiUrl: agent.apiUrl,
apiKey: agent.apiKey ? '***' + agent.apiKey.slice(-4) : '未设置',
apiModel: agent.apiModel
})
} else {
console.log(' 未设置API配置将使用默认URL配置')
}
return true
}
const readFileContent = (file: File) => {
console.log('📁 开始读取文件:', file.name, '大小:', file.size, '类型:', file.type)
const reader = new FileReader()
reader.onload = e => {
try {
console.log('📄 文件读取完成开始解析JSON')
const content = e.target?.result as string
const jsonData = JSON.parse(content)
console.log('🔍 解析的JSON数据:', jsonData)
console.log(
'📊 数据类型:',
Array.isArray(jsonData) ? '数组' : '对象',
'长度:',
Array.isArray(jsonData) ? jsonData.length : 'N/A'
)
if (!Array.isArray(jsonData)) {
console.error('❌ JSON格式错误: 必须为数组格式')
ElMessage.error('JSON格式错误: 必须为数组格式')
return
}
console.log('🔍 开始验证智能体数据...')
const validAgents = jsonData.filter((agent, index) => {
console.log(`🔍 验证第${index + 1}个智能体:`, agent.Name || '未命名')
// 验证必需字段
if (!agent.Name || typeof agent.Name !== 'string') {
console.error(`❌ 智能体${index + 1}缺少Name字段或格式错误`)
return false
}
if (!agent.Icon || typeof agent.Icon !== 'string') {
console.error(`❌ 智能体${index + 1}缺少Icon字段或格式错误`)
return false
}
if (!agent.Profile || typeof agent.Profile !== 'string') {
console.error(`❌ 智能体${index + 1}缺少Profile字段或格式错误`)
return false
}
// 验证API配置
if (!validateApiConfig(agent)) {
console.error(`❌ 智能体${index + 1}API配置验证失败`)
return false
}
return true
})
console.log('🔄 开始处理智能体数据...')
// 修改发送到后端的数据
const processedAgents = validAgents.map(agent => ({
Name: agent.Name,
Profile: agent.Profile,
Icon: agent.Icon,
Classification: agent.Classification || '',
apiUrl: agent.apiUrl,
apiKey: agent.apiKey,
apiModel: agent.apiModel
}))
console.log(
'📤 发送到后端的智能体数据:',
processedAgents.map(a => ({
Name: a.Name,
apiUrl: a.apiUrl,
apiKey: a.apiKey ? '***' + a.apiKey.slice(-4) : '未设置',
apiModel: a.apiModel
}))
)
// 调用API
api
.setAgents(processedAgents)
.then(response => {
console.log('✅ 后端API调用成功响应:', response)
ElMessage.success('智能体上传成功')
})
.catch(error => {
console.error('❌ 后端API调用失败:', error)
ElMessage.error('智能体上传失败')
})
// 更新store
console.log('💾 更新智能体store...')
agentsStore.setAgents(processedAgents)
console.log('✅ 智能体store更新完成')
// 调用API
console.log('🌐 开始调用后端API...')
api
.setAgents(processedAgents)
.then(() => {
console.log('✅ 后端API调用成功')
ElMessage.success('智能体上传成功')
})
.catch(error => {
console.error('❌ 后端API调用失败:', error)
ElMessage.error('智能体上传失败')
})
} catch (error) {
console.error('❌ JSON解析错误:', error)
ElMessage.error('JSON解析错误')
}
}
reader.onerror = error => {
console.error('❌ 文件读取错误:', error)
ElMessage.error('文件读取错误')
}
reader.readAsText(file)
}
@@ -95,7 +195,7 @@ const agentList = computed(() => {
if (!agentsStore.agents.length) {
return {
selected,
unselected,
unselected
}
}
for (const agent of agentsStore.agents) {
@@ -110,14 +210,14 @@ const agentList = computed(() => {
obj[agent.Classification] = arr
unselected.push({
title: agent.Classification,
data: arr,
data: arr
})
}
}
return {
selected,
unselected: unselected,
unselected: unselected
}
})
</script>
@@ -126,7 +226,7 @@ const agentList = computed(() => {
<div class="agent-repo h-full flex flex-col" id="agent-repo">
<!-- 头部 -->
<div class="flex items-center justify-between">
<span class="text-[18px] font-bold">智能体库</span>
<span class="text-[18px] font-bold text-[var(--color-text-title-header)]">智能体库</span>
<!-- 上传文件 -->
<input type="file" accept=".json" @change="handleFileSelect" class="hidden" ref="fileInput" />
<div class="plus-button" @click="triggerFileSelect">
@@ -144,14 +244,22 @@ const agentList = computed(() => {
</div>
</div>
<!-- 底部提示栏 -->
<div class="w-full grid grid-cols-3 gap-x-[10px] bg-[#1d222b] rounded-[20px] p-[8px] mt-[10px]">
<div
class="w-full grid grid-cols-3 gap-x-[10px] bg-[var(--color-bg-indicator)] rounded-[20px] p-[8px] mt-[10px]"
>
<div
v-for="item in Object.values(agentMapDuty)"
:key="item.key"
class="flex items-center justify-center gap-x-1"
>
<div
class="w-[8px] h-[8px] rounded-full"
:style="{
background: item.color,
border: `1px solid ${item.border}`
}"
></div>
<span class="text-[12px]">{{ item.name }}</span>
<div class="w-[8px] h-[8px] rounded-full" :style="{ background: item.color }"></div>
</div>
</div>
</div>
@@ -162,7 +270,7 @@ const agentList = computed(() => {
padding: 0 8px;
.plus-button {
background: #1d2128;
background: var(--color-bg-tertiary);
width: 24px;
height: 24px;
padding: 0;
@@ -174,7 +282,7 @@ const agentList = computed(() => {
transition: all 0.3s ease;
&:hover {
background: #374151;
background: var(--color-bg-quaternary);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);
}
}