// ==UserScript== // @name 店小蜜价格助手 // @namespace http://tampermonkey.net/ // @version 1.8.0 // @description 5倍价格 // @author Rayu // @match https://www.dianxiaomi.com/web/shopeeSite/edit* // @grant none // @license MIT // @downloadURL none // ==/UserScript== (function() { 'use strict'; // --- 配置项 --- const HIGHLIGHT_COLOR = 'yellow'; const MULTIPLE_THRESHOLD = 5; const INCLUDE_EQUAL = true; const DEBOUNCE_DELAY = 300; let debounceTimer; let skuElementsMap = new Map(); function cleanName(name) { return name ? name.replace(/\s+/g, ' ').trim() : ''; } /** * 获取两个主题的信息 * @returns {Object} { theme1: {name, checkboxes}, theme2: {name, checkboxes} } */ function getThemesInfo() { const themeBoxes = document.querySelectorAll('#skuAttrInfo .theme-box'); const themes = []; themeBoxes.forEach(box => { const headerTextEl = box.querySelector('.theme-box-header .theme-value-text'); const themeName = headerTextEl ? cleanName(headerTextEl.title || headerTextEl.textContent) : ''; const checkboxes = Array.from(box.querySelectorAll('.checkbox-group input[type="checkbox"]:checked')); if (themeName && checkboxes.length > 0) { themes.push({ name: themeName, checkboxes: checkboxes }); } }); return themes.length >= 2 ? { theme1: themes[0], theme2: themes[1] } : null; } /** * 获取 SKU 数据 */ function getSkuData() { const skuData = []; skuElementsMap.clear(); const headerRow = document.querySelector('#skuDataInfo thead tr'); let theme1ColIndex = -1; let theme2ColIndex = -1; let priceColIndex = -1; if (headerRow) { const headers = headerRow.querySelectorAll('th'); let themeColCount = 0; headers.forEach((th, index) => { const text = cleanName(th.textContent); if (text.includes('价格')) { priceColIndex = index; } else if (themeColCount === 0 && (text.includes('顏色') || text.includes('重量') || text.includes('尺寸') || text.includes('款式') || text.includes('规格'))) { theme1ColIndex = index; themeColCount++; } else if (themeColCount === 1 && (text.includes('顏色') || text.includes('重量') || text.includes('尺寸') || text.includes('款式') || text.includes('规格'))) { theme2ColIndex = index; themeColCount++; } }); } if (theme1ColIndex === -1 || theme2ColIndex === -1 || priceColIndex === -1) { console.error('[Shopee Price Highlighter & Filter] Could not find theme or price columns.'); return []; } const dataRows = document.querySelectorAll('#skuDataInfo tbody tr'); dataRows.forEach((row) => { const cells = row.querySelectorAll('td'); if (cells.length === 0) return; const theme1Cell = cells[theme1ColIndex]; const theme2Cell = cells[theme2ColIndex]; const priceCell = cells[priceColIndex]; const theme1Value = cleanName(theme1Cell ? theme1Cell.textContent : ''); const theme2Value = cleanName(theme2Cell ? theme2Cell.textContent : ''); const priceInput = priceCell ? priceCell.querySelector('input.g-form-component') : null; if (!theme1Value || !theme2Value || !priceInput) return; const price = parseFloat(priceInput.value); if (!isNaN(price) && price > 0) { const combinedName = `${theme1Value} ${theme2Value}`; const data = { combinedName: combinedName, theme1Value: theme1Value, theme2Value: theme2Value, price: price, priceInput: priceInput }; skuData.push(data); skuElementsMap.set(combinedName, data); } }); console.log(`[Shopee Price Highlighter & Filter] Found ${skuData.length} valid SKUs.`); return skuData; } /** * 高亮价格 */ function highlightPrices() { console.log('[Shopee Price Highlighter & Filter] Executing price highlight...'); const skuData = getSkuData(); if (skuData.length === 0) return; const validPrices = skuData.map(s => s.price).filter(price => !isNaN(price) && price > 0); if (validPrices.length === 0) { skuData.forEach(sku => { sku.priceInput.style.backgroundColor = ''; }); return; } const minPrice = Math.min(...validPrices); const thresholdPrice = minPrice * MULTIPLE_THRESHOLD; console.log(`[Shopee Price Highlighter & Filter] Min Price: ${minPrice}, Threshold: ${thresholdPrice} (${MULTIPLE_THRESHOLD}x)`); skuData.forEach(sku => { sku.priceInput.style.backgroundColor = ''; if (!isNaN(sku.price)) { if (INCLUDE_EQUAL) { if (sku.price >= thresholdPrice) { sku.priceInput.style.backgroundColor = HIGHLIGHT_COLOR; } } else { if (sku.price > thresholdPrice) { sku.priceInput.style.backgroundColor = HIGHLIGHT_COLOR; } } } }); } function setCheckboxState(checkboxInput, checked) { if (!checkboxInput || checkboxInput.checked === checked) return; checkboxInput.checked = checked; const event = new Event('change', { bubbles: true }); checkboxInput.dispatchEvent(event); console.log(`[Shopee Price Highlighter & Filter] Checkbox for "${cleanName(checkboxInput.value)}" set to ${checked}`); } /** * 移除最低价选项 */ function removeLowestPriceSku() { const themesInfo = getThemesInfo(); if (!themesInfo) { alert('无法识别变种主题结构'); return; } // 判断哪个主题选项更多 const { theme1, theme2 } = themesInfo; let targetTheme, otherTheme; if (theme1.checkboxes.length >= theme2.checkboxes.length) { targetTheme = theme1; otherTheme = theme2; } else { targetTheme = theme2; otherTheme = theme1; } if (targetTheme.checkboxes.length <= 1) { alert(`${targetTheme.name}至少需要保留一个选项`); return; } // 计算每个目标选项的最低价格(代表价格) const optionPrices = new Map(); targetTheme.checkboxes.forEach(checkbox => { const optionName = cleanName(checkbox.value); let minPriceForOption = Infinity; otherTheme.checkboxes.forEach(otherCheckbox => { const otherOptionName = cleanName(otherCheckbox.value); // 根据主题顺序组合 let combinedName; if (targetTheme === theme1) { combinedName = `${optionName} ${otherOptionName}`; } else { combinedName = `${otherOptionName} ${optionName}`; } const skuData = skuElementsMap.get(combinedName); if (skuData && skuData.price < minPriceForOption) { minPriceForOption = skuData.price; } }); if (minPriceForOption !== Infinity) { optionPrices.set(checkbox, minPriceForOption); } }); // 找到最低价的选项 let lowestCheckbox = null; let lowestPrice = Infinity; optionPrices.forEach((price, checkbox) => { if (price < lowestPrice) { lowestPrice = price; lowestCheckbox = checkbox; } }); if (lowestCheckbox) { const optionName = cleanName(lowestCheckbox.value); setCheckboxState(lowestCheckbox, false); console.log(`[Shopee Price Highlighter & Filter] Removed lowest price option in ${targetTheme.name}: ${optionName} (代表价格: ${lowestPrice})`); setTimeout(highlightPrices, DEBOUNCE_DELAY); } } /** * 移除最高价选项 */ function removeHighestPriceSku() { const themesInfo = getThemesInfo(); if (!themesInfo) { alert('无法识别变种主题结构'); return; } const { theme1, theme2 } = themesInfo; let targetTheme, otherTheme; if (theme1.checkboxes.length >= theme2.checkboxes.length) { targetTheme = theme1; otherTheme = theme2; } else { targetTheme = theme2; otherTheme = theme1; } if (targetTheme.checkboxes.length <= 1) { alert(`${targetTheme.name}至少需要保留一个选项`); return; } const optionPrices = new Map(); targetTheme.checkboxes.forEach(checkbox => { const optionName = cleanName(checkbox.value); let minPriceForOption = Infinity; otherTheme.checkboxes.forEach(otherCheckbox => { const otherOptionName = cleanName(otherCheckbox.value); let combinedName; if (targetTheme === theme1) { combinedName = `${optionName} ${otherOptionName}`; } else { combinedName = `${otherOptionName} ${optionName}`; } const skuData = skuElementsMap.get(combinedName); if (skuData && skuData.price < minPriceForOption) { minPriceForOption = skuData.price; } }); if (minPriceForOption !== Infinity) { optionPrices.set(checkbox, minPriceForOption); } }); let highestCheckbox = null; let highestPrice = -Infinity; optionPrices.forEach((price, checkbox) => { if (price > highestPrice) { highestPrice = price; highestCheckbox = checkbox; } }); if (highestCheckbox) { const optionName = cleanName(highestCheckbox.value); setCheckboxState(highestCheckbox, false); console.log(`[Shopee Price Highlighter & Filter] Removed highest price option in ${targetTheme.name}: ${optionName} (代表价格: ${highestPrice})`); setTimeout(highlightPrices, DEBOUNCE_DELAY); } } // --- MutationObserver --- const observerCallback = function(mutationsList, observer) { clearTimeout(debounceTimer); debounceTimer = setTimeout(() => { highlightPrices(); }, DEBOUNCE_DELAY); }; let observerRetryCount = 0; const maxObserverRetries = 10; const observerRetryInterval = 500; function initializeObserver() { const targetNode = document.getElementById('skuDataInfo'); if (targetNode) { const config = { childList: true, subtree: true, attributes: true, attributeFilter: ['value'] }; const observer = new MutationObserver(observerCallback); observer.observe(targetNode, config); const skuAttrInfoNode = document.getElementById('skuAttrInfo'); if (skuAttrInfoNode) { const attrConfig = { childList: true, subtree: true, attributes: true, attributeFilter: ['checked'] }; const attrObserver = new MutationObserver(observerCallback); attrObserver.observe(skuAttrInfoNode, attrConfig); console.log('[Shopee Price Highlighter & Filter] MutationObserver started for both sections.'); } console.log('[Shopee Price Highlighter & Filter] MutationObserver initialized.'); highlightPrices(); } else if (observerRetryCount < maxObserverRetries) { observerRetryCount++; console.warn(`[Shopee Price Highlighter & Filter] Retrying observer init (${observerRetryCount}/${maxObserverRetries})...`); setTimeout(initializeObserver, observerRetryInterval); } else { console.error('[Shopee Price Highlighter & Filter] Failed to initialize observer.'); } } /** * 添加功能按钮 */ let addButtonsRetryCount = 0; const maxAddButtonsRetries = 10; const addButtonsRetryInterval = 500; function addFilterButtons() { if (document.getElementById('skuFilterButtons')) { console.log('[Shopee Price Highlighter & Filter] Filter buttons already exist.'); return; } const targetElementForButtons = document.querySelector("#skuDataInfo > div.form-card-content > div > div > div.mb-20.flex-justify-between"); if (targetElementForButtons) { const buttonContainer = document.createElement('div'); buttonContainer.id = 'skuFilterButtons'; buttonContainer.style.cssText = 'display: flex; gap: 10px; align-items: center;'; const createButton = (text, onClickHandler) => { const button = document.createElement('button'); button.className = 'ant-btn ant-btn-default css-1oz1bg8'; button.textContent = text; button.style.backgroundColor = '#e6f7ff'; button.style.borderColor = '#91d5ff'; button.style.color = '#1890ff'; button.style.minWidth = 'unset'; button.style.padding = '4px 12px'; button.style.height = '32px'; button.addEventListener('click', onClickHandler); return button; }; buttonContainer.appendChild(createButton('移除最低价', removeLowestPriceSku)); buttonContainer.appendChild(createButton('移除最高价', removeHighestPriceSku)); targetElementForButtons.appendChild(buttonContainer); console.log('[Shopee Price Highlighter & Filter] Filter buttons added successfully.'); addButtonsRetryCount = 0; } else if (addButtonsRetryCount < maxAddButtonsRetries) { addButtonsRetryCount++; console.warn(`[Shopee Price Highlighter & Filter] Retrying button addition (${addButtonsRetryCount}/${maxAddButtonsRetries})...`); setTimeout(addFilterButtons, addButtonsRetryInterval); } else { console.error('[Shopee Price Highlighter & Filter] Failed to add filter buttons.'); } } // --- 启动脚本 --- function init() { initializeObserver(); addFilterButtons(); setTimeout(addFilterButtons, 1500); setTimeout(highlightPrices, 1500); setTimeout(addFilterButtons, 4000); setTimeout(highlightPrices, 4000); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();