From 18aa445b32ca5424ed7969e732876d5d44ea795d Mon Sep 17 00:00:00 2001 From: attacker Date: Mon, 15 Jun 2026 03:35:30 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BB=AA=E8=A1=A8=E7=9B=98=E6=AD=BB?= =?UTF-8?q?=E6=8C=89=E9=92=AE=E4=BF=AE=E5=A4=8D+K8s=E8=BF=9B=E5=BA=A6?= =?UTF-8?q?=E6=9D=A1=E5=8F=AF=E8=A7=86=E5=8C=96+=E6=A0=87=E9=A2=98?= =?UTF-8?q?=E7=AE=80=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Dashboard: 移除无功能的"实时监控"按钮,"刷新"按钮接上数据重载(useCallback+spin动画) - DashboardRecent: 4个"查看全部"按钮接上路由导航(告警→/alerts, 发布→/releases, 服务器→/servers, 集群→/clusters) - K8sNodes: CPU/Mem使用率从纯文本改为迷你进度条(颜色阈值:绿<60%/黄60-85%/红>85%) - K8sWorkloads: Deployment副本比用进度条可视化(满=绿/未满=黄) - K8sClusters: 页面标题"K8s集群管理"→"集群列表" --- .../src/components/dashboard/Dashboard.tsx | 115 +++++++++--------- .../components/dashboard/DashboardRecent.tsx | 13 +- frontend/src/components/k8s/K8sClusters.tsx | 2 +- frontend/src/components/k8s/K8sNodes.tsx | 28 ++++- frontend/src/components/k8s/K8sWorkloads.tsx | 17 ++- 5 files changed, 104 insertions(+), 71 deletions(-) diff --git a/frontend/src/components/dashboard/Dashboard.tsx b/frontend/src/components/dashboard/Dashboard.tsx index ee1604f..d6ebbae 100644 --- a/frontend/src/components/dashboard/Dashboard.tsx +++ b/frontend/src/components/dashboard/Dashboard.tsx @@ -1,6 +1,6 @@ -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { Button } from '@/components/ui/button'; -import { Clock, Activity, Server, Cloud, Bell, DollarSign } from 'lucide-react'; +import { Clock, Server, Cloud, Bell, DollarSign } from 'lucide-react'; import { dashboardStatsApi, serverApi, k8sApi } from '@/services/api'; import { StatCard } from './DashboardStats'; import { CostOverviewCard, TopResourceUsageCard, ResourceUsageCard, computeMonthChange } from './DashboardCharts'; @@ -13,7 +13,8 @@ import { } from './DashboardRecent'; export function Dashboard() { - const [loading, setLoading] = useState(true); + const [initialLoading, setInitialLoading] = useState(true); + const [refreshing, setRefreshing] = useState(false); const [stats, setStats] = useState(null); const [servers, setServers] = useState([]); const [clusters, setClusters] = useState([]); @@ -22,55 +23,57 @@ export function Dashboard() { const [expCerts, setExpCerts] = useState([]); const [metrics, setMetrics] = useState([]); // 真实 Agent 指标时间序列 // Logan.Li - useEffect(() => { - const fetchData = async () => { - setLoading(true); - try { - const [statsRes, serversRes, clustersRes, alertsRes, releasesRes, metricsRes] = await Promise.all([ - dashboardStatsApi.getStats().catch((e) => { - console.error('Failed to fetch dashboard stats:', e); - return null; - }), - serverApi.getServers({ page: 1, page_size: 100 }).catch((e) => { - console.error('Failed to fetch servers:', e); - return null; - }), - k8sApi.getClusters().catch((e) => { - console.error('Failed to fetch k8s clusters:', e); - return null; - }), - dashboardStatsApi.getAlerts(10).catch((e) => { - console.error('Failed to fetch dashboard alerts:', e); - return null; - }), - dashboardStatsApi.getReleases(5).catch((e) => { - console.error('Failed to fetch dashboard releases:', e); - return null; - }), - // 获取真实 Agent 指标时间序列(过去24小时按小时聚合) // Logan.Li - dashboardStatsApi.getMetrics(24).catch((e) => { - console.error('Failed to fetch metrics:', e); - return []; - }), - ]); + const refreshData = useCallback(async () => { + setRefreshing(true); + try { + const [statsRes, serversRes, clustersRes, alertsRes, releasesRes, metricsRes] = await Promise.all([ + dashboardStatsApi.getStats().catch((e) => { + console.error('Failed to fetch dashboard stats:', e); + return null; + }), + serverApi.getServers({ page: 1, page_size: 100 }).catch((e) => { + console.error('Failed to fetch servers:', e); + return null; + }), + k8sApi.getClusters().catch((e) => { + console.error('Failed to fetch k8s clusters:', e); + return null; + }), + dashboardStatsApi.getAlerts(10).catch((e) => { + console.error('Failed to fetch dashboard alerts:', e); + return null; + }), + dashboardStatsApi.getReleases(5).catch((e) => { + console.error('Failed to fetch dashboard releases:', e); + return null; + }), + // 获取真实 Agent 指标时间序列(过去24小时按小时聚合) // Logan.Li + dashboardStatsApi.getMetrics(24).catch((e) => { + console.error('Failed to fetch metrics:', e); + return []; + }), + ]); - setStats(statsRes); - setServers(serversRes?.list ?? []); - setClusters(clustersRes?.list ?? []); - setAlerts(Array.isArray(alertsRes) ? alertsRes : []); - setReleases(Array.isArray(releasesRes) ? releasesRes : []); - setExpCerts(statsRes?.expiring_certs ?? []); - setMetrics(Array.isArray(metricsRes) ? metricsRes : []); - } catch (error) { - console.error('Failed to load dashboard data:', error); - } finally { - setLoading(false); - } - }; - fetchData(); + setStats(statsRes); + setServers(serversRes?.list ?? []); + setClusters(clustersRes?.list ?? []); + setAlerts(Array.isArray(alertsRes) ? alertsRes : []); + setReleases(Array.isArray(releasesRes) ? releasesRes : []); + setExpCerts(statsRes?.expiring_certs ?? []); + setMetrics(Array.isArray(metricsRes) ? metricsRes : []); + } catch (error) { + console.error('Failed to load dashboard data:', error); + } finally { + setRefreshing(false); + setInitialLoading(false); + } }, []); - if (loading) { + useEffect(() => { + refreshData(); + }, [refreshData]); + + if (initialLoading) { return (
@@ -92,16 +95,10 @@ export function Dashboard() {

仪表盘

实时监控和运维概览

-
- - -
+
{/* Stats Grid */} diff --git a/frontend/src/components/dashboard/DashboardRecent.tsx b/frontend/src/components/dashboard/DashboardRecent.tsx index 3fdf933..fb59f59 100644 --- a/frontend/src/components/dashboard/DashboardRecent.tsx +++ b/frontend/src/components/dashboard/DashboardRecent.tsx @@ -1,3 +1,4 @@ +import { useNavigate } from 'react-router-dom'; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; @@ -12,6 +13,7 @@ import { // 告警消息卡片 export function AlertMessagesCard({ alerts }: { alerts: any[] }) { + const navigate = useNavigate(); const recentAlerts = alerts.filter((a) => a.status === 'firing').slice(0, 8); return ( @@ -22,7 +24,7 @@ export function AlertMessagesCard({ alerts }: { alerts: any[] }) { 告警消息 最近触发的告警事件 - @@ -150,6 +152,7 @@ export function CertificateExpiryCard({ expCerts }: { expCerts: any[] }) { // 发布历史卡片 export function ReleaseHistoryCard({ releases }: { releases: any[] }) { + const navigate = useNavigate(); return ( @@ -158,7 +161,7 @@ export function ReleaseHistoryCard({ releases }: { releases: any[] }) { 发布历史 今日应用发布记录 - @@ -229,6 +232,7 @@ export function ReleaseHistoryCard({ releases }: { releases: any[] }) { // 服务器状态卡片 export function ServerStatusCard({ servers }: { servers: any[] }) { + const navigate = useNavigate(); const runningServers = servers.filter((s) => s.status === 'running').length; const stoppedServers = servers.filter((s) => s.status === 'stopped').length; const maintenanceServers = servers.filter((s) => s.status === 'maintenance').length; @@ -241,7 +245,7 @@ export function ServerStatusCard({ servers }: { servers: any[] }) { 服务器状态 服务器运行状态分布 - @@ -292,6 +296,7 @@ export function ServerStatusCard({ servers }: { servers: any[] }) { // K8s集群状态卡片 export function K8sClusterCard({ clusters }: { clusters: any[] }) { + const navigate = useNavigate(); return ( @@ -300,7 +305,7 @@ export function K8sClusterCard({ clusters }: { clusters: any[] }) { K8s集群 Kubernetes集群健康状态 - diff --git a/frontend/src/components/k8s/K8sClusters.tsx b/frontend/src/components/k8s/K8sClusters.tsx index 2dd5424..5ea8460 100644 --- a/frontend/src/components/k8s/K8sClusters.tsx +++ b/frontend/src/components/k8s/K8sClusters.tsx @@ -110,7 +110,7 @@ export function K8sClusters() { {/* Page Header */}
-

K8s集群管理

+

集群列表

管理Kubernetes集群资源

diff --git a/frontend/src/components/k8s/K8sNodes.tsx b/frontend/src/components/k8s/K8sNodes.tsx index 62a03e7..510f456 100644 --- a/frontend/src/components/k8s/K8sNodes.tsx +++ b/frontend/src/components/k8s/K8sNodes.tsx @@ -145,10 +145,30 @@ export function K8sNodes() { {n.cpu ? `${n.cpu}核` : '-'} / {n.memory ? `${Math.round(n.memory / 1024)}GB` : '-'} -
- CPU {(n.cpu_usage || 0).toFixed(1)}% - Mem {(n.memory_usage || 0).toFixed(1)}% -
+ {(() => { + const cpu = n.cpu_usage || 0; + const mem = n.memory_usage || 0; + const bar = (val: number) => val >= 85 ? 'bg-red-500' : val >= 60 ? 'bg-yellow-500' : 'bg-emerald-500'; + const txt = (val: number) => val >= 85 ? 'text-red-400' : val >= 60 ? 'text-yellow-400' : 'text-emerald-400'; + return ( +
+
+ CPU +
+
+
+ {cpu.toFixed(0)}% +
+
+ Mem +
+
+
+ {mem.toFixed(0)}% +
+
+ ); + })()} {n.pod_count || 0} / {n.max_pods || '-'} {n.kubelet_version || '-'} diff --git a/frontend/src/components/k8s/K8sWorkloads.tsx b/frontend/src/components/k8s/K8sWorkloads.tsx index c01c7c9..3f65847 100644 --- a/frontend/src/components/k8s/K8sWorkloads.tsx +++ b/frontend/src/components/k8s/K8sWorkloads.tsx @@ -146,9 +146,20 @@ export function K8sWorkloads() { {d.name} {d.namespace} - - {d.ready_replicas || 0} / {d.replicas || 0} - + {(() => { + const ready = d.ready_replicas || 0; + const total = d.replicas || 0; + const pct = total > 0 ? (ready / total) * 100 : 0; + const done = ready === total; + return ( +
+
+
+
+ {ready}/{total} +
+ ); + })()} {d.image || '-'} {d.strategy || 'RollingUpdate'}