update
This commit is contained in:
@@ -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<number> {
|
||||
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<void> {
|
||||
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.');
|
||||
}
|
||||
} // *** 最后的方法后面不需要逗号 ***
|
||||
};
|
||||
|
||||
@@ -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<string, { rx: number, tx: number, timestamp: number }>();
|
||||
|
||||
@@ -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<void> { // --- 改为 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,6 +107,7 @@ 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 ---
|
||||
// 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,6 +355,7 @@ export class StatusMonitorService {
|
||||
}).on('data', (data: Buffer) => {
|
||||
output += data.toString('utf8');
|
||||
}).stderr.on('data', (data: Buffer) => {
|
||||
// --- 移除 console.warn ---
|
||||
// console.warn(`[StatusMonitor] Command '${command}' stderr: ${data.toString('utf8').trim()}`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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<string, string> = {};
|
||||
for (const key in settingsToUpdate) {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -643,6 +643,19 @@
|
||||
"saveFailed": "保存 Docker 设置失败。",
|
||||
"invalidInterval": "刷新间隔必须是正整数。"
|
||||
}
|
||||
},
|
||||
"statusMonitor": {
|
||||
"title": "状态监控设置",
|
||||
"refreshIntervalLabel": "状态刷新间隔 (秒):",
|
||||
"refreshIntervalHint": "获取服务器 CPU、内存、磁盘等状态的频率(最小为 1)。",
|
||||
"saveButton": "保存状态监控设置",
|
||||
"success": {
|
||||
"saved": "状态监控设置已成功保存。"
|
||||
},
|
||||
"error": {
|
||||
"saveFailed": "保存状态监控设置失败。",
|
||||
"invalidInterval": "刷新间隔必须是正整数。"
|
||||
}
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
|
||||
@@ -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<keyof SettingsState> = [
|
||||
'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<keyof SettingsState> = [
|
||||
'language', 'ipWhitelist', 'maxLoginAttempts', 'loginBanDuration',
|
||||
'showPopupFileEditor', 'shareFileEditorTabs', 'ipWhitelistEnabled',
|
||||
'autoCopyOnSelect', 'dockerStatusIntervalSeconds', 'dockerDefaultExpand' // +++ 添加 Docker 设置键 +++
|
||||
'autoCopyOnSelect', 'dockerStatusIntervalSeconds', 'dockerDefaultExpand',
|
||||
'statusMonitorIntervalSeconds' // +++ 添加状态监控间隔键 +++
|
||||
];
|
||||
const filteredUpdates: Partial<SettingsState> = {};
|
||||
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,
|
||||
|
||||
@@ -156,6 +156,21 @@
|
||||
</div>
|
||||
<!-- END: Docker Settings Section -->
|
||||
|
||||
<!-- NEW: Status Monitor Settings Section -->
|
||||
<div class="settings-section">
|
||||
<h2>{{ t('settings.statusMonitor.title') }}</h2>
|
||||
<form @submit.prevent="handleUpdateStatusMonitorInterval">
|
||||
<div class="form-group">
|
||||
<label for="statusMonitorInterval">{{ t('settings.statusMonitor.refreshIntervalLabel') }}</label>
|
||||
<input type="number" id="statusMonitorInterval" v-model.number="statusMonitorIntervalLocal" min="1" step="1" required>
|
||||
<small>{{ t('settings.statusMonitor.refreshIntervalHint') }}</small>
|
||||
</div>
|
||||
<button type="submit" :disabled="statusMonitorLoading">{{ statusMonitorLoading ? $t('common.saving') : t('settings.statusMonitor.saveButton') }}</button>
|
||||
<p v-if="statusMonitorMessage" :class="{ 'success-message': statusMonitorSuccess, 'error-message': !statusMonitorSuccess }">{{ statusMonitorMessage }}</p>
|
||||
</form>
|
||||
</div>
|
||||
<!-- END: Status Monitor Settings Section -->
|
||||
|
||||
<div class="settings-section">
|
||||
<h2>{{ $t('settings.ipWhitelist.title') }}</h2>
|
||||
<p>{{ $t('settings.ipWhitelist.description') }}</p>
|
||||
@@ -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 = () => {
|
||||
|
||||
Reference in New Issue
Block a user