From 7b05e2dd3ca14a0f4ead98851f1d5d6969407456 Mon Sep 17 00:00:00 2001 From: ryan Date: Thu, 18 Jun 2026 19:09:20 +0800 Subject: [PATCH] =?UTF-8?q?style(wavelet):=20=E7=B4=A7=E5=87=91=E5=8C=96?= =?UTF-8?q?=E5=85=A8=E7=90=83=E6=80=81=E5=8A=BF=E6=9D=BF=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 地图固定高度约 260px,指标改为侧栏密集单元格,图例行内化以降低首屏占用。 --- .../components/dashboard/world-stage-map.tsx | 6 +- .../components/dashboard/world-stage.tsx | 246 +++++++++--------- docs/changelog/index.md | 1 + 3 files changed, 131 insertions(+), 122 deletions(-) diff --git a/Wavelet/frontend/app/(main)/components/dashboard/world-stage-map.tsx b/Wavelet/frontend/app/(main)/components/dashboard/world-stage-map.tsx index c1efaf1f..f4d83936 100644 --- a/Wavelet/frontend/app/(main)/components/dashboard/world-stage-map.tsx +++ b/Wavelet/frontend/app/(main)/components/dashboard/world-stage-map.tsx @@ -368,9 +368,9 @@ export function WorldStageMap({ return 1; } - const widthScale = Math.min(Math.max(width / 960, 0.52), 1.08); - const heightScale = Math.min(Math.max(height / 520, 0.72), 1.1); - const compactViewportScale = width < 640 ? 0.9 : 1; + const widthScale = Math.min(Math.max(width / 720, 0.48), 1.02); + const heightScale = Math.min(Math.max(height / 260, 0.65), 1.05); + const compactViewportScale = width < 640 ? 0.88 : 1; return Number( (Math.min(widthScale, heightScale) * compactViewportScale).toFixed(3), diff --git a/Wavelet/frontend/app/(main)/components/dashboard/world-stage.tsx b/Wavelet/frontend/app/(main)/components/dashboard/world-stage.tsx index 19ce2a49..1cd8023f 100644 --- a/Wavelet/frontend/app/(main)/components/dashboard/world-stage.tsx +++ b/Wavelet/frontend/app/(main)/components/dashboard/world-stage.tsx @@ -38,56 +38,47 @@ const WorldStageMap = dynamic( {ssr: false}, ); -type LegendTone = 'source' | 'healthy' | 'warning' | 'danger'; +const LEGEND_ITEMS = [ + {dot: 'bg-blue-500', label: '来源'}, + {dot: 'bg-emerald-500', label: '正常'}, + {dot: 'bg-amber-500', label: '承压'}, + {dot: 'bg-destructive', label: '异常'}, +] as const; -const legendToneClass: Record = { - source: 'bg-blue-500/10 border-blue-500/20 text-blue-600 dark:text-blue-400', - healthy: - 'bg-emerald-500/10 border-emerald-500/20 text-emerald-600 dark:text-emerald-400', - warning: - 'bg-amber-500/10 border-amber-500/20 text-amber-600 dark:text-amber-400', - danger: - 'bg-destructive/10 border-destructive/20 text-destructive', -}; - -function LegendBadge({label, tone}: {label: string; tone: LegendTone}) { - return ( - - {label} - - ); -} - -function StageMetricCard({ +function CompactMetric({ label, value, hint, icon: Icon, progress, + className, }: { label: string; value: string; hint: string; icon: ComponentType<{className?: string}>; progress?: number; + className?: string; }) { return ( - - - {label} - - - -
{value}
- {typeof progress === 'number' ? ( - - ) : null} -

{hint}

-
-
+
+
+ {label} + +
+ + {value} + + {typeof progress === 'number' ? ( + + ) : null} + {hint} +
); } @@ -125,7 +116,7 @@ export function WorldStage({ observer.disconnect(); } }, - {rootMargin: '180px 0px'}, + {rootMargin: '120px 0px'}, ); observer.observe(mapViewport); @@ -153,106 +144,123 @@ export function WorldStage({ const mapModeLabel = sourceCountries.length > 0 - ? '访客来源热度' + ? '访客来源' : geoConfiguredNodes > 0 - ? '节点地理坐标' - : '节点覆盖信号'; + ? '节点坐标' + : '覆盖信号'; + + const metrics = [ + { + label: '在线覆盖', + value: formatPercent(onlineRate), + hint: `${summary.online_nodes}/${summary.total_nodes} 在线`, + icon: Server, + progress: onlineRate, + }, + { + label: '运行健康', + value: formatPercent(healthyRate), + hint: `${summary.unhealthy_nodes} 个异常`, + icon: ShieldCheck, + progress: healthyRate, + }, + { + label: '窗口请求', + value: formatCompactNumber(traffic.request_count), + hint: `QPS ${traffic.estimated_qps.toFixed(1)}`, + icon: Activity, + }, + { + label: '平均 CPU', + value: formatPercent(capacity.average_cpu_usage_percent), + hint: `${capacity.high_cpu_nodes} 个偏高`, + icon: Cpu, + progress: capacity.average_cpu_usage_percent, + }, + { + label: '平均内存', + value: formatPercent(capacity.average_memory_usage_percent), + hint: `${capacity.high_memory_nodes} 个偏高`, + icon: MemoryStick, + progress: capacity.average_memory_usage_percent, + }, + { + label: '高存储', + value: formatCompactNumber(capacity.high_storage_nodes), + hint: `${summary.offline_nodes} 离线 · ${summary.pending_nodes} 待接入`, + icon: HardDrive, + }, + ] as const; return ( -
- - -
-
- - - 全球态势板 - - - 节点地理分布与访客来源热度一览 - -
- - {mapModeLabel} - + + +
+
+ + + 全球态势板 + + {mapModeLabel} + + + + 节点分布与来源热度 +
-
- - - - +
+ {LEGEND_ITEMS.map((item) => ( + + + {item.label} + + ))}
- - +
+ + + +
-
+
{shouldRenderMap ? ( ) : ( - +
+ +
)}
{shouldRenderMap && nodes.length === 0 ? ( -
-
- 当前暂无节点接入。地图已完成初始化,后续会在这里展示节点位置与健康状态。 -
+
+

+ 暂无节点,接入后将展示地理分布 +

) : null}
- - -
- - - - - - -
-
+
+ {metrics.map((metric) => ( + + ))} +
+
+ + ); } \ No newline at end of file diff --git a/docs/changelog/index.md b/docs/changelog/index.md index 94da27e4..4d9ed54a 100644 --- a/docs/changelog/index.md +++ b/docs/changelog/index.md @@ -21,6 +21,7 @@ sidebar: false - Wavelet 总览仪表盘图表对齐旧前端 ECharts 样式:24 小时请求/容量趋势补充请求量与错误量展示,新增网络与磁盘趋势、Top 节点榜单、来源分布、状态码分布与 Top Domain 板块。 - Wavelet 总览仪表盘迁移旧前端「全球态势板」:世界地图展示节点健康与来源国家热度,并保留在线覆盖、流量与容量摘要指标。 - Wavelet 全球态势板视觉对齐系统风格:改用 `Card` / `Badge` / `Progress` 与虚线边框指标卡,地图配色与 tooltip 跟随主题 token。 +- Wavelet 全球态势板布局紧凑化:地图高度降至约 260px,指标改为侧栏密集单元格,图例改为行内色点标注。 - 将 `openflare-server` 后端业务域迁移至 `Wavelet/internal/apps/openflare/`,通过 `/api/*` legacy 兼容层保持旧前端 API 路径不变。 - 新增 OpenFlare 业务表 goose 迁移(`of_options`、`of_origins`、`of_proxy_routes`、`of_nodes`、`of_waf_*`、`of_tls_*`、`of_config_versions`、`of_pages_*`、`of_apply_logs` 等)。