From 38961ba10e5ad4beff6dfc15890b7f4b9a78b868 Mon Sep 17 00:00:00 2001 From: IanShaw027 <131567472+IanShaw027@users.noreply.github.com> Date: Thu, 15 Jan 2026 19:50:31 +0800 Subject: [PATCH] =?UTF-8?q?refactor(ops):=20=E4=BC=98=E5=8C=96=E9=98=88?= =?UTF-8?q?=E5=80=BC=E6=A3=80=E6=9F=A5=E7=B3=BB=E7=BB=9F=E5=92=8C=E5=B8=83?= =?UTF-8?q?=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 阈值检查系统优化: - 引入三级阈值系统(normal/warning/critical) - 统一阈值判断逻辑,支持警告和严重两个级别 - 移除硬编码的 TTFT 颜色判断,改用阈值配置 - 新增 getThresholdColorClass 统一颜色映射 布局优化: - 优化详细指标在卡片内的响应式布局 - 改进宽屏下的卡片布局显示 - 优化指标数值的对齐和间距 --- frontend/src/views/admin/ops/OpsDashboard.vue | 4 +- .../ops/components/OpsDashboardHeader.vue | 120 ++++++++++-------- 2 files changed, 68 insertions(+), 56 deletions(-) diff --git a/frontend/src/views/admin/ops/OpsDashboard.vue b/frontend/src/views/admin/ops/OpsDashboard.vue index ff2a434d..d33f0f64 100644 --- a/frontend/src/views/admin/ops/OpsDashboard.vue +++ b/frontend/src/views/admin/ops/OpsDashboard.vue @@ -693,8 +693,8 @@ onMounted(async () => { async function loadThresholds() { try { - const settings = await opsAPI.getAlertRuntimeSettings() - metricThresholds.value = settings.thresholds || null + const thresholds = await opsAPI.getMetricThresholds() + metricThresholds.value = thresholds || null } catch (err) { console.warn('[OpsDashboard] Failed to load thresholds', err) metricThresholds.value = null diff --git a/frontend/src/views/admin/ops/components/OpsDashboardHeader.vue b/frontend/src/views/admin/ops/components/OpsDashboardHeader.vue index 2d52b6e8..8e868bba 100644 --- a/frontend/src/views/admin/ops/components/OpsDashboardHeader.vue +++ b/frontend/src/views/admin/ops/components/OpsDashboardHeader.vue @@ -169,42 +169,54 @@ const updatedAtLabel = computed(() => { return props.lastUpdated.toLocaleTimeString() }) -// --- Color coding for TTFT --- -function getTTFTColor(ms: number | null | undefined): string { - if (ms == null) return 'text-gray-900 dark:text-white' - if (ms < 500) return 'text-green-600 dark:text-green-400' - if (ms < 1000) return 'text-yellow-600 dark:text-yellow-400' - if (ms < 2000) return 'text-orange-600 dark:text-orange-400' - return 'text-red-600 dark:text-red-400' -} - // --- Threshold checking helpers --- -function isSLABelowThreshold(slaPercent: number | null): boolean { - if (slaPercent == null) return false +type ThresholdLevel = 'normal' | 'warning' | 'critical' + +function getSLAThresholdLevel(slaPercent: number | null): ThresholdLevel { + if (slaPercent == null) return 'normal' const threshold = props.thresholds?.sla_percent_min - if (threshold == null) return false - return slaPercent < threshold + if (threshold == null) return 'normal' + if (slaPercent < threshold) return 'critical' + if (slaPercent < threshold / 0.8) return 'warning' + return 'normal' } -function isTTFTAboveThreshold(ttftP99Ms: number | null): boolean { - if (ttftP99Ms == null) return false +function getTTFTThresholdLevel(ttftMs: number | null): ThresholdLevel { + if (ttftMs == null) return 'normal' const threshold = props.thresholds?.ttft_p99_ms_max - if (threshold == null) return false - return ttftP99Ms > threshold + if (threshold == null) return 'normal' + if (ttftMs >= threshold) return 'critical' + if (ttftMs >= threshold * 0.8) return 'warning' + return 'normal' } -function isRequestErrorRateAboveThreshold(errorRatePercent: number | null): boolean { - if (errorRatePercent == null) return false +function getRequestErrorRateThresholdLevel(errorRatePercent: number | null): ThresholdLevel { + if (errorRatePercent == null) return 'normal' const threshold = props.thresholds?.request_error_rate_percent_max - if (threshold == null) return false - return errorRatePercent > threshold + if (threshold == null) return 'normal' + if (errorRatePercent >= threshold) return 'critical' + if (errorRatePercent >= threshold * 0.8) return 'warning' + return 'normal' } -function isUpstreamErrorRateAboveThreshold(upstreamErrorRatePercent: number | null): boolean { - if (upstreamErrorRatePercent == null) return false +function getUpstreamErrorRateThresholdLevel(upstreamErrorRatePercent: number | null): ThresholdLevel { + if (upstreamErrorRatePercent == null) return 'normal' const threshold = props.thresholds?.upstream_error_rate_percent_max - if (threshold == null) return false - return upstreamErrorRatePercent > threshold + if (threshold == null) return 'normal' + if (upstreamErrorRatePercent >= threshold) return 'critical' + if (upstreamErrorRatePercent >= threshold * 0.8) return 'warning' + return 'normal' +} + +function getThresholdColorClass(level: ThresholdLevel): string { + switch (level) { + case 'critical': + return 'text-red-600 dark:text-red-400' + case 'warning': + return 'text-yellow-600 dark:text-yellow-400' + default: + return 'text-green-600 dark:text-green-400' + } } // --- Realtime / Overview labels --- @@ -1197,7 +1209,7 @@ function handleToolbarRefresh() {
{{ t('admin.ops.sla') }} - +
-
+
{{ ttftP99Ms ?? '-' }}
ms (P99)
-
-
- {{ t('admin.ops.p95') }} - {{ ttftP95Ms ?? '-' }} +
+
+ P95: + {{ ttftP95Ms ?? '-' }} ms
-
- {{ t('admin.ops.p90') }} - {{ ttftP90Ms ?? '-' }} +
+ P90: + {{ ttftP90Ms ?? '-' }} ms
-
- {{ t('admin.ops.p50') }} - {{ ttftP50Ms ?? '-' }} +
+ P50: + {{ ttftP50Ms ?? '-' }} ms
-
+
Avg: - {{ ttftAvgMs ?? '-' }} + {{ ttftAvgMs ?? '-' }} ms
-
+
Max: - {{ ttftMaxMs ?? '-' }} + {{ ttftMaxMs ?? '-' }} ms
@@ -1335,7 +1347,7 @@ function handleToolbarRefresh() { {{ t('admin.ops.requestDetails.details') }}
-
+
{{ errorRatePercent == null ? '-' : `${errorRatePercent.toFixed(2)}%` }}
@@ -1361,7 +1373,7 @@ function handleToolbarRefresh() { {{ t('admin.ops.requestDetails.details') }}
-
+
{{ upstreamErrorRatePercent == null ? '-' : `${upstreamErrorRatePercent.toFixed(2)}%` }}