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:
369
src/pages/Dashboard.tsx
Normal file
369
src/pages/Dashboard.tsx
Normal file
@@ -0,0 +1,369 @@
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
Card,
|
||||
CardContent,
|
||||
Grid,
|
||||
Button,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Paper,
|
||||
Chip,
|
||||
LinearProgress,
|
||||
Select,
|
||||
MenuItem,
|
||||
FormControl,
|
||||
InputLabel,
|
||||
} from '@mui/material';
|
||||
import {
|
||||
TrendingUp as TrendingUpIcon,
|
||||
TrendingDown as TrendingDownIcon,
|
||||
Assessment as AssessmentIcon,
|
||||
Speed as SpeedIcon,
|
||||
Error as ErrorIcon,
|
||||
CheckCircle as CheckCircleIcon,
|
||||
} 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 MetricCard = 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 MetricValue = styled(Typography)(({ theme }) => ({
|
||||
fontSize: '2rem',
|
||||
fontWeight: 700,
|
||||
lineHeight: 1.2,
|
||||
}));
|
||||
|
||||
const TrendIndicator = styled(Box)<{ trend: 'up' | 'down' }>(({ trend, theme }) => ({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '0.25rem',
|
||||
color: trend === 'up' ? '#28A745' : '#DC3545',
|
||||
fontSize: '0.875rem',
|
||||
fontWeight: 500,
|
||||
}));
|
||||
|
||||
const StatusChip = styled(Chip)<{ status: string }>(({ status, theme }) => ({
|
||||
fontSize: '0.75rem',
|
||||
height: '24px',
|
||||
backgroundColor:
|
||||
status === 'success' ? '#E8F5E8' :
|
||||
status === 'warning' ? '#FFF3CD' :
|
||||
status === 'error' ? '#F8D7DA' : '#F8F9FA',
|
||||
color:
|
||||
status === 'success' ? '#155724' :
|
||||
status === 'warning' ? '#856404' :
|
||||
status === 'error' ? '#721C24' : '#666',
|
||||
}));
|
||||
|
||||
const mockMetrics = {
|
||||
totalQueries: { value: 12847, trend: 'up', change: '+12.5%' },
|
||||
avgResponseTime: { value: '2.3s', trend: 'down', change: '-8.2%' },
|
||||
successRate: { value: '98.7%', trend: 'up', change: '+0.3%' },
|
||||
activeUsers: { value: 1256, trend: 'up', change: '+5.7%' },
|
||||
};
|
||||
|
||||
const mockRecentQueries = [
|
||||
{
|
||||
id: 1,
|
||||
query: '如何配置RAG Pipeline的参数?',
|
||||
user: 'user@example.com',
|
||||
status: 'success',
|
||||
responseTime: '1.8s',
|
||||
timestamp: '2024-01-15 14:30:25',
|
||||
knowledgeBase: '产品文档库',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
query: '客服系统的常见问题有哪些?',
|
||||
user: 'admin@company.com',
|
||||
status: 'success',
|
||||
responseTime: '2.1s',
|
||||
timestamp: '2024-01-15 14:28:15',
|
||||
knowledgeBase: '客服FAQ',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
query: '法律合规要求的详细说明',
|
||||
user: 'legal@company.com',
|
||||
status: 'warning',
|
||||
responseTime: '4.2s',
|
||||
timestamp: '2024-01-15 14:25:10',
|
||||
knowledgeBase: '法律合规',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
query: '员工培训流程和要求',
|
||||
user: 'hr@company.com',
|
||||
status: 'error',
|
||||
responseTime: 'N/A',
|
||||
timestamp: '2024-01-15 14:22:45',
|
||||
knowledgeBase: '培训资料',
|
||||
},
|
||||
];
|
||||
|
||||
const Dashboard: React.FC = () => {
|
||||
const [timeRange, setTimeRange] = useState('24h');
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<PageHeader>
|
||||
<Typography variant="h4" fontWeight={600} color="#333">
|
||||
运营监控
|
||||
</Typography>
|
||||
<FormControl size="small" sx={{ minWidth: 120 }}>
|
||||
<InputLabel>时间范围</InputLabel>
|
||||
<Select
|
||||
value={timeRange}
|
||||
onChange={(e) => setTimeRange(e.target.value)}
|
||||
label="时间范围"
|
||||
>
|
||||
<MenuItem value="1h">最近1小时</MenuItem>
|
||||
<MenuItem value="24h">最近24小时</MenuItem>
|
||||
<MenuItem value="7d">最近7天</MenuItem>
|
||||
<MenuItem value="30d">最近30天</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</PageHeader>
|
||||
|
||||
{/* 关键指标卡片 */}
|
||||
<Grid container spacing={3} sx={{ mb: 3 }}>
|
||||
<Grid item xs={12} sm={6} md={3}>
|
||||
<MetricCard>
|
||||
<CardContent>
|
||||
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||
<Box>
|
||||
<Typography color="text.secondary" variant="body2">
|
||||
总查询数
|
||||
</Typography>
|
||||
<MetricValue color="primary">
|
||||
{mockMetrics.totalQueries.value.toLocaleString()}
|
||||
</MetricValue>
|
||||
<TrendIndicator trend={mockMetrics.totalQueries.trend}>
|
||||
{mockMetrics.totalQueries.trend === 'up' ?
|
||||
<TrendingUpIcon fontSize="small" /> :
|
||||
<TrendingDownIcon fontSize="small" />
|
||||
}
|
||||
{mockMetrics.totalQueries.change}
|
||||
</TrendIndicator>
|
||||
</Box>
|
||||
<AssessmentIcon color="primary" sx={{ fontSize: '3rem', opacity: 0.3 }} />
|
||||
</Box>
|
||||
</CardContent>
|
||||
</MetricCard>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={6} md={3}>
|
||||
<MetricCard>
|
||||
<CardContent>
|
||||
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||
<Box>
|
||||
<Typography color="text.secondary" variant="body2">
|
||||
平均响应时间
|
||||
</Typography>
|
||||
<MetricValue color="warning.main">
|
||||
{mockMetrics.avgResponseTime.value}
|
||||
</MetricValue>
|
||||
<TrendIndicator trend={mockMetrics.avgResponseTime.trend}>
|
||||
{mockMetrics.avgResponseTime.trend === 'up' ?
|
||||
<TrendingUpIcon fontSize="small" /> :
|
||||
<TrendingDownIcon fontSize="small" />
|
||||
}
|
||||
{mockMetrics.avgResponseTime.change}
|
||||
</TrendIndicator>
|
||||
</Box>
|
||||
<SpeedIcon color="warning" sx={{ fontSize: '3rem', opacity: 0.3 }} />
|
||||
</Box>
|
||||
</CardContent>
|
||||
</MetricCard>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={6} md={3}>
|
||||
<MetricCard>
|
||||
<CardContent>
|
||||
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||
<Box>
|
||||
<Typography color="text.secondary" variant="body2">
|
||||
成功率
|
||||
</Typography>
|
||||
<MetricValue color="success.main">
|
||||
{mockMetrics.successRate.value}
|
||||
</MetricValue>
|
||||
<TrendIndicator trend={mockMetrics.successRate.trend}>
|
||||
{mockMetrics.successRate.trend === 'up' ?
|
||||
<TrendingUpIcon fontSize="small" /> :
|
||||
<TrendingDownIcon fontSize="small" />
|
||||
}
|
||||
{mockMetrics.successRate.change}
|
||||
</TrendIndicator>
|
||||
</Box>
|
||||
<CheckCircleIcon color="success" sx={{ fontSize: '3rem', opacity: 0.3 }} />
|
||||
</Box>
|
||||
</CardContent>
|
||||
</MetricCard>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={6} md={3}>
|
||||
<MetricCard>
|
||||
<CardContent>
|
||||
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||
<Box>
|
||||
<Typography color="text.secondary" variant="body2">
|
||||
活跃用户
|
||||
</Typography>
|
||||
<MetricValue color="info.main">
|
||||
{mockMetrics.activeUsers.value.toLocaleString()}
|
||||
</MetricValue>
|
||||
<TrendIndicator trend={mockMetrics.activeUsers.trend}>
|
||||
{mockMetrics.activeUsers.trend === 'up' ?
|
||||
<TrendingUpIcon fontSize="small" /> :
|
||||
<TrendingDownIcon fontSize="small" />
|
||||
}
|
||||
{mockMetrics.activeUsers.change}
|
||||
</TrendIndicator>
|
||||
</Box>
|
||||
<AssessmentIcon color="info" sx={{ fontSize: '3rem', opacity: 0.3 }} />
|
||||
</Box>
|
||||
</CardContent>
|
||||
</MetricCard>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* 系统状态 */}
|
||||
<Grid container spacing={3} sx={{ mb: 3 }}>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Card sx={{ border: '1px solid #E5E5E5' }}>
|
||||
<CardContent>
|
||||
<Typography variant="h6" fontWeight={600} mb={2}>
|
||||
系统状态
|
||||
</Typography>
|
||||
<Box mb={2}>
|
||||
<Box display="flex" justifyContent="space-between" mb={1}>
|
||||
<Typography variant="body2">CPU 使用率</Typography>
|
||||
<Typography variant="body2" fontWeight={600}>45%</Typography>
|
||||
</Box>
|
||||
<LinearProgress variant="determinate" value={45} sx={{ height: 8, borderRadius: 4 }} />
|
||||
</Box>
|
||||
<Box mb={2}>
|
||||
<Box display="flex" justifyContent="space-between" mb={1}>
|
||||
<Typography variant="body2">内存使用率</Typography>
|
||||
<Typography variant="body2" fontWeight={600}>67%</Typography>
|
||||
</Box>
|
||||
<LinearProgress variant="determinate" value={67} color="warning" sx={{ height: 8, borderRadius: 4 }} />
|
||||
</Box>
|
||||
<Box>
|
||||
<Box display="flex" justifyContent="space-between" mb={1}>
|
||||
<Typography variant="body2">存储使用率</Typography>
|
||||
<Typography variant="body2" fontWeight={600}>23%</Typography>
|
||||
</Box>
|
||||
<LinearProgress variant="determinate" value={23} color="success" sx={{ height: 8, borderRadius: 4 }} />
|
||||
</Box>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} md={6}>
|
||||
<Card sx={{ border: '1px solid #E5E5E5' }}>
|
||||
<CardContent>
|
||||
<Typography variant="h6" fontWeight={600} mb={2}>
|
||||
知识库状态
|
||||
</Typography>
|
||||
<Box display="flex" flexDirection="column" gap={1}>
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
<Typography variant="body2">产品文档库</Typography>
|
||||
<StatusChip status="success" label="正常" size="small" />
|
||||
</Box>
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
<Typography variant="body2">客服FAQ</Typography>
|
||||
<StatusChip status="success" label="正常" size="small" />
|
||||
</Box>
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
<Typography variant="body2">法律合规</Typography>
|
||||
<StatusChip status="warning" label="同步中" size="small" />
|
||||
</Box>
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
<Typography variant="body2">培训资料</Typography>
|
||||
<StatusChip status="error" label="错误" size="small" />
|
||||
</Box>
|
||||
</Box>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* 最近查询记录 */}
|
||||
<Card sx={{ border: '1px solid #E5E5E5' }}>
|
||||
<CardContent>
|
||||
<Typography variant="h6" fontWeight={600} mb={2}>
|
||||
最近查询记录
|
||||
</Typography>
|
||||
<TableContainer>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>查询内容</TableCell>
|
||||
<TableCell>用户</TableCell>
|
||||
<TableCell>知识库</TableCell>
|
||||
<TableCell>状态</TableCell>
|
||||
<TableCell>响应时间</TableCell>
|
||||
<TableCell>时间</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{mockRecentQueries.map((query) => (
|
||||
<TableRow key={query.id} hover>
|
||||
<TableCell>
|
||||
<Typography variant="body2" sx={{ maxWidth: '300px', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
||||
{query.query}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell>{query.user}</TableCell>
|
||||
<TableCell>{query.knowledgeBase}</TableCell>
|
||||
<TableCell>
|
||||
<StatusChip status={query.status} label={
|
||||
query.status === 'success' ? '成功' :
|
||||
query.status === 'warning' ? '警告' : '失败'
|
||||
} size="small" />
|
||||
</TableCell>
|
||||
<TableCell>{query.responseTime}</TableCell>
|
||||
<TableCell>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
{query.timestamp}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Dashboard;
|
||||
Reference in New Issue
Block a user