京东历史价格走势图 by 京价保

在京东商品页面增加历史价格走势图

  1. // ==UserScript==
  2. // @name 京东历史价格走势图 by 京价保
  3. // @namespace 京价保
  4. // @version 0.2
  5. // @description 在京东商品页面增加历史价格走势图
  6. // @author 京价保
  7. // @match *://item.jd.com/*
  8. // @match *://re.jd.com/*
  9. // @run-at document-end
  10. // @grant GM_xmlhttpRequest
  11. // @connect api.zaoshu.so
  12. // @require https://cdn.jsdelivr.net/npm/@antv/g2@4.0.14/dist/g2.min.js
  13. // @require https://gf.qytechs.cn/libraries/GM_setStyle/0.0.15/GM_setStyle.js
  14. // ==/UserScript==
  15.  
  16. (function () {
  17. "use strict";
  18.  
  19. let styleNode = GM_setStyle({
  20. data: `
  21. .jjbPriceChart{
  22. position: static;
  23. float: left;
  24. width: 100%;
  25. background-color: #fff;
  26. border: 1px solid #eee;
  27. }
  28.  
  29. .jjbPriceChart .title {
  30. font-size: 14px;
  31. line-height: 24px;
  32. height: 28px;
  33. padding: 6px 10px;
  34. background-color: #f7f7f7;
  35. border-bottom: 1px solid #eee;
  36. }
  37.  
  38. .g2-tooltip{
  39. position: absolute;
  40. background: #ffffffe0;
  41. border: 1px solid #dededec2;
  42. padding: 1em 2em;
  43. min-width: 180px;
  44. transition: all 0.4s ease-out;
  45. }
  46.  
  47. .g2-tooltip .promotions li{
  48. font-size: 12px;
  49. margin-bottom: 0.5em;
  50. }
  51.  
  52. .g2-tooltip .g2-tooltip-list li{
  53. font-size: 14px;
  54. margin-bottom: 0.5em;
  55. }
  56.  
  57. .g2-tooltip .tag{
  58. color: #df3033;
  59. background: 0 0;
  60. border: 1px solid #df3033;
  61. padding: 2px 3px;
  62. margin-right: 5px;
  63. display: inline-block;
  64. line-height: 16px;
  65. }
  66.  
  67. .jjbPriceChart .provider {
  68. padding: 3px 10px;
  69. position: absolute;
  70. right: 5px;
  71. bottom: 5px;
  72. }
  73.  
  74. #jjbPriceChart{
  75. width: 100%;
  76. background: #fff;
  77. }
  78.  
  79. #disablePriceChart{
  80. float: right;
  81. padding: 0px 4px;
  82. cursor: pointer;
  83. font-size: 16px;
  84. font-weight: 600;
  85. }
  86.  
  87. #jjbPriceChart .no_data,
  88. #jjbPriceChart .loading {
  89. text-align: center;
  90. padding: 20px;
  91. background-position-y: top;
  92. padding-top: 30px;
  93. margin-top: 10px;
  94. }
  95.  
  96. .first_area_md {
  97. height: auto !important;
  98. position: relative;
  99. }
  100.  
  101. .first_area_md .jjbPriceChart{
  102. margin-top: 20px;
  103. }
  104.  
  105. .price_notice{
  106. display: none;
  107. }
  108.  
  109. .utm_source-notice{
  110. text-align: center;
  111. background: #fdedd2;
  112. color: #947219;
  113. padding: 0.2em .6em;
  114. }
  115.  
  116. .utm_source-notice .report{
  117. float: right;
  118. cursor: pointer;
  119. color: #cc1f1f;
  120. background: #fff;
  121. padding: 0px 10px;
  122. border-radius: 2px;
  123. }
  124.  
  125. .reportUtmSource .weui-dialog{
  126. max-width: 500px;
  127. background: #f8f8f8;
  128. }
  129.  
  130. #specialPromotion {
  131. width: 80%;
  132. display: inline-block;
  133. height: 22px;
  134. line-height: 24px;
  135. }
  136.  
  137. .special-promotion-item{
  138. padding: 3px 10px;
  139. vertical-align: middle;
  140. }
  141.  
  142. .special-promotion-item a{
  143. font-size: 14px;
  144. vertical-align: middle;
  145. }
  146.  
  147. .special-promotion-item .icon{
  148. padding-right: 10px;
  149. float: left;
  150. }
  151.  
  152.  
  153. #specialPromotion .promotions{
  154. float: left;
  155. width: 80%;
  156. }
  157.  
  158. #specialPromotion .controller{
  159. width: 20%;
  160. float: right;
  161. display: flex;
  162. align-items: flex-end;
  163. justify-content: flex-end;
  164. padding: 8px 0px;
  165. }
  166.  
  167. #specialPromotion .controller .item__child {
  168. cursor: pointer;
  169. min-width: 12px;
  170. min-width: 1.2rem;
  171. min-height: 4px;
  172. min-height: 0.4rem;
  173. display: block;
  174. margin: 0 3px;
  175. border: 1px solid #ddd;
  176. background-color: #dddddd;
  177. }
  178.  
  179. #specialPromotion .controller .item__child.on {
  180. background-color: #7abd53;
  181. }
  182.  
  183. #specialPromotion .controller .item__child:hover {
  184. background-color: #096
  185. }
  186. `,
  187. });
  188.  
  189. let urlInfo = /(https|http):\/\/item.jd.com\/([0-9]*).html/g.exec(
  190. window.location.href
  191. );
  192. if (window.location.host == "re.jd.com") {
  193. urlInfo = /(https|http):\/\/re.jd.com\/cps\/item\/([0-9]*).html/g.exec(
  194. window.location.href
  195. );
  196. }
  197. let sku = urlInfo[2];
  198.  
  199. console.log("sku", sku);
  200.  
  201. let priceChartDOM = `
  202. <div class="jjbPriceChart">
  203. <h4 class="title">
  204. 价格走势
  205. <select name="days">
  206. <option value="30">最近30天</option>
  207. <option value="60">最近60天</option>
  208. <option value="90">最近90天</option>
  209. </select>
  210. <div id="specialPromotion">
  211. </div>
  212. </h4>
  213. <div id="jjbPriceChart">
  214. <div class="ELazy-loading loading">加载中</div>
  215. </div>
  216. <span class="provider"><a href="https://blog.jjb.im/price-chart.html" target="_blank">由京价保提供</a></span>
  217. </div>
  218. `;
  219. if ($(".product-intro").length > 0) {
  220. $(".product-intro").append(priceChartDOM);
  221. }
  222.  
  223. if ($(".first_area_md").length > 0) {
  224. $(".first_area_md").append(priceChartDOM);
  225. }
  226.  
  227. function timestampToDateNumber(timestamp) {
  228. return new Date(timestamp).toISOString().slice(0, 10).replace(/-/g, "");
  229. }
  230.  
  231. var slideIndex = 1;
  232. function showPromotions(n) {
  233. var i;
  234. var x = document.getElementsByClassName("special-promotion-item");
  235. slideIndex = n;
  236. if (n > x.length) {
  237. slideIndex = 1;
  238. }
  239. if (n < 1) {
  240. slideIndex = x.length;
  241. }
  242. for (i = 0; i < x.length; i++) {
  243. x[i].style.display = "none";
  244. }
  245. $(`#specialPromotion .controller .item__child`).removeClass("on");
  246. setTimeout(() => {
  247. $(
  248. `#specialPromotion .controller .item__child:eq(${slideIndex - 1})`
  249. ).addClass("on");
  250. }, 10);
  251. console.log("showPromotions", n, slideIndex, x[slideIndex - 1]);
  252. if (x[slideIndex - 1]) {
  253. x[slideIndex - 1].style.display = "block";
  254. }
  255. }
  256.  
  257. function dealWithData(data) {
  258. if (data.chart.length > 2) {
  259. $("#jjbPriceChart").html("");
  260. let specialPromotion = data.specialPromotion;
  261. let chart = new G2.Chart({
  262. container: "jjbPriceChart",
  263. autoFit: true,
  264. padding: [50, 50, 80, 50],
  265. height: 300,
  266. });
  267. chart.data(data.chart);
  268. chart.scale({
  269. timestamp: {
  270. type: "time",
  271. mask: "MM-DD HH:mm",
  272. range: [0, 1],
  273. tickCount: 5,
  274. },
  275. });
  276. chart.scale("value", {
  277. min: data.averagePrice ? data.averagePrice / 3 : 0,
  278. nice: true,
  279. });
  280. chart
  281. .line()
  282. .position("timestamp*value")
  283. .shape("hv")
  284. .color("key")
  285. .tooltip({
  286. fields: ["key", "value", "timestamp"],
  287. callback: (key, value, timestamp) => {
  288. const itemDate = timestampToDateNumber(timestamp);
  289. return {
  290. key,
  291. value: value,
  292. date: itemDate,
  293. };
  294. },
  295. });
  296. chart.tooltip({
  297. showCrosshairs: true, // 展示 Tooltip 辅助线
  298. shared: true,
  299. showTitle: true,
  300. customContent: (title, items) => {
  301. let itemDom = "";
  302. let promotionsDom = "";
  303. let promotions = [];
  304.  
  305. items.forEach((item) => {
  306. promotions = data.promotionLogs.find(function (promotion) {
  307. return promotion.date == item.date;
  308. });
  309. itemDom += `<li style="color:${item.color}"><span class="price-type">${item.key}</span>: ${item.value} 元</li>`;
  310. });
  311. promotions &&
  312. promotions.detail &&
  313. promotions.detail.forEach((item) => {
  314. promotionsDom += `<li><span class="tag">${item.typeName}</span><span class="description">${item.description}</span></li>`;
  315. });
  316. return `<div class="g2-tooltip">
  317. <div class="g2-tooltip-title" style="margin-bottom: 4px;">${title}</div>
  318. <ul class="g2-tooltip-list">${itemDom}</ul>
  319. <ul class="promotions">${promotionsDom}</ul>
  320. </div>`;
  321. },
  322. });
  323.  
  324. let specialPromotionDom = ``;
  325. specialPromotion &&
  326. specialPromotion.forEach((item) => {
  327. specialPromotionDom += `<div class="special-promotion-item"><a class="promotion-item" style="${
  328. item.style
  329. }" href="${item.url}" target="_break">${
  330. item.icon
  331. ? `<span class="icon"><img src="${item.icon}"/></span>`
  332. : ""
  333. }${item.title}</a></div>`;
  334. });
  335. let specialPromotionControllerDom = ``;
  336. specialPromotion &&
  337. specialPromotion.forEach((item, index) => {
  338. specialPromotionControllerDom += `<span class="item__child" data-index="${index}"></span>`;
  339. });
  340. $("#specialPromotion").html(`
  341. <div class="promotions">${specialPromotionDom}</div>
  342. <div class="controller">${specialPromotionControllerDom}</div>
  343. `);
  344. chart.render();
  345. setTimeout(() => {
  346. showPromotions(Math.floor(Math.random() * specialPromotion.length) + 1);
  347. $("#specialPromotion .controller .item__child").live(
  348. "click",
  349. function () {
  350. let index = $(this).data("index");
  351. console.log("index", index);
  352. showPromotions(index + 1);
  353. }
  354. );
  355. }, 50);
  356.  
  357. setInterval(() => {
  358. showPromotions(Math.floor(Math.random() * specialPromotion.length) + 1);
  359. }, 30000);
  360. } else {
  361. $("#jjbPriceChart").html(`<div class="no_data">暂无数据</div>`);
  362. }
  363. }
  364.  
  365. function getPriceChart(sku, days) {
  366. GM_xmlhttpRequest({
  367. method: "GET",
  368. url: `https://api.zaoshu.so/price/${sku}/detail?days=${days}`,
  369. headers: {
  370. Referer: location.href,
  371. "User-agent": "Mozilla/4.0 (compatible) Greasemonkey",
  372. },
  373. onload: function (responseDetails) {
  374. dealWithData(JSON.parse(responseDetails.responseText));
  375. },
  376. onerror: function () {
  377. $("#jjbPriceChart").html(
  378. `<div id="retry" class="no_data">查询失败,点击重试</div>`
  379. );
  380. $("#retry").bind("click", () => {
  381. getPriceChart(sku);
  382. });
  383. },
  384. });
  385. }
  386.  
  387. setTimeout(function () {
  388. getPriceChart(sku)
  389. $('.jjbPriceChart. select[name=days]').change(function () {
  390. getPriceChart(sku, $(this).val());
  391. });
  392. }, 1000)
  393. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址