京东秒杀价格过滤

按价格过滤并排序(价格升序)秒杀商品列表

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         京东秒杀价格过滤
// @version      0.4.1.4
// @icon         https://www.jd.com/favicon.ico
// @description  按价格过滤并排序(价格升序)秒杀商品列表
// @author       You!
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_addStyle
// @include      *://miaosha.jd.com/
// @include      *://miaosha.jd.com/pinpai.html?brand_id=*
// @include      *://miaosha.jd.com/brand.html?brand_id=*
// @include      *://miaosha.jd.com/category.html?cate_id=*
// @require      https://code.jquery.com/jquery-3.3.1.slim.min.js
// @run-at       document-end
// @namespace    https://greasyfork.org/zh-CN/scripts/33454-京东秒杀价格过滤
// ==/UserScript==

(function() {
    'use strict';

    /**********************************************************/
    //如果要过滤商品名,启用该函数,但是需要自己实现过滤逻辑:
    //    pname 传入商品名
    //    catId 传入商品分类编号,编号代表的意义请自行对照京东秒杀的页面地址
    //    命中返回 true, 否侧返回 false
    /*在行首插入单行注释(//)以启用该商品名过滤函数
    function filterPName(pname, cat, catId) {
        switch(cat) {
            case 'mainpage': //首页
                break;

            case 'pinpai': //品牌秒杀
                break;

            case 'brand': //品类秒杀
                break;

            case 'category': //分类秒杀
                switch(parseInt(catId)) {
                    case 34: //潮流鞋靴
                        if (!/男/.test(pname)) return false;
                    break;
                }
                break;
        }
        return true;
    }
    /**********************************************************/

    //扫描页面的间隔频率时间
    var timerFreq = 500;

    var currentPathname = location.pathname.toLowerCase();
    var currentPageKey = (
        currentPathname !== '/' ?
        'jd_miaosha_' + location.pathname.split(/[\/\.]/)[1].toLowerCase() + '_' + location.search.split('&')[0].split('=')[1] :
        'jd_miaosha_mainpage_0'
    );
    var priceInfo = loadPagePrice(currentPageKey);

    uiInit();
    doFilter();

    //向页面右下角的浮动工具栏尾部追加价格过滤部件
    function uiInit() {
        switch(currentPathname) {
            case '/':
                var timeline = $('#timeline a.timeline_item_link');
                if (timeline.length > 0) {
                    var waitTimelineLoad = function() {
                        if ($('div.skwrap ul.seckill_mod_goodslist').attr('priceFiltered') === undefined) {
                            doFilter();
                        }
                        else setTimeout(waitTimelineLoad, timerFreq);
                    };
                    timeline.click(function() {
                        setTimeout(function() {
                            $('div.skwrap ul.seckill_mod_goodslist').attr('priceFiltered', true);
                            saveCurrentPagePrice();
                            waitTimelineLoad();
                        }, 0);
                    });
                }
                else {
                    setTimeout(uiInit, timerFreq);
                    return;
                }
                break;
        }
        var uiPos = $('#sk_mod_er ul');
        if (uiPos.length > 0) {
            var checked = priceInfo.usePageSetting ? ' checked="true"' : '';
            GM_addStyle('.fltReload{font-size:10px;font-weight:700;color:#e23a3a;background-color:#fff;border-radius:5px;padding:0px 5px}'+
                        '.fltCount{display:inline-block;text-align:right;width:35px}'
                       );
            uiPos.append($(
                '<li class="sk_mod_er_mobile" style="color:white;padding:7px;float:left;text-align:left">'+
                '<nobr>正则过滤<div class="fltCount"><a class="fltReload" href="javascript:;" id="fltRetPName" title0="符合商品名过滤条件的商品数量:">刷新</a></div></nobr><br>'+
                '<input id="fltNameRegx" type="text" style="width:80px;margin-top:3px" value="'+priceInfo.regstr+'"/><br/>'+
                '<nobr>价格范围<div class="fltCount"><a class="fltReload" href="javascript:;" id="fltRetPrice" title0="同时符合价格范围过滤条件的商品数量:">刷新</a></div></nobr><br>'+
                '<input id="fltPriceMin" type="number" style="width:80px;margin-top:3px" value="'+priceInfo.min+'"/><br/>'+
                '<input id="fltPriceMax" type="number" style="width:80px;margin-top:5px" value="'+priceInfo.max+'"/><br/>'+
                '<nobr><input id="fltPriceOne" type="checkbox" style="margin:6px -3px 0px 0px"'+checked+'"/><label for="fltPriceOne" style="float:right;margin-top:2px">使用独立设置</label></nobr><br/>'+
                '</li>'
            ));
            $('a.fltReload,#fltNameRegx,#fltPriceMin,#fltPriceMax').change(function() {
                setTimeout(function() {
                    saveCurrentPagePrice();
                    doFilter();
                }, 0);
            });
            $('#fltPriceOne').click(function() {
                var oldChecked = !this.checked;
                setTimeout(function() {
                    saveCurrentPagePrice(oldChecked);
                    $('#fltPriceMin').attr('value', priceInfo.min);
                    $('#fltPriceMax').attr('value', priceInfo.max);
                    doFilter();
                }, 0);
            });
        } else setTimeout(uiInit, timerFreq);
    }

    //对页面执行过滤排序操作
    function doFilter() {
        var counts, counts2;
        switch(currentPathname) {
            case '/': //秒杀首页
                var g0 = $('div.skwrap');
                var mg = $('div.moregoods');
                //先加载“更多好货”
                if (mg.hasClass('o2loading')) {
                    mg.removeAttr('style');
                }
                else if (mg.hasClass('o2data-lazyload')) {
                    var loadMoreGoods = function() {
                        if (unsafeWindow.o2widgetLazyload !== undefined && unsafeWindow.o2widgetLazyload.detectRender !== undefined) {
                            mg.attr('style', 'position:fixed;top:0;display:hidden');
                            unsafeWindow.o2widgetLazyload.detectRender();
                        }
                        else setTimeout(loadMoreGoods, timerFreq);
                    };
                    loadMoreGoods();
                }
                else {
                    mg.css('min-height', '0');
                    //价格过滤
                    counts = filterGoods(g0); //正在抢购
                    //秒杀首页“即将开始”时段
                    if (mg.css('display') !== 'none') {
                        mg.removeAttr('style');
                        counts2 = filterGoods(mg); //更多好货
                    }

                    //处理“正在抢购”列表中的其他类型方块
                    var ul = g0.find('ul.seckill_mod_goodslist');
                    var all = groupObjs(ul.find('>li'), function(k, o) {
                        o = $(o);
                        if (o.hasClass('seckill_mod_goods')) return 0;
                        if (o.hasClass('spsk_brand_list_item')) return 1;
                        if (o.hasClass('brand_fullcut_item')) return 2;
                        return 3;
                    });
                    for(var i=0; i<4; ++i) {
                        ul.append($(all[i]).remove());
                    }
                    //强制加载方块内的图片
                    forceLoadLazyImgs(g0);
                }
                break;

            case '/pinpai.html': //品牌秒杀
            case '/brand.html': //品类秒杀
                counts = filterGoods($('div.bprd'));
                break;

            case '/category.html': //分类秒杀
                counts = filterGoods($('div.catinfo_seckillnow')); //当天正在秒杀的商品
                counts2 = filterGoods($('div.catinfo_startsoon')); //明日秒杀的商品
                break;
        }
        if (counts && counts[0] > 0) {
            var fltRetPName = counts[1];
            var fltRetPrice = counts[2];
            if (counts2) {
                fltRetPName += counts2[1];
                fltRetPrice += counts2[2];
            }
            var tagA = $('#fltRetPName');
            tagA.text(fltRetPName);
            tagA.attr('title', tagA.attr('title0') + counts[1] + (counts2 ? ' + ' + counts2[1] : ''));

            tagA = $('#fltRetPrice');
            tagA.text(fltRetPrice);
            tagA.attr('title', tagA.attr('title0') + counts[2] + (counts2 ? ' + ' + counts2[2] : ''));
        }
        else setTimeout(doFilter, timerFreq);
    }

    //对页面内的一个具体分类执行过滤排序操作
    function filterGoods(goodsList) {
        var goods = goodsList.find('li.seckill_mod_goods');
        var fltRetPName = 0;
        var fltRetPrice = 0;
        if (goods.length > 0) {
            fltRetPName = goods.length;

            var groups = groupObjs(goods, function(k, o) {
                o = $(o);
                //记录原始索引位置
                if (o.attr('idx') === undefined) o.attr('idx', k);
                //按商品名过滤
                if ((typeof(filterPName) === 'function' && !filterPName(getProductName(o), priceInfo.pageCat, priceInfo.pageCatId)) ||
                    (priceInfo.regexp && !priceInfo.regexp.test(getProductName(o)))) {
                    --fltRetPName;
                    return false;
                }
                //按价格过滤
                var price = getPrice(o);
                if (price.now < priceInfo.min || (0 < priceInfo.max && priceInfo.min <= priceInfo.max && priceInfo.max < price.now)) return false;
                //标注优惠力度
                var priceOff = price.pre - price.now;
                var discount = price.now / price.pre;
                var goodsInfo = o.find('.seckill_mod_goods_info');
                if (discount <= 0.1) {
                    goodsInfo.attr('title', '1折以下(优惠'+priceOff+')').css('background-color', 'gold'); //1折以下
                } else if (discount <= 0.3) {
                    goodsInfo.attr('title', '3折以下(优惠'+priceOff+')').css('background-color', 'springgreen'); //3折以下
                } else if (discount <= 0.5) {
                    goodsInfo.attr('title', '5折以下(优惠'+priceOff+')').css('background-color', '#BBFFEE'); //5折以下
                } else if (discount <= 0.75) {
                    goodsInfo.attr('title', '75折以下(优惠'+priceOff+')').css('background-color', '#CCEEFF'); //75折以下
                } else if (priceOff >= 90) {
                    goodsInfo.attr('title', '优惠超过90('+priceOff+')').css('background-color', '#FFEEEE'); //优惠超过90
                } else {
                    goodsInfo.removeAttr('title').css('background-color', '');
                }
                //命中
                return true;
            });

            var niceGoods = groups[true];
            if (niceGoods && niceGoods.length > 0) {
                fltRetPrice = niceGoods.length;

                //排序
                niceGoods.sort(function(g1, g2) {
                    return getPrice(g1).now - getPrice(g2).now || //<---------------------------------------如果要降序,修改这里
                        $(g1).attr('idx') - $(g2).attr('idx');
                });
                //取消图片延迟加载
                forceLoadLazyImgs(niceGoods);

                //如果上一次过滤结果无符合条件商品,隐藏提示
                var noGoods = goodsList.find('#noGoods');
                if (noGoods.length == 1) groups[false].push(noGoods[0]);
            } else {
                niceGoods = goodsList.find('#noGoods')[0] || $('<center id="noGoods"><h2><br/>该分类下的所有商品都不符合过滤条件。<br/><br/><br/></h2></center');
            }

            //替换原内容
            $(niceGoods).css('display', '');
            $(groups[false]).css('display', 'none');
            $(goods[0].closest('ul')).prepend($(niceGoods).remove());
        }
        return [goods.length, fltRetPName, fltRetPrice];
    }

    //取消图片延迟加载
    function forceLoadLazyImgs(elms) {
        var lazyImgs = $(elms).find('img[data-lazy-img][data-lazy-img!="done"]');
        if (lazyImgs.length > 0) lazyImgs.each(function(idx) {
            var img = $(this);
            this.src = img.attr('data-lazy-img');
            img.removeAttr('data-lazy-img');
        });
    }

    //加载/保存页面价格设置
    function loadPagePrice(pageKey) {
        var g_min = GM_getValue('jd_miaosha_price_min');
        var g_max = GM_getValue('jd_miaosha_price_max');
        if (g_min === undefined) g_min = 0;
        if (g_max === undefined) g_max = 0;

        var p_min = GM_getValue(pageKey + '_price_min');
        var p_max = GM_getValue(pageKey + '_price_max');
        if (p_min === undefined) p_min = 0;
        if (p_max === undefined) p_max = 0;

        var checked = GM_getValue(pageKey);
        var min = checked ? p_min : g_min;
        var max = checked ? p_max : g_max;

        var rsrc = GM_getValue(pageKey + '_name_regex_src');
        if (rsrc === undefined) rsrc = '';
        var rflg = GM_getValue(pageKey + '_name_regex_flg');
        if (rflg === undefined) rflg = '';
        var re = rsrc.length > 0 ? new RegExp(rsrc, rflg) : undefined;
        var rs = rflg.length > 0 ? re.toString() : rsrc;

        var parts = pageKey.split('_');
        return {
            pageKey: pageKey,
            pageCat: parts[parts.length - 2],
            pageCatId: parts[parts.length - 1],
            usePageSetting: checked,
            min: min,
            max: max,
            p_min: p_min,
            p_max: p_max,
            g_min: g_min,
            g_max: g_max,
            regstr: rs,
            regexp: re,
        };
    }
    function saveCurrentPagePrice(oldChecked) {
        var min = getFloat($('#fltPriceMin').first().val());
        var max = getFloat($('#fltPriceMax').first().val());
        var keyMin, keyMax;

        //更新
        priceInfo.usePageSetting = $('#fltPriceOne').first().is(':checked');
        if (oldChecked === undefined) oldChecked = priceInfo.usePageSetting;
        if (oldChecked) {
            priceInfo.p_min = min;
            priceInfo.p_max = max;
            keyMin = currentPageKey + '_price_min';
            keyMax = currentPageKey + '_price_max';
        }
        else {
            priceInfo.g_min = min;
            priceInfo.g_max = max;
            keyMin = 'jd_miaosha_price_min';
            keyMax = 'jd_miaosha_price_max';
        }
        if (priceInfo.usePageSetting) {
            priceInfo.min = priceInfo.p_min;
            priceInfo.max = priceInfo.p_max;
        }
        else {
            priceInfo.min = priceInfo.g_min;
            priceInfo.max = priceInfo.g_max;
        }
        priceInfo.regstr = $('#fltNameRegx').first().val().trim();
        if (priceInfo.regstr.length > 0) {
            var r = /^\/(.*)\/([igm]{0,3})$/.exec(priceInfo.regstr);
            priceInfo.regexp = r ? new RegExp(r[1], r[2]) : new RegExp(priceInfo.regstr);
        }
        else priceInfo.regexp = undefined;

        //保存
        if (min === 0) GM_deleteValue(keyMin); else GM_setValue(keyMin, min);
        if (max === 0) GM_deleteValue(keyMax); else GM_setValue(keyMax, max);

        if (priceInfo.usePageSetting || priceInfo.p_min > 0 || priceInfo.p_max > 0)
            GM_setValue(currentPageKey, priceInfo.usePageSetting);
        else GM_deleteValue(currentPageKey);

        var regexKey = currentPageKey + '_name_regex';
        if (priceInfo.regexp) {
            var src = priceInfo.regexp.source;
            if (src.length > 0 && src !== '(?:)')
                GM_setValue(regexKey + '_src', priceInfo.regexp.source);
            else GM_deleteValue(regexKey + '_src');

            var flg = priceInfo.regexp.flags;
            if (flg.length > 0)
                GM_setValue(regexKey + '_flg', flg);
            else GM_deleteValue(regexKey + '_flg');
        }
        else {
            GM_deleteValue(regexKey + '_src');
            GM_deleteValue(regexKey + '_flg');
        }
    }

    //获取商品名
    function getProductName(elm) {
        return $(elm).find('.seckill_mod_goods_title').first().text().trim();
    }
    //获取商品价格
    function getPrice(elm) {
        return {
            now: getFloat($(elm).find('.seckill_mod_goods_price_now').first().text()),
            pre: getFloat($(elm).find('.seckill_mod_goods_price_pre').first().text())
        };
    }
    //字串转浮点数
    function getFloat(str) {
        str = str.replace('¥','').replace('¥','').trim();
        if (!/^-?(\d+|\d+\.\d+|\.\d+)([eE][-+]?\d+)?$/.test(str)) return 0; //非浮点数字串
        return parseFloat(str);
    }
})();

function groupObjs(objs, callback) {
    if (objs instanceof jQuery) objs = $.makeArray(objs);
    var ret = {};
    for(var i=0; i<objs.length; ++i) {
        var obj = objs[i];
        var key = callback(i, obj);
        if(!(key in ret)) ret[key] = [];
        ret[key].push(obj);
    }
    return ret;
}