/** * 仪表盘功能模块 * 提供主题切换、数据统计、系统状态监控等功能 */ class DashboardManager { constructor() { this.refreshInterval = null; this.timeInterval = null; this.activities = []; this.lastDataSnapshot = null; this.init(); } /** * 初始化仪表盘 */ init() { console.log('初始化仪表盘管理器...'); // 初始化主题切换器 this.initializeThemeSwitcher(); // 更新当前时间 this.updateCurrentTime(); this.timeInterval = setInterval(() => this.updateCurrentTime(), 1000); // 加载仪表盘数据 this.loadDashboardData(); // 绑定仪表盘事件 this.bindEvents(); // 添加活动记录 this.addActivity('仪表盘初始化成功', 'success'); // 设置自动刷新(30秒) this.refreshInterval = setInterval(() => this.loadDashboardData(), 30000); } /** * 主题切换器初始化 */ initializeThemeSwitcher() { const themeToggle = document.getElementById('theme-toggle'); if (!themeToggle) { console.warn('主题切换器元素未找到'); return; } // 获取保存的主题或使用默认主题 const currentTheme = localStorage.getItem('theme') || 'light'; console.log('当前主题:', currentTheme); // 应用主题 this.applyTheme(currentTheme); // 设置切换器状态 themeToggle.checked = currentTheme === 'dark'; // 监听主题切换事件 themeToggle.addEventListener('change', (event) => { const newTheme = event.target.checked ? 'dark' : 'light'; console.log('切换主题到:', newTheme); this.applyTheme(newTheme); localStorage.setItem('theme', newTheme); // 添加活动记录 this.addActivity(`切换到${newTheme === 'dark' ? '暗色' : '明亮'}主题`, 'info'); }); console.log('主题切换器初始化完成'); } /** * 应用主题 */ applyTheme(theme) { document.documentElement.setAttribute('data-theme', theme); // 更新元标签以支持系统主题 let themeColorMeta = document.querySelector('meta[name="theme-color"]'); if (!themeColorMeta) { themeColorMeta = document.createElement('meta'); themeColorMeta.name = 'theme-color'; document.head.appendChild(themeColorMeta); } // 根据主题设置元标签颜色 if (theme === 'dark') { themeColorMeta.content = '#1e293b'; } else { themeColorMeta.content = '#ffffff'; } console.log(`主题 ${theme} 已应用`); } /** * 更新当前时间 */ updateCurrentTime() { const now = new Date(); const timeString = now.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' }); const currentTimeElement = document.getElementById('current-time'); if (currentTimeElement) { currentTimeElement.textContent = timeString; } } /** * 加载仪表盘数据 */ async loadDashboardData() { try { console.log('开始加载仪表盘数据...'); // 显示加载状态 this.setLoadingState(true); // 并行加载各种数据 const [devices, topology, credentials, activeConnections, terminalSessions] = await Promise.all([ this.fetchWithFallback('/api/devices', []), this.fetchWithFallback('/api/topology', { nodes: {}, links: [] }), this.fetchWithFallback('/api/credentials', []), this.getActiveSessions(), this.getTerminalSessions() ]); console.log('数据加载完成:', { devices: devices.length, topology: Object.keys(topology.nodes || {}).length, credentials: credentials.length, activeConnections: activeConnections.length, terminalSessions: terminalSessions.length }); // 检测数据变化 const currentSnapshot = { devicesCount: devices.length, topologyCount: Object.keys(topology.nodes || {}).length, credentialsCount: credentials.length, connectionsCount: activeConnections.length, sessionsCount: terminalSessions.length }; // 如果有数据变化,记录活动 if (this.lastDataSnapshot) { this.detectDataChanges(this.lastDataSnapshot, currentSnapshot); } this.lastDataSnapshot = currentSnapshot; // 更新统计数据 this.updateDashboardStats(devices, topology, credentials, activeConnections, terminalSessions); // 更新设备概览 this.updateDevicesOverview(devices); // 检查系统状态 await this.checkSystemStatus(); } catch (error) { console.error('加载仪表盘数据失败:', error); this.addActivity('加载仪表盘数据失败', 'error'); } finally { this.setLoadingState(false); } } /** * 检测数据变化 */ detectDataChanges(oldSnapshot, newSnapshot) { if (newSnapshot.devicesCount !== oldSnapshot.devicesCount) { const change = newSnapshot.devicesCount - oldSnapshot.devicesCount; this.addActivity(`设备数量${change > 0 ? '增加' : '减少'}${Math.abs(change)}个`, change > 0 ? 'success' : 'warning'); } if (newSnapshot.topologyCount !== oldSnapshot.topologyCount) { const change = newSnapshot.topologyCount - oldSnapshot.topologyCount; this.addActivity(`拓扑节点${change > 0 ? '增加' : '减少'}${Math.abs(change)}个`, 'info'); } if (newSnapshot.credentialsCount !== oldSnapshot.credentialsCount) { const change = newSnapshot.credentialsCount - oldSnapshot.credentialsCount; this.addActivity(`凭据${change > 0 ? '增加' : '减少'}${Math.abs(change)}个`, 'info'); } if (newSnapshot.connectionsCount !== oldSnapshot.connectionsCount) { const change = newSnapshot.connectionsCount - oldSnapshot.connectionsCount; this.addActivity(`活跃连接${change > 0 ? '增加' : '减少'}${Math.abs(change)}个`, change > 0 ? 'success' : 'info'); } if (newSnapshot.sessionsCount !== oldSnapshot.sessionsCount) { const change = newSnapshot.sessionsCount - oldSnapshot.sessionsCount; this.addActivity(`终端会话${change > 0 ? '增加' : '减少'}${Math.abs(change)}个`, change > 0 ? 'success' : 'info'); } } /** * 获取活跃会话数 */ async getActiveSessions() { try { // 首先尝试从后端API获取活跃连接 const response = await fetch('/api/connections/active'); if (response.ok) { const connections = await response.json(); return connections || []; } } catch (error) { console.warn('无法从API获取活跃连接:', error); } // 从全局会话管理器获取活跃会话 if (window.sessionManager && window.sessionManager.activeSessions) { return Object.keys(window.sessionManager.activeSessions); } // 从终端管理器获取 if (window.terminalManager && window.terminalManager.terminals) { return Object.keys(window.terminalManager.terminals); } return []; } /** * 获取真正的终端会话数 */ async getTerminalSessions() { try { // 尝试从终端管理器获取 if (window.terminalManager && window.terminalManager.terminals) { return Object.keys(window.terminalManager.terminals); } // 尝试从会话管理器获取 if (window.sessionManager && window.sessionManager.activeSessions) { return Object.keys(window.sessionManager.activeSessions); } // 尝试从DOM获取终端标签页数量 const terminalTabs = document.querySelectorAll('.terminal-tab'); if (terminalTabs.length > 0) { return Array.from(terminalTabs).map((tab, index) => `terminal-${index}`); } return []; } catch (error) { console.warn('获取终端会话失败:', error); return []; } } /** * 带回退的网络请求 */ async fetchWithFallback(url, fallback) { try { const response = await fetch(url); if (response.ok) { const data = await response.json(); console.log(`API ${url} 请求成功:`, data); return data; } else { console.warn(`API ${url} 请求失败 (${response.status}):`, response.statusText); return fallback; } } catch (error) { console.warn(`API ${url} 请求异常:`, error); return fallback; } } /** * 设置加载状态 */ setLoadingState(loading) { const refreshBtn = document.getElementById('refresh-dashboard'); if (refreshBtn) { if (loading) { refreshBtn.innerHTML = ' 加载中'; refreshBtn.disabled = true; } else { refreshBtn.innerHTML = ' 刷新'; refreshBtn.disabled = false; } } } /** * 更新仪表盘统计数据 */ updateDashboardStats(devices, topology, credentials, activeConnections, terminalSessions) { // 设备数量 const devicesCount = devices.length; this.updateStatCard('dashboard-devices-count', devicesCount); this.updateStatChange('dashboard-devices-change', `+${devicesCount}`); // 活跃连接数(会话数) const connectionsCount = activeConnections.length; this.updateStatCard('dashboard-connections-count', connectionsCount); this.updateStatChange('dashboard-connections-change', connectionsCount > 0 ? `+${connectionsCount}` : '0'); // 拓扑节点数 const topologyNodes = topology.nodes ? Object.keys(topology.nodes).length : 0; this.updateStatCard('dashboard-topology-nodes', topologyNodes); this.updateStatChange('dashboard-topology-change', `+${topologyNodes}`); // 终端会话数(修正:之前错误地显示凭据数量) const sessionsCount = terminalSessions.length; this.updateStatCard('dashboard-sessions-count', sessionsCount); this.updateStatChange('dashboard-sessions-change', `+${sessionsCount}`); } /** * 更新统计卡片的数值 */ updateStatCard(elementId, value) { const element = document.getElementById(elementId); if (element) { const currentValue = parseInt(element.textContent) || 0; const targetValue = parseInt(value) || 0; if (currentValue !== targetValue) { this.animateNumber(element, currentValue, targetValue, 500); } } } /** * 更新统计变化指示器 */ updateStatChange(elementId, value) { const element = document.getElementById(elementId); if (element) { element.textContent = value; // 根据值设置样式 element.className = 'stat-change'; if (value.startsWith('+') && value !== '+0') { element.classList.add('positive'); } else if (value.startsWith('-')) { element.classList.add('negative'); } else { element.classList.add('neutral'); } } } /** * 数字动画效果 */ animateNumber(element, start, end, duration) { const startTime = performance.now(); const updateNumber = (currentTime) => { const elapsed = currentTime - startTime; const progress = Math.min(elapsed / duration, 1); // 使用缓动函数 const easedProgress = 1 - Math.pow(1 - progress, 3); const current = Math.round(start + (end - start) * easedProgress); element.textContent = current; if (progress < 1) { requestAnimationFrame(updateNumber); } }; requestAnimationFrame(updateNumber); } /** * 更新设备概览 */ updateDevicesOverview(devices) { const devicesOverview = document.getElementById('devices-overview'); if (!devicesOverview) return; if (!devices || devices.length === 0) { devicesOverview.innerHTML = `
暂无设备