diff --git a/frontend/src/components/layout/Header.tsx b/frontend/src/components/layout/Header.tsx index abcf701..7208056 100644 --- a/frontend/src/components/layout/Header.tsx +++ b/frontend/src/components/layout/Header.tsx @@ -1,6 +1,6 @@ import { useState } from 'react'; import { cn } from '@/lib/utils'; -import { useThemeStore, useTabStore, useNotificationStore, useUserStore } from '@/store'; +import { useThemeStore, useNotificationStore, useUserStore } from '@/store'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Badge } from '@/components/ui/badge'; @@ -17,7 +17,6 @@ import { Search, Bell, Menu, - X, Settings, User, LogOut, @@ -33,7 +32,6 @@ import { useTheme, THEME_OPTIONS } from '@/hooks/useTheme'; export function Header() { const { sidebarCollapsed, toggleSidebar } = useThemeStore(); - const { tabs, activeTab, removeTab, setActiveTab } = useTabStore(); const { notifications, unreadCount, markAsRead, markAllAsRead } = useNotificationStore(); const { user, logout } = useUserStore(); const { theme, setTheme } = useTheme(); @@ -226,36 +224,7 @@ export function Header() { - {/* Tabs */} - {tabs.length > 0 && ( -
- {tabs.map((tab) => ( -
setActiveTab(tab.id)} - > - {tab.title} - {tab.closable && ( - - )} -
- ))} -
- )} + {/* Tabs 已移除 — 侧边栏导航已足够,tab 栏多余且遮挡内容 */} ); } diff --git a/frontend/src/components/monitor/alert-center/AlertCenter.tsx b/frontend/src/components/monitor/alert-center/AlertCenter.tsx index 31ce844..a2f53a0 100644 --- a/frontend/src/components/monitor/alert-center/AlertCenter.tsx +++ b/frontend/src/components/monitor/alert-center/AlertCenter.tsx @@ -46,18 +46,23 @@ export interface Alert { annotations: Record; } +// Logan.Li — 字段名与后端 alert.go AlertRule 对齐 interface AlertRule { - id: string; + id: number; name: string; description: string; enabled: boolean; - severity: 'critical' | 'warning' | 'info'; - query: string; - duration: string; - threshold: number; - labels: string[]; - notificationChannels: string[]; - lastEvaluated: string; + severity: string; + type?: string; + source?: string; + expression?: string; + duration?: number; + threshold?: number; + labels?: string; + annotations?: string; + notify_channels?: string; + created_at?: string; + updated_at?: string; } interface NotificationChannel { @@ -103,7 +108,24 @@ const AlertCenter: React.FC = () => { setLoading(true); try { const res = await alertCenterApi.getAlerts(); - const data = res.list || []; + const raw = (res.list || []) as any[]; + // Logan.Li — 后端字段映射到前端 Alert 接口 + const data: Alert[] = raw.map(a => ({ + id: String(a.id), + title: a.rule_name || a.title || `告警 #${a.id}`, + description: a.message || a.description || '', + severity: a.severity || 'info', + status: a.status || 'firing', + source: a.target || a.source || '', + service: a.service || '', + metric: a.metric || '', + value: a.value || 0, + threshold: a.threshold || 0, + startedAt: a.started_at || a.startedAt || a.created_at || new Date().toISOString(), + resolvedAt: a.resolved_at || a.resolvedAt, + labels: typeof a.labels === 'string' ? (() => { try { return JSON.parse(a.labels || '{}'); } catch { return {}; } })() : (a.labels || {}), + annotations: typeof a.annotations === 'string' ? (() => { try { return JSON.parse(a.annotations || '{}'); } catch { return {}; } })() : (a.annotations || {}), + })); setAlerts(data); setStats({ firing: data.filter((a: Alert) => a.status === 'firing').length, @@ -171,7 +193,7 @@ const AlertCenter: React.FC = () => { const toggleRule = async (ruleId: string, enabled: boolean) => { try { await alertCenterApi.toggleRule(ruleId); - setRules(rules.map(r => r.id === ruleId ? { ...r, enabled } : r)); + setRules(rules.map(r => String(r.id) === ruleId ? { ...r, enabled } : r)); toast.success('规则状态已更新'); } catch (error) { toast.error('更新失败'); @@ -531,20 +553,31 @@ const AlertCenter: React.FC = () => {

{rule.description}

- {rule.query} - 持续 {rule.duration} - 阈值: {rule.threshold} -
-
- {rule.notificationChannels.map(ch => ( - {ch} - ))} + {rule.expression && ( + {rule.expression} + )} + {rule.duration ? 持续 {rule.duration}秒 : null}
+ {(() => { + let channels: string[] = []; + try { + const raw = rule.notify_channels; + if (raw) channels = Array.isArray(raw) ? raw : JSON.parse(raw); + } catch { /* ignore */ } + if (!channels.length) return null; + return ( +
+ {channels.map(ch => ( + {ch} + ))} +
+ ); + })()}
toggleRule(rule.id, checked)} + onCheckedChange={(checked) => toggleRule(String(rule.id), checked)} />