Files
xiaoxia-saas/apps/web/src/pages/admin/SystemMonitor.tsx
T
2026-06-21 18:03:45 +08:00

232 lines
7.7 KiB
TypeScript

/**
* 系统监控页面
*/
import React, { useState, useEffect } from 'react';
import { Card, Row, Col, Progress, Badge, Table, Tag, Button, Space } from 'antd';
import {
CheckCircleOutlined,
CloseCircleOutlined,
SyncOutlined,
ReloadOutlined,
DatabaseOutlined,
ApiOutlined,
CloudServerOutlined,
ThunderboltOutlined,
} from '@ant-design/icons';
const SystemMonitor: React.FC = () => {
const [refreshTime, setRefreshTime] = useState(new Date());
// 模拟实时数据
const [systemMetrics, setSystemMetrics] = useState({
cpu: 35,
memory: 62,
disk: 48,
network: 28,
});
// 服务健康状态
const services = [
{ name: 'API 服务', status: 'healthy', uptime: '99.98%', responseTime: '45ms' },
{ name: 'PostgreSQL', status: 'healthy', uptime: '99.99%', responseTime: '8ms' },
{ name: 'Redis', status: 'healthy', uptime: '99.97%', responseTime: '2ms' },
{ name: 'Celery Worker', status: 'healthy', uptime: '99.95%', responseTime: '-' },
{ name: 'Object Storage', status: 'warning', uptime: '99.85%', responseTime: '120ms' },
];
// API 请求统计
const apiStats = [
{ endpoint: 'POST /api/v1/auth/login', requests: 15420, avgTime: '48ms', errors: 12 },
{ endpoint: 'GET /api/v1/workspaces', requests: 89340, avgTime: '32ms', errors: 5 },
{ endpoint: 'POST /api/v1/projects', requests: 6780, avgTime: '156ms', errors: 8 },
{ endpoint: 'GET /api/v1/assets', requests: 124560, avgTime: '68ms', errors: 23 },
{ endpoint: 'POST /api/v1/subscriptions/subscribe', requests: 234, avgTime: '890ms', errors: 2 },
];
// 错误日志
const recentErrors = [
{ time: '2024-06-17 16:45:32', level: 'ERROR', service: 'API', message: 'Database connection timeout' },
{ time: '2024-06-17 16:42:15', level: 'WARN', service: 'Object Storage', message: 'Slow response detected (>1s)' },
{ time: '2024-06-17 16:38:41', level: 'ERROR', service: 'Celery', message: 'Task retry limit exceeded' },
];
const serviceColumns = [
{
title: '服务名称',
dataIndex: 'name',
key: 'name',
render: (text: string) => <><CloudServerOutlined /> {text}</>,
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
render: (status: string) => {
const config: Record<string, { color: string; icon: React.ReactNode; text: string }> = {
healthy: { color: 'success', icon: <CheckCircleOutlined />, text: '正常' },
warning: { color: 'warning', icon: <SyncOutlined spin />, text: '警告' },
error: { color: 'error', icon: <CloseCircleOutlined />, text: '故障' },
};
const { color, text } = config[status] || config.healthy;
return <Badge status={color as any} text={text} />;
},
},
{ title: '可用性', dataIndex: 'uptime', key: 'uptime' },
{ title: '响应时间', dataIndex: 'responseTime', key: 'responseTime' },
];
const apiColumns = [
{ title: 'API 端点', dataIndex: 'endpoint', key: 'endpoint' },
{ title: '请求数', dataIndex: 'requests', key: 'requests', sorter: (a: any, b: any) => a.requests - b.requests },
{ title: '平均响应时间', dataIndex: 'avgTime', key: 'avgTime' },
{
title: '错误数',
dataIndex: 'errors',
key: 'errors',
render: (errors: number) => (
<Tag color={errors > 10 ? 'red' : errors > 5 ? 'orange' : 'green'}>{errors}</Tag>
),
},
];
const errorColumns = [
{ title: '时间', dataIndex: 'time', key: 'time', width: 180 },
{
title: '级别',
dataIndex: 'level',
key: 'level',
width: 100,
render: (level: string) => (
<Tag color={level === 'ERROR' ? 'red' : 'orange'}>{level}</Tag>
),
},
{ title: '服务', dataIndex: 'service', key: 'service', width: 120 },
{ title: '错误信息', dataIndex: 'message', key: 'message' },
];
const refreshMetrics = () => {
setSystemMetrics({
cpu: Math.floor(Math.random() * 30) + 30,
memory: Math.floor(Math.random() * 20) + 55,
disk: Math.floor(Math.random() * 10) + 45,
network: Math.floor(Math.random() * 40) + 20,
});
setRefreshTime(new Date());
};
// 模拟实时更新
useEffect(() => {
const interval = setInterval(refreshMetrics, 5000);
return () => clearInterval(interval);
}, []);
const getProgressColor = (value: number) => {
if (value > 80) return '#ff4d4f';
if (value > 60) return '#faad14';
return '#52c41a';
};
return (
<div style={{ padding: '24px' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 24 }}>
<h1>系统监控</h1>
<Space>
<span style={{ color: '#8c8c8c' }}>上次更新: {refreshTime.toLocaleTimeString()}</span>
<Button icon={<ReloadOutlined />} onClick={refreshMetrics}>刷新</Button>
</Space>
</div>
{/* 系统资源监控 */}
<Row gutter={16} style={{ marginBottom: 24 }}>
<Col span={6}>
<Card>
<div style={{ textAlign: 'center' }}>
<ThunderboltOutlined style={{ fontSize: 32, color: '#1890ff', marginBottom: 8 }} />
<div style={{ marginBottom: 16 }}>CPU 使用率</div>
<Progress
type="circle"
percent={systemMetrics.cpu}
strokeColor={getProgressColor(systemMetrics.cpu)}
/>
</div>
</Card>
</Col>
<Col span={6}>
<Card>
<div style={{ textAlign: 'center' }}>
<DatabaseOutlined style={{ fontSize: 32, color: '#52c41a', marginBottom: 8 }} />
<div style={{ marginBottom: 16 }}>内存使用率</div>
<Progress
type="circle"
percent={systemMetrics.memory}
strokeColor={getProgressColor(systemMetrics.memory)}
/>
</div>
</Card>
</Col>
<Col span={6}>
<Card>
<div style={{ textAlign: 'center' }}>
<CloudServerOutlined style={{ fontSize: 32, color: '#faad14', marginBottom: 8 }} />
<div style={{ marginBottom: 16 }}>磁盘使用率</div>
<Progress
type="circle"
percent={systemMetrics.disk}
strokeColor={getProgressColor(systemMetrics.disk)}
/>
</div>
</Card>
</Col>
<Col span={6}>
<Card>
<div style={{ textAlign: 'center' }}>
<ApiOutlined style={{ fontSize: 32, color: '#722ed1', marginBottom: 8 }} />
<div style={{ marginBottom: 16 }}>网络使用率</div>
<Progress
type="circle"
percent={systemMetrics.network}
strokeColor={getProgressColor(systemMetrics.network)}
/>
</div>
</Card>
</Col>
</Row>
{/* 服务健康状态 */}
<Card title="服务健康状态" style={{ marginBottom: 24 }}>
<Table
columns={serviceColumns}
dataSource={services}
rowKey="name"
pagination={false}
/>
</Card>
{/* API 请求统计 */}
<Card title="API 请求统计 (最近 1 小时)" style={{ marginBottom: 24 }}>
<Table
columns={apiColumns}
dataSource={apiStats}
rowKey="endpoint"
pagination={false}
/>
</Card>
{/* 最近错误 */}
<Card title="最近错误日志">
<Table
columns={errorColumns}
dataSource={recentErrors}
rowKey="time"
pagination={false}
/>
</Card>
</div>
);
};
export default SystemMonitor;
export const Component = SystemMonitor;