P1: kubernetes/system API 拆为子目录模块,导出工具下沉到 lib/export-utils,
http 统一 buildApiUrl/getWsBase;HostMonitor、DeploymentEdit* 拆至子组件。
P2: 新增 hooks/api(queryKeys、useTerminalSessions、useHostMonitor),
TerminalSessionsTab 与 HostMonitor 接入 TanStack Query。
P3: 保留 services/api 路径,未迁移至 api/ 目录。
86 lines
4.7 KiB
TypeScript
86 lines
4.7 KiB
TypeScript
// 系统信息 Tab 内容 // Logan.Li
|
||
import { Activity, HardDrive } from 'lucide-react';
|
||
import { Card, CardContent } from '@/components/ui/card';
|
||
import { TabsContent } from '@/components/ui/tabs';
|
||
import { parseJSON } from './utils';
|
||
|
||
interface SystemInfoSectionProps {
|
||
selectedHost: string;
|
||
serverInfo: any;
|
||
latest: any;
|
||
heartbeatInfo: any;
|
||
portInfoCount: number;
|
||
}
|
||
|
||
export function SystemInfoSection({
|
||
selectedHost, serverInfo, latest, heartbeatInfo, portInfoCount,
|
||
}: SystemInfoSectionProps) {
|
||
const disk = latest?.disk_info ? parseJSON(latest.disk_info) : null;
|
||
const parts = disk ? (Array.isArray(disk) ? disk : (disk.partitions || [])) : [];
|
||
|
||
return (
|
||
<TabsContent value="system" className="mt-3 space-y-3">
|
||
<Card className="bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]">
|
||
<CardContent className="p-4">
|
||
<div className="grid grid-cols-2 md:grid-cols-4 gap-x-6 gap-y-3 text-xs">
|
||
{[
|
||
['主机名', serverInfo?.hostname || selectedHost],
|
||
['IP 地址', serverInfo?.ip || '—'],
|
||
['操作系统', serverInfo?.os || '—'],
|
||
['内核版本', serverInfo?.kernel || '—'],
|
||
['CPU 核心数', serverInfo?.cpu_cores || '—'],
|
||
['总内存', serverInfo?.mem_total ? `${serverInfo.mem_total.toFixed(0)} GB` : '—'],
|
||
['已用内存', serverInfo?.mem_used ? `${serverInfo.mem_used.toFixed(1)} GB` : '—'],
|
||
['运行时长', serverInfo?.uptime ? `${Math.floor(serverInfo.uptime / 86400)}天 ${Math.floor((serverInfo.uptime % 86400) / 3600)}时` : '—'],
|
||
['Agent 版本', heartbeatInfo?.version || serverInfo?.agent_version || '—'],
|
||
['Agent 序列号', heartbeatInfo?.report_seq != null ? String(heartbeatInfo.report_seq) : '—'],
|
||
['监听端口数', portInfoCount > 0 ? `${portInfoCount}` : '—'],
|
||
['Agent RSS', heartbeatInfo?.self_rss_mb ? `${heartbeatInfo.self_rss_mb.toFixed(1)} MB` : '—'],
|
||
].map(([label, val]) => (
|
||
<div key={label as string}>
|
||
<span className="text-[var(--dg-text-muted)]">{label}:</span>
|
||
<span className="text-[var(--dg-text-primary)] font-mono">{val}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
{latest && (
|
||
<Card className="bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]">
|
||
<CardContent className="p-4">
|
||
<div className="flex items-center gap-2 mb-2">
|
||
<Activity className="h-4 w-4 text-[var(--dg-text-muted)]" />
|
||
<h3 className="text-xs font-medium text-[var(--dg-text-primary)]">系统负载 (Load Average)</h3>
|
||
</div>
|
||
<div className="flex items-center gap-6 text-xs">
|
||
<div><span className="text-[var(--dg-text-muted)]">1min </span><span className="text-sm font-bold text-slate-200">{latest.load1 ?? '--'}</span></div>
|
||
<div><span className="text-[var(--dg-text-muted)]">5min </span><span className="text-sm font-bold text-slate-200">{latest.load5 ?? '--'}</span></div>
|
||
<div><span className="text-[var(--dg-text-muted)]">15min </span><span className="text-sm font-bold text-slate-200">{latest.load15 ?? '--'}</span></div>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
)}
|
||
|
||
{parts.length > 0 && (
|
||
<Card className="bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]">
|
||
<CardContent className="p-4">
|
||
<h3 className="text-xs font-medium text-[var(--dg-text-primary)] mb-2 flex items-center gap-2"><HardDrive className="h-4 w-4" />磁盘分区</h3>
|
||
<div className="space-y-1.5">
|
||
{parts.map((p: any, i: number) => (
|
||
<div key={i} className="flex items-center gap-2 text-xs">
|
||
<span className="text-[var(--dg-text-muted)] w-24 font-mono truncate">{p.mount || p.mountpoint || p.device || `disk${i}`}</span>
|
||
<div className="flex-1 h-1.5 bg-slate-700 rounded-full overflow-hidden">
|
||
<div className="h-full rounded-full bg-blue-500" style={{ width: `${Math.min(p.usage_percent || p.used_percent || 0, 100)}%` }} />
|
||
</div>
|
||
<span className="text-[var(--dg-text-primary)] w-28 text-right tabular-nums whitespace-nowrap">{(p.used_gb ?? 0).toFixed(1)}/{(p.total_gb ?? 0).toFixed(0)}GB</span>
|
||
<span className="text-blue-400 w-14 text-right tabular-nums whitespace-nowrap">{(p.usage_percent ?? p.used_percent ?? 0).toFixed(1)}%</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
)}
|
||
</TabsContent>
|
||
);
|
||
} |