22k4已购游戏搜索

已购买游戏详情页添加搜索功能

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

You will need to install an extension such as Tampermonkey to install this script.

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         22k4已购游戏搜索
// @namespace    http://tampermonkey.net/
// @version      2024-10-07
// @description   已购买游戏详情页添加搜索功能
// @author       口吃者
// @match        https://22k4.com/member/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=22k4.com
// @grant        none
// @license MIT
// ==/UserScript==
/* 
    1. 多次快速点击分页按钮造成卡死
    2. 可以增加保存配置功能,在没有购买新游戏时,可以保存配置,下次直接加载
*/
class ProductCatalog {
    constructor() {
        this.pages = []; // 存储每页的商品列表
        this.productByName = new Map(); // 存储商品名称到商品对象的映射
    }

    // 添加商品到指定页
    addProductToPage(pageIndex, product) {
        if (!this.pages[pageIndex]) {
        this.pages[pageIndex] = [];
        }

        // 检查商品名称是否已存在
        const existingProduct = this.productByName.get(product.name.toLowerCase());
        if (existingProduct) {
        console.log(`Product with name "${product.name}" already exists.`);
        return; // 商品名称已存在,不添加
        }

        // 添加商品到对应页,并更新映射
        this.pages[pageIndex].push(product);
        this.productByName.set(product.name.toLowerCase(), product);
    }

    // 根据商品名称查询商品
    findProductByName(name) {
        const lowerCaseName = name.toLowerCase();
        return this.productByName.get(lowerCaseName);
    }
    // 模糊查询商品
    searchProductsByPartialName(query) {
        const results = [];

        // 遍历所有商品
        this.productByName.forEach((product, name) => {
        if (name.includes(query.toLowerCase())) {
            results.push(product);
        }
        });

        return results;
    }

    // 获取所有商品
    getAllProducts() {
        return this.productByName.values();
    }

    // 获取某一页的所有商品
    getProductsOnPage(pageIndex) {
        return this.pages[pageIndex] || [];
    }
}
const catalog = new ProductCatalog();
var requestsCompleted = false;
var pickUpGameHref = '#';
var searchGameHref = '#';
(function() {
    'use strict';
    addPanel();
    // Your code here...
})();
window.onload = function() {
    // 示例:检查请求是否全部完成
    let btnSwitch = document.querySelector('#btnSwitch');
    checkTimer = setTimeout(() => {
        btnSwitch.textContent = "正在获取游戏中..."
    }, 1000); // 这只是一个示例,实际应用中应该根据实际场景决定何时检查

    //获取总页数
    let pageCount = document.querySelector("#PageCount").value;
    let pageSize = document.querySelector("#PageSize").value;
    let totalPages = getTotalPages(pageCount, pageSize);
    console.log(totalPages);
    const urls = Array.from({ length: totalPages }, (_, i) =>
                            `https://22k4.com/member/product.php?page=${i + 1}`
                           );

    // 创建一个 Promise 数组
    const fetchPromises = urls.map(url =>
                                   fetch(url, {
        method: 'GET',
        credentials: 'include', // 使得请求会发送 Cookie
        headers: {
            'Content-Type': 'text/html;charset=utf-8',
        }
    }).then(response => {
        if (!response.ok) {
            throw new Error("HTTP error " + response.status);
        }
        return response.text(); // 读取为文本
    })
                                  );

    // 使用 Promise.all 来等待所有请求完成
    Promise.all(fetchPromises)
        .then(htmlTexts => {
        // 处理每个 HTML 文档
        htmlTexts.forEach((htmlText, index) => {
            // 创建一个范围较小的虚拟文档环境
            const parser = new DOMParser();
            const doc = parser.parseFromString(htmlText, "text/html");
            getAllItemDetails(doc, index + 1);
            requestsCompleted = true;
            clearTimeout(checkTimer);
            btnSwitch.textContent = "面板"
        });
    })
        .catch(error => {
        console.error('There was a problem with one of the fetch operations:', error);
    });

    //输入框
    document.getElementById('iptName').addEventListener('input', function(event) {
        let input = event.target.value;
        // 显示建议列表
        displaySuggestions(input, 1);
    })
    // 初始化分页图标
    updatePaginationIcons(1, 1); // 假设初始页码为1,总页数为1(待更新)
    var cssText = `
        #input_suggestion li {
            color: aliceblue;
            cursor: pointer;
        }
        #input_suggestion li:hover {
            color: cadetblue;
            background-color: #f0f0f0;
        }
        #pagination span { 
            display: inline-block; 
            width: 25px;
            height: 25px; 
            line-height: 25px; 
            text-align: center; 
            border: 1px solid #ccc; 
            cursor: pointer;
            background: azure;
            margin-inline-end: 5px;
            margin-inline-start: 5px;
        }
    `
    GMaddStyle(cssText);

}
function addPanel(){
    function genDiv(cls, id){
        let d = document.createElement('div');
        d.style.cssText = 'vertical-align:middle;';
        if(cls){ d.className = cls };
        if(id){ d.id = id };
        return d;
    }
    //创建一个可以自由弹出的面板
    function genPanel(name, visiable){
        let panel = genDiv(name, name);
        panel.style.cssText = 'background: rgba(58, 58, 58, 0.8);position: fixed;top: 50%;right: 0px;';
        panel.style.cssText += 'text-align: center;transform: translate(0px, -50%);z-index: 100;';
        panel.style.cssText += 'border: 1px solid rgb(83, 83, 83);padding: 5px;border-radius: 10px 0 0 10px;';
        panel.style.cssText += 'transition:right 0.8s;right:-300px;'
        return panel;
    }
    function genButton(test, foo, id, fooParams = {}){
        let b = document.createElement('button');
        b.textContent = test;
        b.style.verticalAlign = 'inherit';
        // 使用箭头函数创建闭包来保存 fooParams 并传递给 foo
        b.addEventListener('click', () => {
            foo.call(b, ...Object.values(fooParams)); // 使用 call 方法确保 this 指向按钮对象
        });
        if(id){ b.id = id };
        return b;
    }
    function genInput(id, value, tips, number) {
        let i = document.createElement('input');
        i.id = id;
        i.style.cssText = 'border:none;border-radius:0;margin:0px 5px;width:60%;text-align:center;';
        i.style.cssText += 'color:#000;background:#fff;vertical-align:inherit;';
        if (value) { i.value = value; }
        if (tips) { i.placeholder = tips; }
        if (number) {
            i.type = 'number';
            i.step = 0.01;
            i.min = 0;
        }
        return i;
    }
    function genUl(items, id) {
        // 创建一个新的<ul>元素
        let ul = document.createElement('ul');
        
        // 如果提供了类名,添加到<ul>元素上
        if (id) {
            ul.id = id;
        }
        
        // // 遍历items数组,为每个项目创建一个<li>元素并添加到<ul>中
        // items.forEach(function(item) {
        //     let li = document.createElement('li');
            
        //     // 这里简单地将item的值设置为<li>的文本内容
        //     // 根据实际需求,这里可以进一步定制化,比如添加事件监听器等
        //     li.textContent = item;
            
        //     // 将<li>添加到<ul>中
        //     ul.appendChild(li);
        // });
        
        return ul;
    }
    function genSpan(id, textContentSpan) {
        let s = document.createElement('span');
        s.id = id;
        s.textContent = textContentSpan;
        return s;
    }
    //展开指定面板
    function showPanel(panelId){
        let panel = document.getElementById(panelId);
        if (panel.style.right == '-300px') {
            panel.style.right = '0';
        } else {
            panel.style.right = '-300px';
        }
    }
    let btnSwitch = genButton('面板', showPanel, 'btnSwitch', {param1: 'autoSell'});
    let panelFunc = genPanel('autoSell', false);    //初始隐藏
    //加入位置
    let lBtnArea = document.querySelector('body > div.page > div.container.m_top_30 > div');
    lBtnArea.insertBefore(btnSwitch, lBtnArea.children[0]);
    lBtnArea.insertBefore(panelFunc, lBtnArea.children[0]);
    //自动出售(加入到面板中) 搜索
    let divName = genDiv();
    let divPagination = genDiv(null, 'pagination');
    let button01 = genButton('搜索', searchGame, 'searchGame');
    let button02 = genButton('提货', pickUpGame, 'pickUpGame');
    let iptName = genInput('iptName', '', ' 游戏名称', false);
    let inputSuggestion = genUl(['无'], 'input_suggestion');
    let prePageSpan = genSpan('prePageSpan', '<');
    let nextPageSpan = genSpan('nextPageSpan', '>');

    divName.appendChild(button01);
    divName.appendChild(button02);
    divName.appendChild(iptName);
    divName.appendChild(inputSuggestion);

    divPagination.append(prePageSpan);
    divPagination.append(nextPageSpan);

    panelFunc.appendChild(divName);
    panelFunc.appendChild(divPagination);
    //todo 可优化成bilibili增强的样式表写法
    let h5Element = document.querySelector("body > div.page > div.container.m_top_30 > div > h5");
    btnSwitch.style.cssText += 'display: inline-block; margin-right: 10px;'
    btnSwitch.style.cssText += 'font-weight: bold;font-size: 16px;'
    h5Element.style.cssText += 'display: inline-block; margin-right: 10px;'
}
function searchGame(){
    // 使用 window.open 方法打开链接
    var url = this.getAttribute('data-url');// 链接动态变化,添加格外属性,每次点击重新获取
    window.open(url, '_blank'); // '_blank' 表示在一个新的浏览器标签页中打开
}
function pickUpGame(event){
    // 使用 window.open 方法打开链接
    var url = this.getAttribute('data-url');// 链接动态变化,添加格外属性,每次点击重新获取
    window.open(url, '_blank'); // '_blank' 表示在一个新的浏览器标签页中打开
}
function getTotalPages(input, eachPage) {
    // 将字符串转换为整数,如果是数字类型则直接使用
    let num = typeof input === 'string' ? parseInt(input, 10) : input;
    let eachPage01 = typeof eachPage === 'string' ? parseInt(eachPage, 10) : eachPage;

    // 检查转换后的值是否为 NaN 或者小于等于 0
    if (isNaN(num) || num <= 0 || isNaN(eachPage01) || eachPage01 <= 0) {
      throw new Error('getTotalPages error:Invalid input: input must be a positive integer.');
    }

    // 计算总页数
    // Math.ceil() 用于向上取整
    const totalPages = Math.ceil(num / eachPage01);

    return totalPages;
}
function getAllItemDetails(pageDocument, page){
    let itemList = pageDocument.querySelectorAll('tbody > tr');
    itemList.forEach(item => {
        let itemId = item.id;
        let itemImg = item.querySelector('img').src;
        let itemName = item.querySelector('a').textContent;
        let itemPrice = item.querySelector('span').textContent;
        let itemOrderNo = item.querySelector('td:nth-child(3) > p:nth-child(1)').textContent;
        let itemOrderTime = item.querySelector('td:nth-child(3) > p:nth-child(2)').textContent;
        let itemPickup = item.querySelector('td:nth-child(4) > p > a').href;
        let itemSearch = item.querySelector('td:nth-child(2) > p:nth-child(1) > b > a').href;
        // 创建一个新的 product 对象
        const product = {
            id: itemId, // 假设这是一个生成唯一 ID 的方法
            name: itemName,
            imageUrl: itemImg,
            price: itemPrice,
            orderNo: itemOrderNo,
            orderTime: itemOrderTime,
            pickUp: itemPickup,
            searchGame: itemSearch
        };
        catalog.addProductToPage(page - 1, product);
    })
}
function displaySuggestions(input, page) {
    const suggestions = catalog.searchProductsByPartialName(input).map(product => product.name);

    let suggestionsList = document.getElementById('input_suggestion');
    suggestionsList.innerHTML = ''; // 清空现有建议

    // 根据输入过滤建议
    let filteredSuggestions = suggestions;

    const itemsPerPage = 7; // 每页显示的建议数
    var totalPages = Math.ceil(filteredSuggestions.length / itemsPerPage);
    const start = (page - 1) * itemsPerPage;
    const end = Math.min(start + itemsPerPage, filteredSuggestions.length);

    // 显示过滤后的建议
    if (filteredSuggestions.length > 0 && filteredSuggestions.length >= start) {
        suggestionsList.style.display = 'block';
        // 获取输入框元素
        const iptName = document.getElementById('iptName');
        
        // 显示当前页的建议
        for (let i = start; i < end; i++) {
            let item = document.createElement('li');
            item.textContent = filteredSuggestions[i];

            if(item._clickHandler){
                item.removeEventListener('click', handler);
                delete item._clickHandler; // 清理保存的引用
            }
            // 创建一个通用的事件处理函数,并保存其引用
            const handler = () => handleSuggestionClick(item);
            item._clickHandler = handler; // 保存函数引用
            item.addEventListener('click', handler);
            suggestionsList.appendChild(item);
        }
    } else {
        suggestionsList.style.display = 'none'; // 如果没有建议则隐藏列表
    }

    // 更新分页图标的状态
    updatePaginationIcons(page, totalPages);
}

function updatePaginationIcons(currentPage, totalPages) {
    const prevPageIcon = document.getElementById('prePageSpan');
    const nextPageIcon = document.getElementById('nextPageSpan');

    if(currentPage === -1 && totalPages === -1){
        prevPageIcon.style.display = 'none';
        nextPageIcon.style.display = 'none';
        return;
    }

    prevPageIcon.style.display = currentPage === 1 ? 'none' : 'inline-block';
    nextPageIcon.style.display = currentPage === totalPages ? 'none' : 'inline-block';
    if(prevPageIcon._clickHandler){
        prevPageIcon.removeEventListener('click', prevPageIcon._clickHandler);
        delete prevPageIcon._clickHandler;
    }
    if(nextPageIcon._clickHandler){
        nextPageIcon.removeEventListener('click', nextPageIcon._clickHandler);
        delete nextPageIcon._clickHandler;
    }
    const preHandler = () => displaySuggestions(document.getElementById('iptName').value, currentPage - 1);
    const nextHandler = () => displaySuggestions(document.getElementById('iptName').value, currentPage + 1);
    prevPageIcon._clickHandler = preHandler;
    nextPageIcon._clickHandler = nextHandler;
    prevPageIcon.addEventListener('click', preHandler);
    nextPageIcon.addEventListener('click', nextHandler);
}
function GMaddStyle(css){
    var myStyle = document.createElement('style');
    myStyle.textContent = css;
    var doc = document.head || document.documentElement;
    doc.appendChild(myStyle);
}
function handleSuggestionClick(suggestion) {
    const iptName = document.getElementById('iptName');
    iptName.value = suggestion.textContent;

    pickUpGameHref = catalog.findProductByName(suggestion.textContent).pickUp;
    searchGameHref = catalog.findProductByName(suggestion.textContent).searchGame;

    document.querySelector('#pickUpGame').setAttribute('data-url', pickUpGameHref);
    document.querySelector('#searchGame').setAttribute('data-url', searchGameHref);

    // 防止点击建议后再点击分页卡死
    const prevPageIcon = document.getElementById('prePageSpan');
    const nextPageIcon = document.getElementById('nextPageSpan');
    prevPageIcon.style.display = 'none';
    nextPageIcon.style.display = 'none';

    const suggestionsList = document.getElementById('input_suggestion');
    suggestionsList.style.display = 'none'; // 关闭建议列表
}