From 8adbbe11e9202335da04596a2d5e4a6cb40ade95 Mon Sep 17 00:00:00 2001 From: yinjianm Date: Sun, 19 Apr 2026 05:31:12 +0800 Subject: [PATCH] =?UTF-8?q?feat(frontend):=20=E5=A2=9E=E5=8A=A0=20CPU=20?= =?UTF-8?q?=E6=A0=B8=E5=BF=83=E8=AF=A6=E6=83=85=E5=BC=B9=E7=AA=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 CPU 概览卡片和“查看核心”入口, 支持按当前占用展示核心详情、平均值与最忙核心, 并补充中英日文案。 --- .../frontend/src/components/StatusMonitor.vue | 153 +++++++---- .../components/StatusMonitorCpuCoreModal.vue | 251 ++++++++++++++++++ packages/frontend/src/locales/en-US.json | 7 + packages/frontend/src/locales/ja-JP.json | 7 + packages/frontend/src/locales/zh-CN.json | 7 + 5 files changed, 377 insertions(+), 48 deletions(-) create mode 100644 packages/frontend/src/components/StatusMonitorCpuCoreModal.vue diff --git a/packages/frontend/src/components/StatusMonitor.vue b/packages/frontend/src/components/StatusMonitor.vue index 87cb0a7..e32580a 100644 --- a/packages/frontend/src/components/StatusMonitor.vue +++ b/packages/frontend/src/components/StatusMonitor.vue @@ -61,6 +61,7 @@
{{ t('statusMonitor.cpuLabel') }} +
{{ t('statusMonitor.cpuUsageTitle') }}
{{ displayCpuCores }}
@@ -68,24 +69,24 @@
-
-
+
+
-
-
- {{ item.label }} - {{ item.value }} -
-
- -
-
+ {{ item.label }} + {{ item.value }}
+
@@ -263,6 +264,12 @@ :session-id="activeSessionId" @close="isProcessManagerVisible = false" /> +
@@ -271,6 +278,7 @@ import { computed, ref, watch, type CSSProperties, type PropType } from 'vue'; import { useI18n } from 'vue-i18n'; import { storeToRefs } from 'pinia'; import ProcessManagerModal from './ProcessManagerModal.vue'; +import StatusMonitorCpuCoreModal from './StatusMonitorCpuCoreModal.vue'; import StatusMonitorCpuHistoryChart from './StatusMonitorCpuHistoryChart.vue'; import StatusMonitorNetworkHistoryChart from './StatusMonitorNetworkHistoryChart.vue'; import { useSessionStore } from '../stores/session.store'; @@ -293,6 +301,7 @@ const connectionsStore = useConnectionsStore(); const uiNotificationsStore = useUiNotificationsStore(); const { sessions } = storeToRefs(sessionStore); const { statusMonitorShowIpBoolean } = storeToRefs(settingsStore); +const isCpuCoreModalVisible = ref(false); const isProcessManagerVisible = ref(false); const props = defineProps({ @@ -316,6 +325,7 @@ const currentCpuHistory = computed(() => currentSess const currentNetRxHistory = computed(() => currentSessionState.value?.statusMonitorManager?.netRxHistory?.value ?? Array(24).fill(null)); const currentNetTxHistory = computed(() => currentSessionState.value?.statusMonitorManager?.netTxHistory?.value ?? Array(24).fill(null)); +const displayCpuPercent = computed(() => clampPercent(currentServerStatus.value?.cpuPercent)); const displayMemoryPercent = computed(() => clampPercent(currentServerStatus.value?.memPercent)); const displayDiskPercent = computed(() => clampPercent(currentServerStatus.value?.diskPercent)); const currentStatusError = computed(() => currentSessionState.value?.statusMonitorManager?.statusError?.value ?? null); @@ -530,6 +540,49 @@ const cpuCoreItems = computed(() => { }); }); +const cpuAveragePercent = computed(() => { + if (cpuCoreItems.value.length === 0) { + return displayCpuPercent.value; + } + + const total = cpuCoreItems.value.reduce((sum, item) => sum + item.percent, 0); + return total / cpuCoreItems.value.length; +}); + +const cpuBusiestCore = computed(() => { + if (cpuCoreItems.value.length === 0) { + return null; + } + + return cpuCoreItems.value.reduce((highest, item) => (item.percent > highest.percent ? item : highest)); +}); + +const cpuSummaryItems = computed(() => { + const items = [ + { + key: 'current', + label: t('statusMonitor.cpuCurrentStat'), + value: `${displayCpuPercent.value.toFixed(1)}%`, + }, + ]; + + if (cpuBusiestCore.value) { + items.push({ + key: 'busiest', + label: t('statusMonitor.cpuBusiestCoreStat'), + value: `${cpuBusiestCore.value.label} / ${cpuBusiestCore.value.value}`, + }); + } + + items.push({ + key: 'average', + label: t('statusMonitor.cpuAverageStat'), + value: `${cpuAveragePercent.value.toFixed(1)}%`, + }); + + return items; +}); + const networkFlowItems = computed(() => [ { key: 'download', @@ -983,50 +1036,54 @@ const copyIpToClipboard = async (ipAddress: string | null) => { background: linear-gradient(90deg, #7dd3fc, #2563eb); } -.usage-lane--cpu { - gap: 6px; - border-radius: 10px; - padding: 6px 8px; -} - -.usage-lane--cpu .usage-lane__content { - gap: 6px; -} - -.usage-lane--cpu .usage-lane__label { - font-size: 11px; -} - -.usage-lane--cpu .usage-lane__value-inline { - font-size: 14px; -} - -.usage-lane--cpu .usage-lane__track { - height: 6px; -} - -.cpu-core-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(116px, 1fr)); - align-content: start; - gap: 6px; - min-height: 0; - height: 100%; - overflow-y: auto; - padding-right: 2px; -} - -.cpu-core-panel { +.cpu-summary-panel { min-height: 0; border-radius: 16px; border: 1px solid rgba(148, 163, 184, 0.08); background: linear-gradient(180deg, rgba(255, 255, 255, 0.035), rgba(255, 255, 255, 0.02)), radial-gradient(circle at top left, rgba(59, 130, 246, 0.04), transparent 60%); - padding: 8px 10px; + padding: 10px; overflow: hidden; } +.cpu-summary-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(118px, 1fr)); + gap: 8px; +} + +.cpu-summary-card { + display: grid; + gap: 6px; + min-width: 0; + border-radius: 12px; + border: 1px solid rgba(148, 163, 184, 0.08); + background: rgba(255, 255, 255, 0.03); + padding: 10px; +} + +.cpu-summary-card__label { + color: #8ea0b1; + font-size: 10px; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.cpu-summary-card__value { + color: #f8fbff; + font-size: 14px; + font-weight: 800; + line-height: 1.35; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; +} + +.cpu-summary-action { + margin-top: 10px; + width: 100%; +} + .module-split { display: grid; gap: 12px; diff --git a/packages/frontend/src/components/StatusMonitorCpuCoreModal.vue b/packages/frontend/src/components/StatusMonitorCpuCoreModal.vue new file mode 100644 index 0000000..0be2ef8 --- /dev/null +++ b/packages/frontend/src/components/StatusMonitorCpuCoreModal.vue @@ -0,0 +1,251 @@ + + + + + diff --git a/packages/frontend/src/locales/en-US.json b/packages/frontend/src/locales/en-US.json index 3ae8512..4940379 100644 --- a/packages/frontend/src/locales/en-US.json +++ b/packages/frontend/src/locales/en-US.json @@ -692,6 +692,13 @@ "cpuHistoryRecentPoints": "Latest {count} samples", "cpuUsageLabel": "CPU Usage (%)", "cpuCoreLabel": "Core {index}", + "cpuCurrentStat": "Current", + "cpuAverageStat": "Average", + "cpuBusiestCoreStat": "Busiest Core", + "cpuViewAllCores": "View Cores", + "cpuCoreModalTitle": "CPU Core Details", + "cpuCoreModalSubtitle": "Sorted by current usage", + "cpuCoreModalEmpty": "No core data available", "memoryUsageLabelUnit": "Memory Usage ({unit})", "networkDownloadLabelUnit": "Download ({unit})", "networkUploadLabelUnit": "Upload ({unit})", diff --git a/packages/frontend/src/locales/ja-JP.json b/packages/frontend/src/locales/ja-JP.json index 18076a5..67fe65f 100644 --- a/packages/frontend/src/locales/ja-JP.json +++ b/packages/frontend/src/locales/ja-JP.json @@ -1427,6 +1427,13 @@ "cpuHistoryRecentPoints": "直近 {count} 件のサンプル", "cpuUsageLabel": "CPU使用率 (%)", "cpuCoreLabel": "コア {index}", + "cpuCurrentStat": "現在", + "cpuAverageStat": "平均", + "cpuBusiestCoreStat": "最大負荷コア", + "cpuViewAllCores": "コアを見る", + "cpuCoreModalTitle": "CPUコア詳細", + "cpuCoreModalSubtitle": "現在使用率順", + "cpuCoreModalEmpty": "コアデータがありません", "memoryUsageLabelUnit": "メモリ使用量 ({unit})", "networkDownloadLabelUnit": "ダウンロード ({unit})", "networkUploadLabelUnit": "アップロード ({unit})", diff --git a/packages/frontend/src/locales/zh-CN.json b/packages/frontend/src/locales/zh-CN.json index 4734f47..36bf761 100644 --- a/packages/frontend/src/locales/zh-CN.json +++ b/packages/frontend/src/locales/zh-CN.json @@ -692,6 +692,13 @@ "cpuHistoryRecentPoints": "最近 {count} 个采样点", "cpuUsageLabel": "CPU 使用率 (%)", "cpuCoreLabel": "核心 {index}", + "cpuCurrentStat": "当前", + "cpuAverageStat": "平均", + "cpuBusiestCoreStat": "最忙核心", + "cpuViewAllCores": "查看核心", + "cpuCoreModalTitle": "CPU 核心详情", + "cpuCoreModalSubtitle": "按当前占用排序", + "cpuCoreModalEmpty": "暂无核心数据", "memoryUsageLabelUnit": "内存使用 ({unit})", "networkDownloadLabelUnit": "下载 ({unit})", "networkUploadLabelUnit": "上传 ({unit})",