feat: V1 - DAG编排/告警系统/Agent循环/知识库RAG/Webhook + 审核管理富上下文修复
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import {
|
||||
Card, Table, Button, Tag, Space, message, Badge, Empty, Typography,
|
||||
} from 'antd';
|
||||
import {
|
||||
BellOutlined, CheckOutlined, ReloadOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { alertApi } from '@/api';
|
||||
import type { Alert } from '@/types';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
const severityConfig: Record<string, { label: string; color: string; badge: string }> = {
|
||||
critical: { label: '严重', color: 'red', badge: 'error' },
|
||||
warning: { label: '警告', color: 'orange', badge: 'warning' },
|
||||
info: { label: '通知', color: 'blue', badge: 'processing' },
|
||||
};
|
||||
|
||||
const alertTypeConfig: Record<string, string> = {
|
||||
budget_warning: '预算警告',
|
||||
budget_exceeded: '预算超限',
|
||||
task_failed: '任务失败',
|
||||
worker_offline: 'Worker离线',
|
||||
};
|
||||
|
||||
export default function Alerts() {
|
||||
const [alerts, setAlerts] = useState<Alert[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [filter, setFilter] = useState<string>('all'); // all, unread, read
|
||||
|
||||
const loadData = useCallback(() => {
|
||||
setLoading(true);
|
||||
const isRead = filter === 'unread' ? false : filter === 'read' ? true : undefined;
|
||||
alertApi.list(isRead).then(setAlerts).catch(() => {}).finally(() => setLoading(false));
|
||||
}, [filter]);
|
||||
|
||||
useEffect(() => { loadData(); }, [loadData]);
|
||||
|
||||
const handleMarkRead = async (id: number) => {
|
||||
try {
|
||||
await alertApi.markRead(id);
|
||||
message.success('已标记为已读');
|
||||
loadData();
|
||||
} catch { message.error('操作失败'); }
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '级别', dataIndex: 'severity', key: 'severity', width: 80,
|
||||
render: (s: string) => {
|
||||
const cfg = severityConfig[s] || { label: s, color: 'default', badge: 'default' };
|
||||
return <Badge status={cfg.badge as any} text={cfg.label} />;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '类型', dataIndex: 'alert_type', key: 'alert_type', width: 120,
|
||||
render: (t: string, r: Alert) => <Tag color={severityConfig[r.severity]?.color || 'default'}>{alertTypeConfig[t] || t}</Tag>,
|
||||
},
|
||||
{
|
||||
title: '消息', dataIndex: 'message', key: 'message',
|
||||
render: (m: string) => <Text style={{ whiteSpace: 'pre-wrap' }}>{m}</Text>,
|
||||
},
|
||||
{
|
||||
title: '时间', dataIndex: 'created_at', key: 'created_at', width: 160,
|
||||
render: (t: string) => <Text style={{ fontSize: 12 }}>{dayjs(t).format('MM-DD HH:mm:ss')}</Text>,
|
||||
},
|
||||
{
|
||||
title: '状态', dataIndex: 'is_read', key: 'is_read', width: 80,
|
||||
render: (r: boolean) => r ? <Text type="secondary">已读</Text> : <Tag color="orange">未读</Tag>,
|
||||
},
|
||||
{
|
||||
title: '操作', key: 'action', width: 80,
|
||||
render: (_: any, r: Alert) => r.is_read ? null : (
|
||||
<Button size="small" icon={<CheckOutlined />} onClick={() => handleMarkRead(r.id)}>已读</Button>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const filterButtons = [
|
||||
{ key: 'all', label: '全部' },
|
||||
{ key: 'unread', label: '未读' },
|
||||
{ key: 'read', label: '已读' },
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card
|
||||
title={<Space><BellOutlined /> 告警中心</Space>}
|
||||
extra={
|
||||
<Space>
|
||||
{filterButtons.map(f => (
|
||||
<Button key={f.key} type={filter === f.key ? 'primary' : 'default'} onClick={() => setFilter(f.key)}>{f.label}</Button>
|
||||
))}
|
||||
<Button icon={<ReloadOutlined />} onClick={loadData} />
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
{alerts.length === 0 && !loading ? (
|
||||
<Empty description="暂无告警记录" />
|
||||
) : (
|
||||
<Table columns={columns} dataSource={alerts} rowKey="id" loading={loading} pagination={{ pageSize: 15 }} />
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user