import { AlertTriangle, CheckCircle, XCircle, Clock, MessageSquare, RefreshCw, Filter, Info, AlertOctagon, Bell, ChevronRight, } from "lucide-react"; import React, { useEffect, useState, useCallback } from "react"; import { Button } from "../ui/button"; import { Badge } from "../ui/badge"; import { Input } from "../ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "../ui/select"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from "../ui/dialog"; import { Label } from "../ui/label"; import { Textarea } from "../ui/textarea"; import { inspectionAlertApi, type AlertRecord, type AlertLevel, type AlertRecordStatus, type AlertStats, } from "../../infrastructure/inspection/inspectionApi"; import { toast } from "../ui/toast"; const LEVEL_COLORS: Record = { critical: { bg: "bg-red-500/10", text: "text-red-500", border: "border-red-500/30", dot: "bg-red-500", }, warning: { bg: "bg-orange-500/10", text: "text-orange-500", border: "border-orange-500/30", dot: "bg-orange-500", }, info: { bg: "bg-blue-500/10", text: "text-blue-500", border: "border-blue-500/30", dot: "bg-blue-500", }, }; const LEVEL_LABELS: Record = { critical: "严重", warning: "警告", info: "提示", }; const STATUS_LABELS: Record = { active: "待处理", acknowledged: "已确认", resolved: "已恢复", closed: "已关闭", }; const STATUS_COLORS: Record = { active: "text-red-500", acknowledged: "text-orange-500", resolved: "text-green-500", closed: "text-muted-foreground", }; export function AlertCenterPage() { const [alerts, setAlerts] = useState([]); const [stats, setStats] = useState(null); const [loading, setLoading] = useState(true); const [selectedAlert, setSelectedAlert] = useState(null); const [detailOpen, setDetailOpen] = useState(false); const [noteDialogOpen, setNoteDialogOpen] = useState(false); const [noteText, setNoteText] = useState(""); // 筛选 const [filterLevel, setFilterLevel] = useState("all"); const [filterStatus, setFilterStatus] = useState("all"); const [searchText, setSearchText] = useState(""); const [activeTab, setActiveTab] = useState<"active" | "all">("active"); const loadData = useCallback(async () => { setLoading(true); try { const [statsData, alertsData] = await Promise.all([ inspectionAlertApi.getStats(), activeTab === "active" ? inspectionAlertApi.getActive() : inspectionAlertApi.list({ limit: 200 }), ]); setStats(statsData); setAlerts(alertsData); } catch (e: any) { console.error("[AlertCenter] 加载失败:", e); } finally { setLoading(false); } }, [activeTab]); useEffect(() => { loadData(); }, [loadData]); // 筛选后的数据 const filteredAlerts = (alerts ?? []).filter((a) => { if (filterLevel !== "all" && a.level !== filterLevel) return false; if (filterStatus !== "all" && a.status !== filterStatus) return false; if (searchText) { const s = searchText.toLowerCase(); if ( !a.device_name.toLowerCase().includes(s) && !a.rule_name.toLowerCase().includes(s) && !a.message.toLowerCase().includes(s) ) return false; } return true; }); const handleViewDetail = (alert: AlertRecord) => { setSelectedAlert(alert); setDetailOpen(true); }; const handleAcknowledge = async () => { if (!selectedAlert) return; try { await inspectionAlertApi.acknowledge(selectedAlert.id, "operator"); toast.success("已确认告警"); setDetailOpen(false); loadData(); } catch (e: any) { toast.error("操作失败"); } }; const handleResolve = async () => { if (!selectedAlert) return; try { await inspectionAlertApi.resolve(selectedAlert.id, "manual", "operator"); toast.success("已恢复告警"); setDetailOpen(false); loadData(); } catch (e: any) { toast.error("操作失败"); } }; const handleClose = () => { if (!selectedAlert) return; setNoteText(""); setNoteDialogOpen(true); }; const handleCloseConfirm = async () => { if (!selectedAlert) return; try { await inspectionAlertApi.close(selectedAlert.id, noteText); toast.success("已关闭告警"); setNoteDialogOpen(false); setDetailOpen(false); loadData(); } catch (e: any) { toast.error("操作失败"); } }; const formatTime = (ts: number) => { return new Date(ts).toLocaleString(); }; const getTimeAgo = (ts: number) => { const diff = Date.now() - ts; const mins = Math.floor(diff / 60000); if (mins < 1) return "刚刚"; if (mins < 60) return `${mins} 分钟前`; const hours = Math.floor(mins / 60); if (hours < 24) return `${hours} 小时前`; const days = Math.floor(hours / 24); return `${days} 天前`; }; return (
{/* 顶部统计 */}

告警中心

监控设备巡检产生的告警

{/* 统计卡片 */}
严重告警
{stats?.active_critical ?? 0}
警告
{stats?.active_warning ?? 0}
提示
{stats?.active_info ?? 0}
已恢复
{stats?.resolved ?? 0}
{/* 筛选栏 */}
{activeTab === "all" && ( )} setSearchText(e.target.value)} className="w-48" />
{/* 告警列表 */}
{loading ? (
加载中...
) : filteredAlerts.length === 0 ? (

暂无告警记录

{activeTab === "active" ? "所有设备运行正常" : "还没有产生过告警"}

) : (
{filteredAlerts.map((alert) => { const colors = LEVEL_COLORS[alert.level]; return (
handleViewDetail(alert)} className="flex items-center gap-4 px-6 py-3 hover:bg-accent/30 cursor-pointer transition-colors" > {/* 级别指示点 */}
{/* 主要内容 */}
{alert.device_name} {LEVEL_LABELS[alert.level]} {STATUS_LABELS[alert.status]}
{alert.rule_name} — {alert.message}
{/* 时间 */}
{getTimeAgo(alert.triggered_at)}
{formatTime(alert.triggered_at)}
); })}
)}
{/* 详情弹窗 */} {selectedAlert && ( <>
告警详情
设备名称
{selectedAlert.device_name}
告警级别
{LEVEL_LABELS[selectedAlert.level]}
规则名称
{selectedAlert.rule_name}
告警消息
{selectedAlert.message}
当前值
{selectedAlert.metric_value}
阈值
{selectedAlert.threshold}
触发时间
{formatTime(selectedAlert.triggered_at)}
当前状态
{STATUS_LABELS[selectedAlert.status]}
{selectedAlert.note && (
备注
{selectedAlert.note}
)}
{selectedAlert.status === "active" && ( )} {(selectedAlert.status === "active" || selectedAlert.status === "acknowledged") && ( )} {selectedAlert.status !== "closed" && ( )}
)}
{/* 关闭备注弹窗 */} 关闭告警