From 2e69a00049dfd68552d0cfea6d49ead9ca041c27 Mon Sep 17 00:00:00 2001 From: Baobhan Sith <80159437+Heavrnl@users.noreply.github.com> Date: Mon, 12 May 2025 21:33:23 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E7=9B=91=E8=A7=86=E5=99=A8=EF=BC=8C=E9=87=8D=E6=9E=84=E4=B8=BA?= =?UTF-8?q?=E5=8D=95=E4=BE=8B=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/LayoutRenderer.vue | 115 ++--- .../frontend/src/components/StatusCharts.vue | 395 +++++++++--------- .../frontend/src/components/StatusMonitor.vue | 116 +++-- .../src/composables/useStatusMonitor.ts | 50 ++- packages/frontend/src/types/server.types.ts | 9 +- 5 files changed, 377 insertions(+), 308 deletions(-) diff --git a/packages/frontend/src/components/LayoutRenderer.vue b/packages/frontend/src/components/LayoutRenderer.vue index 030df39..c9515af 100644 --- a/packages/frontend/src/components/LayoutRenderer.vue +++ b/packages/frontend/src/components/LayoutRenderer.vue @@ -159,12 +159,9 @@ const componentProps = computed(() => { // FileManager 可能也需要转发事件,例如文件操作相关的,暂时省略 }; case 'statusMonitor': - // 仅当有活动会话时才返回实际 props,否则返回空对象 - if (!currentActiveSession) return {}; + // 始终渲染,传递 activeSessionId return { - sessionId: props.activeSessionId ?? '', // 确保 sessionId 不为 null - serverStatus: currentActiveSession.statusMonitorManager.serverStatus.value, // 此时 currentActiveSession 必不为 null - statusError: currentActiveSession.statusMonitorManager.statusError.value, // 此时 currentActiveSession 必不为 null + activeSessionId: props.activeSessionId, // 传递 activeSessionId class: 'pane-content', }; case 'editor': @@ -253,17 +250,11 @@ const sidebarProps = computed(() => (paneName: PaneName | null, side: 'left' | ' return baseProps; // Return only base props if no active session } case 'statusMonitor': - // Only provide props if there's an active session - if (activeSession.value) { - return { - ...baseProps, - sessionId: activeSession.value.sessionId, // Pass session ID - serverStatus: activeSession.value.statusMonitorManager.serverStatus.value, - statusError: activeSession.value.statusMonitorManager.statusError.value, - }; - } else { - return baseProps; // Return only base props if no active session - } + // 始终渲染,传递 activeSessionId + return { + ...baseProps, + activeSessionId: props.activeSessionId, // 传递 activeSessionId + }; // Add cases for other components if they need specific props or event forwarding in the sidebar // case 'commandHistory': return { ...baseProps, onExecuteCommand: (cmd: string) => emit('sendCommand', cmd) }; // case 'quickCommands': return { ...baseProps, onExecuteCommand: (cmd: string) => emit('sendCommand', cmd) }; @@ -493,20 +484,13 @@ onMounted(() => { \ No newline at end of file diff --git a/packages/frontend/src/components/StatusMonitor.vue b/packages/frontend/src/components/StatusMonitor.vue index 1f07713..6c6769b 100644 --- a/packages/frontend/src/components/StatusMonitor.vue +++ b/packages/frontend/src/components/StatusMonitor.vue @@ -6,14 +6,20 @@ {{ t('statusMonitor.title') }} + +
+ + {{ t('layout.noActiveSession.title') }} +
+ -
+
- {{ t('statusMonitor.errorPrefix') }} {{ statusError }} + {{ t('statusMonitor.errorPrefix') }} {{ currentStatusError }}
-
+
{{ t('statusMonitor.loading') }}
@@ -40,10 +46,10 @@
-
+
- {{ serverStatus.cpuPercent?.toFixed(1) ?? t('statusMonitor.notAvailable') }}% + {{ currentServerStatus.cpuPercent?.toFixed(1) ?? t('statusMonitor.notAvailable') }}%
@@ -53,7 +59,7 @@
-
+
{{ memDisplay }}
@@ -66,9 +72,9 @@
-
+
{{ swapDisplay }}
@@ -80,7 +86,7 @@
-
+
{{ diskDisplay }}
@@ -91,32 +97,36 @@
- +
- {{ formatBytesPerSecond(serverStatus?.netRxRate) }} + {{ formatBytesPerSecond(currentServerStatus?.netRxRate) }} - {{ formatBytesPerSecond(serverStatus?.netTxRate) }} + {{ formatBytesPerSecond(currentServerStatus?.netTxRate) }}
- + +
- diff --git a/packages/frontend/src/composables/useStatusMonitor.ts b/packages/frontend/src/composables/useStatusMonitor.ts index bce0087..5cf7dc4 100644 --- a/packages/frontend/src/composables/useStatusMonitor.ts +++ b/packages/frontend/src/composables/useStatusMonitor.ts @@ -18,10 +18,28 @@ export interface StatusMonitorDependencies { */ export function createStatusMonitorManager(sessionId: string, wsDeps: StatusMonitorDependencies) { const { onMessage, isConnected } = wsDeps; + const MAX_HISTORY_POINTS = 60; // 图表显示的点数 const serverStatus = ref(null); const statusError = ref(null); // 存储状态获取错误 + // --- 历史数据存储 --- + // 初始化为包含60个 null 或 0 的数组,这样图表初始时有占位 + const cpuHistory = ref<(number | null)[]>(Array(MAX_HISTORY_POINTS).fill(null)); + const memUsedHistory = ref<(number | null)[]>(Array(MAX_HISTORY_POINTS).fill(null)); // Store memUsed in MB + const netRxHistory = ref<(number | null)[]>(Array(MAX_HISTORY_POINTS).fill(null)); // Store rate in Bytes/sec + const netTxHistory = ref<(number | null)[]>(Array(MAX_HISTORY_POINTS).fill(null)); // Store rate in Bytes/sec + + // --- 辅助函数:更新历史数据数组 --- + const updateHistory = (historyRef: Ref<(number | null)[]>, newValue: number | undefined) => { + const currentHistory = historyRef.value; + currentHistory.shift(); // 移除最旧的数据点 + // 如果新值无效(undefined 或 null),推入 null,否则推入数字 + currentHistory.push((newValue === undefined || newValue === null || isNaN(newValue)) ? null : newValue); + historyRef.value = [...currentHistory]; // 触发响应式更新 + }; + + // --- WebSocket 消息处理 --- const handleStatusUpdate = (payload: MessagePayload, message?: WebSocketMessage) => { // 检查消息是否属于此会话 @@ -29,12 +47,20 @@ export function createStatusMonitorManager(sessionId: string, wsDeps: StatusMoni return; // 忽略不属于此会话的消息 } - // console.debug(`[会话 ${sessionId}][状态监控模块] 收到 status_update:`, JSON.stringify(payload)); // 添加日志 - if (payload && payload.status) { - serverStatus.value = payload.status; + // console.debug(`[会话 ${sessionId}][状态监控模块] 收到 status_update:`, JSON.stringify(payload)); + if (payload?.status) { + const newStatus: ServerStatus = payload.status; + serverStatus.value = newStatus; statusError.value = null; // 收到有效状态时清除错误 + + // 更新历史数据 + updateHistory(cpuHistory, newStatus.cpuPercent); + updateHistory(memUsedHistory, newStatus.memUsed); + updateHistory(netRxHistory, newStatus.netRxRate); + updateHistory(netTxHistory, newStatus.netTxRate); + } else { - console.warn(`[会话 ${sessionId}][状态监控模块] 收到缺少 payload.status 的 status_update 消息`); + console.warn(`[会话 ${sessionId}][状态监控模块] 收到无效的 status_update 消息`); // 可以选择设置一个错误状态,表明数据格式不正确 // statusError.value = '收到的状态数据格式无效'; } @@ -113,11 +139,17 @@ export function createStatusMonitorManager(sessionId: string, wsDeps: StatusMoni // --- 暴露接口 --- return { - serverStatus: readonly(serverStatus), // 只读状态 - statusError: readonly(statusError), // 只读错误状态 - registerStatusHandlers, // 暴露注册函数,以便在需要时可以重新注册 - unregisterAllStatusHandlers, // 暴露注销函数,以便在需要时可以手动注销 - cleanup, // 暴露清理函数,在会话关闭时调用 + serverStatus: readonly(serverStatus), // 当前状态 + statusError: readonly(statusError), // 错误状态 + // --- 暴露历史数据 --- + cpuHistory: readonly(cpuHistory), + memUsedHistory: readonly(memUsedHistory), + netRxHistory: readonly(netRxHistory), + netTxHistory: readonly(netTxHistory), + // --- 控制函数 --- + registerStatusHandlers, + unregisterAllStatusHandlers, + cleanup, }; } diff --git a/packages/frontend/src/types/server.types.ts b/packages/frontend/src/types/server.types.ts index bded8c0..19dbb2d 100644 --- a/packages/frontend/src/types/server.types.ts +++ b/packages/frontend/src/types/server.types.ts @@ -8,8 +8,13 @@ export interface ServerStatus { diskUsed?: number; // KB diskTotal?: number; // KB cpuModel?: string; - // 可以根据后端实际发送的数据添加更多字段 - // 例如:swapPercent?, swapUsed?, swapTotal?, netRxRate?, netTxRate?, netInterface?, osName?, loadAvg?, timestamp? + swapPercent?: number; + swapUsed?: number; // MB + swapTotal?: number; // MB + netRxRate?: number; // Bytes/sec + netTxRate?: number; // Bytes/sec + netInterface?: string; + osName?: string; } // 可以根据需要添加其他与服务器或连接状态相关的类型