feat: 监控告警体系统一重构 + 自动化任务调度引擎 + 监控大屏拆分
告警中心 - 新增统一告警(alert_unified)、监控告警规则(monitor_alert_rule)、指标目录(alert_metric_catalog) - 通知策略与消息分发(alert_notify_policy/dispatch/message)、静默规则 - 告警引擎按阈值/SNMP 拆分(alert_engine_monitor/snmp) 监控采集 - SNMP 常量/模板与历史指标(snmp templates/constants、snmp_metrics_history、snmp_collect) - 中间件监控 Redis 采集与种子(middleware_monitor_redis/seed) - 站点探测 pkg(siteprobe) + 站点视图(site_view) 自动化任务 - cron 调度器(cronscheduler)、脚本运行器(scriptrunner)、系统任务(systask)及运行记录(cron_job_run、system_task) 审计与凭证 - 审计设置(audit_setting) + 历史清理(auditcleaner) 前端 - 监控页面(host/middleware/site/snmp)拆分为子模块并大屏化 - 告警中心多 Tab(规则/指标目录/通知策略/静默)、任务管理组件重构、Agent CLI Markdown 渲染 - 移除旧脚本卡片/详情/日志组件,统一为 ScriptTable/ScriptCodeEditor/TerminalOutput Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
1 parent
092d0e4b9e
commit
471353e3d7
190 files changed
+16640
-3484
No files matched your search
@@ -0,0 +1,135 @@
|
||||
// SNMP 概览图表 — 设备状态分布 + CPU 对比 // Logan.Li
|
||||
import { useMemo } from 'react';
|
||||
import {
|
||||
BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip,
|
||||
ResponsiveContainer, Cell, PieChart, Pie,
|
||||
} from 'recharts';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { BarChart3, PieChart as PieChartIcon } from 'lucide-react';
|
||||
import type { SnmpDevice, SnmpOverviewItem } from './helpers';
|
||||
import { fmtMetricValue, statusColor } from './helpers';
|
||||
import { MonitorChartTooltip, MONITOR_PIE_STROKE, MonitorPieTooltip } from '../shared/monitorChartTheme';
|
||||
|
||||
interface Props {
|
||||
devices: SnmpDevice[];
|
||||
overview: SnmpOverviewItem[];
|
||||
}
|
||||
|
||||
export function SnmpOverviewCharts({ devices, overview }: Props) {
|
||||
const statusData = useMemo(() => {
|
||||
const online = devices.filter((d) => d.status === 'online').length;
|
||||
const error = devices.filter((d) => d.status === 'error').length;
|
||||
const offline = devices.length - online - error;
|
||||
return [
|
||||
{ name: '在线', value: online, fill: 'var(--dg-green)' },
|
||||
{ name: '异常', value: error, fill: 'var(--dg-red)' },
|
||||
{ name: '离线', value: offline, fill: 'var(--dg-text-muted)' },
|
||||
].filter((d) => d.value > 0);
|
||||
}, [devices]);
|
||||
|
||||
const cpuData = useMemo(() => {
|
||||
return overview
|
||||
.filter((o) => o.cpu != null && o.cpu > 0)
|
||||
.map((o) => ({
|
||||
name: o.name.length > 10 ? `${o.name.slice(0, 10)}…` : o.name,
|
||||
fullName: o.name,
|
||||
cpu: o.cpu ?? 0,
|
||||
fill: statusColor(o.status),
|
||||
}))
|
||||
.sort((a, b) => b.cpu - a.cpu);
|
||||
}, [overview]);
|
||||
|
||||
if (devices.length === 0) return null;
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-3">
|
||||
<Card className="lg:col-span-2 bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]">
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<BarChart3 className="h-4 w-4 text-[var(--dg-text-muted)]" />
|
||||
<h3 className="text-sm font-medium text-[var(--dg-text-primary)]">设备 CPU 对比</h3>
|
||||
</div>
|
||||
{cpuData.length === 0 ? (
|
||||
<p className="text-xs text-[var(--dg-text-muted)] py-8 text-center">暂无 CPU 指标,请先完成采集</p>
|
||||
) : (
|
||||
<ResponsiveContainer width="100%" height={Math.max(160, cpuData.length * 36)}>
|
||||
<BarChart data={cpuData} layout="vertical" margin={{ top: 0, right: 16, left: 0, bottom: 0 }}>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="var(--dg-border)" horizontal={false} />
|
||||
<XAxis
|
||||
type="number"
|
||||
domain={[0, 100]}
|
||||
tick={{ fontSize: 10, fill: 'var(--dg-text-muted)' }}
|
||||
tickLine={false}
|
||||
axisLine={{ stroke: 'var(--dg-border)' }}
|
||||
tickFormatter={(v: number) => `${v}%`}
|
||||
/>
|
||||
<YAxis
|
||||
type="category"
|
||||
dataKey="name"
|
||||
width={72}
|
||||
tick={{ fontSize: 10, fill: 'var(--dg-text-muted)' }}
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
/>
|
||||
<Tooltip
|
||||
content={(
|
||||
<MonitorChartTooltip
|
||||
valueFormatter={(v) => fmtMetricValue(v, '%')}
|
||||
nameFormatter={() => 'CPU'}
|
||||
/>
|
||||
)}
|
||||
labelFormatter={(_, payload) => {
|
||||
const item = payload?.[0]?.payload as { fullName?: string } | undefined;
|
||||
return item?.fullName ?? '';
|
||||
}}
|
||||
/>
|
||||
<Bar dataKey="cpu" radius={[0, 4, 4, 0]} maxBarSize={20} isAnimationActive={false}>
|
||||
{cpuData.map((entry) => (
|
||||
<Cell key={entry.fullName} fill={entry.fill} />
|
||||
))}
|
||||
</Bar>
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]">
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<PieChartIcon className="h-4 w-4 text-[var(--dg-text-muted)]" />
|
||||
<h3 className="text-sm font-medium text-[var(--dg-text-primary)]">设备状态</h3>
|
||||
</div>
|
||||
<ResponsiveContainer width="100%" height={180}>
|
||||
<PieChart>
|
||||
<Pie
|
||||
data={statusData}
|
||||
dataKey="value"
|
||||
nameKey="name"
|
||||
cx="50%"
|
||||
cy="50%"
|
||||
innerRadius={42}
|
||||
outerRadius={68}
|
||||
paddingAngle={2}
|
||||
isAnimationActive={false}
|
||||
>
|
||||
{statusData.map((entry) => (
|
||||
<Cell key={entry.name} fill={entry.fill} stroke={MONITOR_PIE_STROKE} strokeWidth={2} />
|
||||
))}
|
||||
</Pie>
|
||||
<Tooltip content={<MonitorPieTooltip />} />
|
||||
</PieChart>
|
||||
</ResponsiveContainer>
|
||||
<div className="flex flex-wrap justify-center gap-3 mt-1">
|
||||
{statusData.map((d) => (
|
||||
<div key={d.name} className="flex items-center gap-1.5 text-xs text-[var(--dg-text-muted)]">
|
||||
<span className="w-2 h-2 rounded-full" style={{ backgroundColor: d.fill }} />
|
||||
{d.name} {d.value}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user