import { useState, useEffect, useCallback } from 'react'; import { useParams, useNavigate, Link } from 'react-router-dom'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { RefreshCw, Loader2, Cpu, Server, Container, ChevronRight, Cloud, Boxes, Shield, Activity, } from 'lucide-react'; import { kubernetesApi } from '@/api'; import { useToast } from '@/hooks/use-toast'; import { PageShell } from '@/components/layout/PageShell'; import { ContentPanel } from '@/components/layout/ContentPanel'; import { StatStrip } from '@/components/ui/stat-strip'; import type { JsonData } from '@/types/common'; import i18n from '@/i18n'; const NAMESPACE_ICON: Record>> = { 'kube-system': Server, 'kube-public': Shield, 'kube-node-lease': Activity, 'default': Cloud, }; export default function ClusterOverviewPage() { const { clusterCode } = useParams<{ clusterCode: string }>(); const navigate = useNavigate(); const { toast } = useToast(); const [, setClusters] = useState([]); const [nodes, setNodes] = useState([]); const [namespaces, setNamespaces] = useState([]); const [clusterInfo, setClusterInfo] = useState(null); const [loading, setLoading] = useState(false); // Load cluster list + current cluster info const loadClusterInfo = useCallback(async () => { try { const res: JsonData = await kubernetesApi.getClusters(); const list = res?.list || []; setClusters(list); const current = list.find((c: JsonData) => (c.code || String(c.id)) === clusterCode); setClusterInfo(current || null); if (!current && list.length > 0 && clusterCode) { toast({ title: i18n.t('kubernetes.clusteroverview.s9ff765c1'), description: `找不到集群 ${clusterCode}`, variant: 'destructive' }); } } catch { setClusters([]); } }, [clusterCode]); const loadData = useCallback(async () => { if (!clusterCode) return; setLoading(true); try { const [nodesRes, nsRes] = await Promise.all([ kubernetesApi.getNodes(clusterCode).catch(() => []), kubernetesApi.getNamespaces(clusterCode).catch(() => []), ]); setNodes(Array.isArray(nodesRes) ? nodesRes : (nodesRes?.list || nodesRes?.data || [])); const nsList: string[] = Array.isArray(nsRes) ? nsRes : (nsRes?.list || nsRes?.data || []); setNamespaces(nsList); } catch { setNodes([]); setNamespaces([]); } finally { setLoading(false); } }, [clusterCode]); useEffect(() => { loadClusterInfo(); }, [loadClusterInfo]); useEffect(() => { loadData(); }, [loadData]); const getNodeRole = (node: JsonData) => { if (node?.role === 'master' || node?.role === 'control-plane') return 'bg-[var(--dg-blue)]/20 text-[var(--dg-blue)] border-[var(--dg-blue)]/30'; return 'bg-[var(--dg-text-muted)]/20 text-[var(--dg-text-secondary)] border-[var(--dg-text-muted)]/30'; }; return ( } metrics={!loading ? ( ) : undefined} actions={( )} > {/* 节点列表 // Logan.Li */}

节点列表

{i18n.t('kubernetes.secret.secretName')} {i18n.t('system.userRole')} {i18n.t('system.terminalStatus')} IP OS Kubelet CPU {i18n.t('kubernetes.pod.memory')} {loading ? ( {i18n.t('monitor.logPanel.logResults.loading')} ) : nodes.length === 0 ? ( {i18n.t('kubernetes.topology.noNodes')} ) : ( nodes.map((n: JsonData) => { const cpuCores = n.cpu || 0; const cpuUsageM = n.cpu_usage_m ? parseFloat(n.cpu_usage_m.replace('m','')) : 0; const cpuPct = cpuCores > 0 ? (cpuUsageM / 1000 / cpuCores) * 100 : 0; const memMb = n.memory_mb || 0; const memUsageMi = n.mem_usage_mi ? parseFloat(n.mem_usage_mi.replace('Mi','').replace('Ki','').replace('Gi','')) : 0; // Normalize to MiB const memUsageNorm = n.mem_usage_mi?.includes('Ki') ? memUsageMi / 1024 : n.mem_usage_mi?.includes('Gi') ? memUsageMi * 1024 : memUsageMi; const memPct = memMb > 0 ? (memUsageNorm / memMb) * 100 : 0; const color = (pct: number) => pct > 80 ? 'bg-[var(--dg-red)]' : pct > 60 ? 'bg-[var(--dg-orange)]' : 'bg-[var(--dg-green)]'; return ( navigate(`/kubernetes/${clusterCode}/nodes/${n.name}`)}> {n.name} {n.role || 'worker'} {n.status || 'Unknown'} {n.ip || '-'} {n.os || '-'} {n.kubelet_version || '-'} {n.cpu_usage_m ? (
{cpuPct.toFixed(0)}%
) : ( {cpuCores} 核 )} {n.mem_usage_mi ? (
{memPct.toFixed(0)}%
) : ( {memMb}Mi )} ); }) )}
{/* Namespace list */}

命名空间

管理全部
{namespaces.map((ns) => { const Icon = NAMESPACE_ICON[ns] || Container; return (
{ns}
); })}
); }