您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
JLC_SHOP_SEARCH_TOOL_2.0.
// ==UserScript== // @name JLC_SHOP_SEARCH_TOOL_2.0 // @namespace http://tampermonkey.net/ // @version 2.1.2 // @description JLC_SHOP_SEARCH_TOOL_2.0. // @author Lx // @match https://so.szlcsc.com/global.html** // @match https://list.szlcsc.com/brand** // @match https://list.szlcsc.com/catalog** // @icon https://www.google.com/s2/favicons?sz=64&domain=szlcsc.com // @require https://update.greasyfork.org/scripts/446666/1389793/jQuery%20Core%20minified.js // @resource searchCSS https://gitee.com/mlx6_admin/public_resource_lc/raw/master/search.css // @grant GM_xmlhttpRequest // @grant GM_getResourceText // @grant GM_openInTab // @grant GM_addStyle // @connect szlcsc.com // @license MIT // ==/UserScript== /** * Message 全局消息通知组件 * 使用方式: * window.$message.success('操作成功') * window.$message.error('操作失败') * window.$message.warning('警告信息') * window.$message.info('普通信息') */ (function(window) { // 样式定义 const style = ` .message-container { position: fixed; top: 20px; left: 0; right: 0; display: flex; flex-direction: column; align-items: center; pointer-events: none; z-index: 1000000000000000000; } .message-item { min-width: 300px; max-width: 600px; padding: 8px 20px; margin-bottom: 10px; border-radius: 4px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); background: #fff; transition: all 0.3s; display: flex; align-items: center; pointer-events: auto; overflow: hidden; } .message-item.success { background-color: #f0f9eb; color: #67c23a; border: 1px solid #e1f3d8; } .message-item.error { background-color: #fef0f0; color: #f56c6c; border: 1px solid #fde2e2; } .message-item.warning { background-color: #fdf6ec; color: #e6a23c; border: 1px solid #faecd8; } .message-item.info { background-color: #edf2fc; color: #909399; border: 1px solid #ebeeef; } .message-icon { margin-right: 10px; font-size: 18px; } .message-content { flex: 1; font-size: 14px; line-height: 1.5; } .message-close { margin-left: 15px; color: #c0c4cc; cursor: pointer; font-size: 16px; } .message-close:hover { color: #909399; } .message-fade-enter-active, .message-fade-leave-active { transition: all 0.3s; } .message-fade-enter, .message-fade-leave-to { opacity: 0; transform: translateY(-20px); } `; // 添加样式到head const styleElement = document.createElement('style'); styleElement.innerHTML = style; document.head.appendChild(styleElement); // 消息队列 let messages = []; let container = null; // 创建消息容器 function createContainer() { if (!container) { container = document.createElement('div'); container.className = 'message-container'; document.body.appendChild(container); } return container; } // 创建消息元素 function createMessage(type, message, duration = 3000) { const container = createContainer(); const messageId = Date.now() + Math.random().toString(36).substr(2, 9); const messageEl = document.createElement('div'); messageEl.className = `message-item ${type}`; messageEl.dataset.id = messageId; // 图标 const iconMap = { success: '✓', error: '✕', warning: '⚠', info: 'ℹ' }; const iconEl = document.createElement('span'); iconEl.className = 'message-icon'; iconEl.textContent = iconMap[type] || ''; // 内容 const contentEl = document.createElement('span'); contentEl.className = 'message-content'; contentEl.textContent = message; // 关闭按钮 const closeEl = document.createElement('span'); closeEl.className = 'message-close'; closeEl.innerHTML = '×'; closeEl.onclick = () => removeMessage(messageId); messageEl.appendChild(iconEl); messageEl.appendChild(contentEl); messageEl.appendChild(closeEl); // 添加到DOM container.appendChild(messageEl); // 触发动画 setTimeout(() => { messageEl.style.opacity = '1'; messageEl.style.transform = 'translateY(0)'; }, 10); // 自动关闭 if (duration > 0) { setTimeout(() => { removeMessage(messageId); }, duration); } // 添加到消息队列 messages.push({ id: messageId, element: messageEl }); return messageId; } // 移除消息 function removeMessage(id) { const index = messages.findIndex(msg => msg.id === id); if (index === -1) return; const { element } = messages[index]; // 触发离开动画 element.style.opacity = '0'; element.style.transform = 'translateY(-20px)'; // 动画结束后移除 setTimeout(() => { if (element.parentNode) { element.parentNode.removeChild(element); } // 从队列中移除 messages.splice(index, 1); // 如果没有消息了,移除容器 if (messages.length === 0 && container) { document.body.removeChild(container); container = null; } }, 300); } // 清除所有消息 function clearAll() { messages.forEach(msg => { if (msg.element.parentNode) { msg.element.parentNode.removeChild(msg.element); } }); messages = []; if (container) { document.body.removeChild(container); container = null; } } // 导出到全局 window.$message = { success: (message, duration) => createMessage('success', message, duration), error: (message, duration) => createMessage('error', message, duration), warning: (message, duration) => createMessage('warning', message, duration), info: (message, duration) => createMessage('info', message, duration), closeAll: clearAll }; })(window); (async function () { 'use strict'; const searchCSS = GM_getResourceText("searchCSS") GM_addStyle(searchCSS) /** * 空列表占位组件 * @class EmptyState */ class EmptyState { /** * 构造函数 * @param {string} selector 容器选择器 * @param {object} options 配置选项 */ constructor(selector, options = {}) { // 默认配置 const defaults = { icon: 'fa fa-inbox', // 图标类名(推荐使用Font Awesome) iconSize: 48, // 图标尺寸 title: '暂无数据', // 主标题 description: '', // 描述文本 showAction: false, // 是否显示操作按钮 actionText: '刷新', // 按钮文字 actionClass: 'btn btn-primary', // 按钮样式类 onAction: null // 按钮点击回调 }; // 合并配置 this.settings = $.extend({}, defaults, options); // 容器元素 this.$container = $(selector); if (this.$container.length === 0) { console.error('EmptyState: 容器元素未找到'); return; } // 初始化 this._init(); } /** * 初始化组件 * @private */ _init() { // 创建占位元素 this.$element = $(` <div class="empty-state" style="display: none;"> <div class="empty-state-icon"> <i class="${this.settings.icon}"></i> </div> <div class="empty-state-content"> <h3 class="empty-state-title">${this.settings.title}</h3> ${this.settings.description ? `<p class="empty-state-desc">${this.settings.description}</p>` : ''} </div> ${this.settings.showAction ? ` <div class="empty-state-actions"> <button class="${this.settings.actionClass}">${this.settings.actionText}</button> </div> ` : ''} </div> `); // 设置图标尺寸 this.$element.find('.empty-state-icon i').css({ 'font-size': `${this.settings.iconSize}px`, 'width': `${this.settings.iconSize}px`, 'height': `${this.settings.iconSize}px` }); // 添加到容器 this.$container.append(this.$element); // 绑定事件 if (this.settings.showAction && this.settings.onAction) { this.$element.find('button').on('click', this.settings.onAction); } // 确保样式存在 this._ensureStyles(); } /** * 确保样式存在 * @private */ _ensureStyles() { if ($('#empty-state-styles').length === 0) { $('<style id="empty-state-styles">') .text(` .empty-state { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 40px 20px; text-align: center; color: #6c757d; } .empty-state-icon { margin-bottom: 20px; color: #adb5bd; } .empty-state-icon i { display: inline-flex; align-items: center; justify-content: center; } .empty-state-content { max-width: 500px; } .empty-state-title { margin: 0 0 10px; font-size: 18px; font-weight: 500; color: #343a40; } .empty-state-desc { margin: 0; font-size: 14px; line-height: 1.5; } .empty-state-actions { margin-top: 20px; } .btn { display: inline-block; font-weight: 400; text-align: center; white-space: nowrap; vertical-align: middle; user-select: none; border: 1px solid transparent; padding: 0.375rem 0.75rem; font-size: 1rem; line-height: 1.5; border-radius: 0.25rem; transition: all 0.15s ease-in-out; cursor: pointer; } .btn-primary { color: #fff; background-color: #007bff; border-color: #007bff; } .btn-primary:hover { background-color: #0069d9; border-color: #0062cc; } `) .appendTo('head'); } } /** * 显示空状态 * @public */ show() { // 隐藏容器内其他内容 this.$container.children().not(this.$element).hide(); // 显示占位 this.$element.show(); return this; } /** * 隐藏空状态 * @public */ hide() { this.$element.hide(); // 显示容器内其他内容 this.$container.children().show(); return this; } /** * 切换显示状态 * @param {boolean} [state] 显示/隐藏 * @public */ toggle(state) { if (typeof state === 'undefined') { state = !this.$element.is(':visible'); } return state ? this.show() : this.hide(); } /** * 更新配置 * @param {object} newSettings 新配置 * @public */ update(newSettings) { // 合并新配置 this.settings = $.extend({}, this.settings, newSettings); // 更新图标 if (newSettings.icon) { const $icon = this.$element.find('.empty-state-icon i'); $icon.attr('class', newSettings.icon); if (newSettings.iconSize) { $icon.css({ 'font-size': `${newSettings.iconSize}px`, 'width': `${newSettings.iconSize}px`, 'height': `${newSettings.iconSize}px` }); } } // 更新标题 if (newSettings.title) { this.$element.find('.empty-state-title').text(newSettings.title); } // 更新描述 if (newSettings.description !== undefined) { const $desc = this.$element.find('.empty-state-desc'); if (newSettings.description) { if ($desc.length) { $desc.text(newSettings.description); } else { this.$element.find('.empty-state-content').append( `<p class="empty-state-desc">${newSettings.description}</p>` ); } } else if ($desc.length) { $desc.remove(); } } // 更新操作按钮 if (newSettings.showAction !== undefined || newSettings.actionText || newSettings.actionClass || newSettings.onAction) { const $actions = this.$element.find('.empty-state-actions'); if (this.settings.showAction) { if ($actions.length) { $actions.html(`<button class="${this.settings.actionClass}">${this.settings.actionText}</button>`); } else { this.$element.append(` <div class="empty-state-actions"> <button class="${this.settings.actionClass}">${this.settings.actionText}</button> </div> `); } if (this.settings.onAction) { this.$element.find('button').off('click').on('click', this.settings.onAction); } } else if ($actions.length) { $actions.remove(); } } return this; } /** * 销毁组件 * @public */ destroy() { this.$element.remove(); this.$element = null; this.$container = null; } } // 使用示例 // $(function() { // // 初始化 // const emptyState = new EmptyState('#list-container', { // icon: 'fa fa-search', // title: '没有找到结果', // description: '请尝试其他搜索条件', // showAction: true, // actionText: '重新加载', // onAction: function() { // console.log('执行刷新操作'); // } // }); // // // 显示空状态 // emptyState.show(); // // // 更新内容 // // emptyState.update({ // // title: '新的标题', // // description: '新的描述信息' // // }); // // // 隐藏空状态 // // emptyState.hide(); // // // 销毁实例 // // emptyState.destroy(); // }); class PriceCalculator { /** * 根据数量和价格数据计算最终价格 * @param {number} quantity 购买数量 * @param {number} theRatio 起订量 * @param {Array} productPriceList 产品价格列表 * @param {Object} priceDiscount 折扣信息 * @returns {Object} 计算结果 { originalPrice, discountPrice, discountRate, priceTier } */ static calculatePrice(quantity, theRatio, productPriceList, priceDiscount) { // 验证输入 if (!productPriceList || !Array.isArray(productPriceList) || productPriceList.length === 0) { throw new Error('无效的产品价格列表'); } if (quantity <= 0) { throw new Error('数量必须大于0'); } // 1. 找到对应的价格区间 const priceTier = this.findPriceTier(quantity, theRatio, productPriceList); if (!priceTier) { throw new Error('找不到对应数量的价格区间'); } // 2. 计算原始价格 const originalPrice = priceTier.productPrice; // 3. 计算折扣价格 let discountPrice = originalPrice; let discountRate = 1; // 默认无折扣 if (priceDiscount && priceDiscount.priceList) { const discountTier = this.findDiscountTier(quantity, priceDiscount.priceList); if (discountTier) { discountRate = discountTier.discount; discountPrice = originalPrice * discountRate; } } return { originalPrice: this.formatPrice(originalPrice), discountPrice: this.formatPrice(discountPrice), discountTotalPrice: this.formatPrice(discountPrice * quantity), discountRateText: this.formatPrice(discountRate * 100) + '%', discountRate, priceTier: priceTier, minEncapsulationUnitPrice: priceTier.minEncapsulationUnitPrice ? this.formatPrice(priceTier.minEncapsulationUnitPrice) : null }; } /** * 根据数量查找对应的价格区间 * @param {number} quantity * @param {number} theRatio * @param {Array} priceList * @returns {Object|null} */ static findPriceTier(quantity, theRatio, priceList) { for (const tier of priceList) { const start = tier.startPurchasedNumber * theRatio; let end = tier.endPurchasedNumber * theRatio; // 处理无限数量的情况 (endPurchasedNumber = -1) if (end === -1) end = Infinity; if (quantity >= start && quantity <= end) { return tier; } } return null; } /** * 根据数量查找对应的折扣区间 * @param {number} quantity * @param {Array} discountList * @returns {Object|null} */ static findDiscountTier(quantity, discountList) { for (const tier of discountList) { const start = tier.spNumber; let end = tier.epNumber; // 处理无限数量的情况 (epNumber = -1) if (end === -1) end = Infinity; if (quantity >= start && quantity <= end) { return tier; } } return null; } /** * 格式化价格,保留4位小数 * @param {number} price * @returns {number} */ static formatPrice(price) { return parseFloat(price.toFixed(4)); } } class ConditionalWaiter { /** * 创建一个条件等待器 * @param {Object} options 配置选项 * @param {number} [options.maxWaitTime=5000] 最大等待时间(毫秒) * @param {number} [options.checkInterval=100] 检查间隔(毫秒) */ constructor(options = {}) { this.maxWaitTime = options.maxWaitTime || 5000; // 默认5秒 this.checkInterval = options.checkInterval || 100; // 默认100毫秒 this.timeoutId = null; this.intervalId = null; } /** * 等待直到条件满足 * @param {Function} conditionFn 条件函数,返回true时停止等待 * @param {Function} [callback] 条件满足时的回调 * @return {Promise} 返回Promise,可在条件满足时resolve */ waitUntil(conditionFn, callback) { return new Promise((resolve, reject) => { const startTime = Date.now(); // 设置超时定时器 this.timeoutId = setTimeout(() => { this.clear(); const err = new Error(`Condition not met within ${this.maxWaitTime}ms`); if (callback) callback(err, false); reject(err); }, this.maxWaitTime); // 递归检查函数 const checkCondition = () => { try { if (conditionFn()) { this.clear(); if (callback) callback(null, true); resolve(true); } else if (Date.now() - startTime < this.maxWaitTime) { this.intervalId = setTimeout(checkCondition, this.checkInterval); } } catch (error) { this.clear(); if (callback) callback(error, false); reject(error); } }; // 开始第一次检查 checkCondition(); }); } /** * 清除所有定时器 */ clear() { if (this.timeoutId) { clearTimeout(this.timeoutId); this.timeoutId = null; } if (this.intervalId) { clearTimeout(this.intervalId); this.intervalId = null; } } /** * 销毁实例 */ destroy() { this.clear(); } } const Util = { /** * 根据value排序Map * @param {*} map * @returns */ sortMapByValue: function (map) { var arrayObj = Array.from(map); // 按照value值降序排序 arrayObj.sort(function (a, b) { return b[1] - a[1]; // 修改为降序排序 }); return arrayObj; }, /** * GET请求封装 * @param {*} url */ getAjax: function (url) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ url: url, method: 'GET', onload: (r) => { resolve(r.response); }, onerror: (err) => { reject(err); } }); }); }, /** * POST请求封装 * @param {*} url * @param {*} data */ postAjaxJSON: function (url, data) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ url: url, method: 'POST', headers: { 'Content-Type': 'application/json' }, data: JSON.stringify(data), // 确保数据被正确转换为JSON字符串 onload: (r) => { resolve(r.response); }, onerror: (err) => { reject(err); } }); }); }, /** * 获取品牌名称 * 支持列表: * 1、XUNDA(讯答) * 2、立创开发板 * 3、50元德立品牌优惠 * 4、<新人专享>15元芯声品牌优惠 * @param text */ brandNameProcess: function (text) { let replaceText = text; try { // 取括号里的品牌名称 如:ICEY(冰禹) if (replaceText.includes("(")) { const t = replaceText.split(/\(|\)/g).filter((e => e)); replaceText = (1 === t.length ? t[0] : t.length > 1 ? t[t.length - 1] : name) } else { const t = /<.+>/g.exec(text) if (t != null) { replaceText = t[0].replace(/<|>/g, '') if (replaceText === '新人专享') { replaceText = text.replace(/^.[^元]*元(.*)品牌.*$/, '$1') } } else { replaceText = text.replace(/^.[^元]*元(.*)品牌.*$/, '$1') } } } catch (e) { console.error(e) } finally { return replaceText } }, jsonToUrlParam: function (json, ignoreFields = '') { return Object.keys(json) .filter(key => ignoreFields.indexOf(key) === -1) .map(key => key + '=' + encodeURIComponent(json[key])).join('&'); // 使用 encodeURIComponent 避免URL编码问题 }, /** * POST请求封装 * @param {*} url * @param {*} jsonData */ postFormAjax: function (url, jsonData) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ url: url, data: this.jsonToUrlParam(jsonData), // 使用 Util.jsonToUrlParam 方法 method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, onload: (r) => { resolve(r.response); }, onerror: (err) => { reject(err); } }); }); }, /** * 有进度的等待所有异步任务的执行 * @param {*} requests * @param {*} callback * @returns */ allWithProgress: function (requests, callback) { let index = 0; requests.forEach(item => { item.then(() => { index++; const progress = (index / requests.length) * 100; callback({ total: requests.length, cur: index, progress: progress }); }).catch((err) => { console.error(err); }); }); return Promise.all(requests); }, /** * 等待 * @param {*} timeout * @returns */ sleep: function (timeout) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(true); }, timeout); }); }, /** * 等待 执行函数 * @param {*} timeout * @param {*} func * @returns */ sleepFunc: function (timeout, func) { return new Promise((resolve, reject) => { setTimeout(() => { func && func(); }, timeout); }); }, /** * 获取本地缓存 * @param {*} key */ getLocalData: function (k) { return localStorage.getItem(k); }, /** * 设置本地缓存 * @param {*} key * @param {*} value */ setLocalData: function (k, v) { localStorage.setItem(k, v); }, /** * 获取session缓存 * @param {*} key */ getSessionData: function (k) { return sessionStorage.getItem(k); }, /** * 设置session缓存 * @param {*} key * @param {*} value */ setSessionData: function (k, v) { sessionStorage.setItem(k, v); } }; // ======================================================== // 基础方法 const Base = { // 优惠券只保存1元购的优惠券信息 allOneCouponMap: new Map(), // 存放新人券的map isNewCouponMap: new Map(), // 存放非新人券的map isNotNewCouponMap: new Map(), // 搜索页获取每一行的元素 getSearchRows: () => { const rows = $('div.product-group-leader section, div.group section'); rows.each(function () { if (!$(this).hasClass('line-box')) { $(this).addClass('line-box'); } }); return rows; }, // 获取顶级的行元素 getParentRow: (that) => $(that).closest('.line-box'), // 获取顶级的行元素并查找 getParentRowWithFind: (that, selector) => Base.getParentRow(that).find(selector) } /** * 一键索索淘宝 */ class SearchPageHelper { // 使用私有静态字段保存单例实例 static instance; constructor() { // 如果实例已存在,直接返回 if (SearchPageHelper.instance) { return SearchPageHelper.instance; } // 初始化成员变量 this.someCouponMapping = { "MDD": "辰达半导体", }; // 保存当前实例 SearchPageHelper.instance = this; } /** * 搜索列表中,对品牌颜色进行上色 * list.szlcsc.com/catalog */ static catalogBrandColor() { const brands = Array.from(Base.allOneCouponMap.entries()); let index = 0; function processBatch() { const batchSize = 10; // 每帧处理10个品牌 const end = Math.min(index + batchSize, brands.length); for (; index < end; index++) { const [brandName, brandDetail] = brands[index]; const $brandEle = $(`li[title*="${brandName}"], span[title*="${brandName}"], a.brand-name[title*="${brandName}"]`) .not('[style*="background-color"]') .not('.isNew, .isNotNew'); if ($brandEle.length > 0) { $brandEle.css({ "background-color": brandDetail.isNew ? '#00bfffb8' : '#7fffd4b8' }).addClass(brandDetail.isNew ? 'isNew' : 'isNotNew'); } } if (index < brands.length) { requestAnimationFrame(processBatch); } } processBatch(); } /** * 筛选条件:多选品牌 * @param {*} isNew 是否新人券 true/false */ async multiFilterBrand(isNew) { $('li:contains("品牌"):contains("多选") div:contains("多选")').last().click(); await Util.sleep(3000); const elementStr = isNew ? 'isNew' : 'isNotNew'; $(`.${elementStr}`).each(function () { // 品牌名称 const brandNameOrigin = Util.brandNameProcess($(this).text().trim().trim()); if (Base.allOneCouponMap.has(brandNameOrigin)) { if (Base.allOneCouponMap.get(brandNameOrigin).isNew === isNew) { // 多选框选中 $(this).find('label').click(); } } }) await Util.sleep(800); $('button[data-need-query*="lcsc_vid="][data-spm-reset]:contains("确定")').click(); } /** * 类目筛选按钮租 */ btnsRender() { if ($('#_remind').length === 0) { $('li:contains("品牌"):contains("多选")').append(` <div id='_remind'> <span class='row_center get_new_coupon'><p class='new_'></p>新人券</span> <span class='row_center get_notnew_coupon'><p class='not_new_'></p>非新人券</span> </div> <style> #_remind { display: inline-block; position: absolute; top: 0px; right: 100px; width: 100px; } .row_center { display: inline-flex; align-items: center; } .new_ { background-color: #00bfffb8; margin-right: 10px; width: 20px; height: 10px; } .not_new_ { background-color: #7fffd4b8; margin-right: 10px; width: 20px; height: 10px; } .get_new_coupon, .get_notnew_coupon { cursor: pointer; } .get_new_coupon:hover, .get_notnew_coupon:hover { background: #e1e1e1; } </style> `) // 多选新人券 $('.get_new_coupon').click(() => this.multiFilterBrand(true)) // 多选非新人券 $('.get_notnew_coupon').click(() => this.multiFilterBrand(false)) } } /** * 获取优惠券列表信息,并暂存在变量集合中 * 只获取1元购的优惠券 */ async getAllCoupon() { const buildData = (jsonText) => { const json = JSON.parse(jsonText); if (json.code === 200) { // 取数据 const resultMap = json.result.couponModelVOListMap; const datas = ['1', '2', '3', '4', '5']; let allCouponNotNew = []; for (const key of datas) { // 合并所有类型的优惠券 if (resultMap[key]) { allCouponNotNew = [...allCouponNotNew, ...resultMap[key]]; } // 优惠券处理 processCouponList(allCouponNotNew, this.someCouponMapping); } console.log('allOneCouponMap: ', Base.allOneCouponMap); console.log('isNewCouponMap: ', Base.isNewCouponMap); } } // 处理单个优惠券 const processItem = (couponItem, referenceMap, someCouponMapping) => { // 是否新人券 const isNew = couponItem.couponName.includes("<新人专享>"); // 一些优惠券特殊处理 for (let key in someCouponMapping) { if (couponItem.couponTypeName == key) { const newBrandName = someCouponMapping[key] // 存到变量Map中 const newVar = { brandId: couponItem.brandIds, brandNames: couponItem.brandNames, couponName: couponItem.couponName, // 优惠券名称 isNew: isNew, // 是否新人专享 couponPrice: couponItem.couponAmount, //优惠券金额减免 minOrderMoney: couponItem.minOrderMoney, //要求最低金额 pay: couponItem.minOrderMoney - couponItem.couponAmount, // 实际支付金额 brandName: newBrandName, // 品牌名称 couponId: couponItem.uuid, // 优惠券id isHaved: couponItem.isReceive, // 是否已经领取 isUsed: couponItem.isUse, // 是否已经使用过 brandIndexHref: couponItem.targetUrl, // 对应的品牌主页地址 couponLink: `https://www.szlcsc.com/getCoupon/${couponItem.uuid}`, // 领券接口地址 }; referenceMap.set(newBrandName, newVar); isNew && (Base.isNewCouponMap.set(couponItem.brandNames, newVar)); !isNew && (Base.isNotNewCouponMap.set(couponItem.brandNames, newVar)); } } // 存到变量Map中 const newVar1 = { brandId: couponItem.brandIds, brandNames: couponItem.brandNames, couponName: couponItem.couponName, // 优惠券名称 isNew: isNew, // 是否新人专享 couponPrice: couponItem.couponAmount, //优惠券金额减免 minOrderMoney: couponItem.minOrderMoney, //要求最低金额 pay: couponItem.minOrderMoney - couponItem.couponAmount, // 实际支付金额 brandName: couponItem.couponTypeName, // 品牌名称 couponId: couponItem.uuid, // 优惠券id isHaved: couponItem.isReceive, // 是否已经领取 isUsed: couponItem.isUse, // 是否已经使用过 brandIndexHref: couponItem.targetUrl, // 对应的品牌主页地址 couponLink: `https://www.szlcsc.com/getCoupon/${couponItem.uuid}`, // 领券接口地址 }; referenceMap.set(couponItem.couponTypeName, newVar1); isNew && (Base.isNewCouponMap.set(couponItem.brandNames, newVar1)); !isNew && (Base.isNotNewCouponMap.set(couponItem.brandNames, newVar1)); } // 优惠券简单封装 const processCouponList = (couponList, someCouponMapping) => { // 遍历 for (let couponItem of couponList) { const { couponAmount, minOrderMoney } = couponItem; // 1元购 if ((minOrderMoney - couponAmount) === 1) { processItem(couponItem, Base.allOneCouponMap, someCouponMapping) } } } // 获取缓存的我的优惠券数据 const couponData = Util.getSessionData('COUPON_DATA'); if (couponData) { if ([...Base.allOneCouponMap.keys()].length == 0) { buildData(couponData); } return; } // http获取优惠券信息 let json = await Util.getAjax(`https://activity.szlcsc.com/activity/coupon`); Util.setSessionData('COUPON_DATA', json); buildData(json); } /** * 一键搜索淘宝 */ appendSearchTbBtn() { if ($('.searchTaobao_').length === 0) { // 预售拼团 不处理,其他的都追加按钮 $('button:contains("加入购物车")').after(` <button type="button" class="mb-[6px] h-[32px] w-full rounded-[6px] bg-[#0093E6] text-[12px] text-[white] hover:bg-[#47B2ED] searchTaobao_">一键搜淘宝</button> `) } else if ($('.searchTaobao_:not([addedClickHandler])').length > 0) { /** * 非阻容,其他数据处理 * @param {*} parents 行级标签 * @param {*} resArr 数据存放的数组 */ function other(parents, resArr) { let productName = parents.find('dl dd:eq(0)').text().trim() || ''; if (productName.length === 0 || resArr.length > 0) { return; } let footprint = parents.find('dl:contains("封装") dd span').text().trim() || ''; resArr.push(productName); resArr.push(footprint); } /** * 电阻数据处理 * @param {*} parents 行级标签 * @param {*} resArr 数据存放的数组 */ function R(parents, resArr) { const r = parents.find('dl:contains("阻值") dd span:eq(0)').text().replace('Ω', '').trim() || ''; if (r.length === 0 || resArr.length > 0) { return; } const f = parents.find('dl:contains("封装") dd span:eq(0)').text().trim() || ''; const j = parents.find('dl:contains("精度") dd span:eq(0)').text().replace('±', '').trim() || ''; resArr.push(r); resArr.push(f); resArr.push(j); } /** * 电容数据处理 * @param {*} parents 行级标签 * @param {*} resArr 数据存放的数组 */ function C(parents, resArr) { const c = parents.find('dl:contains("容值") dd span:eq(0)').text().trim() || ''; if (c.length === 0 || resArr.length > 0) { return; } const v = parents.find('dl:contains("额定电压") dd span:eq(0)').text().trim() || ''; const j = parents.find('dl:contains("精度") dd span:eq(0)').text().replace('±', '').trim() || ''; const f = parents.find('dl:contains("封装") dd span:eq(0)').text().trim() || ''; resArr.push(c); resArr.push(v); resArr.push(j); resArr.push(f); } $('.searchTaobao_:not([addedClickHandler])').attr('addedClickHandler', true).on('click', function (params) { let searchArrVals = []; const $parents = Base.getParentRow(this); // 阻容处理、其他元件处理 R($parents, searchArrVals); C($parents, searchArrVals); other($parents, searchArrVals); GM_openInTab(`https://s.taobao.com/search?q=${searchArrVals.join('/')}`, { active: true, insert: true, setParent: true }) }) } } /** * 左侧封装搜索 */ appendLeftRowBtns() { const rows = this.getSearchRows(); [...rows.find('button:contains("复制")')].forEach(row => { const $btn = $(row); const specName = $btn.closest('section').find('dl:contains("封装")').find('dd').text(); if ($btn.length > 0 && $btn.siblings('button:contains("封装精确匹配")').length === 0) { // $btn.before(`<button spec-name="${specName}" select-type="MHC" style="width: 110px;" class='btn_search_manual mr-[10px] flex h-[26px] items-center justify-center rounded-[13px] bg-[#F5F6F9] text-[#333] hover:bg-[#ECEEF0]'> // <svg style="margin-right: 3px" t="1748570264460" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6531" width="16" height="16"><path d="M949.76 884.3264a88.68864 88.68864 0 0 1-25.64096 62.67904 87.14752 87.14752 0 0 1-123.76576 0.16896l-164.29568-160.87552a382.4128 382.4128 0 0 1-26.43968 12.6208 382.83776 382.83776 0 0 1-300.032 0 383.38048 383.38048 0 0 1-122.48064-83.39968 391.296 391.296 0 0 1 0-550.36928 384.56832 384.56832 0 0 1 627.55328 123.648 391.00416 391.00416 0 0 1-40.704 376.57088l150.32882 156.56448a88.576 88.576 0 0 1 25.47712 62.39232z m-153.6512-444.04736c0-186.33216-150.41536-337.92-335.30368-337.92s-335.32928 151.6032-335.32928 337.92S275.89632 778.24 460.8 778.24s335.3088-151.64928 335.3088-337.96096z m-503.61344 168.90368a240.45568 240.45568 0 0 1 0-337.73568l34.63168 40.07424a183.46496 183.46496 0 0 0 0 257.50528z" fill="#fa6650" p-id="6532"></path></svg> // 封装模糊匹配 // </button>`); $btn.before(`<button spec-name="${specName}" select-type="JQC" style="width: 110px;" class='btn_search_manual mr-[10px] flex h-[26px] items-center justify-center rounded-[13px] bg-[#F5F6F9] text-[#333] hover:bg-[#ECEEF0]'> <svg style="margin-right: 3px" t="1748569569792" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7704" width="16" height="16"><path d="M945.71 946c-18.67 18.67-49.21 18.67-67.88 0L674.18 742.35c-18.67-18.67-18.67-49.21 0-67.88 18.67-18.67 49.21-18.67 67.88 0l203.65 203.65c18.66 18.66 18.66 49.21 0 67.88z" fill="#CDD8F8" p-id="7705"></path><path d="M447.71 832c-51.82 0-102.11-10.16-149.49-30.2-45.73-19.34-86.79-47.02-122.04-82.27-35.25-35.25-62.93-76.31-82.27-122.04-20.04-47.37-30.2-97.67-30.2-149.49s10.16-102.11 30.2-149.49c19.34-45.73 47.02-86.79 82.27-122.04 35.25-35.25 76.31-62.93 122.04-82.27C345.6 74.16 395.89 64 447.71 64S549.82 74.16 597.2 94.2c45.73 19.34 86.79 47.02 122.04 82.27 35.25 33.25 62.93 76.31 82.27 122.04 20.04 47.37 30.2 97.67 30.2 149.49s-10.16 102.11-30.2 149.49c-19.34 45.73-47.02 86.79-82.27 122.04-35.25 35.25-76.31 62.93-122.04 82.27-47.38 20.04-97.67 30.2-149.49 30.2z m0-667.83c-75.81 0-147.09 29.52-200.7 83.13S163.88 372.18 163.88 448s29.52 147.09 83.13 200.7c53.61 53.61 124.88 83.13 200.7 83.13s147.09-29.52 200.7-83.13c53.61-53.61 83.13-124.88 83.13-200.7s-29.52-147.09-83.13-200.7-124.89-83.13-200.7-83.13z" fill="#5C76F9" p-id="7706"></path></svg> 封装精确匹配 </button>`); } }); $('.btn_search_manual').off('click').on('click', function (e) { this._clickSpecFunc($(e.currentTarget), $(e.currentTarget).attr('spec-name'), $(e.currentTarget).attr('select-type')); }.bind(this)); } /** * 获取搜索结果行 */ getSearchRows() { return Base.getSearchRows(); } /** * 封装模糊匹配 * @param specName * @private */ _MHCEachClick(that, specName) { if ($(`.det-screen:contains("封装:") label.fuxuanku-lable:contains("${specName}")`).length > 0) { $(`.det-screen:contains("封装:") label.fuxuanku-lable:contains("${specName}")`).click(); } else { if (specName.includes('-')) { this._MHCEachClick(specName.split('-').slice(0, -1).join('-')); } } } /** * 封装精确匹配 * @param specName * @private */ async _JQCEachClick(that, specName) { console.log('Base.getParentRow(that)', Base.getParentRow(that)) await Util.sleep(200); if ($(`.det-screen:contains("封装:") label.fuxuanku-lable[title="${specName}"]`).length > 0) { $(`.det-screen:contains("封装:") label.fuxuanku-lable[title="${specName}"]`).click(); } else { if (specName.includes('-')) { this._JQCEachClick(specName.split('-').slice(0, -1).join('-')); } } } async _clickSpecFunc(that, specName, selectType) { // 封装的筛选条件那一行 展开规格 $('li:contains("封装"):contains("多选")').find('div:contains("多选")').click(); switch (selectType) { // 模糊查 case "MHC": this._MHCEachClick(that, specName); break; // 精确查 case "JQC": this._JQCEachClick(that, specName); break; } // 查找规格对应的选项 $(`.det-screen:contains("封装:") input[value="确定"]`).click(); } }; /** * 搜索页凑单列表 */ class SearchListHelper { // 使用私有静态字段保存单例实例 static instance; // 是否已添加右下角的按钮 static btnStatus = false; // 请求状态 static fetchStatus = false; // 分页大小 static searchPageRealSize = 200; // 查询结果暂存 static listData = []; // 初始化默认选中状态 static defaultTabs = { region: '江苏', userType: false }; constructor() { // 如果实例已存在,直接返回 if (SearchListHelper.instance) { return SearchListHelper.instance; } // 保存当前实例 SearchListHelper.instance = this; } static async start(brandsNameOrSearchText, brandsId, maxCount, stock, parallel = false) { SearchListHelper.fetchStatus = false; SearchListHelper.listData = await SearchListHelper.getBrandsProducts(brandsNameOrSearchText, brandsId, maxCount, stock, parallel); console.log(SearchListHelper.listData); SearchListHelper.setCouponSign(); SearchListHelper.renderMinPriceSearch(); } // 设置商品列表的券标记 static setCouponSign() { SearchListHelper.listData.forEach(e => { const newCoupon = Base.isNewCouponMap.get(e.lightBrandName); const notNewCoupon = Base.isNotNewCouponMap.get(e.lightBrandName); newCoupon && (e.isNew = newCoupon.isNew); notNewCoupon && (e.isNew = notNewCoupon.isNew); }) } // 渲染页面 renderListItems() { const stock = 'js'; const searchValue = $('#global-seach-input').val(); SearchListHelper.start(searchValue, null, 300, stock, false); } render() { if (!this.btnStatus) { $('head').prepend(`<style> .floating-button { position: fixed; border-radius: 5px; right: 30px; bottom: 30px; z-index: 9999; background-color: #409EFF; /* Element UI 主色 */ color: white; padding: 15px 20px; font-size: 15px; font-weight: bold; text-align: center; box-shadow: 0 8px 12px rgba(0, 0, 0, 0.1); transition: all 0.3s ease; cursor: pointer; border: none; outline: none; user-select: none; } .floating-button:hover { background-color: #337ecc; /* 深蓝 */ box-shadow: 0 12px 16px rgba(0, 0, 0, 0.15); } .floating-button:active { transform: scale(0.95); /* 点击反馈 */ } .floating-card { display: none; position: fixed; border-radius: 10px; width: 1220px; right: 30px; bottom: 90px; min-height: 30vh; max-height: 85vh; overflow: hidden; border: 2px solid #199fe9; z-index: 10000000; padding: 5px; background: white; } /* Tab 容器,占满卡片顶部宽度 */ .tab-container { display: flex; justify-content: space-between; align-items: center; padding: 5px 10px 8px 10px; border-bottom: 1px solid #ebeef5; /*background-color: #f0f2f5;*/ } /* Tab 按钮 */ .tab-button { width: 45%; flex: 1; /* 平均分配宽度 */ margin: 0; /* 移除额外间距 */ padding: 8px 12px; border: 2px solid #409EFF; background-color: white; color: #333; font-size: 16px; cursor: pointer; transition: all 0.2s ease; text-align: center; box-sizing: border-box; } /* 圆角处理:第一项左圆角,最后一项右圆角 */ .tab-button:first-child { border-top-left-radius: 10px; border-bottom-left-radius: 10px; } .tab-group:nth-child(2) .tab-button:nth-child(2) { border-left: unset; border-right: unset; } .tab-button:last-child { border-top-right-radius: 10px; border-bottom-right-radius: 10px; } /* 按钮悬停 */ .tab-button:hover { background-color: #ecf5ff; } .tab-group { width: 45%; display: flex; height: 50px; justify-content: space-between; } /* 按钮选中样式 */ .tab-button.active { background-color: #409EFF; color: white; font-weight: bold; } .card-body { overflow-x: hidden; overflow-y: scroll; height: 65vh; } .inside-page.list-items .line-box .one img { width: 185px !important; } </style>`); $('body').prepend(` <button id="searchListButton" class="floating-button">排序列表</button> <!-- 卡片容器 --> <div id="cardContainer" class="floating-card"> <!-- 卡片头部,包含 Tab 切换 --> <div class="card-header"> <div class="tab-container"> <div class="tab-group"> <button class="tab-button" data-group="region" data-value="广东">广东</button> <button class="tab-button active" data-group="region" data-value="江苏">江苏</button> </div> <div class="tab-group"> <button class="tab-button" data-group="userType" data-value="all">全部</button> <button class="tab-button" data-group="userType" data-value="true">新人</button> <button class="tab-button active" data-group="userType" data-value="false">非新人</button> </div> </div> </div> <div class="card-body" id="listContainer"> <!-- 列表项将动态插入到这里 --> </div> </div> `); // 使用 jQuery 为按钮绑定点击事件 $('#searchListButton').on('click', async () => { $('#cardContainer').toggle(); // 初始化 const emptyState = new EmptyState('#listContainer', { icon: 'fa fa-search', title: '没有找到结果 或 正在请求数据,请稍等。。', showAction: false, onAction: function() { console.log('执行刷新操作'); } }); // 显示空状态 emptyState.show(); await this.renderListItems(); emptyState.hide(); }); // 点击 Tab 按钮 $('.tab-button').on('click', function () { const $button = $(this); const group = $button.data('group'); // 获取 data-group 属性 const value = $button.data('value'); // 获取 data-value 属性 // 移除同组所有按钮的 active 类 $('.tab-button').filter(function () { return $(this).data('group') === group; }).removeClass('active'); // 为当前按钮添加 active 类 $button.addClass('active'); // 更新选中状态 SearchListHelper.defaultTabs[group] = value; console.log('当前选中:', SearchListHelper.defaultTabs); SearchListHelper.renderMinPriceSearch(); }); this.btnStatus = true; } } /** * 搜索主页的凑单逻辑 */ async renderMainPageMinPriceSearch() { // 避免重复执行 if (this.globalSearchEnd) return; const searchInput = $('#search-input'); const searchValue = searchInput.val(); // 输入为空时清空缓存并返回 if (!searchValue?.trim()) { searchTempList = []; return; } this.globalSearchEnd = true; try { // 获取总页数 const totalPage = searchTotalPage(); // 收集表单数据 const formData = Array.from($('form#allProjectFrom > input[type="hidden"]')) .filter(item => !$(item).attr('id')?.includes('SloganVal') && !$(item).attr('id')?.includes('LinkUrlVal')) .reduce((acc, item) => { const name = $(item).attr('name'); acc[name] = $(item).val(); return acc; }, {}); // 创建并发请求队列(限制并发数为3) const requestQueue = []; for (let pn = 1; pn <= totalPage && pn <= 30; pn++) { const data = { ...formData, pageNumber: pn, k: searchValue, sk: searchValue, localQueryKeyword: '' }; requestQueue.push( $.ajax({ url: "https://so.szlcsc.com/search", method: "POST", data: data, timeout: 10000 // 添加超时限制 }) ); } // 显示加载进度 const progressContainer = $('.wait-h2'); progressContainer.html(`数据加载中...</br>(共${Math.min(totalPage, 30)}页,正在加载第1页...`); // 并发执行请求并跟踪进度 const results = await allWithProgress(requestQueue, ({total, cur}) => { progressContainer.html(`数据加载中...</br>(共${total}页,正在加载第${cur}页... ${Math.round((cur / total) * 100)}%)`); }); // 处理响应数据(过滤无效响应) const validResults = results.filter( res => res?.code === 200 && res.result?.productRecordList?.length > 0 ); // 合并搜索结果(使用Set去重) searchTempList = [...new Set([ ...searchTempList, ...validResults.flatMap(res => res.result.productRecordList) ])]; // 渲染结果 renderMinPriceSearch(); // 延迟触发筛选事件(使用MutationObserver替代setTimeout) const observer = new MutationObserver(() => { $('#js-filter-btn').trigger('click'); observer.disconnect(); }); observer.observe(document.body, {childList: true, subtree: true}); console.timeEnd('搜索首页凑单渲染速度'); } catch (error) { console.error('搜索请求失败:', error); $('.wait-h2').html('搜索加载失败,请重试'); this.globalSearchEnd = false; } } /** * 搜索页-查找最低价 列表渲染方法 */ static renderMinPriceSearch() { // 如果广东仓和江苏仓同时没有货的话,那么就属于订货商品,不需要显示 // 如果没有价格区间,证明是停售商品 var newList = SearchListHelper.listData.filter(item => !(parseInt(item.jsWarehouseStockNumber || 0) <= 0 && parseInt(item.gdWarehouseStockNumber || 0) <= 0) && item.productPriceList.length > 0); // 去重 const map = new Map(); newList.forEach(item => { map.set(item.productId, item); }); newList = [...map.values()]; // 列表自动正序,方便凑单 newList.sort((o1, o2) => { return (o1.theRatio * o1.productPriceList[0].productPrice * (o1.listProductDiscount || 10) / 10).toFixed(6) - (o2.theRatio * o2.productPriceList[0].productPrice * (o2.listProductDiscount || 10) / 10).toFixed(6); }); // 指定仓库 const {region, userType} = SearchListHelper.defaultTabs; newList.forEach(e => { if (userType === 'all' && region === '江苏') { e.show = (e.jsWarehouseStockNumber > 0); return } else if (userType === 'all' && region === '广东') { e.show = (e.gdWarehouseStockNumber > 0); return } if (region === '江苏') { e.show = (e.jsWarehouseStockNumber > 0) && e.isNew == userType; return } else if (region == '广东') { e.show = (e.gdWarehouseStockNumber > 0) && e.isNew == userType; return } }) // 取指定条数的数据。默认50个 const html = newList.slice(0, (SearchListHelper.searchPageRealSize || 50)).map(item => { const { productId, lightStandard, lightProductCode, productCode, productMinEncapsulationNumber, productMinEncapsulationUnit, productName, productModel, lightProductModel, productGradePlateId, productPriceList, priceDiscount, listProductDiscount, productGradePlateName, hkConvesionRatio, convesionRatio, theRatio, smtStockNumber, smtLabel, productStockStatus, isPlusDiscount, productUnit, isPresent, isGuidePrice, minBuyNumber, hasSampleRule, breviaryImageUrl, luceneBreviaryImageUrls, productType, productTypeCode, pdfDESProductId, gdWarehouseStockNumber, jsWarehouseStockNumber, paramLinkedMap, recentlySalesCount, batchStockLimit, isNew, show, } = item; return `<table class="inside inside-page tab-data no-one-hk list-items" style="${show ? 'display: block;' : 'display: none;'}" id="product-tbody-line-${productId}" width="100%" border="0" cellspacing="0" cellpadding="0" data-curpage="1" data-mainproductindex="0" pid="${productId}" psid data-batchstocklimit="${batchStockLimit}" data-encapstandard="${lightStandard}" data-hassamplerule="${hasSampleRule}" data-productcode="${productCode}" data-productminencapsulationnumber="${productMinEncapsulationNumber}" data-productminencapsulationunit="${productMinEncapsulationUnit}" data-productmodel="${productModel}" data-productname="${productName}" data-productstockstatus="${productStockStatus}" data-convesionratio="${convesionRatio}" data-theratio="${theRatio}" data-hkconvesionratio="${hkConvesionRatio}" data-productunit="${productUnit}" data-isplusdiscount="${isPlusDiscount}" data-isguideprice="${isGuidePrice}" data-ispresent="${isPresent}" data-brandid="${productGradePlateId}" data-brandname="${productGradePlateName}" data-productmodel-unlight="${lightProductModel}" data-istiprovider data-isptoc data-firstprice="${productPriceList[0].discountPrice || productPriceList[0].productPrice}" data-minbuynumber="${minBuyNumber}" data-provider data-reposition data-productid="${productId}"> <tbody> <tr class="no-tj-tr add-cart-tr" data-inventory-type="local" pid="${productId}"> <td class="line-box"> <div class="one line-box-left"> <a class="one-to-item-link" href="https://item.szlcsc.com/${productId}.html?fromZone=s_s__%2522123%2522" target="_blank" data-trackzone="s_s__"123"" onclick="goItemDetailBuriedPoint('${productId}', this, 'picture', 's_s__"${$("#search-input").val()}"', null, '0')"> <img src="${breviaryImageUrl}" productid="${productId}" alt="${productName}" xpath="${breviaryImageUrl}" data-urls="${luceneBreviaryImageUrls}" showflag="yes" onerror="javascript:this.src='//static.szlcsc.com/ecp/assets/web/static/images/default_pic.gif'"> </a> </div> <div class="line-box-right"> <div class="line-box-right-bottom"> <div class="two"> <div class="two-01 two-top"> <ul class="l02-zb"> <li class="li-ellipsis"> <a title="${productName}" class="ellipsis product-name-link item-go-detail" href="https://item.szlcsc.com/${productId}.html?fromZone=s_s__%2522123%2522" target="_blank" data-trackzone="s_s__"123"" onclick="goItemDetailBuriedPoint('${productId}', this, 'name', 's_s__"${$("#search-input").val()}"', null, '0')"> ${lightProductModel}</a> </li> <li class="band li-ellipsis" onclick="commonBuriedPoint(this, 'go_brand')"> <span class="c9a9a9a" title="品牌:${productGradePlateName}">品牌:</span> <a class="brand-name" title="点击查看${productGradePlateName}的品牌信息" href="https://list.szlcsc.com/brand/${productGradePlateId}.html" target="_blank"> ${productGradePlateName} </a> </li> <li class="li-ellipsis"> <span class="c9a9a9a" title="封装:${lightStandard}">封装:</span> <span title="${lightStandard}">${lightStandard}</span> </li> <li> </li> </ul> <ul class="l02-zb params-list"> ${Object.keys(paramLinkedMap).map(key => { return `<li class="li-ellipsis"> <span class="c9a9a9a">${key}</span>: <span title="${paramLinkedMap[key]}">${paramLinkedMap[key]}</span> </li>` }).join('')} </ul> <ul class="l02-zb"> <li class="li-ellipsis" onclick="commonBuriedPoint(this, 'go_catalog')"> <span class="c9a9a9a">类目:</span> <a title="${productType}" target="_blank" class="catalog ellipsis underLine" href="https://list.szlcsc.com/catalog/${productTypeCode}.html"> ${productType} </a> </li> <li class="li-ellipsis"> <span class="c9a9a9a">编号:</span> <span>${lightProductCode}</span> </li> <li class="li-ellipsis"> <span class="c9a9a9a">详细:</span> <a class="sjsc underLine" target="_blank" href="https://item.szlcsc.com/datasheet/1N4148W/${productId}.html" productid="${productId}" param-click="${pdfDESProductId}"> 数据手册 </a> </li> </ul> </div> <div class="two-bottom"> <!-- <li class="tag-wrapper">--> <!--</li>--> <!--<div class="three-box-bottom common-label common-useless-label">--> <!--<section class="smt-price" id="SP-LIST">--> <!-- <a target="_blank" data-pc="${lightProductCode}" class="to-jlc-smt-list"--> <!-- href="https://www.jlcsmt.com/lcsc/detail?componentCode=${lightProductCode}&stockNumber=10&presaleOrderSource=shop">嘉立创贴片惊喜价格(库存<span--> <!-- class="smtStockNumber">${smtStockNumber}</span>)</a>--> <!--</section>--> <!--</div>--> <!-- SMT 基础库、扩展库 --> <!--<div class="smt-flag common-label">${smtLabel}</div> --> </div> </div> <div class="three-box hasSMT"> <div class="three-box-top"> <div class="three"> <ul class="three-nr"> ${listProductDiscount != null && listProductDiscount < 10 ? ` <li class="three-nr-01"> <span>${listProductDiscount}折</span> <span class="show-discount-icon"> <div class="common-float-dialog"> <div class="common-float-content"> <ul class="cel-item num-cel"> <li></li> ${productPriceList.map(item => { return `<li>${item.startPurchasedNumber * theRatio}+: </li>`; }).join('')} </ul> <ul class="cel-item mr5"> <li class="text-align-center">折后价</li> ${productPriceList.map(item => { return `<li>¥${parseFloat((item.productPrice * (listProductDiscount || 10) / 10).toFixed(6))}</li>`; }).join('')} </ul> <ul class="cel-item not-plus-o-price-cel"> <li class="text-align-center">原价</li> ${productPriceList.map(item => { return `<li class="o-price">¥${item.productPrice}</li>`; }).join('')} </ul> </div> <s class="f-s"><i class="f-i"></i></s> </div> </span> </li> ` : ''} <p class="minBuyMoney_" style=" width: fit-content; padding: 2px 3px; font-weight: 600; color: #0094e7;">最低购入价: ${parseFloat((theRatio * productPriceList[0].productPrice * (listProductDiscount || 10) / 10).toFixed(6))}</p> ${productPriceList.map(item => { const discountPrice = parseFloat((item.productPrice * (listProductDiscount || 10) / 10).toFixed(6)); return `<li class="three-nr-item"> <div class="price-warp price-warp-local"> <p class="ppbbz-p no-special " minordernum="${item.startPurchasedNumber * theRatio}" originalprice="${item.productPrice}" discountprice="${discountPrice}" orderprice="${discountPrice}"> ${item.startPurchasedNumber * theRatio}+: </p> <span class="ccd ccd-ppbbz show-price-span" minordernum="${item.startPurchasedNumber * theRatio}" data-endpurchasednumber="${item.endPurchasedNumber}" data-productprice="${item.productPrice}" data-productprice-discount orderprice="${item.productPrice}" data-startpurchasednumber="${item.startPurchasedNumber}"> ¥${discountPrice} </span> </div> </li>`; }).join('')} </ul> </div> <div class="three three-hk"> </div> </div> </div> <div class="conformity-box"> <div class="conformity-box-top"> <div class="three-change"> <ul class="finput"> <li class="stocks stocks-change stocks-style" local-show="yes" hk-usd-show="no"> <div class="stock-nums-gd">广东仓:<span style="font-weight:bold">${gdWarehouseStockNumber}</span></div> <div class="stock-nums-js">江苏仓:<span style="font-weight:bold">${jsWarehouseStockNumber}</span></div> </li> <li class="display-none"> <div local-show="no" hk-usd-show="yes"> </div> </li> </ul> <!-- <div class="smt-stock">广东SMT仓: <span style="font-weight:bold"> ${smtStockNumber} </span></div> --> </div> <div class="ffour"> <ul class="finput"> <li class="price-input price-input-gd local-input"> <input type="text" maxlength="9" unit-type="single" class="cartnumbers " pluszk="false" oninput="if(value>${gdWarehouseStockNumber})value=${gdWarehouseStockNumber};if(value<0)value=0" max="${gdWarehouseStockNumber}" min="${productMinEncapsulationNumber}" data-theratio="${theRatio}" data-pricediscount='${JSON.stringify(priceDiscount)}' data-productpricelist='${JSON.stringify(productPriceList)}' data-unitnum="${productMinEncapsulationNumber}" placeholder="广东仓" data-defaultwarehouse="sz" data-type="gd" data-gdstock="${gdWarehouseStockNumber}" value> <div class="unit "> <span class="cur-unit ">个</span> <i class="xl"></i> <div class="unit-contain" style="display: none;"> <div class="unit-type"> <span class="unit-one">个</span> <span class="unit-two">${productMinEncapsulationUnit}</span> </div> <i class="sl"></i> </div> </div> </li> <li class="price-input price-input-js local-input"> <input type="text" maxlength="9" unit-type="single" class="cartnumbers " pluszk="false" oninput="if(value>${gdWarehouseStockNumber})value=${gdWarehouseStockNumber};if(value<0)value=0" max="${jsWarehouseStockNumber}" min="${productMinEncapsulationNumber}" data-theratio="${theRatio}" data-pricediscount='${JSON.stringify(priceDiscount)}' data-productpricelist='${JSON.stringify(productPriceList)}' data-unitnum="${productMinEncapsulationNumber}" placeholder="江苏仓" data-defaultwarehouse="sz" data-type="js" data-jsstock="${jsWarehouseStockNumber}" value> <div class="unit "> <span class="cur-unit ">个</span> <i class="xl"></i> <div class="unit-contain" style="display: none;"> <div class="unit-type"> <span class="unit-one">个</span> <span class="unit-two">${productMinEncapsulationUnit}</span> </div> <i class="sl"></i> </div> </div> </li> <li class="totalPrice-li"> ${productMinEncapsulationNumber}个/${productMinEncapsulationUnit} 总额:<span class="goldenrod totalPrice">¥0</span> <div class="plus_mj"> <div class="plus-flag"> <span><span class="mj-name"></span>已优惠<span class="mj-money">0</span>元!</span> <s><i></i></s> </div> </div> </li> </ul> <button type="button" style=" width: 130px; height: 33px; border: none; border-radius: 2px; background: #ff7800; color: #fff; " class="pan-list-btn addCartBtn" data-theratio="${theRatio}" data-productcode="${productCode}">加入购物车</button> <ul class="pan"> <li class="pan-list"> <div class="stocks"> <span>近期成交${recentlySalesCount}单</span> </div> </ul> </div> </div> </div> </div> </div> </td> </tr> <tr class="more-channel-products items-overseas-products display-none hide-tr"> <td colspan="6" id="overseasList" oldbatchproductlist isgroupproduct="false" listlength="30" productid="${productId}"> </td> </tr> <tr class="more-channel-products items-hk-products display-none hide-tr"> <td colspan="6" id="hkProductList" oldbatchproductlist isgroupproduct="false" listlength="30" productid="${productId}"> </td> </tr> </tbody> </table>`; }).join(''); $('#listContainer').html(html); $('.cartnumbers').off('change').on('change', function () { let val = $(this).val(); if (!val) { return ; } const theratio = $(this).data("theratio"); val = Math.max(parseInt(val / theratio) * theratio, theratio); $(this).val(val); const productPriceList = $(this).data("productpricelist"); const priceDiscount = $(this).data("pricediscount"); const result = PriceCalculator.calculatePrice(val, theratio, productPriceList, priceDiscount); console.log(val) console.log('计算结果:', result); Base.getParentRow(this).find('.totalPrice').text('¥' + result.discountTotalPrice.toFixed(2)); }); $('.addCartBtn').off('click').on('click', function () { let num = [...Base.getParentRowWithFind(this, '.cartnumbers')] .reduce((a,b)=> a + (parseInt($(b).val()) || 0), 0); if (!num) { const newNum = parseInt($(this).data('theratio')); window.$message.error(`数量为空,已为您修正数量为:${newNum}!`, 3000); num = newNum; } Util.postFormAjax(`https://cart.szlcsc.com/cart/quick`, { productCode: $(this).data('productcode'), productNumber: num }).then(e => { if(JSON.parse(e).code === 200) { window.$message.success('加入购物车成功!', 3000); } }); }); } /** * 获取品牌商品列表 * @param brandsNameOrSearchText 品牌名称或者搜索框内容 * @param brandsId 品牌id,可为空,不为空时提高搜索准确率 * @param maxCount 最大商品数量,为空时返回所有商品 * @param stock 仓库,js/gd * @returns {Promise<unknown>} */ static getBrandsProducts_old(brandsNameOrSearchText, brandsId = null, maxCount = null, stock = 'js') { return new Promise((resolve, reject) => { const url = 'https://so.szlcsc.com/search'; let products = []; let counts = 0; const getData = (page) => { let data = { os: '', dp: '', sb: 1, // 价格从低排序 queryPlaceProduction: '', pn: page, c: '', k: brandsNameOrSearchText, tc: 0, pds: 0, pa: 0, pt: 0, gp: 0, queryProductDiscount: '', st: '', sk: brandsNameOrSearchText, searchSource: '', bp: '', ep: '', bpTemp: '', epTemp: '', stock: stock, needChooseCusType: '', 'link-phone': '', companyName: '', taxpayerIdNum: '', realityName: '', } if (brandsId) data.queryProductGradePlateId = brandsId Util.postFormAjax(url, data).then(res => { if (!res) return reject('获取品牌商品列表失败') res = typeof res === 'object' ? res : JSON.parse(res) if (!res.code || res.code !== 200) return reject(res.msg || '获取品牌商品列表失败') if (!res.result || !res.result.productRecordList || res.result.productRecordList.length === 0) return resolve(products) products = products.concat(res.result.productRecordList) counts += res.result.productRecordList.length if (maxCount && counts >= maxCount) return resolve(products) getData(page + 1) }).catch(err => { reject(err) }) } getData(1); }) } /** * 获取品牌商品列表(支持并行或单线程) * @param brandsNameOrSearchText 品牌名称或者搜索框内容 * @param brandsId 品牌id,可为空,不为空时提高搜索准确率 * @param maxCount 最大商品数量,为空时返回所有商品 * @param stock 仓库,js/gd * @param parallel 是否并行执行请求,默认为false(单线程) * @returns {Promise<unknown>} */ static getBrandsProducts(brandsNameOrSearchText, brandsId = null, maxCount = null, stock = 'js', parallel = false) { return new Promise((resolve, reject) => { const url = 'https://so.szlcsc.com/search'; let products = []; let counts = 0; // 获取单页数据 const getPageData = (page) => { let data = { os: '', dp: '', sb: 1, queryPlaceProduction: '', pn: page, c: '', k: brandsNameOrSearchText, tc: 0, pds: 0, pa: 0, pt: 0, gp: 0, queryProductDiscount: '', st: '', sk: brandsNameOrSearchText, searchSource: '', bp: '', ep: '', bpTemp: '', epTemp: '', stock: stock, needChooseCusType: '', 'link-phone': '', companyName: '', taxpayerIdNum: '', realityName: '', }; if (brandsId) data.queryProductGradePlateId = brandsId; return Util.postFormAjax(url, data) .then(res => { if (!res) throw new Error('获取品牌商品列表失败'); res = typeof res === 'object' ? res : JSON.parse(res); if (!res.code || res.code !== 200) throw new Error(res.msg || '获取品牌商品列表失败'); return res.result.productRecordList || []; }); }; // 单线程模式(顺序执行) const executeSequentially = (page) => { getPageData(page) .then(pageProducts => { if (pageProducts.length === 0) return resolve(products); products = products.concat(pageProducts); counts += pageProducts.length; if (maxCount && counts >= maxCount) { return resolve(products.slice(0, maxCount)); } executeSequentially(page + 1); }) .catch(err => reject(err)); }; // 并行模式(不考虑顺序) const executeInParallel = () => { // 先获取第一页,确定总页数 getPageData(1) .then(firstPageProducts => { if (firstPageProducts.length === 0) return resolve([]); // 假设每页数量相同,计算总页数 const estimatedTotalPages = maxCount ? Math.ceil(maxCount / firstPageProducts.length) : 10; // 默认最多10页(防止无限请求) // 生成所有页面的请求数组 const pageRequests = []; for (let i = 1; i <= estimatedTotalPages; i++) { pageRequests.push(getPageData(i)); } // 并行执行所有请求 Promise.all(pageRequests) .then(allPages => { const allProducts = allPages.flat(); if (maxCount) { resolve(allProducts.slice(0, maxCount)); } else { resolve(allProducts); } }) .catch(err => reject(err)); }) .catch(err => reject(err)); }; // 根据 parallel 参数选择执行模式 if (parallel) { executeInParallel(); } else { executeSequentially(1); } }); } } // 分类品牌颜色定时器开启状态,默认false let catalogBrandColorTaskIsStartStatus = false; // 搜索页启动 function searchStart() { // 每行追加到按钮组 const searchPageHelper = new SearchPageHelper(); searchPageHelper.appendLeftRowBtns(); searchPageHelper.appendSearchTbBtn(); // 优惠券信息获取 searchPageHelper.getAllCoupon(); // // 搜索页按钮组渲染 searchPageHelper.btnsRender(); // // 定时上色 if (!catalogBrandColorTaskIsStartStatus) { setInterval(SearchPageHelper.catalogBrandColor, 3000); catalogBrandColorTaskIsStartStatus = true; } const searchListHelper = new SearchListHelper(); searchListHelper.render(); } // 搜索页判断 let isSearchPage = () => location.href.includes('so.szlcsc.com/global.html') || location.href.includes('list.szlcsc.com/brand') || location.href.includes('list.szlcsc.com/catalog'); setInterval(function () { if (isSearchPage()) { searchStart(); } }, 1500) })()