AI 提示词:油猴脚本智能生成器

30 浏览
2 试用
0 购买
Oct 22, 2025更新

本提示词模板专为浏览器自动化脚本开发设计,能够根据用户需求智能生成功能完整、安全可靠的UserScript代码。通过深度分析目标网站特性和自动化需求,提供结构清晰、注释详尽的脚本代码,支持多种浏览器扩展兼容。亮点包括:采用任务分步法确保代码逻辑严密,内置安全检测机制防止恶意脚本生成,支持动态元素识别和事件处理,提供完善的错误处理和兼容性保障。该模板能够显著降低脚本开发门槛,让非专业用户也能快速创建高质量的浏览器自动化脚本,同时为专业开发者提供灵活的定制空间,适用于网页内容修改、数据提取、界面优化等多种应用场景。

脚本概述

  • 功能简介:
    • 在商品列表页与详情页实时监听价格与库存变动;读取价格、促销标签与券信息。
    • 对“低于自定义阈值”或出现“满减”的商品进行高亮,并在右侧生成“今日促销清单”(可导出CSV)。
    • 为每个商品插入“一键加入监控/收藏”按钮;支持批量勾选并导出CSV。
    • 列表页提供“只看降价”“只看包邮”的快速筛选;顶部展示最近5个降价提醒。
  • 技术特点:
    • 使用 MutationObserver 监听动态加载与价格/库存节点变动。
    • 滚动事件节流,减少回流与重绘,保障性能。
    • 本地存储(GM_setValue/GM_getValue)保存阈值、监控列表、价格历史与筛选偏好。
    • 无跨域请求、无注入页面上下文、无敏感信息硬编码,遵循浏览器安全策略。
  • 兼容性说明:
    • 仅在 Chromium 内核浏览器(Chrome / Edge)下启用。
    • 适用于 Tampermonkey(建议 v4.19+)。
    • run-at document-end,避免阻塞页面渲染。

完整代码

// ==UserScript==
// @name         Deals助手:促销高亮+监控+CSV导出(shop.example.cn)
// @namespace    https://example.user.scripts
// @version      1.0.0
// @description  列表/详情页监听价格与库存变动;低价/满减高亮;今日促销清单;批量勾选导出CSV;只看降价/包邮筛选;顶部最近降价提醒。仅Chrome/Edge生效。
// @author       YourName
// @match        https://shop.example.cn/*
// @run-at       document-end
// @noframes
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @compatible   chrome
// @compatible   edge
// ==/UserScript==

(function () {
  'use strict';

  // 仅在Chrome/Edge生效
  const ua = navigator.userAgent || '';
  const isChromium = /\bChrome\/\d+/.test(ua) || /\bEdg\/\d+/.test(ua);
  if (!isChromium) {
    console.info('[Deals助手] 非Chrome/Edge浏览器,脚本不启用。');
    return;
  }

  // --------------------------- 配置与选择器 ---------------------------
  const STORAGE_KEYS = {
    THRESHOLD: 'tm_deals_threshold',
    WATCHLIST: 'tm_deals_watchlist',
    LAST_PRICES: 'tm_deals_last_prices',
    DROPS: 'tm_deals_price_drops',
    FILTERS: 'tm_deals_filters',
    HIDDEN_TOPBAR: 'tm_deals_hidden_topbar',
  };

  // 默认选择器(可在下方“自定义建议”中调整)
  const SELECTORS = {
    list: {
      container: '.deal-list, .product-list, [data-role="deal-list"], .items, .list',
      item: '.deal-item, .product-card, li[data-sku], .item, .product',
      id: '[data-sku], [data-id]',
      link: 'a[href*="/item/"], a[href*="/detail"], .title a, .name a',
      title: '.title, .product-title, [data-role="title"], .name',
      price: '.price, .current-price, [data-role="price"], .p-price, .deal-price',
      stock: '.stock, .inventory, [data-role="stock"], .p-stock',
      coupon: '.coupon, .voucher, [data-role="coupon"], .coupon-area',
      promo: '.promo, .tag, .promotion, [data-role="promo"], .p-promos',
      shipping: '.shipping, .postage, .freight, [data-role="shipping"], .tag-shipping',
    },
    detail: {
      root: '.product-detail, #product-detail, [data-role="detail-root"], .detail',
      id: '[data-sku], [data-id], meta[itemprop="sku"]',
      title: 'h1.title, .product-name, [data-role="title"], h1',
      price: '.price, .current-price, [data-role="price"], .p-price, .deal-price',
      stock: '.stock, .inventory, [data-role="stock"], .p-stock',
      coupon: '.coupon-box, .voucher, [data-role="coupon"], .coupon-area',
      promo: '.promo, .promotion, [data-role="promo"], .p-promos',
      shipping: '.shipping, .postage, .freight, [data-role="shipping"], .ship',
    }
  };

  // --------------------------- 工具函数 ---------------------------
  const $ = (sel, root = document) => root.querySelector(sel);
  const $$ = (sel, root = document) => Array.from(root.querySelectorAll(sel));
  const text = (el) => (el ? (el.textContent || '').trim() : '');
  const nowTs = () => Date.now();
  const clamp = (n, min, max) => Math.min(Math.max(n, min), max);

  function parsePrice(str) {
    if (!str) return NaN;
    // 支持 "¥123.45"、"¥123"、"123元"、"123.45" 等
    const m = String(str).replace(/,/g, '').match(/(\d+(?:\.\d+)?)/);
    return m ? parseFloat(m[1]) : NaN;
  }

  function hasFreeShipping(s) {
    if (!s) return false;
    const t = s.replace(/\s+/g, '');
    return /包邮|免邮|免运费/.test(t);
  }

  function hasFullReduction(s) {
    if (!s) return false;
    const t = s.replace(/\s+/g, '');
    return /满减|满\d+减\d+/.test(t);
  }

  function toCSV(rows) {
    // rows: Array<Array<string|number>>
    const escape = (v) => {
      const s = (v == null ? '' : String(v)).replace(/"/g, '""');
      return `"${s}"`;
    };
    const lines = rows.map((r) => r.map(escape).join(',')).join('\r\n');
    return '\ufeff' + lines; // 加BOM,便于Excel识别中文
  }

  function download(filename, content, mime = 'text/csv;charset=utf-8;') {
    const blob = new Blob([content], { type: mime });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.style.display = 'none';
    a.href = url;
    a.download = filename;
    document.body.appendChild(a);
    a.click();
    setTimeout(() => {
      URL.revokeObjectURL(url);
      a.remove();
    }, 0);
  }

  function throttle(fn, wait = 100) {
    let last = 0, timer = null, ctx, args;
    return function throttled(...a) {
      const now = Date.now();
      ctx = this; args = a;
      if (now - last >= wait) {
        last = now;
        fn.apply(ctx, args);
      } else {
        clearTimeout(timer);
        timer = setTimeout(() => {
          last = Date.now();
          fn.apply(ctx, args);
        }, wait - (now - last));
      }
    };
  }

  function debounce(fn, wait = 150) {
    let timer = null;
    return function debounced(...args) {
      clearTimeout(timer);
      timer = setTimeout(() => fn.apply(this, args), wait);
    };
  }

  function hashId(str) {
    // 简易hash(避免过长ID)
    let h = 0;
    for (let i = 0; i < str.length; i++) {
      h = (h << 5) - h + str.charCodeAt(i);
      h |= 0;
    }
    return 'H' + Math.abs(h);
  }

  // --------------------------- 状态与存储 ---------------------------
  let threshold = Number(GM_getValue(STORAGE_KEYS.THRESHOLD, 99)); // 默认阈值99
  let watchlist = GM_getValue(STORAGE_KEYS.WATCHLIST, {}); // { id: { favorite: bool, threshold: number } }
  let lastPrices = GM_getValue(STORAGE_KEYS.LAST_PRICES, {}); // { id: { price: number, stock: string, t: number } }
  let drops = GM_getValue(STORAGE_KEYS.DROPS, []); // [ {id,title,oldPrice,newPrice,time,url} ]
  let filters = GM_getValue(STORAGE_KEYS.FILTERS, { onlyDrop: false, onlyFreeShip: false });
  let topbarHidden = !!GM_getValue(STORAGE_KEYS.HIDDEN_TOPBAR, false);

  // 内存结构
  const itemState = new Map(); // id -> { el, title, price, stock, coupon, promo, shipping, url, isDrop, isHighlighted, selected }
  const itemObservers = new Map(); // id -> MutationObserver
  const todayHighlightSet = new Set(); // id集合(被高亮的今日促销清单)

  // --------------------------- 样式 ---------------------------
  GM_addStyle(`
  .tmdeals-topbar {
    position: fixed; top: 0; left: 0; right: 0; z-index: 999999;
    background: #0f172a; color: #fff; font-size: 12px; font-family: system-ui, -apple-system, "Segoe UI", Roboto, Arial;
    display: flex; align-items: center; gap: 10px; padding: 6px 10px; box-shadow: 0 2px 8px rgba(0,0,0,.15);
  }
  .tmdeals-topbar a, .tmdeals-topbar button { color: #fff; }
  .tmdeals-topbar .drops { display: flex; gap: 10px; flex-wrap: nowrap; overflow: hidden; }
  .tmdeals-topbar .drop-item { white-space: nowrap; opacity: .9; }
  .tmdeals-topbar .threshold { margin-left: auto; display: flex; align-items: center; gap: 6px; }
  .tmdeals-topbar .threshold input {
    width: 72px; padding: 2px 6px; border-radius: 4px; border: 1px solid #334155; background: #0b1220; color: #fff;
  }
  .tmdeals-topbar .filters { display: flex; gap: 8px; margin-left: 10px; }
  .tmdeals-topbar .btn {
    border: 1px solid #334155; background: #0b1220; padding: 2px 6px; border-radius: 4px; cursor: pointer;
  }
  .tmdeals-topbar .close { margin-left: 8px; background: transparent; border: none; color: #94a3b8; cursor: pointer; }

  .tmdeals-sidebar {
    position: fixed; top: 64px; right: 10px; width: 300px; max-height: calc(100vh - 80px);
    z-index: 999998; background: #ffffff; color: #111827; border: 1px solid #e5e7eb; border-radius: 8px;
    box-shadow: 0 6px 24px rgba(0,0,0,.12); display: flex; flex-direction: column; overflow: hidden;
  }
  .tmdeals-sidebar .header { padding: 8px 10px; background: #f8fafc; border-bottom: 1px solid #e5e7eb; display: flex; align-items: center; justify-content: space-between; }
  .tmdeals-sidebar .list { padding: 8px 10px; overflow: auto; }
  .tmdeals-sidebar .item { font-size: 12px; padding: 6px 0; border-bottom: 1px dashed #e5e7eb; }
  .tmdeals-sidebar .item a { color: #2563eb; text-decoration: none; }
  .tmdeals-sidebar .footer { padding: 8px 10px; border-top: 1px solid #e5e7eb; display: flex; gap: 8px; }
  .tmdeals-sidebar .btn { background: #2563eb; color: #fff; border: none; border-radius: 4px; padding: 6px 8px; cursor: pointer; }
  .tmdeals-sidebar .btn.secondary { background: #f1f5f9; color: #111827; }

  .tmdeals-toolbar {
    position: fixed; bottom: 16px; left: 16px; z-index: 999997; background: #fff; color: #111827;
    border: 1px solid #e5e7eb; border-radius: 8px; box-shadow: 0 6px 24px rgba(0,0,0,.12);
    display: flex; gap: 8px; align-items: center; padding: 8px 10px; font-size: 12px;
  }
  .tmdeals-toolbar .btn { background: #f1f5f9; border: 1px solid #e5e7eb; border-radius: 4px; padding: 6px 8px; cursor: pointer; }
  .tmdeals-checkbox { margin-right: 6px; }

  .tmdeals-highlight {
    outline: 2px solid #22c55e !important;
    box-shadow: 0 0 0 4px rgba(34,197,94,0.15) !important;
    border-radius: 6px;
  }
  .tmdeals-drop-badge {
    display: inline-block; margin-left: 6px; padding: 0 6px; font-size: 12px; color: #16a34a; background: #dcfce7; border-radius: 12px;
  }
  .tmdeals-ctrls {
    display: inline-flex; gap: 6px; margin-left: 8px;
  }
  .tmdeals-ctrls .btn {
    font-size: 12px; padding: 2px 6px; border: 1px dashed #94a3b8; border-radius: 4px; background: #f8fafc; cursor: pointer;
  }
  .tmdeals-hidden { display: none !important; }
  `);

  // --------------------------- UI 构建 ---------------------------
  let topbarEl = null;
  let sidebarEl = null;
  let toolbarEl = null;

  function createTopbar() {
    if (topbarEl) return;
    topbarEl = document.createElement('div');
    topbarEl.className = 'tmdeals-topbar';
    topbarEl.innerHTML = `
      <strong>降价提醒</strong>
      <div class="drops"></div>
      <div class="filters">
        <label><input type="checkbox" data-action="toggle-only-drop"${filters.onlyDrop ? ' checked' : ''}> 只看降价</label>
        <label><input type="checkbox" data-action="toggle-only-freeship"${filters.onlyFreeShip ? ' checked' : ''}> 只看包邮</label>
      </div>
      <div class="threshold">
        <span>阈值¥</span>
        <input type="number" min="0" step="0.01" value="${threshold}">
        <button class="btn" data-action="save-threshold">保存</button>
      </div>
      <button class="close" title="隐藏">✕</button>
    `;
    document.body.appendChild(topbarEl);
    if (topbarHidden) {
      topbarEl.style.display = 'none';
    }
    // 事件
    const dropsBox = topbarEl.querySelector('.drops');
    const refreshDrops = () => {
      const list = (drops || []).slice(-5).reverse();
      dropsBox.innerHTML = list.map(d => {
        return `<span class="drop-item" title="${new Date(d.time).toLocaleString()}">${escapeHtml(d.title)} ¥${d.oldPrice} → ¥${d.newPrice}</span>`;
      }).join('') || '<span class="drop-item">暂无数据</span>';
    };
    refreshDrops();

    topbarEl.addEventListener('click', (e) => {
      const target = e.target;
      if (!(target instanceof Element)) return;
      const action = target.getAttribute('data-action');
      if (action === 'save-threshold') {
        const input = topbarEl.querySelector('.threshold input');
        const v = Number(input.value);
        if (!isNaN(v) && v >= 0) {
          threshold = v;
          GM_setValue(STORAGE_KEYS.THRESHOLD, threshold);
          applyAllHighlights();
        }
      } else if (action === 'toggle-only-drop') {
        // handled by change event
      } else if (action === 'toggle-only-freeship') {
        // handled by change event
      }
    });

    topbarEl.querySelector('.close').addEventListener('click', () => {
      topbarEl.style.display = 'none';
      GM_setValue(STORAGE_KEYS.HIDDEN_TOPBAR, true);
    });

    topbarEl.addEventListener('change', (e) => {
      const target = e.target;
      if (!(target instanceof HTMLInputElement)) return;
      if (target.getAttribute('data-action') === 'toggle-only-drop') {
        filters.onlyDrop = target.checked;
        GM_setValue(STORAGE_KEYS.FILTERS, filters);
        applyFilters();
      } else if (target.getAttribute('data-action') === 'toggle-only-freeship') {
        filters.onlyFreeShip = target.checked;
        GM_setValue(STORAGE_KEYS.FILTERS, filters);
        applyFilters();
      }
    });

    // 暴露刷新方法
    createTopbar.refreshDrops = refreshDrops;
  }

  function createSidebar() {
    if (sidebarEl) return;
    sidebarEl = document.createElement('div');
    sidebarEl.className = 'tmdeals-sidebar';
    sidebarEl.innerHTML = `
      <div class="header">
        <strong>今日促销清单</strong>
        <span class="count">0</span>
      </div>
      <div class="list"></div>
      <div class="footer">
        <button class="btn" data-action="export-today">导出清单CSV</button>
        <button class="btn secondary" data-action="copy-today">复制CSV</button>
      </div>
    `;
    document.body.appendChild(sidebarEl);

    sidebarEl.addEventListener('click', (e) => {
      const btn = e.target.closest('button');
      if (!btn) return;
      const action = btn.getAttribute('data-action');
      if (action === 'export-today') {
        exportTodayCSV();
      } else if (action === 'copy-today') {
        copyTodayCSV();
      }
    });

    updateSidebar();
  }

  function createToolbar() {
    if (toolbarEl) return;
    toolbarEl = document.createElement('div');
    toolbarEl.className = 'tmdeals-toolbar';
    toolbarEl.innerHTML = `
      <label><input type="checkbox" class="tmdeals-select-all"> 全选</label>
      <button class="btn" data-action="invert">反选</button>
      <button class="btn" data-action="export-selected">导出选中CSV</button>
    `;
    document.body.appendChild(toolbarEl);

    toolbarEl.addEventListener('change', (e) => {
      const checkbox = e.target.closest('.tmdeals-select-all');
      if (!checkbox) return;
      const checked = checkbox.checked;
      itemState.forEach(st => {
        st.selected = checked;
        if (st.el && st.el.isConnected) {
          const cb = st.el.querySelector('.tmdeals-item-checkbox');
          if (cb) cb.checked = checked;
        }
      });
    });
    toolbarEl.addEventListener('click', (e) => {
      const btn = e.target.closest('button');
      if (!btn) return;
      const action = btn.getAttribute('data-action');
      if (action === 'invert') {
        itemState.forEach(st => {
          st.selected = !st.selected;
          const cb = st.el?.querySelector('.tmdeals-item-checkbox');
          if (cb) cb.checked = !!st.selected;
        });
      } else if (action === 'export-selected') {
        exportSelectedCSV();
      }
    });
  }

  function updateSidebar() {
    if (!sidebarEl) return;
    const listEl = sidebarEl.querySelector('.list');
    const countEl = sidebarEl.querySelector('.count');
    const items = Array.from(todayHighlightSet)
      .map(id => itemState.get(id))
      .filter(Boolean);

    countEl.textContent = String(items.length);
    listEl.innerHTML = items.map(st => {
      const price = isFinite(st.price) ? `¥${st.price}` : '—';
      const flags = [
        st.isDrop ? '降价' : '',
        hasFullReduction(st.promo) ? '满减' : '',
        (st.coupon ? '券' : ''),
        hasFreeShipping(st.shipping) ? '包邮' : ''
      ].filter(Boolean).join('·');
      return `<div class="item">
        <a href="${st.url || 'javascript:'}" target="_blank" rel="noreferrer">${escapeHtml(st.title || st.id)}</a>
        <div>${price} ${flags ? `<span style="color:#16a34a">[${flags}]</span>` : ''}</div>
      </div>`;
    }).join('') || '<div class="item">暂无符合条件的商品</div>';
  }

  function escapeHtml(s) {
    return String(s || '')
      .replace(/&/g, '&amp;').replace(/</g, '&lt;')
      .replace(/>/g, '&gt;').replace(/"/g, '&quot;')
      .replace(/'/g, '&#39;');
  }

  // --------------------------- 页面分析 ---------------------------
  function detectPageType() {
    // 判断是否为详情页(存在明显详情根节点或URL包含特征)
    const isDetail = !!document.querySelector(SELECTORS.detail.root) ||
                     /\/item\/|\/detail/ig.test(location.pathname);
    return { isDetail };
  }

  // --------------------------- 处理逻辑 ---------------------------
  const scheduleScan = debounce(() => {
    if (detectPageType().isDetail) {
      processDetailPage();
    } else {
      processListPage();
    }
  }, 120);

  function processListPage() {
    const container = $(SELECTORS.list.container) || document.body;
    const items = $$(SELECTORS.list.item, container);
    if (!items.length) return;
    items.forEach(el => processOneItemInList(el));
    applyFilters();
  }

  function processDetailPage() {
    const root = $(SELECTORS.detail.root) || document.body;
    // 用详情根节点作为单个“item”
    processOneDetail(root);
  }

  function extractFromListItem(el) {
    const idNode = el.querySelector(SELECTORS.list.id);
    const linkEl = el.querySelector(SELECTORS.list.link);
    const titleEl = el.querySelector(SELECTORS.list.title);
    const priceEl = el.querySelector(SELECTORS.list.price);
    const stockEl = el.querySelector(SELECTORS.list.stock);
    const couponEl = el.querySelector(SELECTORS.list.coupon);
    const promoEl = el.querySelector(SELECTORS.list.promo);
    const shippingEl = el.querySelector(SELECTORS.list.shipping);

    const url = linkEl ? linkEl.href : (location.origin + location.pathname);
    let id = idNode?.getAttribute('data-sku') || idNode?.getAttribute('data-id');
    if (!id) {
      // 从URL或标题派生ID
      const base = (url || '') + '|' + text(titleEl);
      id = hashId(base);
    }
    const title = text(titleEl) || (linkEl ? text(linkEl) : '');
    const price = parsePrice(text(priceEl));
    const stock = text(stockEl);
    const coupon = text(couponEl);
    const promo = text(promoEl);
    const shipping = text(shippingEl);

    return { id, url, title, price, stock, coupon, promo, shipping, priceEl, stockEl };
  }

  function extractFromDetail(root) {
    const idNode = root.querySelector(SELECTORS.detail.id);
    const titleEl = root.querySelector(SELECTORS.detail.title);
    const priceEl = root.querySelector(SELECTORS.detail.price);
    const stockEl = root.querySelector(SELECTORS.detail.stock);
    const couponEl = root.querySelector(SELECTORS.detail.coupon);
    const promoEl = root.querySelector(SELECTORS.detail.promo);
    const shippingEl = root.querySelector(SELECTORS.detail.shipping);

    const url = location.href;
    let id = idNode?.getAttribute('content') || idNode?.getAttribute('data-sku') || idNode?.getAttribute('data-id');
    if (!id) {
      const base = (url || '') + '|' + text(titleEl);
      id = hashId(base);
    }
    const title = text(titleEl);
    const price = parsePrice(text(priceEl));
    const stock = text(stockEl);
    const coupon = text(couponEl);
    const promo = text(promoEl);
    const shipping = text(shippingEl);

    return { id, url, title, price, stock, coupon, promo, shipping, priceEl, stockEl };
  }

  function ensureItemControls(el, id) {
    if (el.querySelector('.tmdeals-item-checkbox')) return;
    // 插入勾选框与按钮(尽量靠近标题或价格)
    const ctrlWrap = document.createElement('span');
    ctrlWrap.className = 'tmdeals-ctrls';
    ctrlWrap.innerHTML = `
      <label><input type="checkbox" class="tmdeals-item-checkbox tmdeals-checkbox"></label>
      <button class="btn" data-action="monitor">监控</button>
      <button class="btn" data-action="fav">收藏</button>
    `;

    // 优先放在标题附近
    let anchor = el.querySelector(SELECTORS.list.title) || el.querySelector(SELECTORS.detail.title) || el;
    anchor.appendChild(ctrlWrap);

    ctrlWrap.addEventListener('click', (e) => {
      const btn = e.target.closest('button');
      if (!btn) return;
      const action = btn.getAttribute('data-action');
      if (action === 'monitor') {
        const w = watchlist[id] || {};
        w.threshold = threshold;
        watchlist[id] = w;
        GM_setValue(STORAGE_KEYS.WATCHLIST, watchlist);
        btn.textContent = '已监控';
      } else if (action === 'fav') {
        const w = watchlist[id] || {};
        w.favorite = true;
        watchlist[id] = w;
        GM_setValue(STORAGE_KEYS.WATCHLIST, watchlist);
        btn.textContent = '已收藏';
      }
    });

    const cb = ctrlWrap.querySelector('.tmdeals-item-checkbox');
    cb.addEventListener('change', (e) => {
      const st = itemState.get(id);
      if (st) st.selected = cb.checked;
    });
  }

  function processOneItemInList(el) {
    if (!el || el.dataset.tmdealsProcessed) return;
    const data = extractFromListItem(el);
    if (!data.id) return;

    // 保存/更新状态
    const prev = itemState.get(data.id);
    const st = Object.assign(prev || {}, data, { el });
    itemState.set(data.id, st);

    // 价格/库存变动检测
    detectChangeAndMark(st);

    // 高亮策略(价格<=阈值 或 存在满减)
    const highlight = shouldHighlight(st);
    applyHighlight(st, highlight);

    // 注入控件
    ensureItemControls(el, data.id);

    // 监听价格与库存节点变动(精确到文本变更)
    attachNodeObservers(st);

    el.dataset.tmdealsProcessed = '1';
  }

  function processOneDetail(root) {
    if (root.dataset.tmdealsProcessed) return;
    const data = extractFromDetail(root);
    if (!data.id) return;

    const prev = itemState.get(data.id);
    const st = Object.assign(prev || {}, data, { el: root });
    itemState.set(data.id, st);

    detectChangeAndMark(st);
    const highlight = shouldHighlight(st);
    applyHighlight(st, highlight);
    ensureItemControls(root, data.id);
    attachNodeObservers(st);

    root.dataset.tmdealsProcessed = '1';
  }

  function shouldHighlight(st) {
    const lowPrice = isFinite(st.price) && st.price >= 0 && st.price <= threshold;
    const fullReduction = hasFullReduction(st.promo);
    return !!(lowPrice || fullReduction);
  }

  function applyHighlight(st, highlight) {
    if (!st || !st.el) return;
    st.isHighlighted = !!highlight;

    if (st.isHighlighted) {
      st.el.classList.add('tmdeals-highlight');
      todayHighlightSet.add(st.id);
    } else {
      st.el.classList.remove('tmdeals-highlight');
      todayHighlightSet.delete(st.id);
    }
    updateSidebar();
  }

  function detectChangeAndMark(st) {
    // 降价检测
    const lp = lastPrices[st.id];
    if (lp && isFinite(lp.price) && isFinite(st.price) && st.price < lp.price) {
      st.isDrop = true;
      addDropRecord(st, lp.price, st.price);
      // 在标题后追加“降价”徽标(若无则添加)
      const titleEl = st.el.querySelector(SELECTORS.list.title) || st.el.querySelector(SELECTORS.detail.title);
      if (titleEl && !titleEl.querySelector('.tmdeals-drop-badge')) {
        const b = document.createElement('span');
        b.className = 'tmdeals-drop-badge';
        b.textContent = '降价';
        titleEl.appendChild(b);
      }
    } else {
      st.isDrop = false;
    }

    // 保存最新价格/库存
    lastPrices[st.id] = { price: st.price, stock: st.stock, t: nowTs() };
    GM_setValue(STORAGE_KEYS.LAST_PRICES, lastPrices);
  }

  function addDropRecord(st, oldPrice, newPrice) {
    const record = {
      id: st.id,
      title: st.title || st.id,
      oldPrice: Number(oldPrice.toFixed ? oldPrice.toFixed(2) : oldPrice),
      newPrice: Number(newPrice.toFixed ? newPrice.toFixed(2) : newPrice),
      time: Date.now(),
      url: st.url || location.href,
    };
    drops.push(record);
    if (drops.length > 50) drops.splice(0, drops.length - 50);
    GM_setValue(STORAGE_KEYS.DROPS, drops);
    if (createTopbar.refreshDrops) createTopbar.refreshDrops();
  }

  function attachNodeObservers(st) {
    // 避免重复绑定
    if (itemObservers.has(st.id)) return;
    const obs = new MutationObserver(debounce(() => {
      // 重新抽取关键字段(限当前元素)
      if (!st.el || !st.el.isConnected) {
        // 断开观察
        const o = itemObservers.get(st.id);
        if (o) o.disconnect();
        itemObservers.delete(st.id);
        return;
      }
      const isDetail = detectPageType().isDetail && st.el.matches(SELECTORS.detail.root + ', body, html, .detail');
      const data = isDetail ? extractFromDetail(st.el) : extractFromListItem(st.el);
      Object.assign(st, data);

      detectChangeAndMark(st);
      const highlight = shouldHighlight(st);
      applyHighlight(st, highlight);
      applyFilters(); // 受“只看降价/包邮”影响
    }, 120));

    const targets = [st.priceEl, st.stockEl].filter(Boolean);
    if (!targets.length) return;
    targets.forEach(t => {
      obs.observe(t, { characterData: true, childList: true, subtree: true });
    });
    itemObservers.set(st.id, obs);
  }

  // --------------------------- 筛选与应用 ---------------------------
  function applyFilters() {
    itemState.forEach((st, id) => {
      if (!st.el) return;
      let visible = true;
      if (filters.onlyDrop && !st.isDrop) visible = false;
      if (filters.onlyFreeShip && !hasFreeShipping(st.shipping)) visible = false;
      st.el.classList.toggle('tmdeals-hidden', !visible);
    });
  }

  function applyAllHighlights() {
    itemState.forEach((st) => {
      const highlight = shouldHighlight(st);
      applyHighlight(st, highlight);
    });
    applyFilters();
  }

  // --------------------------- CSV 导出 ---------------------------
  function buildCSVRows(states) {
    const header = ['ID', '标题', '价格', '库存', '促销', '券', '运费', '是否降价', 'URL', '时间'];
    const rows = [header];
    states.forEach(st => {
      rows.push([
        st.id,
        st.title || '',
        isFinite(st.price) ? st.price : '',
        st.stock || '',
        st.promo || '',
        st.coupon || '',
        st.shipping || '',
        st.isDrop ? '是' : '否',
        st.url || '',
        new Date().toLocaleString()
      ]);
    });
    return rows;
  }

  function exportSelectedCSV() {
    const selected = Array.from(itemState.values()).filter(st => st.selected);
    if (!selected.length) {
      alert('请先勾选要导出的商品。');
      return;
    }
    const rows = buildCSVRows(selected);
    const csv = toCSV(rows);
    download(`选中商品_${formatDate()}.csv`, csv);
  }

  function exportTodayCSV() {
    const items = Array.from(todayHighlightSet).map(id => itemState.get(id)).filter(Boolean);
    if (!items.length) {
      alert('今日促销清单为空。');
      return;
    }
    const rows = buildCSVRows(items);
    const csv = toCSV(rows);
    download(`今日促销清单_${formatDate()}.csv`, csv);
  }

  function copyTodayCSV() {
    const items = Array.from(todayHighlightSet).map(id => itemState.get(id)).filter(Boolean);
    const rows = buildCSVRows(items);
    const csv = toCSV(rows);
    navigator.clipboard.writeText(csv).then(() => {
      alert('已复制CSV到剪贴板。');
    }).catch(() => {
      alert('复制失败,请检查浏览器权限。');
    });
  }

  function formatDate(d = new Date()) {
    const p = (n) => String(n).padStart(2, '0');
    return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}_${p(d.getHours())}${p(d.getMinutes())}${p(d.getSeconds())}`;
    }

  // --------------------------- 观察与滚动节流 ---------------------------
  const onScroll = throttle(() => {
    // 预留:可在此添加懒处理逻辑(如按需处理可视区域新元素)
    // 当前仅维持UI稳定,无额外操作,避免性能负担
  }, 120);

  function observeContainerMutations() {
    const root = detectPageType().isDetail ? (document.querySelector(SELECTORS.detail.root) || document.body)
                                           : (document.querySelector(SELECTORS.list.container) || document.body);
    const obs = new MutationObserver((muts) => {
      let needScan = false;
      for (const m of muts) {
        if (m.addedNodes && m.addedNodes.length) {
          needScan = true; break;
        }
      }
      if (needScan) scheduleScan();
    });
    obs.observe(root, { childList: true, subtree: true });
  }

  // --------------------------- 菜单命令 ---------------------------
  try {
    GM_registerMenuCommand('设置阈值', () => {
      const v = prompt('请输入价格阈值(元):', String(threshold));
      if (v == null) return;
      const num = Number(v);
      if (!isNaN(num) && num >= 0) {
        threshold = num;
        GM_setValue(STORAGE_KEYS.THRESHOLD, threshold);
        // 顶栏输入也更新
        const input = topbarEl?.querySelector('.threshold input');
        if (input) input.value = String(threshold);
        applyAllHighlights();
      } else {
        alert('输入无效,请输入非负数字。');
      }
    });
    GM_registerMenuCommand('显示/隐藏 顶部降价栏', () => {
      topbarHidden = !topbarHidden;
      GM_setValue(STORAGE_KEYS.HIDDEN_TOPBAR, topbarHidden);
      if (topbarEl) topbarEl.style.display = topbarHidden ? 'none' : '';
    });
  } catch (e) {
    // 某些环境不支持菜单
  }

  // --------------------------- 启动 ---------------------------
  function init() {
    createTopbar();
    createSidebar();
    createToolbar();
    scheduleScan();
    observeContainerMutations();
    window.addEventListener('scroll', onScroll, { passive: true });
  }

  // 启动
  init();

})();

使用说明

  • 安装步骤:
    1. 安装 Tampermonkey 扩展(Chrome Web Store 或 Edge Add-ons)。
    2. 新建用户脚本,粘贴“完整代码”,保存。
    3. 打开 https://shop.example.cn/deals 或任意商品详情页(同域),脚本会自动运行。
  • 功能说明:
    • 顶部“降价提醒栏”:展示最近5条降价记录;可设置全局“价格阈值”,并开启“只看降价/只看包邮”快速筛选。可点击“✕”隐藏(可在脚本菜单再次打开)。
    • 右侧“今日促销清单”:收集“满足阈值”或“出现满减”的商品,实时更新;支持“导出CSV”“复制CSV”。
    • 列表/详情商品卡片:
      • 自动注入勾选框与“监控”“收藏”按钮。“监控”将商品按当前阈值加入监控列表(本地存储);“收藏”标记收藏。
      • 检测到“降价”时在标题后显示徽标,并记录到“降价提醒栏”。
    • 底部工具条:支持“全选/反选/导出选中CSV”。
    • 监听内容变动:使用 MutationObserver 追踪页面动态加载和价格/库存文本变动,智能更新高亮与清单。
  • 注意事项:
    • 仅在 Chrome/Edge 生效;其他浏览器自动不启用。
    • 本脚本只在当前页面内工作,不会发起跨域网络请求,不采集或上传隐私数据。
    • 若目标网站结构不同,请按“自定义建议”调整选择器。
    • 为保证性能:已对滚动监听进行节流,DOM变动处理采用去抖。大量商品时仍建议适度分页查看。

自定义建议

  • 参数调整:
    • 全局价格阈值:顶部栏输入后点击“保存”,或使用脚本菜单“设置阈值”。
    • 选择器修改:根据实际页面结构调整 SELECTORS(list/detail)中的 CSS 选择器,以便正确读取标题、价格、库存、促销、券与包邮标签。
  • 功能扩展:
    • 增加“到货/库存恢复”提醒:在 detectChangeAndMark 中加入库存从“无货/缺货”到“有货”的检测逻辑与提醒记录。
    • 自定义高亮条件:例如支持“含券金额≥X”或“促销标签包含某关键字”时高亮。
    • 监控阈值按商品单独设置:为每个已监控商品提供阈值编辑弹窗。
  • 故障排除:
    • 无法识别价格/标题:检查开发者工具中的元素结构,调整 SELECTORS 中对应字段。
    • “只看包邮”无效:确认页面是否存在“包邮/免邮/免运费”等字样或特定标签元素,更新 SELECTORS.list.shipping。
    • CSV 导出乱码:已添加 BOM;若仍异常,尝试使用 Excel“数据-自文本/CSV”导入或改用 LibreOffice 打开。
    • 顶部栏不显示:检查是否通过“显示/隐藏 顶部降价栏”关闭;或清除对应 GM 存储键后刷新。

示例详情

适用用户

电商运营经理

快速搭建价格监测与优惠提醒脚本,自动高亮重点商品信息,整理促销清单,减少逐页查看与重复点击。

新媒体与社区运营

一键过滤关键词与噪音内容,整理公开评论要点,生成互动周报草案,配合快捷回复提升日常运营效率。

产品经理与增长团队

无需改动代码即可做页面微改与可用性验证,高亮关键路径、标注交互瓶颈,为A/B决策提供更直观的现场证据。

解决的问题

用一条高效提示词,帮你把“想做的网页自动化”快速变成“可运行的油猴脚本”。通过需求-分析-设计-生成-审查-优化的全流程打包输出,让非技术用户也能在几分钟内得到可用脚本,开发者则拥有可深度定制的工程化模板。核心价值:1) 高成功率:采用分步思考与逻辑校验,减少脚本失效与反复调试;2) 更安全:内置合规与安全检查,避免违规与隐私风险;3) 真易用:结构清晰、注释完整、附安装与使用指南;4) 更稳定:适配动态元素、异常处理、浏览器兼容;5) 可扩展:可在价格监控、社媒自动化、内容过滤、数据提取、学习场景等快速复用。即刻试用,3分钟拿到可运行脚本,显著缩短从想法到上线的距离,提升效率并降低成本。

特征总结

一键生成可直接运行的浏览器脚本,自动补全注释与结构,让你快速落地自动化想法。
根据目标网站特性智能识别关键元素与交互流程,减少手动定位与反复调试时间。
内置安全校验与合规守则,自动规避敏感行为与风险调用,保护账号与网站正常运行。
支持动态内容与延迟加载场景,自动等待、重试与异常处理,保障脚本稳定可用。
适配多款脚本管理器与主流浏览器,一次生成,多端使用,团队共享更省心。
提供任务分步设计与清晰模块划分,便于后期扩展、复用与维护,持续降低人力投入。
自动生成使用指南与自定义参数位,少改几处即可切换站点或规则,快速复用脚本资产。
针对营销、运营、学习等场景预设常用范式,如价格监控、内容过滤、界面美化等即装即用。
提供错误提示与调试日志输出,新手也能定位问题,专业用户可快速优化性能。

如何使用购买的提示词模板

1. 直接在外部 Chat 应用中使用

将模板生成的提示词复制粘贴到您常用的 Chat 应用(如 ChatGPT、Claude 等),即可直接对话使用,无需额外开发。适合个人快速体验和轻量使用场景。

2. 发布为 API 接口调用

把提示词模板转化为 API,您的程序可任意修改模板参数,通过接口直接调用,轻松实现自动化与批量处理。适合开发者集成与业务系统嵌入。

3. 在 MCP Client 中配置使用

在 MCP client 中配置对应的 server 地址,让您的 AI 应用自动调用提示词模板。适合高级用户和团队协作,让提示词在不同 AI 工具间无缝衔接。

AI 提示词价格
¥10.00元 ¥20.00元
立减 50%
还剩 00:00:00
先用后买,用好了再付款,超安全!

您购买后可以获得什么

获得完整提示词模板
- 共 817 tokens
- 3 个可调节参数
{ 网站地址 } { 功能描述 } { 特殊要求 }
获得社区贡献内容的使用权
- 精选社区优质案例,助您快速上手提示词
限时免费

不要错过!

免费获取高级提示词-优惠即将到期

17
:
23
小时
:
59
分钟
:
59