|
|
|
@@ -151,7 +151,7 @@ function severityTag(severity: InfrastructureAlert['severity']) {
|
|
|
|
|
return <Tag tone="info">提示</Tag>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const alertColumns: Array<TableColumn<InfrastructureAlert>> = [
|
|
|
|
|
function makeAlertColumns(onMarkRead: (alert: InfrastructureAlert) => void, readingFingerprint: string): Array<TableColumn<InfrastructureAlert>> { return [
|
|
|
|
|
{ key: 'severity', title: '级别', width: '82px', render: (record) => severityTag(record.severity) },
|
|
|
|
|
{
|
|
|
|
|
key: 'alert', title: '告警', width: '280px', render: (record) => (
|
|
|
|
@@ -162,7 +162,12 @@ const alertColumns: Array<TableColumn<InfrastructureAlert>> = [
|
|
|
|
|
{ key: 'value', title: '当前值 / 阈值', width: '150px', render: (record) => `${record.currentValue || '—'} / ${record.threshold || '—'}` },
|
|
|
|
|
{ key: 'startedAt', title: '开始时间', width: '150px', render: (record) => formatTime(record.startedAt) },
|
|
|
|
|
{ key: 'duration', title: '持续时间', width: '120px', render: (record) => formatDuration(record.startedAt) },
|
|
|
|
|
];
|
|
|
|
|
{
|
|
|
|
|
key: 'actions', title: '操作', width: '112px', render: (record) => record.acknowledged
|
|
|
|
|
? <Tag tone="neutral">已读</Tag>
|
|
|
|
|
: <Button disabled={readingFingerprint === record.fingerprint} icon={<CheckCircle2 size={14} />} onClick={() => onMarkRead(record)} size="sm" variant="ghost">{readingFingerprint === record.fingerprint ? '处理中' : '标记已读'}</Button>,
|
|
|
|
|
},
|
|
|
|
|
]; }
|
|
|
|
|
|
|
|
|
|
export function AdminSystemMonitoringPage() {
|
|
|
|
|
const [range, setRange] = useState<InfrastructureMonitoringRange>('24h');
|
|
|
|
@@ -174,6 +179,8 @@ export function AdminSystemMonitoringPage() {
|
|
|
|
|
const [showSettings, setShowSettings] = useState(false);
|
|
|
|
|
const [settingsError, setSettingsError] = useState('');
|
|
|
|
|
const [savingSettings, setSavingSettings] = useState(false);
|
|
|
|
|
const [readingFingerprint, setReadingFingerprint] = useState('');
|
|
|
|
|
const [readError, setReadError] = useState('');
|
|
|
|
|
const requestSequence = useRef(0);
|
|
|
|
|
const pendingRequests = useRef(0);
|
|
|
|
|
|
|
|
|
@@ -226,6 +233,25 @@ export function AdminSystemMonitoringPage() {
|
|
|
|
|
}
|
|
|
|
|
}, [draftThresholds, loadData, settings]);
|
|
|
|
|
|
|
|
|
|
const markAlertRead = useCallback(async (alert: InfrastructureAlert) => {
|
|
|
|
|
setReadingFingerprint(alert.fingerprint);
|
|
|
|
|
setReadError('');
|
|
|
|
|
try {
|
|
|
|
|
const result = await adminApi.markInfrastructureAlertRead(alert.fingerprint, alert.startedAt);
|
|
|
|
|
setOverview((current) => current ? {
|
|
|
|
|
...current,
|
|
|
|
|
alerts: current.alerts.map((item) => item.fingerprint === result.fingerprint && Date.parse(item.startedAt) === Date.parse(result.activeAt)
|
|
|
|
|
? { ...item, acknowledged: true, acknowledgedAt: result.acknowledgedAt }
|
|
|
|
|
: item),
|
|
|
|
|
} : current);
|
|
|
|
|
window.dispatchEvent(new Event('cmpp-infrastructure-alert-count-refresh'));
|
|
|
|
|
} catch (reason) {
|
|
|
|
|
setReadError(reason instanceof Error ? reason.message : '活动告警标记已读失败');
|
|
|
|
|
} finally {
|
|
|
|
|
setReadingFingerprint('');
|
|
|
|
|
}
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
void loadData(true);
|
|
|
|
|
void loadSettings();
|
|
|
|
@@ -263,6 +289,7 @@ export function AdminSystemMonitoringPage() {
|
|
|
|
|
const metrics = overview?.metrics;
|
|
|
|
|
const serviceHealthy = overview?.summary.serviceHealthy ?? 0;
|
|
|
|
|
const serviceTotal = overview?.summary.serviceTotal ?? 6;
|
|
|
|
|
const alertColumns = useMemo(() => makeAlertColumns((alert) => { void markAlertRead(alert); }, readingFingerprint), [markAlertRead, readingFingerprint]);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<section className="page-stack admin-system-monitoring-page">
|
|
|
|
@@ -364,6 +391,7 @@ export function AdminSystemMonitoringPage() {
|
|
|
|
|
|
|
|
|
|
<section className="surface system-monitoring-alerts" id="active-alerts">
|
|
|
|
|
<header><div><AlertTriangle size={18} /><strong>活动告警</strong><Tag tone={overview?.summary.activeAlerts ? 'warning' : 'success'}>{overview?.summary.activeAlerts ?? 0}</Tag></div><span><Clock3 size={14} /> 刷新于 {formatTime(overview?.collectedAt ?? null)}</span></header>
|
|
|
|
|
{readError ? <div className="system-monitoring-unavailable" role="alert"><AlertTriangle size={18} /><div><strong>标记已读失败</strong><span>{readError}</span></div></div> : null}
|
|
|
|
|
<Table columns={alertColumns} data={overview?.alerts ?? []} emptyText={overview?.available ? '当前没有活动告警' : '监控不可用,无法读取活动告警'} pagination={false} rowKey="fingerprint" />
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|