wmsHelper

wms操作助手

目前为 2023-12-05 提交的版本,查看 最新版本

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