// ==UserScript== // @name JLC_SHOP_SEARCH_TOOL_2.0 // @namespace http://tampermonkey.net/ // @version 2.1.3 // @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** // @require https://gitee.com/mlx6_admin/public_resource_lc/raw/master/public/jquery-351.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 = $(`
`); // 设置图标尺寸 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) { $(' `) // 多选新人券 $('.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 allCouponNotNew = Object.values(resultMap).flat(); // 优惠券处理 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(` `) } 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(``); $btn.before(``); } }); $('.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() || ''; let brandId = null; if (location.pathname.indexOf('brand') >= 0) { brandId = /\d+/.exec(location.pathname)[0] || null; } SearchListHelper.start(searchValue, brandId, 300, stock, false); } render() { if (!this.btnStatus) { $('head').prepend(``); $('body').prepend(`