wmsHelper

wms操作助手

// ==UserScript==
// @name         wmsHelper
// @namespace    http://tampermonkey.net/
// @version      0.4 切换图片生成到40084
// @description  wms操作助手
// @author       Ziker
// @match        https://wms.yqn.com/62100/*
// @match        https://pr-wms.yqn.com/62100/*
// @match        https://qa4-wms.yqn.com/62100/*
// @require      https://code.jquery.com/jquery-3.4.1.min.js
// @icon         https://favicon.qqsuu.cn/work.yqn.com
// @grant GM_openInTab
// @grant unsafeWindow
// @grant window.close
// @grant window.focus
// @run-at       document-body
// @noframes
// @license           AGPL License
// ==/UserScript==

window.jq = $.noConflict(true);

(function (window) {
    window.pageHelper = {
        // 等待元素可见
        async waitElementVisible(visibleTag, index, fun) {
            let node = jq(visibleTag)
            if (node === null || node[index] === null || node[index] === undefined) {
                setTimeout(() => {
                    pageHelper.waitElementVisible(visibleTag, index, fun)
                }, 500)
            } else {
                fun()
            }
        },
        sleep(duration) {
            return new Promise(resolve => {
                setTimeout(resolve, duration)
            })
        },
        showToast(msg, duration) {
            const oldNotify = document.querySelector(".custom-notify");
            if (oldNotify !== undefined && oldNotify !== null) {
                document.body.removeChild(oldNotify)
            }
            // 显示提示
            duration = isNaN(duration) ? 3000 : duration;
            const m = document.createElement('div');
            m.className = "custom-notify"
            m.innerHTML = msg;
            m.style.cssText = "display: flex;justify-content: center;align-items: center;width:80%; min-width:180px; " +
                "background:#000000; opacity:0.98; height:auto;min-height: 50px;font-size:25px; color:#fff; " +
                "line-height:30px; text-align:center; border-radius:4px; position:fixed; top:60%; left:10%; z-index:999999;";
            document.body.appendChild(m);
            setTimeout(function () {
                const d = 0.5;
                m.style.webkitTransition = '-webkit-transform ' + d + 's ease-in, opacity ' + d + 's ease-in';
                m.style.opacity = '0';
                setTimeout(function () {
                    if (document.body.contains(m)) {
                        document.body.removeChild(m)
                    }
                }, d * 1000);
            }, duration);
        },
        // 模拟键盘按下
        clickUpAndDown(inputElement, keyChar) {
            let lastValue = inputElement.value;
            inputElement.value = keyChar;
            let event = new Event("input", {bubbles: true});
            //  React15
            event.simulated = true;
            //  React16 内部定义了descriptor拦截value,此处重置状态
            let tracker = inputElement._valueTracker;
            if (tracker) {
                tracker.setValue(lastValue);
            }
            inputElement.dispatchEvent(event);
        }
    }
})(window);


(function () {
    'use strict';
    const domain = window.location.href.indexOf("qa4") >= 0 ? 'qa4-' : window.location.href.indexOf("pr-") >= 0 ? 'pr-' : '';
    jq(document).ready(function () {
        // 顶部工具栏变化
        waitObserve("#app", () => {
            if (nonNull(document.querySelector(".forkButton"))) {
                return;
            }
            const toolbar = document.querySelector(".yqn-topbar-module");
            appendFlagNode(toolbar, "forkButton")
            const firstChildNode = toolbar.childNodes[0];
            // toolbar.insertBefore(createTextButton("PDA理货", () => {
            //     getCurrentRelocation(true, id => {
            //         if (isNull(id)) {
            //             window.pageHelper.showToast("当前登录用户在当前仓库暂无实时理货任务", 4000)
            //         } else {
            //             GM_openInTab("https://" + domain + "wms.yqn.com/62100/transfer-ship/detail/" + id, false)
            //         }
            //     })
            // }, "ant-btn ant-btn-link perf-tracked yqn-button download-record"), firstChildNode)
            //
            // toolbar.insertBefore(createTextButton("PDA移库", () => {
            //     getCurrentRelocation(false, id => {
            //         if (isNull(id)) {
            //             window.pageHelper.showToast("当前登录用户在当前仓库暂无移库计划任务", 4000)
            //         } else {
            //             GM_openInTab("https://" + domain + "wms.yqn.com/62100/transfer-ship/detail/" + id, false)
            //         }
            //     })
            // }, "ant-btn ant-btn-link perf-tracked yqn-button download-record"), firstChildNode)
            //
            // toolbar.insertBefore(createTextButton("PDA拣货", () => {
            //     getCurrentPickingOrder(id => {
            //         if (isNull(id)) {
            //             window.pageHelper.showToast("当前登录用户在当前仓库暂无拣货任务", 4000)
            //         } else {
            //             GM_openInTab("https://" + domain + "wms.yqn.com/62100/picking/detail/" + id, false)
            //         }
            //     })
            // }, "ant-btn ant-btn-link perf-tracked yqn-button download-record"), firstChildNode)

            toolbar.insertBefore(createTextButton("叉车", () => {
                getMaxForkliftCode(forkLift => {
                    if (forkLift === '') {
                        window.pageHelper.showToast("没找到叉车", 1000)
                    } else {
                        window.pageHelper.showToast(imageHtml(forkLift), 4000)
                    }
                })
            }, "ant-btn ant-btn-link perf-tracked yqn-button download-record"), firstChildNode)

            toolbar.insertBefore(createTextButton("入库码", () => {
                getFirstContainerNo(1, containerNo => {
                    if (isNull(containerNo)) {
                        window.pageHelper.showToast("好像没容器了", 1000)
                    } else {
                        window.pageHelper.showToast(imageHtml(containerNo), 4000)
                    }
                })
            }, "ant-btn ant-btn-link perf-tracked yqn-button download-record"), firstChildNode)
            toolbar.insertBefore(createTextButton("出库码", () => {
                getFirstContainerNo(2, containerNo => {
                    if (isNull(containerNo)) {
                        window.pageHelper.showToast("好像没容器了", 1000)
                    } else {
                        window.pageHelper.showToast(imageHtml(containerNo), 4000)
                    }
                })
            }, "ant-btn ant-btn-link perf-tracked yqn-button download-record"), firstChildNode)
            toolbar.insertBefore(createTextButton("装车码", () => {
                getFirstContainerNo(3, containerNo => {
                    if (isNull(containerNo)) {
                        window.pageHelper.showToast("好像没容器了", 1000)
                    } else {
                        window.pageHelper.showToast(imageHtml(containerNo), 4000)
                    }
                })
            }, "ant-btn ant-btn-link perf-tracked yqn-button download-record"), firstChildNode)
        })
        // 处理表格
        parseTable()
        // 监听 content 页面变动
        waitObserve(".layout-common-page", () => {
            // 关闭非 Chrome 游览器顶部下载提示
            const svgIcon = document.querySelector(".yqn-icon.close");
            if (svgIcon) {
                const clickEvent = new MouseEvent("click", {
                    bubbles: true,
                    cancelable: true
                });
                svgIcon.dispatchEvent(clickEvent);
            }

            const module = document.querySelector(".yqn-header")
            if (isNull(module)) {
                return;
            }
            // 拣货单详情页面
            if (module.textContent.indexOf("拣货单") >= 0 && module.textContent.indexOf("详情") >= 0) {
                pickingOrder()
            }
            // 移库单详情页面
            if (module.textContent.indexOf("移库单") >= 0 && module.textContent.indexOf("详情") >= 0) {

            }
            // 出库单详情页面
            if (module.textContent.indexOf("出库单") >= 0 && module.textContent.indexOf("详情") >= 0) {
                outboundOrder()
            }
            // 拣货单列表
            if (module.textContent.indexOf("拣货单") >= 0 && module.textContent.indexOf("详情") <= 0) {
                pickingOrderList()
            }
            // 装车单列表
            if (module.textContent.indexOf("装车单") >= 0 && module.textContent.indexOf("详情") <= 0) {
                packingOrderList()
            }
            // 验货复核
            if (module.textContent.indexOf("验货复核") >= 0) {
                inspectionGoods()
            }
        })
    })

    function parseTable() {
        setTimeout(() => {
            parseTable()
            tableExecute()
        }, 1500)
    }

    // 拣货单页面
    function pickingOrder() {
        const buttons = document.querySelector(".yqn-parser-button-list");
        if (nonNull(buttons) && isNull(document.querySelector(".custom-outbound-button"))) {
            appendFlagNode(buttons, "custom-outbound-button")
            buttons.appendChild(createButton("去复核", () => {
                const customOutCode = document.querySelector(".pop-card-c.plaintext");
                GM_openInTab("https://" + domain + "wms.yqn.com/62100/outbound-order/inspection-goods?code=" + customOutCode.textContent, false)
            }))
        }
    }

    // 出库单页面
    function outboundOrder() {
        const buttons = document.querySelector(".yqn-parser-button-list");
        if (nonNull(buttons) && isNull(document.querySelector(".custom-outbound-button"))) {
            appendFlagNode(buttons, "custom-outbound-button")
            queryPickingOrder(data => {
                if (nonNull(data)) {
                    // 跳转拣货单按钮
                    buttons.appendChild(createButton("拣货单", () => {
                        const customOutCode = document.querySelector(".pop-card-c.plaintext");
                        GM_openInTab("https://" + domain + "wms.yqn.com/62100/picking/list?customOutCode=" + customOutCode.textContent, false)
                    }))
                    // 去复核按钮
                    buttons.appendChild(createButton("去复核", () => {
                        GM_openInTab("https://" + domain + "wms.yqn.com/62100/outbound-order/inspection-goods?code=" + data.code, false)
                    }))
                }
            })
            queryLoadingOrder(data => {
                if (nonNull(data)) {
                    // 跳转装车单按钮
                    buttons.appendChild(createButton("装车单", () => {
                        const customOutCode = document.querySelector(".pop-card-c.plaintext");
                        GM_openInTab("https://" + domain + "wms.yqn.com/62100/packing/list?customOutCode=" + customOutCode.textContent, false)
                    }))
                }
            })
        }
    }

    // 拣货单列表
    function pickingOrderList() {
        const customOutCode = new URLSearchParams(window.location.href.split('?')[1]).get('customOutCode');
        if (isNull(customOutCode)) {
            return
        }
        // 搜索出库单号的拣货单
        const node = document.querySelector("#outboundCode");
        if (nonNull(node) && isNull(document.querySelector(".custom-input"))) {
            appendFlagNode(document.body, "custom-input")
            window.pageHelper.clickUpAndDown(node, customOutCode)
        }
    }

    // 装车单列表
    function packingOrderList() {
        const customOutCode = new URLSearchParams(window.location.href.split('?')[1]).get('customOutCode');
        if (isNull(customOutCode)) {
            return
        }
        // 搜索出库单号的装车单
        const node = document.querySelector("#outBoundOrderCode");
        if (nonNull(node) && isNull(document.querySelector(".custom-input"))) {
            appendFlagNode(document.body, "custom-input")
            window.pageHelper.sleep(400)
                .then(() => {
                    window.pageHelper.clickUpAndDown(node, customOutCode)
                    const search = document.querySelector(".yqn-filter-operation .ant-btn.ant-btn-primary.perf-tracked.yqn-button");
                    search.click()
                })
        }
    }

    // 验货复核
    function inspectionGoods() {
        const code = new URLSearchParams(window.location.href.split('?')[1]).get('code');
        if (isNull(code)) {
            return
        }
        const node = document.querySelector("#pickContainerNo");
        if (nonNull(node) && isNull(document.querySelector(".custom-input"))) {
            appendFlagNode(document.body, "custom-input")
            window.pageHelper.clickUpAndDown(node, code)
            // 创建一个KeyboardEvent对象并设置相关属性
            const event = new KeyboardEvent('keydown', {
                key: 'Enter',
                bubbles: true,
                cancelable: true,
                keyCode: 13
            });
            // 通过目标元素触发KeyboardEvent对象
            node.dispatchEvent(event);
        }
    }

    // 表格处理
    function tableExecute() {
        try {
            // table 容器
            const containers = document.querySelectorAll(".ant-table-container");
            if (containers == null || containers.length === 0) {
                return
            }
            for (let i = 0; i < containers.length; i++) {
                const container = containers[i];
                const tHeader = container.querySelector(".ant-table-header")
                let keyIndexArray = []
                const ths = tHeader.querySelectorAll("th");
                for (let i = 0; i < ths.length; i++) {
                    const text = ths[i].innerText.toLowerCase();
                    if (text.indexOf("sku".toLowerCase()) >= 0 ||
                        text.indexOf("UPC".toLowerCase()) >= 0 ||
                        text.indexOf("识别码".toLowerCase()) >= 0 ||
                        text.indexOf("容器号".toLowerCase()) >= 0 ||
                        text.indexOf("库位".toLowerCase()) >= 0 ||
                        text.indexOf("叉车编码".toLowerCase()) >= 0 ||
                        text.indexOf("容器编码".toLowerCase()) >= 0 ||
                        text.indexOf("序列号".toLowerCase()) >= 0 ||
                        text.indexOf("SN码".toLowerCase()) >= 0) {
                        keyIndexArray.push(i)
                    }
                }
                const tBody = container.querySelector(".ant-table-tbody")
                let lines = tBody.querySelectorAll(".ant-table-row.ant-table-row-level-0");
                if (lines == null) {
                    lines = tBody.querySelectorAll(".ant-table-row.ant-table-row-level-0")
                }
                for (let i = 0; i < lines.length; i++) {
                    const line = lines[i];
                    let tds = line.querySelectorAll("td");
                    if (tds[keyIndexArray[0]].innerText.indexOf("QRC") >= 0 || line.innerText.indexOf("QRC") >= 0) {
                        continue
                    }
                    let dataArray = []
                    for (let i = 0; i < tds.length; i++) {
                        if (keyIndexArray.indexOf(i) >= 0) {
                            dataArray.push(tds[i].innerText)
                        }
                    }
                    if (dataArray.length > 0 && tds[keyIndexArray[0]].innerText.indexOf("QRC") < 0) {
                        let td = tds[keyIndexArray[0]];
                        if (td.querySelector("span") != null) {
                            td = td.querySelector("span")
                        }
                        td.appendChild(createTextButton("QRC", () => {
                            window.pageHelper.showToast(imageHtml(dataArray), 7000)
                        }))
                    }
                }
            }
        } catch (e) {
            throw e
        }
    }

    // 追加标记节点
    function appendFlagNode(node, flag) {
        const divFlag = document.createElement("div");
        node.appendChild(divFlag)
        divFlag.className = flag;
        divFlag.style.display = "none"
    }

    function imageHtml() {
        let html = "<div style=\"display: flex;margin-top: 10px;margin-bottom: 10px\">";
        if (arguments.length === 1 && arguments[0] instanceof Array) {
            for (let i = 0; i < arguments[0].length; i++) {
                html += "    <img src=\"https://gw-wms.yqn.com/api/40084/yqn_video/additional/qrcode_create_png?code=" + arguments[0][i] + "&width=220&height=220&desc=1\" alt='" + arguments[0][i] + "'/>";
                if (i !== arguments[0].length - 1) {
                    html += "    <span style=\"width: 170px\"></span>";
                }
            }
        } else {
            for (let i = 0; i < arguments.length; i++) {
                html += "    <img src=\"https://gw-wms.yqn.com/api/40084/yqn_video/additional/qrcode_create_png?code=" + arguments[i] + "&width=220&height=220&desc=1\" alt='" + arguments[i] + "'/>";
                if (i !== arguments.length - 1) {
                    html += "    <span style=\"width: 170px\"></span>";
                }
            }
        }
        html += "</div>";
        return html;
    }

    function nonNull(o) {
        return o !== null && o !== undefined;
    }

    function isNull(o) {
        return o === null || o === undefined;
    }

    // 等待出现并监听变化
    function waitObserve(visibleTag, fun, attributes = true) {
        window.pageHelper.waitElementVisible(visibleTag, 0, () => {
            new MutationObserver(function (mutationsList) {
                fun()
            }).observe(document.querySelector(visibleTag), {
                attributes: attributes,
                childList: true,
                subtree: true,
                characterData: true
            })
        })
    }

    // 创建文本按钮
    function createTextButton(name, listener, className = "ant-btn ant-btn-link perf-tracked yqn-button yqn-link-no-padding customer-button") {
        const button = document.createElement("button")
        button.type = "button"
        button.id = name
        button.className = className
        button.onclick = listener
        const span = document.createElement("span")
        span.textContent = name
        button.appendChild(span)
        return button;
    }

    // 创建按钮
    function createButton(name, listener) {
        const button = document.createElement("button")
        button.type = "button"
        button.id = name
        button.className = "ant-btn ant-btn-default perf-tracked yqn-button"
        button.onclick = listener
        const a = document.createElement("a")
        a.textContent = name
        a.className = "render-button-text"
        button.appendChild(a)
        return button;
    }

    // 拿一个新容器
    function getFirstContainerNo(type, fuc) {
        request('/yqn_wms/bg/container/v2/list', {
            "statusList": [1],
            "containerType": type
        }, data => {
            if (nonNull(data.content) && data.content.length > 0) {
                fuc(data.content[0].containerCode)
            } else {
                fuc(null)
            }
        })
    }

    // 拿一个全能叉车
    function getMaxForkliftCode(fuc) {
        request('/yqn_wms/bg/forklift/v2/list', {}, data => {
            if (nonNull(data.content) && data.content.length > 0) {
                let emptyCode = ''
                let code = ''
                let areMaxLayer = 0
                let areMaxLen = 0
                let emptyAreMaxLayer = 0;
                for (let i = 0; i < data.content.length; i++) {
                    const forklift = data.content[i];
                    const num = isNull(forklift.maximumLayer) ? 0 : forklift.maximumLayer;
                    // 空库区
                    if (isNull(forklift.warehouseAreaList) || forklift.warehouseAreaList.length === 0) {
                        if (emptyAreMaxLayer <= num) {
                            emptyAreMaxLayer = num;
                            emptyCode = forklift.code;
                        }
                    } else {
                        // 非空库区
                        if (areMaxLen <= forklift.warehouseAreaList.length && areMaxLayer <= num) {
                            areMaxLen = forklift.warehouseAreaList.length;
                            areMaxLayer = num;
                            code = forklift.code;
                        }
                    }
                }
                fuc(emptyCode.length !== 0 ? emptyCode : code)
            } else {
                fuc(null)
            }
        })
    }

    // 拿当前拣货单
    function getCurrentPickingOrder(fuc) {
        request('/yqn_wms/app/picking/v2/get_task', {}, data => {
            fuc(data.id)
        })
    }

    // 拿移库单
    function getCurrentRelocation(real, fuc) {
        request('/yqn_wms/app/relocation/v2/' + (real ? 'get_immediately' : 'get_task'), {}, data => {
            fuc(data.id)
        })
    }

    // 查询出库单对应拣货单
    function queryPickingOrder(fuc) {
        request('/yqn_wms/bg/picking/v2/list', {
            "outboundCode": new URLSearchParams(window.location.href.split('?')[1]).get('code'),
        }, data => {
            if (nonNull(data.content) && data.content.length > 0) {
                for (let i = 0; i < data.content.length; i++) {
                    fuc(data.content[i])
                }
            } else {
                fuc(null)
            }
        })
    }

    // 查询出库单对应装车单
    function queryLoadingOrder(fuc) {
        request('/yqn_wms/bg/loading_order/v2/list', {
            "outBoundOrderCode": new URLSearchParams(window.location.href.split('?')[1]).get('code')
        }, data => {
            if (nonNull(data.content) && data.content.length > 0) {
                for (let i = 0; i < data.content.length; i++) {
                    fuc(data.content[i])
                }
            } else {
                fuc(null)
            }
        })
    }

    // 发起请求
    function request(interfaceUrl, model, success) {
        const url = 'https://' + domain + 'gw-wms.yqn.com/api/40081/api/call' + interfaceUrl
        model.page = 1
        model.size = 100
        model.warehouseId = localStorage.getItem("warehouseId")
        jq.ajax({
            url: url,
            method: 'POST',
            xhrFields: {
                withCredentials: true
            },
            crossDomain: true,
            contentType: 'application/json',
            data: JSON.stringify({
                "header": {
                    "xSourceAppId": "63008",
                    "guid": "6f87e073-1da1-4017-b2de-c109abcd6d123",
                    "lang": "zh",
                    "timezone": "Asia/Shanghai"
                },
                "model": model
            }),
            success: function (response) {
                if (response.code === 200 && nonNull(response.data)) {
                    success(response.data)
                }
            },
            error: function (xhr, status, error) {
                console.log('Request failed:', error);
            }
        });
    }
})();