wmsHelper

wms操作助手

目前为 2023-12-04 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name wmsHelper
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.1
  5. // @description wms操作助手
  6. // @author Ziker
  7. // @match https://wms.yqn.com/62100/*
  8. // @require https://code.jquery.com/jquery-3.4.1.min.js
  9. // @icon https://favicon.qqsuu.cn/work.yqn.com
  10. // @grant unsafeWindow
  11. // @grant window.close
  12. // @grant window.focus
  13. // @run-at document-body
  14. // @noframes
  15. // @license AGPL License
  16. // ==/UserScript==
  17.  
  18. window.jq = $.noConflict(true);
  19.  
  20. (function (window) {
  21. window.pageHelper = {
  22. // 等待元素可见
  23. async waitElementVisible(visibleTag, index, fun) {
  24. let node = jq(visibleTag)
  25. if (node === null || node[index] === null || node[index] === undefined) {
  26. setTimeout(() => {
  27. pageHelper.waitElementVisible(visibleTag, index, fun)
  28. }, 500)
  29. } else {
  30. fun()
  31. }
  32. },
  33. sleep(duration) {
  34. return new Promise(resolve => {
  35. setTimeout(resolve, duration)
  36. })
  37. },
  38. showToast(msg, duration) {
  39. const oldNotify = document.querySelector(".custom-notify");
  40. if (oldNotify !== undefined && oldNotify !== null) {
  41. document.body.removeChild(oldNotify)
  42. }
  43. // 显示提示
  44. duration = isNaN(duration) ? 3000 : duration;
  45. const m = document.createElement('div');
  46. m.className = "custom-notify"
  47. m.innerHTML = msg;
  48. m.style.cssText = "display: flex;justify-content: center;align-items: center;width:60%; min-width:180px; " +
  49. "background:#000000; opacity:0.98; height:auto;min-height: 50px;font-size:25px; color:#fff; " +
  50. "line-height:30px; text-align:center; border-radius:4px; position:fixed; top:70%; left:20%; z-index:999999;";
  51. document.body.appendChild(m);
  52. setTimeout(function () {
  53. const d = 0.5;
  54. m.style.webkitTransition = '-webkit-transform ' + d + 's ease-in, opacity ' + d + 's ease-in';
  55. m.style.opacity = '0';
  56. setTimeout(function () {
  57. if (document.body.contains(m)) {
  58. document.body.removeChild(m)
  59. }
  60. }, d * 1000);
  61. }, duration);
  62. }
  63. }
  64. })(window);
  65.  
  66.  
  67. (function () {
  68. 'use strict';
  69. jq(document).ready(function () {
  70. // 顶部工具栏变化
  71. waitObserve("#app", () => {
  72. if (nonNull(document.querySelector(".forkButton"))) {
  73. return;
  74. }
  75. const toolbar = document.querySelector(".yqn-topbar-module");
  76. appendFlagNode(toolbar, "forkButton")
  77. const firstChildNode = toolbar.childNodes[0];
  78. toolbar.insertBefore(createTextButton("----万能叉车----", () => {
  79. getMaxForkliftCode(forkLift => {
  80. if (forkLift === '') {
  81. window.pageHelper.showToast("没找到叉车", 1000)
  82. } else {
  83. window.pageHelper.showToast(imageHtml(forkLift), 4000)
  84. }
  85. })
  86. }, "ant-btn ant-btn-link perf-tracked yqn-button download-record"), firstChildNode)
  87.  
  88. toolbar.insertBefore(createTextButton("----新容器----", () => {
  89. getFirstContainerNo(containerNo => {
  90. if (containerNo === '') {
  91. window.pageHelper.showToast("好像没容器了", 1000)
  92. } else {
  93. window.pageHelper.showToast(imageHtml(containerNo), 4000)
  94. }
  95. })
  96. }, "ant-btn ant-btn-link perf-tracked yqn-button download-record"), firstChildNode)
  97. })
  98. // 监听 content 页面变动
  99. waitObserve(".yqn-base-page", () => {
  100. const module = document.querySelector(".yqn-header")
  101. if (isNull(module)) {
  102. return;
  103. }
  104. // 拣货单详情页面
  105. if (module.textContent.indexOf("拣货单") >= 0 && module.textContent.indexOf("详情") >= 0) {
  106. pickingOrder()
  107. }
  108. // 拣货单详情页面
  109. if (module.textContent.indexOf("移库单") >= 0 && module.textContent.indexOf("详情") >= 0) {
  110. relocationOrder()
  111. }
  112. })
  113. })
  114.  
  115. // 拣货单页面
  116. function pickingOrder() {
  117. tableExecute(line => {
  118. const sku = line.querySelectorAll("td")[1].innerText;
  119. const warehouseLocation1 = line.querySelectorAll("td")[4].innerText;
  120. line.querySelectorAll("td")[1].appendChild(createTextButton("=>生成", () => {
  121. window.pageHelper.showToast(imageHtml(sku, warehouseLocation1), 7000)
  122. }))
  123. })
  124. }
  125.  
  126. // 移库单页面
  127. function relocationOrder() {
  128. tableExecute(line => {
  129. const sku = line.querySelectorAll("td")[1].innerText;
  130. const warehouseLocation1 = line.querySelectorAll("td")[4].innerText;
  131. const warehouseLocation2 = line.querySelectorAll("td")[5].innerText;
  132. line.querySelectorAll("td")[1].appendChild(createTextButton("=>生成", () => {
  133. window.pageHelper.showToast(imageHtml(sku, warehouseLocation1, warehouseLocation2), 7000)
  134. }))
  135. })
  136. }
  137.  
  138. // 表格处理
  139. function tableExecute(lineFun) {
  140. waitObserve(".ant-tabs-content.ant-tabs-content-top .ant-table-container .ant-table-tbody", () => {
  141. if (nonNull(document.querySelector(".customer-button-sku"))) {
  142. return;
  143. }
  144. appendFlagNode(document.querySelector(".yqn-header"), "customer-button-sku")
  145. window.pageHelper.sleep(500)
  146. .then(() => {
  147. const tbody = document.querySelector(".ant-tabs-content.ant-tabs-content-top .ant-table-container .ant-table-tbody")
  148. const lines = tbody.querySelectorAll(".ant-table-row.ant-table-row-level-0");
  149. for (let i = 0; i < lines.length; i++) {
  150. lineFun(lines[i])
  151. }
  152. })
  153. })
  154. }
  155.  
  156. // 追加标记节点
  157. function appendFlagNode(node, flag) {
  158. const divFlag = document.createElement("div");
  159. node.appendChild(divFlag)
  160. divFlag.className = flag;
  161. divFlag.style.display = "none"
  162. }
  163.  
  164. function imageHtml() {
  165. let html = "<div style=\"display: flex;margin-top: 10px;margin-bottom: 10px\">";
  166. for (let i = 0; i < arguments.length; i++) {
  167. html += " <img src=\"https://wwsix.cn/endpoint/v1/qrcode/create?code=" + arguments[i] + "&width=200&height=200&desc=1\" alt='" + arguments[i] + "'/>";
  168. html += " <span style=\"width: 100px\"></span>";
  169. }
  170. html += "</div>";
  171. return html;
  172. }
  173.  
  174. function nonNull(o) {
  175. return o !== null && o !== undefined;
  176. }
  177.  
  178. function isNull(o) {
  179. return o === null || o === undefined;
  180. }
  181.  
  182. // 等待出现并监听变化
  183. function waitObserve(visibleTag, fun, attributes = true) {
  184. window.pageHelper.waitElementVisible(visibleTag, 0, () => {
  185. new MutationObserver(function (mutationsList) {
  186. fun()
  187. }).observe(document.querySelector(visibleTag), {
  188. attributes: attributes,
  189. childList: true,
  190. subtree: true,
  191. characterData: true
  192. })
  193. })
  194. }
  195.  
  196. // 创建文本按钮
  197. function createTextButton(name, listener, className = "ant-btn ant-btn-link perf-tracked yqn-button yqn-link-no-padding customer-button") {
  198. const button = document.createElement("button")
  199. button.type = "button"
  200. button.id = name
  201. button.className = className
  202. button.onclick = listener
  203. const span = document.createElement("span")
  204. span.textContent = name
  205. button.appendChild(span)
  206. return button;
  207. }
  208.  
  209. // 创建按钮
  210. function createButton(name, listener) {
  211. const button = document.createElement("button")
  212. button.type = "button"
  213. button.id = name
  214. button.className = "ant-btn ant-btn-default perf-tracked yqn-button"
  215. button.onclick = listener
  216. const a = document.createElement("a")
  217. a.textContent = name
  218. a.className = "render-button-text"
  219. button.appendChild(a)
  220. return button;
  221. }
  222.  
  223. const domain = window.location.href.indexOf("qa4") >= 0 ? 'qa4-' : window.location.href.indexOf("pr-") >= 0 ? 'pr-' : '';
  224.  
  225. // 拿一个新容器
  226. function getFirstContainerNo(fuc) {
  227. jq.ajax({
  228. url: 'https://' + domain + 'gw-wms.yqn.com/api/40081/api/call/yqn_wms/bg/container/v2/list',
  229. method: 'POST',
  230. xhrFields: {
  231. withCredentials: true
  232. },
  233. crossDomain: true,
  234. contentType: 'application/json',
  235. data: JSON.stringify({
  236. "header": {
  237. "xSourceAppId": "63008",
  238. "guid": "6f87e073-1da1-4017-b2de-c109abcd6d123",
  239. "lang": "zh",
  240. "timezone": "Asia/Shanghai"
  241. },
  242. "model": {
  243. "page": 1,
  244. "size": 20,
  245. "statusList": [
  246. 1
  247. ],
  248. "warehouseId": localStorage.getItem("warehouseId")
  249. }
  250. }),
  251. success: function (response) {
  252. if (response.code === 200 && nonNull(response.data) && nonNull(response.data.content)) {
  253. if (response.data.content.length > 0) {
  254. fuc(response.data.content[0].containerCode)
  255. } else {
  256. fuc('')
  257. }
  258. }
  259. },
  260. error: function (xhr, status, error) {
  261. console.log('Request failed:', error);
  262. }
  263. });
  264. }
  265.  
  266. // 拿一个全能叉车
  267. function getMaxForkliftCode(fuc) {
  268. jq.ajax({
  269. url: 'https://' + domain + 'gw-wms.yqn.com/api/40081/api/call/yqn_wms/bg/forklift/v2/list',
  270. method: 'POST',
  271. xhrFields: {
  272. withCredentials: true
  273. },
  274. crossDomain: true,
  275. contentType: 'application/json',
  276. data: JSON.stringify({
  277. "header": {
  278. "xSourceAppId": "63008",
  279. "guid": "6f87e073-1da1-4017-b2de-c109abcd6d123",
  280. "lang": "zh",
  281. "timezone": "Asia/Shanghai"
  282. },
  283. "model": {
  284. "page": 1,
  285. "size": 100,
  286. "warehouseId": localStorage.getItem("warehouseId")
  287. }
  288. }),
  289. success: function (response) {
  290. if (response.code === 200 && nonNull(response.data) && nonNull(response.data.content) && response.data.content.length > 0) {
  291. let emptyCode = ''
  292. let code = ''
  293. let areMaxLayer = 0
  294. let areMaxLen = 0
  295. let emptyAreMaxLayer = 0;
  296. for (let i = 0; i < response.data.content.length; i++) {
  297. const forklift = response.data.content[i];
  298. const num = isNull(forklift.maximumLayer) ? 0 : forklift.maximumLayer;
  299. // 空库区
  300. if (isNull(forklift.warehouseAreaList) || forklift.warehouseAreaList.length === 0) {
  301. if (emptyAreMaxLayer <= num) {
  302. emptyAreMaxLayer = num;
  303. emptyCode = forklift.code;
  304. }
  305. } else {
  306. // 非空库区
  307. if (areMaxLen <= forklift.warehouseAreaList.length && areMaxLayer <= num) {
  308. areMaxLen = forklift.warehouseAreaList.length;
  309. areMaxLayer = num;
  310. code = forklift.code;
  311. }
  312. }
  313. }
  314. fuc(emptyCode.length !== 0 ? emptyCode : code)
  315. }
  316. },
  317. error: function (xhr, status, error) {
  318. console.log('Request failed:', error);
  319. }
  320. });
  321. }
  322. })();