Greasy Fork

Greasy Fork is available in English.

闲管家一键上架助手

在商品页面复制商品信息并在闲鱼管家后台上架页面插入“一键填充”按钮,添加下载商品详情图片的功能。包括选品功能,可在闲鱼商详页导出猜你喜欢的数据到Excel,支持动态数据

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         闲管家一键上架助手
// @namespace    http://tampermonkey.net/
// @version      1.31
// @description  在商品页面复制商品信息并在闲鱼管家后台上架页面插入“一键填充”按钮,添加下载商品详情图片的功能。包括选品功能,可在闲鱼商详页导出猜你喜欢的数据到Excel,支持动态数据
// @author       九旬 wx:728396024    mail:[email protected]
// @match        https://h5.m.goofish.com/item?id=*
// @match        https://www.goofish.com/item*
// @match        https://goofish.pro/*
// @match        https://www.goofish.pro/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=goofish.pro
// @grant        GM_setValue
// @grant        GM_getValue
// @require      https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.all.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js
// @license      MIT
// ==/UserScript==

(function () {
  "use strict";

  // 使用 unsafeWindow 访问原始 window 对象
  const unsafeWindow = window.unsafeWindow || window;

  interceptRequests();

  function showToast(message, type = "success") {
    Swal.fire({
      toast: true,
      position: "top-end",
      icon: type,
      title: message,
      showConfirmButton: false,
      timer: 1500,
    });
  }

  function downloadImage(url, fileName) {
    fetch(url)
      .then(response => response.blob())
      .then(blob => {
        const a = document.createElement("a");
        a.href = URL.createObjectURL(blob);
        a.download = `${fileName}.jpg`;
        a.click();
        URL.revokeObjectURL(a.href);
      })
      .catch(error => console.error("图片下载失败:", error));
  }

  // 生成随机数
  function getRandomNumber(min, max) {
    const randomInt = Math.floor(Math.random() * (max / 10 - min / 10 + 1)) + min / 10;
    return randomInt * 10;
  }

  // 获取价格,适配不同格式,比如 1~10、10
  function getPrice(priceStr) {
    // 移除特殊字符 "¥" 和 "\n"
    const cleanedStr = priceStr.replace(/[¥\n]/g, "");

    // 使用正则表达式提取所有数字
    const numbers = cleanedStr.match(/\d+(\.\d+)?/g);

    if (numbers === null) {
      return null; // 如果没有匹配到数字,返回null
    }

    // 将提取出的字符串数字转换为浮点数
    const prices = numbers.map(Number);

    // 找出最大值
    const maxPrice = Math.max(...prices);

    return maxPrice;
  }

  // 商品页面逻辑
  if (window.location.href.includes("https://www.goofish.com/item")) {
    setTimeout(() => {
      const button = document.createElement("button");
      let goofishImages = document.querySelectorAll("div.slick-slide:not(.slick-cloned)").length;

      button.innerHTML = `复制商品信息 & 下载图片( <b> ${goofishImages} </b>)`;
      button.style.position = "fixed";
      button.style.bottom = "20px";
      button.style.left = "20px";
      button.style.zIndex = 1000;
      button.style.padding = "10px 20px";
      button.style.backgroundColor = "#28a745";
      button.style.color = "white";
      button.style.border = "none";
      button.style.borderRadius = "5px";
      button.style.cursor = "pointer";

      document.body.appendChild(button);

      button.addEventListener("click", () => {
        copyGoods();
        downloadImages();
      });
    }, 1500);

    // 复制商品信息
    function copyGoods() {
      const detailTextElements = document.querySelectorAll("[class*='desc']")[1];
      const priceElements = document.querySelectorAll("[class*='price']")[0];

      if (detailTextElements && priceElements) {
        const detailText = detailTextElements.innerText;
        const indexOfFirstNewLine = detailText.indexOf(" ");
        let productName, productDescription;

        if (indexOfFirstNewLine !== -1) {
          productName = detailText.slice(0, indexOfFirstNewLine).trim();
          productDescription = detailText.trim();
        } else {
          productName = detailText.trim();
          productDescription = "";
        }

        // 裁剪商品标题到30个中文字以内
        const maxTitleLength = 30;
        productName = productName.slice(0, maxTitleLength);

        const productPrice = getPrice(priceElements.innerText.trim());

        GM_setValue("productName", productName);
        // GM_setValue("productDescription", productDescription);
        GM_setValue("productPrice", productPrice);

        console.log("商品信息已存储");
        console.log("商品名:", productName);
        // console.log("商品详情:", productDescription);
        console.log("商品价格:", productPrice);

        showToast("商品信息已复制");
      } else {
        console.error("未找到商品详情或价格元素");
        showToast("未找到商品详情或价格元素", "error");
      }
    }

    // 下载商品图片
    function downloadImages() {
      // 获取所有 class 为 slick-slide 且不包含 slick-cloned 的 div 元素
      const goofishImageDivs = document.querySelectorAll("div.slick-slide:not(.slick-cloned)");

      // 遍历这些 div 元素,获取其中的 img 标签
      const goofishImages = Array.from(goofishImageDivs).map(div => div.querySelector("img"));

      // 过滤掉可能不存在 img 标签的 div
      const filteredGoofishImages = goofishImages.filter(img => img !== null);

      filteredGoofishImages.forEach((img, index) => {
        setTimeout(() => {
          let src = img.src.replace(/_webp$/, ""); // 去掉_webp并下载jpg格式
          downloadImage(src, `${index + 1}`);
        }, index * 100); // 每个下载任务之间延迟100毫秒
      });
      showToast("图片正在开始");
    }
  }

  // 闲鱼管家上架页面逻辑
  if (window.location.hostname.includes("goofish.pro")) {
    const button = document.createElement("button");
    button.innerText = "一键填充";
    button.style.position = "fixed";
    button.style.bottom = "10px";
    button.style.left = "150px";
    button.style.zIndex = 1000;
    button.style.padding = "10px 20px";
    button.style.backgroundColor = "#28a745";
    button.style.color = "white";
    button.style.border = "none";
    button.style.borderRadius = "5px";
    button.style.cursor = "pointer";

    document.body.appendChild(button);

    button.addEventListener("click", () => {
      // 检查当前页面是否是商品添加页面
      if (!window.location.href.includes("/sale/product/add")) {
        window.location.href = "https://goofish.pro/sale/product/add?from=%2Fon-sale";
        setTimeout(() => fillProductInfo(), 1500);
        return;
      } else {
        fillProductInfo();
      }
    });
  }

  function fillProductInfo() {
    // 从 Tampermonkey 存储中获取商品信息
    const productName = GM_getValue("productName", "");
    const productDescription = GM_getValue("productDescription", "") || productName;
    let prict = GM_getValue("productPrice");
    let productPrice = 100;
    if (prict < 2) {
      productPrice = 1.9;
    } else {
      productPrice = parseFloat(GM_getValue("productPrice", "100")) - 0.1;
    }

    if (!productName || !productDescription || isNaN(productPrice)) {
      showToast("请先去闲鱼详情页复制商品信息", "warning");
      return;
    }

    console.log("读取到的商品名:", productName);
    console.log("读取到的商品详情:", productDescription);
    console.log("读取到的商品价格:", productPrice);

    // 选择类目
    const categoryElements = document.querySelectorAll(".release-history");
    if (categoryElements.length > 0) {
      categoryElements[0].click();
    } else {
      console.error("未找到类目选择元素");
      showToast("请选择商品分类", "error");
    }

    setTimeout(() => {
      // 选择店铺
      const shopList = document.querySelectorAll("ul.auth-list li");
      if (shopList.length > 1) {
        // 默认选择第一个店铺
        const unSelectedIconElements = document.querySelector(".un-selected-icon");
        if (unSelectedIconElements) {
          unSelectedIconElements.click();
        }
      } else {
        console.error("请先创建闲鱼店铺");
        showToast("请先创建闲鱼店铺", "error");
      }

      setTimeout(() => {
        // 填充商品名
        const inputElement = document.querySelector('input[placeholder="请输入商品标题,最多允许输入30个汉字"]');
        if (inputElement) {
          inputElement.value = productName;
          const event = new Event("input", { bubbles: true });
          inputElement.dispatchEvent(event);
        } else {
          console.error("未找到商品标题输入框");
          showToast("未找到商品标题输入框", "error");
        }

        // 填充商品描述
        const descriptionElements = document.querySelector('textarea[placeholder="请输入商品描述"]');
        if (descriptionElements) {
          descriptionElements.value = productDescription;
          const event = new Event("input", { bubbles: true });
          descriptionElements.dispatchEvent(event);
        } else {
          console.error("未找到商品描述输入框");
          showToast("未找到商品描述输入框", "error");
        }

        // 搜索一个价格 0=原价 1=售价
        const inputElements = document.querySelectorAll('input[placeholder="¥ 0.00"]');
        // 填充原价
        if (inputElements[0]) {
          inputElements[0].value = getRandomNumber(200, 500);
          const event = new Event("input", { bubbles: true });
          inputElements[0].dispatchEvent(event);
        } else {
          console.error("未找到价格输入框");
          showToast("未找到价格输入框", "error");
        }
        // 填充价格
        if (inputElements[1]) {
          inputElements[1].value = productPrice.toFixed(2);
          const event = new Event("input", { bubbles: true });
          inputElements[1].dispatchEvent(event);
        } else {
          console.error("未找到价格输入框");
          showToast("未找到价格输入框", "error");
        }

        // // 填充库存
        // const productStore = 1;
        // if (inputElements.length > 5) {
        //   inputElements[6].value = productStore;
        //   const event = new Event("input", { bubbles: true });
        //   inputElements[6].dispatchEvent(event);
        // } else {
        //   console.error("未找到库存输入框");
        //   showToast("未找到库存输入框", "error");
        // }

        // const expressFeeItem = document.querySelector(".express_fee-item");
        // const radioElements = expressFeeItem.querySelectorAll(".el-radio")[0];
        // const isSelet = radioElements.querySelector('input[type="radio"]').value;
        // console.log(isSelet);
        // if (radioElements.length) {
        //   radioElements[0].click(); // 包邮
        // } else {
        //   console.error("未找到发布商品时机的单选框");
        //   showToast("未找到发布商品时机的单选框", "error");
        // }

        showToast("商品信息已填充");
      }, 500);
    }, 500);
  }

  // 闲鱼详情页:提取并显示想要人数、浏览量和转化率
  if (window.location.href.includes("https://www.goofish.com/item")) {
    setTimeout(() => {
      // const spanElement = document.querySelector("#ice-container > div.content-container--gIWgkNkm > div.item-container--yLJD5VZj > div.item-main-container--jhpFKlaS > div.item-main-info--rA5Bmpa5 > div.tips--JYdXhSNh > div.want--mVAXJTGv");
      const spanElement = document.querySelectorAll("[class*='want']")[0];
      if (spanElement) {
        const textContent = spanElement.textContent.trim();

        // 初始化变量
        let wantText = "0人想要";
        let viewText = "0浏览";

        // 检查字符串内容并进行拆分和处理
        if (textContent.includes("人想要") && textContent.includes("浏览")) {
          // 如果同时包含 "人想要" 和 "浏览"
          [wantText, viewText] = textContent.split(" ");
        } else if (textContent.includes("浏览")) {
          // 只有 "浏览"
          viewText = textContent;
        }

        // 提取数字部分并转换为整数
        const wantNumber = parseInt(wantText.replace("人想要", "").trim(), 10) || 0;
        const viewNumber = parseInt(viewText.replace("浏览", "").trim(), 10) || 0;

        let rate = 0;
        if (wantNumber != 0 || viewNumber != 0) {
          rate = wantNumber / viewNumber;
        }
        const conversionRate = (rate * 100).toFixed(0);
        const conversionRateText = conversionRate + "%";

        const statsDiv = document.createElement("div");
        statsDiv.style.position = "fixed";
        statsDiv.style.bottom = "63px";
        statsDiv.style.left = "20px";
        statsDiv.style.backgroundColor = "#93ab9b";
        statsDiv.style.borderRadius = "6px";
        statsDiv.style.color = "white";
        statsDiv.style.padding = "10px";
        statsDiv.style.zIndex = "1000";
        statsDiv.style.fontSize = "14px";

        const conversionRateSpan = document.createElement("b");
        conversionRateSpan.textContent = conversionRateText;
        conversionRateSpan.style.backgroundColor = conversionRate > 7 ? "green" : "red";
        statsDiv.style.backgroundColor = conversionRate > 7 ? "#93ab9b" : "rgb(211 131 131)";

        conversionRateSpan.style.padding = "2px 4px";
        conversionRateSpan.setAttribute("id", "conversion-rate");

        statsDiv.innerHTML = `
想要数 : <b id="want-num">${wantNumber}</b><br>
浏览量 : <b id="view-num">${viewNumber}</b><br>
转化率 : `;
        statsDiv.appendChild(conversionRateSpan);

        document.body.appendChild(statsDiv);

        if (conversionRate < 7) {
          console.log(`转化率太低了 ${conversionRate}%`);
        }
      } else {
        console.error("无法找到目标 span 元素");
      }
    }, 1000);
  }

  // 拦截并处理接口请求
  function interceptRequests() {
    // 覆盖原生的 XMLHttpRequest
    const originalXHR = unsafeWindow.XMLHttpRequest;
    unsafeWindow.XMLHttpRequest = function () {
      const xhr = new originalXHR();

      // 保存原始的 open 方法
      const originalOpen = xhr.open;
      xhr.open = function (method, url, ...rest) {
        this._url = url; // 保存请求 URL
        return originalOpen.apply(this, [method, url, ...rest]);
      };

      // 保存原始的 send 方法
      const originalSend = xhr.send;
      xhr.send = function (...args) {
        this.addEventListener("load", function () {
          // 拦截商品详情接口,获取详情文本内容
          if (this._url.includes("h5api.m.goofish.com/h5/mtop.taobao.idle.pc.detail")) {
            try {
              const data = JSON.parse(this.responseText);
              console.log("接收到的数据:", data);
              if (data?.data?.itemDO?.desc) {
                let productDescription = data.data.itemDO.desc;
                const goodsId = new URL(location.href).searchParams.get("id");
                //                 productDescription += `\n \n[钉子]发的是百 度 网 盘 链 接,永不失效,售出不退。
                // [钉子]任何情况,不要申请退款,私信沟通给你处理,小店经营不易。
                // [钉子]所有文件均获取自网络公开渠道,仅供学习和交流使用,所有版权归版权人所有,如版权方认为侵犯了您的版权,请及时联系小店删除。`;
                //                 productDescription += `\n${goodsId}`;
                GM_setValue("productDescription", productDescription);
                console.log("商品详情:", productDescription);
              }
            } catch (error) {
              console.error("解析 XHR 响应时发生错误:", error);
            }
          }

          // 拦截商品详情接口,获取详情文本内容
          if (this._url.includes("h5api.m.goofish.com/h5/mtop.taobao.idle.item.web.recommend.list")) {
            try {
              const data = JSON.parse(this.responseText);
              console.log("接收到的数据:", data);

              // 检查 data.data.cardList 是否为数组
              if (data && Array.isArray(data.data?.cardList)) {
                // 提取有效的数据
                const tempData = data.data.cardList.filter(item => {
                  if (item && item.cardData && item.cardData.itemId) {
                    const price = parseInt(item.cardData.price);
                    return price > 0;
                  }
                  return false;
                });

                // 合并到全局数组 window.collectedData
                window.collectedData.push(...tempData);
                console.log("新数据已追加:", data.data.cardList);

                // 去重:使用一个 Set 来追踪已经存在的 itemId
                const uniqueItems = [];
                const itemIdSet = new Set();

                // 遍历 window.collectedData,添加未重复的 item
                for (const item of window.collectedData) {
                  const itemId = item.cardData?.itemId;
                  if (itemId && !itemIdSet.has(itemId)) {
                    uniqueItems.push(item);
                    itemIdSet.add(itemId);
                  }
                }

                // 更新 window.collectedData 为去重后的结果
                window.collectedData = uniqueItems;

                console.log("去重后的数据:", window.collectedData);

                // 更新数据计数
                updateDataCount();
              }
            } catch (error) {
              console.error("解析 XHR 响应时发生错误:", error);
            }
          }
        });
        return originalSend.apply(this, args);
      };

      return xhr;
    };
  }

  // 自动写入类目(暂定为电子资料)
  // if (window.location.hostname.includes("goofish.pro")) {
  //   // 要写入的键
  //   const key = "goods_select";

  //   // 要写入的值(注意是一个字符串)
  //   const value = JSON.stringify([
  //     {
  //       name: "电子资料",
  //       id: [99, "eebfcb1cd9bfce8e212e21d79c0262e7", "eebfcb1cd9bfce8e212e21d79c0262e7", "3cdbae6d47df9251a7f7e02f36b0b49a"],
  //       item_biz_type: 2,
  //     },
  //   ]);

  //   // 检查localStorage中是否已经存在该键
  //   if (!localStorage.getItem(key)) {
  //     // 将键值对写入localStorage
  //     // localStorage.setItem(key, value);
  //     // console.log(`已写入localStorage: ${key} = ${value}`);
  //   } else {
  //     console.log(`localStorage中已存在: ${key} = ${localStorage.getItem(key)}`);
  //   }
  // }

  repleaceUrl();
  // 避免闲管家的域名混用,带www和不带www的,因为两者的cookie不同,导致登录状态是不共享的
  function repleaceUrl() {
    // 获取当前页面的 URL
    const currentUrl = window.location.href;

    // 判断是否是以 'https://www.goofish.pro/' 开头
    if (currentUrl.startsWith("https://www.goofish.pro/")) {
      // 使用正则表达式替换 'www.' 为 ''
      const newUrl = currentUrl.replace("https://www.", "https://");

      // 跳转到新的 URL
      window.location.replace(newUrl);
    }
  }

  // 闲鱼商详页导出猜你喜欢的数据到Excel,支持动态数据

  if (window.location.href.includes("https://www.goofish.com/item?")) {
    exportGoodsList();
  }

  // 更新商品数量显示
  function updateDataCount() {
    const countElement = document.getElementById("data-count");
    if (countElement) {
      countElement.innerText = window.collectedData.length;
    }
  }

  function exportGoodsList() {
    setTimeout(() => {
      insertDownloadButton();
    }, 0);

    // 全局数组,用于存储商品数据,确保全局数据存储在 window 对象上
    if (!window.collectedData) {
      window.collectedData = [];
    }

    interceptRequests();

    // 插入下载按钮到页面
    function insertDownloadButton() {
      const button = document.createElement("button");
      button.innerHTML = `导出 [猜你喜欢] 商品 (<b id="data-count">0</b>)`;
      button.style.position = "fixed";
      button.style.width = "240px";
      button.style.left = "50%";
      button.style.marginLeft = "-120px";
      button.style.bottom = "20px";
      button.style.zIndex = 9999;
      button.style.padding = "10px 20px";
      button.style.backgroundColor = "rgb(40, 167, 69)";
      button.style.color = "#FFFFFF";
      button.style.border = "none";
      button.style.borderRadius = "5px";
      button.style.cursor = "pointer";
      button.addEventListener("click", downloadExcel);
      document.body.appendChild(button);
    }

    // 时间戳转换成日期格式
    function timestampToFormattedDate(timestamp) {
      if (!timestamp) return "";
      var date = new Date(parseInt(timestamp));
      var year = date.getFullYear(); // 获取年份
      var month = date.getMonth() + 1; // 获取月份,月份是从0开始的,所以需要加1
      var day = date.getDate(); // 获取日期

      // 月份和日期如果是单数,需要在前面补0
      month = month < 10 ? "0" + month : month;
      day = day < 10 ? "0" + day : day;

      return `${year}-${month}-${day}`;
    }

    // 下载Excel文件
    function downloadExcel() {
      if (window.collectedData.length === 0) {
        alert("没有数据可导出");
        return;
      }

      // 创建Excel工作簿
      const workbook = XLSX.utils.book_new();
      const worksheetData = window.collectedData
        .map(item => {
          console.log(111, item.cardData?.trackEventParamInfo?.args?.publishTime);
          return {
            //商品ID: item.cardData.itemId || '',
            //链接: `https://www.goofish.com/item?id=` + item.cardData.itemId,
            链接: {
              f: `HYPERLINK("https://www.goofish.com/item?id=${item.cardData.itemId}", "https://www.goofish.com/item?id=${item.cardData.itemId}")`,
            },
            标题: {
              f: `HYPERLINK("https://www.goofish.com/item?id=${item.cardData.itemId}", "${item.cardData.title}")`,
            },
            想要数: parseInt(item.cardData.clickParam.args.wantNum, 10) || "",
            价格: parseInt(item.cardData.price) - 0.1 || "",
            城市: item.cardData.area || "",
            发布日期: timestampToFormattedDate(item.cardData?.clickParam?.args?.publishTime),
          };
        })
        .sort((a, b) => b.想要数 - a.想要数);

      // 将数据转化为工作表
      const worksheet = XLSX.utils.json_to_sheet(worksheetData);
      XLSX.utils.book_append_sheet(workbook, worksheet, "猜你喜欢");

      // 生成Excel并触发下载
      const workbookOut = XLSX.write(workbook, { bookType: "xlsx", type: "array" });
      const blob = new Blob([workbookOut], { type: "application/octet-stream" });
      const url = URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = url;
      a.download = "猜你喜欢商品.xlsx";
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      URL.revokeObjectURL(url);
    }
  }
})();