您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
自动清理CSDN网站上的广告、垃圾内容和登录(不可用)弹窗,支持自定义配置。优化UI设计,使用现代化美观样式,避免影响CSDN原有样式。
// ==UserScript== // @name CSDN-PRO,CSDN优化工具 // @namespace http://tampermonkey.net/ // @version 1.0.8 // @description 自动清理CSDN网站上的广告、垃圾内容和登录(不可用)弹窗,支持自定义配置。优化UI设计,使用现代化美观样式,避免影响CSDN原有样式。 // @author tanzz // @match https://*.csdn.net/* // @grant GM_addStyle // @run-at document-idle // @license MIT // ==/UserScript== (function () { 'use strict'; // 脚本版本,用于检测更新 const SCRIPT_VERSION = '1.0.8'; // 调试开关配置 - 控制调试日志输出 // 生产环境保持关闭,确保用户体验 const DEBUG_CONFIG = { enabled: false, // 生产环境静默模式 (关闭所有调试输出) logLevel: 'warn' // 仅显示警告和错误 }; // 初始配置结构(树形) const defaultConfig = { version: SCRIPT_VERSION, // 添加版本信息 login: { enabled: true, name: '登录(不可用)清理', children: { loginPopups: { enabled: true, name: '登录(不可用)弹窗', selectors: ['.login-mark', '.passport-login-container', '.modal-backdrop', '.passport-login-tip-container', 'body > div.passport-login-tip-container.false' ] }, } }, leftSide: { enabled: true, name: '左侧栏清理', children: { author: { enabled: true, name: '作者信息', selectors: ['#asideProfile'] }, carouselAds: { enabled: true, name: '轮播图广告', selectors: ['.swiper-remuneration-container'] }, cKnow: { enabled: true, name: '知道', selectors: ['#mainBox > aside > div.c-blog-side-box'] }, hisSelection: { enabled: true, name: 'TA的精选', selectors: ['#his-selection'] }, hotArticle: { enabled: true, name: '热门文章', selectors: ['#asideHotArticle'] }, } }, ads: { enabled: true, name: '广告清理', children: { bannerAds: { enabled: true, name: '横幅广告', selectors: ['.banner-ad', '.advertisement'] }, feedAds: { enabled: true, name: '信息流广告', selectors: ['.feed-advert', '.flow-ad'] }, tracking: { enabled: true, name: '统计跟踪元素', selectors: ['.csdn-tracking-statistics'] }, } }, garbageContent: { enabled: true, name: '垃圾内容清理', children: { toolbar: { enabled: true, name: '标题栏', children: { vipIcon: { enabled: true, name: '会员图标', selectors: ['.toolbar-btn-vip'] } } }, recommendedArticles: { enabled: true, name: '推荐文章', selectors: ['.recommend-article', '.related-read'] }, footerContent: { enabled: true, name: '底部垃圾内容', selectors: ['.blog-footer-bottom'] }, toolbox: { enabled: true, name: '工具栏', children: { aiAssistant: { enabled: true, name: 'AI助手', selectors: ['.slide-details-cknows-box'] }, appBox: { enabled: true, name: '应用下载', selectors: ['a.option-box.styleab[data-type="app"]'] }, chatBox: { enabled: true, name: '客服', selectors: [ 'a.option-box.styleab[data-type="cs"]', ] }, toolbox: { enabled: true, name: '收藏弹窗', selectors: ['.tool-active-list'], } } } } }, // 定期检查的时间间隔(毫秒) checkInterval: 1000 }; // 加载用户配置 let userConfig; function loadConfig() { try { const savedConfig = localStorage.getItem('csdnCleanerConfig'); debugLog('debug', '从localStorage读取的原始配置数据:', savedConfig); if (savedConfig) { const parsedConfig = JSON.parse(savedConfig); debugLog('debug', '从localStorage加载的配置:', parsedConfig); // 检查版本更新 if (parsedConfig.version !== SCRIPT_VERSION) { debugLog('info', `检测到脚本版本更新: ${parsedConfig.version} -> ${SCRIPT_VERSION}`); debugLog('info', '将使用新的默认配置但保留用户设置'); // 版本更新时,清理旧缓存并保留用户设置 userConfig = JSON.parse(JSON.stringify(defaultConfig)); // 清理可能的旧版本缓存数据 const oldKeys = [ 'csdnCleanerConfig_old', 'csdnCleanerCache', 'csdnCleanerSelectors', 'csdnCleanerVersion' ]; oldKeys.forEach(key => { if (localStorage.getItem(key)) { localStorage.removeItem(key); debugLog('debug', `已清理旧缓存: ${key}`); } }); // 只保留用户的启用/禁用设置,使用新的选择器 function preserveUserSettings(target, source) { for (let key in source) { if (key === 'selectors' || key === 'version') { // 选择器和版本号始终使用新的 continue; } if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) { if (target[key] && target[key].enabled !== undefined) { source[key].enabled = target[key].enabled; } if (source[key].children) { preserveUserSettings(target[key]?.children || {}, source[key].children); } } } } preserveUserSettings(parsedConfig, userConfig); saveConfig(); // 保存更新后的配置 debugLog('info', '版本更新完成,旧缓存已清理'); } else { // 版本相同,正常合并 userConfig = JSON.parse(JSON.stringify(defaultConfig)); // 深合并配置,确保结构完整 function deepMerge(target, source) { for (let key in source) { if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) { if (!target[key]) target[key] = {}; if (typeof target[key] === 'object' && !Array.isArray(target[key])) { deepMerge(target[key], source[key]); } } else { target[key] = source[key]; } } return target; } deepMerge(userConfig, parsedConfig); debugLog('debug', '合并后的配置:', userConfig); } } else { debugLog('info', '未找到已保存的配置,使用默认配置'); userConfig = JSON.parse(JSON.stringify(defaultConfig)); } } catch (error) { debugLog('error', '加载配置失败:', error); userConfig = JSON.parse(JSON.stringify(defaultConfig)); } } loadConfig(); // 保存配置到localStorage function saveConfig() { try { const configToSave = JSON.stringify(userConfig, null, 2); debugLog('debug', '准备保存的配置:', configToSave); localStorage.setItem('csdnCleanerConfig', configToSave); debugLog('info', '配置已成功保存到localStorage'); // 验证保存 const savedConfig = localStorage.getItem('csdnCleanerConfig'); debugLog('debug', '保存后验证:', savedConfig); } catch (error) { debugLog('error', '保存配置失败:', error); alert('保存配置失败: ' + error.message); } } // 强制清理缓存 function forceClearCache() { try { debugLog('info', '开始强制清理缓存...'); // 清理所有相关缓存 const keysToRemove = [ 'csdnCleanerConfig', 'csdnCleanerConfig_old', 'csdnCleanerCache', 'csdnCleanerSelectors', 'csdnCleanerVersion' ]; keysToRemove.forEach(key => { if (localStorage.getItem(key)) { localStorage.removeItem(key); debugLog('debug', `已清理缓存: ${key}`); } }); debugLog('info', '缓存清理完成,将重新加载默认配置'); // 重新加载配置 userConfig = JSON.parse(JSON.stringify(defaultConfig)); saveConfig(); alert('缓存已清理!页面将刷新以应用最新配置。'); location.reload(); } catch (error) { debugLog('error', '清理缓存失败:', error); alert('清理缓存失败: ' + error.message); } } // 递归获取所有启用的选择器 function getAllEnabledSelectors(configNode) { let selectors = []; // 如果节点本身不启用,直接返回空数组 if (!configNode.enabled) { debugLog('debug', '节点已禁用,跳过:', configNode.name || 'unnamed'); return selectors; } if (configNode.selectors) { selectors = selectors.concat(configNode.selectors); debugLog('debug', `节点 ${configNode.name || '未命名'} 的选择器:`, configNode.selectors); } if (configNode.children) { Object.values(configNode.children).forEach(child => { selectors = selectors.concat(getAllEnabledSelectors(child)); }); } return selectors; } // 移除元素函数 function removeElements(selectors) { selectors.forEach(selector => { const elements = document.querySelectorAll(selector); elements.forEach(el => { // 先尝试隐藏元素 el.style.display = 'none'; // 然后从DOM中移除 if (el.parentNode) { el.parentNode.removeChild(el); } }); }); } // 统一调试日志函数 function debugLog(level, ...args) { if (!DEBUG_CONFIG.enabled) return; const levels = ['debug', 'info', 'warn', 'error']; const currentLevelIndex = levels.indexOf(DEBUG_CONFIG.logLevel); const messageLevelIndex = levels.indexOf(level); if (messageLevelIndex >= currentLevelIndex) { console[level](`[CSDN清理工具 ${level.toUpperCase()}]:`, ...args); } } // 主清理函数 function cleanPage() { debugLog('info', '开始清理页面...'); // 动态记录所有配置状态 function logConfigStatus(config, prefix = '') { Object.entries(config).forEach(([key, value]) => { if (typeof value === 'object' && value !== null && key !== 'selectors') { if (value.enabled !== undefined) { debugLog('debug', `${prefix}${key}: ${value.enabled ? '启用' : '禁用'}`); } if (value.children) { logConfigStatus(value.children, `${prefix}${key}.`); } } }); } // 记录所有配置状态 logConfigStatus(userConfig); // 动态记录所有选择器匹配情况 function logSelectors(config, prefix = '') { Object.entries(config).forEach(([key, value]) => { if (typeof value === 'object' && value !== null) { if (value.enabled && value.selectors) { value.selectors.forEach(selector => { const elements = document.querySelectorAll(selector); debugLog('debug', `${prefix}${key} - 选择器 "${selector}" 匹配到 ${elements.length} 个元素`); }); } if (value.children) { logSelectors(value.children, `${prefix}${key}.`); } } }); } // 记录所有选择器匹配情况(仅在调试模式下显示) logSelectors(userConfig); // 获取所有启用的选择器 const allSelectors = []; function collectAllSelectors(config) { let selectors = []; Object.values(config).forEach(node => { if (typeof node === 'object' && node !== null) { selectors = selectors.concat(getAllEnabledSelectors(node)); } }); return selectors; } const allEnabledSelectors = collectAllSelectors(userConfig); debugLog('debug', '启用的选择器统计:', { 总选择器数量: allEnabledSelectors.length }); // 移除所有匹配的元素 removeElements(allEnabledSelectors); // 移除可能的弹窗事件监听 document.body.style.overflow = 'auto'; // 恢复页面滚动 debugLog('info', '页面清理完成'); } // 显示版本信息 debugLog('info', `CSDN Cleaner v${SCRIPT_VERSION} 正式版已加载`); // 初始清理 cleanPage(); // 设置定期检查,处理动态加载的内容 setInterval(cleanPage, defaultConfig.checkInterval); // 创建配置面板UI function createConfigPanel() { // 添加样式 - 现代化的美观设计 GM_addStyle( '#csdn-cleaner-panel {' + ' position: fixed;' + ' top: 50%;' + ' transform: translateY(-50%);' + ' right: 20px;' + ' width: 320px;' + ' max-height: 80vh;' + ' background: #ffffff;' + ' border: 1px solid #e2e8f0;' + ' border-radius: 12px;' + ' box-shadow: 0 10px 25px rgba(0,0,0,0.1), 0 4px 6px rgba(0,0,0,0.05);' + ' font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;' + ' font-size: 14px;' + ' line-height: 1.5;' + ' z-index: 9999;' + ' overflow: hidden;' + ' backdrop-filter: blur(10px);' + ' display: flex;' + ' flex-direction: column;' + '}' + '#csdn-cleaner-toggle {' + ' position: fixed;' + ' top: 50%;' + ' right: 0px;' + ' transform: translateY(-50%);' + ' width: 24px;' + ' height: 60px;' + ' background: linear-gradient(135deg, rgba(102, 126, 234, 0.7) 0%, rgba(118, 75, 162, 0.7) 100%);' + ' border: none;' + ' border-radius: 12px 0 0 12px;' + ' color: white;' + ' cursor: pointer;' + ' font-size: 10px;' + ' line-height: 1.1;' + ' z-index: 1000;' + ' box-shadow: -1px 0 6px rgba(102, 126, 234, 0.15);' + ' transition: all 0.3s ease;' + ' display: flex;' + ' align-items: center;' + ' justify-content: center;' + ' opacity: 0.5;' + ' writing-mode: vertical-lr;' + ' text-orientation: mixed;' + ' padding: 6px 2px;' + '}' + '#csdn-cleaner-toggle:hover {' + ' opacity: 0.8;' + ' transform: translateY(-50%) translateX(-1px);' + ' box-shadow: -2px 0 8px rgba(102, 126, 234, 0.25);' + '}' + '#csdn-cleaner-panel h3 {' + ' margin: 0 0 20px 0;' + ' padding: 20px 20px 0 20px;' + ' font-size: 18px;' + ' font-weight: 700;' + ' color: #1a202c;' + ' text-align: center;' + ' background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);' + ' -webkit-background-clip: text;' + ' -webkit-text-fill-color: transparent;' + ' background-clip: text;' + '}' + '.config-group {' + ' margin-bottom: 12px;' + '}' + '.config-header {' + ' display: flex;' + ' align-items: center;' + ' margin-bottom: 6px;' + ' cursor: pointer;' + ' padding: 8px 0;' + ' border-radius: 6px;' + ' transition: all 0.2s ease;' + '}' + '.config-header:hover {' + ' background: #f7fafc;' + ' transform: translateX(2px);' + '}' + '.config-header input[type="checkbox"] {' + ' margin: 0 10px 0 0;' + ' width: 16px;' + ' height: 16px;' + ' cursor: pointer;' + ' accent-color: #667eea;' + '}' + '.config-header label {' + ' cursor: pointer;' + ' user-select: none;' + ' color: #2d3748;' + ' font-size: 14px;' + ' font-weight: 500;' + '}' + '.config-children {' + ' margin-left: 24px;' + ' margin-top: 6px;' + ' border-left: 2px solid #e2e8f0;' + ' padding-left: 12px;' + '}' + '.config-children .config-header label {' + ' font-size: 13px;' + ' color: #4a5568;' + ' font-weight: 400;' + '}' + '.button-container {' + ' padding: 0 20px 15px 20px;' + ' margin-top: 15px;' + ' border-top: 1px solid #e2e8f0;' + ' padding-top: 12px;' + ' flex-shrink: 0;' + ' display: flex;' + ' gap: 10px;' + '}' + '.save-btn, .reset-btn {' + ' border: none;' + ' padding: 10px 16px;' + ' border-radius: 8px;' + ' cursor: pointer;' + ' font-size: 14px;' + ' font-weight: 600;' + ' width: 50%;' + ' margin-top: 8px;' + ' transition: all 0.3s ease;' + ' font-family: inherit;' + '}' + '.save-btn {' + ' background: linear-gradient(135deg, #48bb78 0%, #38a169 100%);' + ' color: white;' + ' box-shadow: 0 2px 8px rgba(72, 187, 120, 0.3);' + '}' + '.save-btn:hover {' + ' transform: translateY(-1px);' + ' box-shadow: 0 4px 12px rgba(72, 187, 120, 0.4);' + '}' + '.reset-btn {' + ' background: linear-gradient(135deg, #f56565 0%, #e53e3e 100%);' + ' color: white;' + ' box-shadow: 0 2px 8px rgba(245, 101, 101, 0.3);' + '}' + '.reset-btn:hover {' + ' transform: translateY(-1px);' + ' box-shadow: 0 4px 12px rgba(245, 101, 101, 0.4);' + '}' + '.scroll-container {' + ' padding: 0 20px;' + ' max-height: 450px;' + ' overflow-y: auto;' + ' scrollbar-width: thin;' + ' scrollbar-color: #cbd5e0 #f7fafc;' + ' overscroll-behavior: contain;' + '}' + '.scroll-container::-webkit-scrollbar {' + ' width: 6px;' + '}' + '.scroll-container::-webkit-scrollbar-track {' + ' background: #f7fafc;' + ' border-radius: 3px;' + '}' + '.scroll-container::-webkit-scrollbar-thumb {' + ' background: #cbd5e0;' + ' border-radius: 3px;' + '}' + '.scroll-container::-webkit-scrollbar-thumb:hover {' + ' background: #a0aec0;' + '}' ); // 切换按钮 - 可拖拽 const toggleBtn = document.createElement('button'); toggleBtn.id = 'csdn-cleaner-toggle'; toggleBtn.textContent = '设置'; toggleBtn.draggable = true; document.body.appendChild(toggleBtn); // 拖拽功能 let isDragging = false; let currentY; let initialY; let yOffset = 0; // 从localStorage读取保存的位置 const savedPosition = localStorage.getItem('csdnCleanerTogglePosition'); if (savedPosition) { try { const pos = JSON.parse(savedPosition); toggleBtn.style.top = pos.top + 'px'; toggleBtn.style.transform = 'translateY(0)'; yOffset = pos.top; } catch (e) { // 如果位置数据无效,使用默认位置 } } toggleBtn.addEventListener('dragstart', function(e) { isDragging = true; initialY = e.clientY - yOffset; toggleBtn.style.transition = 'none'; e.dataTransfer.effectAllowed = 'move'; e.dataTransfer.setData('text/html', ''); }); document.addEventListener('dragover', function(e) { if (isDragging) { e.preventDefault(); e.dataTransfer.dropEffect = 'move'; } }); document.addEventListener('drop', function(e) { if (isDragging) { e.preventDefault(); isDragging = false; currentY = e.clientY; yOffset = currentY - initialY; // 限制在可视窗口内 const maxY = window.innerHeight - toggleBtn.offsetHeight; const newTop = Math.max(0, Math.min(currentY - initialY, maxY)); toggleBtn.style.top = newTop + 'px'; toggleBtn.style.transform = 'translateY(0)'; // 保存位置到localStorage localStorage.setItem('csdnCleanerTogglePosition', JSON.stringify({ top: newTop })); toggleBtn.style.transition = 'all 0.3s ease'; } }); toggleBtn.addEventListener('dragend', function(e) { if (isDragging) { isDragging = false; toggleBtn.style.transition = 'all 0.3s ease'; } }); // 触摸设备支持 let touchStartY = 0; let touchCurrentY = 0; toggleBtn.addEventListener('touchstart', function(e) { touchStartY = e.touches[0].clientY; toggleBtn.style.transition = 'none'; }); toggleBtn.addEventListener('touchmove', function(e) { e.preventDefault(); touchCurrentY = e.touches[0].clientY; const deltaY = touchCurrentY - touchStartY; const currentTop = parseInt(toggleBtn.style.top) || (window.innerHeight / 2 - toggleBtn.offsetHeight / 2); // 限制在可视窗口内 const maxY = window.innerHeight - toggleBtn.offsetHeight; const newTop = Math.max(0, Math.min(currentTop + deltaY, maxY)); toggleBtn.style.top = newTop + 'px'; toggleBtn.style.transform = 'translateY(0)'; touchStartY = touchCurrentY; // 保存位置 localStorage.setItem('csdnCleanerTogglePosition', JSON.stringify({ top: newTop })); }); toggleBtn.addEventListener('touchend', function(e) { toggleBtn.style.transition = 'all 0.3s ease'; }); // 创建配置面板 const panel = document.createElement('div'); panel.id = 'csdn-cleaner-panel'; panel.style.display = 'none'; document.body.appendChild(panel); // 创建标题容器 const titleContainer = document.createElement('div'); titleContainer.style.cssText = 'display: flex; align-items: center; justify-content: space-between; padding: 20px 20px 0 20px; margin-bottom: 20px;'; // 创建面板标题 const title = document.createElement('h3'); title.textContent = 'CSDN清理工具'; title.style.cssText = 'margin: 0; font-size: 18px; font-weight: 700; color: #1a202c; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;'; // 创建清理缓存小按钮 const clearCacheBtn = document.createElement('button'); clearCacheBtn.textContent = '⚡'; clearCacheBtn.title = '清理缓存'; clearCacheBtn.style.cssText = 'width: 28px; height: 28px; background: #f7fafc; border: 1px solid #e2e8f0; border-radius: 6px; font-size: 14px; cursor: pointer; transition: all 0.2s ease; display: flex; align-items: center; justify-content: center;'; clearCacheBtn.addEventListener('mouseenter', function() { this.style.background = '#edf2f7'; this.style.transform = 'scale(1.1)'; }); clearCacheBtn.addEventListener('mouseleave', function() { this.style.background = '#f7fafc'; this.style.transform = 'scale(1)'; }); clearCacheBtn.addEventListener('click', function(e) { e.stopPropagation(); debugLog('info', '清理缓存按钮被点击'); if (confirm('确定要清理缓存并重新加载配置吗?')) { forceClearCache(); } }); titleContainer.appendChild(title); titleContainer.appendChild(clearCacheBtn); panel.appendChild(titleContainer); // 创建可滚动的内容容器 const scrollContainer = document.createElement('div'); scrollContainer.className = 'scroll-container'; scrollContainer.style.cssText = 'flex: 1; overflow-y: auto; padding: 0 20px;'; panel.appendChild(scrollContainer); // 根据路径获取配置值 function getConfigValueByPath(path) { const parts = path.split('.'); let current = userConfig; // 处理顶层节点 if (parts.length === 1) { return current[parts[0]] ? current[parts[0]].enabled : undefined; } // 处理嵌套节点 for (let i = 0; i < parts.length - 1; i++) { if (current[parts[i]] && current[parts[i]].children) { current = current[parts[i]].children; } else { return undefined; // 路径无效 } } // 获取最终节点 if (current[parts[parts.length - 1]]) { return current[parts[parts.length - 1]].enabled; } return undefined; } // 根据路径更新配置 function updateConfigByPath(path, key, value) { const parts = path.split('.'); let current = userConfig; // 处理顶层节点 if (parts.length === 1) { if (current[parts[0]]) { current[parts[0]][key] = value; } return; } // 处理嵌套节点 for (let i = 0; i < parts.length - 1; i++) { if (current[parts[i]] && current[parts[i]].children) { current = current[parts[i]].children; } else { return; // 路径无效 } } // 更新最终节点 if (current[parts[parts.length - 1]]) { current[parts[parts.length - 1]][key] = value; } } // 创建配置项 function createConfigItems(configNode, parentElement, path) { const group = document.createElement('div'); group.className = 'config-group'; const header = document.createElement('div'); header.className = 'config-header'; const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; // 根据当前保存的配置值设置复选框状态 const savedValue = getConfigValueByPath(path); checkbox.checked = savedValue !== undefined ? savedValue : configNode.enabled; checkbox.dataset.path = path; checkbox.addEventListener('change', function () { updateConfigByPath(path, 'enabled', this.checked); debugLog('debug', `已更新 ${path}.enabled 为 ${this.checked}`); // 更新子节点 const childCheckboxes = document.querySelectorAll(`input[data-path^="${path}."]`); childCheckboxes.forEach(cb => { cb.checked = this.checked; updateConfigByPath(cb.dataset.path, 'enabled', this.checked); debugLog('debug', `已更新 ${cb.dataset.path}.enabled 为 ${this.checked}`); }); // 更新父节点 updateParentCheckboxState(this); // 自动保存配置 saveConfig(); debugLog('info', '配置已自动保存'); }); const label = document.createElement('label'); label.textContent = configNode.name || path.split('.').pop(); header.appendChild(checkbox); header.appendChild(label); group.appendChild(header); // 如果有子项,创建子项 if (configNode.children) { const childrenContainer = document.createElement('div'); childrenContainer.className = 'config-children'; Object.entries(configNode.children).forEach(([key, childNode]) => { const childPath = path ? `${path}.${key}` : key; createConfigItems(childNode, childrenContainer, childPath); }); group.appendChild(childrenContainer); } parentElement.appendChild(group); } // 更新父节点复选框状态 function updateParentCheckboxState(childCheckbox) { const childPath = childCheckbox.dataset.path; const parentPath = childPath.substring(0, childPath.lastIndexOf('.')); if (parentPath) { const parentCheckbox = document.querySelector(`input[data-path="${parentPath}"]`); if (parentCheckbox) { const childCheckboxes = document.querySelectorAll(`input[data-path^="${parentPath}."]`); const allChecked = Array.from(childCheckboxes).every(cb => cb.checked); const anyChecked = Array.from(childCheckboxes).some(cb => cb.checked); if (allChecked) { parentCheckbox.checked = true; parentCheckbox.indeterminate = false; } else if (anyChecked) { parentCheckbox.checked = false; parentCheckbox.indeterminate = true; } else { parentCheckbox.checked = false; parentCheckbox.indeterminate = false; } // 更新父节点配置 updateConfigByPath(parentPath, 'enabled', allChecked); // 递归更新更上层的父节点 - 限制递归深度以避免堆栈溢出 if (parentPath.split('.').length < 5) { updateParentCheckboxState(parentCheckbox); } } } } // 动态添加所有配置项 function addAllConfigItems(config, container) { Object.entries(config).forEach(([key, value]) => { if (typeof value === 'object' && value !== null && key !== 'version' && key !== 'checkInterval') { createConfigItems(value, container, key); } }); } // 添加所有配置项 addAllConfigItems(userConfig, scrollContainer); // 初始化所有父节点状态 function initAllParentStates() { // 获取所有顶级配置项 const topLevelKeys = Object.keys(userConfig).filter(key => typeof userConfig[key] === 'object' && userConfig[key] !== null && key !== 'version' && key !== 'checkInterval' ); // 为每个顶级配置项初始化父节点状态 topLevelKeys.forEach(key => { const childCheckboxes = document.querySelectorAll(`input[data-path^="${key}."]`); if (childCheckboxes.length > 0) { updateParentCheckboxState(childCheckboxes[0]); } }); } // 调用初始化函数 initAllParentStates(); // 创建按钮容器 - 精简布局 const buttonContainer = document.createElement('div'); buttonContainer.style.cssText = 'display: flex; gap: 8px; padding: 12px 16px; border-top: 1px solid #e2e8f0; background: #f8fafc; flex-shrink: 0;'; buttonContainer.style.flexShrink = '0'; buttonContainer.className = 'button-container'; // 保存按钮 const saveBtn = document.createElement('button'); saveBtn.className = 'save-btn'; saveBtn.textContent = '保存'; saveBtn.style.cssText = 'flex: 1; padding: 8px 12px; font-size: 13px; font-weight: 600; color: white; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border: none; border-radius: 6px; cursor: pointer; transition: all 0.2s ease;'; debugLog('debug', '保存按钮已创建'); saveBtn.addEventListener('click', function () { debugLog('info', '保存按钮被点击'); saveConfig(); alert('配置已保存!页面将自动刷新以应用新配置。'); location.reload(); }); // 重置按钮 const resetBtn = document.createElement('button'); resetBtn.className = 'reset-btn'; resetBtn.textContent = '重置'; resetBtn.style.cssText = 'flex: 1; padding: 8px 12px; font-size: 13px; font-weight: 600; color: white; background: linear-gradient(135deg, #fc8181 0%, #f56565 100%); border: none; border-radius: 6px; cursor: pointer; transition: all 0.2s ease;'; debugLog('debug', '重置按钮已创建'); resetBtn.addEventListener('click', function () { debugLog('info', '重置按钮被点击'); if (confirm('确定要重置为默认配置吗?此操作不可撤销!')) { userConfig = JSON.parse(JSON.stringify(defaultConfig)); debugLog('debug', '重置后的配置:', userConfig); // 更新UI状态 function updateCheckboxes(config, path = '') { Object.entries(config).forEach(([key, node]) => { if (typeof node === 'object' && node !== null) { const currentPath = path ? `${path}.${key}` : key; const checkbox = document.querySelector(`input[data-path="${currentPath}"]`); if (checkbox) { checkbox.checked = node.enabled; } if (node.children) { updateCheckboxes(node.children, currentPath); } } }); } updateCheckboxes(defaultConfig); // 保存重置后的配置 saveConfig(); // 重新执行清理 cleanPage(); debugLog('info', '配置已重置为默认值'); alert('配置已重置为默认值!页面将自动刷新以应用默认配置。'); location.reload(); // 重新加载页面以应用默认配置 } }); // 将按钮添加到按钮容器 buttonContainer.appendChild(saveBtn); buttonContainer.appendChild(resetBtn); // 将按钮容器添加到面板 panel.appendChild(buttonContainer); debugLog('debug', '按钮容器已添加到面板'); debugLog('debug', '面板中的子元素数量:', panel.children.length); // 切换面板显示/隐藏 toggleBtn.addEventListener('click', function (e) { e.stopPropagation(); debugLog('debug', '切换按钮被点击'); const isVisible = panel.style.display !== 'none'; panel.style.display = isVisible ? 'none' : 'block'; // 控制下层页面滚动 if (!isVisible) { // 不再修改body样式,让页面保持正常滚动 } debugLog('debug', '面板显示状态:', !isVisible); }); // 点击面板外部关闭面板 document.addEventListener('click', function (e) { if (panel.style.display !== 'none') { const isClickInsidePanel = panel.contains(e.target); const isClickOnToggle = toggleBtn.contains(e.target); if (!isClickInsidePanel && !isClickOnToggle) { panel.style.display = 'none'; } } }); // 阻止面板内部点击事件冒泡 panel.addEventListener('click', function (e) { e.stopPropagation(); }); // 检查面板初始状态 console.log('面板初始显示状态:', panel.style.display); } // 创建配置面板 createConfigPanel(); // 监控DOM变化,处理动态添加的元素 const observer = new MutationObserver(cleanPage); observer.observe(document.body, { childList: true, subtree: true, attributes: false, characterData: false }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址