大健云仓自动导入SHOPIFY产品油猴脚本

// ==UserScript==
// @name         GIGAB2B自动批量导入(带倒计时显示+日志页面+失败快速重试)
// @namespace    http://tampermonkey.net/
// @version      1.9
// @description  GIGAB2B自动化批量导入,成功后按自定义间隔,失败后2分钟重试,并记录日志,可独立查看日志页面
// @author       你的名字
// @match        https://shop.gigab2b.com/import-list
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    let intervalId = null;
    let countdownId = null;
    let statusDiv = null;
    let countdownSeconds = 0;
    let running = false;
    let intervalMinutes = 5;
    let startBtn, stopBtn;
    let lastImportSuccess = true; // 记录上一次是否成功

    // 日志存储到 localStorage
    function saveLog(result, detail) {
        const logs = JSON.parse(localStorage.getItem('gigab2b_import_logs') || '[]');
        logs.unshift({
            time: new Date().toLocaleString(),
            result,
            detail
        });
        if (logs.length > 100) logs.length = 100;
        localStorage.setItem('gigab2b_import_logs', JSON.stringify(logs));
    }

    // 日志页面弹窗
    function showLogPage() {
        const logs = JSON.parse(localStorage.getItem('gigab2b_import_logs') || '[]');
        let html = `
            <html>
            <head>
                <title>GIGAB2B 导入日志</title>
                <style>
                    body { font-family: monospace; background: #f9f9f9; margin: 0; padding: 20px; }
                    table { border-collapse: collapse; width: 100%; }
                    th, td { border: 1px solid #ccc; padding: 8px; }
                    th { background: #eee; }
                    tr.success td { color: green; }
                    tr.fail td { color: red; }
                    .clear-btn { margin: 10px 0; padding: 5px 10px; }
                </style>
            </head>
            <body>
                <h2>GIGAB2B 导入日志</h2>
                <button class="clear-btn" onclick="localStorage.removeItem('gigab2b_import_logs');location.reload();">清空日志</button>
                <table>
                    <tr><th>时间</th><th>结果</th><th>详情</th></tr>
                    ${logs.map(log => `
                        <tr class="${log.result === '成功' ? 'success' : 'fail'}">
                            <td>${log.time}</td>
                            <td>${log.result}</td>
                            <td>${log.detail}</td>
                        </tr>
                    `).join('')}
                </table>
            </body>
            </html>
        `;
        const win = window.open('', '_blank', 'width=700,height=500');
        win.document.write(html);
        win.document.close();
    }

    // 日志和状态显示
    function log(msg) {
        if (statusDiv) statusDiv.textContent = '状态:' + msg;
        console.log(msg);
    }

    function waitForSelector(selector, timeout = 10000) {
        return new Promise((resolve, reject) => {
            const interval = 100;
            let elapsed = 0;
            const timer = setInterval(() => {
                const el = document.querySelector(selector);
                if (el) {
                    clearInterval(timer);
                    resolve(el);
                }
                elapsed += interval;
                if (elapsed >= timeout) {
                    clearInterval(timer);
                    reject('元素未出现: ' + selector);
                }
            }, interval);
        });
    }

    async function getCurrentPage() {
        try {
            await waitForSelector('.el-pager');
            const activePage = document.querySelector('.el-pager li.number.active');
            if (activePage) {
                return parseInt(activePage.textContent.trim());
            }
            return 1;
        } catch (e) {
            log('获取当前页码失败:' + e);
            return 1;
        }
    }

    async function hasNextPage() {
        try {
            const pageElements = document.querySelectorAll('.el-pager li.number');
            return pageElements.length > 1;
        } catch (e) {
            log('检查是否有下一页失败:' + e);
            return false;
        }
    }

    async function clickPage(pageNumber) {
        try {
            await waitForSelector('.el-pager');
            await new Promise(r => setTimeout(r, 2000));
            const pageElements = document.querySelectorAll('.el-pager li.number');
            for (let element of pageElements) {
                if (element.textContent.trim() === pageNumber.toString()) {
                    element.click();
                    log(`已点击第 ${pageNumber} 页`);
                    await new Promise(r => setTimeout(r, 5000));
                    const activePage = document.querySelector('.el-pager li.number.active');
                    if (activePage && activePage.textContent.trim() === pageNumber.toString()) {
                        await waitForSelector('.opbar-container__checkall input[type="checkbox"]');
                        log(`第 ${pageNumber} 页加载完成`);
                        return true;
                    } else {
                        log(`第 ${pageNumber} 页切换失败`);
                        return false;
                    }
                }
            }
            log(`未找到第 ${pageNumber} 页`);
            return false;
        } catch (e) {
            log('翻页操作失败:' + e);
            return false;
        }
    }

    async function clickSelectAll() {
        const selectAllInput = await waitForSelector('.opbar-container__checkall input[type="checkbox"]');
        if (!selectAllInput.checked) {
            selectAllInput.click();
            log('已勾选 Select All');
            await new Promise(r => setTimeout(r, 2000));
        }
    }

    async function clickImportButton() {
        const importBtn = await waitForSelector('.el-button--primary.sh-button.ml-12');
        importBtn.click();
        log('已点击 Import 按钮');
        await new Promise(r => setTimeout(r, 2000));
    }

    async function checkAboen() {
        await waitForSelector('.el-dialog__footer');
        const labels = document.querySelectorAll('.el-checkbox-group .el-checkbox');
        for (let label of labels) {
            if (label.innerText.trim().includes('aboen')) {
                const input = label.querySelector('input[type="checkbox"]');
                if (!input.checked) {
                    input.click();
                    log('已勾选 aboen');
                    await new Promise(r => setTimeout(r, 2000));
                }
                break;
            }
        }
    }

    // 记录导入成功/失败日志
    async function clickDialogImport() {
        await waitForSelector('.el-dialog__footer');
        let success = false;
        for (let i = 0; i < 10; i++) {
            const dialogs = document.querySelectorAll('.el-dialog');
            for (let dialog of dialogs) {
                if (dialog.offsetParent === null) continue;
                const btns = dialog.querySelectorAll('button.el-button--primary.sh-button');
                for (let btn of btns) {
                    if (btn.innerText && btn.innerText.trim().toLowerCase().includes('import')) {
                        btn.scrollIntoView({behavior: "auto", block: "center"});
                        btn.focus();
                        btn.click();
                        log('已点击弹窗 Import 按钮');
                        success = true;
                        await new Promise(r => setTimeout(r, 2000));
                        // 检查成功弹窗
                        const successNotification = document.querySelector('.el-notification.sh-white-notification .el-notification__title');
                        if (successNotification && successNotification.textContent.includes('Request for batch import has been submitted')) {
                            log('导入任务提交成功');
                            saveLog('成功', '导入任务提交成功');
                            return true;
                        }
                        // 检查失败弹窗
                        const errorMessage = document.querySelector('.el-message--error .el-message__content');
                        if (errorMessage) {
                            log('导入任务提交失败:' + errorMessage.textContent);
                            saveLog('失败', errorMessage.textContent);
                            return false;
                        }
                        break;
                    }
                }
                if (success) break;
            }
            if (success) break;
            await new Promise(r => setTimeout(r, 1000));
        }
        if (!success) {
            log('未找到或未能点击弹窗 Import 按钮');
            saveLog('失败', '未找到或未能点击弹窗 Import 按钮');
            return false;
        }
        return false;
    }

    async function main() {
    try {
        log('流程开始');
        await new Promise(r => setTimeout(r, 3000));

        // 判断页数
        const hasNext = await hasNextPage();

        if (hasNext) {
            // 有第二页,始终切到第二页
            await clickPage(2);
        } // 否则就在第一页

        await clickSelectAll();
        await clickImportButton();
        await checkAboen();
        const importResult = await clickDialogImport();

        lastImportSuccess = !!importResult;

        if (importResult) {
            log('导入任务成功提交,等待处理完成');
        } else {
            log('导入任务提交失败,将在2分钟后重试');
        }

        await new Promise(r => setTimeout(r, 3000));

        // 再次判断是否还有第二页
        const hasNextAgain = await hasNextPage();
        let nextInterval = importResult ? intervalMinutes * 60 : 120; // 成功用自定义,失败2分钟

        if (hasNextAgain) {
            log(`本轮流程完成,下一轮倒计时开始:${nextInterval}秒`);
            startCountdown(nextInterval, 2); // 始终切到第二页
        } else {
            log('所有操作已完成,脚本停止');
            running = false;
            if (countdownId) clearInterval(countdownId);
            if (statusDiv) statusDiv.textContent = '状态:所有操作已完成';
            if (startBtn) startBtn.disabled = false;
            if (stopBtn) stopBtn.disabled = true;
        }
    } catch (e) {
        log('自动化流程出错:' + e);
        saveLog('失败', '自动化流程出错:' + e);
        startCountdown(120, 1); // 出错也2分钟重试
    }
}

function startCountdown(seconds, nextPage) {
    const targetTime = Date.now() + seconds * 1000;
    if (countdownId) clearInterval(countdownId);
    countdownId = setInterval(() => {
        if (!running) {
            clearInterval(countdownId);
            return;
        }
        const now = Date.now();
        countdownSeconds = Math.max(0, Math.round((targetTime - now) / 1000));
        if (statusDiv) statusDiv.textContent = '状态:本轮流程完成,下一轮倒计时开始:' + countdownSeconds + '秒';
        if (countdownSeconds <= 0) {
            clearInterval(countdownId);
            if (running) {
                main(); // 直接进入 main,由 main 决定是否切页
            }
        }
    }, 1000);
}

    function createControlPanel() {
        const panel = document.createElement('div');
        panel.style.position = 'fixed';
        panel.style.right = '30px';
        panel.style.bottom = '30px';
        panel.style.background = 'white';
        panel.style.border = '1px solid #ccc';
        panel.style.padding = '16px';
        panel.style.zIndex = 99999;
        panel.style.boxShadow = '0 2px 8px rgba(0,0,0,0.15)';
        panel.style.borderRadius = '8px';

        const label = document.createElement('label');
        label.textContent = '循环间隔(分钟): ';
        const input = document.createElement('input');
        input.type = 'number';
        input.value = 5;
        input.min = 1;
        input.style.width = '50px';
        label.appendChild(input);

        startBtn = document.createElement('button');
        startBtn.textContent = '开始';
        startBtn.style.marginLeft = '10px';

        stopBtn = document.createElement('button');
        stopBtn.textContent = '停止';
        stopBtn.style.marginLeft = '10px';
        stopBtn.disabled = true;

        const logBtn = document.createElement('button');
        logBtn.textContent = '查看日志';
        logBtn.style.marginLeft = '10px';
        logBtn.onclick = showLogPage;

        statusDiv = document.createElement('div');
        statusDiv.textContent = '状态:等待开始';
        statusDiv.style.marginTop = '10px';
        statusDiv.style.fontSize = '14px';

        panel.appendChild(label);
        panel.appendChild(startBtn);
        panel.appendChild(stopBtn);
        panel.appendChild(logBtn);
        panel.appendChild(statusDiv);
        document.body.appendChild(panel);

        startBtn.onclick = () => {
            intervalMinutes = parseInt(input.value, 10);
            if (isNaN(intervalMinutes) || intervalMinutes < 1) {
                alert('请输入有效的循环间隔(最小1分钟)');
                return;
            }
            startBtn.disabled = true;
            stopBtn.disabled = false;
            running = true;
            log('已启动循环,每' + intervalMinutes + '分钟执行一次');
            main();
        };

        stopBtn.onclick = () => {
            running = false;
            if (intervalId) clearInterval(intervalId);
            if (countdownId) clearInterval(countdownId);
            intervalId = null;
            countdownId = null;
            startBtn.disabled = false;
            stopBtn.disabled = true;
            log('已停止循环');
        };
    }

    setTimeout(() => {
        createControlPanel();
    }, 2000);

})();

By 行政