// deployment-view/PodsTab.tsx — 容器组 Tab // Logan.Li // v2: 虚拟滚动,支持 1000+ Pod 场景流畅渲染 import { useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { useVirtualizer } from '@tanstack/react-virtual'; import { Card, CardContent } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { ChevronDown, ChevronRight, Terminal, HardDrive, MoreVertical, FileText, FileCode, Trash2, } from 'lucide-react'; import { statusColor } from '../k8s-yaml-utils'; import { PodConditionsRow } from './helpers'; import type { DeploymentViewProps } from './types'; const GRID_COLS = '28px minmax(120px,3fr) 76px minmax(80px,1fr) minmax(70px,1fr) 56px 56px 88px'; const MIN_WIDTH = '700px'; const ROW_HEIGHT = 56; export function PodsTab({ clusterCode, pods, onShowPodLogs, onShowPodTerminal, onShowPodYaml, onShowPodFileBrowser, onDeletePod, expandedPod, setExpandedPod, }: DeploymentViewProps & { expandedPod: string | null; setExpandedPod: (v: string | null) => void }) { const { t } = useTranslation(); const scrollRef = useRef(null); // eslint-disable-next-line react-hooks/incompatible-library const virtualizer = useVirtualizer({ count: pods.length, getScrollElement: () => scrollRef.current, estimateSize: () => ROW_HEIGHT, overscan: 8, }); if (pods.length === 0) { return
{t('k8s.pods.empty')}
; } const virtualRows = virtualizer.getVirtualItems(); const totalHeight = virtualizer.getTotalSize(); return ( {/* 表头 */}
{t('k8s.pods.col.name')} {t('k8s.pods.col.status')} {t('k8s.pods.col.node')} IP {t('k8s.pods.col.restart')} Age {t('k8s.pods.col.actions')}
{/* 虚拟滚动列表 */}
{virtualRows.map((vr) => { const pod = pods[vr.index]; if (!pod) return null; const expanded = expandedPod === pod.name; const nodeName = pod.node_name || pod.node; const sc = statusColor(pod.status); const containers = (pod as unknown as Record).containers as Record[] | undefined; return (
{/* 列表行 */}
{pod.name} {pod.status || 'Unknown'} {pod.ip || pod.pod_ip || '—'} 0 ? 'dg-text-yellow' : 'text-[var(--dg-text-muted)]'}`}> {pod.restart_count || 0} {pod.age || '—'}
onShowPodLogs(pod)} className="text-[var(--dg-text-primary)]"> {t('k8s.pods.action.logs')} onShowPodYaml(pod)} className="text-[var(--dg-text-primary)]"> YAML onDeletePod?.(pod)} className="dg-text-red focus:dg-text-red"> {t('k8s.pods.action.delete')}
{/* 展开详情 */} {expanded && (
{containers && containers.length > 0 && (

{t('k8s.pods.containers')}

{containers.map((c, idx) => (
{String(c.name ?? '')} {c.ready ? 'Ready' : 'Pending'}

{String(c.image ?? '')}

{c.resources && (
CPU {String((c.resources as Record>)?.requests?.cpu ?? '0')} / {String((c.resources as Record>)?.limits?.cpu ?? '—')} {t('k8s.pods.memory')} {String((c.resources as Record>)?.requests?.memory ?? '0')} / {String((c.resources as Record>)?.limits?.memory ?? '—')}
)}
))}
)}
)}
); })}
{/* 行数统计 */} {pods.length > 0 && (
{t('k8s.pods.totalCount', { count: pods.length })} {pods.length > 200 && `· ${t('k8s.pods.virtualScroll')}`}
)}
); }