feat: 优化 MCP 页面
- 改进 MCP 页面展示和交互 - 优化样式 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, nextTick } from 'vue'
|
||||
import { ref, nextTick, onMounted, computed } from 'vue'
|
||||
import { useModelSettings } from './settings/useModelSettings'
|
||||
|
||||
interface MCPServer {
|
||||
id: number
|
||||
@@ -94,13 +95,23 @@ const newSkillForm = ref({
|
||||
model: 'gpt-4o',
|
||||
})
|
||||
|
||||
const availableModels = [
|
||||
{ name: 'gpt-4o', provider: 'OpenAI', icon: 'fa-openai' },
|
||||
{ name: 'gpt-4o-mini', provider: 'OpenAI', icon: 'fa-openai' },
|
||||
{ name: 'claude-3-5-sonnet', provider: 'Anthropic', icon: 'fa-robot' },
|
||||
{ name: 'gemini-2.0-flash', provider: 'Google', icon: 'fa-google' },
|
||||
{ name: 'gpt-5', provider: 'OpenAI', icon: 'fa-openai' },
|
||||
]
|
||||
// 从 Model Settings 获取模型
|
||||
const { models, fetchModels } = useModelSettings()
|
||||
|
||||
// 过滤 chat 类型的模型用于下拉框
|
||||
const availableModels = computed(() => {
|
||||
return models.value
|
||||
.filter((m: any) => m.model_type === 'chat')
|
||||
.map((m: any) => ({
|
||||
name: m.model,
|
||||
provider: m.provider,
|
||||
icon: 'fa-brain'
|
||||
}))
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
fetchModels()
|
||||
})
|
||||
|
||||
const goToStep2 = () => {
|
||||
if (!newSkillForm.value.name.trim()) return
|
||||
@@ -199,8 +210,10 @@ const toggleFolder = (folder: FolderItem) => {
|
||||
// 新建文件夹
|
||||
const isCreatingFolder = ref(false)
|
||||
const newFolderName = ref('')
|
||||
const showNewFolderInput = () => {
|
||||
const newFolderParentId = ref<string | null>(null)
|
||||
const showNewFolderInput = (parentId: string | null = null) => {
|
||||
newFolderName.value = ''
|
||||
newFolderParentId.value = parentId
|
||||
isCreatingFolder.value = true
|
||||
}
|
||||
|
||||
@@ -209,17 +222,41 @@ const createFolder = () => {
|
||||
isCreatingFolder.value = false
|
||||
return
|
||||
}
|
||||
fileTree.value.push({
|
||||
|
||||
const newFolder: FileItem = {
|
||||
id: 'folder-' + Date.now(),
|
||||
name: newFolderName.value,
|
||||
icon: 'fa-folder',
|
||||
type: 'folder',
|
||||
expanded: true,
|
||||
children: []
|
||||
})
|
||||
}
|
||||
|
||||
if (newFolderParentId.value) {
|
||||
// 添加到指定文件夹作为子文件夹
|
||||
const parentFolder = findFolderById(fileTree.value, newFolderParentId.value)
|
||||
if (parentFolder && parentFolder.children) {
|
||||
parentFolder.children.push(newFolder)
|
||||
}
|
||||
} else {
|
||||
// 添加到根目录
|
||||
fileTree.value.push(newFolder)
|
||||
}
|
||||
isCreatingFolder.value = false
|
||||
}
|
||||
|
||||
// 递归查找文件夹
|
||||
const findFolderById = (folders: FileItem[], id: string): FileItem | null => {
|
||||
for (const folder of folders) {
|
||||
if (folder.id === id) return folder
|
||||
if (folder.children) {
|
||||
const found = findFolderById(folder.children, id)
|
||||
if (found) return found
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// 新建文件
|
||||
const isCreatingFile = ref(false)
|
||||
const newFileName = ref('')
|
||||
@@ -356,6 +393,17 @@ const editingFileType = ref('')
|
||||
// 选中文件/文件夹状态
|
||||
// 鼠标悬停状态
|
||||
const hoveringItem = ref<string | null>(null)
|
||||
// 当前选中的文件夹(用于创建子文件夹/子文件)
|
||||
const selectedFolder = ref<string | null>(null)
|
||||
|
||||
// 选中文件夹
|
||||
const selectFolder = (folderId: string) => {
|
||||
if (selectedFolder.value === folderId) {
|
||||
selectedFolder.value = null // 再次点击取消选中
|
||||
} else {
|
||||
selectedFolder.value = folderId
|
||||
}
|
||||
}
|
||||
|
||||
// 删除文件
|
||||
const deleteFile = (file: FileItem, parentId: string | null) => {
|
||||
@@ -944,25 +992,26 @@ const statusClass = (status: string) => {
|
||||
<label class="block text-sm font-medium text-gray-300 mb-2">
|
||||
<i class="fa-solid fa-brain mr-2 text-primary-cyan"></i>Select Model
|
||||
</label>
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div
|
||||
<el-select
|
||||
v-model="newSkillForm.model"
|
||||
placeholder="Select a model"
|
||||
class="w-full"
|
||||
size="large"
|
||||
popper-class="dark-select-dropdown"
|
||||
>
|
||||
<el-option
|
||||
v-for="model in availableModels"
|
||||
:key="model.name"
|
||||
@click="newSkillForm.model = model.name"
|
||||
class="p-3 rounded-xl border-2 cursor-pointer transition-all hover:scale-105"
|
||||
:class="newSkillForm.model === model.name
|
||||
? 'border-primary-orange bg-dark-600 shadow-lg shadow-primary-orange/20'
|
||||
: 'border-dark-500 bg-dark-700 hover:border-gray-500'"
|
||||
:label="`${model.name} (${model.provider})`"
|
||||
:value="model.name"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<i :class="['fa-solid', model.icon, 'text-lg']"></i>
|
||||
<div>
|
||||
<div class="font-medium text-white text-sm">{{ model.name }}</div>
|
||||
<div class="text-xs text-gray-500">{{ model.provider }}</div>
|
||||
</div>
|
||||
<i :class="['fa-solid', model.icon, 'text-primary-cyan']"></i>
|
||||
<span class="text-white">{{ model.name }}</span>
|
||||
<span class="text-gray-500 text-sm">({{ model.provider }})</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1052,10 +1101,11 @@ const statusClass = (status: string) => {
|
||||
>
|
||||
<!-- 文件夹标题 -->
|
||||
<div
|
||||
@click.stop="selectFolder(folder.id)"
|
||||
@mouseenter="hoveringItem = folder.id"
|
||||
@mouseleave="hoveringItem = null"
|
||||
class="flex items-center gap-2 px-2 py-1.5 cursor-pointer hover:bg-dark-700 group transition-colors"
|
||||
:class="{ 'bg-dark-700': hoveringItem === folder.id }"
|
||||
:class="{ 'bg-dark-700': hoveringItem === folder.id || selectedFolder === folder.id }"
|
||||
>
|
||||
<i
|
||||
@click.stop="toggleFolder(folder)"
|
||||
@@ -1068,6 +1118,8 @@ const statusClass = (status: string) => {
|
||||
<span
|
||||
class="text-sm text-gray-300 group-hover:text-white font-medium flex-1"
|
||||
>{{ folder.name }}</span>
|
||||
<!-- 选中状态指示器 -->
|
||||
<span v-if="selectedFolder === folder.id" class="text-xs text-primary-cyan">Selected</span>
|
||||
<button
|
||||
@click.stop="deleteFolder(folder)"
|
||||
class="text-red-400 hover:text-red-300 p-1 rounded hover:bg-dark-600 transition-colors opacity-0 group-hover:opacity-100"
|
||||
|
||||
Reference in New Issue
Block a user