chore: sync local updates
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import type { ServerMetrics } from '../../types';
|
||||
import { formatBytes, formatUptime, formatPercent } from '../../utils/format';
|
||||
import { CircularProgress } from '../Common/CircularProgress';
|
||||
import { formatBytes, formatUptime, formatPercent, formatLatencyMs } from '../../utils/format';
|
||||
import './ServerDetail.css';
|
||||
|
||||
interface ServerDetailProps {
|
||||
@@ -20,12 +19,12 @@ export const ServerDetail = ({ metrics, serverName, onClose }: ServerDetailProps
|
||||
|
||||
<div className="modal-body">
|
||||
<section className="detail-section">
|
||||
<h3>系统信息</h3>
|
||||
<h3 className="detail-category-heading">系统与环境</h3>
|
||||
<div className="system-info-container">
|
||||
{/* 系统基本信息 */}
|
||||
<div className="system-info-group">
|
||||
<h4 className="info-group-title">基本信息</h4>
|
||||
<div className="system-info-grid">
|
||||
<div className="system-info-grid system-info-grid--cols-3">
|
||||
<div className="system-info-item">
|
||||
<div className="system-info-content">
|
||||
<span className="system-info-label">主机名</span>
|
||||
@@ -59,7 +58,7 @@ export const ServerDetail = ({ metrics, serverName, onClose }: ServerDetailProps
|
||||
{/* 运行时信息 */}
|
||||
<div className="system-info-group">
|
||||
<h4 className="info-group-title">运行时信息</h4>
|
||||
<div className="system-info-grid">
|
||||
<div className="system-info-grid system-info-grid--cols-3">
|
||||
<div className="system-info-item system-info-item-highlight">
|
||||
<div className="system-info-content">
|
||||
<span className="system-info-label">运行时间</span>
|
||||
@@ -72,6 +71,22 @@ export const ServerDetail = ({ metrics, serverName, onClose }: ServerDetailProps
|
||||
<span className="system-info-value">{metrics.system.processCount}</span>
|
||||
</div>
|
||||
</div>
|
||||
{metrics.system.loggedInUsers !== undefined && (
|
||||
<div className="system-info-item">
|
||||
<div className="system-info-content">
|
||||
<span className="system-info-label">登录会话</span>
|
||||
<span className="system-info-value">{metrics.system.loggedInUsers}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{metrics.system.tcpConnections !== undefined && (
|
||||
<div className="system-info-item">
|
||||
<div className="system-info-content">
|
||||
<span className="system-info-label">TCP 连接数</span>
|
||||
<span className="system-info-value">{metrics.system.tcpConnections}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{metrics.system.packageCount > 0 && (
|
||||
<div className="system-info-item">
|
||||
<div className="system-info-content">
|
||||
@@ -86,7 +101,7 @@ export const ServerDetail = ({ metrics, serverName, onClose }: ServerDetailProps
|
||||
{/* 性能指标 */}
|
||||
<div className="system-info-group">
|
||||
<h4 className="info-group-title">性能指标</h4>
|
||||
<div className="system-info-grid">
|
||||
<div className="system-info-grid system-info-grid--cols-3">
|
||||
<div className="system-info-item">
|
||||
<div className="system-info-content">
|
||||
<span className="system-info-label">磁盘读取</span>
|
||||
@@ -129,34 +144,48 @@ export const ServerDetail = ({ metrics, serverName, onClose }: ServerDetailProps
|
||||
{/* 延迟信息 */}
|
||||
{metrics.latency && (
|
||||
<div className="system-info-group">
|
||||
<h4 className="info-group-title">延迟检测</h4>
|
||||
<div className="system-info-grid">
|
||||
<h4 className="info-group-title">网络延迟(采集端测量)</h4>
|
||||
<p className="latency-section-hint">
|
||||
下列指标均在采集器所在机器上探测:第一项为到监控中心 gRPC 地址的 TCP
|
||||
建连时间;外网三项为采集端对公网站点的 TCP(:80 / :443)建连耗时,非 ICMP ping。
|
||||
</p>
|
||||
<div className="system-info-grid system-info-grid--cols-4">
|
||||
<div className="system-info-item system-info-item-highlight">
|
||||
<div className="system-info-content">
|
||||
<span className="system-info-label">客户端到服务器</span>
|
||||
<div className="system-info-content system-info-content--stack">
|
||||
<span className="system-info-label">采集器到监控中心</span>
|
||||
<span className="system-info-sublabel">与 CENTRAL_GRPC 的 TCP 连通</span>
|
||||
<span className="system-info-value highlight">
|
||||
{metrics.latency.clientToServer >= 0 ? `${metrics.latency.clientToServer} ms` : '超时'}
|
||||
{formatLatencyMs(metrics.latency.clientToServer)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{metrics.latency.external && (
|
||||
<>
|
||||
<div className="system-info-item">
|
||||
<div className="system-info-content">
|
||||
<div className="system-info-content system-info-content--stack">
|
||||
<span className="system-info-label">百度 (baidu.com)</span>
|
||||
<span className="system-info-value">{metrics.latency.external['baidu.com'] || '超时'}</span>
|
||||
<span className="system-info-sublabel">采集端 TCP 探测</span>
|
||||
<span className="system-info-value">
|
||||
{metrics.latency.external['baidu.com'] || '超时'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="system-info-item">
|
||||
<div className="system-info-content">
|
||||
<div className="system-info-content system-info-content--stack">
|
||||
<span className="system-info-label">谷歌 (google.com)</span>
|
||||
<span className="system-info-value">{metrics.latency.external['google.com'] || '超时'}</span>
|
||||
<span className="system-info-sublabel">采集端 TCP 探测</span>
|
||||
<span className="system-info-value">
|
||||
{metrics.latency.external['google.com'] || '超时'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="system-info-item">
|
||||
<div className="system-info-content">
|
||||
<div className="system-info-content system-info-content--stack">
|
||||
<span className="system-info-label">GitHub (github.com)</span>
|
||||
<span className="system-info-value">{metrics.latency.external['github.com'] || '超时'}</span>
|
||||
<span className="system-info-sublabel">采集端 TCP 探测</span>
|
||||
<span className="system-info-value">
|
||||
{metrics.latency.external['github.com'] || '超时'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
@@ -168,152 +197,152 @@ export const ServerDetail = ({ metrics, serverName, onClose }: ServerDetailProps
|
||||
</section>
|
||||
|
||||
<section className="detail-section">
|
||||
<h3>CPU</h3>
|
||||
<div className="cpu-section-content">
|
||||
<div className="cpu-circular-group">
|
||||
<div className="cpu-circular">
|
||||
<CircularProgress
|
||||
value={metrics.cpu.usagePercent}
|
||||
label=""
|
||||
size={140}
|
||||
color={metrics.cpu.usagePercent > 80 ? '#f87171' : '#86efac'}
|
||||
subLabel={formatPercent(metrics.cpu.usagePercent)}
|
||||
/>
|
||||
</div>
|
||||
{metrics.cpu.loadAverages && metrics.cpu.loadAverages.length > 0 && metrics.cpu.cores > 0 && (
|
||||
<div className="cpu-load-circular">
|
||||
<CircularProgress
|
||||
value={Math.min((metrics.cpu.loadAverages[0] / metrics.cpu.cores) * 100, 100)}
|
||||
label="负载"
|
||||
size={140}
|
||||
color={metrics.cpu.loadAverages[0] / metrics.cpu.cores > 0.8 ? '#fde047' : '#a7f3d0'}
|
||||
subLabel={metrics.cpu.loadAverages[0].toFixed(2)}
|
||||
/>
|
||||
<h3 className="detail-category-heading">处理器与内存</h3>
|
||||
<div className="detail-subsection">
|
||||
<h4 className="detail-subheading">处理器</h4>
|
||||
<div className="detail-plain">
|
||||
<div className="detail-grid detail-grid--cpu">
|
||||
<div className="detail-item detail-item--span-full">
|
||||
<span className="item-label">型号</span>
|
||||
<span className="item-value">{metrics.cpu.model}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="detail-grid">
|
||||
<div className="detail-item">
|
||||
<span className="item-label">型号</span>
|
||||
<span className="item-value">{metrics.cpu.model}</span>
|
||||
</div>
|
||||
<div className="detail-item">
|
||||
<span className="item-label">核心数</span>
|
||||
<span className="item-value">{metrics.cpu.cores}</span>
|
||||
</div>
|
||||
<div className="detail-item">
|
||||
<span className="item-label">使用率</span>
|
||||
<span className="item-value highlight">{formatPercent(metrics.cpu.usagePercent)}</span>
|
||||
</div>
|
||||
{metrics.cpu.temperature && metrics.cpu.temperature > 0 && (
|
||||
<div className="detail-item">
|
||||
<span className="item-label">温度</span>
|
||||
<span className="item-value">{metrics.cpu.temperature.toFixed(1)}°C</span>
|
||||
<span className="item-label">核心数</span>
|
||||
<span className="item-value">{metrics.cpu.cores}</span>
|
||||
</div>
|
||||
)}
|
||||
{metrics.cpu.loadAverages && metrics.cpu.loadAverages.length > 0 && (
|
||||
<div className="detail-item">
|
||||
<span className="item-label">负载平均值</span>
|
||||
<span className="item-value">{metrics.cpu.loadAverages.join(' / ')}</span>
|
||||
<span className="item-label">使用率</span>
|
||||
<span className="item-value emphasize">{formatPercent(metrics.cpu.usagePercent)}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{metrics.cpu.perCoreUsage && metrics.cpu.perCoreUsage.length > 0 && (
|
||||
<div className="per-core-section">
|
||||
<h4>各核心使用率</h4>
|
||||
<div className="core-grid-circular">
|
||||
{metrics.cpu.perCoreUsage.map((core) => (
|
||||
<div key={core.core} className="core-item-circular">
|
||||
<CircularProgress
|
||||
value={core.percent}
|
||||
label={`Core ${core.core}`}
|
||||
size={140}
|
||||
color={core.percent > 80 ? '#f87171' : '#86efac'}
|
||||
subLabel={formatPercent(core.percent)}
|
||||
/>
|
||||
{metrics.cpu.temperature && metrics.cpu.temperature > 0 && (
|
||||
<div className="detail-item">
|
||||
<span className="item-label">温度</span>
|
||||
<span className="item-value">{metrics.cpu.temperature.toFixed(1)}°C</span>
|
||||
</div>
|
||||
))}
|
||||
)}
|
||||
{metrics.cpu.loadAverages && metrics.cpu.loadAverages.length > 0 && (
|
||||
<>
|
||||
<div className="detail-item">
|
||||
<span className="item-label">负载 (1 / 5 / 15 min)</span>
|
||||
<span className="item-value">{metrics.cpu.loadAverages.map((l) => l.toFixed(2)).join(' / ')}</span>
|
||||
</div>
|
||||
{metrics.cpu.cores > 0 && (
|
||||
<div className="detail-item">
|
||||
<span className="item-label">相对核心(1 min 负载)</span>
|
||||
<span className="item-value">
|
||||
{formatPercent(Math.min((metrics.cpu.loadAverages[0] / metrics.cpu.cores) * 100, 100))}{' '}
|
||||
<span className="item-sub">
|
||||
({metrics.cpu.loadAverages[0].toFixed(2)} / {metrics.cpu.cores} 核)
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
{metrics.cpu.perCoreUsage && metrics.cpu.perCoreUsage.length > 0 && (
|
||||
<div className="per-core-plain">
|
||||
<h4 className="detail-subheading detail-subheading--inline">各核心使用率</h4>
|
||||
<div className="per-core-grid">
|
||||
{metrics.cpu.perCoreUsage.map((core) => (
|
||||
<div key={core.core} className="per-core-chip">
|
||||
<span className="per-core-name">核心 {core.core}</span>
|
||||
<span className="per-core-pct">{formatPercent(core.percent)}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<section className="detail-section">
|
||||
<h3>内存</h3>
|
||||
<div className="memory-section-content">
|
||||
<div className="memory-circular">
|
||||
<CircularProgress
|
||||
value={metrics.memory.usedPercent}
|
||||
label=""
|
||||
size={140}
|
||||
color={metrics.memory.usedPercent > 80 ? '#fde047' : '#6ee7b7'}
|
||||
subLabel={formatPercent(metrics.memory.usedPercent)}
|
||||
/>
|
||||
</div>
|
||||
<div className="detail-grid">
|
||||
<div className="detail-item">
|
||||
<span className="item-label">总容量</span>
|
||||
<span className="item-value">{formatBytes(metrics.memory.totalBytes)}</span>
|
||||
</div>
|
||||
<div className="detail-item">
|
||||
<span className="item-label">已使用</span>
|
||||
<span className="item-value">{formatBytes(metrics.memory.usedBytes)}</span>
|
||||
</div>
|
||||
<div className="detail-item">
|
||||
<span className="item-label">可用</span>
|
||||
<span className="item-value">{formatBytes(metrics.memory.freeBytes)}</span>
|
||||
</div>
|
||||
<div className="detail-item">
|
||||
<span className="item-label">使用率</span>
|
||||
<span className="item-value highlight">{formatPercent(metrics.memory.usedPercent)}</span>
|
||||
<div className="detail-subsection">
|
||||
<h4 className="detail-subheading">内存</h4>
|
||||
<div className="detail-plain">
|
||||
<div className="detail-grid">
|
||||
<div className="detail-item">
|
||||
<span className="item-label">使用率</span>
|
||||
<span className="item-value emphasize">{formatPercent(metrics.memory.usedPercent)}</span>
|
||||
</div>
|
||||
<div className="detail-item">
|
||||
<span className="item-label">总容量</span>
|
||||
<span className="item-value">{formatBytes(metrics.memory.totalBytes)}</span>
|
||||
</div>
|
||||
<div className="detail-item">
|
||||
<span className="item-label">已使用</span>
|
||||
<span className="item-value">{formatBytes(metrics.memory.usedBytes)}</span>
|
||||
</div>
|
||||
<div className="detail-item">
|
||||
<span className="item-label">可用</span>
|
||||
<span className="item-value">{formatBytes(metrics.memory.freeBytes)}</span>
|
||||
</div>
|
||||
{(metrics.memory.swapTotalBytes !== undefined ||
|
||||
metrics.memory.swapType !== undefined) && (
|
||||
<>
|
||||
<div className="detail-item">
|
||||
<span className="item-label">Swap 总量</span>
|
||||
<span className="item-value">
|
||||
{formatBytes(metrics.memory.swapTotalBytes ?? 0)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="detail-item">
|
||||
<span className="item-label">Swap 已用</span>
|
||||
<span className="item-value">
|
||||
{formatBytes(metrics.memory.swapUsedBytes ?? 0)}
|
||||
{metrics.memory.swapUsedPercent !== undefined && (
|
||||
<span className="item-sub"> ({formatPercent(metrics.memory.swapUsedPercent)})</span>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="detail-item">
|
||||
<span className="item-label">Swap 可用</span>
|
||||
<span className="item-value">
|
||||
{formatBytes(metrics.memory.swapFreeBytes ?? 0)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="detail-item">
|
||||
<span className="item-label">Swap 类型</span>
|
||||
<span className="item-value">{metrics.memory.swapType ?? '—'}</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="detail-section">
|
||||
<h3>存储</h3>
|
||||
<div className="storage-grid-circular">
|
||||
<h3 className="detail-category-heading">存储与磁盘</h3>
|
||||
<ul className="storage-plain-list">
|
||||
{metrics.storage.map((disk, index) => (
|
||||
<div key={index} className="storage-item-circular">
|
||||
<CircularProgress
|
||||
value={disk.usedPercent}
|
||||
label={disk.mount}
|
||||
size={160}
|
||||
color={disk.usedPercent > 90 ? '#f87171' : '#a7f3d0'}
|
||||
subLabel={formatPercent(disk.usedPercent)}
|
||||
/>
|
||||
<div className="storage-details-mini">
|
||||
<div>总: {formatBytes(disk.totalBytes)}</div>
|
||||
<div>用: {formatBytes(disk.usedBytes)}</div>
|
||||
</div>
|
||||
</div>
|
||||
<li key={index} className="storage-plain-row">
|
||||
<div className="storage-plain-head">
|
||||
<span className="storage-mount">{disk.mount}</span>
|
||||
<span className="storage-pct">{formatPercent(disk.usedPercent)}</span>
|
||||
</div>
|
||||
<div className="storage-meta">
|
||||
已用 {formatBytes(disk.usedBytes)} / 共 {formatBytes(disk.totalBytes)} · 可用{' '}
|
||||
{formatBytes(disk.freeBytes)}
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</div>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
{metrics.gpu && metrics.gpu.length > 0 && metrics.gpu[0].status !== 'not_available' && (
|
||||
<section className="detail-section">
|
||||
<h3>GPU</h3>
|
||||
<h3 className="detail-category-heading">图形设备</h3>
|
||||
{metrics.gpu.map((gpu, index) => (
|
||||
<div key={index} className="gpu-item">
|
||||
<div className="gpu-header">
|
||||
<strong>{gpu.name}</strong>
|
||||
<span className={`gpu-status ${gpu.status}`}>{gpu.status}</span>
|
||||
</div>
|
||||
<div className="gpu-bar-container">
|
||||
<div className="gpu-bar-label">利用率</div>
|
||||
<div className="gpu-bar-bg">
|
||||
<div
|
||||
className="gpu-bar"
|
||||
style={{ width: `${gpu.utilizationPercent}%` }}
|
||||
/>
|
||||
</div>
|
||||
<div className="gpu-bar-value">{formatPercent(gpu.utilizationPercent)}</div>
|
||||
</div>
|
||||
<div className="detail-grid">
|
||||
<div className="detail-item">
|
||||
<span className="item-label">利用率</span>
|
||||
<span className="item-value emphasize">{formatPercent(gpu.utilizationPercent)}</span>
|
||||
</div>
|
||||
<div className="detail-item">
|
||||
<span className="item-label">显存总量</span>
|
||||
<span className="item-value">{gpu.memoryTotalMB} MB</span>
|
||||
@@ -336,167 +365,150 @@ export const ServerDetail = ({ metrics, serverName, onClose }: ServerDetailProps
|
||||
|
||||
{metrics.network && metrics.network.length > 0 && (
|
||||
<section className="detail-section">
|
||||
<h3>网络接口</h3>
|
||||
<div className="network-list">
|
||||
<h3 className="detail-category-heading">网络</h3>
|
||||
<div className="network-compact" role="table" aria-label="网络接口">
|
||||
<div className="network-compact__head" role="row">
|
||||
<span role="columnheader">接口</span>
|
||||
<span role="columnheader">IP</span>
|
||||
<span role="columnheader">MAC</span>
|
||||
<span role="columnheader">接收</span>
|
||||
<span role="columnheader">发送</span>
|
||||
</div>
|
||||
{metrics.network.map((iface, index) => (
|
||||
<div key={index} className="network-item">
|
||||
<div className="network-header">
|
||||
<div className="network-name-group">
|
||||
<div className="network-name-info">
|
||||
<strong className="network-name">{iface.name}</strong>
|
||||
{iface.ipAddress && (
|
||||
<span className="network-ip has-ip">
|
||||
{iface.ipAddress}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="network-details">
|
||||
{(iface.macAddress || iface.ipAddress) && (
|
||||
<div className="network-detail-row">
|
||||
{iface.macAddress && (
|
||||
<div className="network-detail-item">
|
||||
<span className="network-detail-label">MAC 地址</span>
|
||||
<span className="network-detail-value">{iface.macAddress}</span>
|
||||
</div>
|
||||
)}
|
||||
{iface.ipAddress && (
|
||||
<div className="network-detail-item">
|
||||
<span className="network-detail-label">IP 地址</span>
|
||||
<span className="network-detail-value">{iface.ipAddress}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="network-traffic">
|
||||
<div className="traffic-item">
|
||||
<div className="traffic-header">
|
||||
<span className="traffic-label">接收流量</span>
|
||||
<span className="traffic-value">{formatBytes(iface.rxBytes)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="traffic-item">
|
||||
<div className="traffic-header">
|
||||
<span className="traffic-label">发送流量</span>
|
||||
<span className="traffic-value">{formatBytes(iface.txBytes)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div key={index} className="network-compact__row" role="row">
|
||||
<strong className="network-compact__iface" role="cell">
|
||||
{iface.name}
|
||||
</strong>
|
||||
<span
|
||||
className={
|
||||
iface.ipAddress && iface.ipAddress !== 'N/A'
|
||||
? 'network-compact__ip network-compact__ip--ok'
|
||||
: 'network-compact__placeholder'
|
||||
}
|
||||
role="cell"
|
||||
>
|
||||
{iface.ipAddress && iface.ipAddress !== 'N/A' ? iface.ipAddress : '—'}
|
||||
</span>
|
||||
<span
|
||||
className="network-compact__mac"
|
||||
role="cell"
|
||||
title={iface.macAddress || undefined}
|
||||
>
|
||||
{iface.macAddress || '—'}
|
||||
</span>
|
||||
<span className="network-compact__num" role="cell" title="接收流量">
|
||||
{formatBytes(iface.rxBytes)}
|
||||
</span>
|
||||
<span className="network-compact__num" role="cell" title="发送流量">
|
||||
{formatBytes(iface.txBytes)}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{metrics.system.topProcesses && metrics.system.topProcesses.length > 0 && (
|
||||
{((metrics.system.topProcesses && metrics.system.topProcesses.length > 0) ||
|
||||
(metrics.system.dockerStats && metrics.system.dockerStats.available)) && (
|
||||
<section className="detail-section">
|
||||
<h3>Top 5 进程</h3>
|
||||
<div className="process-list">
|
||||
{metrics.system.topProcesses.map((proc, index) => (
|
||||
<div key={index} className="process-item">
|
||||
<div className="process-rank">{index + 1}</div>
|
||||
<div className="process-content">
|
||||
<div className="process-header">
|
||||
<div className="process-name-group">
|
||||
<strong className="process-name">{proc.name}</strong>
|
||||
<span className="process-pid">PID: {proc.pid}</span>
|
||||
</div>
|
||||
<h3 className="detail-category-heading">进程与服务</h3>
|
||||
{metrics.system.topProcesses && metrics.system.topProcesses.length > 0 && (
|
||||
<div className="detail-subsection">
|
||||
<h4 className="detail-subheading">资源占用较高的进程(前 10)</h4>
|
||||
<div className="process-compact" role="table" aria-label="进程占用">
|
||||
<div className="process-compact__head" role="row">
|
||||
<span role="columnheader">#</span>
|
||||
<span role="columnheader">进程</span>
|
||||
<span role="columnheader">PID</span>
|
||||
<span role="columnheader">CPU</span>
|
||||
<span role="columnheader">内存</span>
|
||||
</div>
|
||||
{metrics.system.topProcesses.map((proc, index) => (
|
||||
<div key={index} className="process-compact__row" role="row">
|
||||
<span className="process-compact__rank" role="cell">
|
||||
{index + 1}
|
||||
</span>
|
||||
<span className="process-compact__name" role="cell" title={proc.name}>
|
||||
{proc.name}
|
||||
</span>
|
||||
<span className="process-compact__pid" role="cell">
|
||||
{proc.pid}
|
||||
</span>
|
||||
<span className="process-compact__num" role="cell">
|
||||
{proc.cpu}%
|
||||
</span>
|
||||
<span className="process-compact__num process-compact__mem" role="cell">
|
||||
{proc.memory}%
|
||||
<span className="process-compact__mem-abs"> {proc.memoryMB.toFixed(1)} MB</span>
|
||||
</span>
|
||||
</div>
|
||||
<div className="process-metrics">
|
||||
<div className="process-metric-item">
|
||||
<div className="metric-header">
|
||||
<span className="metric-label">CPU</span>
|
||||
<span className="metric-value">{proc.cpu}%</span>
|
||||
</div>
|
||||
<div className="metric-bar-container">
|
||||
<div
|
||||
className="metric-bar cpu-bar"
|
||||
style={{ width: `${Math.min(proc.cpu, 100)}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="process-metric-item">
|
||||
<div className="metric-header">
|
||||
<span className="metric-label">内存</span>
|
||||
<span className="metric-value">{proc.memory}% ({proc.memoryMB.toFixed(1)} MB)</span>
|
||||
</div>
|
||||
<div className="metric-bar-container">
|
||||
<div
|
||||
className="metric-bar memory-bar"
|
||||
style={{ width: `${Math.min(proc.memory, 100)}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{metrics.system.dockerStats && metrics.system.dockerStats.available && (
|
||||
<div className="detail-subsection">
|
||||
<h4 className="detail-subheading">Docker</h4>
|
||||
{metrics.system.dockerStats.version && (
|
||||
<div className="detail-grid">
|
||||
<div className="detail-item">
|
||||
<span className="item-label">Docker 版本</span>
|
||||
<span className="item-value">{metrics.system.dockerStats.version}</span>
|
||||
</div>
|
||||
<div className="detail-item">
|
||||
<span className="item-label">运行中的容器</span>
|
||||
<span className="item-value">{metrics.system.dockerStats.running}</span>
|
||||
</div>
|
||||
<div className="detail-item">
|
||||
<span className="item-label">停止的容器</span>
|
||||
<span className="item-value">{metrics.system.dockerStats.stopped}</span>
|
||||
</div>
|
||||
<div className="detail-item">
|
||||
<span className="item-label">镜像数量</span>
|
||||
<span className="item-value">{metrics.system.dockerStats.imageCount}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
)}
|
||||
|
||||
{metrics.system.dockerStats && metrics.system.dockerStats.available && (
|
||||
<section className="detail-section">
|
||||
<h3>Docker 信息</h3>
|
||||
{metrics.system.dockerStats.version && (
|
||||
<div className="detail-grid">
|
||||
<div className="detail-item">
|
||||
<span className="item-label">Docker 版本</span>
|
||||
<span className="item-value">{metrics.system.dockerStats.version}</span>
|
||||
</div>
|
||||
<div className="detail-item">
|
||||
<span className="item-label">运行中的容器</span>
|
||||
<span className="item-value">{metrics.system.dockerStats.running}</span>
|
||||
</div>
|
||||
<div className="detail-item">
|
||||
<span className="item-label">停止的容器</span>
|
||||
<span className="item-value">{metrics.system.dockerStats.stopped}</span>
|
||||
</div>
|
||||
<div className="detail-item">
|
||||
<span className="item-label">镜像数量</span>
|
||||
<span className="item-value">{metrics.system.dockerStats.imageCount}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{metrics.system.dockerStats.runningNames && metrics.system.dockerStats.runningNames.length > 0 && (
|
||||
<div className="docker-containers-section">
|
||||
<h4>运行中的容器</h4>
|
||||
<div className="docker-names-list">
|
||||
{metrics.system.dockerStats.runningNames.map((name, index) => (
|
||||
<span key={index} className="docker-name-tag running">
|
||||
{name}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{metrics.system.dockerStats.stoppedNames && metrics.system.dockerStats.stoppedNames.length > 0 && (
|
||||
<div className="docker-containers-section">
|
||||
<h4>停止的容器</h4>
|
||||
<div className="docker-names-list">
|
||||
{metrics.system.dockerStats.stoppedNames.map((name, index) => (
|
||||
<span key={index} className="docker-name-tag stopped">
|
||||
{name}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{metrics.system.dockerStats.imageNames && metrics.system.dockerStats.imageNames.length > 0 && (
|
||||
<div className="docker-containers-section">
|
||||
<h4>镜像列表</h4>
|
||||
<div className="docker-names-list">
|
||||
{metrics.system.dockerStats.imageNames.map((name, index) => (
|
||||
<span key={index} className="docker-name-tag image">
|
||||
{name}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
{metrics.system.dockerStats.runningNames && metrics.system.dockerStats.runningNames.length > 0 && (
|
||||
<div className="docker-containers-section">
|
||||
<h4 className="detail-subheading detail-subheading--minor">运行中的容器</h4>
|
||||
<div className="docker-names-list">
|
||||
{metrics.system.dockerStats.runningNames.map((name, index) => (
|
||||
<span key={index} className="docker-name-tag running">
|
||||
{name}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{metrics.system.dockerStats.stoppedNames && metrics.system.dockerStats.stoppedNames.length > 0 && (
|
||||
<div className="docker-containers-section">
|
||||
<h4 className="detail-subheading detail-subheading--minor">停止的容器</h4>
|
||||
<div className="docker-names-list">
|
||||
{metrics.system.dockerStats.stoppedNames.map((name, index) => (
|
||||
<span key={index} className="docker-name-tag stopped">
|
||||
{name}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{metrics.system.dockerStats.imageNames && metrics.system.dockerStats.imageNames.length > 0 && (
|
||||
<div className="docker-containers-section">
|
||||
<h4 className="detail-subheading detail-subheading--minor">镜像列表</h4>
|
||||
<div className="docker-names-list">
|
||||
{metrics.system.dockerStats.imageNames.map((name, index) => (
|
||||
<span key={index} className="docker-name-tag image">
|
||||
{name}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
@@ -504,7 +516,8 @@ export const ServerDetail = ({ metrics, serverName, onClose }: ServerDetailProps
|
||||
|
||||
{metrics.system.systemLogs && metrics.system.systemLogs.length > 0 && (
|
||||
<section className="detail-section">
|
||||
<h3>系统日志</h3>
|
||||
<h3 className="detail-category-heading">诊断与日志</h3>
|
||||
<h4 className="detail-subheading">系统日志</h4>
|
||||
<div className="logs-container">
|
||||
{metrics.system.systemLogs.map((log, index) => (
|
||||
<div key={index} className="log-line">
|
||||
|
||||
Reference in New Issue
Block a user