Greasy Fork

Greasy Fork is available in English.

【抖店】价格监控

根据指定规则高亮显示价格

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         【抖店】价格监控
// @namespace    http://tampermonkey.net/
// @version      0.9
// @description  根据指定规则高亮显示价格
// @author       [email protected]
// @match        https://fxg.jinritemai.com/ffa/morder/order/list?btm_ppre=*
// @grant        GM_getValue
// @grant        GM_setValue
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    // 注入CSS样式
    const styleSheet = document.createElement('style');
    styleSheet.textContent = `
        .price-monitor-container {
            position: fixed;
            top: calc(5vh + 20px); /* 向下移动3%的视窗高度 */
            right: 20px;
            background: rgba(255, 255, 255, 0.95);
            padding: 20px;
            border-radius: 12px;
            box-shadow: 0 4px 24px rgba(0, 0, 0, 0.1);
            backdrop-filter: blur(10px);
            z-index: 9999;
            max-height: 80vh;
            overflow-y: auto;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            min-width: 320px;
            transition: all 0.3s ease;
        }

        .price-monitor-container.collapsed {
            min-width: auto;
            padding: 12px;
            background: rgba(144, 238, 144, 0.3); /* 浅绿色背景,带透明度 */
            backdrop-filter: blur(10px);
            border: 1px solid rgba(144, 238, 144, 0.5); /* 浅绿色边框 */
            box-shadow: 0 2px 12px rgba(144, 238, 144, 0.2); /* 浅绿色阴影 */
        }

        .price-monitor-container.collapsed:hover {
            background: rgba(144, 238, 144, 0.4); /* 悬停时稍微加深 */
        }

        .price-monitor-container.collapsed .price-monitor-content {
            display: none;
        }

        .price-monitor-container.collapsed .price-monitor-status {
            color: #2e8b57; /* 折叠时的文字颜色为深绿色 */
        }

        .price-monitor-toggle {
            position: absolute;
            top: 12px;
            right: 12px;
            width: 24px;
            height: 24px;
            border: none;
            background: transparent;
            cursor: pointer;
            padding: 0;
            display: flex;
            align-items: center;
            justify-content: center;
            color: #1d1d1f;
            opacity: 0.6;
            transition: all 0.2s ease;
        }

        .price-monitor-toggle:hover {
            opacity: 1;
        }

        .price-monitor-toggle svg {
            width: 16px;
            height: 16px;
            transition: transform 0.3s ease;
        }

        .collapsed .price-monitor-toggle svg {
            transform: rotate(180deg);
        }

        .price-monitor-status {
            display: flex;
            align-items: center;
            font-size: 14px;
            color: #1d1d1f;
            margin-right: 24px;
        }

        .collapsed .price-monitor-status {
            margin: 0;
        }

        .price-monitor-title {
            margin: 0 0 20px 0;
            font-size: 18px;
            font-weight: 600;
            color: #1d1d1f;
        }

        .price-monitor-item {
            margin-bottom: 15px;
            padding: 12px;
            background: rgba(0, 0, 0, 0.02);
            border-radius: 8px;
            transition: all 0.3s ease;
        }

        .price-monitor-item:hover {
            background: rgba(0, 0, 0, 0.04);
        }

        .price-monitor-input {
            width: 100%;
            padding: 8px 12px;
            margin: 4px 0;
            border: 1px solid #d2d2d7;
            border-radius: 6px;
            font-size: 14px;
            transition: all 0.3s ease;
        }

        .price-monitor-input:focus {
            outline: none;
            border-color: #0071e3;
            box-shadow: 0 0 0 3px rgba(0, 113, 227, 0.1);
        }

        .price-monitor-button {
            background: #0071e3;
            color: white;
            border: none;
            padding: 8px 16px;
            border-radius: 6px;
            font-size: 14px;
            font-weight: 500;
            cursor: pointer;
            transition: all 0.3s ease;
        }

        .price-monitor-button:hover {
            background: #0077ED;
        }

        .price-monitor-button.secondary {
            background: #f5f5f7;
            color: #1d1d1f;
            margin-right: 8px;
        }

        .price-monitor-button.secondary:hover {
            background: #e8e8ed;
        }

        .price-monitor-button.delete {
            background: #ff3b30;
            padding: 4px 8px;
            font-size: 12px;
        }

        .price-monitor-button.delete:hover {
            background: #ff453a;
        }

        .price-monitor-actions {
            display: flex;
            justify-content: flex-end;
            margin-top: 15px;
            padding-top: 15px;
            border-top: 1px solid #d2d2d7;
        }
    `;
    document.head.appendChild(styleSheet);

    // 默认配置
    const defaultConfig = {
        items: [
            { name: '乌黑芝麻230克*8瓶x1', expectedPrice: 119.8 }
        ]
    };

    // 从存储中获取配置
    let config = GM_getValue('priceConfig', defaultConfig);

    // 创建配置界面
    function createConfigUI() {
        const container = document.createElement('div');
        container.className = 'price-monitor-container';

        // 创建内容容器
        const content = document.createElement('div');
        content.className = 'price-monitor-content';

        // 创建状态指示器
        const status = document.createElement('div');
        status.className = 'price-monitor-status';
        status.textContent = '价格监控已启用';

        // 创建折叠按钮
        const toggleButton = document.createElement('button');
        toggleButton.className = 'price-monitor-toggle';
        toggleButton.innerHTML = `
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                <path d="M18 15l-6-6-6 6"/>
            </svg>
        `;

        // 添加折叠/展开功能
        let isCollapsed = GM_getValue('uiCollapsed', false);
        if (isCollapsed) {
            container.classList.add('collapsed');
        }

        toggleButton.onclick = () => {
            isCollapsed = !isCollapsed;
            container.classList.toggle('collapsed');
            GM_setValue('uiCollapsed', isCollapsed);
        };

        // 创建标题
        const title = document.createElement('h3');
        title.className = 'price-monitor-title';
        title.textContent = '价格监控配置';
        content.appendChild(title);

        // 创建配置列表
        const configList = document.createElement('div');
        configList.id = 'configList';
        content.appendChild(configList);

        // 创建操作按钮容器
        const actions = document.createElement('div');
        actions.className = 'price-monitor-actions';

        // 添加按钮
        const addButton = document.createElement('button');
        addButton.textContent = '添加商品';
        addButton.className = 'price-monitor-button secondary';
        addButton.onclick = addConfigItem;
        actions.appendChild(addButton);

        // 保存按钮
        const saveButton = document.createElement('button');
        saveButton.textContent = '保存配置';
        saveButton.className = 'price-monitor-button';
        saveButton.onclick = saveConfig;
        actions.appendChild(saveButton);

        content.appendChild(actions);

        // 组装界面
        container.appendChild(status);
        container.appendChild(toggleButton);
        container.appendChild(content);
        document.body.appendChild(container);

        // 渲染现有配置
        renderConfigItems();

        // 更新状态显示
        function updateStatus() {
            const activeItems = config.items.length;
            status.textContent = `监控中: ${activeItems}个商品`;
        }
        updateStatus();
    }

    // 渲染配置项
    function renderConfigItems() {
        const configList = document.getElementById('configList');
        configList.innerHTML = '';

        config.items.forEach((item, index) => {
            const itemDiv = document.createElement('div');
            itemDiv.className = 'price-monitor-item';
            itemDiv.innerHTML = `
                <div>
                    <input type="text" placeholder="商品名称" value="${item.name}"
                           class="price-monitor-input item-name" data-index="${index}">
                    <div style="display: flex; gap: 8px; margin-top: 8px;">
                        <input type="number" placeholder="预期价格" value="${item.expectedPrice}"
                               class="price-monitor-input item-price" data-index="${index}" style="flex: 1;">
                        <button class="price-monitor-button delete" onclick="this.parentElement.parentElement.parentElement.remove()">删除</button>
                    </div>
                </div>
            `;
            configList.appendChild(itemDiv);
        });
    }

    // 添加配置项
    function addConfigItem() {
        config.items.push({ name: '', expectedPrice: 0 });
        renderConfigItems();
    }

    // 保存配置
    function saveConfig() {
        const newConfig = { items: [] };
        const names = document.querySelectorAll('.item-name');
        const prices = document.querySelectorAll('.item-price');

        for (let i = 0; i < names.length; i++) {
            newConfig.items.push({
                name: names[i].value,
                expectedPrice: parseFloat(prices[i].value)
            });
        }

        config = newConfig;
        GM_setValue('priceConfig', config);
        alert('配置已保存');
    }

    // 获取价格样式
    function getPriceStyle(currentPrice, expectedPrice) {
        if (currentPrice === expectedPrice) {
            return { backgroundColor: '#90EE90', color: '#006400' }; // 绿色底色
        } else {
            return { backgroundColor: '#FFB6C1', color: '#8B0000' }; // 深红色底色
        }
    }

    // 检查元素
    function checkElements() {
        const containers = document.querySelectorAll('tr');
        const results = [];

        containers.forEach(container => {
            const relativeElement = container.querySelector('.index_ellipsis__MJ7fR');
            const priceElement = container.querySelector('.index_amountWrap___vXdu.index_marked__FC_BX');

            if (relativeElement && priceElement) {
                const relativeText = relativeElement.textContent.trim();
                const priceText = priceElement.textContent.trim();
                const priceMatch = priceText.match(/([\d,.]+)/);

                if (priceMatch && priceMatch[1]) {
                    const currentPrice = parseFloat(priceMatch[1].replace(/,/g, ''));

                    // 检查是否匹配配置的商品
                    const matchedItem = config.items.find(item => relativeText.includes(item.name));
                    if (matchedItem) {
                        const style = getPriceStyle(currentPrice, matchedItem.expectedPrice);
                        priceElement.style.backgroundColor = style.backgroundColor;
                        priceElement.style.color = style.color;

                        results.push({
                            name: relativeText,
                            price: currentPrice,
                            expectedPrice: matchedItem.expectedPrice,
                            isExpected: currentPrice === matchedItem.expectedPrice
                        });
                    }
                }
            }
        });

        // 输出结果
        if (results.length > 0) {
            console.group('价格监控结果');
            results.forEach(result => {
                const priceInfo = `当前价格: ¥${result.price.toFixed(2)} / 预期价格: ¥${result.expectedPrice.toFixed(2)}`;
                console.log(
                    `%c${result.name}%c${priceInfo}`,
                    'color: #333; font-weight: normal;',
                    `color: ${result.isExpected ? '#006400' : '#8B0000'}; font-weight: bold;`
                );
            });
            console.groupEnd();
        }
    }

    // 创建配置界面
    createConfigUI();

    // 定时检查
    setInterval(checkElements, 5000);
})();