// ==UserScript== // @name TEMU,SPU导出商品识别码表格 // @namespace http://tampermonkey.net/ // @version 2.1 // @description SPU抓取 + 自定义值下拉增删改查 + CSV导出 // @author 助手 // @match https://agentseller.temu.com/govern/information-supplementation // @grant none // @run-at document-end // ==/UserScript== (function() { 'use strict'; // ===================== 本地存储管理 ===================== const STORAGE_KEY = "spu_export_options"; function getOptions() { try { let data = JSON.parse(localStorage.getItem(STORAGE_KEY)); if (!data || data.length === 0) { data = [{ value: "TX F202501", label: "欧区识别码" }]; saveOptions(data); } return data; } catch { return [{ value: "TX F202501", label: "欧区识别码" }]; } } function saveOptions(options) { localStorage.setItem(STORAGE_KEY, JSON.stringify(options)); } // ===================== 抓取SPU列表 ===================== function getSPUList() { const spuList = []; let index = 1; const step = 4; while (true) { const selector = `#agentseller-layout-content > div > div > div > div.rocket-table-wrapper > div > div > div > div > div > table > tbody > tr:nth-child(${index}) > td:nth-child(1) > div > div:nth-child(2) > div:nth-child(2)`; const el = document.querySelector(selector); if (!el) break; const text = el.textContent.trim(); const spuMatch = text.match(/\d+/); if (spuMatch) { spuList.push(spuMatch[0]); } index += step; } return spuList; } // ===================== 导出CSV ===================== async function exportSPUToCSV(customText) { const spuList = getSPUList(); if (spuList.length === 0) { alert("未抓取到任何 SPU 数据"); return; } const header1 = "商品识别码(多个请用;间隔)需要与实物标签上的批次号/序列号保持一致,否则将影响商品售卖/发货入库"; const header2 = "如果该商品填过识别码,默认保留老识别码并新增本次新识别码。如需要将历史识别码更新掉,请填写“更新”"; const rows = [ [header1, header2], ...spuList.map(spu => [spu, customText]) ]; const csvContent = "\uFEFF" + rows.map(e => e.join(",")).join("\n"); const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = "具体数据(请将需导入数据填写到该sheet).csv"; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); alert(`导出完成!共 ${spuList.length} 条数据`); } // ===================== 创建控制面板 ===================== function createPanel() { const container = document.createElement("div"); container.style = "display:inline-flex; gap:6px; align-items:center; margin-left:10px;"; // 下拉选择框 const select = document.createElement("select"); select.style = "padding:4px 8px; border:1px solid #dcdfe6; border-radius:4px; min-width:190px;"; // 值输入框 const valInput = document.createElement("input"); valInput.placeholder = "填充值"; valInput.style = "padding:4px 8px; border:1px solid #dcdfe6; border-radius:4px; width:120px;"; // 备注输入框 const labelInput = document.createElement("input"); labelInput.placeholder = "备注"; labelInput.style = "padding:4px 8px; border:1px solid #dcdfe6; border-radius:4px; width:100px;"; // 按钮 const addBtn = document.createElement("button"); addBtn.textContent = "新增"; addBtn.style = "padding:4px 8px; background:#1677ff; color:#fff; border:none; border-radius:4px;cursor:pointer;"; const editBtn = document.createElement("button"); editBtn.textContent = "编辑"; editBtn.style = "padding:4px 8px; background:#fa8c16; color:#fff; border:none; border-radius:4px;cursor:pointer;"; const delBtn = document.createElement("button"); delBtn.textContent = "删除"; delBtn.style = "padding:4px 8px; background:#ff4d4f; color:#fff; border:none; border-radius:4px;cursor:pointer;"; const exportBtn = document.createElement("button"); exportBtn.textContent = "导出CSV"; exportBtn.style = "padding:4px 12px; background:#00b42a; color:#fff; border:none; border-radius:4px;cursor:pointer;"; // 渲染下拉列表 function renderSelect() { select.innerHTML = ""; const opts = getOptions(); opts.forEach((opt, i) => { const option = document.createElement("option"); option.value = opt.value; option.textContent = `${opt.value}(${opt.label})`; option.dataset.index = i; select.appendChild(option); }); // ===================== 修复在这里 ===================== // 渲染完成后,自动把第一项填入输入框 if(select.options.length > 0){ select.selectedIndex = 0; const firstOpt = opts[0]; valInput.value = firstOpt.value; labelInput.value = firstOpt.label; } } renderSelect(); // 切换下拉时自动填充 select.addEventListener("change", () => { const idx = select.selectedIndex; if (idx < 0) return; const opt = getOptions()[idx]; valInput.value = opt.value; labelInput.value = opt.label; }); // 新增 addBtn.addEventListener("click", () => { const v = valInput.value.trim(); const l = labelInput.value.trim(); if (!v) return alert("请输入填充值"); const list = getOptions(); list.push({ value: v, label: l }); saveOptions(list); renderSelect(); alert("新增成功"); }); // 编辑 editBtn.addEventListener("click", () => { const idx = select.selectedIndex; if (idx < 0) return alert("请先选择一项"); const v = valInput.value.trim(); const l = labelInput.value.trim(); if (!v) return alert("请输入填充值"); const list = getOptions(); list[idx] = { value: v, label: l }; saveOptions(list); renderSelect(); alert("编辑成功"); }); // 删除 delBtn.addEventListener("click", () => { const idx = select.selectedIndex; if (idx < 0) return alert("请先选择一项"); if (!confirm("确定删除该项?")) return; const list = getOptions(); list.splice(idx, 1); saveOptions(list); renderSelect(); valInput.value = ""; labelInput.value = ""; alert("删除成功"); }); // 导出 exportBtn.addEventListener("click", async () => { const v = valInput.value.trim(); if (!v) return alert("请选择或输入填充值"); await exportSPUToCSV(v); }); // 组装面板 container.append(select, valInput, labelInput, addBtn, editBtn, delBtn, exportBtn); return container; } // ===================== 等待元素加载 + 插入面板 ===================== function waitForElementAndInject() { const xpath = "/html/body/div[1]/div[1]/section/section/section/main/div/div/div/button[3]"; const check = setInterval(() => { const targetBtn = document.evaluate( xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null ).singleNodeValue; if (targetBtn) { clearInterval(check); const panel = createPanel(); targetBtn.after(panel); console.log("✅ 工具面板已成功植入指定位置"); } }, 500); } // 启动 waitForElementAndInject(); })();