JD内容删除助手

发表于 视频

// ==UserScript==
// @name         JD内容列表批量删除助手-修复删除按钮
// @namespace    http://tampermonkey.net/
// @version      1.7
// @match        https://dr.jd.com/n/content-list.html*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    let running = false;
    let stopFlag = false;
    let doneCount = 0;

    function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    function setStatus(text) {
        const status = document.getElementById('jd-click-status');
        if (status) status.innerText = text;
        console.log('[JD删除助手]', text);
    }

    function realClick(el) {
        if (!el) return false;

        el.scrollIntoView({ block: 'center', inline: 'center' });

        const rect = el.getBoundingClientRect();
        const x = rect.left + rect.width / 2;
        const y = rect.top + rect.height / 2;

        ['mouseover', 'mousedown', 'mouseup', 'click'].forEach(type => {
            el.dispatchEvent(new MouseEvent(type, {
                bubbles: true,
                cancelable: true,
                view: window,
                clientX: x,
                clientY: y
            }));
        });

        return true;
    }

    async function waitFor(getter, timeout = 15000) {
        const start = Date.now();

        while (Date.now() - start < timeout) {
            const el = getter();
            if (el) return el;
            await sleep(300);
        }

        return null;
    }

    function findDeleteButton() {
        const items = [...document.querySelectorAll('span[class*="_manageAction_"]')];

        return items.find(el => el.textContent.trim() === '删除');
    }

    function findConfirmButton() {
        const popovers = [...document.querySelectorAll('.jd-popover')];

        for (const popover of popovers) {
            if (!popover.innerText.includes('确定删除吗')) continue;

            const buttons = [...popover.querySelectorAll('button')];

            const confirmBtn = buttons.find(btn =>
                btn.textContent.trim() === '确定' ||
                btn.classList.contains('jd-btn-primary')
            );

            if (confirmBtn) return confirmBtn;
        }

        return [...document.querySelectorAll('button')].find(btn =>
            btn.textContent.trim() === '确定' &&
            btn.className.includes('jd-btn-primary')
        );
    }

    function getConfig() {
        let times = parseInt(document.getElementById('jd-delete-times')?.value, 10);
        let interval = parseInt(document.getElementById('jd-delete-interval')?.value, 10);

        if (!times || times < 1) times = 1;
        if (!interval || interval < 1000) interval = 2000;

        return { times, interval };
    }

    async function deleteOnce(index, total) {
        setStatus(`第 ${index}/${total} 次:查找删除按钮`);

        const deleteBtn = await waitFor(findDeleteButton, 15000);

        if (!deleteBtn) {
            setStatus('删除按钮没找到');
            return false;
        }

        realClick(deleteBtn);
        setStatus(`第 ${index}/${total} 次:已点击删除`);

        await sleep(500);

        const confirmBtn = await waitFor(findConfirmButton, 15000);

        if (!confirmBtn) {
            setStatus('确定按钮没找到');
            return false;
        }

        realClick(confirmBtn);
        doneCount++;

        setStatus(`第 ${index}/${total} 次:已点击确定`);

        await sleep(800);

        return true;
    }

    async function startLoop() {
        if (running) {
            setStatus('正在执行中');
            return;
        }

        const { times, interval } = getConfig();

        running = true;
        stopFlag = false;
        doneCount = 0;

        setStatus(`开始执行:${times} 次,间隔 ${interval}ms`);

        for (let i = 1; i <= times; i++) {
            if (stopFlag) {
                setStatus(`已停止,完成 ${doneCount} 次`);
                break;
            }

            const ok = await deleteOnce(i, times);

            if (!ok) {
                setStatus(`第 ${i} 次失败,停止执行`);
                break;
            }

            if (i < times) {
                setStatus(`等待 ${interval}ms 后继续`);
                await sleep(interval);
            }
        }

        running = false;

        if (!stopFlag) {
            setStatus(`执行结束,共完成 ${doneCount} 次`);
        }
    }

    async function runOnce() {
        if (running) {
            setStatus('正在执行中');
            return;
        }

        running = true;
        stopFlag = false;
        doneCount = 0;

        await deleteOnce(1, 1);

        running = false;
        setStatus(`单次执行完成:${doneCount} 次`);
    }

    function stopRun() {
        stopFlag = true;
        setStatus('正在停止,当前操作结束后停止');
    }

    function createUI() {
        if (document.getElementById('jd-click-panel')) return;

        const panel = document.createElement('div');
        panel.id = 'jd-click-panel';
        panel.style.cssText = `
            position: fixed;
            right: 20px;
            bottom: 80px;
            z-index: 999999999;
            width: 210px;
            background: #fff;
            border: 1px solid #ddd;
            border-radius: 10px;
            padding: 10px;
            box-shadow: 0 4px 15px rgba(0,0,0,.25);
            font-size: 14px;
        `;

        panel.innerHTML = `
            <div style="font-weight:bold;text-align:center;margin-bottom:8px;">
                JD批量删除助手
            </div>

            <div style="font-size:12px;">执行次数</div>
            <input id="jd-delete-times" type="number" value="5" min="1"
                style="width:100%;box-sizing:border-box;margin:4px 0 8px;padding:6px;border:1px solid #ccc;border-radius:5px;">

            <div style="font-size:12px;">删除间隔 / 毫秒</div>
            <input id="jd-delete-interval" type="number" value="2000" min="1000"
                style="width:100%;box-sizing:border-box;margin:4px 0 8px;padding:6px;border:1px solid #ccc;border-radius:5px;">

            <button id="jd-start-btn" style="width:100%;padding:8px;margin-bottom:6px;background:#e1251b;color:#fff;border:none;border-radius:6px;cursor:pointer;">
                开始循环删除
            </button>

            <button id="jd-once-btn" style="width:100%;padding:8px;margin-bottom:6px;background:#333;color:#fff;border:none;border-radius:6px;cursor:pointer;">
                执行一次
            </button>

            <button id="jd-stop-btn" style="width:100%;padding:8px;background:#999;color:#fff;border:none;border-radius:6px;cursor:pointer;">
                停止
            </button>

            <div id="jd-click-status" style="margin-top:8px;font-size:12px;color:#666;text-align:center;">
                等待执行
            </div>
        `;

        document.body.appendChild(panel);

        document.getElementById('jd-start-btn').onclick = startLoop;
        document.getElementById('jd-once-btn').onclick = runOnce;
        document.getElementById('jd-stop-btn').onclick = stopRun;
    }

    const initTimer = setInterval(() => {
        if (document.body) {
            createUI();
            clearInterval(initTimer);
        }
    }, 500);

})();


发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。