From 943f09d357f53afaf1caf5cf11fa32c1fa60fdc9 Mon Sep 17 00:00:00 2001 From: NellPoi Date: Tue, 11 Aug 2026 17:34:58 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E8=BF=90=E8=90=A5?= =?UTF-8?q?=E7=9B=91=E6=8E=A7=E5=86=85=E5=AD=98=E5=AE=B9=E9=87=8F=E6=98=BE?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ops/components/OpsDashboardHeader.vue | 3 ++- .../ops/utils/__tests__/opsFormatters.spec.ts | 25 +++++++++++++++++++ .../views/admin/ops/utils/opsFormatters.ts | 13 ++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 frontend/src/views/admin/ops/utils/__tests__/opsFormatters.spec.ts diff --git a/frontend/src/views/admin/ops/components/OpsDashboardHeader.vue b/frontend/src/views/admin/ops/components/OpsDashboardHeader.vue index 8e5c9e8379..16e86b2161 100644 --- a/frontend/src/views/admin/ops/components/OpsDashboardHeader.vue +++ b/frontend/src/views/admin/ops/components/OpsDashboardHeader.vue @@ -10,6 +10,7 @@ import { opsAPI, type OpsDashboardOverview, type OpsMetricThresholds, type OpsRe import type { OpsRequestDetailsPreset } from './OpsRequestDetailsModal.vue' import { useAdminSettingsStore } from '@/stores' import { formatNumber } from '@/utils/format' +import { formatMemorySizeMB } from '../utils/opsFormatters' type RealtimeWindow = '1min' | '5min' | '30min' | '1h' @@ -1462,7 +1463,7 @@ function handleToolbarRefresh() { {{ systemMetrics?.memory_used_mb == null || systemMetrics?.memory_total_mb == null ? '-' - : `${formatNumber(systemMetrics.memory_used_mb)} / ${formatNumber(systemMetrics.memory_total_mb)} MB` + : `${formatMemorySizeMB(systemMetrics.memory_used_mb)} / ${formatMemorySizeMB(systemMetrics.memory_total_mb)}` }} diff --git a/frontend/src/views/admin/ops/utils/__tests__/opsFormatters.spec.ts b/frontend/src/views/admin/ops/utils/__tests__/opsFormatters.spec.ts new file mode 100644 index 0000000000..56b121b37f --- /dev/null +++ b/frontend/src/views/admin/ops/utils/__tests__/opsFormatters.spec.ts @@ -0,0 +1,25 @@ +import { describe, expect, it } from 'vitest' + +import { formatMemorySizeMB } from '../opsFormatters' + +describe('formatMemorySizeMB', () => { + it('keeps capacities below one GiB in MB', () => { + expect(formatMemorySizeMB(61)).toBe('61 MB') + expect(formatMemorySizeMB(1023)).toBe('1023 MB') + }) + + it('converts capacities at or above one GiB to GB', () => { + expect(formatMemorySizeMB(1024)).toBe('1 GB') + expect(formatMemorySizeMB(1536)).toBe('1.5 GB') + expect(formatMemorySizeMB(32768)).toBe('32 GB') + }) + + it('handles zero and invalid values without producing a misleading unit', () => { + expect(formatMemorySizeMB(0)).toBe('0 MB') + expect(formatMemorySizeMB(-1)).toBe('-') + expect(formatMemorySizeMB(Number.NaN)).toBe('-') + expect(formatMemorySizeMB(Number.POSITIVE_INFINITY)).toBe('-') + expect(formatMemorySizeMB(null)).toBe('-') + expect(formatMemorySizeMB(undefined)).toBe('-') + }) +}) diff --git a/frontend/src/views/admin/ops/utils/opsFormatters.ts b/frontend/src/views/admin/ops/utils/opsFormatters.ts index d503b5a5f1..9c96b0b021 100644 --- a/frontend/src/views/admin/ops/utils/opsFormatters.ts +++ b/frontend/src/views/admin/ops/utils/opsFormatters.ts @@ -73,3 +73,16 @@ export function formatByteRate(bytes: number, windowMinutes: number): string { const seconds = Math.max(1, (windowMinutes || 1) * 60) return `${formatBytes(bytes / seconds, 1)}/s` } + +/** + * 格式化 Ops 接口返回的内存容量。 + * + * 后端的 `*_mb` 字段以 1024² 字节为单位换算得到;这里沿用该口径, + * 避免把大容量数值交给通用数字缩写格式化后出现“3.2万 MB”这类不直观的显示。 + */ +export function formatMemorySizeMB(value: number | null | undefined): string { + if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) return '-' + if (value === 0) return '0 MB' + + return formatBytes(value * 1024 * 1024, 1) +}