// ==UserScript== // @name 晋江文学城作者屏蔽 // @namespace http://tampermonkey.net/ // @version 0.8 // @description 在搜索结果页面屏蔽不想看的作者的文章 // @author CursorAI // @license MIT // @match *://*.jjwxc.net/* // @grant GM_getValue // @grant GM_setValue // @grant GM_addStyle // @run-at document-end // @downloadURL https://update.greasyfork.icu/scripts/518578/%E6%99%8B%E6%B1%9F%E6%96%87%E5%AD%A6%E5%9F%8E%E4%BD%9C%E8%80%85%E5%B1%8F%E8%94%BD.user.js // @updateURL https://update.greasyfork.icu/scripts/518578/%E6%99%8B%E6%B1%9F%E6%96%87%E5%AD%A6%E5%9F%8E%E4%BD%9C%E8%80%85%E5%B1%8F%E8%94%BD.meta.js // ==/UserScript== (function() { 'use strict'; // 添加样式 GM_addStyle(` .block-panel { position: fixed; right: 20px; top: 20px; background: white; padding: 15px; border: 1px solid #ccc; border-radius: 5px; z-index: 1001; box-shadow: 0 2px 5px rgba(0,0,0,0.2); width: 300px; transition: all 0.3s ease; } .block-panel.login-active { top: 320px !important; } .block-panel h3 { margin: 0 0 10px 0; color: #333; display: flex; justify-content: space-between; align-items: center; } .block-panel input { padding: 5px; margin-right: 5px; width: 200px; } .block-panel button { padding: 5px 10px; background: #4CAF50; color: white; border: none; border-radius: 3px; cursor: pointer; } .blocked-content { display: none !important; } #blockList { max-height: 300px; overflow-y: auto; margin-top: 10px; display: none; } #blockList.show { display: block; } .author-item { display: flex; justify-content: space-between; align-items: center; padding: 5px; border-bottom: 1px solid #eee; } .remove-btn { background: #ff4444 !important; padding: 2px 8px !important; font-size: 12px; } .toggle-btn { background: #666 !important; } `); let blockedAuthors = GM_getValue('blockedAuthors', []); let contextMenu = null; // 添加一个辅助函数来检查登录状态 function hasLoginElements() { return document.getElementById('qrCodeDiv') || document.querySelector('.login-qrcode'); } // 简化 createControlPanel 函数 function createControlPanel() { const panel = document.createElement('div'); panel.className = 'block-panel'; if (hasLoginElements()) { panel.classList.add('login-active'); } panel.innerHTML = `

作者屏蔽

`; document.body.appendChild(panel); // 绑定事件 document.getElementById('addBlock').addEventListener('click', () => addBlockedAuthor()); document.getElementById('toggleList').addEventListener('click', toggleBlockList); // 绑定导出/导入事件 document.getElementById('exportList').addEventListener('click', exportBlockList); document.getElementById('importList').addEventListener('click', importBlockList); updateBlockList(); } // 切换列表显示 function toggleBlockList() { const blockList = document.getElementById('blockList'); const toggleBtn = document.getElementById('toggleList'); const isShown = blockList.classList.toggle('show'); toggleBtn.textContent = isShown ? '隐藏列表' : '显示列表'; } // 创建右键菜单 function createContextMenu(e, author) { if (contextMenu) { contextMenu.remove(); } // 创建新菜单元素 const menu = document.createElement('div'); // 获取鼠标位置 const x = e.clientX; const y = e.clientY; // 获取窗口尺寸 const windowWidth = window.innerWidth; const windowHeight = window.innerHeight; const menuWidth = 150; const menuHeight = 40; // 调整位置,确保菜单不会超出视窗 let adjustedX = x; let adjustedY = y; // 如果菜单会超出右边界,向左偏移 if (x + menuWidth > windowWidth) { adjustedX = x - menuWidth; } // 如果菜单会超出下边界,向上偏移 if (y + menuHeight > windowHeight) { adjustedY = y - menuHeight; } const menuStyle = ` position: fixed; left: ${adjustedX}px; top: ${adjustedY}px; background: white; border: 1px solid #ccc; border-radius: 4px; padding: 5px 0; box-shadow: 2px 2px 5px rgba(0,0,0,0.2); z-index: 999999 !important; min-width: 120px; user-select: none; font-size: 14px; color: #333; `.trim(); menu.style.cssText = menuStyle; // 创建菜单项 const menuItem = document.createElement('div'); const menuItemStyle = ` padding: 8px 15px; cursor: pointer; white-space: nowrap; background: white; transition: background-color 0.2s; `.trim(); menuItem.style.cssText = menuItemStyle; const isBlocked = blockedAuthors.includes(author); menuItem.textContent = `${isBlocked ? '取消屏蔽' : '屏蔽'} "${author}"`; // 添加hover效果 menuItem.addEventListener('mouseover', () => { menuItem.style.backgroundColor = '#f0f0f0'; }); menuItem.addEventListener('mouseout', () => { menuItem.style.backgroundColor = 'white'; }); // 添加点击事件 menuItem.addEventListener('click', () => { if (isBlocked) { removeBlockedAuthor(author); } else { addBlockedAuthor(author); } menu.remove(); contextMenu = null; }); menu.appendChild(menuItem); document.body.appendChild(menu); contextMenu = menu; } // 修改绑定右键事件的函数 function bindContextMenu() { document.addEventListener('contextmenu', function(e) { const authorLink = e.target.closest('a[href*="oneauthor.php"]'); if (authorLink) { e.preventDefault(); e.stopPropagation(); const author = authorLink.textContent.trim(); createContextMenu(e, author); // 传入事件对象 return false; } }, true); // 点击其他地方关闭菜单 document.addEventListener('click', function(e) { if (contextMenu && !contextMenu.contains(e.target)) { contextMenu.remove(); contextMenu = null; } }); } // 添加屏蔽作者 function addBlockedAuthor(author) { const authorName = author || document.getElementById('authorInput')?.value.trim(); if (authorName && !blockedAuthors.includes(authorName)) { blockedAuthors.push(authorName); GM_setValue('blockedAuthors', blockedAuthors); updateBlockList(); hideBlockedContent(); if (document.getElementById('authorInput')) { document.getElementById('authorInput').value = ''; } } } // 移除屏蔽作者 function removeBlockedAuthor(author) { blockedAuthors = blockedAuthors.filter(a => a !== author); GM_setValue('blockedAuthors', blockedAuthors); updateBlockList(); hideBlockedContent(); } // 更新屏蔽列表显示 function updateBlockList() { const blockList = document.getElementById('blockList'); if (blockList) { blockList.innerHTML = blockedAuthors .sort((a, b) => a.localeCompare(b, 'zh-CN')) .map(author => `
${author}
`).join(''); blockList.querySelectorAll('.remove-btn').forEach(button => { button.addEventListener('click', function() { const author = this.getAttribute('data-author'); removeBlockedAuthor(author); }); }); } } // 隐藏被屏蔽的内容 function hideBlockedContent() { const authorElements = document.querySelectorAll('a[href*="oneauthor.php"]'); authorElements.forEach(authorElement => { const authorName = authorElement.textContent.trim(); if (blockedAuthors.includes(authorName)) { let articleContainer = authorElement.closest('tr'); if (articleContainer) { articleContainer.classList.add('blocked-content'); } } }); } // 导出黑名单 function exportBlockList() { const data = { version: '1.0', timestamp: new Date().toISOString(), authors: blockedAuthors }; const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `晋江作者黑名单_${new Date().toLocaleDateString()}.json`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } // 导入黑名单 function importBlockList() { const input = document.createElement('input'); input.type = 'file'; input.accept = '.json'; input.onchange = function(e) { const file = e.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = function(e) { try { const data = JSON.parse(e.target.result); // 验证文件格式 if (!data.authors || !Array.isArray(data.authors)) { throw new Error('无效的黑名单文件格式'); } // 显示导入选项对话框 const importType = confirm( `发现 ${data.authors.length} 个作者。\n` + `点击"确定"合并到当前列表\n` + `点击"取消"覆盖当前列表` ); if (importType) { // 合并选项:添加新作者到现有列表 const newAuthors = data.authors.filter(author => !blockedAuthors.includes(author)); if (newAuthors.length > 0) { blockedAuthors = [...blockedAuthors, ...newAuthors]; GM_setValue('blockedAuthors', blockedAuthors); updateBlockList(); hideBlockedContent(); alert(`合并成功!新增 ${newAuthors.length} 个作者。`); } else { alert('没有新的作者需要添加。'); } } else { // 覆盖选项:完全替换现有列表 blockedAuthors = [...new Set(data.authors)]; // 去重 GM_setValue('blockedAuthors', blockedAuthors); updateBlockList(); hideBlockedContent(); alert(`覆盖成功!共导入 ${blockedAuthors.length} 个作者。`); } } catch (err) { alert('导入失败:' + err.message); } }; reader.readAsText(file); }; input.click(); } // 简化 observer 部分 const observer = new MutationObserver(() => { const panel = document.querySelector('.block-panel'); if (panel) { panel.classList.toggle('login-active', hasLoginElements()); } }); // 初始化 function init() { setTimeout(() => { // 清理可能存在的旧面板 const oldPanels = document.querySelectorAll('.block-panel'); oldPanels.forEach(panel => panel.remove()); // 创建新面板 createControlPanel(); hideBlockedContent(); bindContextMenu(); // 找到所有可能的登录按钮 const loginLinks = document.querySelectorAll('a[href*="login"], a[onclick*="login"]'); loginLinks.forEach(link => { link.addEventListener('click', () => { document.querySelector('.block-panel')?.classList.add('login-active'); }); }); // 监听关闭按钮 document.addEventListener('click', (e) => { if (e.target.matches('a[onclick*="unblockUI"]')) { document.querySelector('.block-panel')?.classList.remove('login-active'); } }); // 添加登录元素检测和位置调整 observer.observe(document.body, { childList: true, subtree: true }); }, 1500); } window.addEventListener('load', init); })();