// 屏蔽管理页面功能实现 // 初始化屏蔽管理页面 function initShieldPage() { // 加载屏蔽规则统计信息 loadShieldStats(); // 加载本地规则 loadLocalRules(); // 加载远程黑名单 loadRemoteBlacklists(); // 设置事件监听器 setupShieldEventListeners(); } // 加载屏蔽规则统计信息 async function loadShieldStats() { showLoading('加载屏蔽规则统计信息...'); try { const response = await fetch('/api/shield'); if (!response.ok) { throw new Error(`加载失败: ${response.status}`); } const stats = await response.json(); // 更新统计信息 const elements = [ { id: 'domain-rules-count', value: stats.domainRulesCount }, { id: 'domain-exceptions-count', value: stats.domainExceptionsCount }, { id: 'regex-rules-count', value: stats.regexRulesCount }, { id: 'regex-exceptions-count', value: stats.regexExceptionsCount }, { id: 'hosts-rules-count', value: stats.hostsRulesCount }, { id: 'blacklist-count', value: stats.blacklistCount } ]; elements.forEach(item => { const element = document.getElementById(item.id); if (element) { element.textContent = item.value || 0; } }); hideLoading(); } catch (error) { console.error('加载屏蔽规则统计信息失败:', error); showErrorMessage('加载屏蔽规则统计信息失败'); hideLoading(); } } // 加载本地规则 async function loadLocalRules() { showLoading('加载本地规则...'); try { const response = await fetch('/api/shield/localrules'); if (!response.ok) { throw new Error(`加载失败: ${response.status}`); } const data = await response.json(); // 更新本地规则数量显示 if (document.getElementById('local-rules-count')) { document.getElementById('local-rules-count').textContent = data.localRulesCount || 0; } // 合并所有本地规则 let rules = []; // 添加域名规则 if (Array.isArray(data.domainRules)) { rules = rules.concat(data.domainRules); } // 添加域名排除规则 if (Array.isArray(data.domainExceptions)) { rules = rules.concat(data.domainExceptions); } // 添加正则规则 if (Array.isArray(data.regexRules)) { rules = rules.concat(data.regexRules); } // 添加正则排除规则 if (Array.isArray(data.regexExceptions)) { rules = rules.concat(data.regexExceptions); } updateRulesTable(rules); hideLoading(); } catch (error) { console.error('加载本地规则失败:', error); showErrorMessage('加载本地规则失败'); hideLoading(); } } // 加载远程规则 async function loadRemoteRules() { showLoading('加载远程规则...'); try { const response = await fetch('/api/shield/remoterules'); if (!response.ok) { throw new Error(`加载失败: ${response.status}`); } const data = await response.json(); // 更新远程规则数量显示 if (document.getElementById('remote-rules-count')) { document.getElementById('remote-rules-count').textContent = data.remoteRulesCount || 0; } // 合并所有远程规则 let rules = []; // 添加域名规则 if (Array.isArray(data.domainRules)) { rules = rules.concat(data.domainRules); } // 添加域名排除规则 if (Array.isArray(data.domainExceptions)) { rules = rules.concat(data.domainExceptions); } // 添加正则规则 if (Array.isArray(data.regexRules)) { rules = rules.concat(data.regexRules); } // 添加正则排除规则 if (Array.isArray(data.regexExceptions)) { rules = rules.concat(data.regexExceptions); } updateRulesTable(rules); hideLoading(); } catch (error) { console.error('加载远程规则失败:', error); showErrorMessage('加载远程规则失败'); hideLoading(); } } // 更新规则表格 function updateRulesTable(rules) { const tbody = document.getElementById('rules-table-body'); // 清空表格 tbody.innerHTML = ''; if (rules.length === 0) { const emptyRow = document.createElement('tr'); emptyRow.innerHTML = '暂无规则'; tbody.appendChild(emptyRow); return; } // 对于大量规则,限制显示数量 const maxRulesToShow = 1000; // 限制最大显示数量 const rulesToShow = rules.length > maxRulesToShow ? rules.slice(0, maxRulesToShow) : rules; // 使用DocumentFragment提高性能 const fragment = document.createDocumentFragment(); rulesToShow.forEach(rule => { const tr = document.createElement('tr'); tr.className = 'border-b border-gray-200'; const tdRule = document.createElement('td'); tdRule.className = 'py-3 px-4'; tdRule.textContent = rule; const tdAction = document.createElement('td'); tdAction.className = 'py-3 px-4 text-right'; const deleteBtn = document.createElement('button'); deleteBtn.className = 'delete-rule-btn px-3 py-1 bg-danger text-white rounded-md hover:bg-danger/90 transition-colors text-sm'; deleteBtn.dataset.rule = rule; deleteBtn.innerHTML = ''; deleteBtn.addEventListener('click', handleDeleteRule); tdAction.appendChild(deleteBtn); tr.appendChild(tdRule); tr.appendChild(tdAction); fragment.appendChild(tr); }); // 一次性添加所有行到DOM tbody.appendChild(fragment); // 如果有更多规则,添加提示 if (rules.length > maxRulesToShow) { const infoRow = document.createElement('tr'); infoRow.innerHTML = `显示前 ${maxRulesToShow} 条规则,共 ${rules.length} 条`; tbody.appendChild(infoRow); } } // 处理删除规则 async function handleDeleteRule(e) { const rule = e.target.closest('.delete-rule-btn').dataset.rule; showLoading('删除规则中...'); try { const response = await fetch('/api/shield', { method: 'DELETE', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ rule }) }); if (!response.ok) { throw new Error('Failed to delete rule'); } showSuccessMessage('规则删除成功'); // 重新加载规则 loadLocalRules(); // 重新加载统计信息 loadShieldStats(); hideLoading(); } catch (error) { console.error('Error deleting rule:', error); showErrorMessage('删除规则失败'); hideLoading(); } } // 添加新规则 async function handleAddRule() { const rule = document.getElementById('new-rule').value.trim(); if (!rule) { showErrorMessage('规则不能为空'); return; } showLoading('添加规则中...'); try { const response = await fetch('/api/shield', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ rule }) }); if (!response.ok) { throw new Error('Failed to add rule'); } showSuccessMessage('规则添加成功'); // 清空输入框 document.getElementById('new-rule').value = ''; // 重新加载规则 loadLocalRules(); // 重新加载统计信息 loadShieldStats(); hideLoading(); } catch (error) { console.error('Error adding rule:', error); showErrorMessage('添加规则失败'); hideLoading(); } } // 加载远程黑名单 async function loadRemoteBlacklists() { showLoading('加载远程黑名单...'); try { const response = await fetch('/api/shield/blacklists'); if (!response.ok) { throw new Error(`加载失败: ${response.status}`); } const blacklists = await response.json(); // 确保blacklists是数组 const blacklistArray = Array.isArray(blacklists) ? blacklists : []; updateBlacklistsTable(blacklistArray); hideLoading(); } catch (error) { console.error('加载远程黑名单失败:', error); showErrorMessage('加载远程黑名单失败'); hideLoading(); } } // 更新黑名单表格 function updateBlacklistsTable(blacklists) { const tbody = document.getElementById('blacklists-table-body'); // 清空表格 tbody.innerHTML = ''; if (blacklists.length === 0) { const emptyRow = document.createElement('tr'); emptyRow.innerHTML = '暂无黑名单'; tbody.appendChild(emptyRow); return; } // 对于大量黑名单,限制显示数量 const maxBlacklistsToShow = 100; // 限制最大显示数量 const blacklistsToShow = blacklists.length > maxBlacklistsToShow ? blacklists.slice(0, maxBlacklistsToShow) : blacklists; // 使用DocumentFragment提高性能 const fragment = document.createDocumentFragment(); blacklistsToShow.forEach(blacklist => { const tr = document.createElement('tr'); tr.className = 'border-b border-gray-200'; // 名称单元格 const tdName = document.createElement('td'); tdName.className = 'py-3 px-4'; tdName.textContent = blacklist.Name; // URL单元格 const tdUrl = document.createElement('td'); tdUrl.className = 'py-3 px-4 truncate max-w-xs'; tdUrl.textContent = blacklist.URL; // 状态单元格 const tdStatus = document.createElement('td'); tdStatus.className = 'py-3 px-4 text-center'; const statusDot = document.createElement('span'); statusDot.className = `inline-block w-3 h-3 rounded-full ${blacklist.Enabled ? 'bg-success' : 'bg-gray-300'}`; tdStatus.appendChild(statusDot); // 操作单元格 const tdActions = document.createElement('td'); tdActions.className = 'py-3 px-4 text-right space-x-2'; // 更新按钮 const updateBtn = document.createElement('button'); updateBtn.className = 'update-blacklist-btn px-3 py-1 bg-primary text-white rounded-md hover:bg-primary/90 transition-colors text-sm'; updateBtn.dataset.url = blacklist.URL; updateBtn.innerHTML = ''; updateBtn.addEventListener('click', handleUpdateBlacklist); // 删除按钮 const deleteBtn = document.createElement('button'); deleteBtn.className = 'delete-blacklist-btn px-3 py-1 bg-danger text-white rounded-md hover:bg-danger/90 transition-colors text-sm'; deleteBtn.dataset.url = blacklist.URL; deleteBtn.innerHTML = ''; deleteBtn.addEventListener('click', handleDeleteBlacklist); tdActions.appendChild(updateBtn); tdActions.appendChild(deleteBtn); tr.appendChild(tdName); tr.appendChild(tdUrl); tr.appendChild(tdStatus); tr.appendChild(tdActions); fragment.appendChild(tr); }); // 一次性添加所有行到DOM tbody.appendChild(fragment); // 如果有更多黑名单,添加提示 if (blacklists.length > maxBlacklistsToShow) { const infoRow = document.createElement('tr'); infoRow.innerHTML = `显示前 ${maxBlacklistsToShow} 个黑名单,共 ${blacklists.length} 个`; tbody.appendChild(infoRow); } } // 处理更新单个黑名单 async function handleUpdateBlacklist(e) { const url = e.target.closest('.update-blacklist-btn').dataset.url; showLoading('更新黑名单中...'); try { const response = await fetch(`/api/shield/blacklists/${encodeURIComponent(url)}/update`, { method: 'POST' }); if (!response.ok) { throw new Error('Failed to update blacklist'); } showSuccessMessage('黑名单更新成功'); // 重新加载黑名单 loadRemoteBlacklists(); // 重新加载统计信息 loadShieldStats(); hideLoading(); } catch (error) { console.error('Error updating blacklist:', error); showErrorMessage('更新黑名单失败'); hideLoading(); } } // 处理删除黑名单 async function handleDeleteBlacklist(e) { const url = e.target.closest('.delete-blacklist-btn').dataset.url; showLoading('删除黑名单中...'); try { const response = await fetch(`/api/shield/blacklists/${encodeURIComponent(url)}`, { method: 'DELETE' }); if (!response.ok) { throw new Error('Failed to delete blacklist'); } showSuccessMessage('黑名单删除成功'); // 重新加载黑名单 loadRemoteBlacklists(); // 重新加载统计信息 loadShieldStats(); hideLoading(); } catch (error) { console.error('Error deleting blacklist:', error); showErrorMessage('删除黑名单失败'); hideLoading(); } } // 处理添加黑名单 async function handleAddBlacklist() { const name = document.getElementById('blacklist-name').value.trim(); const url = document.getElementById('blacklist-url').value.trim(); if (!name || !url) { showErrorMessage('名称和URL不能为空'); return; } showLoading('添加黑名单中...'); try { const response = await fetch('/api/shield/blacklists', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name, url }) }); if (!response.ok) { throw new Error('Failed to add blacklist'); } showSuccessMessage('黑名单添加成功'); // 清空输入框 document.getElementById('blacklist-name').value = ''; document.getElementById('blacklist-url').value = ''; // 重新加载黑名单 loadRemoteBlacklists(); // 重新加载统计信息 loadShieldStats(); hideLoading(); } catch (error) { console.error('Error adding blacklist:', error); showErrorMessage('添加黑名单失败'); hideLoading(); } } // 设置事件监听器 function setupShieldEventListeners() { // 本地规则管理事件 document.getElementById('save-rule-btn').addEventListener('click', handleAddRule); // 远程黑名单管理事件 document.getElementById('save-blacklist-btn').addEventListener('click', handleAddBlacklist); // 添加切换查看本地规则和远程规则的事件监听 if (document.getElementById('view-local-rules-btn')) { document.getElementById('view-local-rules-btn').addEventListener('click', loadLocalRules); } if (document.getElementById('view-remote-rules-btn')) { document.getElementById('view-remote-rules-btn').addEventListener('click', loadRemoteRules); } } // 显示成功消息 function showSuccessMessage(message) { showNotification(message, 'success'); } // 显示错误消息 function showErrorMessage(message) { showNotification(message, 'error'); } // 显示加载状态 function showLoading(message = '加载中...') { // 移除现有加载状态 hideLoading(); // 创建加载状态元素 const loading = document.createElement('div'); loading.className = 'loading-overlay fixed inset-0 bg-black/50 flex items-center justify-center z-50'; loading.innerHTML = `
${message}
`; document.body.appendChild(loading); } // 隐藏加载状态 function hideLoading() { const loading = document.querySelector('.loading-overlay'); if (loading) { loading.remove(); } } // 显示通知 function showNotification(message, type = 'info') { // 移除现有通知 const existingNotification = document.querySelector('.notification'); if (existingNotification) { existingNotification.remove(); } // 创建新通知 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`; // 设置通知样式 if (type === 'success') { notification.classList.add('bg-green-500', 'text-white'); } else if (type === 'error') { notification.classList.add('bg-red-500', 'text-white'); } else { notification.classList.add('bg-blue-500', 'text-white'); } notification.innerHTML = `
${message}
`; document.body.appendChild(notification); // 显示通知 setTimeout(() => { notification.classList.remove('opacity-0'); notification.classList.add('opacity-100'); }, 10); // 3秒后隐藏通知 setTimeout(() => { notification.classList.remove('opacity-100'); notification.classList.add('opacity-0'); setTimeout(() => { notification.remove(); }, 300); }, 3000); } // 页面加载完成后初始化 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initShieldPage); } else { initShieldPage(); } // 当切换到屏蔽管理页面时重新加载数据 document.addEventListener('DOMContentLoaded', () => { // 监听hash变化,当切换到屏蔽管理页面时重新加载数据 window.addEventListener('hashchange', () => { if (window.location.hash === '#shield') { initShieldPage(); } }); });