// 全局配置 const API_BASE_URL = '/api'; // DOM 加载完成后执行 document.addEventListener('DOMContentLoaded', function() { // 初始化面板切换 initPanelNavigation(); // 初始化通知组件 initNotification(); // 加载初始数据 loadInitialData(); // 定时更新数据 setInterval(loadInitialData, 60000); // 每分钟更新一次 }); // 初始化面板导航 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`](); } }); }); } // 初始化通知组件 function initNotification() { window.showNotification = function(message, type = 'info') { const notification = document.getElementById('notification'); const notificationMessage = document.getElementById('notification-message'); // 设置消息和类型 notificationMessage.textContent = message; notification.className = 'notification show ' + type; // 自动关闭 setTimeout(() => { notification.classList.remove('show'); }, 3000); }; } // 加载初始数据 function loadInitialData() { // 加载服务器状态 fetch(`${API_BASE_URL}/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 = '离线'; }); // 加载统计数据 fetch(`${API_BASE_URL}/stats`) .then(response => response.json()) .then(data => { // 更新统计数据 if (data && data.dns) { updateStatCards(data.dns); } }) .catch(error => { console.error('获取统计数据失败:', error); window.showNotification('获取统计数据失败', 'error'); }); } // 更新统计卡片数据 function updateStatCards(stats) { const statElements = { 'blocked-count': stats.Blocked || 0, 'allowed-count': stats.Allowed || 0, 'error-count': stats.Errors || 0, 'total-queries': stats.Queries || 0, 'rules-count': 0, 'hosts-count': 0 }; for (const [id, value] of Object.entries(statElements)) { const element = document.getElementById(id); if (element) { element.textContent = formatNumber(value); } } } // 通用API请求函数 function apiRequest(endpoint, method = 'GET', data = null) { const headers = { 'Content-Type': 'application/json' }; const config = { method, headers }; if (data && (method === 'POST' || method === 'PUT' || method === 'DELETE')) { config.body = JSON.stringify(data); } return fetch(`${API_BASE_URL}${endpoint}`, config) .then(response => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); }); } // 数字格式化函数 function formatNumber(num) { if (num >= 1000000) { return (num / 1000000).toFixed(1) + 'M'; } else if (num >= 1000) { return (num / 1000).toFixed(1) + 'K'; } return num.toString(); } // 确认对话框函数 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'; } }); }); }