diff --git a/web/src/components/Sidebar.vue b/web/src/components/Sidebar.vue index c18c2ba..880685f 100644 --- a/web/src/components/Sidebar.vue +++ b/web/src/components/Sidebar.vue @@ -54,7 +54,7 @@ const group2 = computed(() => [ // 第3组: Skills, Tools, Script, Plan, Memory const group3 = computed(() => [ { name: 'Skills', icon: 'fa-wand-magic-sparkles', badge: 21, path: '/mcp' }, - { name: 'Tools', icon: 'fa-tools', badge: 13, path: '/model-apis' }, + { name: 'Tools', icon: 'fa-tools', badge: 13, path: '/tools' }, { name: 'Script', icon: 'fa-code', path: '/script' }, { name: 'Plan', icon: 'fa-clock', path: '/plan' }, { name: 'Memory', icon: 'fa-brain', path: '/memory' }, diff --git a/web/src/router/index.ts b/web/src/router/index.ts index 48d2720..5a1078c 100644 --- a/web/src/router/index.ts +++ b/web/src/router/index.ts @@ -5,7 +5,7 @@ import Chat from '@/views/Chat.vue' import Agents from '@/views/Agents.vue' import Team from '@/views/Team.vue' import Skill from '@/views/Skill.vue' -import ModelAPIs from '@/views/ModelAPIs.vue' +import Tools from '@/views/Tools.vue' import Database from '@/views/Database.vue' import Script from '@/views/Script.vue' import Plan from '@/views/Plan.vue' @@ -49,9 +49,9 @@ const router = createRouter({ component: Skill }, { - path: '/model-apis', - name: 'model-apis', - component: ModelAPIs + path: '/tools', + name: 'tools', + component: Tools }, { path: '/database', diff --git a/web/src/views/Skill.vue b/web/src/views/Skill.vue index e15d175..6658417 100644 --- a/web/src/views/Skill.vue +++ b/web/src/views/Skill.vue @@ -1,330 +1,52 @@ - - + + diff --git a/web/src/views/Tools.vue b/web/src/views/Tools.vue new file mode 100644 index 0000000..0dc5394 --- /dev/null +++ b/web/src/views/Tools.vue @@ -0,0 +1,265 @@ + + + + + diff --git a/web/src/views/skill/useSkills.ts b/web/src/views/skill/useSkills.ts new file mode 100644 index 0000000..47a9c67 --- /dev/null +++ b/web/src/views/skill/useSkills.ts @@ -0,0 +1,191 @@ +import { ref, computed } from 'vue' +import { ElMessageBox } from 'element-plus' + +export interface Skill { + id: number + name: string + description: string + type: string + category: string + status: 'running' | 'stopped' | 'error' + port: number + createdAt: string + tools: number +} + +export function useSkills() { + // Skill 数据 + const skills = ref([ + { id: 1, name: 'Linear', description: 'Linear API integration for project management', type: 'API', category: 'api', status: 'running', port: 3001, createdAt: '2025-04-10', tools: 12 }, + { id: 2, name: 'Google Maps', description: 'Google Maps API for location services', type: 'Google Maps', category: 'api', status: 'running', port: 3002, createdAt: '2025-04-08', tools: 8 }, + { id: 3, name: 'File Explorer', description: 'File system explorer and editor', type: 'File System', category: 'filesystem', status: 'error', port: 3003, createdAt: '2025-04-05', tools: 15 }, + { id: 4, name: 'PostgreSQL', description: 'PostgreSQL database operations', type: 'Database', category: 'database', status: 'running', port: 3004, createdAt: '2025-04-12', tools: 10 }, + { id: 5, name: 'GitHub', description: 'GitHub API integration', type: 'GitHub', category: 'communication', status: 'stopped', port: 3005, createdAt: '2025-04-11', tools: 20 }, + { id: 6, name: 'Slack', description: 'Slack messaging integration', type: 'Communication', category: 'communication', status: 'running', port: 3006, createdAt: '2025-04-09', tools: 6 }, + { id: 7, name: 'OpenAI', description: 'OpenAI GPT models for AI conversations', type: 'AI/ML', category: 'ai', status: 'running', port: 3007, createdAt: '2025-04-07', tools: 5 }, + { id: 8, name: 'MySQL', description: 'MySQL database operations', type: 'Database', category: 'database', status: 'stopped', port: 3008, createdAt: '2025-04-06', tools: 10 }, + ]) + + // 搜索和筛选 + const searchQuery = ref('') + const filterStatus = ref('all') + + // 编辑状态 + const isEditing = ref(false) + const isCreating = ref(false) + const editingSkill = ref(null) + + // 表单 + const editForm = ref({ + name: '', + type: '', + category: '', + port: 3000, + description: '', + }) + + const newSkillForm = ref({ + name: '', + type: 'API', + category: 'api', + port: 3000, + description: '', + }) + + // 分类选项 + const categories = [ + { value: 'api', label: 'API' }, + { value: 'database', label: 'Database' }, + { value: 'filesystem', label: 'File System' }, + { value: 'communication', label: 'Communication' }, + { value: 'ai', label: 'AI/ML' }, + ] + + const types = ['API', 'Database', 'File System', 'Communication', 'AI/ML'] + + // 筛选 + const filteredSkills = computed(() => { + return skills.value.filter(skill => { + const matchSearch = skill.name.toLowerCase().includes(searchQuery.value.toLowerCase()) || + skill.description.toLowerCase().includes(searchQuery.value.toLowerCase()) + const matchStatus = filterStatus.value === 'all' || skill.status === filterStatus.value + return matchSearch && matchStatus + }) + }) + + // 状态样式 + const statusClass = (status: string) => { + switch (status) { + case 'running': return 'bg-green-500' + case 'stopped': return 'bg-gray-500' + case 'error': return 'bg-red-500' + default: return 'bg-gray-500' + } + } + + // 打开创建弹窗 + const openCreate = () => { + newSkillForm.value = { name: '', type: 'API', category: 'api', port: 3000, description: '' } + isCreating.value = true + } + + // 关闭创建弹窗 + const closeCreate = () => { + isCreating.value = false + } + + // 保存新技能 + const saveNewSkill = () => { + const newId = Math.max(...skills.value.map(s => s.id)) + 1 + skills.value.push({ + id: newId, + name: newSkillForm.value.name, + description: newSkillForm.value.description, + type: newSkillForm.value.type, + category: newSkillForm.value.category, + status: 'stopped', + port: newSkillForm.value.port, + createdAt: new Date().toISOString().split('T')[0], + tools: 0, + }) + isCreating.value = false + } + + // 打开编辑弹窗 + const openEdit = (skill: Skill) => { + editingSkill.value = skill + editForm.value = { + name: skill.name, + type: skill.type, + category: skill.category, + port: skill.port, + description: skill.description, + } + isEditing.value = true + } + + // 关闭编辑弹窗 + const closeEdit = () => { + isEditing.value = false + editingSkill.value = null + } + + // 保存编辑 + const saveEdit = () => { + const index = skills.value.findIndex(s => s.id === editingSkill.value!.id) + if (index !== -1) { + skills.value[index] = { + ...skills.value[index], + name: editForm.value.name, + type: editForm.value.type, + category: editForm.value.category, + port: editForm.value.port, + description: editForm.value.description, + } + } + isEditing.value = false + } + + // 切换状态 + const toggleStatus = (skill: Skill) => { + if (skill.status === 'running') skill.status = 'stopped' + else if (skill.status === 'stopped') skill.status = 'running' + } + + // 删除技能 + const deleteSkill = (id: number) => { + ElMessageBox.confirm('Are you sure you want to delete this skill?', 'Confirm Delete', { + confirmButtonText: 'Delete', + cancelButtonText: 'Cancel', + type: 'warning', + }).then(() => { + skills.value = skills.value.filter(s => s.id !== id) + }).catch(() => {}) + } + + return { + // State + skills, + searchQuery, + filterStatus, + isEditing, + isCreating, + editingSkill, + editForm, + newSkillForm, + categories, + types, + // Computed + filteredSkills, + // Methods + statusClass, + openCreate, + closeCreate, + saveNewSkill, + openEdit, + closeEdit, + saveEdit, + toggleStatus, + deleteSkill, + } +}