// 全局配置 const API_BASE_URL = '.'; // DOM 加载完成后执行 document.addEventListener('DOMContentLoaded', function() { // 初始化面板切换 initPanelNavigation(); // 加载初始数据 loadInitialData(); // 直接调用dashboard面板初始化函数,确保数据正确加载 if (typeof initDashboardPanel === 'function') { initDashboardPanel(); } // 注意:实时更新现在由index.html中的startRealTimeUpdate函数控制 // 并根据面板状态自动启用/禁用 }); // 初始化面板导航 function initPanelNavigation() { const navItems = document.querySelectorAll('.nav-item'); const panels = document.querySelectorAll('.panel'); navItems.forEach(item => { item.addEventListener('click', function() { // 移除所有活动类 navItems.forEach(nav => nav.classList.remove('active')); panels.forEach(panel => panel.classList.remove('active')); // 添加当前活动类 this.classList.add('active'); const target = this.getAttribute('data-target'); document.getElementById(target).classList.add('active'); // 面板激活时执行相应的初始化函数 if (window[`init${target.charAt(0).toUpperCase() + target.slice(1)}Panel`]) { window[`init${target.charAt(0).toUpperCase() + target.slice(1)}Panel`](); } }); }); } // 保留原有的通知函数作为兼容层 // 现在主通知功能由index.html中的showNotification函数实现 if (typeof window.showNotification === 'undefined') { window.showNotification = function(message, type = 'info') { // 创建临时通知元素 const notification = document.createElement('div'); notification.className = `notification notification-${type} show`; notification.innerHTML = `
${message}
`; notification.style.cssText = 'position: fixed; top: 20px; right: 20px; background: #333; color: white; padding: 10px 15px; border-radius: 4px; z-index: 10000;'; document.body.appendChild(notification); setTimeout(() => { notification.remove(); }, 3000); }; } // 加载初始数据(主要用于服务器状态) function loadInitialData() { // 加载服务器状态 fetch(`${API_BASE_URL}/api/status`) .then(response => response.json()) .then(data => { // 更新服务器状态指示器 const statusDot = document.querySelector('.status-dot'); const serverStatus = document.getElementById('server-status'); if (data && data.status === 'running') { statusDot.classList.add('connected'); serverStatus.textContent = '运行中'; } else { statusDot.classList.remove('connected'); serverStatus.textContent = '离线'; } }) .catch(error => { console.error('获取服务器状态失败:', error); // 更新状态为离线 const statusDot = document.querySelector('.status-dot'); const serverStatus = document.getElementById('server-status'); statusDot.classList.remove('connected'); serverStatus.textContent = '离线'; // 使用新的通知功能 if (typeof window.showNotification === 'function') { window.showNotification('获取服务器状态失败', 'danger'); } }); // 注意:统计数据更新现在由dashboard.js中的updateStatCards函数处理 } // 注意:统计卡片数据更新现在由dashboard.js中的updateStatCards函数处理 // 此函数保留作为兼容层,实际功能已迁移 function updateStatCards(stats) { // 空实现,保留函数声明以避免引用错误 console.log('更新统计卡片 - 此功能现在由dashboard.js处理'); } // 注意:获取规则数量功能现在由dashboard.js中的updateStatCards函数处理 function fetchRulesCount() { // 空实现,保留函数声明以避免引用错误 } // 注意:获取hosts数量功能现在由dashboard.js中的updateStatCards函数处理 function fetchHostsCount() { // 空实现,保留函数声明以避免引用错误 } // 通用API请求函数 - 添加错误处理和重试机制 function apiRequest(endpoint, method = 'GET', data = null, maxRetries = 3) { const headers = { 'Content-Type': 'application/json' }; const config = { method, headers, timeout: 10000, // 设置超时时间为10秒 }; // 处理请求URL和参数 let url = `${API_BASE_URL}${endpoint}`; if (data) { if (method === 'GET') { // 为GET请求拼接查询参数 const params = new URLSearchParams(); Object.keys(data).forEach(key => { params.append(key, data[key]); }); url += `?${params.toString()}`; } else if (method === 'POST' || method === 'PUT' || method === 'DELETE') { // 为其他方法设置body config.body = JSON.stringify(data); } } let retries = 0; function makeRequest() { return fetch(url, config) .then(response => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } // 检查响应是否完整 const contentType = response.headers.get('content-type'); if (contentType && contentType.includes('application/json')) { // 使用.text()先获取响应文本,处理可能的JSON解析错误 return response.text().then(text => { try { return JSON.parse(text); } catch (e) { console.error('JSON解析错误:', e, '响应文本:', text); // 针对ERR_INCOMPLETE_CHUNKED_ENCODING错误进行重试 if (retries < maxRetries) { retries++; console.warn(`请求失败,正在进行第${retries}次重试...`); return new Promise(resolve => setTimeout(() => resolve(makeRequest()), 1000 * retries)); } throw new Error('JSON解析失败且重试次数已达上限'); } }); } return response.json(); }) .catch(error => { console.error('API请求错误:', error); // 检查是否为网络错误或ERR_INCOMPLETE_CHUNKED_ENCODING相关错误 if ((error.name === 'TypeError' && error.message.includes('Failed to fetch')) || error.message.includes('incomplete chunked encoding')) { if (retries < maxRetries) { retries++; console.warn(`网络错误,正在进行第${retries}次重试...`); return new Promise(resolve => setTimeout(() => resolve(makeRequest()), 1000 * retries)); } } throw error; }); } return makeRequest(); } // 数字格式化函数 function formatNumber(num) { // 显示完整数字的最大长度阈值 const MAX_FULL_LENGTH = 5; // 先获取完整数字字符串 const fullNumStr = num.toString(); // 如果数字长度小于等于阈值,直接返回完整数字 if (fullNumStr.length <= MAX_FULL_LENGTH) { return fullNumStr; } // 否则使用缩写格式 if (num >= 1000000) { return (num / 1000000).toFixed(1) + 'M'; } else if (num >= 1000) { return (num / 1000).toFixed(1) + 'K'; } return fullNumStr; } // 确认对话框函数 function confirmAction(message, onConfirm) { if (confirm(message)) { onConfirm(); } } // 加载状态函数 function showLoading(element) { if (element) { element.innerHTML = '加载中...'; } } // 错误状态函数 function showError(element, message) { if (element) { element.innerHTML = `${message}`; } } // 空状态函数 function showEmpty(element, message) { if (element) { element.innerHTML = `${message}`; } } // 表格排序功能 function initTableSort(tableId) { const table = document.getElementById(tableId); if (!table) return; const headers = table.querySelectorAll('thead th'); headers.forEach(header => { header.addEventListener('click', function() { const columnIndex = Array.from(headers).indexOf(this); const isAscending = this.getAttribute('data-sort') !== 'asc'; // 重置所有标题 headers.forEach(h => h.setAttribute('data-sort', '')); this.setAttribute('data-sort', isAscending ? 'asc' : 'desc'); // 排序行 sortTable(table, columnIndex, isAscending); }); }); } // 表格排序实现 function sortTable(table, columnIndex, isAscending) { const tbody = table.querySelector('tbody'); const rows = Array.from(tbody.querySelectorAll('tr')); // 排序行 rows.sort((a, b) => { const aValue = a.cells[columnIndex].textContent.trim(); const bValue = b.cells[columnIndex].textContent.trim(); // 尝试数字排序 const aNum = parseFloat(aValue); const bNum = parseFloat(bValue); if (!isNaN(aNum) && !isNaN(bNum)) { return isAscending ? aNum - bNum : bNum - aNum; } // 字符串排序 return isAscending ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue); }); // 重新添加行 rows.forEach(row => tbody.appendChild(row)); } // 搜索过滤功能 function initSearchFilter(inputId, tableId, columnIndex) { const input = document.getElementById(inputId); const table = document.getElementById(tableId); if (!input || !table) return; input.addEventListener('input', function() { const filter = this.value.toLowerCase(); const rows = table.querySelectorAll('tbody tr'); rows.forEach(row => { const cell = row.cells[columnIndex]; if (cell) { const text = cell.textContent.toLowerCase(); row.style.display = text.includes(filter) ? '' : 'none'; } }); }); }