diff --git a/frontend/src/components/monitor/dashboard/HostMonitorDashboard.tsx b/frontend/src/components/monitor/dashboard/HostMonitorDashboard.tsx
deleted file mode 100644
index fcce414..0000000
--- a/frontend/src/components/monitor/dashboard/HostMonitorDashboard.tsx
+++ /dev/null
@@ -1,496 +0,0 @@
-// 主机监控大屏 — Grafana 风格:统计卡片 + 资源条状图 + 可排序列表 // Logan.Li
-// 参照: Grafana node-dashboard.json (表格+时序) / kubernetes-dashboard.json (bargauge+阈值)
-import { useState, useEffect, useCallback } from 'react';
-import { useNavigate } from 'react-router-dom';
-import { useTranslation } from 'react-i18next';
-import {
- Server,
- Cpu,
- HardDrive,
- Activity,
- Monitor,
- Bell,
- ArrowUpDown,
- ArrowUp,
- ArrowDown,
- RefreshCw,
- Search,
- ChevronLeft,
- ChevronRight,
- Wifi,
- WifiOff,
-} from 'lucide-react';
-
-import { Button } from '@/components/ui/button';
-import { Input } from '@/components/ui/input';
-import { Badge } from '@/components/ui/badge';
-import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
-import {
- Select, SelectContent, SelectItem, SelectTrigger, SelectValue
-} from '@/components/ui/select';
-import { serverApi } from '@/api';
-import type { JsonData } from '@/types/common';
-
-type SortField = 'hostname' | 'ip' | 'agent_version' | 'agent_status' | 'cpu_usage' | 'memory_usage' | 'disk_usage' | 'region';
-type SortDir = 'asc' | 'desc';
-
-function getUsageBar(val: number) {
- const v = val || 0;
- const color = v > 85 ? 'bg-red-500' : v > 60 ? 'bg-amber-500' : 'bg-emerald-500';
- return (
-
- );
-}
-
-export default function HostMonitorDashboard() {
- const { t } = useTranslation(['monitor', 'common']);
- const navigate = useNavigate();
- const [hosts, setHosts] = useState([]);
- const [total, setTotal] = useState(0);
- const [page, setPage] = useState(1);
- const [pageSize, setPageSize] = useState(10);
- const [search, setSearch] = useState('');
- const [loading, setLoading] = useState(true);
- const [refreshing, setRefreshing] = useState(false);
-
- // 排序状态
- const [sortField, setSortField] = useState('hostname');
- const [sortDir, setSortDir] = useState('asc');
-
- // 统计
- const [stats, setStats] = useState<{ online: number; offline: number; total: number }>({ online: 0, offline: 0, total: 0 });
-
- const loadData = useCallback(async (isRefresh = false) => {
- if (isRefresh) setRefreshing(true);
- else setLoading(true);
- try {
- const params: JsonData = { page, page_size: pageSize };
- if (search.trim()) params.keyword = search.trim();
- const res = await serverApi.getServers(params);
- const list = (res as JsonData)?.list || [];
- setHosts(list);
- setTotal((res as JsonData)?.total || 0);
-
- // 统计
- const online = list.filter((h: JsonData) => h.agent_status === 'online').length;
- setStats({ online, offline: list.length - online, total: (res as JsonData)?.total || 0 });
- } catch {
- setHosts([]);
- setTotal(0);
- } finally {
- setLoading(false);
- setRefreshing(false);
- }
- }, [page, pageSize, search]);
-
- useEffect(() => { loadData(); }, [loadData]);
-
- // 排序后的数据
- const sorted = [...hosts].sort((a: JsonData, b: JsonData) => {
- let va: JsonData | undefined, vb: JsonData | undefined;
- if (sortField === 'region') {
- va = (a.region?.name || a.region?.Name || '').toLowerCase();
- vb = (b.region?.name || b.region?.Name || '').toLowerCase();
- } else if (sortField === 'agent_status') {
- va = a.agent_status || 'offline';
- vb = b.agent_status || 'offline';
- } else if (sortField === 'agent_version') {
- va = a.agent_version || '';
- vb = b.agent_version || '';
- } else {
- va = a[sortField] || '';
- vb = b[sortField] || '';
- }
-
- if (typeof va === 'number' && typeof vb === 'number') {
- return sortDir === 'asc' ? va - vb : vb - va;
- }
- const sa = String(va).toLowerCase();
- const sb = String(vb).toLowerCase();
- if (sa < sb) return sortDir === 'asc' ? -1 : 1;
- if (sa > sb) return sortDir === 'asc' ? 1 : -1;
- return 0;
- });
-
- const handleSort = (field: SortField) => {
- if (sortField === field) {
- setSortDir(d => d === 'asc' ? 'desc' : 'asc');
- } else {
- setSortField(field);
- setSortDir('asc');
- }
- };
-
- const SortIcon = ({ field }: { field: SortField }) => {
- if (sortField !== field) return ;
- return sortDir === 'asc'
- ?
- : ;
- };
-
- const totalPages = Math.ceil(total / pageSize);
-
- const handleSearch = () => {
- setPage(1);
- loadData();
- };
-
- // 状态标签颜色
- const statusBadge = (status: string) => {
- if (status === 'online') return {t('common:online')};
- if (status === 'running') return {t('common:running')};
- if (status === 'stopped') return {t('common:stopped')};
- return {status || '--'};
- };
-
- return (
-
- {/* 统计卡片 — Grafana Quick Facts 风格 (参照 JVM Dashboard) */}
-
-
-
-
-
-
{stats.total}
-
{t('monitor:hostDashboard.hostTotal')}
-
-
-
-
-
-
-
-
{stats.online}
-
{t('monitor:hostDashboard.agentOnline')}
-
-
-
-
-
-
-
-
{stats.offline}
-
{t('common:offline')}
-
-
-
- {(() => {
- const onlines = hosts.filter((h: JsonData) => h.agent_status === 'online');
- const avgCpu = onlines.length > 0 ? onlines.reduce((s: number, h: JsonData) => s + (h.cpu_usage || 0), 0) / onlines.length : 0;
- const avgMem = onlines.length > 0 ? onlines.reduce((s: number, h: JsonData) => s + (h.memory_usage || 0), 0) / onlines.length : 0;
- const cpuColor = avgCpu > 85 ? 'dg-text-red' : avgCpu > 60 ? 'dg-text-yellow' : 'dg-text-green';
- const memColor = avgMem > 85 ? 'dg-text-red' : avgMem > 60 ? 'dg-text-yellow' : 'dg-text-green';
- return (<>
-
-
-
-
-
{avgCpu.toFixed(1)}%
-
{t('monitor:hostDashboard.avgCpu')}
-
-
-
-
-
-
-
-
{avgMem.toFixed(1)}%
-
{t('monitor:hostDashboard.avgMemory')}
-
-
-
- >);
- })()}
-
-
- {/* 资源条状图 — Grafana Bargauge 风格 (参照 K8s Node CPU/Memory Ratio) */}
- {hosts.filter((h: JsonData) => h.agent_status === 'online').length > 0 && (
-
- {/* CPU 排行 */}
-
-
-
-
- {t('monitor:hostDashboard.cpuTop5')}
- {t('monitor:hostDashboard.threshold')}
-
-
-
- {[...hosts]
- .filter((h: JsonData) => h.agent_status === 'online')
- .sort((a: JsonData, b: JsonData) => (b.cpu_usage || 0) - (a.cpu_usage || 0))
- .slice(0, 5)
- .map((h: JsonData) => {
- const val = h.cpu_usage || 0;
- return (
-
-
{h.hostname}
-
-
= 85
- ? 'linear-gradient(90deg, #ef4444, #f87171)'
- : val >= 60
- ? 'linear-gradient(90deg, #f59e0b, #fbbf24)'
- : 'linear-gradient(90deg, #10b981, #34d399)'
- }}
- />
-
-
= 85 ? 'dg-text-red' : val >= 60 ? 'dg-text-yellow' : 'dg-text-green'
- }`}>{val.toFixed(1)}%
-
- );
- })}
-
-
-
- {/* 内存排行 */}
-
-
-
-
- {t('monitor:hostDashboard.memTop5')}
- {t('monitor:hostDashboard.threshold')}
-
-
-
- {[...hosts]
- .filter((h: JsonData) => h.agent_status === 'online')
- .sort((a: JsonData, b: JsonData) => (b.memory_usage || 0) - (a.memory_usage || 0))
- .slice(0, 5)
- .map((h: JsonData) => {
- const val = h.memory_usage || 0;
- return (
-
-
{h.hostname}
-
-
= 85
- ? 'linear-gradient(90deg, #ef4444, #f87171)'
- : val >= 60
- ? 'linear-gradient(90deg, #f59e0b, #fbbf24)'
- : 'linear-gradient(90deg, #10b981, #34d399)'
- }}
- />
-
-
= 85 ? 'dg-text-red' : val >= 60 ? 'dg-text-yellow' : 'dg-text-green'
- }`}>{val.toFixed(1)}%
-
- );
- })}
-
-
-
- )}
-
- {/* 表格卡片 */}
-
-
-
-
-
-
{t('monitor:hostDashboard.listTitle')}
-
{t('monitor:hostDashboard.realtime')}
-
-
-
-
- setSearch(e.target.value)}
- onKeyDown={(e) => e.key === 'Enter' && handleSearch()}
- />
-
-
-
-
-
-
- {loading ? (
-
-
-
{t('common:loading')}
-
- ) : sorted.length === 0 ? (
-
-
-
{t('monitor:hostDashboard.noData')}
-
{t('monitor:hostDashboard.noDataDesc')}
-
- ) : (
- <>
-
-
-
-
- {[
- { key: 'hostname', label: t('monitor:hostDashboard.colHostId'), w: '' },
- { key: 'agent_version', label: t('monitor:hostDashboard.colAgentVersion'), w: 'w-[100px]' },
- { key: 'agent_status', label: t('monitor:hostDashboard.colAgentStatus'), w: 'w-[90px]' },
- { key: 'cpu_usage', label: 'CPU', w: 'w-[130px]' },
- { key: 'memory_usage', label: t('monitor:hostMetric.memory'), w: 'w-[130px]' },
- { key: 'disk_usage', label: t('monitor:hostMetric.disk'), w: 'w-[130px]' },
- { key: 'region', label: t('monitor:hostDashboard.colRegion'), w: 'w-[120px]' },
- { key: 'ip', label: t('monitor:hostDashboard.colIp'), w: 'w-[140px]' },
- ].map(col => (
- | handleSort(col.key as SortField)}
- >
-
- {col.label}
-
-
- |
- ))}
- {t('common:actions')} |
-
-
-
- {sorted.map((h: JsonData) => (
-
- {/* 主机名/ID */}
-
-
-
-
- {h.hostname}
- ID: {h.id}
-
-
- |
- {/* Agent 版本 */}
-
- {h.agent_version || '--'}
- |
- {/* Agent 状态 */}
-
- {statusBadge(h.agent_status)}
- |
- {/* CPU 使用率 */}
-
-
-
- {getUsageBar(h.cpu_usage)}
-
- |
- {/* 内存使用率 */}
-
-
-
- {getUsageBar(h.memory_usage)}
-
- |
- {/* 磁盘使用率 */}
-
-
-
- {getUsageBar(h.disk_usage)}
-
- |
- {/* 所在区域 */}
-
-
- {h.region?.name || h.region?.Name || (h.region ? h.region : '--')}
-
- |
- {/* IP 地址 */}
-
- {h.ip || '--'}
- |
- {/* 操作栏 */}
-
-
-
-
-
- |
-
- ))}
-
-
-
-
- {/* 分页 */}
-
-
- {t('monitor:hostDashboard.perPage')}
-
- {t('monitor:hostDashboard.totalCount', { count: total })}
-
-
-
-
- {page} / {Math.max(totalPages, 1)}
-
-
-
-
- >
- )}
-
-
-
- );
-}