// ==UserScript== // @name 京东单价助手 // @namespace http://greasyfork.org/ // @version 1.21 // @description 京东(jd.com)单价助手,增加了商品列表显示单价(每公斤/升价格)功能,帮助你在京东(jingdong)找到最划算的商品 // @author Yarmu // @match *://search.jd.com/* // @match *://list.jd.com/* // @match *://item.jd.com/* // @grant GM_setValue // @grant GM_getValue // @supportURL https://greasyfork.org/zh-CN/scripts/30522-%E4%BA%AC%E4%B8%9C%E5%8D%95%E4%BB%B7%E5%8A%A9%E6%89%8B // @downloadURL https://update.greasyfork.icu/scripts/30522/%E4%BA%AC%E4%B8%9C%E5%8D%95%E4%BB%B7%E5%8A%A9%E6%89%8B.user.js // @updateURL https://update.greasyfork.icu/scripts/30522/%E4%BA%AC%E4%B8%9C%E5%8D%95%E4%BB%B7%E5%8A%A9%E6%89%8B.meta.js // ==/UserScript== (function() { var host = window.location.host.toLowerCase(); window.priceTipEnabled = GM_getValue('jd_price_tip_enabled', true); if (host === 'list.jd.com' || host === 'search.jd.com') { addPriceTipListener('.p-price', addListPriceTip, 1000); addPriceTipListener('.f-sort', addListPriceButton); } else if (host === 'item.jd.com') { addPriceTipListener('.p-price', addItemPriceTip); addPriceTipListener('.p-price-plus', addItemPriceTip); addPriceTipListener('.p-price-sam', addItemPriceTip); } })(); function addPriceTipListener(tag, func, time) { var onModifiedFunc = function() { $(this).unbind("DOMSubtreeModified"); func.call(this); $(this).bind("DOMSubtreeModified", onModifiedFunc); }; var eachCallFunc = function() { $(tag).each(function() { if (!$(this).attr('priceTip')) { $(this).attr('priceTip', '1'); onModifiedFunc.call(this); } }); }; eachCallFunc(); if (time) { setInterval(eachCallFunc, time); } } function addListPriceTip() { if (!window.priceTipEnabled) return; //处理plus价格的情况 addPlusListPriceTip.call(this, '.price-plus-1'); addPlusListPriceTip.call(this, '.price-sams-1'); //处理列表价格 var priceItem = $(this).find('strong i'); var price = getFloat(priceItem.text()); if (isNaN(price)) { //处理广告和推广价格 priceItem = $(this).find('strong span'); price = getFloat(priceItem.text()); if (isNaN(price)) { priceItem = $(this).find('strong'); price = getFloat(priceItem.text()); if (isNaN(price)) { priceItem = $(this); price = getFloat(priceItem.text()); } } } if (isNaN(price)) return; var title = null; var index = 0; $(this).parent().find('.p-scroll .ps-wrap .ps-main .ps-item a').each(function(idx) { if ($(this).attr('class') === 'curr' && $(this).attr('title')) { title = $(this).attr('title').trim(); index = idx; return false; } }); var unit = getUnit(title, price); if (unit === null && index === 0) { title = $(this).parent().find('.p-name a em').text().trim(); if (!title) { title = $(this).parent().find('.p-name a').text().trim(); } unit = getUnit(title, price); } if (unit === null) return; var htm = ' (' + unit.price + '/' + unit.unit + ')'; title = '( 助手估重: ' + unit.capacity + unit.unit + ' = ' + unit.tip + ' )\n' + title; priceItem.append(htm); $(this).attr('priceCapacity', unit.capacity); $(this).attr('priceUnit', unit.unit); addPlusListPriceTip.call(this, '.price-plus-1'); addPlusListPriceTip.call(this, '.price-sams-1'); var tipItem = $(this).parent().find('.p-name a'); setTimeout(function() { tipItem.attr('title', title); }, 1000); } function addPlusListPriceTip(tag) { var plusItem = $(this).find(tag); if (plusItem.length === 0) return; var priceItem = plusItem.find('em'); var price = getFloat(priceItem.text()); if (isNaN(price)) return; var cap = parseFloat($(this).attr('priceCapacity')); if (isNaN(cap)) return; var unit = $(this).attr('priceUnit'); price = Math.round(parseFloat(price) / cap * 100) / 100; var htm = ' (' + price + '/' + unit + ')'; plusItem.insertAfter($(this)); plusItem.css({'display':'block'}); priceItem.append(htm); } function addListPriceButton() { var button = $(this).find('#priceTipButton'); if (button.length > 0) return; if(!$(this).text().trim()) return; var isEnabled = window.priceTipEnabled; button = $('单价助手'); $(this).append(button); button.click(function() { var nextEnabled = !window.priceTipEnabled; GM_setValue('jd_price_tip_enabled', nextEnabled); window.priceTipEnabled = nextEnabled; $(this).attr('class', (nextEnabled? 'curr': '')); SEARCH.sort(getQueryString('psort')); }); } function addItemPriceTip() { if (!window.priceTipEnabled) return; var priceItem = $(this).find('.price'); var price = getFloat(priceItem.text()); if (isNaN(price)) return; var title = $('.sku-name').text().trim(); var unit = getUnit(title, price); if (unit === null) return; var htm = ' (' + unit.price + '/' + unit.unit + ')'; title = ' ( 助手估重: ' + unit.capacity + unit.unit + ' = ' + unit.tip + ' )'; priceItem.append(htm); if (!window.hasAddItemPriceTip) { window.hasAddItemPriceTip = true; setTimeout(function() { $('#summary-weight .dd').append(title); $('#summary-weight').css({'display':'block'}); }, 1000); } } function getUnit(title, price) { if (!title) return null; if (price <= 0) return null; //处理包含:和送的情况 if (title.match(/:|[))】]送/)){ var titles = title.split(/:|[))】]送/); for (var t=0; t 3 && match[pos.pCount]) { var multiple = match[pos.pCount].match(/\d+/g); if (multiple) for (var i=0; i cap) { tip = ''; cap = 0; count = 0; } else { continue; } } un = unit; tip += match[0] + ' '; cap += capacity; ++count; } } //处理数量在其他位置的情况 if (count == 1 && pos.i !== 0) { var regZhn = '两一二三四五六七八九十'; var regMul = '(?:[^*x×满\\d])\\s*(\\d+|['+regZhn+'])['+regQuant+'](?!\\d+折|松)'; reg = new RegExp(regMul, 'i'); match = reg.exec(title); if (match) { var mul = regZhn.indexOf(match[1]); if (mul == -1) mul = parseInt(match[1]); else if (mul === 0) mul = 2; if (lastMul != mul) { cap *= mul; tip += match[0].substr(1, match[0].length-1).trim(); } } } if (cap > 0) { var unitPrice = parseFloat(price) / cap; //如果单价>1000元或<1元,太大或太小不显示 if (unitPrice > 1000 || unitPrice < 1) return null; else return { capacity: Math.round(cap * 10000) / 10000, unit: un, price: Math.round(unitPrice * 100) / 100, tip: tip.trim() }; } else return null; } function getFloat(str) { str = str.replace('¥','').trim(); if (!/^\d+\.?\d*$/.test(str)) return NaN; return parseFloat(str); } function getQueryString(name) { var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i"); var r = window.location.search.substr(1).match(reg); if (r !== null) return unescape(r[2]); return ''; }