From b862e85ea5630712c083c5850505be9c1d80a0ba Mon Sep 17 00:00:00 2001 From: Baobhan Sith <80159437+Heavrnl@users.noreply.github.com> Date: Sun, 20 Apr 2025 21:53:17 +0800 Subject: [PATCH] update --- .../backend/src/services/settings.service.ts | 51 ++++++++++++++++ .../src/services/status-monitor.service.ts | 59 ++++++++++++------- .../src/settings/settings.controller.ts | 3 +- packages/frontend/src/locales/en.json | 13 ++++ packages/frontend/src/locales/zh.json | 13 ++++ .../frontend/src/stores/settings.store.ts | 19 +++++- packages/frontend/src/views/SettingsView.vue | 43 +++++++++++++- 7 files changed, 174 insertions(+), 27 deletions(-) diff --git a/packages/backend/src/services/settings.service.ts b/packages/backend/src/services/settings.service.ts index 6c66d5e..7883d43 100644 --- a/packages/backend/src/services/settings.service.ts +++ b/packages/backend/src/services/settings.service.ts @@ -6,6 +6,8 @@ const FOCUS_SEQUENCE_KEY = 'focusSwitcherSequence'; // 焦点切换顺序设置 const NAV_BAR_VISIBLE_KEY = 'navBarVisible'; // 导航栏可见性设置键 const LAYOUT_TREE_KEY = 'layoutTree'; // 布局树设置键 const AUTO_COPY_ON_SELECT_KEY = 'autoCopyOnSelect'; // 终端选中自动复制设置键 +const STATUS_MONITOR_INTERVAL_SECONDS_KEY = 'statusMonitorIntervalSeconds'; // 状态监控间隔设置键 +const DEFAULT_STATUS_MONITOR_INTERVAL_SECONDS = 3; // 默认状态监控间隔 export const settingsService = { /** @@ -240,5 +242,54 @@ export const settingsService = { console.error(`[Service] Error calling settingsRepository.setSetting for key ${AUTO_COPY_ON_SELECT_KEY}:`, error); throw new Error('Failed to save auto copy on select setting.'); } + }, // *** 确保这里有逗号 *** + + /** + * 获取状态监控轮询间隔 (秒) + * @returns 返回间隔秒数 (number),如果未设置或无效则返回默认值 + */ + async getStatusMonitorIntervalSeconds(): Promise { + console.log(`[Service] Attempting to get setting for key: ${STATUS_MONITOR_INTERVAL_SECONDS_KEY}`); + try { + const intervalStr = await settingsRepository.getSetting(STATUS_MONITOR_INTERVAL_SECONDS_KEY); + console.log(`[Service] Raw value from repository for ${STATUS_MONITOR_INTERVAL_SECONDS_KEY}:`, intervalStr); + if (intervalStr) { + const intervalNum = parseInt(intervalStr, 10); + // 验证是否为正整数 + if (!isNaN(intervalNum) && intervalNum > 0) { + return intervalNum; + } else { + console.warn(`[Service] Invalid status monitor interval value found ('${intervalStr}'). Returning default.`); + } + } else { + console.log(`[Service] No status monitor interval found in settings. Returning default.`); + } + } catch (error) { + console.error(`[Service] Error getting status monitor interval setting (key: ${STATUS_MONITOR_INTERVAL_SECONDS_KEY}):`, error); + } + // 返回默认值 + return DEFAULT_STATUS_MONITOR_INTERVAL_SECONDS; + }, // *** 确保这里有逗号 *** + + /** + * 设置状态监控轮询间隔 (秒) + * @param interval 间隔秒数 (number) + */ + async setStatusMonitorIntervalSeconds(interval: number): Promise { + console.log(`[Service] setStatusMonitorIntervalSeconds called with: ${interval}`); + // 验证输入是否为正整数 + if (!Number.isInteger(interval) || interval <= 0) { + console.error(`[Service] Attempted to save invalid status monitor interval: ${interval}`); + throw new Error('Invalid interval value provided. Must be a positive integer.'); + } + try { + const intervalStr = String(interval); + console.log(`[Service] Attempting to save setting. Key: ${STATUS_MONITOR_INTERVAL_SECONDS_KEY}, Value: ${intervalStr}`); + await settingsRepository.setSetting(STATUS_MONITOR_INTERVAL_SECONDS_KEY, intervalStr); + console.log(`[Service] Successfully saved setting for key: ${STATUS_MONITOR_INTERVAL_SECONDS_KEY}`); + } catch (error) { + console.error(`[Service] Error calling settingsRepository.setSetting for key ${STATUS_MONITOR_INTERVAL_SECONDS_KEY}:`, error); + throw new Error('Failed to save status monitor interval setting.'); + } } // *** 最后的方法后面不需要逗号 *** }; diff --git a/packages/backend/src/services/status-monitor.service.ts b/packages/backend/src/services/status-monitor.service.ts index 9f0350c..dd0a515 100644 --- a/packages/backend/src/services/status-monitor.service.ts +++ b/packages/backend/src/services/status-monitor.service.ts @@ -1,6 +1,7 @@ import { Client } from 'ssh2'; import { WebSocket } from 'ws'; import { ClientState } from '../websocket'; // 导入统一的 ClientState +import { settingsService } from './settings.service'; // +++ 导入 settingsService +++ // 定义服务器状态的数据结构 (与前端 StatusMonitor.vue 匹配) interface ServerStatus { @@ -31,7 +32,7 @@ interface NetworkStats { } } -const DEFAULT_POLLING_INTERVAL = 1000; // 修改为 1 秒轮询间隔 (毫秒) +// const DEFAULT_POLLING_INTERVAL = 3000; // --- 移除常量,将从 settingsService 获取 --- // 用于存储上一次的网络统计信息以计算速率 const previousNetStats = new Map(); @@ -45,9 +46,9 @@ export class StatusMonitorService { /** * 启动指定会话的状态轮询 * @param sessionId 会话 ID - * @param interval 轮询间隔 (毫秒),可选,默认为 DEFAULT_POLLING_INTERVAL + * @param interval 轮询间隔 (毫秒),可选,默认为 DEFAULT_POLLING_INTERVAL // --- 参数移除 --- */ - startStatusPolling(sessionId: string, interval: number = DEFAULT_POLLING_INTERVAL): void { + async startStatusPolling(sessionId: string): Promise { // --- 改为 async, 移除 interval 参数 --- const state = this.clientStates.get(sessionId); if (!state || !state.sshClient) { //console.warn(`[StatusMonitor] 无法为会话 ${sessionId} 启动状态轮询:状态无效或 SSH 客户端不存在。`); @@ -57,11 +58,23 @@ export class StatusMonitorService { //console.warn(`[StatusMonitor] 会话 ${sessionId} 的状态轮询已在运行中。`); return; } - //console.warn(`[StatusMonitor] 为会话 ${sessionId} 启动状态轮询,间隔 ${interval}ms`); + + // +++ 从 settingsService 获取轮询间隔 +++ + let intervalMs: number; + try { + const intervalSeconds = await settingsService.getStatusMonitorIntervalSeconds(); + intervalMs = intervalSeconds * 1000; + console.log(`[StatusMonitor ${sessionId}] 使用配置的轮询间隔: ${intervalSeconds} 秒 (${intervalMs}ms)`); + } catch (error) { + console.error(`[StatusMonitor ${sessionId}] 获取轮询间隔设置失败,将使用默认值 3000ms:`, error); + intervalMs = 3000; // 出错时回退到 3 秒 + } + + //console.warn(`[StatusMonitor] 为会话 ${sessionId} 启动状态轮询,间隔 ${intervalMs}ms`); // 移除立即执行,让 setInterval 负责第一次调用,给连接更多准备时间 state.statusIntervalId = setInterval(() => { this.fetchAndSendServerStatus(sessionId); - }, interval); + }, intervalMs); // --- 使用获取到的间隔 --- } /** @@ -94,7 +107,8 @@ export class StatusMonitorService { const status = await this.fetchServerStatus(state.sshClient, sessionId); state.ws.send(JSON.stringify({ type: 'status_update', payload: { connectionId: state.dbConnectionId, status } })); } catch (error: any) { - //console.warn(`[StatusMonitor] 获取会话 ${sessionId} 服务器状态失败:`, error); + // --- 移除 console.warn --- + // console.warn(`[StatusMonitor] 获取会话 ${sessionId} 服务器状态失败:`, error); state.ws.send(JSON.stringify({ type: 'status_error', payload: { connectionId: state.dbConnectionId, message: `获取状态失败: ${error.message}` } })); } } @@ -116,7 +130,7 @@ export class StatusMonitorService { const osReleaseOutput = await this.executeSshCommand(sshClient, 'cat /etc/os-release'); const nameMatch = osReleaseOutput.match(/^PRETTY_NAME="?([^"]+)"?/m); status.osName = nameMatch ? nameMatch[1] : (osReleaseOutput.match(/^NAME="?([^"]+)"?/m)?.[1] ?? 'Unknown'); - } catch (err) { console.warn(`[StatusMonitor ${sessionId}] Failed to get OS name:`, err); } + } catch (err) { /* 静默处理 */ } // --- 移除 console.warn --- // --- CPU Model (Try /proc/cpuinfo first, fallback to lscpu) --- try { @@ -126,13 +140,13 @@ export class StatusMonitorService { cpuModelOutput = await this.executeSshCommand(sshClient, "cat /proc/cpuinfo | grep 'model name' | head -n 1"); status.cpuModel = cpuModelOutput.match(/model name\s*:\s*(.*)/i)?.[1].trim(); } catch (procErr) { - console.warn(`[StatusMonitor ${sessionId}] Failed to get CPU model from /proc/cpuinfo, trying lscpu...`, procErr); + // console.warn(`[StatusMonitor ${sessionId}] Failed to get CPU model from /proc/cpuinfo, trying lscpu...`, procErr); // --- 移除 console.warn --- // Fallback to lscpu if /proc/cpuinfo fails try { cpuModelOutput = await this.executeSshCommand(sshClient, "lscpu | grep 'Model name:'"); status.cpuModel = cpuModelOutput.match(/Model name:\s+(.*)/)?.[1].trim(); } catch (lscpuErr) { - console.warn(`[StatusMonitor ${sessionId}] Failed to get CPU model from lscpu as well:`, lscpuErr); + // console.warn(`[StatusMonitor ${sessionId}] Failed to get CPU model from lscpu as well:`, lscpuErr); // --- 移除 console.warn --- } } // If still no model found after both attempts @@ -140,7 +154,7 @@ export class StatusMonitorService { status.cpuModel = 'Unknown'; } } catch (err) { // Catch any unexpected error during the process - console.warn(`[StatusMonitor ${sessionId}] Error getting CPU model:`, err); + // console.warn(`[StatusMonitor ${sessionId}] Error getting CPU model:`, err); // --- 移除 console.warn --- status.cpuModel = 'Unknown'; } @@ -172,7 +186,7 @@ export class StatusMonitorService { } } } else { status.swapTotal = 0; status.swapUsed = 0; status.swapPercent = 0; } - } catch (err) { console.warn(`[StatusMonitor ${sessionId}] Failed to get memory/swap usage:`, err); } + } catch (err) { /* 静默处理 */ } // --- 移除 console.warn --- // --- Disk Usage (Root Partition, POSIX format for compatibility) --- try { @@ -193,7 +207,7 @@ export class StatusMonitorService { } } } - } catch (err) { console.warn(`[StatusMonitor ${sessionId}] Failed to get disk usage:`, err); } + } catch (err) { /* 静默处理 */ } // --- 移除 console.warn --- // --- CPU Usage (Simplified from top) --- try { @@ -203,14 +217,14 @@ export class StatusMonitorService { const idlePercent = parseFloat(idleMatch[1]); status.cpuPercent = parseFloat((100 - idlePercent).toFixed(1)); } - } catch (err) { console.warn(`[StatusMonitor ${sessionId}] Failed to get CPU usage from top:`, err); } + } catch (err) { /* 静默处理 */ } // --- 移除 console.warn --- // --- Load Average --- try { const uptimeOutput = await this.executeSshCommand(sshClient, 'uptime'); const match = uptimeOutput.match(/load average(?:s)?:\s*([\d.]+)[, ]?\s*([\d.]+)[, ]?\s*([\d.]+)/); if (match) status.loadAvg = [parseFloat(match[1]), parseFloat(match[2]), parseFloat(match[3])]; - } catch (err) { console.warn(`[StatusMonitor ${sessionId}] Failed to get uptime/load average:`, err); } + } catch (err) { /* 静默处理 */ } // --- 移除 console.warn --- // --- Network Rates --- try { @@ -233,9 +247,9 @@ export class StatusMonitorService { } else { status.netRxRate = 0; status.netTxRate = 0; } // First run or no time diff previousNetStats.set(sessionId, { rx: currentRx, tx: currentTx, timestamp }); - } else { console.warn(`[StatusMonitor ${sessionId}] Could not find stats for default interface ${defaultInterface}`); } + } else { /* 静默处理 */ } // --- 移除 console.warn --- } - } catch (err) { console.warn(`[StatusMonitor ${sessionId}] Failed to get network stats:`, err); } + } catch (err) { /* 静默处理 */ } // --- 移除 console.warn --- } catch (error) { console.error(`[StatusMonitor ${sessionId}] General error fetching server status:`, error); @@ -256,7 +270,7 @@ export class StatusMonitorService { output = await this.executeSshCommand(sshClient, 'cat /proc/net/dev'); } catch (error) { // 如果命令失败,记录警告并返回 null - console.warn("[StatusMonitor] Failed to execute 'cat /proc/net/dev':", error); + // console.warn("[StatusMonitor] Failed to execute 'cat /proc/net/dev':", error); // --- 移除 console.warn --- return null; } // 如果命令成功,继续解析 @@ -276,7 +290,7 @@ export class StatusMonitorService { return Object.keys(stats).length > 0 ? stats : null; } catch (parseError) { // 如果解析失败,记录错误并返回 null - console.error("[StatusMonitor] Error parsing /proc/net/dev output:", parseError); + // console.error("[StatusMonitor] Error parsing /proc/net/dev output:", parseError); // --- 移除 console.error --- return null; } } @@ -293,10 +307,10 @@ export class StatusMonitorService { const interfaceName = output.trim(); if (interfaceName) return interfaceName; // 如果 ip route 没返回有效接口名,也尝试 fallback - console.warn("[StatusMonitor] 'ip route' did not return a valid interface name. Falling back..."); + // console.warn("[StatusMonitor] 'ip route' did not return a valid interface name. Falling back..."); // --- 移除 console.warn --- } catch (error) { - console.warn("[StatusMonitor] Failed to get default interface using 'ip route', falling back:", error); + // console.warn("[StatusMonitor] Failed to get default interface using 'ip route', falling back:", error); // --- 移除 console.warn --- // Fallback: 尝试查找第一个非 lo 接口 try { const netDevOutput = await this.executeSshCommand(sshClient, 'cat /proc/net/dev'); @@ -308,7 +322,7 @@ export class StatusMonitorService { } } } catch (fallbackError) { - console.error("[StatusMonitor] Failed to fallback to /proc/net/dev for interface:", fallbackError); + // console.error("[StatusMonitor] Failed to fallback to /proc/net/dev for interface:", fallbackError); // --- 移除 console.error --- } // Ensure null is returned if both primary and fallback fail within the outer catch return null; @@ -341,7 +355,8 @@ export class StatusMonitorService { }).on('data', (data: Buffer) => { output += data.toString('utf8'); }).stderr.on('data', (data: Buffer) => { - //console.warn(`[StatusMonitor] Command '${command}' stderr: ${data.toString('utf8').trim()}`); + // --- 移除 console.warn --- + // console.warn(`[StatusMonitor] Command '${command}' stderr: ${data.toString('utf8').trim()}`); }); }); }); diff --git a/packages/backend/src/settings/settings.controller.ts b/packages/backend/src/settings/settings.controller.ts index a57b68b..db863fd 100644 --- a/packages/backend/src/settings/settings.controller.ts +++ b/packages/backend/src/settings/settings.controller.ts @@ -33,7 +33,8 @@ export const settingsController = { const allowedSettingsKeys = [ 'language', 'ipWhitelist', 'maxLoginAttempts', 'loginBanDuration', 'showPopupFileEditor', 'shareFileEditorTabs', 'ipWhitelistEnabled', - 'autoCopyOnSelect', 'dockerStatusIntervalSeconds', 'dockerDefaultExpand' // +++ 添加 Docker 设置键 +++ + 'autoCopyOnSelect', 'dockerStatusIntervalSeconds', 'dockerDefaultExpand', + 'statusMonitorIntervalSeconds' // +++ 添加状态监控间隔键 +++ ]; const filteredSettings: Record = {}; for (const key in settingsToUpdate) { diff --git a/packages/frontend/src/locales/en.json b/packages/frontend/src/locales/en.json index 071f033..d7c4c52 100644 --- a/packages/frontend/src/locales/en.json +++ b/packages/frontend/src/locales/en.json @@ -643,6 +643,19 @@ "saveFailed": "Failed to save Docker settings.", "invalidInterval": "Refresh interval must be a positive integer." } + }, + "statusMonitor": { + "title": "Status Monitor Settings", + "refreshIntervalLabel": "Status Refresh Interval (seconds):", + "refreshIntervalHint": "How often to fetch server CPU, memory, disk, etc. status (minimum 1).", + "saveButton": "Save Status Monitor Settings", + "success": { + "saved": "Status monitor settings saved successfully." + }, + "error": { + "saveFailed": "Failed to save status monitor settings.", + "invalidInterval": "Refresh interval must be a positive integer." + } } }, "common": { diff --git a/packages/frontend/src/locales/zh.json b/packages/frontend/src/locales/zh.json index 346cd9b..1d106f6 100644 --- a/packages/frontend/src/locales/zh.json +++ b/packages/frontend/src/locales/zh.json @@ -643,6 +643,19 @@ "saveFailed": "保存 Docker 设置失败。", "invalidInterval": "刷新间隔必须是正整数。" } + }, + "statusMonitor": { + "title": "状态监控设置", + "refreshIntervalLabel": "状态刷新间隔 (秒):", + "refreshIntervalHint": "获取服务器 CPU、内存、磁盘等状态的频率(最小为 1)。", + "saveButton": "保存状态监控设置", + "success": { + "saved": "状态监控设置已成功保存。" + }, + "error": { + "saveFailed": "保存状态监控设置失败。", + "invalidInterval": "刷新间隔必须是正整数。" + } } }, "common": { diff --git a/packages/frontend/src/stores/settings.store.ts b/packages/frontend/src/stores/settings.store.ts index 5ff9c2a..1f94557 100644 --- a/packages/frontend/src/stores/settings.store.ts +++ b/packages/frontend/src/stores/settings.store.ts @@ -16,6 +16,7 @@ interface SettingsState { autoCopyOnSelect?: string; // 'true' or 'false' - 终端选中自动复制 dockerStatusIntervalSeconds?: string; // NEW: Docker 状态刷新间隔 (秒) dockerDefaultExpand?: string; // NEW: Docker 默认展开详情 'true' or 'false' + statusMonitorIntervalSeconds?: string; // NEW: 状态监控轮询间隔 (秒) // Add other general settings keys here as needed [key: string]: string | undefined; // Allow other string settings } @@ -74,7 +75,10 @@ export const useSettingsStore = defineStore('settings', () => { if (settings.value.dockerDefaultExpand === undefined) { settings.value.dockerDefaultExpand = 'false'; // 默认不展开 } - + // NEW: Status Monitor interval default + if (settings.value.statusMonitorIntervalSeconds === undefined) { + settings.value.statusMonitorIntervalSeconds = '3'; // 默认 3 秒 + } // --- 语言设置 --- const langFromSettings = settings.value.language; @@ -124,7 +128,8 @@ export const useSettingsStore = defineStore('settings', () => { const allowedKeys: Array = [ 'language', 'ipWhitelist', 'maxLoginAttempts', 'loginBanDuration', 'showPopupFileEditor', 'shareFileEditorTabs', 'ipWhitelistEnabled', - 'autoCopyOnSelect', 'dockerStatusIntervalSeconds', 'dockerDefaultExpand' // +++ 添加 Docker 设置键 +++ + 'autoCopyOnSelect', 'dockerStatusIntervalSeconds', 'dockerDefaultExpand', + 'statusMonitorIntervalSeconds' // +++ 添加状态监控间隔键 +++ ]; if (!allowedKeys.includes(key)) { console.error(`[SettingsStore] 尝试更新不允许的设置键: ${key}`); @@ -157,7 +162,8 @@ export const useSettingsStore = defineStore('settings', () => { const allowedKeys: Array = [ 'language', 'ipWhitelist', 'maxLoginAttempts', 'loginBanDuration', 'showPopupFileEditor', 'shareFileEditorTabs', 'ipWhitelistEnabled', - 'autoCopyOnSelect', 'dockerStatusIntervalSeconds', 'dockerDefaultExpand' // +++ 添加 Docker 设置键 +++ + 'autoCopyOnSelect', 'dockerStatusIntervalSeconds', 'dockerDefaultExpand', + 'statusMonitorIntervalSeconds' // +++ 添加状态监控间隔键 +++ ]; const filteredUpdates: Partial = {}; let languageUpdate: 'en' | 'zh' | undefined = undefined; @@ -224,6 +230,12 @@ export const useSettingsStore = defineStore('settings', () => { return settings.value.dockerDefaultExpand === 'true'; }); + // NEW: Getter for Status Monitor interval, returning number + const statusMonitorIntervalSecondsNumber = computed(() => { + const val = parseInt(settings.value.statusMonitorIntervalSeconds || '3', 10); + return isNaN(val) || val <= 0 ? 3 : val; // Fallback to 3 if invalid + }); + return { settings, // 只包含通用设置 isLoading, @@ -234,6 +246,7 @@ export const useSettingsStore = defineStore('settings', () => { ipWhitelistEnabled, // 暴露 IP 白名单启用状态 autoCopyOnSelectBoolean, dockerDefaultExpandBoolean, // +++ 暴露 Docker 默认展开 getter +++ + statusMonitorIntervalSecondsNumber, // +++ 暴露状态监控间隔 getter +++ // 移除外观相关的 getters 和 actions loadInitialSettings, updateSetting, diff --git a/packages/frontend/src/views/SettingsView.vue b/packages/frontend/src/views/SettingsView.vue index 2762b59..dbe76ce 100644 --- a/packages/frontend/src/views/SettingsView.vue +++ b/packages/frontend/src/views/SettingsView.vue @@ -156,6 +156,21 @@ + +
+

{{ t('settings.statusMonitor.title') }}

+
+
+ + + {{ t('settings.statusMonitor.refreshIntervalHint') }} +
+ +

{{ statusMonitorMessage }}

+
+
+ +

{{ $t('settings.ipWhitelist.title') }}

{{ $t('settings.ipWhitelist.description') }}

@@ -252,7 +267,7 @@ const { t } = useI18n(); // --- Reactive state from store --- // 使用 storeToRefs 获取响应式 getter,包括 language -const { settings, isLoading: settingsLoading, error: settingsError, showPopupFileEditorBoolean, shareFileEditorTabsBoolean, autoCopyOnSelectBoolean, dockerDefaultExpandBoolean, language: storeLanguage } = storeToRefs(settingsStore); // +++ 添加 dockerDefaultExpandBoolean 和 language getter +++ +const { settings, isLoading: settingsLoading, error: settingsError, showPopupFileEditorBoolean, shareFileEditorTabsBoolean, autoCopyOnSelectBoolean, dockerDefaultExpandBoolean, statusMonitorIntervalSecondsNumber, language: storeLanguage } = storeToRefs(settingsStore); // +++ 添加 statusMonitorIntervalSecondsNumber getter +++ // --- Local state for forms --- const ipWhitelistInput = ref(''); @@ -290,6 +305,10 @@ const dockerExpandDefault = ref(false); // 本地状态,用于 Docker 默认 const dockerSettingsLoading = ref(false); const dockerSettingsMessage = ref(''); const dockerSettingsSuccess = ref(false); +const statusMonitorIntervalLocal = ref(3); // 本地状态,用于状态监控间隔 v-model +const statusMonitorLoading = ref(false); +const statusMonitorMessage = ref(''); +const statusMonitorSuccess = ref(false); // --- Watcher to sync local form state with store state --- @@ -308,6 +327,7 @@ watch(settings, (newSettings, oldSettings) => { autoCopyEnabled.value = autoCopyOnSelectBoolean.value; // 同步选中即复制状态 dockerInterval.value = parseInt(newSettings.dockerStatusIntervalSeconds || '2', 10); // 同步 Docker 间隔 dockerExpandDefault.value = dockerDefaultExpandBoolean.value; // 同步 Docker 默认展开状态 + statusMonitorIntervalLocal.value = statusMonitorIntervalSecondsNumber.value; // 同步状态监控间隔 }, { deep: true, immediate: true }); // immediate: true to run on initial load @@ -398,6 +418,27 @@ const handleUpdateDockerSettings = async () => { } }; +// --- Status Monitor interval setting method --- +const handleUpdateStatusMonitorInterval = async () => { + statusMonitorLoading.value = true; + statusMonitorMessage.value = ''; + statusMonitorSuccess.value = false; + try { + const intervalValue = statusMonitorIntervalLocal.value; + if (isNaN(intervalValue) || intervalValue < 1 || !Number.isInteger(intervalValue)) { + throw new Error(t('settings.statusMonitor.error.invalidInterval')); // 需要添加翻译 + } + await settingsStore.updateSetting('statusMonitorIntervalSeconds', String(intervalValue)); // 保存为字符串 + statusMonitorMessage.value = t('settings.statusMonitor.success.saved'); // 需要添加翻译 + statusMonitorSuccess.value = true; + } catch (error: any) { + console.error('更新状态监控间隔失败:', error); + statusMonitorMessage.value = error.message || t('settings.statusMonitor.error.saveFailed'); // 需要添加翻译 + statusMonitorSuccess.value = false; + } finally { + statusMonitorLoading.value = false; + } +}; // --- 外观设置 --- const openStyleCustomizer = () => {