feat: implement RAG dashboard with MUI and react-router
Add new RAG dashboard feature including: - Main application layout with header and sidebar - Multiple pages (Home, KnowledgeBase, PipelineConfig, Dashboard, MCP) - Theme configuration and styling - Routing setup with protected routes - Login page and authentication flow - Various UI components and mock data for dashboard views
This commit is contained in:
516
src/pages/MCP.tsx
Normal file
516
src/pages/MCP.tsx
Normal file
@@ -0,0 +1,516 @@
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
Card,
|
||||
CardContent,
|
||||
Grid,
|
||||
Button,
|
||||
Switch,
|
||||
FormControlLabel,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Chip,
|
||||
IconButton,
|
||||
Menu,
|
||||
MenuItem,
|
||||
TextField,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
Alert,
|
||||
Tabs,
|
||||
Tab,
|
||||
} from '@mui/material';
|
||||
import {
|
||||
Add as AddIcon,
|
||||
MoreVert as MoreVertIcon,
|
||||
Settings as SettingsIcon,
|
||||
Link as LinkIcon,
|
||||
Security as SecurityIcon,
|
||||
Speed as SpeedIcon,
|
||||
CheckCircle as CheckCircleIcon,
|
||||
Error as ErrorIcon,
|
||||
} from '@mui/icons-material';
|
||||
import { styled } from '@mui/material/styles';
|
||||
|
||||
const PageContainer = styled(Box)(({ theme }) => ({
|
||||
padding: '1.5rem',
|
||||
backgroundColor: '#F8F9FA',
|
||||
minHeight: 'calc(100vh - 60px)',
|
||||
}));
|
||||
|
||||
const PageHeader = styled(Box)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginBottom: '1.5rem',
|
||||
}));
|
||||
|
||||
const StatusCard = styled(Card)(({ theme }) => ({
|
||||
height: '100%',
|
||||
border: '1px solid #E5E5E5',
|
||||
transition: 'all 0.2s ease-in-out',
|
||||
'&:hover': {
|
||||
boxShadow: '0 4px 12px rgba(0,0,0,0.1)',
|
||||
},
|
||||
}));
|
||||
|
||||
const StatusChip = styled(Chip)<{ status: string }>(({ status, theme }) => ({
|
||||
fontSize: '0.75rem',
|
||||
height: '24px',
|
||||
backgroundColor:
|
||||
status === 'connected' ? '#E8F5E8' :
|
||||
status === 'connecting' ? '#FFF3CD' :
|
||||
status === 'error' ? '#F8D7DA' : '#F8F9FA',
|
||||
color:
|
||||
status === 'connected' ? '#155724' :
|
||||
status === 'connecting' ? '#856404' :
|
||||
status === 'error' ? '#721C24' : '#666',
|
||||
}));
|
||||
|
||||
const mockMCPServers = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'File System Server',
|
||||
description: '文件系统操作服务器',
|
||||
url: 'mcp://localhost:3001',
|
||||
status: 'connected',
|
||||
version: '1.0.0',
|
||||
tools: ['read_file', 'write_file', 'list_directory'],
|
||||
lastPing: '2024-01-15 14:30:25',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Database Server',
|
||||
description: '数据库查询服务器',
|
||||
url: 'mcp://db.example.com:3002',
|
||||
status: 'connected',
|
||||
version: '1.2.1',
|
||||
tools: ['query_sql', 'execute_sql', 'get_schema'],
|
||||
lastPing: '2024-01-15 14:30:20',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Web Scraper',
|
||||
description: '网页抓取服务器',
|
||||
url: 'mcp://scraper.example.com:3003',
|
||||
status: 'connecting',
|
||||
version: '0.9.5',
|
||||
tools: ['scrape_url', 'extract_text', 'get_links'],
|
||||
lastPing: '2024-01-15 14:28:15',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'API Gateway',
|
||||
description: 'API网关服务器',
|
||||
url: 'mcp://api.example.com:3004',
|
||||
status: 'error',
|
||||
version: '2.1.0',
|
||||
tools: ['call_api', 'auth_token', 'rate_limit'],
|
||||
lastPing: '2024-01-15 14:25:10',
|
||||
},
|
||||
];
|
||||
|
||||
const MCP: React.FC = () => {
|
||||
const [tabValue, setTabValue] = useState(0);
|
||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||
const [selectedServer, setSelectedServer] = useState<number | null>(null);
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [newServer, setNewServer] = useState({
|
||||
name: '',
|
||||
url: '',
|
||||
description: '',
|
||||
});
|
||||
|
||||
const handleTabChange = (event: React.SyntheticEvent, newValue: number) => {
|
||||
setTabValue(newValue);
|
||||
};
|
||||
|
||||
const handleMenuClick = (event: React.MouseEvent<HTMLElement>, serverId: number) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
setSelectedServer(serverId);
|
||||
};
|
||||
|
||||
const handleMenuClose = () => {
|
||||
setAnchorEl(null);
|
||||
setSelectedServer(null);
|
||||
};
|
||||
|
||||
const handleAddServer = () => {
|
||||
setDialogOpen(true);
|
||||
};
|
||||
|
||||
const handleDialogClose = () => {
|
||||
setDialogOpen(false);
|
||||
setNewServer({ name: '', url: '', description: '' });
|
||||
};
|
||||
|
||||
const handleSaveServer = () => {
|
||||
// 保存服务器逻辑
|
||||
console.log('添加服务器:', newServer);
|
||||
handleDialogClose();
|
||||
};
|
||||
|
||||
const connectedServers = mockMCPServers.filter(s => s.status === 'connected').length;
|
||||
const totalTools = mockMCPServers.reduce((acc, server) => acc + server.tools.length, 0);
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<PageHeader>
|
||||
<Box>
|
||||
<Typography variant="h4" fontWeight={600} color="#333">
|
||||
MCP 协议管理
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mt: 0.5 }}>
|
||||
Model Context Protocol - 模型上下文协议服务器管理
|
||||
</Typography>
|
||||
</Box>
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={<AddIcon />}
|
||||
onClick={handleAddServer}
|
||||
sx={{ borderRadius: '6px' }}
|
||||
>
|
||||
添加服务器
|
||||
</Button>
|
||||
</PageHeader>
|
||||
|
||||
<Box sx={{ borderBottom: 1, borderColor: 'divider', mb: 3 }}>
|
||||
<Tabs value={tabValue} onChange={handleTabChange}>
|
||||
<Tab label="服务器管理" />
|
||||
<Tab label="工具配置" />
|
||||
<Tab label="协议监控" />
|
||||
</Tabs>
|
||||
</Box>
|
||||
|
||||
{tabValue === 0 && (
|
||||
<>
|
||||
{/* 状态概览 */}
|
||||
<Grid container spacing={3} sx={{ mb: 3 }}>
|
||||
<Grid item xs={12} sm={6} md={3}>
|
||||
<StatusCard>
|
||||
<CardContent>
|
||||
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||
<Box>
|
||||
<Typography color="text.secondary" variant="body2">
|
||||
连接服务器
|
||||
</Typography>
|
||||
<Typography variant="h4" fontWeight={600} color="success.main">
|
||||
{connectedServers}
|
||||
</Typography>
|
||||
</Box>
|
||||
<CheckCircleIcon color="success" sx={{ fontSize: '3rem', opacity: 0.3 }} />
|
||||
</Box>
|
||||
</CardContent>
|
||||
</StatusCard>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={6} md={3}>
|
||||
<StatusCard>
|
||||
<CardContent>
|
||||
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||
<Box>
|
||||
<Typography color="text.secondary" variant="body2">
|
||||
总服务器数
|
||||
</Typography>
|
||||
<Typography variant="h4" fontWeight={600} color="primary">
|
||||
{mockMCPServers.length}
|
||||
</Typography>
|
||||
</Box>
|
||||
<LinkIcon color="primary" sx={{ fontSize: '3rem', opacity: 0.3 }} />
|
||||
</Box>
|
||||
</CardContent>
|
||||
</StatusCard>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={6} md={3}>
|
||||
<StatusCard>
|
||||
<CardContent>
|
||||
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||
<Box>
|
||||
<Typography color="text.secondary" variant="body2">
|
||||
可用工具
|
||||
</Typography>
|
||||
<Typography variant="h4" fontWeight={600} color="info.main">
|
||||
{totalTools}
|
||||
</Typography>
|
||||
</Box>
|
||||
<SettingsIcon color="info" sx={{ fontSize: '3rem', opacity: 0.3 }} />
|
||||
</Box>
|
||||
</CardContent>
|
||||
</StatusCard>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={6} md={3}>
|
||||
<StatusCard>
|
||||
<CardContent>
|
||||
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||
<Box>
|
||||
<Typography color="text.secondary" variant="body2">
|
||||
平均延迟
|
||||
</Typography>
|
||||
<Typography variant="h4" fontWeight={600} color="warning.main">
|
||||
45ms
|
||||
</Typography>
|
||||
</Box>
|
||||
<SpeedIcon color="warning" sx={{ fontSize: '3rem', opacity: 0.3 }} />
|
||||
</Box>
|
||||
</CardContent>
|
||||
</StatusCard>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* 服务器列表 */}
|
||||
<Card sx={{ border: '1px solid #E5E5E5' }}>
|
||||
<CardContent>
|
||||
<Typography variant="h6" fontWeight={600} mb={2}>
|
||||
MCP 服务器列表
|
||||
</Typography>
|
||||
<TableContainer>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>服务器名称</TableCell>
|
||||
<TableCell>URL</TableCell>
|
||||
<TableCell>状态</TableCell>
|
||||
<TableCell>版本</TableCell>
|
||||
<TableCell>工具数量</TableCell>
|
||||
<TableCell>最后心跳</TableCell>
|
||||
<TableCell>操作</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{mockMCPServers.map((server) => (
|
||||
<TableRow key={server.id} hover>
|
||||
<TableCell>
|
||||
<Box>
|
||||
<Typography variant="body2" fontWeight={600}>
|
||||
{server.name}
|
||||
</Typography>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
{server.description}
|
||||
</Typography>
|
||||
</Box>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Typography variant="body2" fontFamily="monospace">
|
||||
{server.url}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<StatusChip
|
||||
status={server.status}
|
||||
label={
|
||||
server.status === 'connected' ? '已连接' :
|
||||
server.status === 'connecting' ? '连接中' : '错误'
|
||||
}
|
||||
size="small"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Chip
|
||||
label={server.version}
|
||||
size="small"
|
||||
variant="outlined"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Typography variant="body2">
|
||||
{server.tools.length} 个工具
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
{server.lastPing}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={(e) => handleMenuClick(e, server.id)}
|
||||
>
|
||||
<MoreVertIcon />
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</>
|
||||
)}
|
||||
|
||||
{tabValue === 1 && (
|
||||
<Card sx={{ border: '1px solid #E5E5E5' }}>
|
||||
<CardContent>
|
||||
<Typography variant="h6" fontWeight={600} mb={2}>
|
||||
工具配置
|
||||
</Typography>
|
||||
<Alert severity="info" sx={{ mb: 2 }}>
|
||||
配置各个 MCP 服务器提供的工具和权限设置
|
||||
</Alert>
|
||||
<Grid container spacing={2}>
|
||||
{mockMCPServers.map((server) => (
|
||||
<Grid item xs={12} md={6} key={server.id}>
|
||||
<Card variant="outlined">
|
||||
<CardContent>
|
||||
<Typography variant="subtitle1" fontWeight={600} mb={1}>
|
||||
{server.name}
|
||||
</Typography>
|
||||
<Box display="flex" flexDirection="column" gap={1}>
|
||||
{server.tools.map((tool, index) => (
|
||||
<FormControlLabel
|
||||
key={index}
|
||||
control={<Switch defaultChecked size="small" />}
|
||||
label={
|
||||
<Typography variant="body2" fontFamily="monospace">
|
||||
{tool}
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{tabValue === 2 && (
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Card sx={{ border: '1px solid #E5E5E5' }}>
|
||||
<CardContent>
|
||||
<Typography variant="h6" fontWeight={600} mb={2}>
|
||||
<SecurityIcon sx={{ mr: 1, verticalAlign: 'middle' }} />
|
||||
安全监控
|
||||
</Typography>
|
||||
<Box display="flex" flexDirection="column" gap={2}>
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
<Typography variant="body2">SSL/TLS 加密</Typography>
|
||||
<CheckCircleIcon color="success" />
|
||||
</Box>
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
<Typography variant="body2">身份验证</Typography>
|
||||
<CheckCircleIcon color="success" />
|
||||
</Box>
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
<Typography variant="body2">访问控制</Typography>
|
||||
<CheckCircleIcon color="success" />
|
||||
</Box>
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
<Typography variant="body2">审计日志</Typography>
|
||||
<ErrorIcon color="error" />
|
||||
</Box>
|
||||
</Box>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} md={6}>
|
||||
<Card sx={{ border: '1px solid #E5E5E5' }}>
|
||||
<CardContent>
|
||||
<Typography variant="h6" fontWeight={600} mb={2}>
|
||||
<SpeedIcon sx={{ mr: 1, verticalAlign: 'middle' }} />
|
||||
性能监控
|
||||
</Typography>
|
||||
<Box display="flex" flexDirection="column" gap={2}>
|
||||
<Box>
|
||||
<Box display="flex" justifyContent="space-between" mb={1}>
|
||||
<Typography variant="body2">平均响应时间</Typography>
|
||||
<Typography variant="body2" fontWeight={600}>45ms</Typography>
|
||||
</Box>
|
||||
<Box bgcolor="#F8F9FA" p={1} borderRadius="4px">
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
过去24小时内的平均值
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box>
|
||||
<Box display="flex" justifyContent="space-between" mb={1}>
|
||||
<Typography variant="body2">成功率</Typography>
|
||||
<Typography variant="body2" fontWeight={600} color="success.main">
|
||||
99.2%
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box bgcolor="#F8F9FA" p={1} borderRadius="4px">
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
过去24小时内的成功率
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
{/* 菜单 */}
|
||||
<Menu
|
||||
anchorEl={anchorEl}
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={handleMenuClose}
|
||||
>
|
||||
<MenuItem onClick={handleMenuClose}>
|
||||
<SettingsIcon sx={{ mr: 1 }} fontSize="small" />
|
||||
配置
|
||||
</MenuItem>
|
||||
<MenuItem onClick={handleMenuClose}>测试连接</MenuItem>
|
||||
<MenuItem onClick={handleMenuClose}>查看日志</MenuItem>
|
||||
<MenuItem onClick={handleMenuClose} sx={{ color: 'error.main' }}>
|
||||
断开连接
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
|
||||
{/* 添加服务器对话框 */}
|
||||
<Dialog open={dialogOpen} onClose={handleDialogClose} maxWidth="sm" fullWidth>
|
||||
<DialogTitle>添加 MCP 服务器</DialogTitle>
|
||||
<DialogContent>
|
||||
<Box display="flex" flexDirection="column" gap={2} pt={1}>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="服务器名称"
|
||||
value={newServer.name}
|
||||
onChange={(e) => setNewServer(prev => ({ ...prev, name: e.target.value }))}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="服务器 URL"
|
||||
placeholder="mcp://localhost:3000"
|
||||
value={newServer.url}
|
||||
onChange={(e) => setNewServer(prev => ({ ...prev, url: e.target.value }))}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="描述"
|
||||
multiline
|
||||
rows={3}
|
||||
value={newServer.description}
|
||||
onChange={(e) => setNewServer(prev => ({ ...prev, description: e.target.value }))}
|
||||
/>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleDialogClose}>取消</Button>
|
||||
<Button onClick={handleSaveServer} variant="contained">
|
||||
添加
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default MCP;
|
||||
Reference in New Issue
Block a user