// API 基础 URL const API_BASE_URL = '/api'; // 全局状态 let state = { currentTimeRange: '1h', // 与UI默认值保持一致 customStartTime: '', customEndTime: '', currentInterval: '10m' // 固定10分钟区间 } // WebSocket连接 let ws = null; let wsReconnectAttempts = 0; const wsMaxReconnectAttempts = 5; let wsReconnectDelay = 1000; // 图表实例 const charts = {}; // 初始化应用 function initApp() { initCustomTimeRange(); bindEvents(); initPageSwitch(); loadHomeData(); initCharts(); initWebSocket(); // 设置定时刷新 setInterval(loadMetrics, 30000); setInterval(loadServerCount, 30000); } // 初始化自定义时间范围 function initCustomTimeRange() { const now = new Date(); // 默认显示过去24小时 const twentyFourHoursAgo = new Date(now.getTime() - 24 * 60 * 60 * 1000); // 直接使用ISO字符串,包含完整的时区信息 state.customStartTime = twentyFourHoursAgo.toISOString(); state.customEndTime = now.toISOString(); } // 页面切换 function initPageSwitch() { window.addEventListener('hashchange', switchPage); switchPage(); } function switchPage() { const hash = window.location.hash; // 隐藏所有内容 hideAllContent(); // 显示对应内容 if (hash === '#servers') { showContent('serversContent'); loadAllServers(); } else if (hash === '#devices') { showContent('devicesContent'); loadDeviceManagementList(); } else if (hash === '#serverMonitor' || hash.startsWith('#serverMonitor/')) { showContent('serverMonitorContent'); // 提取设备ID let deviceId = ''; if (hash.startsWith('#serverMonitor/')) { deviceId = hash.split('/')[1]; } // 加载服务器信息 if (deviceId) { loadServerInfo(deviceId); } loadMetrics(); } else { showContent('homeContent'); loadHomeData(); } } function hideAllContent() { document.getElementById('homeContent').classList.add('hidden'); document.getElementById('serversContent').classList.add('hidden'); document.getElementById('serverMonitorContent').classList.add('hidden'); document.getElementById('devicesContent').classList.add('hidden'); } function showContent(contentId) { document.getElementById(contentId).classList.remove('hidden'); } // 加载首页数据 async function loadHomeData() { try { // 加载业务视图数据 await loadBusinessViewData(); // 加载告警列表数据 loadAlarmListData(); // 加载服务器数量 loadServerCount(); } catch (error) { console.error('加载首页数据失败:', error); } } // 加载业务视图数据 async function loadBusinessViewData() { try { const response = await fetch(`${API_BASE_URL}/devices/`); const data = await response.json(); let devices = data.devices || []; // 如果没有设备,使用模拟数据 if (devices.length === 0) { devices = [ { id: 'device-1', name: '服务器1', ip: '192.168.1.100' }, { id: 'device-2', name: '服务器2', ip: '192.168.1.101' } ]; } renderBusinessView(devices); } catch (error) { console.error('加载业务视图数据失败:', error); // 使用模拟数据 const mockDevices = [ { id: 'device-1', name: 'LIS', ip: '192.129.6.108', os: 'Linux', status: 'P1' }, { id: 'device-2', name: '互联网医院', ip: '192.129.6.57', os: 'Linux', status: 'P3' }, { id: 'device-3', name: 'HIS', ip: '192.129.51.21', os: 'Windows', status: 'P2' }, { id: 'device-4', name: 'OA', ip: '192.129.6.42', os: 'Linux', status: 'P2' } ]; renderBusinessView(mockDevices); } } // 渲染业务视图 function renderBusinessView(devices) { const tableBody = document.getElementById('businessViewTableBody'); if (!tableBody) return; tableBody.innerHTML = ''; devices.forEach(device => { const row = document.createElement('tr'); row.className = 'hover:bg-gray-50 transition-colors'; row.innerHTML = `
${alarm.message}
${alarm.device}
${device.ip || 'N/A'}
CPU使用率
0.0%
内存使用率
0.0%
磁盘使用率
0.0%
网络流量
0.0 MB/s
服务器名称: ${deviceData.name || deviceId} | IP地址: ${deviceData.ip || '未知'}
`; } } catch (error) { console.error('Failed to load server info:', error); // 使用模拟数据 const serverInfoDisplay = document.getElementById('serverInfoDisplay'); if (serverInfoDisplay) { serverInfoDisplay.innerHTML = `服务器名称: 服务器 ${deviceId} | IP地址: 192.168.1.${deviceId}
`; } } } // 处理指标更新 function handleMetricsUpdate(message) { const { device_id, metrics } = message; // 直接更新,不再检查device_id updateStatusCards(metrics); // 立即刷新数据以确保图表也更新 setTimeout(() => loadMetrics(), 500); } // 更新状态卡片 function updateStatusCards(metrics) { // 更新CPU状态卡片 if (metrics.cpu) { if (Array.isArray(metrics.cpu) && metrics.cpu.length > 0) { const latestCPU = metrics.cpu[metrics.cpu.length - 1].value; const cpuElement = document.getElementById('cpuValue'); if (cpuElement) { cpuElement.textContent = `${latestCPU.toFixed(1)}%`; } } else if (typeof metrics.cpu === 'number') { const cpuElement = document.getElementById('cpuValue'); if (cpuElement) { cpuElement.textContent = `${metrics.cpu.toFixed(1)}%`; } } } // 更新内存状态卡片 if (metrics.memory) { if (Array.isArray(metrics.memory) && metrics.memory.length > 0) { const latestMemory = metrics.memory[metrics.memory.length - 1].value; const memoryElement = document.getElementById('memoryValue'); if (memoryElement) { memoryElement.textContent = `${latestMemory.toFixed(1)}%`; } } else if (typeof metrics.memory === 'number') { const memoryElement = document.getElementById('memoryValue'); if (memoryElement) { memoryElement.textContent = `${metrics.memory.toFixed(1)}%`; } } } // 更新磁盘状态卡片 if (metrics.disk) { const diskElement = document.getElementById('diskValue'); if (diskElement) { if (typeof metrics.disk === 'object' && metrics.disk !== null && !Array.isArray(metrics.disk)) { // 计算所有挂载点的平均使用率 let totalUsage = 0; let mountpointCount = 0; for (const mountpoint in metrics.disk) { const data = metrics.disk[mountpoint]; if (data && Array.isArray(data) && data.length > 0) { const latestValue = data[data.length - 1].value; totalUsage += latestValue; mountpointCount++; } else if (typeof data === 'number') { totalUsage += data; mountpointCount++; } } if (mountpointCount > 0) { const averageUsage = totalUsage / mountpointCount; diskElement.textContent = `${averageUsage.toFixed(1)}%`; } } else if (Array.isArray(metrics.disk) && metrics.disk.length > 0) { const latestDisk = metrics.disk[metrics.disk.length - 1].value; diskElement.textContent = `${latestDisk.toFixed(1)}%`; } else if (typeof metrics.disk === 'number') { diskElement.textContent = `${metrics.disk.toFixed(1)}%`; } } } // 更新网络状态卡片 if (metrics.network) { const networkValueElement = document.getElementById('networkValue'); const networkSentElement = document.getElementById('networkSent'); const networkReceivedElement = document.getElementById('networkReceived'); // 处理不同格式的网络数据 if (metrics.network.sent && metrics.network.received) { // 处理数组格式的数据 if (Array.isArray(metrics.network.sent) && metrics.network.sent.length > 0 && Array.isArray(metrics.network.received) && metrics.network.received.length > 0) { const latestSent = metrics.network.sent[metrics.network.sent.length - 1].value; const latestReceived = metrics.network.received[metrics.network.received.length - 1].value; if (networkValueElement) { // 显示较大的值 const maxValue = Math.max(latestSent, latestReceived); networkValueElement.textContent = formatBytes(maxValue, 2, true); // 显示速率 } if (networkSentElement) { networkSentElement.textContent = formatBytes(latestSent, 2, true); } if (networkReceivedElement) { networkReceivedElement.textContent = formatBytes(latestReceived, 2, true); } } // 处理数值格式的数据 else if (typeof metrics.network.sent === 'number' && typeof metrics.network.received === 'number') { if (networkValueElement) { const maxValue = Math.max(metrics.network.sent, metrics.network.received); networkValueElement.textContent = formatBytes(maxValue, 2, true); } if (networkSentElement) { networkSentElement.textContent = formatBytes(metrics.network.sent, 2, true); } if (networkReceivedElement) { networkReceivedElement.textContent = formatBytes(metrics.network.received, 2, true); } } } else if (typeof metrics.network === 'object' && metrics.network.bytes_sent !== undefined && metrics.network.bytes_received !== undefined) { // WebSocket消息格式 if (networkValueElement) { const maxValue = Math.max(metrics.network.bytes_sent, metrics.network.bytes_received); networkValueElement.textContent = formatBytes(maxValue, 2, true); } if (networkSentElement) { networkSentElement.textContent = formatBytes(metrics.network.bytes_sent, 2, true); } if (networkReceivedElement) { networkReceivedElement.textContent = formatBytes(metrics.network.bytes_received, 2, true); } } } } // 更新图表数据 function updateCharts(cpuData, memoryData, diskData, networkData) { // 数据点排序函数 const sortDataByTime = (data) => { return [...data].sort((a, b) => { return new Date(a.time) - new Date(b.time); }); }; // 计算固定份数X轴数据 const getFixedPointsData = (data) => { if (!Array.isArray(data) || data.length === 0) { return []; } // 根据时间范围计算需要的份数(每份10分钟) let expectedPoints = 6; // 默认1小时,6份 let timeRange = state.currentTimeRange || '1h'; // 如果使用了自定义时间,检查是否是24小时范围 if (state.customStartTime && state.customEndTime) { const startTime = new Date(state.customStartTime); const endTime = new Date(state.customEndTime); const durationHours = (endTime - startTime) / (1000 * 60 * 60); // 根据实际时长设置预期点数 if (durationHours <= 0.5) { timeRange = '30m'; } else if (durationHours <= 1) { timeRange = '1h'; } else if (durationHours <= 2) { timeRange = '2h'; } else if (durationHours <= 6) { timeRange = '6h'; } else if (durationHours <= 12) { timeRange = '12h'; } else { timeRange = '24h'; } } switch(timeRange) { case '30m': expectedPoints = 3; // 30分钟,3份 break; case '1h': expectedPoints = 6; // 1小时,6份 break; case '2h': expectedPoints = 12; // 2小时,12份 break; case '6h': expectedPoints = 36; // 6小时,36份 break; case '12h': expectedPoints = 72; // 12小时,72份 break; case '24h': expectedPoints = 144; // 24小时,144份 break; } // 排序数据 const sortedData = sortDataByTime(data); // 如果数据点足够,直接返回 if (sortedData.length <= expectedPoints) { return sortedData; } // 计算采样步长 const step = Math.ceil(sortedData.length / expectedPoints); const sampled = []; // 采样数据,确保得到期望的份数 for (let i = 0; i < sortedData.length; i += step) { sampled.push(sortedData[i]); if (sampled.length >= expectedPoints) { break; } } // 确保包含最后一个数据点 if (sampled.length < expectedPoints && sortedData.length > 0) { // 如果采样点不够,从末尾补充 const remaining = expectedPoints - sampled.length; for (let i = 1; i <= remaining; i++) { const index = Math.max(0, sortedData.length - i); if (!sampled.some(item => item.time === sortedData[index].time)) { sampled.push(sortedData[index]); } } } // 再次排序,确保时间顺序 return sortDataByTime(sampled); }; // 更新CPU图表 if (cpuData && Array.isArray(cpuData) && cpuData.length > 0 && charts.cpu) { const fixedData = getFixedPointsData(cpuData); charts.cpu.data.datasets[0].data = fixedData.map(item => ({ x: formatTime(item.time), y: item.value })); charts.cpu.update(); } // 更新内存图表 if (memoryData && Array.isArray(memoryData) && memoryData.length > 0 && charts.memory) { const fixedData = getFixedPointsData(memoryData); charts.memory.data.datasets[0].data = fixedData.map(item => ({ x: formatTime(item.time), y: item.value })); charts.memory.update(); } // 更新磁盘图表,支持多个挂载点 if (diskData && charts.disk) { // 定义不同的颜色,用于区分不同的挂载点 const colors = [ { border: '#f59e0b', background: 'rgba(245, 158, 11, 0.1)' }, // 黄色 { border: '#ef4444', background: 'rgba(239, 68, 68, 0.1)' }, // 红色 { border: '#10b981', background: 'rgba(16, 185, 129, 0.1)' }, // 绿色 { border: '#3b82f6', background: 'rgba(59, 130, 246, 0.1)' }, // 蓝色 { border: '#8b5cf6', background: 'rgba(139, 92, 246, 0.1)' }, // 紫色 { border: '#ec4899', background: 'rgba(236, 72, 153, 0.1)' }, // 粉色 ]; // 清空现有的数据集 charts.disk.data.datasets = []; // 为每个挂载点创建独立的数据集 let colorIndex = 0; if (typeof diskData === 'object' && diskData !== null && !Array.isArray(diskData)) { // 处理按挂载点分组的数据 for (const [mountpoint, data] of Object.entries(diskData)) { if (data && Array.isArray(data) && data.length > 0) { // 获取颜色 const color = colors[colorIndex % colors.length]; colorIndex++; // 排序数据 const sortedData = sortDataByTime(data); // 使用固定份数X轴数据计算 const fixedPointsData = getFixedPointsData(sortedData); // 创建数据集 const dataset = { label: `磁盘使用率 (${mountpoint})`, data: fixedPointsData.map(item => ({ x: formatTime(item.time), y: item.value })), borderColor: color.border, backgroundColor: color.background, borderWidth: 2, fill: true, tension: 0.7, pointRadius: 0, pointHoverRadius: 3, }; // 添加数据集 charts.disk.data.datasets.push(dataset); } } } else if (Array.isArray(diskData) && diskData.length > 0) { // 处理单一磁盘数据 const sortedData = sortDataByTime(diskData); // 使用固定份数X轴数据计算 const fixedPointsData = getFixedPointsData(sortedData); const dataset = { label: '磁盘使用率', data: fixedPointsData.map(item => ({ x: formatTime(item.time), y: item.value })), borderColor: '#f59e0b', backgroundColor: 'rgba(245, 158, 11, 0.1)', borderWidth: 2, fill: true, tension: 0.7, pointRadius: 0, pointHoverRadius: 3, }; charts.disk.data.datasets.push(dataset); } // 更新图表 charts.disk.update(); } // 更新网络流量趋势图表(发送总和和接收总和) if (networkData && networkData.sent && networkData.received && charts.network) { // 计算发送总和(时间段内的累积值) if (Array.isArray(networkData.sent) && networkData.sent.length > 0) { // 排序发送数据 const sortedSent = sortDataByTime(networkData.sent); // 计算累积发送总和(MB) let cumulativeSent = 0; const sentSumData = sortedSent.map(item => { // 转换为MB并累积 const mbValue = item.value / (1024 * 1024); cumulativeSent += mbValue; return { time: item.time, value: cumulativeSent }; }); // 使用固定份数X轴数据计算 const fixedPointsSentSum = getFixedPointsData(sentSumData); charts.network.data.datasets[0].data = fixedPointsSentSum.map(item => ({ x: formatTime(item.time), y: item.value })); } // 计算接收总和(时间段内的累积值) if (Array.isArray(networkData.received) && networkData.received.length > 0) { // 排序接收数据 const sortedReceived = sortDataByTime(networkData.received); // 计算累积接收总和(MB) let cumulativeReceived = 0; const receivedSumData = sortedReceived.map(item => { // 转换为MB并累积 const mbValue = item.value / (1024 * 1024); cumulativeReceived += mbValue; return { time: item.time, value: cumulativeReceived }; }); // 使用固定份数X轴数据计算 const fixedPointsReceivedSum = getFixedPointsData(receivedSumData); charts.network.data.datasets[1].data = fixedPointsReceivedSum.map(item => ({ x: formatTime(item.time), y: item.value })); } charts.network.update(); } // 更新网速趋势图表 if (networkData && networkData.sent && networkData.received && charts.speed) { // 更新发送流量 if (Array.isArray(networkData.sent) && networkData.sent.length > 0) { const sortedData = sortDataByTime(networkData.sent); // 使用固定份数X轴数据计算 const fixedPointsData = getFixedPointsData(sortedData); charts.speed.data.datasets[0].data = fixedPointsData.map(item => ({ x: formatTime(item.time), y: item.value / (1024 * 1024) // 转换为MB/s })); } // 更新接收流量 if (Array.isArray(networkData.received) && networkData.received.length > 0) { const sortedData = sortDataByTime(networkData.received); // 使用固定份数X轴数据计算 const fixedPointsData = getFixedPointsData(sortedData); charts.speed.data.datasets[1].data = fixedPointsData.map(item => ({ x: formatTime(item.time), y: item.value / (1024 * 1024) // 转换为MB/s })); } charts.speed.update(); } // 初始化图表(如果尚未初始化) initDetailedCharts(); } // 尝试重连WebSocket function attemptReconnect() { if (wsReconnectAttempts < wsMaxReconnectAttempts) { wsReconnectAttempts++; wsReconnectDelay *= 2; setTimeout(() => { console.log(`尝试重新连接WebSocket (${wsReconnectAttempts}/${wsMaxReconnectAttempts})`); initWebSocket(); }, wsReconnectDelay); } } // 打开模态框 function openModal(isEdit = false, deviceData = null) { const modal = document.getElementById('deviceModal'); const modalContent = document.getElementById('modalContent'); const modalTitle = document.getElementById('modalTitle'); const deviceId = document.getElementById('deviceId'); const deviceName = document.getElementById('deviceName'); const deviceIp = document.getElementById('deviceIp'); const deviceStatus = document.getElementById('deviceStatus'); // 重置表单 document.getElementById('deviceForm').reset(); // 设置模态框标题和数据 if (isEdit && deviceData) { modalTitle.textContent = '编辑设备'; deviceId.value = deviceData.id; deviceName.value = deviceData.name || ''; deviceIp.value = deviceData.ip || ''; deviceStatus.value = deviceData.status || 'inactive'; } else { modalTitle.textContent = '添加设备'; deviceId.value = ''; } // 显示模态框并添加动画 modal.classList.remove('hidden'); // 触发重排后再添加动画类 setTimeout(() => { modalContent.classList.remove('scale-95', 'opacity-0'); modalContent.classList.add('scale-100', 'opacity-100'); }, 10); } // 关闭模态框 function closeModal() { const modal = document.getElementById('deviceModal'); const modalContent = document.getElementById('modalContent'); // 先应用离开动画 modalContent.classList.remove('scale-100', 'opacity-100'); modalContent.classList.add('scale-95', 'opacity-0'); // 动画结束后隐藏模态框 setTimeout(() => { modal.classList.add('hidden'); }, 300); } // 编辑设备 function editDevice(deviceId) { // 查找对应的设备数据 const deviceData = state.deviceList.find(device => device.id === deviceId); if (deviceData) { openModal(true, deviceData); } else { showToast('未找到设备数据', 'error'); } } // 处理设备表单提交 async function handleDeviceFormSubmit(event) { event.preventDefault(); const deviceId = document.getElementById('deviceId').value; const deviceName = document.getElementById('deviceName').value; const deviceIp = document.getElementById('deviceIp').value; const deviceStatus = document.getElementById('deviceStatus').value; // 生成或使用现有ID let idToUse = deviceId; if (!idToUse) { // 为新设备生成ID idToUse = 'device-' + Date.now(); } // 获取当前时间戳 const timestamp = Math.floor(Date.now() / 1000); // 为新设备生成token let token = ''; if (!deviceId) { token = 'token-' + Math.random().toString(36).substring(2, 15); } // 构建完整的设备数据对象,包含所有必需字段 const deviceData = { id: idToUse, name: deviceName, ip: deviceIp, status: deviceStatus, token: token, // 新设备生成token,编辑时不提供(由后端保持原值) created_at: timestamp, // 新设备的创建时间 updated_at: timestamp // 更新时间 }; try { let url = `${API_BASE_URL}/devices/`; let method = 'POST'; // 如果是编辑操作,修改URL和方法 if (deviceId) { url = `${API_BASE_URL}/devices/${deviceId}`; method = 'PUT'; } // 发送请求 const response = await fetch(url, { method: method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(deviceData) }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const result = await response.json(); // 显示成功提示 showToast(deviceId ? '设备更新成功' : '设备添加成功', 'success'); // 关闭模态框 closeModal(); // 重新加载设备列表 await loadDeviceManagementList(); } catch (error) { console.error('保存设备失败:', error); showToast(deviceId ? '设备更新失败' : '设备添加失败', 'error'); } } // 绑定事件 function bindEvents() { // 添加设备按钮事件 const addDeviceBtn = document.getElementById('addDeviceBtn'); if (addDeviceBtn) { addDeviceBtn.addEventListener('click', () => { openModal(false); }); } // 关闭模态框按钮 const closeModalBtn = document.getElementById('closeModalBtn'); if (closeModalBtn) { closeModalBtn.addEventListener('click', closeModal); } // 取消按钮 const cancelModalBtn = document.getElementById('cancelModalBtn'); if (cancelModalBtn) { cancelModalBtn.addEventListener('click', closeModal); } // 设备表单提交 const deviceForm = document.getElementById('deviceForm'); if (deviceForm) { deviceForm.addEventListener('submit', handleDeviceFormSubmit); } // 点击模态框背景关闭 const modal = document.getElementById('deviceModal'); if (modal) { modal.addEventListener('click', (event) => { if (event.target === modal) { closeModal(); } }); } // 自定义时间查询事件 const customTimeQuery = document.getElementById('customTimeQuery'); if (customTimeQuery) { customTimeQuery.addEventListener('click', () => { const startTimeInput = document.getElementById('customStartTime'); const endTimeInput = document.getElementById('customEndTime'); if (startTimeInput && endTimeInput) { // 获取本地时间 const localStartTime = new Date(startTimeInput.value); const localEndTime = new Date(endTimeInput.value); // 转换为ISO字符串(UTC时间) state.customStartTime = localStartTime.toISOString(); state.customEndTime = localEndTime.toISOString(); // 清空当前时间范围状态,只使用自定义时间 state.currentTimeRange = ''; // 重新加载数据 loadMetrics(); } }); } // 初始化自定义时间输入框 const now = new Date(); const startTimeInput = document.getElementById('customStartTime'); const endTimeInput = document.getElementById('customEndTime'); if (startTimeInput && endTimeInput) { // 默认显示过去24小时 const twentyFourHoursAgo = new Date(now.getTime() - 24 * 60 * 60 * 1000); // 显示本地时间格式,YYYY-MM-DDTHH:MM startTimeInput.value = twentyFourHoursAgo.toISOString().slice(0, 16); endTimeInput.value = now.toISOString().slice(0, 16); } // 缩放控件事件处理 const zoomOutBtn = document.getElementById('zoomOutBtn'); const zoomInBtn = document.getElementById('zoomInBtn'); const resetZoomBtn = document.getElementById('resetZoomBtn'); const currentTimeRangeDisplay = document.getElementById('currentTimeRangeDisplay'); if (zoomOutBtn && zoomInBtn && currentTimeRangeDisplay) { // 时间范围选项列表,用于缩放 const timeRanges = ['30m', '1h', '2h', '6h', '12h', '24h']; // 更新当前时间范围显示 const updateTimeRangeDisplay = () => { switch(state.currentTimeRange) { case '30m': currentTimeRangeDisplay.textContent = '过去30分钟'; break; case '1h': currentTimeRangeDisplay.textContent = '过去1小时'; break; case '2h': currentTimeRangeDisplay.textContent = '过去2小时'; break; case '6h': currentTimeRangeDisplay.textContent = '过去6小时'; break; case '12h': currentTimeRangeDisplay.textContent = '过去12小时'; break; case '24h': currentTimeRangeDisplay.textContent = '过去24小时'; break; default: currentTimeRangeDisplay.textContent = '自定义时间范围'; } }; // 初始化显示 updateTimeRangeDisplay(); // 放大事件 zoomInBtn.addEventListener('click', () => { // 只在使用预设时间范围时生效 if (state.customStartTime && state.customEndTime) { // 使用自定义时间时,先清除自定义时间 state.customStartTime = ''; state.customEndTime = ''; state.currentTimeRange = '1h'; } else { // 查找当前时间范围在列表中的索引 const currentIndex = timeRanges.indexOf(state.currentTimeRange); if (currentIndex > 0) { // 放大:使用更小的时间范围 state.currentTimeRange = timeRanges[currentIndex - 1]; } } // 更新显示 updateTimeRangeDisplay(); // 重新加载数据 loadMetrics(); }); // 缩小事件 zoomOutBtn.addEventListener('click', () => { // 只在使用预设时间范围时生效 if (state.customStartTime && state.customEndTime) { // 使用自定义时间时,先清除自定义时间 state.customStartTime = ''; state.customEndTime = ''; state.currentTimeRange = '1h'; } else { // 查找当前时间范围在列表中的索引 const currentIndex = timeRanges.indexOf(state.currentTimeRange); if (currentIndex < timeRanges.length - 1) { // 缩小:使用更大的时间范围 state.currentTimeRange = timeRanges[currentIndex + 1]; } } // 更新显示 updateTimeRangeDisplay(); // 重新加载数据 loadMetrics(); }); } // 重置缩放按钮事件处理 if (resetZoomBtn) { resetZoomBtn.addEventListener('click', () => { // 重置所有图表的缩放 Object.values(charts).forEach(chart => { if (chart && typeof chart.resetZoom === 'function') { chart.resetZoom(); } }); }); } } // 工具函数 function showContent(contentId) { const element = document.getElementById(contentId); if (element) { element.classList.remove('hidden'); } } // 跳转到服务器监控详情页面 function goToServerMonitor(deviceId) { state.currentDeviceID = deviceId; window.location.hash = `#serverMonitor/${deviceId}`; // 加载选中设备的监控数据 setTimeout(() => { loadMetrics(); }, 100); } // 初始化图表选项卡 function initChartTabs() { const tabs = document.querySelectorAll('.chart-tab'); if (tabs.length === 0) return; tabs.forEach(tab => { tab.addEventListener('click', () => { // 移除所有选项卡的激活状态 tabs.forEach(t => { t.classList.remove('active', 'text-blue-600', 'border-blue-600'); t.classList.add('text-gray-600', 'border-transparent'); }); // 添加当前选项卡的激活状态 tab.classList.add('active', 'text-blue-600', 'border-blue-600'); tab.classList.remove('text-gray-600', 'border-transparent'); // 隐藏所有图表容器 const tabId = tab.dataset.tab; const chartContainers = document.querySelectorAll('.chart-container'); chartContainers.forEach(container => { container.classList.add('hidden'); }); // 显示当前选中的图表容器 const activeContainer = document.getElementById(`${tabId}ChartContainer`); if (activeContainer) { activeContainer.classList.remove('hidden'); } }); }); } // 页面加载完成后初始化 window.addEventListener('DOMContentLoaded', () => { initApp(); // 初始化图表选项卡 initChartTabs(); // 添加路由监听,处理hash变化 window.addEventListener('hashchange', handleHashChange); // 初始检查hash handleHashChange(); }); // 处理hash变化 function handleHashChange() { const hash = window.location.hash; if (hash === '#serverMonitor' || hash.startsWith('#serverMonitor/')) { // 延迟一下,确保DOM已经渲染完成 setTimeout(() => { loadMetrics(); }, 300); } }