// ==UserScript== // @name Block Specified Websites with Control Panel (Vue & Element UI) // @namespace https://bbs.tampermonkey.net.cn/ // @version 1.7.1 // @description 更新了外观和调整了界面 // @author // @match *://*/* // @grant GM_getValue // @grant GM_setValue // @run-at document-start // ==/UserScript== (function() { 'use strict'; /* ======================= 工具函数:加载外部资源 ======================= */ function loadScript(url) { return new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = url; script.onload = resolve; script.onerror = reject; document.head.appendChild(script); }); } function loadCSS(url) { return new Promise((resolve, reject) => { const link = document.createElement('link'); link.rel = "stylesheet"; link.href = url; link.onload = resolve; link.onerror = reject; document.head.appendChild(link); }); } /* ======================= 数据存储与基本判断 ======================= */ let blockedSites = GM_getValue('blockedSites', null); if (!blockedSites) { blockedSites = ["youtube.com"]; GM_setValue('blockedSites', blockedSites); } const currentHost = window.location.hostname; function isSiteBlocked(host) { return blockedSites.some(site => host === site || host.endsWith('.' + site)); } /* ======================= 拦截页面 当当前网站在封禁列表中时,直接将 body 内容替换成拦截页面内容, 提示用户该网站已被屏蔽,并提醒利用右上角的控制面板解除屏蔽。 ======================= */ function blockPage() { document.body.innerHTML = `

⚠️ 禁止访问此网站 ⚠️

此网站已被屏蔽

请使用右上角的控制面板解除屏蔽

`; } if (isSiteBlocked(currentHost)) { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', blockPage); } else { blockPage(); } // 注意:这里不终止后续脚本执行,控制面板依然会初始化(容器挂在 document.documentElement 上),方便解除封禁。 } /* ======================= 为 Vue 应用创建容器 将容器添加到 元素中,避免被 body 替换后丢失。 ======================= */ function createContainer() { const container = document.createElement('div'); container.id = 'block-control-app'; document.documentElement.appendChild(container); } /* ======================= 加载 Vue 与 Element UI 资源 ======================= */ function loadLibraries() { return Promise.all([ loadCSS('https://unpkg.com/element-ui/lib/theme-chalk/index.css'), loadScript('https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js'), loadScript('https://unpkg.com/element-ui/lib/index.js') ]); } /* ======================= 自定义拖拽指令 用法说明: - 若希望拖拽时移动的是父元素,则使用 v-drag:parent - 否则直接 v-drag 使该元素自身可拖拽 ======================= */ function registerDragDirective(Vue) { Vue.directive('drag', { bind(el, binding) { const dragTarget = binding.arg === 'parent' ? el.parentElement : el; el.style.cursor = 'move'; el.onmousedown = function(e) { e.preventDefault(); const disX = e.clientX - dragTarget.offsetLeft; const disY = e.clientY - dragTarget.offsetTop; function onMouseMove(e) { dragTarget.style.left = (e.clientX - disX) + 'px'; dragTarget.style.top = (e.clientY - disY) + 'px'; } function onMouseUp() { document.removeEventListener('mousemove', onMouseMove); document.removeEventListener('mouseup', onMouseUp); } document.addEventListener('mousemove', onMouseMove); document.addEventListener('mouseup', onMouseUp); }; } }); } /* ======================= 初始化 Vue 应用 控制面板采用 Element UI 的卡片、输入框、折叠面板等组件构造, 并根据 visible 状态显示或隐藏;隐藏时显示一个可拖拽的悬浮小球。 此处所有按钮均使用了图标而非纯文字(例如:封禁/解除封禁按钮分别使用锁/开锁图标)。 ======================= */ function initVueApp() { new Vue({ el: '#block-control-app', data: { blockedSites: blockedSites, newSite: '', currentHost: currentHost, visible: true // 控制面板是否显示 }, computed: { isBlocked() { return this.blockedSites.some(site => this.currentHost === site || this.currentHost.endsWith('.' + site)); } }, methods: { toggleCurrentSite() { if (this.isBlocked) { this.blockedSites = this.blockedSites.filter(site => !(this.currentHost === site || this.currentHost.endsWith('.' + site))); GM_setValue('blockedSites', this.blockedSites); alert("已解除封禁此网站,请刷新页面。"); location.reload(); } else { this.blockedSites.push(this.currentHost); GM_setValue('blockedSites', this.blockedSites); alert("已封禁此网站,请刷新页面。"); location.reload(); } }, addSite() { const newSiteTrim = this.newSite.trim(); if (newSiteTrim && !this.blockedSites.includes(newSiteTrim)) { this.blockedSites.push(newSiteTrim); GM_setValue('blockedSites', this.blockedSites); this.newSite = ''; } }, removeSite(index) { this.blockedSites.splice(index, 1); GM_setValue('blockedSites', this.blockedSites); location.reload(); }, hidePanel() { this.visible = false; }, showPanel() { this.visible = true; } }, template: `
网站屏蔽面板
  • {{ site }}
` }); } /* ======================= 主初始化流程 ======================= */ function init() { createContainer(); loadLibraries().then(() => { registerDragDirective(Vue); initVueApp(); }).catch(err => { console.error('加载 Vue 或 Element UI 失败:', err); }); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();