Greasy Fork 支持简体中文。

CPP便捷快速扫本

增强版筛选,支持黑名单,支持心愿单

// ==UserScript==
// @name         CPP便捷快速扫本
// @license      MIT
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  增强版筛选,支持黑名单,支持心愿单
// @author       AnchorCat
// @match        https://www.allcpp.cn/allcpp/event/eventdoujinshi.do?*
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// ==/UserScript==

(function () {
    'use strict';

    // 本地默认黑名单值
    const DEFAULT_BLACKLIST = [];

    // 配置参数
    const CONFIG = {
        REQUEST_SIZE: 30,
        LOAD_DELAY: 500,
        // 检查GM中是否存在BLACKLIST数据,不存在则使用默认值并存储
        BLACKLIST: (function () {
            const storedBlacklist = GM_getValue("BLACKLIST", []);
            if (storedBlacklist) {
                return storedBlacklist;
            } else {
                GM_setValue("BLACKLIST", DEFAULT_BLACKLIST);
                return DEFAULT_BLACKLIST;
            }
        })(),
        API_URL: "https://www.allcpp.cn/api/doujinshi/search.do",
        COVER_PREFIX: "https://imagecdn3.allcpp.cn/upload"
    };

    // 从GM中获取缓存
    const CACHE = GM_getValue('REQUEST_CACHE', {});

    // 用于存储正在进行的请求
    const activeRequests = new Set();
    // 请求暂停标记
    let isRequestPaused = false;

    // 获取页面勾选的 typeIds
    function getCheckedTypeIds() {
        const activeTypeBoxes = document.querySelectorAll('.type-box .active');
        const typeIds = [];
        activeTypeBoxes.forEach(box => {
            const id = box.getAttribute('data-id');
            if (id) {
                typeIds.push(id);
            }
        });
        return typeIds.join('%2C');
    }

    // 获取当前活动的eventID
    function getActiveEventDayId() {
        const activeEventDay = document.querySelector('.event-days .active');
        return activeEventDay ? activeEventDay.getAttribute('data-id') : null;
    }

    // 创建悬浮按钮
    const floatBtn = document.createElement('div');
    floatBtn.id = 'float-btn';
    floatBtn.textContent = '打开作品筛选\n(按照当前类型)';
    document.body.appendChild(floatBtn);

    // 悬浮按钮点击处理
    document.body.addEventListener('click', async (e) => {
        if (e.target.id === 'float-btn') {
            const filterPage = createFilterPage();
            filterPage.currentRequestPage = 1;
            const checkedTypeIds = getCheckedTypeIds();
            const eventId = getActiveEventDayId();
            CONFIG.BASE_PARAMS = `eventId=${eventId}&orderBy=1&typeIds=${checkedTypeIds}&pageSize=30`;
            CONFIG.CURRENT_EVENTID = eventId;
            setupIntersectionObserver(filterPage); // 设置曝光监听,用于获取更多数据
        }
    });

    // 创建筛选页面
    function createFilterPage() {
        const existing = document.querySelector('.filter-page');
        if (existing) existing.remove();

        const filterPage = document.createElement('div');
        filterPage.className = 'filter-page';
        filterPage.innerHTML = `
            <div class="close-btn">关闭</div>
            <div class="blacklist-btn">管理Tag黑名单</div>
            <div class="force-refresh-btn">强制刷新</div>
            <h1>作品筛选列表(总计 <span id="total-count">0</span> 页)</h1>
            <div id="works-container"></div>
            <div id="loading-status" style="padding: 20px; text-align: center;"></div>
        `;
        // 黑名单面板交互
        let blacklistPanel = null;
        filterPage.querySelector('.blacklist-btn').addEventListener('click', (e) => {
            if (!blacklistPanel) {
                blacklistPanel = createBlacklistPanel(filterPage);
                filterPage.appendChild(blacklistPanel);

                // 点击外部关闭
                const clickHandler = (event) => {
                    const isDeleteButton = event.target.classList.contains('delete-btn');
                    if (isDeleteButton) return;
                    if (!blacklistPanel.contains(event.target) &&
                        event.target !== e.target) {
                        blacklistPanel.remove();
                        blacklistPanel = null;
                        document.removeEventListener('click', clickHandler);
                    }
                };
                document.addEventListener('click', clickHandler);
            }
        });
        // 绑定关闭事件
        filterPage.querySelector('.close-btn').addEventListener('click', () => {
            filterPage.remove();
        });

        // 绑定强制刷新事件
        filterPage.querySelector('.force-refresh-btn').addEventListener('click', () => {
            isRequestPaused = true; // 暂停新请求
            // 取消所有正在进行的请求
            activeRequests.forEach(request => {
                if (request.abort) {
                    request.abort();
                }
            });
            activeRequests.clear(); // 清空请求队列

            GM_setValue('REQUEST_CACHE', {});
            Object.keys(CACHE).forEach(key => delete CACHE[key]);
            filterPage.querySelector('#works-container').innerHTML = '';
            filterPage.currentRequestPage = 1;

            setTimeout(() => {
                isRequestPaused = false; // 恢复请求
                loadMoreData(filterPage, 1);
            }, 100); // 短暂延迟后重新发起请求
        });
        document.body.appendChild(filterPage);
        return filterPage;
    }

    // 单页加载逻辑
    async function loadPage(page) {
        if (isRequestPaused) {
            return null;
        }
        const url = `${CONFIG.API_URL}?${CONFIG.BASE_PARAMS}&pageIndex=${page}`;
        if (CACHE[url]) {
            console.log(`使用缓存数据,第${page}页`);
            return CACHE[url];
        }

        console.log(`正在请求第${page}页数据`);
        return new Promise((resolve, reject) => {
            const request = GM_xmlhttpRequest({
                method: 'GET',
                url,
                onload: (response) => {
                    activeRequests.delete(request); // 请求完成,从队列中移除
                    if (response.status >= 200 && response.status < 300) {
                        const data = JSON.parse(response.responseText);
                        const result = {
                            works: data.result?.list || [],
                            total: data.result?.total || 0
                        };
                        CACHE[url] = result;
                        GM_setValue('REQUEST_CACHE', CACHE);
                        resolve(result);
                    } else {
                        reject(new Error(`HTTP error: ${response.status}`));
                    }
                },
                onerror: (error) => {
                    activeRequests.delete(request); // 请求出错,从队列中移除
                    reject(new Error(`Network error: ${error.message}`));
                }
            });
            activeRequests.add(request); // 将请求添加到队列中
        });
    }

    function setupIntersectionObserver(filterPage) {
        const placeholder = filterPage.querySelector('#loading-status');
        let isLoading = false;

        const observer = new IntersectionObserver(async (entries, observer) => {
            entries.forEach(entry => {
                if (entry.isIntersecting && !isLoading) {
                    isLoading = true;
                    loadMoreData(filterPage, filterPage.currentRequestPage).then(() => {
                        isLoading = false;
                    });
                }
            });
        });

        observer.observe(placeholder);
    }

    // 加载更多数据(每次加载CONFIG.REQUEST_SIZE条)
    async function loadMoreData(filterPage, startPage) {
        if (isRequestPaused) {
            return;
        }
        const container = filterPage.querySelector('#works-container');
        const status = filterPage.querySelector('#loading-status');
        let currentPage = startPage;
        let loadedCount = 0;
        let hasMore = true;

        while (loadedCount < CONFIG.REQUEST_SIZE && hasMore) {
            if (isRequestPaused) {
                break;
            }
            status.textContent = `正在加载第 ${currentPage} 页...`;
            const result = await loadPage(currentPage);
            currentPage++;
            filterPage.currentRequestPage = currentPage;
            if (!result || result.works.length === 0) {
                hasMore = false;
                status.textContent = "没有更多数据";
                break;
            }

            // 初始化数据存储
            if (!filterPage.allWorks) filterPage.allWorks = [];

            // 添加原始数据并建立索引
            result.works.forEach((work, index) => {
                work.index = filterPage.allWorks.length + 1;
                work.pageIndex = currentPage;
                filterPage.allWorks.push(work);
            });

            // 渲染过滤后的数据
            result.works.forEach(work => {
                if (checkWorkInBlackList(work)) {
                    return;
                }
                renderWorkItem(container, work, filterPage);
                loadedCount++;
            });
            updateCounter(filterPage, result.total);

            await new Promise(r => setTimeout(r, CONFIG.LOAD_DELAY));
        }

        status.textContent = hasMore ? "滚动到底部加载更多" : "已加载全部数据";
    }

    // 更新计数器
    function updateCounter(filterPage, total) {
        const counter = filterPage.querySelector('#total-count');
        if (counter) counter.textContent = Math.ceil(total / 30);
    }

    // 创建黑名单管理面板
    function createBlacklistPanel(filterPage) {
        const panel = document.createElement('div');
        panel.className = 'blacklist-panel';
        panel.innerHTML = `
        <div class="blacklist-controls">
            <input type="text" class="blacklist-input" placeholder="添加/搜索黑名单词">
            <button class="blacklist-add-btn">添加</button>
        </div>
        <div class="blacklist-items"></div>
        <button class="blacklist-export-btn">导出Json文件</button>
        <button class="blacklist-import-btn">导入Json文件</button>
    `;

        // 事件绑定
        const addBtn = panel.querySelector('.blacklist-add-btn');
        const input = panel.querySelector('.blacklist-input');
        const itemsContainer = panel.querySelector('.blacklist-items');
        const exportBtn = panel.querySelector('.blacklist-export-btn');
        const importBtn = panel.querySelector('.blacklist-import-btn');

        // 添加黑名单
        function addToBlacklist() {
            const word = input.value.trim();
            if (word && !CONFIG.BLACKLIST.includes(word)) {
                CONFIG.BLACKLIST.push(word);
                GM_setValue('BLACKLIST', CONFIG.BLACKLIST);
                input.value = '';
                refreshBlacklistItems();
                updateWorksByBlacklist(filterPage);
            }
        }

        // 搜索过滤
        function searchBlacklist() {
            const keyword = input.value.trim().toLowerCase();
            refreshBlacklistItems(keyword);
        }

        // 导出黑名单为 JSON 文件
        function exportBlacklist() {
            const jsonData = JSON.stringify(CONFIG.BLACKLIST, null, 2);
            const blob = new Blob([jsonData], { type: 'application/json' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = 'blacklist.json';
            a.click();
            URL.revokeObjectURL(url);
        }

        // 导入 JSON 文件到黑名单
        function importBlacklist() {
            const inputElement = document.createElement('input');
            inputElement.type = 'file';
            inputElement.accept = 'application/json';
            inputElement.onchange = function (event) {
                const file = event.target.files[0];
                if (file) {
                    const reader = new FileReader();
                    reader.onload = function (e) {
                        try {
                            const importedData = JSON.parse(e.target.result);
                            if (Array.isArray(importedData)) {
                                CONFIG.BLACKLIST = [...new Set([...GM_getValue('BLACKLIST', CONFIG.BLACKLIST), ...importedData])];
                                GM_setValue('BLACKLIST', CONFIG.BLACKLIST);
                                refreshBlacklistItems();
                                updateWorksByBlacklist(filterPage);
                            } else {
                                alert('导入的文件格式不正确,必须是 JSON 数组。');
                            }
                        } catch (error) {
                            alert('导入失败,请确保文件是有效的 JSON 格式。');
                        }
                    };
                    reader.readAsText(file);
                }
            };
            inputElement.click();
        }

        // 点击添加按钮
        addBtn.addEventListener('click', addToBlacklist);

        // 输入框输入时自动搜索
        input.addEventListener('input', searchBlacklist);

        // 点击导出按钮
        exportBtn.addEventListener('click', exportBlacklist);

        // 点击导入按钮
        importBtn.addEventListener('click', importBlacklist);

        // 刷新黑名单列表
        function refreshBlacklistItems(keyword = '') {
            itemsContainer.innerHTML = '';
            CONFIG.BLACKLIST
                .filter(word => word.toLowerCase().includes(keyword)).reverse()
                .forEach(word => {
                    const item = document.createElement('div');
                    item.className = 'blacklist-item';
                    item.innerHTML = `
                    <span>${word}</span>
                    <span class="delete-btn">删除</span>
                `;

                    item.querySelector('.delete-btn').addEventListener('click', () => {
                        CONFIG.BLACKLIST = CONFIG.BLACKLIST.filter(w => w !== word);
                        GM_setValue('BLACKLIST', CONFIG.BLACKLIST);
                        refreshBlacklistItems(input.value.trim().toLowerCase());
                        updateWorksByBlacklist(filterPage);
                    });

                    itemsContainer.appendChild(item);
                });
        }

        // 初始加载
        refreshBlacklistItems();
        return panel;
    }

    // 根据黑名单,从当前的container中移除作品,不刷新全部作品
    function updateWorksByBlacklist(filterPage) {
        const container = filterPage.querySelector('#works-container');
        const works = container.querySelectorAll('.work-item');
        works.forEach(work => {
            const tags = work.querySelectorAll('.tag-text');
            const tagTexts = Array.from(tags).map(tag => tag.textContent);

            // 检查标签文本是否包含黑名单中的词
            if (tagTexts.some(tag => CONFIG.BLACKLIST.includes(tag))) {
                // 若包含,则隐藏该作品项
                work.style.display = 'none';
            } else {
                // 若不包含,则确保该作品项是显示的
                work.style.display = '';
            }
        });
    }

    // 全局刷新作品列表
    function refreshWorks(filterPage) {
        const container = filterPage.querySelector('#works-container');
        container.innerHTML = '';
        filterPage.allWorks.forEach(work => {
            if (!checkWorkInBlackList(work)) {
                renderWorkItem(container, work, filterPage);
            }
        });
    }

    // 判断作品是否命中黑名单
    function checkWorkInBlackList(work) {
        if (CONFIG.BLACKLIST.some(word => work.tag.includes(word))
            || CONFIG.BLACKLIST.some(word => work.themeAlias.includes(word))) {
            return true;
        }
        return false;
    }

    // 独立渲染单个作品的方法
    function renderWorkItem(container, work, filterPage) {
        if (CONFIG.BLACKLIST.some(word => work.tag.includes(word))) return;

        const item = document.createElement('div');
        item.className = 'work-item';
        const coverUrl = `${CONFIG.COVER_PREFIX}${work.coverPicUrl}?x-oss-process=style/rectangle_450-600_60`;

        // 添加 type 图标
        const typeIcon = document.createElement('div');
        typeIcon.className = 'type-icon';
        typeIcon.textContent = work.type;

        // 划分标签并生成可点击按钮
        let tags = [];
        if (work.tag) {
            tags = work.tag.split('|');
        }
        tags.unshift(work.themeAlias); // 存在没写tag的作品,添加原作作为tag标签
        tags.unshift(`圈子: ${work.circleName}(${work.circleID})`); // 存在没写tag的作品,添加原作作为tag标签
        tags.unshift(`作者: ${work.nickname}(${work.userID})`);
        tags = [...new Set(tags)];

        const tagButtons = tags.map(tag => {
            const button = document.createElement('button');
            button.innerHTML = `
            <span class="tag-content">
                <span class="tag-text">${tag}</span>
                <span class="close-emoji">❌</span>
            </span>
        `;
            button.classList.add('tag-button');

            // 鼠标悬停事件
            button.addEventListener('mouseover', () => {
                button.classList.add('hovered');
            });

            // 鼠标移出事件
            button.addEventListener('mouseout', () => {
                button.classList.remove('hovered');
            });

            // 点击事件
            button.addEventListener('click', () => {
                if (!CONFIG.BLACKLIST.includes(tag)) {
                    CONFIG.BLACKLIST.push(tag);
                    GM_setValue('BLACKLIST', CONFIG.BLACKLIST);
                    const rect = button.getBoundingClientRect();
                    showToast(`已将 ${tag} 加入到黑名单`, rect.left, rect.bottom);
                    updateWorksByBlacklist(filterPage);
                }
            });

            return button;
        });

        const tagDiv = document.createElement('div');
        tagDiv.classList.add('tags');
        tagButtons.forEach(button => tagDiv.appendChild(button));

        item.innerHTML = `
        <div class="count-box">
            <div class="count">${work.index}</div>
            <div class="page-index">(页面:${work.pageIndex})</div>
        </div>
        <img class="cover-img" src="${coverUrl}"
             onerror="this.src='https://placehold.co/100x140?text=封面加载失败'">
        <div>
            <h2>
                <a class="title-link" href="https://www.allcpp.cn/d/${work.doujinshiId}.do#tabType=0"
                   target="_blank">
                   ${work.doujinshiName}
                </a>
            </h2>
        </div>
    `;

        // 将 type 图标插入到标题上方
        const titleDiv = item.querySelector('div:nth-child(3)');
        titleDiv.insertBefore(typeIcon, titleDiv.firstChild);

        // 添加标签按钮到作品项中
        item.querySelector('div:nth-child(3)').appendChild(tagDiv);

        // 添加心愿单按钮和显示区域
        const wishlistContainer = document.createElement('div');
        wishlistContainer.className = 'wishlist-container';
        const currentWork = work;
        const wishlistInfoContainer = document.createElement('div');
        wishlistInfoContainer.className = 'wishlist-info';
        renderWishlistInfo(currentWork.doujinshiId, wishlistInfoContainer);
        wishlistContainer.appendChild(wishlistInfoContainer);
        item.appendChild(wishlistContainer);
        container.appendChild(item);
    }

    // 显示 toast 提示的函数
    function showToast(message, left, top) {
        const toast = document.createElement('div');
        toast.classList.add('toast');
        toast.textContent = message;
        // 设置位置并留出空隙
        toast.style.left = `${left}px`;
        toast.style.top = `${top + 5}px`;
        document.body.appendChild(toast);

        // 添加淡入效果
        setTimeout(() => {
            toast.classList.add('toast-fade-in');
        }, 50);

        // 定时移除 toast 并添加淡出效果
        setTimeout(() => {
            toast.classList.add('toast-fade-out');
            setTimeout(() => {
                toast.remove();
            }, 300);
        }, 2000);
    }

    // 查询该作品目前适用于哪个心愿单
    async function getCouldBuyEventList(doujinshiid) {
        const url = `https://www.allcpp.cn/allcpp/djswb/getCouldBuyEventList.do?doujinshiid=${doujinshiid}`;
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'GET',
                url,
                onload: (response) => {
                    if (response.status >= 200 && response.status < 300) {
                        const data = JSON.parse(response.responseText);
                        const validEvents = data.result.filter(event => event.eventId > 0);
                        resolve(validEvents);
                    } else {
                        reject(new Error(`HTTP error: ${response.status}`));
                    }
                },
                onerror: (error) => {
                    reject(new Error(`Network error: ${error.message}`));
                }
            });
        });
    }

    // 添加该作品到对应心愿单
    async function addToWishlist(doujinshiid, eventids) {
        const url = `https://www.allcpp.cn/api/djswb/addDoujinshiWannaBuy.do?doujinshiid=${doujinshiid}&eventids=${eventids}`;
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'GET',
                url,
                onload: (response) => {
                    if (response.status >= 200 && response.status < 300) {
                        resolve();
                    } else {
                        reject(new Error(`HTTP error: ${response.status}`));
                    }
                },
                onerror: (error) => {
                    reject(new Error(`Network error: ${error.message}`));
                }
            });
        });
    }

    // 删除该作品到对应心愿单
    async function deleteFromWishlist(doujinshiid, eventids) {
        const url = `https://www.allcpp.cn/api/djswb/deleteDoujinshiWannaBuy.do?doujinshiid=${doujinshiid}&eventids=${eventids}`;
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'GET',
                url,
                onload: (response) => {
                    if (response.status >= 200 && response.status < 300) {
                        resolve();
                    } else {
                        reject(new Error(`HTTP error: ${response.status}`));
                    }
                },
                onerror: (error) => {
                    reject(new Error(`Network error: ${error.message}`));
                }
            });
        });
    }

    // 渲染心愿单信息
    async function renderWishlistInfo(doujinshiid, container) {
        const eventList = await getCouldBuyEventList(doujinshiid);
        container.innerHTML = '';
        eventList.forEach(event => {
            const eventInfo = document.createElement('div');
            if (event.isWannaBuy) {
                const deleteBtn = document.createElement('button');
                deleteBtn.className = 'wishlist-btn del_wish_button';
                deleteBtn.textContent = `${event.eventId == CONFIG.CURRENT_EVENTID ? `⭐️` : ``} 从【${event.eventName}】心愿单里删除`;
                deleteBtn.addEventListener('click', async () => {
                    await deleteFromWishlist(doujinshiid, event.eventId);
                    // 刷新心愿单显示
                    renderWishlistInfo(doujinshiid, container);
                });
                eventInfo.appendChild(deleteBtn);
            } else {
                const addBtn = document.createElement('button');
                addBtn.className = 'wishlist-btn add_wish_button';
                addBtn.textContent = `${event.eventId == CONFIG.CURRENT_EVENTID ? `⭐️` : ``} 添加到【${event.eventName}】心愿单`;
                addBtn.addEventListener('click', async () => {
                    await addToWishlist(doujinshiid, event.eventId);
                    // 刷新心愿单显示
                    renderWishlistInfo(doujinshiid, container);
                });
                eventInfo.appendChild(addBtn);
            }

            container.appendChild(eventInfo);
        });
    }

})();

// 添加 toast 样式,包含淡入淡出效果
GM_addStyle(`
    .toast {
        position: fixed;
        background-color: rgba(0, 0, 0, 0.7);
        color: white;
        padding: 10px 20px;
        border-radius: 5px;
        z-index: 10003;
        opacity: 0;
        transition: opacity 0.3s;
    }
    .toast-fade-in {
        opacity: 1;
    }
    .toast-fade-out {
        opacity: 0;
    }
`);

GM_addStyle(`
    .type-icon {
        font-family: 'Microsoft YaHei','Helvetica';
        line-height: 20px;
        -webkit-font-smoothing: antialiased;
        display: inline-block;
        margin: 3px 10px 5px 0;
        border-radius: 5px;
        border: 1px solid #e1ca82;
        font-size: 14px;
        padding: 3px 5px;
        cursor: pointer;
        background: #e1ca82;
        color: #fff;
    }
`);

// 添加按钮样式
GM_addStyle(`
    .tag-button {
        margin: 2px;
        padding: 4px 8px;
        background-color: #e0e0e0;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        position: relative;
        white-space: nowrap;

        font-family: 'Microsoft YaHei','Helvetica';
        line-height: 20px;
        -webkit-font-smoothing: antialiased;
        display: inline-block;
        font-size: 14px;

    }
    .tag-button:hover {
        background-color: #bdbdbd;
    }
    .tag-button .tag-content {
        display: flex;
        justify-content: center;
        align-items: center;
    }
    .tag-button .tag-text {
        opacity: 1;
        transition: opacity 0.2s;
    }
    .tag-button .close-emoji {
        opacity: 0;
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        transition: opacity 0.2s;
    }
    .tag-button.hovered .tag-text {
        opacity: 0;
    }
    .tag-button.hovered .close-emoji {
        opacity: 1;
    }
`);

// 关闭按钮
GM_addStyle(`
    .close-btn {
        position: fixed;
        top: 20px;
        right: 20px;
        padding: 8px 16px;
        background: #ff4444;
        color: white;
        border-radius: 4px;
        cursor: pointer;
        z-index: 10001;
}
`);

GM_addStyle(`
    /* 黑名单管理按钮 */
    .blacklist-btn {
        position: fixed;
        top: 20px;
        right: 80px;
        padding: 8px 16px;
        background: #ff9800;
        color: white;
        border-radius: 4px;
        cursor: pointer;
        z-index: 10001;
    }

    .force-refresh-btn {
        position: fixed;
        top: 20px;
        right: 195px;
        padding: 8px 16px;
        background:rgb(69, 37, 215);
        color: white;
        border-radius: 4px;
        cursor: pointer;
        z-index: 10001;
    }

    /* 黑名单管理面板 */
    .blacklist-panel {
        position: fixed;
        top: 60px;
        right: 20px;
        width: 300px;
        background: white;
        box-shadow: 0 2px 10px rgba(0,0,0,0.2);
        z-index: 10002;
        padding: 15px;
        border-radius: 8px;
    }

    .blacklist-controls {
        margin-bottom: 10px;
        display: flex;
        gap: 10px;
    }

    .blacklist-add-btn {
        padding: 6px 12px;
        background-color: #2196F3;
        color: white;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        transition: background-color 0.3s;
        font-size: 14px;
        font-weight: 500;
        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    }
    .blacklist-add-btn:hover {
        background-color: #1976D2;
    }
    .blacklist-add-btn:active {
        background-color: #1565C0;
        box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
    }

    .blacklist-export-btn {
        padding: 6px 12px;
        background-color: #2196F3;
        color: white;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        transition: background-color 0.3s;
        font-size: 14px;
        font-weight: 500;
        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    }
    .blacklist-export-btn:hover {
        background-color: #1976D2;
    }
    .blacklist-export-btn:active {
        background-color: #1565C0;
        box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
    }

    .blacklist-import-btn {
        padding: 6px 12px;
        background-color: #2196F3;
        color: white;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        transition: background-color 0.3s;
        font-size: 14px;
        font-weight: 500;
        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    }
    .blacklist-import-btn:hover {
        background-color: #1976D2;
    }
    .blacklist-import-btn:active {
        background-color: #1565C0;
        box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
    }

    .blacklist-input {
        flex: 1;
        padding: 6px;
        border: 1px solid #ddd;
        border-radius: 4px;
    }

    .blacklist-items {
        max-height: 300px;
        overflow-y: auto;
    }

    .blacklist-item {
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 8px;
        border-bottom: 1px solid #eee;
    }

    .blacklist-item:hover {
        background: #f5f5f5;
    }

    .delete-btn {
        color: #ff4444;
        cursor: pointer;
        margin-left: 10px; /* 原样式中的左外边距 */
        position: relative;
        left: -10px; /* 往左移动 10px */
    }
`);

GM_addStyle(`
    /* 调整序号样式 */
    .count-box {
        position: absolute;
    left: 15px;
    top: 50%;
    text-align: center;
    font-size: 25px;

    /* 使用 transform 进行偏移 */
    transform: translate(0%, -50%);
    }
    .page-index {
        font-size: 0.8em;
        color: #999;
        margin-top:10px;
    }
    #float-btn {
        position: fixed;
        right: 20px;
        bottom: 20px;
        padding: 12px 24px;
        background: #2196F3;
        color: white;
        border-radius: 25px;
        cursor: pointer;
        box-shadow: 0 4px 6px rgba(0,0,0,0.1);
        z-index: 9999;
        transition: 0.3s;
    }
    #float-btn:hover {
        background: #1976D2;
        transform: translateY(-2px);
    }
    .filter-page {
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background: white;
        z-index: 10000;
        overflow-y: auto;
        padding: 20px;
    }
    .work-item {
        display: flex;
        align-items: center;
        padding: 15px;
        border-bottom: 1px solid #eee;
        gap: 20px;
    }
    .cover-img {
        width: 100px;
        height: 140px;
        object-fit: cover;
        border-radius: 4px;
        margin-left: 35px;
    }
    .tags {
        color: #666;
        font-size: 0.9em;
        margin-top:5px;
        max-width: 600px;
    }
    .count {
        min-width: 80px;
        color: #666;
        font-weight: bold;
    }
    .work-item {
        position: relative;
        padding-left: 100px;
    }
    .title-link {
        color: #2196F3;
        text-decoration: none;
    }
    .title-link:hover {
        text-decoration: underline;
    }
`);
GM_addStyle(`
/* 心愿单容器整体样式 */
.wishlist-container {
    border: 1px solid #e0e0e0;
    border-radius: 8px;
    padding: 15px;
    background: #f8f9fa;
}

/* 单个活动条目样式 */
.wishlist-event {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px;
    margin: 8px 0;
    background: white;
    border-radius: 6px;
    box-shadow: 0 2px 4px rgba(0,0,0,0.05);
    transition: box-shadow 0.2s;
}

.wishlist-event:hover {
    box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}

/* 活动名称样式 */
.wishlist-event-name {
    flex: 1;
    font-weight: 500;
    color: #2c3e50;
    font-size: 14px;
    margin-right: 15px;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* 通用按钮基础样式 */
.wishlist-btn {
    padding: 6px 12px;
    border: none;
    border-radius: 20px;
    font-size: 12px;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.2s;
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 5px;
    margin-bottom: 5px;
}

/* 删除按钮特定样式 */
.del_wish_button {
    background: #ff4757;
    color: white;
    min-width: 70px;
}

.del_wish_button:hover {
    background: #ff6b81;
    transform: translateY(-1px);
}

/* 添加按钮特定样式 */
.add_wish_button {
    background: #2ecc71;
    color: white;
    min-width: 100px;
}

.add_wish_button:hover {
    background: #27ae60;
    transform: translateY(-1px);
}

/* 按钮图标样式 */
.wishlist-btn::before {
    content: '';
    display: inline-block;
    width: 14px;
    height: 14px;
    background-size: contain;
}

.del_wish_button::before {
    background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></svg>');
}

.add_wish_button::before {
    background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>');
}
`);