feat(web): CopilotAlert 告警组件 + 告警 API 扩展
- CopilotAlert: 分级告警列表,30秒轮询刷新,危急 banner - copilot.ts 新增 listAlerts 函数
This commit is contained in:
@@ -54,6 +54,10 @@ export function dismissInsight(id: string) {
|
||||
return client.post(`/copilot/insights/${id}/dismiss`);
|
||||
}
|
||||
|
||||
export function listAlerts(params?: { severity?: string; page?: number; page_size?: number }) {
|
||||
return listInsights({ insight_type: 'anomaly', ...params });
|
||||
}
|
||||
|
||||
export function listRules(params?: { page?: number; page_size?: number }) {
|
||||
return client.get<PaginatedResponse<Record<string, unknown>>>('/copilot/rules', { params });
|
||||
}
|
||||
|
||||
106
apps/web/src/components/Copilot/CopilotAlert.tsx
Normal file
106
apps/web/src/components/Copilot/CopilotAlert.tsx
Normal file
@@ -0,0 +1,106 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { Alert, Badge, List, Button, Space, Typography, Spin } from 'antd';
|
||||
import { CheckOutlined } from '@ant-design/icons';
|
||||
import { listAlerts, dismissInsight } from '../../api/copilot';
|
||||
import type { CopilotInsight } from '../../api/copilot';
|
||||
|
||||
const severityConfig: Record<string, { type: 'success' | 'info' | 'warning' | 'error'; label: string }> = {
|
||||
critical: { type: 'error', label: '危急' },
|
||||
warning: { type: 'warning', label: '警告' },
|
||||
info: { type: 'info', label: '提示' },
|
||||
};
|
||||
|
||||
export function CopilotAlert() {
|
||||
const [alerts, setAlerts] = useState<CopilotInsight[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [dismissing, setDismissing] = useState<string | null>(null);
|
||||
|
||||
const fetchAlerts = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await listAlerts({ page_size: 50 });
|
||||
const result = res.data as unknown as { items: CopilotInsight[]; total: number };
|
||||
setAlerts(result.items ?? []);
|
||||
} catch {
|
||||
// 静默
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchAlerts();
|
||||
const timer = setInterval(fetchAlerts, 30_000);
|
||||
return () => clearInterval(timer);
|
||||
}, [fetchAlerts]);
|
||||
|
||||
const handleDismiss = async (id: string) => {
|
||||
setDismissing(id);
|
||||
try {
|
||||
await dismissInsight(id);
|
||||
setAlerts((prev) => prev.filter((a) => a.id !== id));
|
||||
} finally {
|
||||
setDismissing(null);
|
||||
}
|
||||
};
|
||||
|
||||
if (!alerts.length && !loading) return null;
|
||||
|
||||
const criticalCount = alerts.filter((a) => a.severity === 'critical').length;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{criticalCount > 0 && (
|
||||
<Alert
|
||||
type="error"
|
||||
showIcon
|
||||
message={`${criticalCount} 条危急告警`}
|
||||
banner
|
||||
style={{ marginBottom: 16 }}
|
||||
/>
|
||||
)}
|
||||
{loading && alerts.length === 0 ? (
|
||||
<Spin />
|
||||
) : (
|
||||
<List
|
||||
size="small"
|
||||
dataSource={alerts}
|
||||
renderItem={(item) => {
|
||||
const config = severityConfig[item.severity] ?? severityConfig.info;
|
||||
return (
|
||||
<List.Item
|
||||
actions={[
|
||||
<Button
|
||||
key="dismiss"
|
||||
size="small"
|
||||
icon={<CheckOutlined />}
|
||||
loading={dismissing === item.id}
|
||||
onClick={() => handleDismiss(item.id)}
|
||||
>
|
||||
已知悉
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<List.Item.Meta
|
||||
title={
|
||||
<Space>
|
||||
<Badge status={config.type} />
|
||||
<Typography.Text>{item.title}</Typography.Text>
|
||||
</Space>
|
||||
}
|
||||
description={
|
||||
item.content?.suggestion ? (
|
||||
<Typography.Text type="secondary">
|
||||
{item.content.suggestion as string}
|
||||
</Typography.Text>
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
</List.Item>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
export { CopilotBadge } from './CopilotBadge';
|
||||
export { CopilotCard } from './CopilotCard';
|
||||
export { CopilotAlert } from './CopilotAlert';
|
||||
export { useCopilotRisk } from './useCopilotRisk';
|
||||
export { useCopilotInsights } from './useCopilotInsights';
|
||||
|
||||
Reference in New Issue
Block a user