first commit

This commit is contained in:
douboer
2025-10-14 14:18:20 +08:00
commit d93bc02772
66 changed files with 21393 additions and 0 deletions

764
web/src/SimpleApp.vue Normal file
View File

@@ -0,0 +1,764 @@
<template>
<n-config-provider :theme="theme">
<n-global-style />
<n-message-provider>
<div class="app">
<!-- 侧边栏 -->
<div class="sidebar">
<div class="sidebar-header">
<div class="app-logo">
<n-icon size="24" color="#3b82f6">
<Robot />
</n-icon>
<span class="app-title">MCP Client</span>
</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 class="nav-items">
<div
class="nav-item"
:class="{ active: currentRoute === 'chat' }"
@click="currentRoute = 'chat'"
>
<n-icon size="18">
<MessageCircle />
</n-icon>
<span>聊天对话</span>
<div class="nav-indicator" v-if="currentRoute === 'chat'"></div>
</div>
<div
class="nav-item"
:class="{ active: currentRoute === 'tools' }"
@click="currentRoute = 'tools'"
>
<n-icon size="18">
<Tool />
</n-icon>
<span>工具管理</span>
<div class="nav-indicator" v-if="currentRoute === 'tools'"></div>
</div>
<div
class="nav-item"
:class="{ active: currentRoute === 'data' }"
@click="currentRoute = 'data'"
>
<n-icon size="18">
<Database />
</n-icon>
<span>数据管理</span>
<div class="nav-indicator" v-if="currentRoute === 'data'"></div>
</div>
</div>
</div>
<n-divider style="margin: 16px 0;" />
<div class="nav-section">
<div class="section-title">设置</div>
<div class="nav-items">
<div
class="nav-item"
:class="{ active: currentRoute === 'model-providers' }"
@click="currentRoute = 'model-providers'"
>
<n-icon size="18">
<Brain />
</n-icon>
<span>模型服务</span>
<div class="nav-indicator" v-if="currentRoute === 'model-providers'"></div>
</div>
<div
class="nav-item"
:class="{ active: currentRoute === 'display-settings' }"
@click="currentRoute = 'display-settings'"
>
<n-icon size="18">
<Palette />
</n-icon>
<span>显示设置</span>
<div class="nav-indicator" v-if="currentRoute === 'display-settings'"></div>
</div>
<div
class="nav-item"
:class="{ active: currentRoute === 'mcp' }"
@click="currentRoute = 'mcp'"
>
<n-icon size="18">
<Settings />
</n-icon>
<span>MCP 设置</span>
<div class="nav-indicator" v-if="currentRoute === 'mcp'"></div>
</div>
</div>
</div>
</n-scrollbar>
</div>
<!-- 主内容区域 -->
<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>
<!-- 工具页面 -->
<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>
<!-- 数据页面 -->
<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>
<!-- 模型服务页面 -->
<ModelProviders v-else-if="currentRoute === 'model-providers'" />
<!-- 显示设置页面 -->
<!-- 显示设置页面 -->
<DisplaySettings v-else-if="currentRoute === 'display-settings'" />
<!-- MCP 设置页面 -->
<MCPSettings v-else-if="currentRoute === 'mcp'" />
<!-- 默认首页 -->
<div v-else class="content-page">
<div class="welcome-header">
<div class="welcome-logo">
<n-icon size="48" color="#3b82f6">
<Robot />
</n-icon>
</div>
<h1>欢迎使用 MCP Vue Client</h1>
<p>现代化的模型上下文协议客户端</p>
</div>
<div class="welcome-grid">
<n-card
class="welcome-card"
hoverable
@click="currentRoute = 'chat'"
>
<template #cover>
<div class="card-icon chat-icon">
<n-icon size="32">
<MessageCircle />
</n-icon>
</div>
</template>
<h3>开始对话</h3>
<p>与AI模型进行智能对话体验强大的语言理解能力</p>
</n-card>
<n-card
class="welcome-card"
hoverable
@click="currentRoute = 'tools'"
>
<template #cover>
<div class="card-icon tools-icon">
<n-icon size="32">
<Tool />
</n-icon>
</div>
</template>
<h3>使用工具</h3>
<p>执行各种MCP工具扩展AI的能力边界</p>
</n-card>
<n-card
class="welcome-card"
hoverable
@click="currentRoute = 'mcp'"
>
<template #cover>
<div class="card-icon mcp-icon">
<n-icon size="32">
<Settings />
</n-icon>
</div>
</template>
<h3>配置服务</h3>
<p>管理MCP服务器连接搭建强大的AI生态</p>
</n-card>
</div>
<div class="stats-overview">
<n-card title="系统概览">
<div class="stats-grid">
<n-statistic label="已连接服务器" :value="0" />
<n-statistic label="可用工具" :value="0" />
<n-statistic label="对话次数" :value="0" />
<n-statistic label="配置模型" :value="0" />
</div>
</n-card>
</div>
</div>
</div>
</div>
</n-message-provider>
</n-config-provider>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { darkTheme, type GlobalTheme } from 'naive-ui'
import {
MessageCircle,
Tool,
Database,
Cpu as Brain,
Palette,
Settings,
Sun,
Moon,
Robot
} from '@vicons/tabler'
import ModelProviders from './components/ModelProviders.vue'
import DisplaySettings from './components/DisplaySettings.vue'
import MCPSettings from './components/MCPSettings.vue'
import { useModelStore } from './stores/modelStore'
type RouteKey =
| 'chat'
| 'tools'
| 'data'
| 'model-providers'
| 'display-settings'
| 'mcp'
// 状态管理
const modelStore = useModelStore()
// 响应式数据
const currentRoute = ref<RouteKey>('chat')
const isDark = ref(false)
// 计算属性
const theme = computed<GlobalTheme | null>(() => {
return isDark.value ? darkTheme : null
})
// 方法
const toggleTheme = () => {
isDark.value = !isDark.value
document.documentElement.setAttribute('data-theme', isDark.value ? 'dark' : 'light')
}
// 生命周期
onMounted(() => {
// 初始化模型服务状态
modelStore.initialize()
})
</script>
<style scoped>
.app {
display: flex;
height: 100vh;
background: #f8fafc;
}
/* 侧边栏样式 */
.sidebar {
width: 280px;
background: #ffffff;
border-right: 1px solid #e2e8f0;
display: flex;
flex-direction: column;
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.04);
}
.sidebar-header {
padding: 20px 24px;
border-bottom: 1px solid #e2e8f0;
display: flex;
align-items: center;
justify-content: space-between;
}
.app-logo {
display: flex;
align-items: center;
gap: 12px;
}
.app-title {
font-size: 18px;
font-weight: 600;
color: #1e293b;
}
.theme-toggle {
flex-shrink: 0;
}
.sidebar-content {
flex: 1;
padding: 16px 0;
}
.nav-section {
margin-bottom: 24px;
}
.section-title {
padding: 0 24px 12px;
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
color: #64748b;
letter-spacing: 0.5px;
}
.nav-items {
display: flex;
flex-direction: column;
gap: 2px;
}
.nav-item {
position: relative;
display: flex;
align-items: center;
gap: 12px;
padding: 12px 24px;
margin: 0 12px;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
color: #64748b;
cursor: pointer;
transition: all 0.2s ease;
}
.nav-item:hover {
background: #f1f5f9;
color: #334155;
}
.nav-item.active {
background: #eff6ff;
color: #3b82f6;
font-weight: 600;
}
.nav-indicator {
position: absolute;
right: -12px;
width: 3px;
height: 20px;
background: #3b82f6;
border-radius: 2px;
}
/* 主内容区域 */
.main-content {
flex: 1;
display: flex;
flex-direction: column;
overflow-y: auto;
height: calc(100vh - 0px);
}
.content-panel {
flex: 1;
overflow-y: auto;
}
.content-page {
flex: 1;
padding: 32px;
overflow-y: auto;
background: #f8fafc;
}
.page-header {
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 32px;
padding-bottom: 24px;
border-bottom: 1px solid #e2e8f0;
}
.page-header h1 {
margin: 0;
font-size: 28px;
font-weight: 700;
color: #1e293b;
}
.page-header p {
margin: 4px 0 0 0;
color: #64748b;
font-size: 16px;
}
/* 内容网格布局 */
.content-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 24px;
margin-bottom: 32px;
}
.providers-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 20px;
}
.settings-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 24px;
}
.mcp-grid {
display: grid;
gap: 24px;
}
/* 卡片样式 */
.feature-card,
.action-card,
.tools-card,
.stats-card,
.provider-card,
.settings-card,
.mcp-card {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
border: 1px solid #e2e8f0;
}
.feature-item {
display: flex;
align-items: center;
gap: 12px;
font-size: 14px;
color: #475569;
}
.setting-item {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
color: #475569;
}
/* 欢迎页面样式 */
.welcome-header {
text-align: center;
margin-bottom: 48px;
}
.welcome-logo {
margin-bottom: 24px;
}
.welcome-header h1 {
margin: 0 0 12px 0;
font-size: 36px;
font-weight: 700;
color: #1e293b;
}
.welcome-header p {
margin: 0;
font-size: 18px;
color: #64748b;
}
.welcome-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 24px;
margin-bottom: 48px;
}
.welcome-card {
cursor: pointer;
transition: all 0.3s ease;
border: 1px solid #e2e8f0;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.welcome-card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
.card-icon {
display: flex;
align-items: center;
justify-content: center;
height: 80px;
border-radius: 12px 12px 0 0;
}
.chat-icon {
background: linear-gradient(135deg, #3b82f6, #1d4ed8);
color: white;
}
.tools-icon {
background: linear-gradient(135deg, #f59e0b, #d97706);
color: white;
}
.mcp-icon {
background: linear-gradient(135deg, #6366f1, #4f46e5);
color: white;
}
.welcome-card h3 {
margin: 16px 0 8px 0;
font-size: 18px;
font-weight: 600;
color: #1e293b;
}
.welcome-card p {
margin: 0;
color: #64748b;
font-size: 14px;
line-height: 1.6;
}
.stats-overview {
margin-top: 32px;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 24px;
}
/* 响应式设计 */
@media (max-width: 768px) {
.app {
flex-direction: column;
}
.sidebar {
width: 100%;
height: auto;
max-height: 300px;
}
.content-page {
padding: 20px;
}
.page-header {
margin-bottom: 24px;
padding-bottom: 16px;
}
.page-header h1 {
font-size: 24px;
}
.content-grid,
.providers-grid,
.settings-grid,
.welcome-grid {
grid-template-columns: 1fr;
gap: 16px;
}
.welcome-header h1 {
font-size: 28px;
}
.stats-grid {
grid-template-columns: repeat(2, 1fr);
}
}
/* 深色模式 */
[data-theme="dark"] .app {
background: #0f172a;
}
[data-theme="dark"] .sidebar {
background: #1e293b;
border-right-color: #334155;
}
[data-theme="dark"] .sidebar-header {
border-bottom-color: #334155;
}
[data-theme="dark"] .app-title {
color: #f8fafc;
}
[data-theme="dark"] .section-title {
color: #94a3b8;
}
[data-theme="dark"] .nav-item {
color: #94a3b8;
}
[data-theme="dark"] .nav-item:hover {
background: #334155;
color: #f1f5f9;
}
[data-theme="dark"] .nav-item.active {
background: rgba(59, 130, 246, 0.2);
color: #60a5fa;
}
[data-theme="dark"] .content-page {
background: #0f172a;
}
[data-theme="dark"] .page-header {
border-bottom-color: #334155;
}
[data-theme="dark"] .page-header h1 {
color: #f8fafc;
}
[data-theme="dark"] .page-header p {
color: #94a3b8;
}
[data-theme="dark"] .welcome-header h1 {
color: #f8fafc;
}
[data-theme="dark"] .welcome-header p {
color: #94a3b8;
}
[data-theme="dark"] .welcome-card h3 {
color: #f8fafc;
}
[data-theme="dark"] .welcome-card p {
color: #94a3b8;
}
[data-theme="dark"] .feature-item {
color: #cbd5e1;
}
[data-theme="dark"] .setting-item {
color: #cbd5e1;
}
</style>