// main.js - 主脚本文件 // 页面数据缓存对象 const pageDataCache = { // 页面初始化状态 initializedPages: {}, // 页面数据缓存 data: { dashboard: { timestamp: 0, data: null, expiry: 5 * 60 * 1000 // 5分钟过期 }, logs: { timestamp: 0, data: null, expiry: 5 * 60 * 1000 // 5分钟过期 }, shield: { timestamp: 0, data: null, expiry: 10 * 60 * 1000 // 10分钟过期 }, hosts: { timestamp: 0, data: null, expiry: 10 * 60 * 1000 // 10分钟过期 }, gfwlist: { timestamp: 0, data: null, expiry: 10 * 60 * 1000 // 10分钟过期 } }, // 检查缓存是否有效 isCacheValid: function(page) { const cache = this.data[page]; if (!cache || !cache.data) return false; const now = Date.now(); return (now - cache.timestamp) < cache.expiry; }, // 获取缓存数据 getCache: function(page) { if (this.isCacheValid(page)) { return this.data[page].data; } return null; }, // 设置缓存数据 setCache: function(page, data) { if (this.data[page]) { this.data[page].data = data; this.data[page].timestamp = Date.now(); } }, // 清除缓存 clearCache: function(page) { if (this.data[page]) { this.data[page].data = null; this.data[page].timestamp = 0; } }, // 标记页面已初始化 markPageInitialized: function(page) { this.initializedPages[page] = true; }, // 检查页面是否已初始化 isPageInitialized: function(page) { return this.initializedPages[page] || false; } }; // 页面可见性状态 let isPageVisible = true; // 页面初始化函数 - 根据当前hash值初始化对应页面 function initPageByHash() { const hash = window.location.hash.substring(1); // 隐藏所有内容区域 const contentSections = [ document.getElementById('dashboard-content'), document.getElementById('shield-content'), document.getElementById('hosts-content'), document.getElementById('gfwlist-content'), document.getElementById('query-content'), document.getElementById('logs-content'), document.getElementById('config-content') ]; contentSections.forEach(section => { if (section) { section.classList.add('hidden'); } }); // 显示当前页面内容 const currentSection = document.getElementById(`${hash}-content`); if (currentSection) { currentSection.classList.remove('hidden'); } // 更新页面标题 const pageTitle = document.getElementById('page-title'); if (pageTitle) { const titles = { 'dashboard': '仪表盘', 'shield': '屏蔽管理', 'hosts': 'Hosts管理', 'gfwlist': 'GFWList管理', 'query': 'DNS屏蔽查询', 'logs': '查询日志', 'config': '系统设置' }; pageTitle.textContent = titles[hash] || '仪表盘'; } // 页面特定初始化 - 使用setTimeout延迟调用,确保所有脚本文件都已加载完成 if (hash === 'shield') { setTimeout(() => { if (typeof initShieldPage === 'function') { // 检查页面是否已经初始化 if (!pageDataCache.isPageInitialized('shield') || !pageDataCache.isCacheValid('shield')) { initShieldPage(); pageDataCache.markPageInitialized('shield'); } } }, 0); } else if (hash === 'hosts') { setTimeout(() => { if (typeof initHostsPage === 'function') { // 检查页面是否已经初始化 if (!pageDataCache.isPageInitialized('hosts') || !pageDataCache.isCacheValid('hosts')) { initHostsPage(); pageDataCache.markPageInitialized('hosts'); } } }, 0); } else if (hash === 'gfwlist') { setTimeout(() => { if (typeof initGFWListPage === 'function') { // 检查页面是否已经初始化 if (!pageDataCache.isPageInitialized('gfwlist') || !pageDataCache.isCacheValid('gfwlist')) { initGFWListPage(); pageDataCache.markPageInitialized('gfwlist'); } } }, 0); } else if (hash === 'logs') { setTimeout(() => { if (typeof initLogsPage === 'function') { // 检查页面是否已经初始化 if (!pageDataCache.isPageInitialized('logs') || !pageDataCache.isCacheValid('logs')) { initLogsPage(); pageDataCache.markPageInitialized('logs'); } } }, 0); } else if (hash === 'dashboard') { setTimeout(() => { if (typeof loadDashboardData === 'function') { // 检查页面是否已经初始化 if (!pageDataCache.isPageInitialized('dashboard') || !pageDataCache.isCacheValid('dashboard')) { loadDashboardData(); pageDataCache.markPageInitialized('dashboard'); } } }, 0); } } // 格式化运行时间 function formatUptime(milliseconds) { // 简化版的格式化,实际使用时需要根据API返回的数据格式调整 const seconds = Math.floor(milliseconds / 1000); const minutes = Math.floor(seconds / 60); const hours = Math.floor(minutes / 60); const days = Math.floor(hours / 24); if (days > 0) { return `${days}天${hours % 24}小时`; } else if (hours > 0) { return `${hours}小时${minutes % 60}分钟`; } else if (minutes > 0) { return `${minutes}分钟${seconds % 60}秒`; } else { return `${seconds}秒`; } } // 更新系统状态 function updateSystemStatus() { api.getStatus() .then(data => { if (data.error) { throw new Error(data.error); } const uptimeElement = document.getElementById('uptime'); if (uptimeElement) { uptimeElement.textContent = `正常运行中 | ${formatUptime(data.uptime)}`; uptimeElement.classList.remove('text-danger'); } }) .catch(error => { console.error('更新系统状态失败:', error); const uptimeElement = document.getElementById('uptime'); if (uptimeElement) { uptimeElement.textContent = '连接异常'; uptimeElement.classList.add('text-danger'); } }); } // 账户功能 - 下拉菜单、注销和修改密码 function setupAccountFeatures() { // 下拉菜单功能 const accountDropdown = document.getElementById('account-dropdown'); const accountMenu = document.getElementById('account-menu'); const changePasswordBtn = document.getElementById('change-password-btn'); const logoutBtn = document.getElementById('logout-btn'); const changePasswordModal = document.getElementById('change-password-modal'); const closeModalBtn = document.getElementById('close-modal-btn'); const cancelChangePasswordBtn = document.getElementById('cancel-change-password'); const changePasswordForm = document.getElementById('change-password-form'); const passwordMismatch = document.getElementById('password-mismatch'); const newPassword = document.getElementById('new-password'); const confirmPassword = document.getElementById('confirm-password'); // 点击外部关闭下拉菜单 document.addEventListener('click', (e) => { if (accountDropdown && !accountDropdown.contains(e.target)) { accountMenu.classList.add('hidden'); } }); // 点击账户区域切换下拉菜单 if (accountDropdown) { accountDropdown.addEventListener('click', (e) => { e.stopPropagation(); accountMenu.classList.toggle('hidden'); }); } // 打开修改密码模态框 if (changePasswordBtn) { changePasswordBtn.addEventListener('click', () => { accountMenu.classList.add('hidden'); changePasswordModal.classList.remove('hidden'); document.body.style.overflow = 'hidden'; }); } // 关闭修改密码模态框 function closeModal() { changePasswordModal.classList.add('hidden'); document.body.style.overflow = ''; changePasswordForm.reset(); passwordMismatch.classList.add('hidden'); } // 绑定关闭模态框事件 if (closeModalBtn) { closeModalBtn.addEventListener('click', closeModal); } if (cancelChangePasswordBtn) { cancelChangePasswordBtn.addEventListener('click', closeModal); } // 点击模态框外部关闭模态框 if (changePasswordModal) { changePasswordModal.addEventListener('click', (e) => { if (e.target === changePasswordModal) { closeModal(); } }); } // 按ESC键关闭模态框 document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && !changePasswordModal.classList.contains('hidden')) { closeModal(); } }); // 密码匹配验证 if (newPassword && confirmPassword) { confirmPassword.addEventListener('input', () => { if (newPassword.value !== confirmPassword.value) { passwordMismatch.classList.remove('hidden'); } else { passwordMismatch.classList.add('hidden'); } }); newPassword.addEventListener('input', () => { if (newPassword.value !== confirmPassword.value) { passwordMismatch.classList.remove('hidden'); } else { passwordMismatch.classList.add('hidden'); } }); } // 修改密码表单提交 if (changePasswordForm) { changePasswordForm.addEventListener('submit', async (e) => { e.preventDefault(); // 验证密码匹配 if (newPassword.value !== confirmPassword.value) { passwordMismatch.classList.remove('hidden'); return; } const formData = new FormData(changePasswordForm); const data = { currentPassword: formData.get('currentPassword'), newPassword: formData.get('newPassword') }; try { const response = await fetch('/api/change-password', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); const result = await response.json(); if (response.ok && result.status === 'success') { // 密码修改成功 alert('密码修改成功'); closeModal(); } else { // 密码修改失败 alert(result.error || '密码修改失败'); } } catch (error) { console.error('修改密码失败:', error); alert('修改密码失败,请稍后重试'); } }); } // 注销功能 if (logoutBtn) { logoutBtn.addEventListener('click', async () => { try { await fetch('/api/logout', { method: 'POST' }); // 重定向到登录页面 window.location.href = '/login'; } catch (error) { console.error('注销失败:', error); alert('注销失败,请稍后重试'); } }); } } // 页面导航功能 function setupNavigation() { // 侧边栏菜单项 const menuItems = document.querySelectorAll('nav a'); const pageTitle = document.getElementById('page-title'); menuItems.forEach((item, index) => { item.addEventListener('click', (e) => { // 允许浏览器自动更新地址栏中的hash,不阻止默认行为 // 移动端点击菜单项后自动关闭侧边栏 if (window.innerWidth < 768) { closeSidebar(); } }); }); // 移动端侧边栏切换 const toggleSidebar = document.getElementById('toggle-sidebar'); const closeSidebarBtn = document.getElementById('close-sidebar'); const sidebar = document.getElementById('mobile-sidebar'); const sidebarOverlay = document.getElementById('sidebar-overlay'); // 打开侧边栏函数 function openSidebar() { console.log('Opening sidebar...'); if (sidebar) { sidebar.classList.remove('-translate-x-full'); sidebar.classList.add('translate-x-0'); } if (sidebarOverlay) { sidebarOverlay.classList.remove('hidden'); sidebarOverlay.classList.add('block'); // 防止页面滚动 document.body.style.overflow = 'hidden'; } console.log('Sidebar opened successfully'); } // 关闭侧边栏函数 function closeSidebar() { console.log('Closing sidebar...'); if (sidebar) { sidebar.classList.add('-translate-x-full'); sidebar.classList.remove('translate-x-0'); } if (sidebarOverlay) { sidebarOverlay.classList.add('hidden'); sidebarOverlay.classList.remove('block'); // 恢复页面滚动 document.body.style.overflow = ''; } console.log('Sidebar closed successfully'); } // 切换侧边栏函数 function toggleSidebarVisibility() { console.log('Toggling sidebar visibility...'); console.log('Current sidebar classes:', sidebar ? sidebar.className : 'sidebar not found'); if (sidebar && sidebar.classList.contains('-translate-x-full')) { console.log('Sidebar is hidden, opening...'); openSidebar(); } else { console.log('Sidebar is visible, closing...'); closeSidebar(); } } // 绑定切换按钮事件 if (toggleSidebar) { toggleSidebar.addEventListener('click', toggleSidebarVisibility); } // 绑定关闭按钮事件 if (closeSidebarBtn) { closeSidebarBtn.addEventListener('click', closeSidebar); } // 绑定遮罩层点击事件 if (sidebarOverlay) { sidebarOverlay.addEventListener('click', closeSidebar); } // 移动端点击菜单项后自动关闭侧边栏 menuItems.forEach(item => { item.addEventListener('click', () => { // 检查是否是移动设备视图 if (window.innerWidth < 768) { closeSidebar(); } }); }); // 添加键盘事件监听,按ESC键关闭侧边栏 document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { closeSidebar(); } }); } // 主题切换功能 function setupThemeToggle() { const themeToggle = document.getElementById('theme-toggle'); const body = document.body; if (!themeToggle) return; // 从localStorage加载主题偏好 const savedTheme = localStorage.getItem('theme'); if (savedTheme) { body.classList.toggle('dark', savedTheme === 'dark'); updateThemeIcon(themeToggle, savedTheme === 'dark'); } // 添加点击事件监听器 themeToggle.addEventListener('click', () => { const isDark = body.classList.toggle('dark'); updateThemeIcon(themeToggle, isDark); // 保存主题偏好到localStorage localStorage.setItem('theme', isDark ? 'dark' : 'light'); }); } // 更新主题图标 function updateThemeIcon(toggleElement, isDark) { const icon = toggleElement.querySelector('i'); if (icon) { if (isDark) { icon.className = 'fa fa-sun-o text-sm sm:text-lg'; } else { icon.className = 'fa fa-moon-o text-sm sm:text-lg'; } } } // 处理页面可见性变化 function handleVisibilityChange() { if (document.visibilityState === 'visible') { isPageVisible = true; console.log('页面变为可见'); // 当页面重新可见时,检查当前页面是否需要刷新数据 const hash = window.location.hash.substring(1); // 只有当缓存过期时才重新加载数据 if (hash && !pageDataCache.isCacheValid(hash)) { console.log(`缓存已过期,重新加载${hash}页面数据`); // 根据当前页面类型重新加载数据 if (hash === 'dashboard' && typeof loadDashboardData === 'function') { loadDashboardData(); } else if (hash === 'logs' && typeof loadLogs === 'function') { loadLogs(); } else if (hash === 'shield' && typeof initShieldPage === 'function') { initShieldPage(); } else if (hash === 'hosts' && typeof initHostsPage === 'function') { initHostsPage(); } } // 更新系统状态 updateSystemStatus(); } else { isPageVisible = false; console.log('页面变为隐藏'); } } // 初始化函数 function init() { // 设置导航 setupNavigation(); // 设置账户功能 setupAccountFeatures(); // 设置主题切换 setupThemeToggle(); // 初始化页面 initPageByHash(); // 添加hashchange事件监听,处理浏览器前进/后退按钮 window.addEventListener('hashchange', initPageByHash); // 添加页面可见性变化监听 document.addEventListener('visibilitychange', handleVisibilityChange); // 定期更新系统状态 setInterval(updateSystemStatus, 5000); } // 页面加载完成后执行初始化 window.addEventListener('DOMContentLoaded', init);