update at 2025-10-14 21:52:11

This commit is contained in:
douboer
2025-10-14 21:52:11 +08:00
parent ac3ed480ab
commit 4f5eea604e
40 changed files with 15231 additions and 126 deletions

View File

@@ -1,35 +1,50 @@
<template>
<n-config-provider :theme="theme">
<n-config-provider :theme="theme" :theme-overrides="themeOverrides">
<n-global-style />
<n-message-provider>
<div class="app">
<!-- 侧边栏 -->
<div class="sidebar">
<div class="sidebar" :class="{ collapsed: sidebarCollapsed }">
<div class="sidebar-header">
<div class="app-logo">
<n-icon size="24" color="#3b82f6">
<Robot />
</n-icon>
<span class="app-title">MCP Client</span>
<span v-if="!sidebarCollapsed" class="app-title">MCP Client</span>
</div>
<div class="header-actions">
<n-button
v-if="!sidebarCollapsed"
quaternary
circle
@click="toggleTheme"
class="theme-toggle"
>
<template #icon>
<n-icon>
<Sun v-if="isDark" />
<Moon v-else />
</n-icon>
</template>
</n-button>
<n-button
quaternary
circle
@click="toggleSidebar"
class="collapse-toggle"
>
<template #icon>
<n-icon>
<Menu2 />
</n-icon>
</template>
</n-button>
</div>
<n-button
quaternary
circle
@click="toggleTheme"
class="theme-toggle"
>
<template #icon>
<n-icon>
<Sun v-if="isDark" />
<Moon v-else />
</n-icon>
</template>
</n-button>
</div>
<n-scrollbar class="sidebar-content">
<div class="nav-section">
<div class="section-title">核心功能</div>
<div v-if="!sidebarCollapsed" class="section-title">核心功能</div>
<div class="nav-items">
<div
class="nav-item"
@@ -113,113 +128,16 @@
<!-- 主内容区域 -->
<div class="main-content">
<!-- 聊天页面 -->
<div v-if="currentRoute === 'chat'" class="content-page">
<div class="page-header">
<n-icon size="28" color="#3b82f6">
<MessageCircle />
</n-icon>
<div>
<h1>聊天对话</h1>
<p> MCP 服务器进行智能对话</p>
</div>
</div>
<div class="content-grid">
<n-card title="功能特性" class="feature-card">
<n-space vertical>
<div class="feature-item">
<n-icon size="20" color="#10b981">
<Robot />
</n-icon>
<span>多模型支持</span>
</div>
<div class="feature-item">
<n-icon size="20" color="#10b981">
<Tool />
</n-icon>
<span>工具调用</span>
</div>
<div class="feature-item">
<n-icon size="20" color="#10b981">
<Database />
</n-icon>
<span>上下文管理</span>
</div>
</n-space>
</n-card>
<n-card title="快速开始" class="action-card">
<n-space vertical size="large">
<n-button type="primary" size="large" block>
<template #icon>
<n-icon>
<MessageCircle />
</n-icon>
</template>
开始新对话
</n-button>
<n-button size="large" block>
<template #icon>
<n-icon>
<Settings />
</n-icon>
</template>
配置模型
</n-button>
</n-space>
</n-card>
</div>
</div>
<ChatLayout v-if="currentRoute === 'chat'" />
<!-- 工具页面 -->
<div v-else-if="currentRoute === 'tools'" class="content-page">
<div class="page-header">
<n-icon size="28" color="#f59e0b">
<Tool />
</n-icon>
<div>
<h1>工具管理</h1>
<p>管理和执行 MCP 工具</p>
</div>
</div>
<div class="content-grid">
<n-card title="工具列表" class="tools-card">
<n-empty description="暂无可用工具">
<template #extra>
<n-button size="small">
连接 MCP 服务器
</n-button>
</template>
</n-empty>
</n-card>
</div>
</div>
<ToolsManager v-else-if="currentRoute === 'tools'" />
<!-- 数据页面 -->
<div v-else-if="currentRoute === 'data'" class="content-page">
<div class="page-header">
<n-icon size="28" color="#8b5cf6">
<Database />
</n-icon>
<div>
<h1>数据管理</h1>
<p>管理 MCP 资源和数据</p>
</div>
</div>
<div class="content-grid">
<n-card title="资源统计" class="stats-card">
<n-statistic label="文件资源" :value="0" />
</n-card>
<n-card title="数据源" class="stats-card">
<n-statistic label="API 连接" :value="0" />
</n-card>
</div>
</div>
<DataManager v-else-if="currentRoute === 'data'" />
<!-- 模型服务页面 -->
<ModelProviders v-else-if="currentRoute === 'model-providers'" />
<ModelService v-else-if="currentRoute === 'model-providers'" />
<!-- 显示设置页面 -->
<!-- 显示设置页面 -->
@@ -319,11 +237,15 @@ import {
Settings,
Sun,
Moon,
Robot
Robot,
Menu2
} from '@vicons/tabler'
import ModelProviders from './components/ModelProviders.vue'
import DisplaySettings from './components/DisplaySettings.vue'
import MCPSettings from './components/MCPSettings.vue'
import ModelService from './components/ModelService.vue'
import ToolsManager from './components/ToolsManager.vue'
import DataManager from './components/DataManager.vue'
import ChatLayout from './components/Chat/ChatLayout.vue'
import { useModelStore } from './stores/modelStore'
type RouteKey =
@@ -340,22 +262,61 @@ const modelStore = useModelStore()
// 响应式数据
const currentRoute = ref<RouteKey>('chat')
const isDark = ref(false)
const currentThemeColor = ref('#18a058')
const sidebarCollapsed = ref(false)
// 加载主题颜色设置
const loadThemeColor = () => {
try {
const saved = localStorage.getItem('cherry-display-settings')
if (saved) {
const settings = JSON.parse(saved)
currentThemeColor.value = settings.primaryColor || '#18a058'
}
} catch (error) {
console.error('获取主题颜色失败:', error)
}
}
// 计算属性
const theme = computed<GlobalTheme | null>(() => {
return isDark.value ? darkTheme : null
})
const themeOverrides = computed(() => {
const primaryColor = currentThemeColor.value
return {
common: {
primaryColor: primaryColor,
primaryColorHover: primaryColor + 'CC',
primaryColorPressed: primaryColor + '99',
primaryColorSuppl: primaryColor
}
}
})
// 方法
const toggleTheme = () => {
isDark.value = !isDark.value
document.documentElement.setAttribute('data-theme', isDark.value ? 'dark' : 'light')
}
const toggleSidebar = () => {
sidebarCollapsed.value = !sidebarCollapsed.value
}
// 生命周期
onMounted(() => {
// 初始化模型服务状态
modelStore.initialize()
// 加载主题颜色
loadThemeColor()
// 监听主题颜色变化
window.addEventListener('theme-color-changed', (event: any) => {
currentThemeColor.value = event.detail
})
})
</script>
@@ -374,6 +335,11 @@ onMounted(() => {
display: flex;
flex-direction: column;
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.04);
transition: width 0.3s ease;
}
.sidebar.collapsed {
width: 64px;
}
.sidebar-header {
@@ -384,6 +350,18 @@ onMounted(() => {
justify-content: space-between;
}
.sidebar.collapsed .sidebar-header {
padding: 20px 12px;
flex-direction: column;
gap: 12px;
}
.header-actions {
display: flex;
align-items: center;
gap: 8px;
}
.app-logo {
display: flex;
align-items: center;
@@ -437,6 +415,17 @@ onMounted(() => {
color: #64748b;
cursor: pointer;
transition: all 0.2s ease;
white-space: nowrap;
}
.sidebar.collapsed .nav-item {
padding: 12px;
margin: 0 6px;
justify-content: center;
}
.sidebar.collapsed .nav-item span {
display: none;
}
.nav-item:hover {
@@ -459,6 +448,10 @@ onMounted(() => {
border-radius: 2px;
}
.sidebar.collapsed .nav-indicator {
right: -6px;
}
/* 主内容区域 */
.main-content {
flex: 1;