// ==UserScript== // @name SHEIN工具合集 // @namespace http://tampermonkey.net/ // @version 6.0 // @description 资质失败重绑定 + SKC批量上传实拍图 + 批量同意议价 + 商品榜单SKC自动注入 + 批量上传运单 // @author 工具脚本 // @match https://sso.geiwohuo.com/* // @grant none // @require https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js // @run-at document-start // @license MIT // ==/UserScript== (function () { 'use strict'; // ====================== 本地存储:监控开关状态 ====================== const MONITOR_KEY = 'shein_rank_monitor_enabled'; function getMonitorStatus() { return localStorage.getItem(MONITOR_KEY) === 'true'; } function setMonitorStatus(enabled) { localStorage.setItem(MONITOR_KEY, enabled); } // ====================== 全局悬浮球 ====================== function createGlobalFloatBall() { const old = document.getElementById('global-float-ball'); if (old) old.remove(); const wrap = document.createElement('div'); wrap.id = 'global-float-ball'; wrap.style.cssText = ` position: fixed; top: 20px; left: 20px; z-index: 999999; width: 48px; height: 48px; `; const ball = document.createElement('div'); ball.innerText = 'SK'; ball.style.cssText = ` width: 48px; height: 48px; border-radius: 50%; background: #1677ff; color: #fff; display: flex; align-items: center; justify-content: center; font-size: 16px; font-weight: bold; cursor: pointer; box-shadow: 0 2px 10px rgba(0,0,0,0.2); `; const menuPanel = document.createElement('div'); menuPanel.id = 'global-menu'; menuPanel.style.cssText = ` display: none; position: absolute; top: 0; left: 58px; width: 220px; background: #fff; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.15); padding: 12px; `; // 读取监控状态,设置按钮文字和颜色 const monitorEnabled = getMonitorStatus(); const monitorBtnBg = monitorEnabled ? '#52c41a' : '#999'; const monitorBtnText = monitorEnabled ? '✅ 榜单SKC监控(开)' : '⏸️ 榜单SKC监控(关)'; menuPanel.innerHTML = ` `; wrap.appendChild(ball); wrap.appendChild(menuPanel); document.body.appendChild(wrap); let showMenu = false; ball.onclick = () => { showMenu = !showMenu; menuPanel.style.display = showMenu ? 'block' : 'none'; ball.style.background = showMenu ? '#ff4d4f' : '#1677ff'; if (!showMenu) hideAllToolPanels(); }; document.getElementById('showTool1').onclick = () => { hideAllToolPanels(); initTool1(); }; document.getElementById('showTool2').onclick = () => { hideAllToolPanels(); initTool2(); }; document.getElementById('showTool3').onclick = () => { hideAllToolPanels(); initTool3(); }; document.getElementById('showTool4').onclick = () => { hideAllToolPanels(); initTool4(); }; document.getElementById('showTool5').onclick = () => { hideAllToolPanels(); initTool5(); }; // 监控开关点击事件 document.getElementById('toggleRankMonitor').onclick = () => { const newStatus = !getMonitorStatus(); setMonitorStatus(newStatus); const btn = document.getElementById('toggleRankMonitor'); if (newStatus) { btn.textContent = '✅ 榜单SKC监控(开)'; btn.style.background = '#52c41a'; console.log("✅ 榜单监控已开启"); } else { btn.textContent = '⏸️ 榜单SKC监控(关)'; btn.style.background = '#999'; document.querySelectorAll('.injected-skc').forEach(el => el.remove()); console.log("⏸️ 榜单监控已关闭,清空SKC"); } }; } function hideAllToolPanels() { const panel1 = document.getElementById('tool1-panel'); const panel2 = document.getElementById('float-skctool'); const panel3 = document.getElementById('bargain-tool-panel'); const panel4 = document.getElementById('upload-shipment-panel'); const panel5 = document.getElementById('auto-reserve-panel'); if (panel1) panel1.remove(); if (panel2) panel2.remove(); if (panel3) panel3.remove(); if (panel4) panel4.remove(); if (panel5) panel5.remove(); } // ====================== 工具1:资质失败重绑定 ====================== function initTool1() { const API = { getFailSkc: 'https://sso.geiwohuo.com/grcp-api-prefix/pces/skc_compliance_item/list?page_size=100&page_num=1', queryBind: 'https://sso.geiwohuo.com/grcp-api-prefix/pces/certificate_pool/query/list?page_num=1&page_size=20', operate: 'https://sso.geiwohuo.com/grcp-api-prefix/pces/certificate_pool/batch/bind/skc' }; const panel = document.createElement('div'); panel.id = 'tool1-panel'; panel.style.cssText = ` position: fixed; top: 20px; left: 300px; z-index: 999998; background: #fff; padding: 16px; border-radius: 8px; box-shadow: 0 2px 12px rgba(0,0,0,0.15); width: 260px; font-family: system-ui, -apple-system, sans-serif; `; panel.innerHTML = `
=== 工具加载成功 ===\n点击按钮开始全自动重新绑定\n
`; document.body.appendChild(panel); // ✅ 修复:工具1独立 log,不使用 store function log(text) { const el = document.getElementById('logBox'); el.textContent = `[${new Date().toLocaleTimeString()}] ${text}\n` + el.textContent; } const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); async function getFailSkcList() { const includeReview4 = document.getElementById('includeReview4').checked; log('🔍 正在拉取资质失败的SKC...'); try { const res = await fetch(API.getFailSkc, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ "optional_aggregation_status_list": [0, 3, 5], "optional_shelf_status_list": [0, 1, 2, 3], "shelf_status_list": [0, 1, 2, 3], "target_is_required_or_required": 1, "verify_status_list": [], }) }); const data = await res.json(); if (data.code !== '0') throw new Error(data.msg || '拉取失败'); const skcList = (data.info?.data || []) .filter(item => includeReview4 ? true : item.review_state !== 4) .map(item => item.skc) .filter(Boolean); if (skcList.length === 0) { log('✅ 未查询到符合条件的SKC'); return []; } log(`✅ 拉取成功,共 ${skcList.length} 个待处理SKC`); return skcList; } catch (err) { log(`❌ 拉取SKC失败:${err.message}`); return []; } } async function queryBind(skc) { log(`\n[${skc}] 查询绑定信息...`); try { const res = await fetch(API.queryBind, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ certificate_type_code_list: ['childgarment'], spu_name_list: [], pool_sn_list: [], skc_name_list: [skc], sort_field: 'bind_skc_flag_status_last_update_time', sort_type: 'desc' }) }); const data = await res.json(); const bindList = (data.info?.data || []).filter(item => item.bind_flag === 1); if (bindList.length === 0) { log(`[${skc}] ⚠️ 未查询到绑定资质`); return null; } log(`[${skc}] ✅ 获取pool_sn:${bindList[0].pool_sn}`); return bindList[0].pool_sn; } catch (err) { log(`[${skc}] ❌ 查询失败:${err.message}`); return null; } } async function doUnbind(poolSn, skc) { log(`[${skc}] 执行解绑...`); try { const res = await fetch(API.operate, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ bind_certificate_pool_req_list: [{ pool_sn: poolSn, un_bind_skc_name_list: [skc], un_bind_spu_name_list: [] }] }) }); const data = await res.json(); if (data.code === '0') log(`[${skc}] ✅ 解绑成功`); else log(`[${skc}] ❌ 解绑失败:${data.msg}`); } catch (err) { log(`[${skc}] ❌ 解绑异常:${err.message}`); } } async function doBind(poolSn, skc) { log(`[${skc}] 执行绑定...`); try { const res = await fetch(API.operate, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ bind_certificate_pool_req_list: [{ pool_sn: poolSn, bind_skc_name_list: [skc], bind_spu_name_list: [] }] }) }); const data = await res.json(); if (data.code === '0') log(`[${skc}] ✅ 绑定成功`); else log(`[${skc}] ❌ 绑定失败:${data.msg}`); } catch (err) { log(`[${skc}] ❌ 绑定异常:${err.message}`); } } async function processSingle(skc) { const poolSn = await queryBind(skc); if (!poolSn) return; await doUnbind(poolSn, skc); await delay(1000); await doBind(poolSn, skc); } async function startAutoFix() { const btn = document.getElementById('autoFixBtn'); btn.disabled = true; btn.textContent = '⚙️ 执行中...请勿重复点击'; document.getElementById('logBox').textContent = ''; const skcList = await getFailSkcList(); if (skcList.length === 0) { btn.disabled = false; btn.textContent = '一键执行:自动绑定失败资质'; return; } log(`\n=== 开始批量绑定,共 ${skcList.length} 个SKC ==`); for (let i = 0; i < skcList.length; i++) { log(`\n==============================================`); log(`处理第 ${i + 1}/${skcList.length} 个:${skcList[i]}`); await processSingle(skcList[i]); } btn.disabled = false; btn.textContent = '一键执行:自动绑定失败资质'; log(`\n==============================================`); log(`全部绑定任务执行完成!`); } document.getElementById('autoFixBtn').onclick = startAutoFix; } // ====================== 工具2:XLSX + 文件夹自动上传实拍图(缓存版·不重复上传)+ 手动SKC上传 ====================== function initTool2() { const API = { upload: 'https://sso.geiwohuo.com/grcp-api-prefix/grcp/upload_private_attachment_to_pqms', getSkcInfo: 'https://sso.geiwohuo.com/grcp-api-prefix/grcp/gpc/skc/details/v2', algorithm: 'https://sso.geiwohuo.com/grcp-api-prefix/pces/skc/label/query_algorithm_result', save: 'https://sso.geiwohuo.com/grcp-api-prefix/pces/skc/label/batch_save_spspt' }; const FIXED_LABELS = [ { certificate_type_id: 690, group: 2, industries: [106], label_code: "TAGA0075", label_id: 75 }, { certificate_type_id: 612, group: 2, industries: [106], label_code: "TAGA0009", label_id: 9 }, { certificate_type_id: 613, group: 2, industries: [106], label_code: "TAGA0008", label_id: 8 }, { certificate_type_id: 645, group: 2, industries: [106], label_code: "TAGA0042", label_id: 42 }, { certificate_type_id: 700, group: 1, industries: [106], label_code: "TAGA0080", label_id: 80 }, { certificate_type_id: 701, group: 1, industries: [106], label_code: "TAGA0081", label_id: 81 }, { certificate_type_id: 702, group: 1, industries: [106], label_code: "TAGA0082", label_id: 82 }, { certificate_type_id: 703, group: 1, industries: [106], label_code: "TAGA0083", label_id: 83 } ]; const store = { taskList: [], currentIndex: 0, isRunning: false, isPaused: false, success: 0, fail: 0, fileMap: new Map(), fileCache: new Map(), algCache: new Map(), manualFiles: { g1: [], g2: [] } }; function createUI() { const old = document.getElementById('float-skctool'); if (old) old.remove(); const wrap = document.createElement('div'); wrap.id = 'float-skctool'; wrap.style.cssText = `position:fixed; top:20px; left:300px; z-index:999998;`; const panel = document.createElement('div'); panel.style.cssText = ` display:block; width:480px; background:#fff; border-radius:12px; box-shadow:0 4px 20px rgba(0,0,0,0.15); padding:16px; `; panel.innerHTML = `

XLSX+文件夹自动上传实拍图

选择 “童装实拍图包装图” 这个根文件夹

手动批量上传(单文件夹)

`; wrap.appendChild(panel); document.body.appendChild(wrap); document.getElementById('folder-select').addEventListener('change', handleFolder); document.getElementById('xlsx-file').addEventListener('change', handleXlsx); document.getElementById('start').onclick = startRun; document.getElementById('pause').onclick = () => { store.isPaused = true; updateBtn(); }; document.getElementById('resume').onclick = () => { store.isPaused = false; loop(); }; document.getElementById('clearCache').onclick = () => { store.fileCache.clear(); store.algCache.clear(); log("🧹 已清空图片上传&识别缓存"); }; document.getElementById('manual-folder').addEventListener('change', handleManualFolder); document.getElementById('start-manual').onclick = startManualRun; } function log(text) { const el = document.getElementById('log'); const pre = store.isRunning && store.taskList.length ? `[${store.currentIndex + 1}/${store.taskList.length}] ` : ''; el.textContent = `[${new Date().toLocaleTimeString()}] ${pre}${text}\n` + el.textContent; } function updateBtn() { const $ = s => document.getElementById(s); if (!store.isRunning) { $('start').style.display = 'block'; $('pause').style.display = 'none'; $('resume').style.display = 'none'; } else { $('start').style.display = 'none'; $('pause').style.display = store.isPaused ? 'none' : 'block'; $('resume').style.display = store.isPaused ? 'block' : 'none'; } } function handleFolder(e) { const files = Array.from(e.target.files); store.fileMap.clear(); const map = new Map(); for (const f of files) { const path = f.webkitRelativePath; const parts = path.split('/'); if (parts.length < 2) continue; const folder = parts[parts.length - 2]; const name = f.name.toLowerCase(); if (!map.has(folder)) map.set(folder, { g1: [], g2: [] }); if (name.includes('实体图')) map.get(folder).g1.push(f); if (name.includes('外包装图')) map.get(folder).g2.push(f); } store.fileMap = map; log(`✅ 已加载 ${map.size} 个文件夹的图片`); } function handleXlsx(e) { const file = e.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = e => { const wb = XLSX.read(e.target.result, { type: 'binary' }); const ws = wb.Sheets[wb.SheetNames[0]]; const json = XLSX.utils.sheet_to_json(ws); const tasks = json.map(row => ({ skc: (row['SKC'] || '').toString().trim(), folder: (row['款式文件夹'] || '').toString().trim() })).filter(t => t.skc && t.folder); store.taskList = tasks; log(`✅ 读取表格成功:共 ${tasks.length} 个SKC`); }; reader.readAsBinaryString(file); } function handleManualFolder(e) { const files = Array.from(e.target.files); let g1 = [], g2 = []; for (const f of files) { const name = f.name.toLowerCase(); if (name.includes('实体图')) g1.push(f); if (name.includes('外包装图')) g2.push(f); } store.manualFiles = { g1, g2 }; log(`📁 手动文件夹加载完成:实体图${g1.length}张 | 外包装图${g2.length}张`); } function getImages(folderStr) { if (folderStr === '__MANUAL__') { return store.manualFiles; } const folders = folderStr.split('+').map(s => s.trim()); let g1 = [], g2 = []; for (const f of folders) { const item = store.fileMap.get(f); if (item) { g1 = g1.concat(item.g1); g2 = g2.concat(item.g2); } } return { g1, g2 }; } async function upload(file) { const key = `${file.name}_${file.size}_${file.lastModified}`; if (store.fileCache.has(key)) { return store.fileCache.get(key); } const fd = new FormData(); fd.append('file', file); try { const r = await fetch(API.upload, { method: 'POST', body: fd }); const j = await r.json(); const res = j.code === '0' ? j.info : null; store.fileCache.set(key, res); return res; } catch (e) { store.fileCache.set(key, null); return null; } } async function processOne(task) { const { skc, folder } = task; const { g1: files1, g2: files2 } = getImages(folder); if (files1.length === 0 && files2.length === 0) { log(`❌ 未找到图片`); return false; } const g1 = [], g2 = []; for (const f of files1) { const u = await upload(f); if (u) g1.push(u); await new Promise(r => setTimeout(r, 50)); } for (const f of files2) { const u = await upload(f); if (u) g2.push(u); await new Promise(r => setTimeout(r, 50)); } log(`开始处理:${skc} 实体图${g1.length}张 | 外包装图${g2.length}张`); const info = await getSkcInfo(skc); if (!info) return false; const urls1 = g1.map(x => x.url).sort().join('|'); const urls2 = g2.map(x => x.url).sort().join('|'); const algKey = `${skc}_${urls1}_${urls2}`; let alg = null; if (store.algCache.has(algKey)) { alg = store.algCache.get(algKey); log("⚡ 使用缓存识别结果,跳过算法请求"); } else { alg = await algSubmit(skc, info.product_name, g1, g2); if (alg) store.algCache.set(algKey, alg); } if (!alg) return false; const ok = await save(skc, g1, g2, alg); if (ok) store.success++; else store.fail++; return ok; } async function getSkcInfo(skc) { try { const r = await fetch(API.getSkcInfo, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query_list: [{ skc }] }) }); const j = await r.json(); return j.code === '0' ? j.info[0] : null; } catch (e) { log('❌ 获取SKC信息失败'); return null; } } async function algSubmit(skc, pname, g1, g2) { const imgs = [ ...g1.map(x => ({ group: 1, url: x.url })), ...g2.map(x => ({ group: 2, url: x.url })) ]; try { const r = await fetch(API.algorithm, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ label_images: imgs, labels: FIXED_LABELS, product_name: pname, skc, task_id: crypto.randomUUID() }) }); const j = await r.json(); return j.code === '0' ? j.info.data[0] : null; } catch (e) { log('❌ 识别失败'); return null; } } async function save(skc, g1, g2, alg) { const body = g1.map(x => ({ file_url: x.url, file_md5: x.md5, file_name: x.name || 'image.png', upload_dimension: 0, part_code: "" })); const pkg = g2.map(x => ({ file_name: x.name || 'package.png', file_url: x.url, signed_file_url: x.url, file_md5: x.md5, file_group: 2, lang: 'en', source_type: 0, upload_dimension: 0, part_code: "" })); try { const r = await fetch(API.save, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ algorithm_raw_result: alg.algorithm_raw_result, body_file_list: body, package_file_list: pkg, paper_file_list: [], response_duration_ms: alg.response_duration_ms || 200, response_status: alg.response_status || 0, skc_list: [skc] }) }); const j = await r.json(); const ok = j.code === '0'; log(ok ? '✅ 上传保存成功' : '❌ 保存失败'); return ok; } catch (e) { log('❌ 保存异常'); return false; } } async function loop() { if (!store.isRunning || store.isPaused) return; if (store.currentIndex >= store.taskList.length) { store.isRunning = false; updateBtn(); log(`全部完成 | 总数:${store.taskList.length} | 成功:${store.success} | 失败:${store.fail}`); return; } const task = store.taskList[store.currentIndex]; await processOne(task); store.currentIndex++; setTimeout(loop, 600); } function startRun() { if (store.fileMap.size === 0) return log('请先选择图片文件夹'); if (store.taskList.length === 0) return log('请先选择XLSX表格'); store.currentIndex = 0; store.success = 0; store.fail = 0; store.isRunning = true; store.isPaused = false; updateBtn(); loop(); } async function startManualRun() { const g1 = store.manualFiles.g1; const g2 = store.manualFiles.g2; if (g1.length === 0 && g2.length === 0) { return log('❌ 请先选择【单个款式文件夹】'); } const skcText = document.getElementById('manual-skc').value.trim(); if (!skcText) { return log('❌ 请输入SKC(一行一个)'); } const skcList = skcText.split('\n') .map(line => line.trim()) .filter(line => line.length > 0); if (skcList.length === 0) return log('❌ 未识别到有效SKC'); log(`====================================`); log(`📌 手动任务开始:共 ${skcList.length} 个SKC`); store.success = 0; store.fail = 0; for (let i = 0; i < skcList.length; i++) { const skc = skcList[i]; log(`\n【手动任务 ${i + 1}/${skcList.length}】${skc}`); await processOne({ skc, folder: '__MANUAL__' }); await new Promise(r => setTimeout(r, 600)); } log(`\n====================================`); log(`✅ 手动任务全部完成!成功:${store.success} | 失败:${store.fail}`); } createUI(); } // ====================== 工具3:批量同意/拒绝议价 ====================== async function initTool3() { const now = new Date(); const endTime = now.getFullYear() + "-" + String(now.getMonth() + 1).padStart(2, '0') + "-" + String(now.getDate()).padStart(2, '0') + " 23:59:59"; let startMonth = now.getMonth() - 2; let startYear = now.getFullYear(); if (startMonth < 0) { startMonth += 12; startYear -= 1; } const startTime = startYear + "-" + String(startMonth + 1).padStart(2, '0') + "-" + String(now.getDate()).padStart(2, '0') + " 00:00:00"; const API_QUERY = 'https://sso.geiwohuo.com/dpas-api-prefix/dpas/discuss/bargain_page?page_num=1&page_size=100'; const API_AGREE = 'https://sso.geiwohuo.com/dpas-api-prefix/dpas/dpas/nonClothReceiptApi/batch_re_quote'; const QUERY_BODY = { bargain_status: 1, start_time: startTime, end_time: endTime }; async function request(url, opt) { const def = { headers: { 'Content-Type': 'application/json', Cookie: document.cookie }, credentials: 'include', ...opt }; const r = await fetch(url, def); return await r.json(); } const panel = document.createElement('div'); panel.id = 'bargain-tool-panel'; panel.style.cssText = `position:fixed;top:20px;left:300px;z-index:999998;background:#fff;padding:20px;border-radius:8px;box-shadow:0 2px 12px rgba(0,0,0,0.15);width:800px;font-family:system-ui,-apple-system,sans-serif;`; panel.innerHTML = `
🔍 加载中...
`; document.body.appendChild(panel); const qr = await request(API_QUERY, { method: 'POST', body: JSON.stringify(QUERY_BODY) }); if (qr.code !== '0' || !qr.info?.data?.length) { panel.innerHTML = `
✅ 暂无待处理议价
`; return; } const list = qr.info.data.map((item, i) => ({ index: i, skc: item.skc_name || '', sp: item.sku_cost_prices?.[0]?.cost_price_histories?.[0]?.cost_price || 0, sy: item.sku_cost_prices?.[0]?.suggest_cost_price || 0, bsn: item.bargain_sn, dsn: item.document_sn })); // 界面新增拒绝按钮,红色区分 panel.innerHTML = `

✅ 批量同意/拒绝议价

勾选 SKC 商家价 系统价
`; const tb = document.getElementById('bt'); list.forEach(item => { const tr = document.createElement('tr'); tr.innerHTML = ` ${item.skc} ${item.sp} ${item.sy}`; tb.appendChild(tr); }); const cbs = document.querySelectorAll('.bcb'); document.getElementById('ba').onclick = () => cbs.forEach(c => c.checked = true); document.getElementById('bu').onclick = () => cbs.forEach(c => c.checked = false); document.getElementById('bs').onclick = () => { const v = +document.getElementById('bp').value; if (isNaN(v)) return alert('请输入数字'); cbs.forEach((c, i) => c.checked = list[i].sy > v); }; // -------- 原:同意议价 -------- document.getElementById('bsub').onclick = async () => { if (!confirm('确定【同意】选中的议价?')) return; const sel = Array.from(cbs).map((c, i) => c.checked ? list[i] : null).filter(Boolean); if (sel.length === 0) return alert('未选中任何项'); const btn = document.getElementById('bsub'); btn.disabled = true; btn.textContent = '处理中...'; let ok = 0, ng = 0; for (const item of sel) { try { const r = await request(API_AGREE, { method: 'POST', body: JSON.stringify({ confirm_infos: [{ discuss_audit_type: 1, discuss_sn: item.bsn, document_sn: item.dsn }] }) }); if (r.code === '0') ok++; else ng++; } catch (e) { ng++; } await new Promise(r => setTimeout(r, 300)); } btn.disabled = false; btn.textContent = '✅ 提交同意议价'; alert(`处理完成\n同意成功:${ok}\n失败:${ng}`); }; // -------- 新增:拒绝议价 -------- document.getElementById('bsubRefuse').onclick = async () => { if (!confirm('确定【拒绝】选中的议价?')) return; const sel = Array.from(cbs).map((c, i) => c.checked ? list[i] : null).filter(Boolean); if (sel.length === 0) return alert('未选中任何项'); const btn = document.getElementById('bsubRefuse'); btn.disabled = true; btn.textContent = '拒绝中...'; let ok = 0, ng = 0; for (const item of sel) { try { const r = await request(API_AGREE, { method: 'POST', body: JSON.stringify({ confirm_infos: [{ discuss_audit_type: 2, discuss_sn: item.bsn, document_sn: item.dsn }] }) }); if (r.code === '0') ok++; else ng++; } catch (e) { ng++; } await new Promise(r => setTimeout(r, 300)); } btn.disabled = false; btn.textContent = '❌ 提交拒绝议价'; alert(`处理完成\n拒绝成功:${ok}\n失败:${ng}`); }; } // ====================== 工具4:批量上传运单(整合完成 + 自动获取待上传面单订单) ====================== function initTool4() { const oldPanel = document.getElementById('upload-shipment-panel'); if (oldPanel) oldPanel.remove(); const style = document.createElement('style'); style.innerHTML = ` #upload-shipment-panel { position: fixed; top: 70px; left: 300px; z-index: 999998; width: 380px; background: #fff; border: 1px solid #ccc; border-radius: 10px; padding: 15px; box-shadow: 0 0 8px rgba(0,0,0,0.2); font-family: system-ui; } #upload-shipment-panel select, #upload-shipment-panel input { width:100%; padding:6px; margin:4px 0; box-sizing:border-box; border:1px solid #ccc; border-radius:4px; } .ship-btn { width:100%; padding:9px; border:none; border-radius:6px; cursor:pointer; color:white; margin:6px 0; font-size:14px; } .ship-btn-file { background:#2385ee; } .ship-btn-start { background:#19be6b; } .ship-btn-pause { background:#ff5722; } .ship-btn-resume { background:#ff9800; } .ship-btn-auto { background:#722ED1; } #shipFileInput { display:none; } #shipStatus { font-size:13px; margin-top:10px; color:#333; line-height:1.5; } #shipLogContainer { margin-top:12px; padding:10px; height:220px; overflow-y:auto; background:#f7f7f7; border:1px solid #ddd; border-radius:6px; font-size:12px; line-height:1.4; white-space:pre-wrap; } .ship-log-success { color:#0d7f2d; font-weight:bold; } .ship-log-error { color:#d70000; font-weight:bold; } .ship-log-warn { color:#ff8c00; font-weight:bold; } .ship-log-skip { color:#1750d1; font-weight:bold; } `; document.head.appendChild(style); const panel = document.createElement('div'); panel.id = 'upload-shipment-panel'; panel.innerHTML = `

POD登录

商品ID导入方式

操作

请导入表格或点击自动获取
=== 执行日志 ===\n
`; document.body.appendChild(panel); const POD_SITE_MAP = { uk: { loginApi: 'https://crossdiy.haoyipodeur.com/api/user/login', queryApi: 'https://crossdiy.haoyipodeur.com/api/orders/getlist' }, us: { loginApi: 'https://crossdiy2.haoyipod.com/api/user/login', queryApi: 'https://crossdiy2.haoyipod.com/api/orders/getlist' }, ecofeng: { loginApi: 'https://crossdiy.ecofengpod.com/api/user/login', queryApi: 'https://crossdiy.ecofengpod.com/api/orders/getlist' } }; const CONFIG = { queryApi: POD_SITE_MAP.uk.queryApi, loginApi: POD_SITE_MAP.uk.loginApi, uploadApi: 'https://sso.geiwohuo.com/gsp/order/importSingleMultipleExpress', uploadSite: 'shein-es', delay: 800, queryToken: '', currentPodKey: 'uk' }; let orderList = [], XLSX = window.XLSX; let isPaused = false, currentIndex = 0, success = 0, fail = 0, skip = 0; function addLog(text, type = 'info') { const logEl = document.getElementById('shipLogContainer'); const time = new Date().toLocaleTimeString(); const cls = { success: 'ship-log-success', error: 'ship-log-error', warn: 'ship-log-warn', skip: 'ship-log-skip' }[type] || ''; logEl.innerHTML += `[${time}] ${text}\n`; logEl.scrollTop = logEl.scrollHeight; } // ===================== 本地存储登录信息 ===================== const STORAGE_KEY = 'ship_login_info'; function saveLoginInfo(info) { localStorage.setItem(STORAGE_KEY, JSON.stringify(info)); } function loadLoginInfo() { const d = localStorage.getItem(STORAGE_KEY); return d ? JSON.parse(d) : null; } function autoFillLoginInfo() { const info = loadLoginInfo(); if (info) { document.getElementById('shipMerchant').value = info.merchant || ''; document.getElementById('shipMobile').value = info.mobile || ''; document.getElementById('shipPwd').value = info.password || ''; if (info.podKey && POD_SITE_MAP[info.podKey]) { document.getElementById('podSelect').value = info.podKey; CONFIG.currentPodKey = info.podKey; CONFIG.loginApi = POD_SITE_MAP[info.podKey].loginApi; CONFIG.queryApi = POD_SITE_MAP[info.podKey].queryApi; } } } autoFillLoginInfo(); const podSelect = document.getElementById('podSelect'); podSelect.onchange = () => { const k = podSelect.value; CONFIG.currentPodKey = k; CONFIG.loginApi = POD_SITE_MAP[k].loginApi; CONFIG.queryApi = POD_SITE_MAP[k].queryApi; CONFIG.queryToken = ''; document.getElementById('shipLoginStatus').textContent = '已切换仓库,请重新登录'; document.getElementById('shipLoginStatus').style.color = 'red'; }; // 登录 document.getElementById('shipLogin').onclick = async () => { const mch = document.getElementById('shipMerchant').value.trim(); const mobile = document.getElementById('shipMobile').value.trim(); const pwd = document.getElementById('shipPwd').value.trim(); const st = document.getElementById('shipLoginStatus'); if (!mch || !mobile || !pwd) { st.textContent = '请填写完整信息'; st.style.color = 'red'; return; } st.textContent = '登录中...'; st.style.color = '#007bff'; try { const r = await fetch(CONFIG.loginApi, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ merchant: mch, mobile, password: pwd }) }); const j = await r.json(); if (j.code === 1 && j.data?.userinfo?.token) { CONFIG.queryToken = j.data.userinfo.token; st.textContent = '✅ 登录成功'; st.style.color = 'green'; addLog('POD 登录成功', 'success'); saveLoginInfo({ merchant: mch, mobile, password: pwd, podKey: CONFIG.currentPodKey }); } else { st.textContent = '❌ ' + (j.msg || '登录失败'); st.style.color = 'red'; } } catch (e) { st.textContent = '❌ 异常:' + e.message; st.style.color = 'red'; } }; // ========== Excel导入(保留原有功能) ========== document.getElementById('shipSelectFile').onclick = () => document.getElementById('shipFileInput').click(); document.getElementById('shipFileInput').onchange = async (e) => { if (!e.target.files.length) return; const file = e.target.files[0]; const st = document.getElementById('shipStatus'); st.textContent = '读取中...'; const reader = new FileReader(); reader.readAsArrayBuffer(file); reader.onload = (ev) => { try { const wb = XLSX.read(new Uint8Array(ev.target.result), { type: 'array' }); const json = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]], { header: 1 }); const rows = json.filter(r => r?.length); if (rows.length < 2) throw new Error('表格格式无效'); const header = rows[1].map(h => (h || '').trim()); const orderIdx = header.findIndex(c => c.includes('订单号')); const goodsIdx = header.findIndex(c => c.includes('商品ID')); if (orderIdx === -1 || goodsIdx === -1) throw new Error('未找到订单号/商品ID列'); orderList = rows.slice(2).map(r => ({ orderNo: (r[orderIdx] || '').trim(), goodsId: (r[goodsIdx] || '').trim() })).filter(x => x.orderNo && x.goodsId); if (!orderList.length) { st.textContent = '无有效数据'; return; } st.textContent = `✅ 读取成功:${orderList.length} 条`; addLog(`导入 ${orderList.length} 条`, 'success'); document.getElementById('shipStart').disabled = false; } catch (err) { st.textContent = '❌ ' + err.message; addLog(err.message, 'error'); } }; }; // ========== 【新增】自动获取待上传面单订单 ========== document.getElementById('shipAutoGetOrder').onclick = async () => { const btn = document.getElementById('shipAutoGetOrder'); const st = document.getElementById('shipStatus'); btn.disabled = true; btn.textContent = '获取中...'; addLog('正在获取【待上传面单】订单列表...'); try { // 1. 获取订单列表 const orderRes = await fetch('https://sso.geiwohuo.com/gsp/orderPlus/listOrder', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ allocateTimeStart: "2026-03-01 00:00:00", allocateTimeEnd: "2026-06-30 23:59:59", excludeOrderType: 5, tabIndex: 3, page: 1, perPage: 200 }), credentials: 'include' }); const orderJson = await orderRes.json(); const orderIdList = orderJson.info.data.map(item => ({ orderId: item.orderId, orderNo: item.orderNo })); if (orderIdList.length === 0) { addLog('未获取到待上传面单订单', 'warn'); btn.disabled = false; btn.textContent = '📌 自动获取待上传面单订单'; return; } addLog(`获取到 ${orderIdList.length} 个订单,正在查询商品ID...`); // 2. 批量查询商品ID const itemRes = await fetch('https://sso.geiwohuo.com/gsp/orderPlus/listOrderItem', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ orderPlusListPageVOList: orderIdList.map(o => ({ orderId: o.orderId, orderGoodsList: [] })), tabIndex: 3 }), credentials: 'include' }); const itemJson = await itemRes.json(); // 3. 组装 orderNo + goodsId const autoList = []; itemJson.info.forEach(item => { const orderNo = item.orderNo; const goods = item.groupList?.[0]?.goodsList || []; goods.forEach(g => autoList.push({ orderNo, goodsId: g.id + '' })); }); orderList = autoList; st.textContent = `✅ 自动获取成功:${orderList.length} 条`; addLog(`自动获取完成,共 ${orderList.length} 条待上传`, 'success'); document.getElementById('shipStart').disabled = false; } catch (e) { addLog('获取失败:' + e.message, 'error'); } finally { btn.disabled = false; btn.textContent = '📌 自动获取待上传面单订单'; } }; // 暂停/继续 const pauseBtn = document.getElementById('shipPause'); pauseBtn.onclick = () => { isPaused = !isPaused; pauseBtn.textContent = isPaused ? '继续执行' : '暂停执行'; pauseBtn.className = isPaused ? 'ship-btn ship-btn-resume' : 'ship-btn ship-btn-pause'; addLog(isPaused ? '已暂停' : '已继续', 'warn'); }; // 查询运单号 async function queryLogistics(orderNo) { if (!CONFIG.queryToken) { addLog('请先登录', 'warn'); return null; } const p = new URLSearchParams({ third_no: orderNo, pageNumber: 1, pageSize: 100, status: '-2,-1,0,1,2,3,6' }); try { const r = await fetch(`${CONFIG.queryApi}?${p}`, { headers: { Token: CONFIG.queryToken } }); const j = await r.json(); const item = j.data?.list?.[0]; if (!item?.logistics_no) return null; return { expressIdCode: item.logistics || '', expressCode: item.logistics_no }; } catch { return null; } } // 上传运单 async function upload(orderNo, goodsId, ec, eic) { const r = await fetch(CONFIG.uploadApi, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ orderNo, site: CONFIG.uploadSite, orderGoodsExpressInfos: [{ expressCode: ec, expressIdCode: eic, goodsId: Number(goodsId) }] }), credentials: 'include' }); return await r.json(); } function delay() { return new Promise(r => { const check = () => isPaused ? setTimeout(check, 200) : setTimeout(r, CONFIG.delay); check(); }); } // 开始执行 document.getElementById('shipStart').onclick = async () => { if (!CONFIG.queryToken) { alert('请先登录POD'); return; } if (orderList.length === 0) { alert('无任务数据'); return; } const btn = document.getElementById('shipStart'); const st = document.getElementById('shipStatus'); if (!isPaused) { currentIndex = success = fail = skip = 0; document.getElementById('shipLogContainer').innerHTML = '=== 执行日志 ===\n'; addLog('开始任务'); } btn.disabled = true; btn.textContent = '执行中'; pauseBtn.disabled = false; for (; currentIndex < orderList.length; currentIndex++) { const { orderNo, goodsId } = orderList[currentIndex]; st.textContent = `[${currentIndex + 1}/${orderList.length}] ${orderNo}`; try { const logi = await queryLogistics(orderNo); if (!logi) { addLog(`${orderNo} 无运单,跳过`, 'skip'); skip++; await delay(); continue; } const res = await upload(orderNo, goodsId, logi.expressCode, logi.expressIdCode); if (res.code === '0' && res.msg === 'OK') { addLog(`${orderNo} 上传成功`, 'success'); success++; } else { addLog(`${orderNo} 失败:${res.msg}`, 'error'); fail++; } } catch (e) { addLog(`${orderNo} 异常`, 'warn'); fail++; } st.textContent = `进度:${currentIndex + 1}/${orderList.length} 成功:${success} 失败:${fail} 跳过:${skip}`; await delay(); } st.textContent = `🎉 完成!成功:${success} 失败:${fail} 跳过:${skip}`; addLog('任务结束', 'success'); btn.textContent = '开始自动执行'; btn.disabled = false; pauseBtn.disabled = true; isPaused = false; pauseBtn.textContent = '暂停执行'; pauseBtn.className = 'ship-btn ship-btn-pause'; }; } // ====================== 工具5:自动预约(获取+提交 自动时间) ====================== function initTool5() { const panel = document.createElement('div'); panel.id = 'auto-reserve-panel'; panel.style.cssText = ` position: fixed; top: 20px; left: 300px; z-index: 999998; background: #fff; padding: 16px; border-radius: 8px; box-shadow: 0 2px 12px rgba(0,0,0,0.15); width: 320px; font-family: system-ui, -apple-system, sans-serif; `; panel.innerHTML = `

✅ 自动预约(自动2个月时间)

=== 日志输出区 ===
`; document.body.appendChild(panel); // 日志输出 function log(text) { const el = document.getElementById('reserveLog'); const time = new Date().toLocaleTimeString(); el.textContent = `[${time}] ${text}\n` + el.textContent; } // 时间格式化:YYYY-MM-DD HH:mm:ss function formatDateTime(date) { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); const hours = String(date.getHours()).padStart(2, '0'); const minutes = String(date.getMinutes()).padStart(2, '0'); const seconds = String(date.getSeconds()).padStart(2, '0'); return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; } // 主逻辑 async function autoReservePickUp() { const getInfoUrl = 'https://sso.geiwohuo.com/gsp/store/address/prePickupV2/getPrePickInfo'; const commitUrl = 'https://sso.geiwohuo.com/gsp/store/address/prePickupV2/commit'; // 自动时间:当前时间 和 2个月前 const now = new Date(); const endTime = formatDateTime(now); const twoMonthsAgo = new Date(); twoMonthsAgo.setMonth(now.getMonth() - 2); const startTime = formatDateTime(twoMonthsAgo); log(`自动时间范围:${startTime} → ${endTime}`); const searchBody = { orderDeliverPrintListRequest: { page: 1, perPage: 50, placeOrderStartTime: startTime, placeOrderEndTime: endTime } }; try { // 1. 获取预约列表 log('正在获取可预约数据...'); const getRes = await fetch(getInfoUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify(searchBody) }); const getJson = await getRes.json(); if (getJson.code !== '0') { log(`❌ 获取失败:${getJson.msg}`); return; } const pickList = getJson.info.pickInfoList; if (!pickList || pickList.length === 0) { log('✅ 暂无待预约数据'); return; } log(`✅ 获取成功,共 ${pickList.length} 条待预约`); // 2. 组装提交参数 const commitProviderInfoList = pickList.map(item => { const firstCanPick = item.pickDateInfo.pickTimeInfoList.find(d => d.permitPickup === 1); const timeRange = item.pickDateInfo.reserveTimeRanges.at(-1); return { packageTotal: item.packageTotal, formatPackageTotalWeight: { number: item.packageTotalWeight, unionId: 1 }, pickDate: firstCanPick?.pickDate || '', reserveTimeRange: timeRange || '', providerId: item.placeProviderId, warehouseCode: item.placeOrderWarehouseResp.warehouseCode, defaultPackageTotalWeight: item.packageTotalWeight }; }); const commitBody = { commitProviderInfoList, orderDeliverPrintListRequest: searchBody.orderDeliverPrintListRequest }; // 3. 提交预约 log('正在提交预约...'); const commitRes = await fetch(commitUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify(commitBody) }); const commitJson = await commitRes.json(); log(`📌 提交结果:${JSON.stringify(commitJson)}`); if (commitJson.code === '0') { log('✅ 预约提交成功!'); } else { log(`❌ 预约失败:${commitJson.msg}`); } } catch (err) { log(`❌ 异常:${err.message}`); } } document.getElementById('startAutoReserve').onclick = autoReservePickUp; } // ====================== 【整合】商品榜单监控 + SKC注入(自动清除旧数据) ====================== function initRankMonitor() { const TARGET_PATH = '/sbn/marketAnalysis/queryProductRanking'; let rankData = []; let isHooked = false; console.log("🚀 榜单监控模块已启动,监听接口:" + TARGET_PATH); function clearOldSKC() { document.querySelectorAll('.injected-skc').forEach(el => el.remove()); } function startInjectSKU() { if (!getMonitorStatus()) { console.log("ℹ️ 监控未开启,跳过注入"); return; } console.log("🔍 开始查找表格并注入SKC,数据长度:" + rankData.length); if (rankData.length === 0) { console.log("❌ 无榜单数据,无法注入"); return; } const timer = setInterval(() => { const tbody = document.querySelector('tbody'); if (!tbody) { console.log("⏳ 未找到表格,继续等待..."); return; } if (tbody.children.length === 0) { console.log("⏳ 表格无数据,继续等待..."); return; } clearInterval(timer); clearOldSKC(); const trList = tbody.querySelectorAll('tr'); console.log(`✅ 找到表格,共 ${trList.length} 行,数据 ${rankData.length} 条`); trList.forEach((tr, index) => { const item = rankData[index]; if (!item) { console.log(`⚠️ 第 ${index} 行无对应数据`); return; } const skc = item.skc; if (!skc) { console.log(`⚠️ 第 ${index} 行数据无 skc 字段`, item); return; } const tds = tr.querySelectorAll('td'); if (tds.length < 3) { console.log(`⚠️ 第 ${index} 行列数不足`); return; } const targetTd = tds[2]; const skuDiv = document.createElement('div'); skuDiv.className = 'injected-skc'; skuDiv.style.marginTop = '4px'; skuDiv.style.display = 'flex'; skuDiv.style.alignItems = 'center'; skuDiv.style.gap = '6px'; // SKC 文本 + 复制 const skcText = document.createElement('span'); skcText.style.color = '#165DFF'; skcText.style.fontWeight = 'bold'; skcText.style.cursor = 'pointer'; skcText.innerText = `skc: ${skc}`; skcText.addEventListener('click', async () => { await navigator.clipboard.writeText(skc); skcText.style.color = '#00B42A'; setTimeout(() => { skcText.style.color = '#165DFF'; }, 1200); }); // 跳转按钮 const jumpBtn = document.createElement('button'); jumpBtn.innerText = '跳转'; jumpBtn.style.padding = '2px 6px'; jumpBtn.style.fontSize = '12px'; jumpBtn.style.border = 'none'; jumpBtn.style.borderRadius = '4px'; jumpBtn.style.background = '#1677ff'; jumpBtn.style.color = '#fff'; jumpBtn.style.cursor = 'pointer'; jumpBtn.onclick = () => { const siteEl = document.querySelector('.soui-select-ellipsis'); const site = siteEl?.textContent?.trim() || ''; let url = ''; switch (site) { case 'shein-us': url = `https://us.shein.com/pdsearch/${skc}/`; break; case 'shein-fr': url = `https://fr.shein.com/pdsearch/${skc}/`; break; case 'shein-de': url = `https://de.shein.com/pdsearch/${skc}/`; break; case 'shein-es': url = `https://es.shein.com/pdsearch/${skc}/`; break; case 'shein-it': url = `https://it.shein.com/pdsearch/${skc}/`; break; case 'shein-uk': url = `https://www.shein.co.uk/pdsearch/${skc}/`; break; default: alert('不支持的站点:' + site); return; } window.open(url, '_blank'); }; skuDiv.appendChild(skcText); skuDiv.appendChild(jumpBtn); targetTd.appendChild(skuDiv); console.log(`✅ 第 ${index} 行注入成功:${skc}`); }); }, 300); } function hookXHR() { if (isHooked) return; isHooked = true; console.log("🔗 已挂载XHR请求监听"); const oriOpen = XMLHttpRequest.prototype.open; const oriSend = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open = function (method, url) { this._url = url; this._method = method; oriOpen.apply(this, arguments); }; XMLHttpRequest.prototype.send = function (body) { const self = this; if (self._url.includes(TARGET_PATH) && self._method.toUpperCase() === 'POST') { console.log("🎯 捕获到榜单接口请求:", self._url); self.addEventListener('load', function () { if (!getMonitorStatus()) return; try { const res = JSON.parse(self.responseText); console.log("📊 榜单接口返回数据:", res); rankData = res?.info?.data || []; console.log("📦 解析到商品列表:", rankData.length + " 条"); startInjectSKU(); } catch (e) { console.error("❌ 解析榜单数据失败:", e); } }); } oriSend.apply(this, arguments); }; } hookXHR(); } // 启动全部功能 window.addEventListener('load', () => { createGlobalFloatBall(); initRankMonitor(); console.log("✅ SHEIN工具合集加载完成,当前监控状态:" + (getMonitorStatus() ? "开启" : "关闭")); }); })();