// ==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);
})();