lowerCodeHelper

低代码平台助手

当前为 2024-01-04 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         lowerCodeHelper
// @namespace    http://tampermonkey.net/
// @version      0.3.5
// @description  低代码平台助手
// @author       Ziker
// @match        https://ops.iyunquna.com/63008/*
// @match        http://localhost:63342/api/file/*
// @require      https://code.jquery.com/jquery-3.4.1.min.js
// @icon         https://favicon.qqsuu.cn/work.yqn.com
// @note         2023年12月18日20:26:31 V0.3.4 fix RT 显示BUG
// @note         2024年1月4日11:50:57 支持定义包名接口打开文件
// @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 = {
        // 等待元素可见
        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()
            }
        },
        getCurrentAppIndex() {
            const nodes = document.getElementsByClassName("bar-tab")
            for (let i = 0; i < nodes.length; i++) {
                if (nodes[i].className.indexOf("bar-tab-selected") > 0) {
                    return i
                }
            }
            return -1
        },
        getCurrentApiId() {
            const nodes = document.getElementsByClassName("tab-content-presentation-components")
            if (nodes.length === 0) {
                return null
            }
            let className = nodes[this.getCurrentAppIndex()].className
            return className.substring(className.indexOf("theia-tab-") + "theia-tab-".length)
        },
        sleep(duration) {
            return new Promise(resolve => {
                setTimeout(resolve, duration)
            })
        },
        showToast(msg, duration) {
            // 显示提示
            duration = isNaN(duration) ? 3000 : duration
            const m = document.createElement('div')
            m.innerHTML = msg
            m.style.cssText = "display: flex;justify-content: center;align-items: center;width:60%; 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:85%; left:20%; 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 () {
                    document.body.removeChild(m)
                }, d * 1000)
            }, duration)
        },
        // 关闭窗口
        closeWindow() {
            window.opener = null
            window.open('', '_self')
            window.close()
        },
        formatString(str, len, padding) {
            const diff = len - str.toString().length
            if (diff > 0) {
                return padding.repeat(diff) + str
            } else {
                return str
            }
        },
        initSetting() {
            const customSetting = document.createElement("div")
            document.body.appendChild(customSetting)
            customSetting.innerHTML = `
<div id="copy-setting"
     style="display: none;position: absolute;top: 0;right: 0;background-color: #fff3f3;padding: 10px;width: 600px;z-index: 9999">
    <p>拷贝动作</p>
    <p><label>Copy File Id <input type="radio" name="copyValue" value="1" /></label></p>
    <p><label>Rest Api Open File <input type="radio" name="copyValue" value="2" checked/></label>&nbsp;&nbsp;&nbsp;较新版本IDEA需要下载 <a target="_blank" href="https://plugins.jetbrains.com/plugin/19991-ide-remote-control">IDE Remote Control </a> 插件</p>
    <p><label>ToolBox Open File <input type="radio" name="copyValue" value="3" /></label>&nbsp;&nbsp;&nbsp;需要下载 <a target="_blank" href="https://www.jetbrains.com/toolbox-app/">Jetbrains ToolBox</a> 工具箱软件</p>
    <div id="projectPath" style="visibility: visible"><p><label><input name="path" style="width: 100%" type="text" placeholder="项目路径截止到 src 前  E:/yqnProject/yqn-wms/yqn-wms-rest-provider/"/></label></p></div>
    <p style="visibility: hidden"><label>同时打开编排IDE <input type="checkbox" name="openIDE" /></label>&nbsp;&nbsp;&nbsp;</p>
    <div style="display: flex;margin: 5px;justify-content: space-between">
        <button id="save" lang="zh" type="button" class="ant-btn ant-btn-default perf-tracked yqn-button"><span
                style="margin-left: 5px;">save</span></button>
        <button id="close" lang="zh" type="button" class="ant-btn ant-btn-default perf-tracked yqn-button"><span
                style="margin-left: 5px;">close</span></button>
    </div>
</div>
`
            let copyValue = localStorage.getItem("copyValue")
            copyValue = copyValue === null || copyValue === undefined ? 1 : copyValue
            document.querySelector('input[name="copyValue"][value="' + copyValue + '"]').checked = true
            document.getElementById("projectPath").style.visibility = copyValue === "2" ? "visible" : "hidden"

            let path = localStorage.getItem("path")
            document.querySelector('input[name="path"]').value = path === null || path === undefined ? null : path

            let openIDE = JSON.parse(localStorage.getItem("openIDE"))
            document.querySelector('input[name="openIDE"]').checked = openIDE === null || openIDE === undefined ? false : openIDE

            const radios = document.querySelectorAll('input[name="copyValue"]')
            for (let i = 0; i < radios.length; i++) {
                radios[i].onchange = () => {
                    const remoteRadio = document.querySelector('input[name="copyValue"]:checked')
                    document.getElementById("projectPath").style.visibility = remoteRadio.value === "2" ? "visible" : "hidden"
                }
            }
            document.getElementById("save").addEventListener("click", () => {
                const remoteRadio = document.querySelector('input[name="copyValue"]:checked').value
                const pathInput = document.querySelector('input[name="path"]').value
                const openIDEValue = document.querySelector('input[name="openIDE"]').checked
                if (remoteRadio === '2' && (pathInput === null || pathInput.length === 0)) {
                    this.showToast("路径不可为空", 1000)
                } else {
                    localStorage.setItem("copyValue", remoteRadio)
                    localStorage.setItem("path", pathInput)
                    localStorage.setItem("openIDE", JSON.stringify(openIDEValue))
                    this.showToast("保存成功", 1000)
                }
            })
            document.getElementById("close").addEventListener("click", () => {
                document.getElementById("copy-setting").style.display = "none"
            })
        }
    }
})(window);


(function () {
    let historyTrace = ''
    'use strict';
    let appName = null
    let pageNameMap = []
    jq(document).ready(function () {
        if (window.location.href.indexOf("localhost:63342/api/file/") >= 0) {
            window.pageHelper.closeWindow()
            return
        }
        // 监听api tab变动
        waitObserve(".tabs-bar", () => {
            // 应用接口面板
            const appPanelTag = ".tab-content-presentation-components.theia-tab-" + window.pageHelper.getCurrentApiId()
            // 监听属性面板变动
            waitObserve(appPanelTag + " .p-8", () => {
                const p8 = document.querySelector(appPanelTag + " .p-8")
                const panel = p8.querySelector(".ant-form.ant-form-vertical.yqn-form")
                if (nonNull(panel) && isNull(panel.querySelector(".customScriptInfo"))) {
                    const div = document.createElement("div")
                    div.className = "customScriptInfo"
                    panel.appendChild(div)
                    // 获取当前 NodeName
                    const childProcessNode = panel.querySelector("#code")
                    const codeSpan = p8.querySelector(".dashboard-code span")
                    if (isNull(childProcessNode) && isNull(codeSpan)) {
                        return
                    }
                    let nodeName = nonNull(childProcessNode) ? childProcessNode.value : codeSpan.textContent
                    // 获取当前节点信息
                    getNodeInfo(nodeName, node => {
                        // 处理信息
                        const divNode = document.querySelector(appPanelTag + " .customScriptInfo")
                        // 清空显示
                        divNode.innerHTML = ""
                        // 脚本
                        if (nonNull(node.scriptId)) {
                            divNode.appendChild(createButton("脚本:" + node.scriptId, () => copyToClipboard(node.scriptId)))
                        }
                        // 入参
                        let id = node.inputScriptId
                        const inputScriptIds = node.inputScriptIds
                        if (nonNull(inputScriptIds)) {
                            id = nonNull(id) ? id : inputScriptIds.example
                            id = nonNull(id) ? id : inputScriptIds.record
                            id = nonNull(id) ? id : inputScriptIds.recordList
                            id = nonNull(id) ? id : inputScriptIds.condition
                            id = nonNull(id) ? id : inputScriptIds.id
                        }
                        let isJava = true
                        if (isNull(id) && nonNull(node.dslBulidData)) {
                            id = node.dslBulidData.dslScriptId
                            isJava = false
                        }
                        if (nonNull(id)) {
                            divNode.appendChild(createButton("入参:" + id, () => copyToClipboard(id, isJava)))
                        }
                        // 条件
                        const executeScriptId = node.executeScriptId
                        if (nonNull(executeScriptId)) {
                            divNode.appendChild(createButton("条件:" + executeScriptId, () => copyToClipboard(executeScriptId)))
                        }
                        // 校验
                        const assertScriptId = node.assertScriptId
                        if (nonNull(assertScriptId)) {
                            divNode.appendChild(createButton("校验:" + assertScriptId, () => copyToClipboard(assertScriptId)))
                        }
                    })
                }
            })

            // 监听执行历史面板变动
            const executeHistoryBodyTag = appPanelTag + " .test-split-item.test-split-item-right .ant-table-body tbody"
            waitObserve(executeHistoryBodyTag, () => {
                const lines = document.querySelectorAll(executeHistoryBodyTag + "  .ant-table-row.ant-table-row-level-0")
                if (nonNull(lines)) {
                    if (nonNull(lines[0].querySelector(".customer-button-div"))) {
                        return
                    }
                    appendFlagNode(lines[0], "customer-button-div")
                    getExecuteHistory(content => {
                        for (let i = 0; i < content.length; i++) {
                            const tds = lines[i].querySelectorAll("td")
                            const actionCol = tds[tds.length - 1]
                            const detailButton = actionCol.querySelectorAll("button")[0]

                            detailButton.style.display = "none"
                            actionCol.insertBefore(createButton("RT:" + content[i].rt, () => {
                                detailButton.click()
                                historyTrace = tds[2].innerText
                            }), detailButton)
                        }
                    })
                }
            })

            const currentApiId = window.pageHelper.getCurrentApiId();
            if (isNull(pageNameMap[currentApiId])) {
                getApiPackageName(name => pageNameMap[currentApiId] = name)
            }
        })

        // 监听body变动
        waitObserve("body", () => {
            // 监听执行日志流程图变动
            const processPanelTag = ".node-bpmn #canvas .bjs-container .djs-container .viewport .layer-base"
            waitObserve(processPanelTag, () => {
                const processPanel = document.querySelector(processPanelTag)
                if (isNull(processPanel.querySelector(".customer-rt"))) {
                    appendFlagNode(processPanel, "customer-rt")
                    const oldRtNode = processPanel.querySelectorAll(".bpmn-tiny-label");
                    if (nonNull(oldRtNode)) {
                        oldRtNode.forEach(v => v.style.display = "none")
                    }
                    getByTraceId(apiNodeLogList => {
                        for (let i = 0; i < apiNodeLogList.length; i++) {
                            const log = apiNodeLogList[i]
                            const textNode = processPanel.querySelector("[data-element-id='" + log.nodeId + "'] text")
                            if (isNull(textNode)) {
                                continue
                            }
                            const tspan = textNode.querySelector("tspan")
                            if (nonNull(tspan)) {
                                const rtNode = tspan.cloneNode(true)
                                textNode.appendChild(rtNode)
                                rtNode.setAttribute("x", "65")
                                rtNode.setAttribute("y", "15")
                                rtNode.innerHTML = window.pageHelper.formatString(log.rt, 4, '&nbsp;&nbsp;')
                            }
                        }
                    })
                }
            }, false)
        })

        // 工具栏设置按钮
        waitObserve(".app-actions", () => {
            const buttonLists = document.querySelector(".app-actions")
            if (isNull(buttonLists) || nonNull(document.querySelector(".setting-flag"))) {
                return
            }
            appendFlagNode(document.body, "setting-flag")
            const settingButton = document.createElement("div")
            settingButton.style.marginLeft = '10px'
            const button = document.createElement("button")
            button.type = "button"
            button.id = name
            button.className = "ant-btn ant-btn-default perf-tracked yqn-button"
            button.onclick = () => {
                const settingPanel = document.getElementById("copy-setting")
                settingPanel.style.display = settingPanel.style.display === 'block' ? 'none' : "block"
            }
            const span = document.createElement("span")
            span.textContent = "CopySetting"
            button.appendChild(span)
            settingButton.appendChild(button)
            buttonLists.appendChild(settingButton)
        })

        window.pageHelper.initSetting()
        getAppProjectName(data => {
            appName = data
            console.log("项目名称", data)
        })
    })


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

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


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

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

    function copyToClipboard(text, isJava = true) {
        let copyValue = localStorage.getItem("copyValue")
        copyValue = copyValue === null || copyValue === undefined ? '1' : copyValue
        if (copyValue === '1') {
            let textarea = document.createElement('textarea')
            textarea.value = text
            document.body.appendChild(textarea)
            textarea.select()
            document.execCommand('copy')
            document.body.removeChild(textarea)
            window.pageHelper.showToast("已拷贝 " + text, 1500)
        } else if (copyValue === '2') {
            let path = localStorage.getItem("path")
            path = path === null || path === undefined ? null : (path.endsWith("/") ? path : path + '/')
            otherReq("http://127.0.0.1:63342/api/file/" + path + "src/main/java/com/yqn/framework/composer/api/" + pageNameMap[window.pageHelper.getCurrentApiId()] + "/script/Script_" + text + (isJava ? ".java" : ".json"))
            window.pageHelper.showToast("已打开文件,如未打开,检查插件是否安装以及path是否正确", 2000)
        } else if (copyValue === '3') {
            const url = 'jetbrains://idea/navigate/reference?project=' + appName + '&fqn=com.yqn.framework.composer.api.' + pageNameMap[window.pageHelper.getCurrentApiId()] + '.script.Script_' + text;
            window.open(url)
            window.pageHelper.showToast("已打开文件,如未打开,请确认已安装Jetbrains Toolbox ", 2000)
        }
    }

    // 创建按钮
    function createButton(name, listener) {
        const button = document.createElement("button")
        button.type = "button"
        button.id = name
        button.className = "ant-btn ant-btn-link perf-tracked yqn-button yqn-link-no-padding customer-button"
        button.style.marginRight = '5px'
        button.onclick = listener
        const span = document.createElement("span")
        span.textContent = name
        button.appendChild(span)
        return button
    }

    const addressArr = ['', '/process', '/mq', '/job']

    function getApiPackageName(fuc, apiMode = 0) {
        remoteReq('/api/42080/api' + addressArr[apiMode] + '/details_composer', {
            "id": window.pageHelper.getCurrentApiId(),
        }, data => {
            if (isNull(data)) {
                if (apiMode === 0) {
                    getApiPackageName(fuc, 1)
                    getApiPackageName(fuc, 2)
                    getApiPackageName(fuc, 3)
                }
                return
            }
            let processDefine = JSON.parse(data.processDefine)
            fuc(isNull(processDefine.process.packageName) || processDefine.process.packageName === '' ? 'api_' + processDefine.process.id : processDefine.process.packageName)
        })
    }

    // 拿流程信息
    function getNodeInfo(nodeName, fuc, apiMode = 0) {
        remoteReq('/api/42080/api' + addressArr[apiMode] + '/details_composer', {
            "id": window.pageHelper.getCurrentApiId(),
        }, data => {
            if (isNull(data)) {
                if (apiMode === 0) {
                    getNodeInfo(nodeName, fuc, 1)
                    getNodeInfo(nodeName, fuc, 2)
                    getNodeInfo(nodeName, fuc, 3)
                }
                return
            }
            let processDefine = JSON.parse(data.processDefine)
            for (let i = 0; i < processDefine.nodes.length; i++) {
                if (processDefine.nodes[i].code === nodeName) {
                    fuc(processDefine.nodes[i])
                    break
                }
            }
        })
    }

    // 拿历史执行数据
    function getExecuteHistory(fuc) {
        remoteReq('/api/42086/apiLog/list', {}, data => {
            if (nonNull(data) && nonNull(data.content)) {
                fuc(data.content)
            }
        })
    }

    // 拿 trace 执行数据
    function getByTraceId(fuc, apiMode = 0) {
        const apiType = ['api', 'process', 'mq', 'job']
        remoteReq('/api/42086/apiLog/getByTraceId', {
            "traceIdLike": historyTrace,
            "testCaseId": null,
            "apiTypeCode": apiMode === 0 ? "api" : apiType[apiMode]
        }, data => {
            if (nonNull(data) && nonNull(data.apiNodeLogList) && data.apiNodeLogList.length !== 0) {
                fuc(data.apiNodeLogList)
            } else if (apiMode === 0) {
                getByTraceId(fuc, 1)
                getByTraceId(fuc, 2)
                getByTraceId(fuc, 3)
            }
        })
    }

    // 获取应用名称
    function getAppProjectName(fuc) {
        remoteReq('/api/42080/application/getById', {}, data => {
            if (nonNull(data)) {
                fuc(data.appName)
            }
        })
    }


    function remoteReq(url, model, fuc) {
        model.apiId = window.pageHelper.getCurrentApiId()
        model.appId = new URLSearchParams(window.location.href.split('?')[1]).get('appId')
        model.env = "qa4"
        model.page = 1
        model.size = 20
        jq.ajax({
            url: 'https://gw-ops.iyunquna.com' + 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) {
                    fuc(response.data)
                }
            },
            error: function (xhr, status, error) {
                console.log('Request failed:', error)
            }
        })
    }


    function otherReq(url) {
        jq.ajax(url, {
            method: "GET",
            xhrFields: {
                withCredentials: true
            },
            crossDomain: true
        })
    }
})();