模版之家icon嗅探

一键获取页面所有SVG图片,支持批量下载和复制SVG代码

当前为 2025-08-07 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         模版之家icon嗅探
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  一键获取页面所有SVG图片,支持批量下载和复制SVG代码
// @author       You
// @match        https://www.bootstrapmb.com/*
// @grant        GM_addStyle
// @grant        GM_setClipboard
// @grant        GM_notification
// @require      https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js
// ==/UserScript==

(function() {
    'use strict';

    // 添加样式
    GM_addStyle(`
        #svgSnifferBtn {
            position: fixed;
            top: 20px;
            right: 20px;
            z-index: 9999;
            background: #4CAF50;
            color: white;
            border: none;
            padding: 10px 15px;
            border-radius: 4px;
            cursor: pointer;
            font-weight: bold;
            box-shadow: 0 2px 10px rgba(0,0,0,0.2);
        }
        #svgSnifferBtn:hover {
            background: #45a049;
        }
        #svgModal {
            display: none;
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            width: 80%;
            max-width: 800px;
            max-height: 80vh;
            background: white;
            z-index: 10000;
            border-radius: 8px;
            box-shadow: 0 4px 20px rgba(0,0,0,0.3);
            overflow: hidden;
            font-family: Arial, sans-serif;
        }
        .modal-header {
            background: #3498db;
            color: white;
            padding: 15px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .modal-header h2 {
            margin: 0;
            font-size: 1.2rem;
        }
        .close-btn {
            background: none;
            border: none;
            color: white;
            font-size: 1.5rem;
            cursor: pointer;
        }
        .modal-content {
            padding: 20px;
            overflow-y: auto;
            max-height: 60vh;
        }
        .svg-item {
            display: flex;
            align-items: center;
            padding: 10px;
            border-bottom: 1px solid #eee;
        }
        .svg-checkbox {
            margin-right: 15px;
        }
        .svg-preview {
            width: 40px;
            height: 40px;
            margin-right: 15px;
            border: 1px solid #ddd;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .svg-preview svg {
            max-width: 100%;
            max-height: 100%;
        }
        .svg-name {
            flex-grow: 1;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }
        .overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0,0,0,0.5);
            z-index: 9999;
            display: none;
        }
        .action-bar {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 10px 15px;
            background: #f9f9f9;
            border-bottom: 1px solid #eee;
        }
        .select-all-control {
            display: flex;
            align-items: center;
        }
        .action-buttons {
            display: flex;
            gap: 10px;
        }
        .action-btn {
            padding: 8px 15px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-weight: bold;
        }
        .download-btn {
            background: #27ae60;
            color: white;
        }
        .copy-btn {
            background: #2980b9;
            color: white;
        }
        .loading {
            text-align: center;
            padding: 20px;
            font-size: 1.2rem;
            color: #666;
        }
        .copy-notification {
            position: fixed;
            top: 20px;
            left: 50%;
            transform: translateX(-50%);
            background-color: #4CAF50;
            color: white;
            padding: 10px 20px;
            border-radius: 4px;
            z-index: 100000;
            opacity: 0;
            transition: opacity 0.5s;
            pointer-events: none;
            white-space: nowrap;
        }
    `);

    // 创建嗅探按钮
    const snifferBtn = document.createElement('button');
    snifferBtn.id = 'svgSnifferBtn';
    snifferBtn.textContent = '嗅探SVG';
    document.body.appendChild(snifferBtn);

    // 创建模态框
    const modal = document.createElement('div');
    modal.id = 'svgModal';
    modal.innerHTML = `
        <div class="modal-header">
            <h2>SVG图片列表</h2>
            <button class="close-btn">&times;</button>
        </div>
        <div class="action-bar">
            <div class="select-all-control">
                <label>
                    <input type="checkbox" id="selectAll">
                    全选
                </label>
            </div>
            <div class="action-buttons">
                <button class="action-btn download-btn">下载选中</button>
                <button class="action-btn copy-btn">复制SVG</button>
            </div>
        </div>
        <div class="modal-content" id="svgList">
            <div class="loading">正在收集SVG资源...</div>
        </div>
    `;
    document.body.appendChild(modal);

    // 创建遮罩层
    const overlay = document.createElement('div');
    overlay.className = 'overlay';
    document.body.appendChild(overlay);

    // 创建复制通知元素
    const copyNotification = document.createElement('div');
    copyNotification.className = 'copy-notification';
    copyNotification.textContent = '已复制到剪贴板!';
    document.body.appendChild(copyNotification);

    // 全局变量存储SVG项目
    let globalSvgItems = [];

    // 收集SVG函数
    function collectSVGs() {
        const svgItems = [];
        
        // 1. 收集主展示区SVG
        const mainSvgContainer = document.querySelector('.item-container.svg.icons .item-thumb');
        if (mainSvgContainer) {
            const svg = mainSvgContainer.querySelector('svg');
            if (svg) {
                const title = document.querySelector('h1')?.textContent || '主SVG';
                const id = 'main-svg';
                svgItems.push({
                    name: title,
                    svg: svg.outerHTML,
                    id: id
                });
            }
        }

        // 2. 收集网格区SVG
        const gridItems = document.querySelectorAll('.icon-grid .gridItem');
        gridItems.forEach(item => {
            const svgContainer = item.querySelector('.svgUrl');
            if (svgContainer) {
                const svg = svgContainer.querySelector('svg');
                if (svg) {
                    const title = item.querySelector('.gridItem__title')?.textContent || '未命名';
                    const id = item.id || `svg-${Math.random().toString(36).substr(2, 9)}`;
                    svgItems.push({
                        name: title,
                        svg: svg.outerHTML,
                        id: id
                    });
                }
            }
        });

        // 3. 收集其他区域的SVG
        const allSvgs = document.querySelectorAll('svg');
        allSvgs.forEach(svg => {
            // 跳过已经收集的SVG
            if (!svgItems.some(item => item.svg === svg.outerHTML)) {
                // 尝试获取最近的标题
                let title = '未命名SVG';
                let parent = svg.parentElement;
                while (parent) {
                    if (parent.querySelector('h1, h2, h3, h4, h5, h6')) {
                        title = parent.querySelector('h1, h2, h3, h4, h5, h6').textContent;
                        break;
                    }
                    parent = parent.parentElement;
                }
                
                const id = `svg-${Math.random().toString(36).substr(2, 9)}`;
                svgItems.push({
                    name: title,
                    svg: svg.outerHTML,
                    id: id
                });
            }
        });

        return svgItems;
    }

    // 显示SVG列表
    function showSVGList() {
        const svgList = document.getElementById('svgList');
        svgList.innerHTML = '<div class="loading">正在收集SVG资源...</div>';
        modal.style.display = 'block';
        overlay.style.display = 'block';
        
        // 使用setTimeout让UI有机会更新
        setTimeout(() => {
            try {
                const svgItems = collectSVGs();
                globalSvgItems = svgItems; // 存储到全局变量
                
                if (svgItems.length === 0) {
                    svgList.innerHTML = '<div class="loading">没有找到SVG资源</div>';
                    return;
                }
                
                svgList.innerHTML = '';
                
                svgItems.forEach(item => {
                    const itemElement = document.createElement('div');
                    itemElement.className = 'svg-item';
                    itemElement.innerHTML = `
                        <input type="checkbox" class="svg-checkbox" data-id="${item.id}" checked>
                        <div class="svg-preview">${item.svg}</div>
                        <div class="svg-name" title="${item.name}">${item.name}</div>
                    `;
                    svgList.appendChild(itemElement);
                });
            } catch (error) {
                console.error('SVG嗅探错误:', error);
                svgList.innerHTML = `<div class="loading">错误: ${error.message}</div>`;
            }
        }, 100);
    }

    // 下载选中的SVG
    function downloadSelected() {
        const checkboxes = document.querySelectorAll('.svg-checkbox:checked');
        if (checkboxes.length === 0) {
            alert('请至少选择一个SVG!');
            return;
        }
        
        const zip = new JSZip();
        
        checkboxes.forEach(checkbox => {
            const id = checkbox.dataset.id;
            const item = globalSvgItems.find(i => i.id === id);
            if (item) {
                // 清理文件名
                const cleanName = item.name.replace(/[^\w\u4e00-\u9fa5]/g, '_');
                zip.file(`${cleanName}.svg`, item.svg);
            }
        });
        
        zip.generateAsync({type: 'blob'}).then(content => {
            saveAs(content, 'svg_collection.zip');
        });
    }

    // 使用TextArea复制文本
    function copyUsingTextArea(text) {
        return new Promise((resolve) => {
            const textArea = document.createElement('textarea');
            textArea.value = text;
            textArea.style.position = 'fixed';
            textArea.style.top = '0';
            textArea.style.left = '0';
            document.body.appendChild(textArea);
            textArea.focus();
            textArea.select();
            
            try {
                const successful = document.execCommand('copy');
                if (successful) {
                    resolve(true);
                } else {
                    resolve(false);
                }
            } catch (err) {
                resolve(false);
            } finally {
                document.body.removeChild(textArea);
            }
        });
    }

    // 复制选中的纯SVG代码 (仅SVG代码,不包含注释)
    async function copySelected() {
        const checkboxes = document.querySelectorAll('.svg-checkbox:checked');
        if (checkboxes.length === 0) {
            alert('请至少选择一个SVG!');
            return;
        }
        
        // 构建纯SVG代码字符串
        let combinedCode = '';
        
        // 如果有多个选中项目,按行分隔
        if (checkboxes.length > 1) {
            checkboxes.forEach(checkbox => {
                const id = checkbox.dataset.id;
                const item = globalSvgItems.find(i => i.id === id);
                if (item) {
                    combinedCode += `${item.svg}\n\n`;
                }
            });
        } 
        // 如果只有一个选中项目,直接复制
        else {
            const id = checkboxes[0].dataset.id;
            const item = globalSvgItems.find(i => i.id === id);
            if (item) {
                combinedCode = item.svg;
            }
        }
        
        // 尝试三种复制方法
        let copySucceeded = false;
        
        // 方法1: 使用Tampermonkey API
        try {
            GM_setClipboard(combinedCode, 'text');
            copySucceeded = true;
        } catch (e) {
            console.log('GM_setClipboard failed, trying other methods');
        }
        
        // 方法2: 使用navigator.clipboard API (仅限安全上下文)
        if (!copySucceeded && navigator.clipboard) {
            try {
                await navigator.clipboard.writeText(combinedCode);
                copySucceeded = true;
            } catch (err) {
                console.log('navigator.clipboard failed');
            }
        }
        
        // 方法3: 使用textArea方法 (最可靠)
        if (!copySucceeded) {
            copySucceeded = await copyUsingTextArea(combinedCode);
        }
        
        // 显示通知
        if (copySucceeded) {
            copyNotification.textContent = `已复制 ${checkboxes.length} 个SVG代码`;
            copyNotification.style.opacity = '1';
            
            // 3秒后隐藏通知
            setTimeout(() => {
                copyNotification.style.opacity = '0';
            }, 3000);
        } else {
            // 如果都失败,使用Tampermonkey通知API或alert
            try {
                GM_notification({
                    text: `复制SVG失败!内容已保存到控制台`,
                    title: 'SVG嗅探器错误',
                    silent: false
                });
            } catch (e) {
                alert('复制失败!请检查控制台获取SVG代码');
            }
            
            // 记录代码到控制台便于用户手动复制
            console.log('========== 复制的SVG代码 ==========');
            console.log(combinedCode);
            console.log('=================================');
        }
    }

    // 事件监听
    snifferBtn.addEventListener('click', showSVGList);
    
    modal.querySelector('.close-btn').addEventListener('click', () => {
        modal.style.display = 'none';
        overlay.style.display = 'none';
    });
    
    overlay.addEventListener('click', () => {
        modal.style.display = 'none';
        overlay.style.display = 'none';
    });
    
    modal.querySelector('.download-btn').addEventListener('click', downloadSelected);
    modal.querySelector('.copy-btn').addEventListener('click', copySelected);
    
    document.getElementById('selectAll').addEventListener('change', (e) => {
        const checkboxes = document.querySelectorAll('.svg-checkbox');
        checkboxes.forEach(checkbox => {
            checkbox.checked = e.target.checked;
        });
    });
})();