1.18升级
This commit is contained in:
parent
38b6e873c8
commit
eebd3240b3
@ -0,0 +1,570 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import ReactECharts from 'echarts-for-react';
|
||||||
|
import type { EChartsOption } from 'echarts';
|
||||||
|
import { Loader2, TrendingUp, Activity, HardDrive, Network as NetworkIcon } from 'lucide-react';
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogBody,
|
||||||
|
} from '@/components/ui/dialog';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Badge } from '@/components/ui/badge';
|
||||||
|
import { useToast } from '@/components/ui/use-toast';
|
||||||
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||||
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
|
import type { ServerResponse, ServerMonitorResponse, TimeRange, MetricType } from '../types';
|
||||||
|
import { TimeRangeLabels, MetricTypeLabels } from '../types';
|
||||||
|
import { getServerMonitorMetrics } from '../service';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
|
interface ServerMonitorDialogProps {
|
||||||
|
open: boolean;
|
||||||
|
onOpenChange: (open: boolean) => void;
|
||||||
|
server: ServerResponse | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ServerMonitorDialog: React.FC<ServerMonitorDialogProps> = ({
|
||||||
|
open,
|
||||||
|
onOpenChange,
|
||||||
|
server,
|
||||||
|
}) => {
|
||||||
|
const { toast } = useToast();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [monitorData, setMonitorData] = useState<ServerMonitorResponse | null>(null);
|
||||||
|
const [selectedTimeRange, setSelectedTimeRange] = useState<TimeRange>('LAST_1_HOUR' as TimeRange);
|
||||||
|
const [autoRefresh, setAutoRefresh] = useState(true);
|
||||||
|
|
||||||
|
// 加载监控数据
|
||||||
|
const loadMonitorData = async () => {
|
||||||
|
if (!server) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const response = await getServerMonitorMetrics(server.id, selectedTimeRange);
|
||||||
|
setMonitorData(response);
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('加载监控数据失败:', error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始加载和时间范围变化时加载数据
|
||||||
|
useEffect(() => {
|
||||||
|
if (open && server) {
|
||||||
|
loadMonitorData();
|
||||||
|
}
|
||||||
|
}, [open, server, selectedTimeRange]);
|
||||||
|
|
||||||
|
// 自动刷新(仅最近1小时)
|
||||||
|
useEffect(() => {
|
||||||
|
if (!open || !autoRefresh || selectedTimeRange !== 'LAST_1_HOUR') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const timer = setInterval(() => {
|
||||||
|
loadMonitorData();
|
||||||
|
}, 30000); // 30秒刷新一次
|
||||||
|
|
||||||
|
return () => clearInterval(timer);
|
||||||
|
}, [open, autoRefresh, selectedTimeRange]);
|
||||||
|
|
||||||
|
// CPU 图表配置
|
||||||
|
const getCpuChartOption = (): EChartsOption => {
|
||||||
|
if (!monitorData?.metrics.cpu || monitorData.metrics.cpu.length === 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const cpuData = monitorData.metrics.cpu;
|
||||||
|
const statistics = monitorData.statistics.cpu;
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: {
|
||||||
|
text: 'CPU 使用率',
|
||||||
|
left: 'center',
|
||||||
|
textStyle: { fontSize: 14, fontWeight: 'bold' },
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
formatter: (params: any) => {
|
||||||
|
const param = params[0];
|
||||||
|
const dataItem = cpuData[param.dataIndex];
|
||||||
|
return `${dayjs(param.name).format('MM-DD HH:mm')}<br/>
|
||||||
|
CPU: ${param.value}%<br/>
|
||||||
|
状态: ${dataItem.status === 'SUCCESS' ? '正常' : '失败'}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '3%',
|
||||||
|
right: '4%',
|
||||||
|
bottom: '10%',
|
||||||
|
top: '20%',
|
||||||
|
containLabel: true,
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
data: cpuData.map(item => item.time),
|
||||||
|
axisLabel: {
|
||||||
|
formatter: (value: string) => dayjs(value).format('HH:mm'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
name: 'CPU (%)',
|
||||||
|
max: 100,
|
||||||
|
axisLabel: {
|
||||||
|
formatter: '{value}%',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: 'CPU使用率',
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
data: cpuData.map(item => item.value),
|
||||||
|
lineStyle: {
|
||||||
|
color: MetricTypeLabels.CPU.color,
|
||||||
|
width: 2,
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
color: {
|
||||||
|
type: 'linear',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{ offset: 0, color: `${MetricTypeLabels.CPU.color}40` },
|
||||||
|
{ offset: 1, color: `${MetricTypeLabels.CPU.color}10` },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
markLine: statistics
|
||||||
|
? {
|
||||||
|
data: [
|
||||||
|
{ yAxis: statistics.avg, name: '平均值', lineStyle: { type: 'dashed', color: '#666' } },
|
||||||
|
],
|
||||||
|
label: {
|
||||||
|
formatter: '平均: {c}%',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// 内存图表配置
|
||||||
|
const getMemoryChartOption = (): EChartsOption => {
|
||||||
|
if (!monitorData?.metrics.memory || monitorData.metrics.memory.length === 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const memoryData = monitorData.metrics.memory;
|
||||||
|
const statistics = monitorData.statistics.memory;
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: {
|
||||||
|
text: '内存使用率',
|
||||||
|
left: 'center',
|
||||||
|
textStyle: { fontSize: 14, fontWeight: 'bold' },
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
formatter: (params: any) => {
|
||||||
|
const param = params[0];
|
||||||
|
const dataItem = memoryData[param.dataIndex];
|
||||||
|
return `${dayjs(param.name).format('MM-DD HH:mm')}<br/>
|
||||||
|
内存: ${param.value}%<br/>
|
||||||
|
已用: ${dataItem.usedGB}GB<br/>
|
||||||
|
状态: ${dataItem.status === 'SUCCESS' ? '正常' : '失败'}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '3%',
|
||||||
|
right: '4%',
|
||||||
|
bottom: '10%',
|
||||||
|
top: '20%',
|
||||||
|
containLabel: true,
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
data: memoryData.map(item => item.time),
|
||||||
|
axisLabel: {
|
||||||
|
formatter: (value: string) => dayjs(value).format('HH:mm'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
name: '内存 (%)',
|
||||||
|
max: 100,
|
||||||
|
axisLabel: {
|
||||||
|
formatter: '{value}%',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '内存使用率',
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
data: memoryData.map(item => item.usagePercent),
|
||||||
|
lineStyle: {
|
||||||
|
color: MetricTypeLabels.MEMORY.color,
|
||||||
|
width: 2,
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
color: {
|
||||||
|
type: 'linear',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{ offset: 0, color: `${MetricTypeLabels.MEMORY.color}40` },
|
||||||
|
{ offset: 1, color: `${MetricTypeLabels.MEMORY.color}10` },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
markLine: statistics
|
||||||
|
? {
|
||||||
|
data: [
|
||||||
|
{ yAxis: statistics.avgPercent, name: '平均值', lineStyle: { type: 'dashed', color: '#666' } },
|
||||||
|
],
|
||||||
|
label: {
|
||||||
|
formatter: '平均: {c}%',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// 网络流量图表配置
|
||||||
|
const getNetworkChartOption = (): EChartsOption => {
|
||||||
|
if (!monitorData?.metrics.network || monitorData.metrics.network.length === 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const networkData = monitorData.metrics.network;
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: {
|
||||||
|
text: '网络流量',
|
||||||
|
left: 'center',
|
||||||
|
textStyle: { fontSize: 14, fontWeight: 'bold' },
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
formatter: (params: any) => {
|
||||||
|
const dataItem = networkData[params[0].dataIndex];
|
||||||
|
return `${dayjs(params[0].name).format('MM-DD HH:mm')}<br/>
|
||||||
|
接收: ${dataItem.rxMBps.toFixed(2)} MB/s<br/>
|
||||||
|
发送: ${dataItem.txMBps.toFixed(2)} MB/s`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
data: ['接收', '发送'],
|
||||||
|
top: '8%',
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '3%',
|
||||||
|
right: '4%',
|
||||||
|
bottom: '10%',
|
||||||
|
top: '20%',
|
||||||
|
containLabel: true,
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
data: networkData.map(item => item.time),
|
||||||
|
axisLabel: {
|
||||||
|
formatter: (value: string) => dayjs(value).format('HH:mm'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
name: '流量 (MB/s)',
|
||||||
|
axisLabel: {
|
||||||
|
formatter: '{value}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '接收',
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
data: networkData.map(item => item.rxMBps),
|
||||||
|
lineStyle: {
|
||||||
|
color: '#3b82f6',
|
||||||
|
width: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '发送',
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
data: networkData.map(item => item.txMBps),
|
||||||
|
lineStyle: {
|
||||||
|
color: '#8b5cf6',
|
||||||
|
width: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// 磁盘使用情况卡片
|
||||||
|
const renderDiskInfo = () => {
|
||||||
|
if (!monitorData?.metrics.disk) {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-center h-64 text-muted-foreground">
|
||||||
|
暂无磁盘数据
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const disk = monitorData.metrics.disk;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<h3 className="text-lg font-semibold">磁盘使用情况</h3>
|
||||||
|
<Badge variant="outline">
|
||||||
|
最大使用率: {disk.maxUsagePercent.toFixed(2)}% ({disk.maxUsagePartition})
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
{disk.partitions.map((partition, index) => (
|
||||||
|
<Card key={index}>
|
||||||
|
<CardContent className="pt-4">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<HardDrive className="h-4 w-4 text-muted-foreground" />
|
||||||
|
<span className="font-mono font-medium">{partition.mountPoint}</span>
|
||||||
|
</div>
|
||||||
|
<Badge
|
||||||
|
variant={partition.usagePercent > 80 ? 'destructive' : 'outline'}
|
||||||
|
>
|
||||||
|
{partition.usagePercent.toFixed(2)}%
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-muted-foreground">
|
||||||
|
{partition.fileSystem}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-muted-foreground">
|
||||||
|
已用 {partition.usedSizeGB}GB / 总计 {partition.totalSizeGB}GB
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="w-full bg-secondary rounded-full h-2">
|
||||||
|
<div
|
||||||
|
className={`h-2 rounded-full ${
|
||||||
|
partition.usagePercent > 80
|
||||||
|
? 'bg-destructive'
|
||||||
|
: partition.usagePercent > 60
|
||||||
|
? 'bg-yellow-500'
|
||||||
|
: 'bg-primary'
|
||||||
|
}`}
|
||||||
|
style={{ width: `${partition.usagePercent}%` }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 统计信息卡片
|
||||||
|
const renderStatistics = () => {
|
||||||
|
if (!monitorData) return null;
|
||||||
|
|
||||||
|
const stats = [];
|
||||||
|
|
||||||
|
if (monitorData.statistics.cpu) {
|
||||||
|
stats.push({
|
||||||
|
icon: <Activity className="h-5 w-5" />,
|
||||||
|
label: 'CPU',
|
||||||
|
avg: `${monitorData.statistics.cpu.avg.toFixed(2)}%`,
|
||||||
|
max: `${monitorData.statistics.cpu.max.toFixed(2)}%`,
|
||||||
|
min: `${monitorData.statistics.cpu.min.toFixed(2)}%`,
|
||||||
|
color: 'text-blue-600',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (monitorData.statistics.memory) {
|
||||||
|
stats.push({
|
||||||
|
icon: <TrendingUp className="h-5 w-5" />,
|
||||||
|
label: '内存',
|
||||||
|
avg: `${monitorData.statistics.memory.avgPercent.toFixed(2)}%`,
|
||||||
|
max: `${monitorData.statistics.memory.maxPercent.toFixed(2)}%`,
|
||||||
|
min: `${monitorData.statistics.memory.minPercent.toFixed(2)}%`,
|
||||||
|
color: 'text-green-600',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (monitorData.statistics.network) {
|
||||||
|
const network = monitorData.statistics.network;
|
||||||
|
const totalRxMB = (network.totalRxBytes / 1024 / 1024).toFixed(2);
|
||||||
|
const totalTxMB = (network.totalTxBytes / 1024 / 1024).toFixed(2);
|
||||||
|
|
||||||
|
stats.push({
|
||||||
|
icon: <NetworkIcon className="h-5 w-5" />,
|
||||||
|
label: '网络',
|
||||||
|
avg: `接收 ${totalRxMB}MB`,
|
||||||
|
max: `发送 ${totalTxMB}MB`,
|
||||||
|
min: '',
|
||||||
|
color: 'text-purple-600',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
|
||||||
|
{stats.map((stat, index) => (
|
||||||
|
<Card key={index}>
|
||||||
|
<CardContent className="pt-4">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className={`${stat.color}`}>{stat.icon}</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<div className="text-sm font-medium text-muted-foreground">{stat.label}</div>
|
||||||
|
<div className="text-xs text-muted-foreground mt-1 space-y-0.5">
|
||||||
|
<div>平均: {stat.avg}</div>
|
||||||
|
<div>最大: {stat.max}</div>
|
||||||
|
{stat.min && <div>最小: {stat.min}</div>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
|
<DialogContent className="max-w-6xl max-h-[90vh] overflow-hidden flex flex-col">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>
|
||||||
|
服务器监控 - {server?.serverName} ({server?.hostIp})
|
||||||
|
</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<DialogBody className="flex-1 overflow-y-auto">
|
||||||
|
<div className="space-y-4">
|
||||||
|
{/* 时间范围选择器 */}
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{Object.entries(TimeRangeLabels).map(([key, value]) => (
|
||||||
|
<Button
|
||||||
|
key={key}
|
||||||
|
variant={selectedTimeRange === key ? 'default' : 'outline'}
|
||||||
|
size="sm"
|
||||||
|
onClick={() => setSelectedTimeRange(key as TimeRange)}
|
||||||
|
>
|
||||||
|
{value.label}
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{selectedTimeRange === 'LAST_1_HOUR' && (
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => setAutoRefresh(!autoRefresh)}
|
||||||
|
>
|
||||||
|
{autoRefresh ? '停止自动刷新' : '开启自动刷新'}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={loadMonitorData}
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : '刷新'}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{loading && !monitorData ? (
|
||||||
|
<div className="flex items-center justify-center h-64">
|
||||||
|
<Loader2 className="h-8 w-8 animate-spin text-primary" />
|
||||||
|
</div>
|
||||||
|
) : monitorData ? (
|
||||||
|
<>
|
||||||
|
{/* 统计信息 */}
|
||||||
|
{renderStatistics()}
|
||||||
|
|
||||||
|
{/* 图表标签页 */}
|
||||||
|
<Tabs defaultValue="cpu" className="w-full">
|
||||||
|
<TabsList className="grid w-full grid-cols-4">
|
||||||
|
<TabsTrigger value="cpu">CPU</TabsTrigger>
|
||||||
|
<TabsTrigger value="memory">内存</TabsTrigger>
|
||||||
|
<TabsTrigger value="network">网络</TabsTrigger>
|
||||||
|
<TabsTrigger value="disk">磁盘</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
|
||||||
|
<TabsContent value="cpu" className="space-y-4">
|
||||||
|
{monitorData.metrics.cpu && monitorData.metrics.cpu.length > 0 ? (
|
||||||
|
<ReactECharts
|
||||||
|
option={getCpuChartOption()}
|
||||||
|
style={{ height: '400px', width: '100%' }}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="flex items-center justify-center h-64 text-muted-foreground">
|
||||||
|
暂无CPU数据
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</TabsContent>
|
||||||
|
|
||||||
|
<TabsContent value="memory" className="space-y-4">
|
||||||
|
{monitorData.metrics.memory && monitorData.metrics.memory.length > 0 ? (
|
||||||
|
<ReactECharts
|
||||||
|
option={getMemoryChartOption()}
|
||||||
|
style={{ height: '400px', width: '100%' }}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="flex items-center justify-center h-64 text-muted-foreground">
|
||||||
|
暂无内存数据
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</TabsContent>
|
||||||
|
|
||||||
|
<TabsContent value="network" className="space-y-4">
|
||||||
|
{monitorData.metrics.network && monitorData.metrics.network.length > 0 ? (
|
||||||
|
<ReactECharts
|
||||||
|
option={getNetworkChartOption()}
|
||||||
|
style={{ height: '400px', width: '100%' }}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="flex items-center justify-center h-64 text-muted-foreground">
|
||||||
|
暂无网络数据
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</TabsContent>
|
||||||
|
|
||||||
|
<TabsContent value="disk" className="space-y-4">
|
||||||
|
{renderDiskInfo()}
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div className="flex items-center justify-center h-64 text-muted-foreground">
|
||||||
|
暂无数据
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</DialogBody>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue
Block a user