// ==UserScript==
// @name JLC_SHOP_SEARCH_TOOL_2.0
// @namespace http://tampermonkey.net/
// @version 2.1.5
// @description 立创商城搜索页助手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.settings.title}
${this.settings.description ?
`
${this.settings.description}
` : ''}
${this.settings.showAction ? `
` : ''}
`);
// 设置图标尺寸
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 cachedFilterParams = null;
// 缓存的筛选参数(搜索页)
static cachedSearchFilterParams = null;
// 初始化默认选中状态
static defaultTabs = {
region: '江苏',
userType: "all"
};
/**
* 初始化请求拦截器,监听品牌页筛选请求
*/
static initRequestInterceptor() {
const brandTargetUrl = 'list.szlcsc.com/brand/product';
const searchTargetUrl = 'so.szlcsc.com/query/product';
// 拦截 XMLHttpRequest
const originalXhrOpen = XMLHttpRequest.prototype.open;
const originalXhrSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function(method, url, ...args) {
this._url = url;
this._method = method;
return originalXhrOpen.apply(this, [method, url, ...args]);
};
XMLHttpRequest.prototype.send = function(body) {
// 品牌页 GET 请求拦截
if (this._url && this._url.includes(brandTargetUrl)) {
const urlObj = new URL(this._url, window.location.origin);
const params = Object.fromEntries(urlObj.searchParams.entries());
SearchListHelper.cachedFilterParams = params;
console.log('[筛选拦截-品牌页] 缓存参数:', params);
}
// 搜索页 POST 请求拦截
if (this._url && this._url.includes(searchTargetUrl) && this._method?.toUpperCase() === 'POST') {
try {
const params = typeof body === 'string' ? JSON.parse(body) : body;
SearchListHelper.cachedSearchFilterParams = params;
console.log('[筛选拦截-搜索页] 缓存参数:', params);
} catch (e) {
console.warn('[筛选拦截-搜索页] 解析请求体失败:', e);
}
}
return originalXhrSend.apply(this, [body]);
};
// 拦截 fetch
const originalFetch = window.fetch;
window.fetch = function(url, options = {}) {
const urlStr = typeof url === 'string' ? url : url?.url || '';
// 品牌页 GET 请求拦截
if (urlStr.includes(brandTargetUrl)) {
const urlObj = new URL(urlStr, window.location.origin);
const params = Object.fromEntries(urlObj.searchParams.entries());
SearchListHelper.cachedFilterParams = params;
console.log('[筛选拦截-品牌页] (fetch) 缓存参数:', params);
}
// 搜索页 POST 请求拦截
if (urlStr.includes(searchTargetUrl) && options.method?.toUpperCase() === 'POST') {
try {
const body = options.body;
const params = typeof body === 'string' ? JSON.parse(body) : body;
SearchListHelper.cachedSearchFilterParams = params;
console.log('[筛选拦截-搜索页] (fetch) 缓存参数:', params);
} catch (e) {
console.warn('[筛选拦截-搜索页] 解析请求体失败:', e);
}
}
return originalFetch.apply(this, [url, options]);
};
console.log('[筛选拦截] 请求拦截器已初始化 (品牌页+搜索页)');
}
constructor() {
// 如果实例已存在,直接返回
if (SearchListHelper.instance) {
return SearchListHelper.instance;
}
// 保存当前实例
SearchListHelper.instance = this;
// 初始化请求拦截器
setTimeout(() => {
SearchListHelper.initRequestInterceptor();
}, 1500);
}
static async start(brandsNameOrSearchText, brandsId, maxCount, stock, parallel = false, onProgress = null) {
SearchListHelper.fetchStatus = false;
// 根据页面类型选择不同的数据获取方式
const isSearchPage = location.href.includes('so.szlcsc.com');
if (isSearchPage) {
// 搜索页使用 getSearchProducts 方法
SearchListHelper.listData = await SearchListHelper.getSearchProducts(brandsNameOrSearchText, maxCount, onProgress);
} else {
// 品牌页使用 getBrandsProducts_new 方法
SearchListHelper.listData = await SearchListHelper.getBrandsProducts_new(brandsNameOrSearchText, brandsId, maxCount, stock, onProgress);
}
console.log(SearchListHelper.listData);
SearchListHelper.setCouponSign();
SearchListHelper.renderMinPriceSearch();
}
/**
* 获取搜索页商品列表(使用缓存的筛选参数)
* @param keyword 搜索关键词
* @param maxCount 最大商品数量
* @param onProgress 进度回调函数
* @returns {Promise}
*/
static getSearchProducts(keyword, maxCount = null, onProgress = null) {
return new Promise((resolve, reject) => {
const url = 'https://so.szlcsc.com/query/product';
let products = [];
let counts = 0;
const getData = (page) => {
// 触发进度回调
if (onProgress) {
onProgress({ loaded: counts, page: page, status: 'loading' });
}
// 默认参数
let data = {
"currentPage": page,
"pageSize": 30,
"catalogIdFilter": "",
"brandIdFilter": "",
"standardFilter": "",
"arrangeFilter": "",
"labelFilter": "",
"authenticationFilter": "",
"keyword": keyword,
"sortNumber": 0,
"satisfyStockType": "",
"startPrice": "",
"endPrice": "",
"demandNumber": "",
"spotFilter": 1,
"discountFilter": 1,
"hasDataFile": false,
"brandPlaceFilter": "",
"secondKeyword": "",
"queryParameterValue": "",
"lastParamName": ""
};
// 如果有缓存的筛选参数,合并到请求参数中
if (SearchListHelper.cachedSearchFilterParams) {
const cached = SearchListHelper.cachedSearchFilterParams;
data = {
...data,
catalogIdFilter: cached.catalogIdFilter || "",
brandIdFilter: cached.brandIdFilter || "",
standardFilter: cached.standardFilter || "",
arrangeFilter: cached.arrangeFilter || "",
labelFilter: cached.labelFilter || "",
authenticationFilter: cached.authenticationFilter || "",
brandPlaceFilter: cached.brandPlaceFilter || "",
startPrice: cached.startPrice || "",
endPrice: cached.endPrice || "",
queryParameterValue: cached.queryParameterValue || "",
lastParamName: cached.lastParamName || ""
};
console.log('[筛选同步-搜索页] 使用缓存的筛选参数:', data);
}
Util.postAjaxJSON(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 || '获取搜索商品列表失败');
const list = res?.result?.searchResult?.productRecordList;
if (!list || list.length === 0) {
if (onProgress) onProgress({ loaded: counts, page: page, status: 'done' });
return resolve(products);
}
products = products.concat(list);
counts += list.length;
if (maxCount && counts >= maxCount) {
if (onProgress) onProgress({ loaded: counts, page: page, status: 'done' });
return resolve(products);
}
getData(page + 1);
}).catch(err => {
reject(err);
});
};
getData(1);
});
}
// 设置商品列表的券标记
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);
})
}
// 渲染页面
async renderListItems(onProgress = null) {
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;
}
await SearchListHelper.start(searchValue, brandId, 300, stock, false, onProgress);
}
render() {
if (!this.btnStatus) {
$('head').prepend(``);
$('body').prepend(`
`);
// 使用 jQuery 为按钮绑定点击事件
const self = this;
$('#searchListButton').on('click', async function() {
const $btn = $(this);
const isShow = $btn.attr('show') !== 'true';
$btn.attr('show', isShow);
if (isShow) {
$('#cardContainer').show();
// 初始化带加载动画的空状态
const emptyState = new EmptyState('#listContainer', {
icon: 'loading-spinner', // 使用加载动画
title: '正在加载数据...',
description: '请稍候,正在获取商品列表'
});
emptyState.show();
// 进度回调函数
const onProgress = (progress) => {
if (progress.status === 'loading') {
emptyState.update({
title: `正在加载第 ${progress.page} 页...`,
description: `已加载 ${progress.loaded} 条商品数据`
});
} else if (progress.status === 'done') {
emptyState.update({
title: '加载完成',
description: `共加载 ${progress.loaded} 条商品数据`
});
}
};
try {
await self.renderListItems(onProgress);
} finally {
emptyState.hide();
emptyState.destroy();
}
} else {
$('#cardContainer').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(`数据加载中...(共${Math.min(totalPage, 30)}页,正在加载第1页...`);
// 并发执行请求并跟踪进度
const results = await allWithProgress(requestQueue, ({total, cur}) => {
progressContainer.html(`数据加载中...(共${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() {
// 修复不同接口productPriceList的层级关系
SearchListHelper.listData.forEach(item => {
// 1. 安全地将 item 的 productVO 属性作为回退源
// 如果 item.productVO 不存在,则使用空对象 {}
const fallbackVO = item.productVO || {};
// 2. 将 item 的属性(排除 productVO 自身)与 fallbackVO 合并。
// 此步骤是关键:
// - {...fallbackVO} 提供 productVO 中的所有值作为默认值。
// - {...item} 覆盖 productVO 中与 item 重名的值,确保 item 自身的值是高优先级的。
const { productVO, ...itemProps } = item; // 暂时排除 productVO 属性
const finalSource = {
...fallbackVO, // 优先级低:提供来自 productVO 的回退值
...itemProps // 优先级高:覆盖所有来自 item 的值
};
// 3. 从最终合并的 finalSource 对象中解构所有变量
// 现在解构出来的变量,如果 item 中有,就是 item 的值;如果 item 中没有,就是 item.productVO 中的值。
Object.assign(item, finalSource);
});
// 如果广东仓和江苏仓同时没有货的话,那么就属于订货商品,不需要显示
// 如果没有价格区间,证明是停售商品
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 `
${Object.keys(paramLinkedMap).map(key => {
return `-
${key}:
${paramLinkedMap[key]}
`
}).join('')}
${listProductDiscount != null && listProductDiscount < 10 ? `
-
${listProductDiscount}折
${productPriceList.map(item => {
return `- ${item.startPurchasedNumber * theRatio}+:
`;
}).join('')}
- 折后价
${productPriceList.map(item => {
return `- ¥${parseFloat((item.productPrice * (listProductDiscount || 10) / 10).toFixed(6))}
`;
}).join('')}
- 原价
${productPriceList.map(item => {
return `- ¥${item.productPrice}
`;
}).join('')}
` : ''}
最低购入价: ${parseFloat((theRatio * productPriceList[0].productPrice * (listProductDiscount || 10) / 10).toFixed(6))}
${productPriceList.map(item => {
const discountPrice = parseFloat((item.productPrice * (listProductDiscount || 10) / 10).toFixed(6));
return `-
${item.startPurchasedNumber * theRatio}+:
¥${discountPrice}
`;
}).join('')}
|
|
|
|
|
`;
}).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}
*/
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, /*品牌id*/
queryProductDiscount: '',
st: '',
sk: brandsNameOrSearchText,
searchSource: '',
bp: '',
ep: '',
bpTemp: '',
epTemp: '',
stock: stock,
needChooseCusType: '',
'link-phone': '',
companyName: '',
taxpayerIdNum: '',
realityName: '',
}
if (brandsId) {
// data.queryProductGradePlateId = brandsId
data.gp = 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 brandsName 品牌名称
* @param brandsId 品牌id,可为空,不为空时提高搜索准确率
* @param maxCount 最大商品数量,为空时返回所有商品
* @param stock 仓库,js/gd
* @param onProgress 进度回调函数,参数为 {loaded: number, page: number}
* @returns {Promise}
*/
static getBrandsProducts_new(brandsName, brandsId = null, maxCount = null, stock = 'js', onProgress = null) {
// sortNumber 10 广东有货排序
// sortNumber 12 江苏有货排序
// sortNumber 6 销量排序
return new Promise((resolve, reject) => {
const url = 'https://list.szlcsc.com/brand/product?';
let products = [];
let counts = 0;
const sortNumber = !brandsId || brandsId.length == 0 ? 0 : (stock == 'js' ? 1 : 2);
const getData = (page) => {
// 触发进度回调
if (onProgress) {
onProgress({ loaded: counts, page: page, status: 'loading' });
}
// 默认参数
let data = {
"currentPage": page,
"pageSize": 30,
"catalogIdFilter": "",
"brandIdFilter": brandsId + "",
"standardFilter": "",
"arrangeFilter": "",
"labelFilter": "",
"keyword": brandsName,
"sortNumber": 6,
"satisfyStockType": "",
"startPrice": "",
"endPrice": "",
"demandNumber": "",
"spotFilter": 1,
"discountFilter": 1,
"hasDataFile": false,
"brandPlaceFilter": "",
"secondKeyword": "",
"queryParameterValue": "",
"lastParamName": ""
}
// 如果有缓存的筛选参数,合并到请求参数中
if (SearchListHelper.cachedFilterParams) {
const cached = SearchListHelper.cachedFilterParams;
// 保留缓存中的筛选条件,但覆盖分页参数
data = {
...data,
catalogIdFilter: cached.catalogIdFilter || "",
standardFilter: cached.standardFilter || "",
arrangeFilter: cached.arrangeFilter || "",
labelFilter: cached.labelFilter || "",
brandPlaceFilter: cached.brandPlaceFilter || "",
smtLabelFilter: cached.smtLabelFilter || "",
startPrice: cached.startPrice || "",
endPrice: cached.endPrice || "",
queryParameterValue: cached.queryParameterValue || "",
lastParamName: cached.lastParamName || ""
};
console.log('[筛选同步] 使用缓存的筛选参数:', data);
}
Util.getAjax(url + Util.jsonToUrlParam(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 || '获取品牌商品列表失败')
const list = res?.result?.searchResult?.productRecordList;
if (!list || list.length === 0) {
if (onProgress) onProgress({ loaded: counts, page: page, status: 'done' });
return resolve(products);
}
products = products.concat(list)
counts += list.length
if (maxCount && counts >= maxCount) {
if (onProgress) onProgress({ loaded: counts, page: page, status: 'done' });
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}
*/
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 getPageDataByNewApi = (page) => {
let data = {
currentPage: 1,
pageSize: 30,
catalogIdFilter:526,
brandIdFilter:15352,
standardFilter: '',
brandPlaceFilter: '',
labelFilter: '',
arrangeFilter: '',
smtLabelFilter: '',
spotFilter:1,
discountFilter: 1,
startPrice: '',
endPrice: '',
sortNumber: 0,
queryParameterValue: '',
lastParamName: '',
keyword: '',
secondKeyword: '',
hasDataFile:false,
demandNumber: '',
satisfyStockType: ''
}
}
// 获取单页数据
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, /*品牌id*/
queryProductDiscount: '',
st: '',
sk: brandsNameOrSearchText,
searchSource: '',
bp: '',
ep: '',
bpTemp: '',
epTemp: '',
stock: stock,
needChooseCusType: '',
'link-phone': '',
companyName: '',
taxpayerIdNum: '',
realityName: '',
};
if (brandsId) {
// data.queryProductGradePlateId = brandsId;
data.gp = 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)
})()