2026-03-05 10:49:46 +08:00
< script setup lang = "ts" >
2026-03-10 16:09:09 +08:00
import { ref , computed } from 'vue'
2026-03-05 10:49:46 +08:00
2026-03-11 14:26:25 +08:00
// 创建智能体弹窗状态
const showCreateModal = ref ( false )
const isCreating = ref ( false )
const newAgent = ref ( {
name : '' ,
description : '' ,
skills : '' ,
knowledge : '' ,
prompt : '' ,
} )
// Skills 选项
const skillsOptions = [
{ value : 'research' , label : 'Research' } ,
{ value : 'coder' , label : 'Coder' } ,
{ value : 'review' , label : 'Code Review' } ,
{ value : 'writer' , label : 'Writer' } ,
{ value : 'analyst' , label : 'Analyst' } ,
{ value : 'assistant' , label : 'Assistant' } ,
]
// Knowledge 选项
const knowledgeOptions = [
{ value : 'general' , label : 'General Knowledge' } ,
{ value : 'codebase' , label : 'Codebase' } ,
{ value : 'docs' , label : 'Documentation' } ,
{ value : 'api' , label : 'API Reference' } ,
]
// 打开创建弹窗
const openCreateModal = ( ) => {
newAgent . value = { name : '' , description : '' , skills : '' , knowledge : '' , prompt : '' }
showCreateModal . value = true
}
// 创建智能体
const createAgent = async ( ) => {
if ( ! newAgent . value . name || ! newAgent . value . skills || ! newAgent . value . knowledge ) {
return
}
isCreating . value = true
try {
// 模拟创建
const newId = Math . max ( ... agents . value . map ( a => a . id ) ) + 1
agents . value . unshift ( {
id : newId ,
name : newAgent . value . name ,
avatar : '🤖' ,
description : newAgent . value . description ,
accentColor : '#f97316' ,
gradient : 'from-orange-500/20 to-amber-500/20' ,
status : 'stopped' ,
framework : skillsOptions . find ( f => f . value === newAgent . value . skills ) ? . label || newAgent . value . skills ,
model : knowledgeOptions . find ( k => k . value === newAgent . value . knowledge ) ? . label || newAgent . value . knowledge ,
mcpServers : 0 ,
createdAt : new Date ( ) . toISOString ( ) . split ( 'T' ) [ 0 ] ,
} )
showCreateModal . value = false
} finally {
isCreating . value = false
}
}
2026-03-05 10:49:46 +08:00
interface Agent {
id : number
name : string
2026-03-10 15:42:21 +08:00
avatar : string
2026-03-05 10:49:46 +08:00
description : string
2026-03-10 15:42:21 +08:00
accentColor : string
gradient : string
2026-03-10 16:09:09 +08:00
status : 'running' | 'stopped'
framework : string
model : string
mcpServers : number
createdAt : string
2026-03-05 10:49:46 +08:00
}
2026-03-10 16:09:09 +08:00
// 管理页面的 agents
2026-03-05 10:49:46 +08:00
const agents = ref < Agent [ ] > ( [
2026-03-10 16:09:09 +08:00
{ id : 1 , name : 'Claude Agent' , avatar : '🧠' , description : 'General purpose AI assistant' , accentColor : '#f97316' , gradient : 'from-orange-500/20 to-amber-500/20' , status : 'running' , framework : 'Google ADK' , model : 'gemini-2.0-flash' , mcpServers : 2 , createdAt : '2025-04-10' } ,
{ id : 2 , name : 'Code Assistant' , avatar : '💻' , description : 'Specialized in code generation' , accentColor : '#3b82f6' , gradient : 'from-blue-500/20 to-cyan-500/20' , status : 'running' , framework : 'OpenAI' , model : 'gpt-4o' , mcpServers : 1 , createdAt : '2025-04-08' } ,
{ id : 3 , name : 'Data Analyst' , avatar : '📊' , description : 'Data analysis and visualization' , accentColor : '#10b981' , gradient : 'from-emerald-500/20 to-green-500/20' , status : 'stopped' , framework : 'PydanticAI' , model : 'gpt-4o-mini' , mcpServers : 3 , createdAt : '2025-04-05' } ,
{ id : 4 , name : 'Research Bot' , avatar : '🔬' , description : 'Academic research assistant' , accentColor : '#8b5cf6' , gradient : 'from-violet-500/20 to-purple-500/20' , status : 'running' , framework : 'LangChain' , model : 'claude-3-5-sonnet' , mcpServers : 2 , createdAt : '2025-04-12' } ,
{ id : 5 , name : '客服助手' , avatar : '🎧' , description : 'Customer support agent' , accentColor : '#ec4899' , gradient : 'from-pink-500/20 to-rose-500/20' , status : 'running' , framework : 'Google ADK' , model : 'gemini-1.5-pro' , mcpServers : 4 , createdAt : '2025-04-11' } ,
2026-03-10 15:42:21 +08:00
] )
2026-03-05 10:49:46 +08:00
2026-03-10 16:09:09 +08:00
const searchQuery = ref ( '' )
const filterStatus = ref ( 'all' )
// 过滤后的 agents
const filteredAgents = computed ( ( ) => {
return agents . value . filter ( agent => {
const matchSearch = agent . name . toLowerCase ( ) . includes ( searchQuery . value . toLowerCase ( ) ) ||
agent . framework . toLowerCase ( ) . includes ( searchQuery . value . toLowerCase ( ) )
const matchStatus = filterStatus . value === 'all' || agent . status === filterStatus . value
return matchSearch && matchStatus
} )
} )
// 统计数据
const stats = computed ( ( ) => ( {
total : agents . value . length ,
running : agents . value . filter ( a => a . status === 'running' ) . length ,
stopped : agents . value . filter ( a => a . status === 'stopped' ) . length ,
} ) )
// 状态颜色
const statusClass = ( status : string ) => {
switch ( status ) {
case 'running' : return 'bg-primary-success'
case 'stopped' : return 'bg-gray-500'
default : return 'bg-gray-500'
2026-03-05 10:49:46 +08:00
}
}
2026-03-10 15:42:21 +08:00
2026-03-10 16:09:09 +08:00
// 切换状态
const toggleStatus = ( agent : Agent ) => {
agent . status = agent . status === 'running' ? 'stopped' : 'running'
2026-03-10 15:42:21 +08:00
}
2026-03-10 16:09:09 +08:00
// 删除 Agent
const deleteAgent = ( id : number ) => {
agents . value = agents . value . filter ( a => a . id !== id )
2026-03-10 15:42:21 +08:00
}
2026-03-05 10:49:46 +08:00
< / script >
< template >
2026-03-10 16:09:09 +08:00
<!-- 主内容区域 -- >
< div class = "p-6 min-h-screen" >
<!-- 顶部导航 -- >
< div class = "flex justify-between items-center mb-6" >
< div class = "flex items-center gap-2" >
2026-03-10 17:39:10 +08:00
< i class = "fa-solid fa-robot text-orange-500" > < / i >
2026-03-10 16:09:09 +08:00
< span class = "font-medium" > Agents < / span >
< / div >
2026-03-11 14:26:25 +08:00
< button @click ="openCreateModal" class = "btn-primary" >
2026-03-10 16:09:09 +08:00
< i class = "fa-solid fa-plus" > < / i >
New Agent
< / button >
< / div >
<!-- Stats Cards -- >
< div class = "grid grid-cols-4 gap-4 mb-6" >
< div class = "bg-dark-700 rounded-xl p-4 border border-dark-500" >
2026-03-10 15:42:21 +08:00
< div class = "flex items-center gap-3" >
2026-03-10 16:09:09 +08:00
< div class = "w-10 h-10 rounded-lg bg-dark-600 flex items-center justify-center" >
< i class = "fa-solid fa-robot text-gray-400" > < / i >
< / div >
< div >
< div class = "text-2xl font-bold text-white" > { { stats . total } } < / div >
< div class = "text-xs text-gray-400" > Total Agents < / div >
2026-03-10 15:42:21 +08:00
< / div >
2026-03-05 10:49:46 +08:00
< / div >
< / div >
2026-03-10 16:09:09 +08:00
< div class = "bg-dark-700 rounded-xl p-4 border border-dark-500" >
< div class = "flex items-center gap-3" >
< div class = "w-10 h-10 rounded-lg bg-primary-success/20 flex items-center justify-center" >
< i class = "fa-solid fa-circle-check text-primary-success" > < / i >
< / div >
< div >
< div class = "text-2xl font-bold text-primary-success" > { { stats . running } } < / div >
< div class = "text-xs text-gray-400" > Running < / div >
2026-03-05 10:49:46 +08:00
< / div >
2026-03-10 15:42:21 +08:00
< / div >
< / div >
2026-03-10 16:09:09 +08:00
< div class = "bg-dark-700 rounded-xl p-4 border border-dark-500" >
< div class = "flex items-center gap-3" >
< div class = "w-10 h-10 rounded-lg bg-gray-500/20 flex items-center justify-center" >
< i class = "fa-solid fa-circle-stop text-gray-400" > < / i >
2026-03-05 10:49:46 +08:00
< / div >
2026-03-10 16:09:09 +08:00
< div >
< div class = "text-2xl font-bold text-gray-400" > { { stats . stopped } } < / div >
< div class = "text-xs text-gray-400" > Stopped < / div >
2026-03-10 15:42:21 +08:00
< / div >
2026-03-05 10:49:46 +08:00
< / div >
< / div >
2026-03-10 16:09:09 +08:00
< div class = "bg-dark-700 rounded-xl p-4 border border-dark-500" >
< div class = "flex items-center gap-3" >
< div class = "w-10 h-10 rounded-lg bg-primary-cyan/20 flex items-center justify-center" >
< i class = "fa-solid fa-plug text-primary-cyan" > < / i >
< / div >
< div >
< div class = "text-2xl font-bold text-primary-cyan" > { { agents . reduce ( ( sum , a ) => sum + a . mcpServers , 0 ) } } < / div >
< div class = "text-xs text-gray-400" > MCP Servers < / div >
2026-03-05 10:49:46 +08:00
< / div >
2026-03-10 15:42:21 +08:00
< / div >
2026-03-10 16:09:09 +08:00
< / div >
< / div >
2026-03-05 10:49:46 +08:00
2026-03-10 16:09:09 +08:00
<!-- 搜索和筛选 -- >
< div class = "flex gap-4 mb-6" >
< div class = "flex-1 relative" >
< i class = "fa-solid fa-search absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" > < / i >
< input
v - model = "searchQuery"
type = "text"
placeholder = "Search agents by name or framework..."
class = "search-input w-full"
>
< / div >
< el-select v-model = "filterStatus" placeholder="Select" class="w-40" size="large" >
< el-option label = "All Status" value = "all" / >
< el-option label = "Running" value = "running" / >
< el-option label = "Stopped" value = "stopped" / >
< / el-select >
< / div >
2026-03-05 10:49:46 +08:00
2026-03-10 16:09:09 +08:00
<!-- Agents 列表 -- >
< div class = "bg-dark-700 rounded-xl overflow-hidden" >
< table class = "w-full" >
< thead class = "bg-dark-600" >
< tr >
< th class = "text-left px-5 py-3 text-sm font-medium text-gray-400" > Agent Name < / th >
< th class = "text-left px-5 py-3 text-sm font-medium text-gray-400" > Framework < / th >
< th class = "text-left px-5 py-3 text-sm font-medium text-gray-400" > Model < / th >
< th class = "text-center px-5 py-3 text-sm font-medium text-gray-400" > MCP < / th >
< th class = "text-left px-5 py-3 text-sm font-medium text-gray-400" > Status < / th >
< th class = "text-left px-5 py-3 text-sm font-medium text-gray-400" > Created < / th >
< th class = "text-right px-5 py-3 text-sm font-medium text-gray-400" > Actions < / th >
< / tr >
< / thead >
< tbody >
< tr v-for = "agent in filteredAgents" :key="agent.id" class="table-row" >
< td class = "px-5 py-4" >
2026-03-10 15:42:21 +08:00
< div class = "flex items-center gap-3" >
2026-03-05 10:49:46 +08:00
< div
2026-03-10 16:09:09 +08:00
class = "w-10 h-10 rounded-lg flex items-center justify-center text-lg"
2026-03-10 15:42:21 +08:00
: style = "{ backgroundColor: agent.accentColor + '20', color: agent.accentColor }"
2026-03-05 10:49:46 +08:00
>
2026-03-10 15:42:21 +08:00
{ { agent . avatar } }
2026-03-05 10:49:46 +08:00
< / div >
2026-03-10 16:09:09 +08:00
< div >
< div class = "font-medium text-white" > { { agent . name } } < / div >
< div class = "text-xs text-gray-500" > { { agent . description } } < / div >
2026-03-05 10:49:46 +08:00
< / div >
< / div >
2026-03-10 16:09:09 +08:00
< / td >
< td class = "px-5 py-4" >
< span class = "bg-dark-500 px-2 py-1 rounded text-sm text-gray-300" > { { agent . framework } } < / span >
< / td >
< td class = "px-5 py-4 text-gray-300" > { { agent . model } } < / td >
< td class = "px-5 py-4 text-center" >
< span class = "text-primary-cyan" > { { agent . mcpServers } } < / span >
< / td >
< td class = "px-5 py-4" >
< div class = "flex items-center gap-2" >
< span class = "w-2 h-2 rounded-full" :class = "statusClass(agent.status)" > < / span >
< span class = "capitalize text-sm text-gray-300" > { { agent . status } } < / span >
< / div >
< / td >
< td class = "px-5 py-4 text-gray-400 text-sm" > { { agent . createdAt } } < / td >
< td class = "px-5 py-4" >
< div class = "flex items-center justify-end gap-2" >
< button
@ click = "toggleStatus(agent)"
class = "btn-icon"
: title = "agent.status === 'running' ? 'Stop' : 'Start'"
>
< i : class = "['fa-solid', agent.status === 'running' ? 'fa-stop' : 'fa-play', 'text-gray-400']" > < / i >
< / button >
< button class = "btn-icon" title = "Edit" >
< i class = "fa-solid fa-pen text-gray-400" > < / i >
< / button >
< button class = "btn-icon" title = "Settings" >
< i class = "fa-solid fa-gear text-gray-400" > < / i >
< / button >
< button
@ click = "deleteAgent(agent.id)"
class = "btn-icon"
title = "Delete"
>
< i class = "fa-solid fa-trash text-gray-400 hover:text-primary-danger" > < / i >
< / button >
< / div >
< / td >
< / tr >
< / tbody >
< / table >
<!-- 空状态 -- >
< div v-if = "filteredAgents.length === 0" class="py-12 text-center text-gray-500" >
< i class = "fa-solid fa-robot text-4xl mb-3" > < / i >
< p > No agents found < / p >
2026-03-05 10:49:46 +08:00
< / div >
2026-03-10 16:09:09 +08:00
< / div >
2026-03-05 10:49:46 +08:00
< / div >
2026-03-11 14:26:25 +08:00
<!-- 创建智能体弹窗 -- >
< Teleport to = "body" >
< div v-if = "showCreateModal" class="fixed inset-0 bg-black/60 flex items-center justify-center z-50" >
< div class = "bg-dark-700 rounded-2xl w-full max-w-lg border border-dark-500 shadow-2xl" >
< div class = "flex items-center justify-between p-5 border-b border-dark-500" >
< h3 class = "text-lg font-semibold" > Create New Agent < / h3 >
< button @click ="showCreateModal = false" class = "text-gray-400 hover:text-white transition-colors" >
< i class = "fa-solid fa-xmark text-xl" > < / i >
< / button >
< / div >
< div class = "p-5 space-y-4" >
< div >
< label class = "block text-sm font-medium text-gray-300 mb-2" > Agent Name * < / label >
< input
v - model = "newAgent.name"
type = "text"
placeholder = "Enter agent name..."
class = "w-full bg-dark-600 border border-dark-500 rounded-lg px-4 py-2.5 text-white placeholder-gray-500 focus:outline-none focus:border-primary-orange"
>
< / div >
< div >
< label class = "block text-sm font-medium text-gray-300 mb-2" > Description < / label >
< textarea
v - model = "newAgent.description"
rows = "3"
placeholder = "Describe what this agent does..."
class = "w-full bg-dark-600 border border-dark-500 rounded-lg px-4 py-2.5 text-white placeholder-gray-500 focus:outline-none focus:border-primary-orange resize-none"
> < / textarea >
< / div >
< div >
< label class = "block text-sm font-medium text-gray-300 mb-2" > Skills * < / label >
< el-select v-model = "newAgent.skills" placeholder="Select skills" class="w-full" size="large" popper-class="dark-select-dropdown" >
< el-option v-for = "s in skillsOptions" :key="s.value" :label="s.label" :value="s.value" / >
< / el-select >
< / div >
< div >
< label class = "block text-sm font-medium text-gray-300 mb-2" > Knowledge * < / label >
< el-select v-model = "newAgent.knowledge" placeholder="Select knowledge" class="w-full" size="large" popper-class="dark-select-dropdown" >
< el-option v-for = "k in knowledgeOptions" :key="k.value" :label="k.label" :value="k.value" / >
< / el-select >
< / div >
< div >
< label class = "block text-sm font-medium text-gray-300 mb-2" > Custom Prompt < / label >
< textarea
v - model = "newAgent.prompt"
rows = "4"
placeholder = "Define the agent's behavior and instructions..."
class = "w-full bg-dark-600 border border-dark-500 rounded-lg px-4 py-2.5 text-white placeholder-gray-500 focus:outline-none focus:border-primary-orange resize-none"
> < / textarea >
< / div >
< / div >
< div class = "flex items-center justify-end gap-3 p-5 border-t border-dark-500" >
< button
@ click = "showCreateModal = false"
class = "px-4 py-2 rounded-lg bg-dark-600 text-gray-300 hover:bg-dark-500 transition-colors"
>
Cancel
< / button >
< button
@ click = "createAgent"
: disabled = "isCreating || !newAgent.name || !newAgent.skills || !newAgent.knowledge"
class = "px-4 py-2 rounded-lg bg-gradient-to-r from-primary-orange to-red-500 text-white hover:from-orange-500 hover:to-red-600 transition-all disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2"
>
< i v-if = "isCreating" class="fa-solid fa-circle-notch fa-spin" > < / i >
{ { isCreating ? 'Creating...' : 'Create Agent' } }
< / button >
< / div >
< / div >
< / div >
< / Teleport >
2026-03-05 10:49:46 +08:00
< / template >