一键生成网页二维码脚本 V1.5(2025年8月21日更新)

发布于 2025-08-16  507 次阅读


本文于 2025年8月21日 9:37 更新,注意查看最新内容

因为有些常用网站不需要显示网页二维码,所以更新了鼠标长按关闭按钮,关闭当前页面或者当前域名的功能:

// ==UserScript==
// @name         页面二维码生成器(带管理功能)
// @namespace    http://tampermonkey.net/
// @version      1.5
// @description  生成当前页面二维码,带完善缓存机制和长按管理隐藏功能
// @author       某知名AI
// @match        *://*/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @connect      cdnjs.cloudflare.com
// @require      https://cdn.jsdelivr.net/npm/qrcode@1.5.1/build/qrcode.min.js
// ==/UserScript==

(function() {
    'use strict';

    // 缓存配置 - 7天有效期
    const CACHE_EXPIRY_DAYS = 7;
    const FONT_CACHE_KEY = 'qrcodeFontCache';
    const FONT_CACHE_TIMESTAMP = 'qrcodeFontTimestamp';

    // 禁用设置的存储键名
    const DISABLED_PAGES_KEY = 'disabledQrcodePages';
    const DISABLED_DOMAINS_KEY = 'disabledQrcodeDomains';

    // 所需字体文件URL
    const fontUrls = {
        woff2: 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/fonts/fontawesome-webfont.woff2?v=4.7.0',
        woff: 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/fonts/fontawesome-webfont.woff?v=4.7.0'
    };

    // 检查缓存是否有效
    function isCacheValid() {
        const timestamp = GM_getValue(FONT_CACHE_TIMESTAMP, 0);
        const now = new Date().getTime();
        const expiryTime = CACHE_EXPIRY_DAYS * 24 * 60 * 60 * 1000;
        return timestamp + expiryTime > now;
    }

    // 从缓存加载字体
    function loadFromCache() {
        const cachedFonts = GM_getValue(FONT_CACHE_KEY, null);
        if (cachedFonts) {
            injectFontStyles(cachedFonts);
            return true;
        }
        return false;
    }

    // 下载字体并缓存
    function downloadAndCacheFonts() {
        // 优先尝试woff2格式,兼容性更好
        fetchFont(fontUrls.woff2, 'woff2')
            .catch(() => {
                // 如果woff2失败,尝试woff格式
                return fetchFont(fontUrls.woff, 'woff');
            })
            .then(({data, format}) => {
                const fontData = {data, format};
                // 存储到缓存
                GM_setValue(FONT_CACHE_KEY, fontData);
                GM_setValue(FONT_CACHE_TIMESTAMP, new Date().getTime());
                injectFontStyles(fontData);
            })
            .catch(() => {
                // 所有字体加载失败时使用基础样式 fallback
                injectFallbackStyles();
            });
    }

    // 下载字体文件
    function fetchFont(url, format) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'GET',
                url: url,
                responseType: 'arraybuffer',
                onload: function(response) {
                    if (response.status === 200 && response.response) {
                        // 转换为base64
                        const base64Data = btoa(
                            new Uint8Array(response.response).reduce(
                                (data, byte) => data + String.fromCharCode(byte),
                                ''
                            )
                        );
                        resolve({data: base64Data, format});
                    } else {
                        reject(new Error(`Failed to load font: ${response.status}`));
                    }
                },
                onerror: function() {
                    reject(new Error('Network error while loading font'));
                },
                ontimeout: function() {
                    reject(new Error('Font loading timed out'));
                }
            });
        });
    }

    // 注入字体样式
    function injectFontStyles(fontData) {
        const style = document.createElement('style');
        style.textContent = `
            @font-face {
                font-family: 'FontAwesome';
                src: url('data:application/font-${fontData.format};base64,${fontData.data}') format('${fontData.format}');
                font-weight: normal;
                font-style: normal;
            }
            .fa {
                display: inline-block;
                font: normal normal normal 14px/1 FontAwesome;
                font-size: inherit;
                text-rendering: auto;
                -webkit-font-smoothing: antialiased;
                -moz-osx-font-smoothing: grayscale;
            }
            .fa-qrcode:before { content: "\\f029"; }
            .fa-times:before { content: "\\f00d"; }
            .fa-cog:before { content: "\\f013"; }
            .fa-file-o:before { content: "\\f15c"; }
            .fa-globe:before { content: "\\f0ac"; }
            .fa-trash:before { content: "\\f1f8"; }

            /* 管理界面样式 */
            .qr-disable-prompt {
                position: fixed;
                width: 250px;
                background-color: white;
                border-radius: 8px;
                box-shadow: 0 3px 15px rgba(0,0,0,0.2);
                padding: 15px;
                z-index: 10001;
                border: 1px solid #eee;
            }
            .qr-disable-prompt h4 {
                margin: 0 0 10px 0;
                padding-bottom: 8px;
                border-bottom: 1px solid #eee;
                font-size: 16px;
            }
            .qr-disable-option {
                display: block;
                width: 100%;
                padding: 8px 10px;
                text-align: left;
                background: none;
                border: none;
                cursor: pointer;
                border-radius: 4px;
                margin-bottom: 5px;
                transition: background-color 0.2s;
            }
            .qr-disable-option:hover {
                background-color: #f1f1f1;
            }
            .qr-disable-option i {
                margin-right: 8px;
                width: 16px;
                text-align: center;
            }
            .qr-prompt-divider {
                margin: 10px 0;
                border: none;
                border-top: 1px dashed #eee;
            }

            .qr-management-modal {
                position: fixed;
                top: 50%;
                left: 50%;
                transform: translate(-50%, -50%);
                width: 90%;
                max-width: 800px;
                background-color: white;
                border-radius: 8px;
                box-shadow: 0 5px 30px rgba(0,0,0,0.2);
                z-index: 10002;
                padding: 20px;
                max-height: 80vh;
                overflow-y: auto;
                display: none;
            }
            .qr-management-modal h3 {
                margin-top: 0;
                padding-bottom: 10px;
                border-bottom: 1px solid #eee;
            }
            .qr-management-section {
                margin-bottom: 25px;
            }
            .qr-management-section h4 {
                margin-bottom: 10px;
                color: #444;
            }
            .qr-disabled-item {
                display: flex;
                justify-content: space-between;
                align-items: center;
                padding: 8px 12px;
                background-color: #f8f9fa;
                border-radius: 4px;
                margin-bottom: 8px;
                word-break: break-all;
            }
            .qr-remove-btn {
                background: none;
                border: none;
                color: #dc3545;
                cursor: pointer;
                padding: 4px 8px;
                border-radius: 3px;
                transition: background-color 0.2s;
            }
            .qr-remove-btn:hover {
                background-color: #ffe3e3;
            }
            .qr-empty-state {
                color: #666;
                padding: 15px;
                text-align: center;
                background-color: #f8f9fa;
                border-radius: 4px;
            }
            .qr-close-management {
                margin-top: 15px;
                padding: 8px 16px;
                background-color: #6c757d;
                color: white;
                border: none;
                border-radius: 4px;
                cursor: pointer;
                transition: all 0.3s ease;
            }
            .qr-close-management:hover {
                background-color: #5a6268;
            }
        `;
        document.head.appendChild(style);
    }

    // 字体加载失败时的 fallback 样式
    function injectFallbackStyles() {
        const style = document.createElement('style');
        style.textContent = `
            .fa-qrcode:before { content: "🔳"; }
            .fa-times:before { content: "✕"; }
            .fa-cog:before { content: "⚙️"; }
            .fa-file-o:before { content: "📄"; }
            .fa-globe:before { content: "🌐"; }
            .fa-trash:before { content: "🗑️"; }

            /* 管理界面样式 */
            .qr-disable-prompt {
                position: fixed;
                width: 250px;
                background-color: white;
                border-radius: 8px;
                box-shadow: 0 3px 15px rgba(0,0,0,0.2);
                padding: 15px;
                z-index: 10001;
                border: 1px solid #eee;
            }
            .qr-disable-prompt h4 {
                margin: 0 0 10px 0;
                padding-bottom: 8px;
                border-bottom: 1px solid #eee;
                font-size: 16px;
            }
            .qr-disable-option {
                display: block;
                width: 100%;
                padding: 8px 10px;
                text-align: left;
                background: none;
                border: none;
                cursor: pointer;
                border-radius: 4px;
                margin-bottom: 5px;
                transition: background-color 0.2s;
            }
            .qr-disable-option:hover {
                background-color: #f1f1f1;
            }
            .qr-disable-option i {
                margin-right: 8px;
                width: 16px;
                text-align: center;
            }
            .qr-prompt-divider {
                margin: 10px 0;
                border: none;
                border-top: 1px dashed #eee;
            }

            .qr-management-modal {
                position: fixed;
                top: 50%;
                left: 50%;
                transform: translate(-50%, -50%);
                width: 90%;
                max-width: 800px;
                background-color: white;
                border-radius: 8px;
                box-shadow: 0 5px 30px rgba(0,0,0,0.2);
                z-index: 10002;
                padding: 20px;
                max-height: 80vh;
                overflow-y: auto;
                display: none;
            }
            .qr-management-modal h3 {
                margin-top: 0;
                padding-bottom: 10px;
                border-bottom: 1px solid #eee;
            }
            .qr-management-section {
                margin-bottom: 25px;
            }
            .qr-management-section h4 {
                margin-bottom: 10px;
                color: #444;
            }
            .qr-disabled-item {
                display: flex;
                justify-content: space-between;
                align-items: center;
                padding: 8px 12px;
                background-color: #f8f9fa;
                border-radius: 4px;
                margin-bottom: 8px;
                word-break: break-all;
            }
            .qr-remove-btn {
                background: none;
                border: none;
                color: #dc3545;
                cursor: pointer;
                padding: 4px 8px;
                border-radius: 3px;
                transition: background-color 0.2s;
            }
            .qr-remove-btn:hover {
                background-color: #ffe3e3;
            }
            .qr-empty-state {
                color: #666;
                padding: 15px;
                text-align: center;
                background-color: #f8f9fa;
                border-radius: 4px;
            }
            .qr-close-management {
                margin-top: 15px;
                padding: 8px 16px;
                background-color: #6c757d;
                color: white;
                border: none;
                border-radius: 4px;
                cursor: pointer;
                transition: all 0.3s ease;
            }
            .qr-close-management:hover {
                background-color: #5a6268;
            }
        `;
        document.head.appendChild(style);
    }

    // 禁用管理相关函数
    function getDisabledPages() {
        return GM_getValue(DISABLED_PAGES_KEY, []);
    }

    function getDisabledDomains() {
        return GM_getValue(DISABLED_DOMAINS_KEY, []);
    }

    function addDisabledPage(url) {
        const pages = getDisabledPages();
        if (!pages.includes(url)) {
            pages.push(url);
            GM_setValue(DISABLED_PAGES_KEY, pages);
        }
    }

    function addDisabledDomain(domain) {
        const domains = getDisabledDomains();
        if (!domains.includes(domain)) {
            domains.push(domain);
            GM_setValue(DISABLED_DOMAINS_KEY, domains);
        }
    }

    function removeDisabledPage(url) {
        const pages = getDisabledPages().filter(page => page !== url);
        GM_setValue(DISABLED_PAGES_KEY, pages);
        return pages;
    }

    function removeDisabledDomain(domain) {
        const domains = getDisabledDomains().filter(d => d !== domain);
        GM_setValue(DISABLED_DOMAINS_KEY, domains);
        return domains;
    }

    function isCurrentPageDisabled() {
        const currentUrl = window.location.href;
        const disabledPages = getDisabledPages();
        return disabledPages.some(page => currentUrl.startsWith(page));
    }

    function isCurrentDomainDisabled() {
        const currentDomain = window.location.hostname;
        const disabledDomains = getDisabledDomains();
        return disabledDomains.includes(currentDomain);
    }

    // 创建管理界面
    function createManagementInterface(buttonContainer, overlay) {
        const managementModal = document.createElement('div');
        managementModal.className = 'qr-management-modal';
        managementModal.innerHTML = `
            <h3><i class="fa fa-cog" style="margin-right:8px;"></i>二维码按钮管理</h3>

            <div class="qr-management-section">
                <h4>已禁用二维码按钮的页面</h4>
                <div id="qr-disabled-pages-container"></div>
            </div>

            <div class="qr-management-section">
                <h4>已禁用二维码按钮的域名</h4>
                <div id="qr-disabled-domains-container"></div>
            </div>

            <button class="qr-close-management">关闭管理</button>
        `;

        document.body.appendChild(managementModal);

        // 关闭按钮事件
        managementModal.querySelector('.qr-close-management').addEventListener('click', () => {
            managementModal.style.display = 'none';
            overlay.style.display = 'none';
        });

        // 渲染禁用列表
        function renderDisabledLists() {
            const pagesContainer = managementModal.querySelector('#qr-disabled-pages-container');
            const domainsContainer = managementModal.querySelector('#qr-disabled-domains-container');

            // 渲染禁用页面
            const disabledPages = getDisabledPages();
            if (disabledPages.length === 0) {
                pagesContainer.innerHTML = '<div class="qr-empty-state">没有禁用任何页面</div>';
            } else {
                pagesContainer.innerHTML = disabledPages.map(page => `
                    <div class="qr-disabled-item">
                        <span>${page}</span>
                        <button class="qr-remove-btn" data-type="page" data-value="${page}">
                            <i class="fa fa-trash"></i>
                        </button>
                    </div>
                `).join('');
            }

            // 渲染禁用域名
            const disabledDomains = getDisabledDomains();
            if (disabledDomains.length === 0) {
                domainsContainer.innerHTML = '<div class="qr-empty-state">没有禁用任何域名</div>';
            } else {
                domainsContainer.innerHTML = disabledDomains.map(domain => `
                    <div class="qr-disabled-item">
                        <span>${domain}</span>
                        <button class="qr-remove-btn" data-type="domain" data-value="${domain}">
                            <i class="fa fa-trash"></i>
                        </button>
                    </div>
                `).join('');
            }

            // 添加删除按钮事件
            managementModal.querySelectorAll('.qr-remove-btn').forEach(btn => {
                btn.addEventListener('click', (e) => {
                    const type = e.target.closest('.qr-remove-btn').dataset.type;
                    const value = e.target.closest('.qr-remove-btn').dataset.value;

                    if (type === 'page') {
                        removeDisabledPage(value);
                    } else if (type === 'domain') {
                        removeDisabledDomain(value);
                    }

                    renderDisabledLists();

                    // 如果当前页面或域名被启用,显示按钮
                    if ((type === 'page' && value === window.location.href) ||
                        (type === 'domain' && value === window.location.hostname)) {
                        buttonContainer.style.display = 'flex';
                    }
                });
            });
        }

        // 显示管理界面
        function showManagement() {
            managementModal.style.display = 'block';
            overlay.style.display = 'block';
            renderDisabledLists();
        }

        return { showManagement };
    }

    // 创建UI元素
    function createUI() {
        // 创建背景遮罩(用于弹窗)
        const overlay = document.createElement('div');
        overlay.style.position = 'fixed';
        overlay.style.top = '0';
        overlay.style.left = '0';
        overlay.style.right = '0';
        overlay.style.bottom = '0';
        overlay.style.backgroundColor = 'rgba(0,0,0,0.5)';
        overlay.style.zIndex = '9999';
        overlay.style.display = 'none';
        overlay.style.backdropFilter = 'blur(2px)';
        document.body.appendChild(overlay);

        // 创建按钮容器
        const buttonContainer = document.createElement('div');
        buttonContainer.style.position = 'fixed';
        buttonContainer.style.left = '10px';
        buttonContainer.style.top = '50%';
        buttonContainer.style.transform = 'translateY(-50%)';
        buttonContainer.style.zIndex = '9999';
        buttonContainer.style.display = 'flex';
        buttonContainer.style.flexDirection = 'column';
        buttonContainer.style.alignItems = 'center';

        // 检查当前页面或域名是否被禁用
        if (isCurrentPageDisabled() || isCurrentDomainDisabled()) {
            buttonContainer.style.display = 'none';
        }

        // 创建二维码按钮
        const qrButton = document.createElement('button');
        qrButton.innerHTML = '<i class="fa fa-qrcode"></i>';

        // 创建关闭按钮
        const hideButton = document.createElement('button');
        hideButton.innerHTML = '<i class="fa fa-times"></i>';
        hideButton.style.position = 'absolute';
        hideButton.style.top = '-10px';
        hideButton.style.right = '-10px';
        hideButton.style.width = '24px';
        hideButton.style.height = '24px';
        hideButton.style.borderRadius = '50%';
        hideButton.style.backgroundColor = '#f44336';
        hideButton.style.color = 'white';
        hideButton.style.border = 'none';
        hideButton.style.cursor = 'pointer';
        hideButton.style.boxShadow = '0 1px 3px rgba(0,0,0,0.2)';
        hideButton.style.display = 'none'; // 默认隐藏
        hideButton.style.alignItems = 'center';
        hideButton.style.justifyContent = 'center';
        hideButton.style.fontSize = '12px';
        hideButton.title = '单击关闭,长按打开管理选项';

        // 二维码按钮样式
        qrButton.style.width = '40px';
        qrButton.style.height = '40px';
        qrButton.style.borderRadius = '8px';
        qrButton.style.backgroundColor = 'rgba(255, 255, 255, 0.8)';
        qrButton.style.color = '#333';
        qrButton.style.border = '1px solid #ddd';
        qrButton.style.cursor = 'pointer';
        qrButton.style.boxShadow = '0 2px 5px rgba(0,0,0,0.1)';
        qrButton.style.transition = 'all 0.3s ease';
        qrButton.style.display = 'flex';
        qrButton.style.alignItems = 'center';
        qrButton.style.justifyContent = 'center';
        qrButton.style.fontSize = '18px';
        qrButton.title = '生成当前页面二维码';

        // 创建禁用提示框
        const disablePrompt = document.createElement('div');
        disablePrompt.className = 'qr-disable-prompt';
        disablePrompt.style.display = 'none';
        disablePrompt.innerHTML = `
            <h4>关闭二维码按钮</h4>
            <button class="qr-disable-option disable-page">
                <i class="fa fa-file-o"></i>在本页关闭
            </button>
            <button class="qr-disable-option disable-domain">
                <i class="fa fa-globe"></i>在本域名关闭
            </button>
            <hr class="qr-prompt-divider">
            <button class="qr-disable-option manage-settings">
                <i class="fa fa-cog"></i>管理设置
            </button>
        `;
        document.body.appendChild(disablePrompt);

        // 按钮容器悬停效果
        buttonContainer.addEventListener('mouseover', () => {
            qrButton.style.width = '50px';
            qrButton.style.backgroundColor = 'white';
            qrButton.style.boxShadow = '0 3px 8px rgba(0,0,0,0.2)';
            hideButton.style.display = 'flex';
        });

        buttonContainer.addEventListener('mouseout', () => {
            // 如果提示框没显示才隐藏关闭按钮
            if (disablePrompt.style.display === 'none') {
                qrButton.style.width = '40px';
                qrButton.style.backgroundColor = 'rgba(255, 255, 255, 0.8)';
                qrButton.style.boxShadow = '0 2px 5px rgba(0,0,0,0.1)';
                hideButton.style.display = 'none';
            }
        });

        // 初始化管理界面
        const management = createManagementInterface(buttonContainer, overlay);

        // 提示框按钮事件
        disablePrompt.querySelector('.qr-disable-option.disable-page').addEventListener('click', () => {
            const currentUrl = window.location.href;
            addDisabledPage(currentUrl);
            buttonContainer.style.display = 'none';
            disablePrompt.style.display = 'none';
            alert('已在本页关闭二维码按钮,刷新页面后生效');
        });

        disablePrompt.querySelector('.qr-disable-option.disable-domain').addEventListener('click', () => {
            const currentDomain = window.location.hostname;
            addDisabledDomain(currentDomain);
            buttonContainer.style.display = 'none';
            disablePrompt.style.display = 'none';
            alert(`已在域名 ${currentDomain} 下关闭二维码按钮,刷新页面后生效`);
        });

        disablePrompt.querySelector('.qr-disable-option.manage-settings').addEventListener('click', () => {
            disablePrompt.style.display = 'none';
            management.showManagement();
        });

        // 长按关闭按钮逻辑
        let pressTimer = null;
        const LONG_PRESS_DELAY = 500; // 长按判定时间:500毫秒
        let longPressTriggered = false; // 标记长按是否已触发

        // 鼠标按下事件
        hideButton.addEventListener('mousedown', (e) => {
            e.stopPropagation();
            longPressTriggered = false; // 重置长按状态

            // 启动计时器
            pressTimer = setTimeout(() => {
                // 长按时间达到,显示管理选项
                longPressTriggered = true; // 标记长按已触发

                // 显示在页面正中间
                disablePrompt.style.left = '50%';
                disablePrompt.style.top = '50%';
                disablePrompt.style.transform = 'translate(-50%, -50%)';

                // 显示弹窗
                disablePrompt.style.display = 'block';
            }, LONG_PRESS_DELAY);
        });

        // 关闭按钮点击事件
        hideButton.addEventListener('click', (e) => {
            e.stopPropagation(); // 阻止事件冒泡到二维码按钮
            buttonContainer.style.display = 'none';
        });

        // 鼠标释放事件
        hideButton.addEventListener('mouseup', (e) => {
            e.stopPropagation();

            // 清除计时器
            if (pressTimer) {
                clearTimeout(pressTimer);
                pressTimer = null;
            }
        });

        // 鼠标离开按钮区域
        hideButton.addEventListener('mouseleave', () => {
            if (pressTimer) {
                clearTimeout(pressTimer);
                pressTimer = null;
            }
            // 只有在未触发长按的情况下才关闭弹窗
            if (!longPressTriggered) {
                disablePrompt.style.display = 'none';
            }
        });

        // 点击其他区域关闭提示框
        document.addEventListener('click', (e) => {
            if (!disablePrompt.contains(e.target) && e.target !== hideButton && !hideButton.contains(e.target)) {
                disablePrompt.style.display = 'none';
                longPressTriggered = false; // 重置状态
            }
        });

        // 点击遮罩关闭所有弹窗
        overlay.addEventListener('click', () => {
            disablePrompt.style.display = 'none';
            longPressTriggered = false;
            document.querySelector('.qr-management-modal').style.display = 'none';
            overlay.style.display = 'none';
        });

        // 弹窗元素(延迟创建)
        let qrModal, qrContainer, closeButton, urlText;

        function initModal() {
            if (qrModal) return;

            // 创建二维码弹窗容器
            qrModal = document.createElement('div');
            qrModal.style.position = 'fixed';
            qrModal.style.top = '0';
            qrModal.style.left = '0';
            qrModal.style.width = '100%';
            qrModal.style.height = '100%';
            qrModal.style.backgroundColor = 'rgba(0,0,0,0.7)';
            qrModal.style.display = 'none';
            qrModal.style.justifyContent = 'center';
            qrModal.style.alignItems = 'center';
            qrModal.style.zIndex = '10000';
            qrModal.style.flexDirection = 'column';
            qrModal.style.backdropFilter = 'blur(3px)';

            // 创建二维码图片容器
            qrContainer = document.createElement('div');
            qrContainer.style.backgroundColor = 'white';
            qrContainer.style.padding = '20px';
            qrContainer.style.borderRadius = '10px';
            qrContainer.style.boxShadow = '0 0 20px rgba(0,0,0,0.5)';
            qrContainer.style.textAlign = 'center';
            qrContainer.style.maxWidth = '90%';
            qrContainer.style.transform = 'scale(0.95)';
            qrContainer.style.transition = 'transform 0.3s ease';

            // 创建关闭按钮
            closeButton = document.createElement('button');
            closeButton.innerHTML = '<i class="fa fa-times"></i> 关闭';
            closeButton.style.marginTop = '20px';
            closeButton.style.padding = '8px 16px';
            closeButton.style.backgroundColor = '#666';
            closeButton.style.color = 'white';
            closeButton.style.border = 'none';
            closeButton.style.borderRadius = '5px';
            closeButton.style.cursor = 'pointer';
            closeButton.style.fontSize = '14px';
            closeButton.style.transition = 'background-color 0.2s';

            closeButton.addEventListener('mouseover', () => {
                closeButton.style.backgroundColor = '#333';
            });

            closeButton.addEventListener('mouseout', () => {
                closeButton.style.backgroundColor = '#666';
            });

            // 页面URL文本显示
            urlText = document.createElement('p');
            urlText.style.wordBreak = 'break-all';
            urlText.style.maxWidth = '300px';
            urlText.style.marginTop = '15px';
            urlText.style.fontSize = '14px';
            urlText.style.color = '#555';

            // 组装弹窗
            qrContainer.appendChild(urlText);
            qrModal.appendChild(qrContainer);
            qrModal.appendChild(closeButton);
            document.body.appendChild(qrModal);

            // 弹窗事件监听
            closeButton.addEventListener('click', () => {
                qrModal.style.display = 'none';
            });

            qrModal.addEventListener('click', (e) => {
                if (e.target === qrModal) {
                    qrModal.style.display = 'none';
                }
            });

            document.addEventListener('keydown', (e) => {
                if (e.key === 'Escape' && qrModal.style.display === 'flex') {
                    qrModal.style.display = 'none';
                }
            });
        }

        // 生成二维码函数
        function generateQRCode() {
            initModal(); // 首次点击时初始化弹窗

            // 清空之前的二维码
            while (qrContainer.firstChild) {
                if (qrContainer.firstChild.tagName === 'IMG' || qrContainer.firstChild.tagName === 'CANVAS') {
                    qrContainer.removeChild(qrContainer.firstChild);
                } else {
                    break;
                }
            }

            // 重置容器缩放
            qrContainer.style.transform = 'scale(0.95)';

            // 获取当前页面URL
            const currentUrl = window.location.href;
            urlText.textContent = currentUrl;

            // 生成二维码
            QRCode.toCanvas(currentUrl, {
                width: 300,
                margin: 1
            }, function (error, canvas) {
                if (error) {
                    console.error(error);
                    alert('生成二维码失败: ' + error.message);
                    return;
                }
                qrContainer.insertBefore(canvas, qrContainer.firstChild);
            });

            // 显示弹窗
            qrModal.style.display = 'flex';
        }

        // 绑定事件
        qrButton.addEventListener('click', generateQRCode);

        // 添加到页面
        buttonContainer.appendChild(qrButton);
        buttonContainer.appendChild(hideButton);
        document.body.appendChild(buttonContainer);
    }

    // 初始化流程 - 优先使用缓存
    if (isCacheValid() && loadFromCache()) {
        // 缓存有效且加载成功,创建UI
        createUI();
    } else {
        // 缓存无效或加载失败,重新下载
        downloadAndCacheFonts();
        // 无论字体加载结果如何,都创建UI(确保基本功能可用)
        createUI();
    }
})();

以下是原版本:

一个经常使用的网站需要搭配手机一起使用,但是由于屏蔽了右键,导致Edge的QR码无法调用,于是用AI写了一个脚本,会在每个页面左边的正中间生成一个二维码图标,点击之后可以一键生成当前网页的二维码。

// ==UserScript==
// @name         页面二维码生成器(完整缓存版)
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  生成当前页面二维码,带完善缓存机制确保图标正常显示
// @author       某知名AI
// @match        *://*/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @connect      cdnjs.cloudflare.com
// @require      https://cdn.jsdelivr.net/npm/qrcode@1.5.1/build/qrcode.min.js
// ==/UserScript==

(function() {
    'use strict';

    // 缓存配置 - 7天有效期
    const CACHE_EXPIRY_DAYS = 7;
    const FONT_CACHE_KEY = 'qrcodeFontCache';
    const FONT_CACHE_TIMESTAMP = 'qrcodeFontTimestamp';

    // 所需字体文件URL
    const fontUrls = {
        woff2: 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/fonts/fontawesome-webfont.woff2?v=4.7.0',
        woff: 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/fonts/fontawesome-webfont.woff?v=4.7.0'
    };

    // 检查缓存是否有效
    function isCacheValid() {
        const timestamp = GM_getValue(FONT_CACHE_TIMESTAMP, 0);
        const now = new Date().getTime();
        const expiryTime = CACHE_EXPIRY_DAYS * 24 * 60 * 60 * 1000;
        return timestamp + expiryTime > now;
    }

    // 从缓存加载字体
    function loadFromCache() {
        const cachedFonts = GM_getValue(FONT_CACHE_KEY, null);
        if (cachedFonts) {
            injectFontStyles(cachedFonts);
            return true;
        }
        return false;
    }

    // 下载字体并缓存
    function downloadAndCacheFonts() {
        // 优先尝试woff2格式,兼容性更好
        fetchFont(fontUrls.woff2, 'woff2')
            .catch(() => {
                // 如果woff2失败,尝试woff格式
                return fetchFont(fontUrls.woff, 'woff');
            })
            .then(({data, format}) => {
                const fontData = {data, format};
                // 存储到缓存
                GM_setValue(FONT_CACHE_KEY, fontData);
                GM_setValue(FONT_CACHE_TIMESTAMP, new Date().getTime());
                injectFontStyles(fontData);
            })
            .catch(() => {
                // 所有字体加载失败时使用基础样式 fallback
                injectFallbackStyles();
            });
    }

    // 下载字体文件
    function fetchFont(url, format) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'GET',
                url: url,
                responseType: 'arraybuffer',
                onload: function(response) {
                    if (response.status === 200 && response.response) {
                        // 转换为base64
                        const base64Data = btoa(
                            new Uint8Array(response.response).reduce(
                                (data, byte) => data + String.fromCharCode(byte),
                                ''
                            )
                        );
                        resolve({data: base64Data, format});
                    } else {
                        reject(new Error(`Failed to load font: ${response.status}`));
                    }
                },
                onerror: function() {
                    reject(new Error('Network error while loading font'));
                },
                ontimeout: function() {
                    reject(new Error('Font loading timed out'));
                }
            });
        });
    }

    // 注入字体样式
    function injectFontStyles(fontData) {
        const style = document.createElement('style');
        style.textContent = `
            @font-face {
                font-family: 'FontAwesome';
                src: url('data:application/font-${fontData.format};base64,${fontData.data}') format('${fontData.format}');
                font-weight: normal;
                font-style: normal;
            }
            .fa {
                display: inline-block;
                font: normal normal normal 14px/1 FontAwesome;
                font-size: inherit;
                text-rendering: auto;
                -webkit-font-smoothing: antialiased;
                -moz-osx-font-smoothing: grayscale;
            }
            .fa-qrcode:before {
                content: "\\f029";
            }
            .fa-times:before {
                content: "\\f00d";
            }
        `;
        document.head.appendChild(style);
    }

    // 字体加载失败时的 fallback 样式
    function injectFallbackStyles() {
        const style = document.createElement('style');
        style.textContent = `
            .fa-qrcode:before { content: "🔳"; }
            .fa-times:before { content: "✕"; }
        `;
        document.head.appendChild(style);
    }

    // 创建UI元素
    function createUI() {
        // 创建按钮容器
        const buttonContainer = document.createElement('div');
        buttonContainer.style.position = 'fixed';
        buttonContainer.style.left = '10px';
        buttonContainer.style.top = '50%';
        buttonContainer.style.transform = 'translateY(-50%)';
        buttonContainer.style.zIndex = '9999';
        buttonContainer.style.display = 'flex';
        buttonContainer.style.flexDirection = 'column';
        buttonContainer.style.alignItems = 'center';

        // 创建二维码按钮
        const qrButton = document.createElement('button');
        qrButton.innerHTML = '<i class="fa fa-qrcode"></i>';

        // 创建关闭按钮(默认隐藏)
        const hideButton = document.createElement('button');
        hideButton.innerHTML = '<i class="fa fa-times"></i>';
        hideButton.style.position = 'absolute';
        hideButton.style.top = '-10px';
        hideButton.style.right = '-10px';
        hideButton.style.width = '24px';
        hideButton.style.height = '24px';
        hideButton.style.borderRadius = '50%';
        hideButton.style.backgroundColor = '#f44336';
        hideButton.style.color = 'white';
        hideButton.style.border = 'none';
        hideButton.style.cursor = 'pointer';
        hideButton.style.boxShadow = '0 1px 3px rgba(0,0,0,0.2)';
        hideButton.style.display = 'none'; // 默认隐藏
        hideButton.style.alignItems = 'center';
        hideButton.style.justifyContent = 'center';
        hideButton.style.fontSize = '12px';
        hideButton.title = '彻底隐藏二维码按钮(刷新页面可恢复)';

        // 二维码按钮样式
        qrButton.style.width = '40px';
        qrButton.style.height = '40px';
        qrButton.style.borderRadius = '8px';
        qrButton.style.backgroundColor = 'rgba(255, 255, 255, 0.8)';
        qrButton.style.color = '#333';
        qrButton.style.border = '1px solid #ddd';
        qrButton.style.cursor = 'pointer';
        qrButton.style.boxShadow = '0 2px 5px rgba(0,0,0,0.1)';
        qrButton.style.transition = 'all 0.3s ease';
        qrButton.style.display = 'flex';
        qrButton.style.alignItems = 'center';
        qrButton.style.justifyContent = 'center';
        qrButton.style.fontSize = '18px';
        qrButton.title = '生成当前页面二维码';

        // 按钮容器悬停效果
        buttonContainer.addEventListener('mouseover', () => {
            qrButton.style.width = '50px';
            qrButton.style.backgroundColor = 'white';
            qrButton.style.boxShadow = '0 3px 8px rgba(0,0,0,0.2)';
            hideButton.style.display = 'flex';
        });

        buttonContainer.addEventListener('mouseout', () => {
            qrButton.style.width = '40px';
            qrButton.style.backgroundColor = 'rgba(255, 255, 255, 0.8)';
            qrButton.style.boxShadow = '0 2px 5px rgba(0,0,0,0.1)';
            hideButton.style.display = 'none';
        });

        // 弹窗元素(延迟创建)
        let qrModal, qrContainer, closeButton, urlText;

        function initModal() {
            if (qrModal) return;

            // 创建二维码弹窗容器
            qrModal = document.createElement('div');
            qrModal.style.position = 'fixed';
            qrModal.style.top = '0';
            qrModal.style.left = '0';
            qrModal.style.width = '100%';
            qrModal.style.height = '100%';
            qrModal.style.backgroundColor = 'rgba(0,0,0,0.7)';
            qrModal.style.display = 'none';
            qrModal.style.justifyContent = 'center';
            qrModal.style.alignItems = 'center';
            qrModal.style.zIndex = '10000';
            qrModal.style.flexDirection = 'column';
            qrModal.style.backdropFilter = 'blur(3px)';

            // 创建二维码图片容器
            qrContainer = document.createElement('div');
            qrContainer.style.backgroundColor = 'white';
            qrContainer.style.padding = '20px';
            qrContainer.style.borderRadius = '10px';
            qrContainer.style.boxShadow = '0 0 20px rgba(0,0,0,0.5)';
            qrContainer.style.textAlign = 'center';
            qrContainer.style.maxWidth = '90%';
            qrContainer.style.transform = 'scale(0.95)';
            qrContainer.style.transition = 'transform 0.3s ease';

            // 创建关闭按钮
            closeButton = document.createElement('button');
            closeButton.innerHTML = '<i class="fa fa-times"></i> 关闭';
            closeButton.style.marginTop = '20px';
            closeButton.style.padding = '8px 16px';
            closeButton.style.backgroundColor = '#666';
            closeButton.style.color = 'white';
            closeButton.style.border = 'none';
            closeButton.style.borderRadius = '5px';
            closeButton.style.cursor = 'pointer';
            closeButton.style.fontSize = '14px';
            closeButton.style.transition = 'background-color 0.2s';

            closeButton.addEventListener('mouseover', () => {
                closeButton.style.backgroundColor = '#333';
            });

            closeButton.addEventListener('mouseout', () => {
                closeButton.style.backgroundColor = '#666';
            });

            // 页面URL文本显示
            urlText = document.createElement('p');
            urlText.style.wordBreak = 'break-all';
            urlText.style.maxWidth = '300px';
            urlText.style.marginTop = '15px';
            urlText.style.fontSize = '14px';
            urlText.style.color = '#555';

            // 组装弹窗
            qrContainer.appendChild(urlText);
            qrModal.appendChild(qrContainer);
            qrModal.appendChild(closeButton);
            document.body.appendChild(qrModal);

            // 弹窗事件监听
            closeButton.addEventListener('click', () => {
                qrModal.style.display = 'none';
            });

            qrModal.addEventListener('click', (e) => {
                if (e.target === qrModal) {
                    qrModal.style.display = 'none';
                }
            });

            document.addEventListener('keydown', (e) => {
                if (e.key === 'Escape' && qrModal.style.display === 'flex') {
                    qrModal.style.display = 'none';
                }
            });
        }

        // 生成二维码函数
        function generateQRCode() {
            initModal(); // 首次点击时初始化弹窗

            // 清空之前的二维码
            while (qrContainer.firstChild) {
                if (qrContainer.firstChild.tagName === 'IMG' || qrContainer.firstChild.tagName === 'CANVAS') {
                    qrContainer.removeChild(qrContainer.firstChild);
                } else {
                    break;
                }
            }

            // 重置容器缩放
            qrContainer.style.transform = 'scale(0.95)';

            // 获取当前页面URL
            const currentUrl = window.location.href;
            urlText.textContent = currentUrl;

            // 生成二维码
            QRCode.toCanvas(currentUrl, {
                width: 300,
                margin: 1
            }, function (error, canvas) {
                if (error) {
                    console.error(error);
                    alert('生成二维码失败: ' + error.message);
                    return;
                }
                qrContainer.insertBefore(canvas, qrContainer.firstChild);
            });

            // 显示弹窗
            qrModal.style.display = 'flex';
        }

        // 彻底隐藏二维码按钮
        function hideQRButtonCompletely() {
            if (buttonContainer.parentNode === document.body) {
                document.body.removeChild(buttonContainer);
            }
        }

        // 绑定事件
        qrButton.addEventListener('click', generateQRCode);
        hideButton.addEventListener('click', hideQRButtonCompletely);

        // 添加到页面
        buttonContainer.appendChild(qrButton);
        buttonContainer.appendChild(hideButton);
        document.body.appendChild(buttonContainer);
    }

    // 初始化流程 - 优先使用缓存
    if (isCacheValid() && loadFromCache()) {
        // 缓存有效且加载成功,创建UI
        createUI();
    } else {
        // 缓存无效或加载失败,重新下载
        downloadAndCacheFonts();
        // 无论字体加载结果如何,都创建UI(确保基本功能可用)
        createUI();
    }
})();

 


这短短的一生,我们最终都会失去。