更新web

This commit is contained in:
Alex Yang
2026-01-21 09:46:49 +08:00
parent ac96c7c10b
commit 073f1961b1
80 changed files with 75919 additions and 12379 deletions

View File

@@ -31,11 +31,10 @@ async function apiRequest(endpoint, method = 'GET', data = null) {
// 竞争:请求或超时
const response = await Promise.race([fetch(url, options), timeoutPromise]);
// 获取响应文本,用于调试和错误处理
// 获取响应文本
const responseText = await response.text();
if (!response.ok) {
// 优化错误响应处理
console.warn(`API请求失败: ${response.status}`);
// 处理401未授权错误重定向到登录页面
@@ -45,73 +44,28 @@ async function apiRequest(endpoint, method = 'GET', data = null) {
return { error: '未授权访问' };
}
// 尝试解析JSON,但如果失败,直接使用原始文本作为错误信息
// 尝试解析JSON错误响应
try {
const errorData = JSON.parse(responseText);
return { error: errorData.error || responseText || `请求失败: ${response.status}` };
} catch (parseError) {
// 当响应不是有效的JSON时(如中文错误信息),直接使用原始文本
console.warn('非JSON格式错误响应:', responseText);
// 当响应不是有效的JSON时直接使用原始文本作为错误信息
return { error: responseText || `请求失败: ${response.status}` };
}
}
// 尝试解析成功响应
// 如果响应文本为空返回null
if (!responseText || responseText.trim() === '') {
return null;
}
// 尝试解析JSON响应
try {
// 首先检查响应文本是否为空
if (!responseText || responseText.trim() === '') {
console.warn('空响应文本');
return null; // 返回null表示空响应
}
// 尝试解析JSON
const parsedData = JSON.parse(responseText);
// 检查解析后的数据是否有效
if (parsedData === null) {
console.warn('解析后的数据为null');
return null;
}
// 允许返回空数组,但不允许返回空对象
if (typeof parsedData === 'object' && !Array.isArray(parsedData) && Object.keys(parsedData).length === 0) {
console.warn('解析后的数据为空对象');
return null;
}
// 限制所有数字为两位小数
const formatNumbers = (obj) => {
if (typeof obj === 'number') {
return parseFloat(obj.toFixed(2));
} else if (Array.isArray(obj)) {
return obj.map(formatNumbers);
} else if (obj && typeof obj === 'object') {
const formattedObj = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
formattedObj[key] = formatNumbers(obj[key]);
}
}
return formattedObj;
}
return obj;
};
const formattedData = formatNumbers(parsedData);
return formattedData;
return parsedData;
} catch (parseError) {
// 详细记录错误信息和响应内容
console.error('JSON解析错误:', parseError);
console.error('原始响应文本:', responseText);
console.error('响应长度:', responseText.length);
console.error('响应前100字符:', responseText.substring(0, 100));
// 如果是位置66附近的错误特别标记
if (parseError.message.includes('position 66')) {
console.error('位置66附近的字符:', responseText.substring(60, 75));
}
// 返回错误对象,让上层处理
return { error: 'JSON解析错误' };
}
} catch (error) {
@@ -156,66 +110,6 @@ const api = {
// 获取查询类型统计
getQueryTypeStats: () => apiRequest('/query/type?t=' + Date.now()),
// 获取屏蔽规则 - 已禁用
getShieldRules: () => {
console.log('屏蔽规则功能已禁用');
return Promise.resolve({}); // 返回空对象而非API调用
},
// 添加屏蔽规则 - 已禁用
addShieldRule: (rule) => {
console.log('屏蔽规则功能已禁用');
return Promise.resolve({ error: '屏蔽规则功能已禁用' });
},
// 删除屏蔽规则 - 已禁用
deleteShieldRule: (rule) => {
console.log('屏蔽规则功能已禁用');
return Promise.resolve({ error: '屏蔽规则功能已禁用' });
},
// 更新远程规则 - 已禁用
updateRemoteRules: () => {
console.log('屏蔽规则功能已禁用');
return Promise.resolve({ error: '屏蔽规则功能已禁用' });
},
// 获取黑名单列表 - 已禁用
getBlacklists: () => {
console.log('屏蔽规则相关功能已禁用');
return Promise.resolve([]); // 返回空数组而非API调用
},
// 添加黑名单 - 已禁用
addBlacklist: (url) => {
console.log('屏蔽规则相关功能已禁用');
return Promise.resolve({ error: '屏蔽规则功能已禁用' });
},
// 删除黑名单 - 已禁用
deleteBlacklist: (url) => {
console.log('屏蔽规则相关功能已禁用');
return Promise.resolve({ error: '屏蔽规则功能已禁用' });
},
// 获取Hosts内容 - 已禁用
getHosts: () => {
console.log('屏蔽规则相关功能已禁用');
return Promise.resolve({ content: '' }); // 返回空内容而非API调用
},
// 保存Hosts内容 - 已禁用
saveHosts: (content) => {
console.log('屏蔽规则相关功能已禁用');
return Promise.resolve({ error: '屏蔽规则功能已禁用' });
},
// 刷新Hosts - 已禁用
refreshHosts: () => {
console.log('屏蔽规则相关功能已禁用');
return Promise.resolve({ error: '屏蔽规则功能已禁用' });
},
// 查询DNS记录 - 兼容多种参数格式
queryDNS: async function(domain, recordType) {
try {
@@ -231,36 +125,22 @@ const api = {
params = { domain, recordType };
}
// 尝试不同的API端点
const endpoints = ['/api/dns/query', '/dns/query', '/api/query', '/query'];
let lastError;
// 使用正确的API端点
const response = await fetch('/api/query', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(params)
});
for (const endpoint of endpoints) {
try {
console.log(`尝试API端点: ${endpoint}`);
const response = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(params)
});
if (response.ok) {
const data = await response.json();
console.log('DNS查询成功:', data);
return data;
} else {
lastError = new Error(`HTTP error! status: ${response.status} for endpoint: ${endpoint}`);
}
} catch (error) {
lastError = error;
console.log(`端点 ${endpoint} 调用失败,尝试下一个`);
}
if (response.ok) {
const data = await response.json();
console.log('DNS查询成功:', data);
return data;
} else {
throw new Error(`HTTP error! status: ${response.status}`);
}
// 如果所有端点都失败,抛出最后一个错误
throw lastError || new Error('所有API端点调用失败');
} catch (error) {
console.error('DNS查询API调用失败:', error);

View File

@@ -60,6 +60,7 @@ function populateConfigForm(config) {
// DNS配置 - 使用函数安全设置值,避免 || 操作符可能的错误处理
setElementValue('dns-port', getSafeValue(dnsServerConfig.Port, 53));
setElementValue('dns-run-mode', getSafeValue(dnsServerConfig.QueryMode, 'parallel'));
setElementValue('dns-upstream-servers', getSafeArray(dnsServerConfig.UpstreamServers).join('\n'));
setElementValue('dns-dnssec-upstream-servers', getSafeArray(dnsServerConfig.DNSSECUpstreamServers).join('\n'));
//setElementValue('dns-stats-file', getSafeValue(dnsServerConfig.StatsFile, 'data/stats.json'));
@@ -69,7 +70,7 @@ function populateConfigForm(config) {
setElementValue('dns-cache-size', getSafeValue(dnsServerConfig.CacheSize, 100));
setElementValue('dns-max-cache-ttl', getSafeValue(dnsServerConfig.MaxCacheTTL, 120));
setElementValue('dns-min-cache-ttl', getSafeValue(dnsServerConfig.MinCacheTTL, 5));
setElementValue('dns-enable-ipv6', getSafeValue(dnsServerConfig.EnableIPv6, false));
setElementValue('dns-enable-ipv6', getSafeValue(dnsServerConfig.enableIPv6, false));
// HTTP配置
setElementValue('http-port', getSafeValue(httpServerConfig.Port, 8080));
// 屏蔽配置
@@ -96,6 +97,8 @@ function setElementValue(elementId, value) {
}
} else if (element.tagName === 'TEXTAREA') {
element.value = value;
} else if (element.tagName === 'SELECT') {
element.value = value;
} else if (element.tagName === 'BUTTON' && element.classList.contains('toggle-btn')) {
const icon = element.querySelector('i');
if (icon) {
@@ -220,6 +223,7 @@ function collectFormData() {
return {
dnsserver: {
port: dnsPort,
queryMode: getElementValue('dns-run-mode') || 'parallel',
upstreamServers: upstreamServers,
dnssecUpstreamServers: dnssecUpstreamServers,
timeout: timeout,
@@ -313,12 +317,12 @@ function showNotification(message, type = 'info') {
// 创建新通知
const notification = document.createElement('div');
notification.className = `notification fixed bottom-4 right-4 px-6 py-3 rounded-lg shadow-lg z-50 transform transition-transform duration-300 ease-in-out translate-y-0 opacity-0`;
notification.className = `notification fixed top-4 right-4 px-6 py-3 rounded-lg shadow-lg z-50 transform transition-transform duration-300 ease-in-out translate-y-0 opacity-0`;
// 设置通知样式兼容Tailwind和原生CSS
notification.style.cssText += `
position: fixed;
bottom: 16px;
top: 16px;
right: 16px;
padding: 16px 24px;
border-radius: 8px;
@@ -331,15 +335,17 @@ function showNotification(message, type = 'info') {
if (type === 'success') {
notification.style.backgroundColor = '#10b981';
notification.style.color = 'white';
notification.innerHTML = `<i class="fa fa-check-circle mr-2"></i>${message}`;
} else if (type === 'error') {
notification.style.backgroundColor = '#ef4444';
notification.style.color = 'white';
notification.innerHTML = `<i class="fa fa-exclamation-circle mr-2"></i>${message}`;
} else {
notification.style.backgroundColor = '#3b82f6';
notification.style.color = 'white';
notification.innerHTML = `<i class="fa fa-info-circle mr-2"></i>${message}`;
}
notification.textContent = message;
document.body.appendChild(notification);
// 显示通知
@@ -383,13 +389,28 @@ async function loadGFWListConfig() {
// 填充GFWList配置表单
function populateGFWListForm(config) {
const gfwListConfig = config.gfwList || {};
const enabled = getSafeValue(gfwListConfig.enabled, false);
setElementValue('gfwlist-enabled', getSafeValue(gfwListConfig.enabled, false));
setElementValue('gfwlist-enabled', enabled);
setElementValue('gfwlist-target-ip', getSafeValue(gfwListConfig.ip, ''));
setElementValue('gfwlist-google', getSafeValue(config.allowGoogle, false));
setElementValue('gfwlist-youtube', getSafeValue(config.allowYouTube, false));
setElementValue('gfwlist-facebook', getSafeValue(config.allowFacebook, false));
setElementValue('gfwlist-twitter', getSafeValue(config.allowTwitter, false));
setElementValue('gfwlist-amazon', getSafeValue(config.allowAmazon, false));
setElementValue('gfwlist-bbc', getSafeValue(config.allowBBC, false));
setElementValue('gfwlist-discord', getSafeValue(config.allowDiscord, false));
setElementValue('gfwlist-dropbox', getSafeValue(config.allowDropbox, false));
setElementValue('gfwlist-microsoft', getSafeValue(config.allowMicrosoft, false));
setElementValue('gfwlist-steam', getSafeValue(config.allowSteam, false));
setElementValue('gfwlist-telegram', getSafeValue(config.allowTelegram, false));
setElementValue('gfwlist-tiktok', getSafeValue(config.allowTikTok, false));
setElementValue('gfwlist-v2ex', getSafeValue(config.allowV2EX, false));
setElementValue('gfwlist-wikimedia', getSafeValue(config.allowWikimedia, false));
setElementValue('gfwlist-yahoo', getSafeValue(config.allowYahoo, false));
// 更新通行网站部分的显示效果
updateAllowedSitesSection(enabled);
}
// 保存GFWList配置
@@ -424,10 +445,53 @@ function collectGFWListFormData() {
allowGoogle: getElementValue('gfwlist-google'),
allowYouTube: getElementValue('gfwlist-youtube'),
allowFacebook: getElementValue('gfwlist-facebook'),
allowTwitter: getElementValue('gfwlist-twitter')
allowTwitter: getElementValue('gfwlist-twitter'),
allowAmazon: getElementValue('gfwlist-amazon'),
allowBBC: getElementValue('gfwlist-bbc'),
allowDiscord: getElementValue('gfwlist-discord'),
allowDropbox: getElementValue('gfwlist-dropbox'),
allowMicrosoft: getElementValue('gfwlist-microsoft'),
allowSteam: getElementValue('gfwlist-steam'),
allowTelegram: getElementValue('gfwlist-telegram'),
allowTikTok: getElementValue('gfwlist-tiktok'),
allowV2EX: getElementValue('gfwlist-v2ex'),
allowWikimedia: getElementValue('gfwlist-wikimedia'),
allowYahoo: getElementValue('gfwlist-yahoo')
};
}
// 更新通行网站部分的显示效果
function updateAllowedSitesSection(enabled) {
const section = document.getElementById('allowed-sites-section');
if (!section) return;
const siteCards = section.querySelectorAll('.bg-gray-50');
const siteLabels = section.querySelectorAll('.text-gray-700');
const siteToggles = section.querySelectorAll('.toggle-btn');
if (enabled) {
// GFWList已启用显示彩色且可点击
section.classList.remove('opacity-50');
siteCards.forEach(card => {
card.style.filter = 'grayscale(0%)';
});
siteToggles.forEach(toggle => {
toggle.disabled = false;
toggle.classList.remove('cursor-not-allowed');
});
} else {
// GFWList已禁用显示灰色且不可点击
section.classList.add('opacity-50');
siteCards.forEach(card => {
card.style.filter = 'grayscale(100%)';
});
siteToggles.forEach(toggle => {
toggle.disabled = true;
toggle.classList.add('cursor-not-allowed');
});
}
}
// 重启GFWList服务
async function handleRestartGFWListService() {
if (!confirm('确定要重启DNS服务吗重启期间服务可能会短暂不可用。')) return;
@@ -460,6 +524,11 @@ function setupGFWListEventListeners() {
// 切换按钮状态
const currentState = this.classList.contains('bg-success');
setElementValue(this.id, !currentState);
// 如果是GFWList启用开关更新通行网站部分的显示效果
if (this.id === 'gfwlist-enabled') {
updateAllowedSitesSection(!currentState);
}
});
});
}

File diff suppressed because it is too large Load Diff

View File

@@ -1232,11 +1232,11 @@ async function updateLogsTable(logs) {
// 构建跟踪器浮窗内容
const trackerTooltip = isTracker ? `
<div class="tracker-tooltip absolute z-50 bg-white shadow-lg rounded-md border p-3 min-w-64 text-sm">
<div class="font-semibold mb-1">已知跟踪器</div>
<div class="mb-1">名称: ${trackerInfo.name}</div>
<div class="mb-1">类别: ${trackersDatabase.categories[trackerInfo.categoryId] || '未知'}</div>
${trackerInfo.url ? `<div class="mb-1">URL: <a href="${trackerInfo.url}" target="_blank" class="text-blue-500 hover:underline">${trackerInfo.url}</a></div>` : ''}
${trackerInfo.source ? `<div class="mb-1">源: ${trackerInfo.source}</div>` : ''}
<div class="font-semibold mb-2">已知跟踪器</div>
<div class="mb-1"><strong>名称:</strong> ${trackerInfo.name || '未知'}</div>
<div class="mb-1"><strong>类别:</strong> ${trackerInfo.categoryId && trackersDatabase && trackersDatabase.categories ? trackersDatabase.categories[trackerInfo.categoryId] : '未知'}</div>
${trackerInfo.url ? `<div class="mb-1"><strong>URL:</strong> <a href="${trackerInfo.url}" target="_blank" class="text-blue-500 hover:underline">${trackerInfo.url}</a></div>` : ''}
${trackerInfo.source ? `<div class="mb-1"><strong>源:</strong> ${trackerInfo.source}</div>` : ''}
</div>
` : '';
@@ -1307,14 +1307,15 @@ async function updateLogsTable(logs) {
const iconContainer = row.querySelector('.tracker-icon-container');
const tooltip = iconContainer.querySelector('.tracker-tooltip');
if (iconContainer && tooltip) {
tooltip.style.display = 'none';
// 移除内联样式使用CSS类控制显示
tooltip.removeAttribute('style');
iconContainer.addEventListener('mouseenter', () => {
tooltip.style.display = 'block';
tooltip.classList.add('visible');
});
iconContainer.addEventListener('mouseleave', () => {
tooltip.style.display = 'none';
tooltip.classList.remove('visible');
});
}
}
@@ -1945,6 +1946,17 @@ async function showLogDetailModal(log) {
</div>
`;
// 构建跟踪器浮窗内容
const trackerTooltip = isTracker ? `
<div class="tracker-tooltip absolute z-50 bg-white shadow-lg rounded-md border p-3 min-w-64 text-sm">
<div class="font-semibold mb-2">已知跟踪器</div>
<div class="mb-1"><strong>名称:</strong> ${trackerInfo.name || '未知'}</div>
<div class="mb-1"><strong>类别:</strong> ${trackerInfo.categoryId && trackersDatabase && trackersDatabase.categories ? trackersDatabase.categories[trackerInfo.categoryId] : '未知'}</div>
${trackerInfo.url ? `<div class="mb-1"><strong>URL:</strong> <a href="${trackerInfo.url}" target="_blank" class="text-blue-500 hover:underline">${trackerInfo.url}</a></div>` : ''}
${trackerInfo.source ? `<div class="mb-1"><strong>源:</strong> ${trackerInfo.source}</div>` : ''}
</div>
` : '';
// 跟踪器信息
const trackerDiv = document.createElement('div');
trackerDiv.className = 'col-span-1 md:col-span-2 space-y-1';
@@ -1953,13 +1965,34 @@ async function showLogDetailModal(log) {
<div class="text-sm font-medium text-gray-900">
${isTracker ? `
<div class="flex items-center">
<i class="fa fa-eye text-red-500 mr-1"></i>
<span>${trackerInfo.name} (${trackersDatabase.categories[trackerInfo.categoryId] || '未知'})</span>
<div class="tracker-icon-container relative">
<i class="fa fa-eye text-red-500 mr-1" title="已知跟踪器"></i>
${trackerTooltip}
</div>
<span>${trackerInfo.name} (${trackerInfo.categoryId && trackersDatabase && trackersDatabase.categories ? trackersDatabase.categories[trackerInfo.categoryId] : '未知'})</span>
</div>
` : '无'}
</div>
`;
// 添加跟踪器图标悬停事件
if (isTracker) {
const iconContainer = trackerDiv.querySelector('.tracker-icon-container');
const tooltip = iconContainer.querySelector('.tracker-tooltip');
if (iconContainer && tooltip) {
// 移除内联样式使用CSS类控制显示
tooltip.removeAttribute('style');
iconContainer.addEventListener('mouseenter', () => {
tooltip.classList.add('visible');
});
iconContainer.addEventListener('mouseleave', () => {
tooltip.classList.remove('visible');
});
}
}
// 解析记录
const recordsDiv = document.createElement('div');
recordsDiv.className = 'col-span-1 md:col-span-2 space-y-1';

View File

@@ -1,115 +1,5 @@
// main.js - 主脚本文件
// 页面导航功能
function setupNavigation() {
// 侧边栏菜单项
const menuItems = document.querySelectorAll('nav a');
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')
];
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();
}
});
}
// 页面初始化函数 - 根据当前hash值初始化对应页面
function initPageByHash() {
const hash = window.location.hash.substring(1);
@@ -186,41 +76,6 @@ function initPageByHash() {
}
}
// 初始化函数
function init() {
// 设置导航
setupNavigation();
// 初始化页面
initPageByHash();
// 添加hashchange事件监听处理浏览器前进/后退按钮
window.addEventListener('hashchange', initPageByHash);
// 定期更新系统状态
setInterval(updateSystemStatus, 5000);
}
// 更新系统状态
function updateSystemStatus() {
fetch('/api/status')
.then(response => response.json())
.then(data => {
const uptimeElement = document.getElementById('uptime');
if (uptimeElement) {
uptimeElement.textContent = `正常运行中 | ${formatUptime(data.uptime)}`;
}
})
.catch(error => {
console.error('更新系统状态失败:', error);
const uptimeElement = document.getElementById('uptime');
if (uptimeElement) {
uptimeElement.textContent = '连接异常';
uptimeElement.classList.add('text-danger');
}
});
}
// 格式化运行时间
function formatUptime(milliseconds) {
// 简化版的格式化实际使用时需要根据API返回的数据格式调整
@@ -240,6 +95,29 @@ function formatUptime(milliseconds) {
}
}
// 更新系统状态
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() {
// 下拉菜单功能
@@ -392,6 +270,107 @@ function setupAccountFeatures() {
}
}
// 页面导航功能
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 init() {
// 设置导航

View File

@@ -1248,7 +1248,7 @@ function showNotification(message, type = 'info') {
// 创建新通知
const notification = document.createElement('div');
notification.className = `notification fixed bottom-4 right-4 px-6 py-3 rounded-lg shadow-lg z-50 transform transition-all duration-300 ease-in-out translate-y-0 opacity-0`;
notification.className = `notification fixed top-4 right-4 px-6 py-3 rounded-lg shadow-lg z-50 transform transition-all duration-300 ease-in-out translate-y-0 opacity-0`;
// 设置通知样式
if (type === 'success') {