Files
dns-server/static/js/main.js
T
Alex Yang f627244b8f 更新
2026-03-30 01:04:46 +08:00

607 lines
20 KiB
JavaScript

// 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分钟过期
},
about: {
timestamp: 0,
data: null,
expiry: 10 * 60 * 1000 // 10分钟过期
},
threats: {
timestamp: 0,
data: null,
expiry: 5 * 60 * 1000 // 5分钟过期
}
},
// 检查缓存是否有效
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('domain-content'),
document.getElementById('logs-content'),
document.getElementById('config-content'),
document.getElementById('about-content'),
document.getElementById('threats-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屏蔽查询',
'domain': '域名查询',
'logs': '查询日志',
'config': '系统设置',
'about': '关于',
'threats': '威胁告警'
};
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);
} else if (hash === 'about') {
setTimeout(() => {
if (typeof initAboutPage === 'function') {
// 检查页面是否已经初始化
if (!pageDataCache.isPageInitialized('about') || !pageDataCache.isCacheValid('about')) {
initAboutPage();
pageDataCache.markPageInitialized('about');
}
}
}, 0);
} else if (hash === 'threats') {
setTimeout(() => {
if (typeof initThreatsPage === 'function') {
// 检查页面是否已经初始化
if (!pageDataCache.isPageInitialized('threats') || !pageDataCache.isCacheValid('threats')) {
initThreatsPage();
pageDataCache.markPageInitialized('threats');
}
}
}, 0);
}
}
// 格式化运行时间
function formatUptime(milliseconds) {
const totalSeconds = Math.floor(milliseconds / 1000);
const days = Math.floor(totalSeconds / (24 * 60 * 60));
const hours = Math.floor((totalSeconds % (24 * 60 * 60)) / (60 * 60));
const minutes = Math.floor((totalSeconds % (60 * 60)) / 60);
const seconds = totalSeconds % 60;
if (days > 0) {
return `${days}${hours}小时${minutes}分钟`;
} else if (hours > 0) {
return `${hours}小时${minutes}分钟${seconds}`;
} else if (minutes > 0) {
return `${minutes}分钟${seconds}`;
} 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();
} else if (hash === 'about' && typeof initAboutPage === 'function') {
initAboutPage();
} else if (hash === 'threats' && typeof initThreatsPage === 'function') {
initThreatsPage();
}
}
// 更新系统状态
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);