feat: 新增设备巡检模块实现完整的巡检告警功能
新增设备巡检模块核心功能,包括: - 巡检数据存储与调度系统 - 华为/H3C交换机及Linux服务器采集器 - 设备扩展配置与告警规则管理 - 主机详情页巡检配置入口 - 产品名称自定义设置功能 - 网络设备SSH exec通道兼容处理
This commit is contained in:
@@ -5,6 +5,9 @@ import {
|
||||
HardDrive,
|
||||
MemoryStick,
|
||||
Network,
|
||||
Server,
|
||||
Plug,
|
||||
Fan,
|
||||
} from 'lucide-react';
|
||||
import React, { memo, useEffect, useMemo, useState } from 'react';
|
||||
import { useI18n } from '../../application/i18n/I18nProvider';
|
||||
@@ -363,6 +366,8 @@ export const SystemOverviewTab = memo(function SystemOverviewTab({
|
||||
isConnected: true,
|
||||
});
|
||||
const hasStats = Boolean(stats.lastUpdated);
|
||||
// Network device detection: backend sets this field only for H3C/Huawei/Cisco etc.
|
||||
const isNetworkDevice = Boolean(stats.networkDevicePorts);
|
||||
|
||||
const memoryPercent = ratioPercent(stats?.memUsed, stats?.memTotal);
|
||||
const mountedDiskUsage = aggregateMountedDiskUsage(stats.disks);
|
||||
@@ -374,6 +379,22 @@ export const SystemOverviewTab = memo(function SystemOverviewTab({
|
||||
const loadOne = stats?.loadAverage?.[0] ?? null;
|
||||
const loadPercent = ratioPercent(loadOne, stats?.cpuCores);
|
||||
|
||||
// Network device specific
|
||||
const ndPorts = stats.networkDevicePorts ?? [];
|
||||
const ndUp = stats.networkDevicePortUp ?? ndPorts.filter(p => p.phy === 'up').length;
|
||||
const ndDown = stats.networkDevicePortDown ?? ndPorts.length - ndUp;
|
||||
|
||||
// Helper to shorten interface names: GigabitEthernet0/0/1 → GE0/0/1
|
||||
const shortPortName = (name: string) => name
|
||||
.replace(/^GigabitEthernet/i, 'GE')
|
||||
.replace(/^TenGigEthernet/i, 'XGE')
|
||||
.replace(/^TwentyFiveGigEthernet/i, '25GE')
|
||||
.replace(/^FortyGigEthernet/i, '40GE')
|
||||
.replace(/^HundredGigEthernet/i, '100GE')
|
||||
.replace(/^Ethernet/i, 'Eth')
|
||||
.replace(/^Serial/i, 'Ser')
|
||||
.replace(/^Vlan-interface/i, 'Vlan');
|
||||
|
||||
useEffect(() => {
|
||||
setHistory([]);
|
||||
}, [sessionId]);
|
||||
@@ -425,6 +446,89 @@ export const SystemOverviewTab = memo(function SystemOverviewTab({
|
||||
<SystemPanelLoading message={t('systemManager.overview.loading')} />
|
||||
) : showEmpty ? (
|
||||
<SystemPanelEmpty icon={Activity} message={t('systemManager.overview.empty')} />
|
||||
) : hasStats && isNetworkDevice ? (
|
||||
/* ==================== NETWORK DEVICE VIEW ==================== */
|
||||
<div className="flex-1 min-h-0 overflow-y-auto px-3 py-3 space-y-4">
|
||||
{/* Banner */}
|
||||
<div className="flex items-center gap-2 text-[11px] font-medium text-muted-foreground">
|
||||
<Server size={13} className="text-violet-500" />
|
||||
<span>网络设备模式 · VRP / Comware CLI</span>
|
||||
</div>
|
||||
|
||||
{/* Main metric cards: CPU + Memory */}
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<MetricCard
|
||||
label="CPU"
|
||||
value={formatPercent(stats.cpu)}
|
||||
detail="处理器使用率"
|
||||
icon={Cpu}
|
||||
gaugeValue={stats.cpu}
|
||||
trendValues={trends.cpu}
|
||||
trendMax={100}
|
||||
tone="text-violet-500"
|
||||
gradientId="nd-cpu-card"
|
||||
/>
|
||||
<MetricCard
|
||||
label="内存"
|
||||
value={memoryPercent !== null ? formatPercent(memoryPercent) : '--'}
|
||||
detail={`${formatMemoryMb(stats.memUsed)} / ${formatMemoryMb(stats.memTotal)}`}
|
||||
icon={MemoryStick}
|
||||
gaugeValue={memoryPercent}
|
||||
trendValues={trends.memory}
|
||||
trendMax={100}
|
||||
tone="text-emerald-500"
|
||||
gradientId="nd-mem-card"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Port status summary */}
|
||||
<section
|
||||
className="rounded-lg border p-3"
|
||||
style={{ borderColor: 'hsl(var(--border) / 0.7)', background: 'hsl(var(--background))' }}
|
||||
>
|
||||
<div className="mb-3 flex items-center justify-between gap-2">
|
||||
<div className="flex items-center gap-1.5 text-xs font-semibold" style={{ color: 'hsl(var(--foreground))' }}>
|
||||
<Network size={13} className="text-cyan-500" />
|
||||
端口状态
|
||||
</div>
|
||||
<div className="flex items-center gap-3 text-[11px]">
|
||||
<span className="text-emerald-500">↑ {ndUp} UP</span>
|
||||
<span className="text-muted-foreground">↓ {ndDown} DOWN</span>
|
||||
<span className="text-muted-foreground">共 {ndPorts.length}</span>
|
||||
</div>
|
||||
</div>
|
||||
{ndPorts.length > 0 ? (
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{ndPorts.slice(0, 48).map((port) => (
|
||||
<div
|
||||
key={port.name}
|
||||
className={cn(
|
||||
'inline-flex items-center gap-1 rounded border px-1.5 py-0.5 text-[10px] font-mono tabular-nums',
|
||||
port.phy === 'up'
|
||||
? 'border-emerald-500/40 bg-emerald-500/10 text-emerald-600'
|
||||
: 'border-muted/40 bg-muted/20 text-muted-foreground',
|
||||
)}
|
||||
title={`${port.name} — ${port.phy.toUpperCase()}`}
|
||||
>
|
||||
<span className={cn('h-1.5 w-1.5 rounded-full', port.phy === 'up' ? 'bg-emerald-500' : 'bg-muted-foreground/40')} />
|
||||
{shortPortName(port.name)}
|
||||
</div>
|
||||
))}
|
||||
{ndPorts.length > 48 && (
|
||||
<span className="text-[10px] text-muted-foreground py-0.5">+{ndPorts.length - 48} 更多</span>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-[11px]" style={{ color: 'hsl(var(--muted-foreground))' }}>暂无端口数据</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
{/* Power / Fan status (placeholders — extend when we add display device parsing) */}
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<InfoPill label="电源" value="未采集" icon={Plug} tone="text-amber-500" />
|
||||
<InfoPill label="风扇" value="未采集" icon={Fan} tone="text-cyan-500" />
|
||||
</div>
|
||||
</div>
|
||||
) : hasStats ? (
|
||||
<div className="flex-1 min-h-0 overflow-y-auto px-3 py-3 space-y-4">
|
||||
{/* Main metric cards */}
|
||||
|
||||
Reference in New Issue
Block a user