dxm

统计当前订单件数 & 针对区域定价的报关金额进行批量填充

  1. // ==UserScript==
  2. // @name dxm
  3. // @namespace https://greasyfork.org/zh-CN/scripts/462551
  4. // @version 2.64
  5. // @author Huang
  6. // @description 统计当前订单件数 & 针对区域定价的报关金额进行批量填充
  7. // @license MIT
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=dianxiaomi.com
  9. // @match https://www.dianxiaomi.com/order/*
  10. // @grant GM_addStyle
  11. // @run-at document-start
  12. // ==/UserScript==
  13.  
  14. (t=>{if(typeof GM_addStyle=="function"){GM_addStyle(t);return}const o=document.createElement("style");o.textContent=t,document.head.append(o)})(" h3.total-num{color:#007bff;position:absolute;left:200px;top:7px;font-size:13px}#orderListTable tr.goodsId{position:relative}.operation-btns{position:absolute;left:300px;top:4px}.operation-btns a{color:#28a745;font-size:13px;margin-right:20px}#dxmOrderDetailDiv{display:none}.total-count{color:red;line-height:30px;font-size:13px;margin-left:10px}.pkg-types{color:gray;line-height:30px;font-size:13px;margin-left:10px} ");
  15.  
  16. (function () {
  17. 'use strict';
  18.  
  19. var __defProp = Object.defineProperty;
  20. var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
  21. var __publicField = (obj, key, value) => __defNormalProp(obj, key + "" , value);
  22. class Utils {
  23. // 模拟input
  24. static tEvent(b, a) {
  25. if (b) {
  26. window.newhtmlevents = window.newhtmlevents || document.createEvent("HTMLEvents");
  27. newhtmlevents.initEvent(a, true, true);
  28. return b.dispatchEvent(newhtmlevents);
  29. }
  30. }
  31. static simulateInput(ele, val) {
  32. this.tEvent(ele, "click");
  33. this.tEvent(ele, "input");
  34. ele.value = val;
  35. this.tEvent(ele, "keyup");
  36. this.tEvent(ele, "change");
  37. this.tEvent(ele, "blur");
  38. }
  39. static htmlParser(htmlString) {
  40. let parser = new DOMParser();
  41. let doc = parser.parseFromString(htmlString, "text/html");
  42. return doc;
  43. }
  44. static isGpsrCountry(countryCode) {
  45. const countries = ["奥地利", "比利时", "保加利亚", "塞浦路斯", "克罗地亚", "捷克共和国", "丹麦", "爱沙尼亚", "芬兰", "法国", "德国", "希腊", "匈牙利", "爱尔兰", "意大利", "拉脱维亚", "立陶宛", "卢森堡", "马耳他", "荷兰", "波兰", "葡萄牙", "罗马尼亚", "斯洛伐克", "斯洛文尼亚", "西班牙", "瑞典"];
  46. return countries.includes(countryCode);
  47. }
  48. /**
  49. * 从URL中提取指定键的查询参数值。
  50. * @param {string} url - 包含查询参数的URL。
  51. * @param {string} key - 要提取值的查询参数键。
  52. * @returns {string|null} - 指定键的查询参数值,如果不存在则返回null。
  53. */
  54. static getUrlKeyValuePairs(url, key) {
  55. const pairs = {};
  56. const parts = url.split("?")[1].split("&");
  57. for (const part of parts) {
  58. const [key2, value] = part.split("=");
  59. pairs[key2] = value;
  60. }
  61. return pairs[key] || null;
  62. }
  63. static isiPad() {
  64. const ua = navigator.userAgent;
  65. const platform = navigator.platform;
  66. if (/iPad/i.test(ua) || platform === "iPad") {
  67. return true;
  68. }
  69. return platform === "MacIntel" && navigator.maxTouchPoints > 1;
  70. }
  71. }
  72. const _Declare = class _Declare {
  73. static init() {
  74. $(document).off("click", `a[onclick="showBatchCustoms();"]`);
  75. $(document).on("click", `a[onclick="showBatchCustoms();"]`, () => {
  76. _Declare.prepareData();
  77. });
  78. _Declare.applyTrackingDirect();
  79. }
  80. static applyTrackingDirect() {
  81. $(`button[onclick="batchMoveProcessed();"]`).each(function() {
  82. $(this).attr("onclick", null);
  83. });
  84. $(document).off("click", "[id^='moveProcessBtn']");
  85. $(document).on("click", "[id^='moveProcessBtn']", () => {
  86. let isCheckedAnyone = $(`#showSelCheckboxNum`).length > 0;
  87. if (!isCheckedAnyone) {
  88. $.fn.message({ type: "error", msg: "请至少选择一个订单ya" });
  89. return;
  90. }
  91. _Declare.prepareData();
  92. showBatchCustoms();
  93. });
  94. }
  95. // 准备要在报关列表用到的数据
  96. static prepareData() {
  97. let t = [], that = this, _reg = /[^0-9.]/g;
  98. $(`input[name='packageId']`).each(function() {
  99. if ($(this).prop("checked")) {
  100. let common = $(this).closest("tr").next().children();
  101. const isJIT = $(this).closest("tr").next()[0].getAttribute("data-platform").includes("Choice");
  102. if (!isJIT) {
  103. let eleList = common.eq(0).find("tr");
  104. let rightPrice = common.eq(1).text().replace(_reg, "");
  105. rightPrice = Number(rightPrice);
  106. let singleItem = eleList.length == 1;
  107. if (singleItem) {
  108. let leftPrice = eleList.find("p:contains('USD')").text().replace(_reg, "");
  109. leftPrice = Number(leftPrice);
  110. let finalPrice = leftPrice < rightPrice ? leftPrice : rightPrice;
  111. t.push(finalPrice);
  112. } else {
  113. eleList.each((i, e) => t.push($(e).find("p:contains('USD')").text().replace(_reg, "")));
  114. }
  115. }
  116. that.finalDeclareValues = t;
  117. }
  118. });
  119. }
  120. static fillValues() {
  121. let that = this;
  122. const [chsName, engName, weight] = ["狗衣服", "Dog Clothes", 50];
  123. $(`input[name="declaredValues"]`).each(function(index) {
  124. Utils.simulateInput($(this).get(0), that.finalDeclareValues[index]);
  125. });
  126. $(`input[name="nameChs"]`).each(function(index) {
  127. if ($(this).val() == "") Utils.simulateInput($(this).get(0), chsName);
  128. });
  129. $(`input[name="nameEns"]`).each(function(index) {
  130. if ($(this).val() == "") Utils.simulateInput($(this).get(0), engName);
  131. });
  132. $(`input[name="weights"]`).each(function(index) {
  133. let curVal = $(this).val();
  134. if (curVal == "" || curVal == 0) Utils.simulateInput($(this).get(0), weight);
  135. });
  136. }
  137. };
  138. __publicField(_Declare, "finalDeclareValues", []);
  139. let Declare = _Declare;
  140. class Summary {
  141. constructor(url) {
  142. this.curURL = url;
  143. if (Utils.isiPad() === false) {
  144. this.compute();
  145. }
  146. this.total();
  147. this.packageTypes();
  148. this.addGpsrTag();
  149. }
  150. /**
  151. * 判断当前url是否可以追踪物流信息。
  152. * @returns {boolean} - 如果可以追踪物流信息,则返回true,否则返回false。
  153. */
  154. ifCanTrack() {
  155. let ifTargetURL = this.curURL.includes("list.htm");
  156. if (ifTargetURL) {
  157. let state = Utils.getUrlKeyValuePairs(this.curURL, "state");
  158. if (state === "shipped") {
  159. return true;
  160. }
  161. return false;
  162. }
  163. return false;
  164. }
  165. // 统计订单件数
  166. compute() {
  167. let titleRows = document.querySelectorAll(".goodsId");
  168. titleRows.forEach((titleRow) => {
  169. let total = 0;
  170. let nextElement = titleRow.nextElementSibling;
  171. this.addFreightAndTrackElements(nextElement, titleRow);
  172. while (nextElement && !nextElement.classList.contains("goodsId")) {
  173. let numBoxes = nextElement.querySelectorAll('[class^="circularSpan"]');
  174. for (let numBox of numBoxes) {
  175. let num = parseInt(numBox.textContent);
  176. total += num;
  177. }
  178. nextElement = nextElement.nextElementSibling;
  179. }
  180. titleRow.insertAdjacentHTML("beforeend", `<h3 class='total-num'>${total}件</h3>`);
  181. });
  182. }
  183. addFreightAndTrackElements(contentRow, titleRow, total) {
  184. const baseURL = `https://csp.aliexpress.com/m_apps`;
  185. const operationDiv = $('<div class="operation-btns"></div>');
  186. let orderId = contentRow.querySelector(".tableOrderId a").innerText;
  187. const freightLink = $(`<a target='_blank' href='${baseURL}/logistic/create_ship_cn?trade_order_id=${orderId}'>查看运费</a>`);
  188. operationDiv.append(freightLink);
  189. let ifCanTrack = this.ifCanTrack();
  190. if (ifCanTrack) {
  191. const logisticsLink = $(`<a target='_blank' href='${baseURL}/logistics/tracking?tradeOrderId=${orderId}'>物流轨迹</a>`);
  192. operationDiv.append(logisticsLink);
  193. }
  194. $(titleRow).append(operationDiv);
  195. }
  196. total() {
  197. let total = 0;
  198. let pieceSpans = document.querySelector("#orderListTable").querySelectorAll('[class^="circularSpan"]');
  199. if (pieceSpans.length == 0) return;
  200. pieceSpans.forEach((pieceSpan) => {
  201. let num = parseInt(pieceSpan.textContent);
  202. total += num;
  203. });
  204. let existingTotal = document.querySelector(".total-count");
  205. if (existingTotal) {
  206. existingTotal.remove();
  207. }
  208. $("#allClassification").append(`<span class="total-count">Total: ${total} 件</span>`);
  209. }
  210. packageTypes() {
  211. let pkgs = document.querySelectorAll(".buyerSelectProvider");
  212. if (pkgs.length == 0) return;
  213. let economy = 0, saver = 0, standard = 0;
  214. pkgs.forEach((pkg) => {
  215. if (pkg.textContent.includes("Economy")) {
  216. economy++;
  217. } else if (pkg.textContent.includes("Saver")) {
  218. saver++;
  219. } else if (pkg.textContent.includes("Standard")) {
  220. standard++;
  221. }
  222. });
  223. let existingTotal = document.querySelector(".pkg-types");
  224. if (existingTotal) {
  225. existingTotal.remove();
  226. }
  227. $("#allClassification").append(`<span class="pkg-types">经济: ${economy}个 简易: ${saver}个 标准: ${standard}个</span>`);
  228. }
  229. addGpsrTag() {
  230. let titleRows = document.querySelectorAll(".goodsId");
  231. titleRows.forEach((titleRow) => {
  232. let nextElement = titleRow.nextElementSibling;
  233. let tag = $(nextElement).find('a[onclick^="dxmOrderDetail("]').next();
  234. let country = tag.closest("td").prev().children().last().text().replace(/「|」/g, "");
  235. let isGSPR = Utils.isGpsrCountry(country);
  236. if (isGSPR) {
  237. tag.append(`<span class="squareSpanOra">G</span>`);
  238. }
  239. });
  240. }
  241. }
  242. class LookDetail {
  243. constructor(responseTags) {
  244. this.DETAIL_PAGE_URL = `https://csp.aliexpress.com/apps/order/detail?orderId=`;
  245. this.detailTags = Utils.htmlParser(responseTags);
  246. this.main();
  247. }
  248. main() {
  249. var strongElements = this.detailTags.querySelectorAll("strong");
  250. strongElements.forEach((ele) => {
  251. let text = ele.textContent;
  252. if (text.includes("站点")) {
  253. if (text.includes("半托管")) {
  254. this.jump2AliOrderDetail(strongElements, "平台编号");
  255. } else {
  256. this.jump2AliOrderDetail(strongElements, "订单号");
  257. }
  258. }
  259. });
  260. }
  261. jump2AliOrderDetail(strongElements, splitText) {
  262. strongElements.forEach((viceEle) => {
  263. let orderNumTag = viceEle.textContent;
  264. if (orderNumTag.includes(splitText)) {
  265. let orderId = orderNumTag.split(splitText)[1].replace(":", "");
  266. let url = `${this.DETAIL_PAGE_URL}${orderId}`;
  267. $("#orderDetailClose1").click();
  268. window.open(url);
  269. }
  270. });
  271. }
  272. }
  273. const originalOpen = XMLHttpRequest.prototype.open;
  274. const originalSend = XMLHttpRequest.prototype.send;
  275. XMLHttpRequest.prototype.open = function(method, url, async, user, password) {
  276. this._requestUrl = url;
  277. return originalOpen.apply(this, arguments);
  278. };
  279. XMLHttpRequest.prototype.send = function(body) {
  280. const handleResponse = () => {
  281. if (this.readyState === 4 && this.status >= 200 && this.status < 300) {
  282. const { _requestUrl: url, responseText: data } = this;
  283. if (!data) return;
  284. Declare.init();
  285. const delay = 50;
  286. const keywords = ["list.htm", "splitList.htm", "mergeList.htm", "searchPackage.htm"];
  287. if (keywords.some((u) => url.includes(u))) setTimeout(() => new Summary(url), delay);
  288. if (url.includes("showBatchCustoms.htm")) setTimeout(() => Declare.fillValues(), delay);
  289. if (url.includes("detail.htm")) setTimeout(() => new LookDetail(data), delay);
  290. }
  291. };
  292. this.addEventListener("readystatechange", handleResponse);
  293. return originalSend.apply(this, arguments);
  294. };
  295.  
  296. })();