Greasy Fork

Greasy Fork is available in English.

美团券搜索

注意需要请求手机页面,先登录账户后,获取列表

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         美团券搜索
// @license      MIT
// @version      1.8
// @description  注意需要请求手机页面,先登录账户后,获取列表
// @author       Bingo95
// @match        https://offsiteact.meituan.com/web/hoae/collection_waimai_v8/index.html*
// @grant        none
// @namespace http://greasyfork.icu/users/1312821
// ==/UserScript==

(function () {
  'use strict';

  // 创建UI元素
  function createUI() {
    // 创建搜索框
    const searchBox = document.createElement('input');
    searchBox.type = 'search';
    searchBox.placeholder = '输入商家名称搜索...';
    searchBox.style.cssText = `
            position: fixed;
            bottom: 20px;
            left: 50%;
            transform: translateX(-50%);
            width: 80%;
            height: 40px;
            padding: 5px 15px;
            border: 1px solid #ccc;
            border-radius: 20px;
            z-index: 9999;
            background: rgba(255, 255, 255, 0.95);
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
        `;

    // 创建加载按钮
    const loadButton = document.createElement('button');
    loadButton.textContent = '加载更多';
    loadButton.style.cssText = `
            position: fixed;
            top: 40px;
            right: 20px;
            padding: 0 20px;
            height: 40px;
            border: none;
            border-radius: 20px;
            background: #FFC107;
            color: #fff;
            font-size: 16px;
            cursor: pointer;
            z-index: 9999;
            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
        `;

    // 创建券面值排序按钮
    const sortButton = document.createElement('button');
    sortButton.textContent = '券面值排序';
    sortButton.style.cssText = `
            position: fixed;
            top: 90px;
            right: 20px;
            padding: 0 20px;
            height: 40px;
            border: none;
            border-radius: 20px;
            background: #4CAF50;
            color: #fff;
            font-size: 16px;
            cursor: pointer;
            z-index: 9999;
            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
        `;

    // 创建计数器
    const counter = document.createElement('div');
    counter.id = 'shop-counter';
    counter.style.cssText = `
            position: fixed;
            top: 40px;
            left: 20px;
            padding: 0 15px;
            height: 40px;
            line-height: 40px;
            font-size: 14px;
            color: #666;
            background: rgba(255, 255, 255, 0.95);
            border-radius: 20px;
            z-index: 9999;
            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
        `;

    document.body.appendChild(searchBox);
    document.body.appendChild(loadButton);
    document.body.appendChild(sortButton);
    document.body.appendChild(counter);

    return { searchBox, loadButton, sortButton, counter };
  }

  // 加载更多数据
  let loadInterval = null;
  let isLoading = false;
  function loadMoreData(loadButton) {
    if (isLoading) {
      // 停止加载
      clearInterval(loadInterval);
      isLoading = false;
      loadButton.textContent = '继续加载';
      loadButton.style.background = '#FFC107';
      loadButton.disabled = false;
      return;
    }
    // 开始加载
    isLoading = true;
    loadButton.textContent = '停止加载';
    loadButton.style.background = '#f44336';
    loadButton.disabled = false;
    let lastCount = 0;
    let sameCountTimes = 0;
    loadInterval = setInterval(() => {
      const scrollHeight = document.documentElement.scrollHeight;
      window.scrollTo(0, scrollHeight);
      const currentCount = document.querySelectorAll(
        '.index-module__poiCardContainer___L28Xd'
      ).length;
      // 如果连续3次数量没变化,认为加载完成
      if (currentCount === lastCount) {
        sameCountTimes++;
        if (sameCountTimes >= 3) {
          clearInterval(loadInterval);
          isLoading = false;
          loadButton.textContent = '加载完成';
          loadButton.style.background = '#ccc';
          loadButton.disabled = false;
        }
      } else {
        sameCountTimes = 0;
        lastCount = currentCount;
      }
    }, 1000);
  }
  // 搜索商家
  function searchShops(keyword) {
    const shops = document.querySelectorAll(
      '.index-module__poiCardContainer___L28Xd'
    );
    let visibleCount = 0;

    shops.forEach((shop) => {
      const name = shop.querySelector('.name').textContent.trim();
      if (keyword && !name.toLowerCase().includes(keyword.toLowerCase())) {
        shop.style.display = 'none';
      } else {
        shop.style.display = 'block';
        visibleCount++;
      }
    });

    // 更新计数器
    const counter = document.getElementById('shop-counter');
    if (counter) {
      counter.textContent = `${visibleCount}/${shops.length}`;
    }
  }

  // 监听列表是否加载完成
  function observeListLoading(loadButton) {
    const observer = new MutationObserver((mutations) => {
      mutations.forEach((mutation) => {
        const nodes = Array.from(mutation.addedNodes);
        nodes.forEach((node) => {
          if (node.nodeType === 1 && node.textContent.includes('没有更多了')) {
            loadButton.textContent = '已加载完成';
            loadButton.style.background = '#ccc';
            loadButton.disabled = false;
            observer.disconnect();
            console.log('列表加载完成');
          }
        });
      });
    });

    observer.observe(document.body, {
      childList: true,
      subtree: true,
    });
  }

  // 券面值排序功能
  function sortCouponsByValue() {
    // 获取所有商家卡片
    const shopCards = Array.from(
      document.querySelectorAll('.index-module__poiCardContainer___L28Xd')
    );
    if (shopCards.length === 0) return;

    // 获取父容器
    const parent = shopCards[0].parentNode;

    // 提取每个卡片下最大券面值
    const cardWithValue = shopCards.map((card) => {
      // 查找所有券
      const coupons = Array.from(card.querySelectorAll('.poiCoupon .value'));
      // 提取所有券面值
      const values = coupons.map((v) => parseFloat(v.textContent.trim()) || 0);
      // 取最大值
      const maxValue = values.length ? Math.max(...values) : 0;
      return { card, maxValue };
    });

    // 按最大券面值降序排序
    cardWithValue.sort((a, b) => b.maxValue - a.maxValue);

    // 重新插入到父容器
    cardWithValue.forEach(({ card }) => {
      parent.appendChild(card);
    });
  }

  // 初始化
  function init() {
    const { searchBox, loadButton, sortButton, counter } = createUI();

    // 添加搜索事件
    searchBox.addEventListener('input', (e) => {
      searchShops(e.target.value.trim());
    });

    // 添加加载按钮事件
    loadButton.addEventListener('click', function () {
      loadMoreData(loadButton);
    });

    // 添加券面值排序按钮事件
    sortButton.addEventListener('click', sortCouponsByValue);

    // 监听列表加载状态
    observeListLoading(loadButton);

    // 初始计数
    searchShops('');
  }

  // 等待页面加载完成后执行
  if (document.readyState === 'complete') {
    init();
  } else {
    window.addEventListener('load', init);
  }
})();