//处理表现层 let nodeList = Array(); //所有新生成的节点全部存储在这里,并且有唯一索引号,所有的定位均通过index进行,即将图保存下来了 let root = { index: 0, //在nodeList中的索引号 id: 0, parentId: 0, type: -1, option: 0, title: "root", sequence: [], parameters: { history: 1, tabIndex: 0, useLoop: false, //是否使用循环中的元素 xpath: "", //xpath iframe: false, //是否在iframe中 wait: 0, //执行后等待 waitType: 0, //等待类型,0代表固定时间,1代表随机等待 beforeJS: "", //执行前执行的js beforeJSWaitTime: 0, //执行前js等待时间 afterJS: "", //执行后执行的js afterJSWaitTime: 0, //执行后js等待时间 waitElement: "", //等待元素 waitElementTime: 10, //等待元素时间 waitElementIframeIndex: 0, //等待元素在第几个iframe中 }, isInLoop: false, //是否处于循环内 }; nodeList.push(root); let queue = new Array(); let actionSequence = new Array(); //存储图结构,每个元素为在nodelist里面的索引值,下面的id和pid根据此数组进行索引,然后再在nodelist里找 let nowNode = null; //存储现在所在的节点 let vueData = {nowNodeIndex: 0}; //存储目前所在节点的索引号,不能直接使用变量而需要用对象包起来 let option = 0; //工具箱选项 let title = ""; let parameterNum = 1; //记录目前的参数个数 let debuggable = true; //是否可以调试 // window.resizeTo( screen.availWidth, screen.availHeight ); //处理逻辑层 let app = new Vue({ el: '#app', data: { list: {nl: nodeList}, index: vueData, nodeType: 0, // 当前元素的类型 nowNode: null, // 用来临时存储元素的节点 codeMode: -1, //代码模式 loopType: -1, //点击循环时候用来循环选项 useLoop: false, //记录是否使用循环内元素 nowArrow: {"position": -1, "pId": 0, "num": 0}, params: {"parameters": []}, //提取数据的参数列表 TClass: -1, //条件分支的条件类别 paraIndex: 0, //当前参数的index XPaths: "", //xpath列表 }, mounted: function () { // setTimeout(function () { // $("#flowchart_graph")[0].scrollTo(0, 10000); // window.scrollTo(0, 10000); // console.log("scroll") // }, 500); }, watch: { nowArrow: { //变量发生变化的时候进行一些操作 deep: true, handler: function (newVal, oldVal) { let arrlist = document.getElementsByClassName("arrow"); if (oldVal != null) { for (let i = 0; i < arrlist.length; i++) { if (arrlist[i].getAttribute("position") == oldVal["position"] && arrlist[i].getAttribute("pid") == oldVal["pId"]) { arrlist[i].style.backgroundColor = ""; // 时刻指示现在应该插入的节点的位置 break; } } } for (let i = 0; i < arrlist.length; i++) { if (arrlist[i].getAttribute("position") == newVal["position"] && arrlist[i].getAttribute("pid") == newVal["pId"]) { arrlist[i].style.backgroundColor = "lavender"; // 时刻指示现在应该插入的节点的位置 break; } } } }, nowNode: { deep: true, handler: function (newVal, oldVal) { updateUI(); } }, loopType: { //循环类型发生变化的时候更新参数值 handler: function (newVal, oldVal) { // this.nowNode["parameters"]["loopType"] = newVal; } }, TClass: { handler: function (newVal, oldVal) { // this.nowNode["parameters"]["class"] = newVal; } }, useLoop: { handler: function (newVal, oldVal) { this.nowNode["parameters"]["useLoop"] = newVal; } }, params: { handler: function (newVal, oldVal) { this.nowNode["parameters"]["params"] = newVal["parameters"]; } }, codeMode: { handler: function (newVal, oldVal) { // this.nowNode["parameters"]["codeMode"] = newVal; // console.log(newVal, oldVal); // if (newVal == undefined){ // this.codeMode = parseInt(oldVal); // } // select绑定一定要用:value!!!不然会出现奇怪的问题 } } }, methods: { handleCodeModeChange: function () { // if (this.codeMode == undefined || this.codeMode == null || this.codeMode == -1) { // return; // } this.nowNode["parameters"]["codeMode"] = this.codeMode; // console.log(this.codeMode, value); switch (parseInt(this.codeMode)) { case 0: this.nowNode["title"] = LANG("执行JavaScript", "Run JavaScript"); break; case 1: this.nowNode["title"] = LANG("运行操作系统命令", "Run OS Command"); break; case 2: this.nowNode["title"] = LANG("执行JavaScript", "Run JavaScript"); break; case 3: this.nowNode["title"] = LANG("退出循环", "Exit Loop"); break; case 4: this.nowNode["title"] = LANG("跳过当前循环", "Skip Loop"); break; case 5: this.nowNode["title"] = LANG("执行Python代码", "Exec Python"); break; case 6: this.nowNode["title"] = LANG("获得Python表达式值", "Eval Python"); break; case 7: this.nowNode["title"] = LANG("暂停程序", "Pause Task"); break; case 8: this.nowNode["title"] = LANG("刷新页面", "Refresh Page"); break; case 9: this.nowNode["title"] = LANG("发送邮件", "Send Email"); break; case -1: // 跳转到其他操作时,不改变标题 break; default: // 默认情况 this.nowNode["title"] = LANG("自定义操作", "Custom Action"); break; } }, handleLoopTypeChange: function () { this.nowNode["parameters"]["loopType"] = this.loopType; switch (parseInt(this.loopType)) { case 0: this.nowNode["title"] = LANG("循环 - 单个元素", "Loop - Single Element"); break; case 1: this.nowNode["title"] = LANG("循环 - 不固定元素列表", "Loop - Dynamic Element List"); break; case 2: this.nowNode["title"] = LANG("循环 - 固定元素列表", "Loop - Fixed Element List"); break; case 3: this.nowNode["title"] = LANG("循环 - 文本列表", "Loop - Text List"); break; case 4: this.nowNode["title"] = LANG("循环 - 网址列表", "Loop - URL List"); break; case 5: this.nowNode["title"] = LANG("循环 - JavaScript命令返回值", "Loop - JavaScript Command Return Value"); break; case 6: this.nowNode["title"] = LANG("循环 - 系统命令返回值", "Loop - OS Command Return Value"); break; case 7: this.nowNode["title"] = LANG("循环 - Python表达式返回值", "Loop - Python Expression Evaluation Value"); break; default: this.nowNode["title"] = LANG("循环", "Loop"); break; } }, handleJudgeTypeChange: function () { this.nowNode["parameters"]["class"] = this.TClass; switch (parseInt(this.TClass)) { case 0: this.nowNode["title"] = LANG("无条件", "No Condition"); break; case 1: this.nowNode["title"] = LANG("当前页面包含文本", "Current Page Contains Text"); break; case 2: this.nowNode["title"] = LANG("当前页面包含元素", "Current Page Contains Element"); break; case 3: this.nowNode["title"] = LANG("当前循环项包含文本", "Current Loop Item Contains Text"); break; case 4: this.nowNode["title"] = LANG("当前循环项包含元素", "Current Loop Item Contains Element"); break; case 5: this.nowNode["title"] = LANG("JavaScript命令返回值", "JavaScript Command Return Value"); break; case 6: this.nowNode["title"] = LANG("系统命令返回值", "OS Command Return Value"); break; case 7: this.nowNode["title"] = LANG("针对当前循环项的JavaScript命令返回值", "JavaScript Command Return Value for Current Loop Item"); break; case 8: this.nowNode["title"] = LANG("执行环境下的Python表达式值", "Python Expression Evaluation Value"); break; default: this.nowNode["title"] = LANG("条件分支", "Condition"); break; } }, getCookies: function () { //获取cookies let command = new WebSocket("ws://localhost:" + getUrlParam("wsport")) command.onopen = function () { let message = { type: 7, //消息类型,0代表连接操作 }; this.send(JSON.stringify(message)); }; }, changeXPaths: function (XPaths) { let result = ""; for (let i = 0; i < XPaths.length; i++) { result += XPaths[i] + "\n"; } this.XPaths = result; }, addParam: function () { //添加参数 this.nowNode["parameters"]["params"].push({ "nodeType": 0, "contentType": 0, "relative": false, "name": LANG("自定义参数_" + parameterNum.toString(), "Custom_Field_" + parameterNum.toString()), "desc": "", "iframe": false, "extractType": 0, "relativeXPath": "//body", "recordASField": 1, "allXPaths": [], "exampleValues": [ { "num": 0, "value": LANG("自定义值", "Custom_Value") } ], "default": "", "beforeJS": "", "beforeJSWaitTime": 0, "JS": "", "JSWaitTime": 0, "afterJS": "", "afterJSWaitTime": 0, "downloadPic": 0, "paraType": "text", }); notifyParameterNum(1); this.paraIndex = this.nowNode["parameters"]["params"].length - 1; setTimeout(function () { $("#app > div.elements > div.toolkitcontain > table.toolkittb4 > tbody > tr:last-child")[0].scrollIntoView(false); //滚动到底部 }, 200); }, modifyParam: function (i) { //修改第i个参数 this.paraIndex = i; let clone_node = DeepClone(this.nowNode); clone_node.option = 11; //单独的提取数据参数节点 clone_node.parameters.index = i; trailElement(clone_node, 0); }, trailParam: function (i) { //试运行第i个参数 let clone_node = DeepClone(this.nowNode); clone_node.option = 11; //单独的提取数据参数节点 clone_node.parameters.index = i; trailElement(clone_node, 1); }, deleteParam: function (i) { //删除第i个参数 this.nowNode["parameters"]["params"].splice(i, 1); //如果参数删除完了,就把提取数据也删掉 if (this.nowNode["parameters"]["params"].length == 0) { deleteElement(); } }, upParam: function (i) { //上移第i个参数 if (i != 0) { let t = this.nowNode["parameters"]["params"].splice(i, 1)[0]; this.nowNode["parameters"]["params"].splice(i - 1, 0, t); } }, downParam: function (i) { //下移第i个参数 if (i != this.nowNode["parameters"]["params"].length - 1) { let t = this.nowNode["parameters"]["params"].splice(i, 1)[0]; this.nowNode["parameters"]["params"].splice(i + 1, 0, t); } }, getType: function (nodeType, contentType) { //根据类型得到字段名称 if (contentType == 2) { return "InnerHTML"; } else if (contentType == 3) { return "OuterHTML"; } if (nodeType == 2) { return LANG("链接地址", "Link Address"); } else if (nodeType == 1) { return LANG("链接文本", "Link Text"); } else if (nodeType == 4) { return LANG("图片地址", "Image Address"); } else { return LANG("文本", "Text"); } } } }) //深复制 function DeepClone(obj) { if (obj === null || typeof obj !== 'object') return obj; let cpObj = obj instanceof Array ? [] : {}; for (let key in obj) cpObj[key] = DeepClone(obj[key]); return cpObj; } // 根据元素类型返回不同元素的样式 function newNode(node) { id = node["id"]; title = node["title"]; type = node["type"]; if (type == 0) //顺序 { return `

${title}

`; } else if (type == 1) //循环 { return `

${title}

`; } else if (type == 2) //判断 { return LANG(`

${title}

点击此处在最右边增加条件分支

`, `

${title}

Click here to add a new condition to the right most

`); } else //判断分支 { return `

${title}

`; } } function arrowMouseDown(e) { if (e.button == 2) //右键点击 { if (option != 0) { app._data.nowArrow = {"position": this.getAttribute('position'), "pId": this.getAttribute('pId'), "num": 0}; } toolBoxKernel(e); } } //增加分支点击事件 function branchClick(e) { let judgeId = this.getAttribute('data'); let l = nodeList.length; let t = { index: l, id: 0, parentId: 0, type: 3, option: 10, // title: LANG("条件分支" + (nodeList[actionSequence[judgeId]]["sequence"].length + 1).toString(), "Condition " + (l + 1).toString()), title: LANG("无条件", "No Condition"), sequence: [], isInLoop: false, }; addParameters(t); nodeList.push(t); // nodeList[actionSequence[judgeId]]["sequence"].splice(0, 0, t.index); //开头插入 nodeList[actionSequence[judgeId]]["sequence"].push(t.index); //结尾插入 refresh(); app._data.nowArrow = {"position": -1, "pId": t["id"], "num": 0}; $("#" + t["id"]).click(); e.stopPropagation(); //防止冒泡 } function operationChange(e, theNode) { try { document.getElementById("contextMenu").remove(); } catch (e) { } if (nowNode != null) { nowNode.style.borderColor = "skyblue"; } nowNode = theNode vueData.nowNodeIndex = actionSequence[theNode.getAttribute("data")]; theNode.style.borderColor = "blue"; handleElement(); //处理元素 if (debuggable) { trailElement(app._data.nowNode, 0); } else { debuggable = true; } e.stopPropagation(); //防止冒泡 } function elementMousedown(e) { if (e.button == 2) //右键点击 { operationChange(e, this); } e.stopPropagation(); //防止冒泡 } //元素点击事件 function elementClick(e) { operationChange(e, this); e.stopPropagation(); } function elementDblClick(e) { try { let nodeType = app._data.nowNode["option"] if (nodeType >= 8) { if (nodeType == 8) { showInfo(LANG("试运行功能不适用于循环操作,请试运行循环内部的具体操作,如点击元素。", "The trial run function is not applicable to loop operations. Please try to run the specific operations in the loop, such as clicking elements.")); } } else { if (nodeType == 5 && (app._data.nowNode["parameters"]["codeMode"] != 0 && app._data.nowNode["parameters"]["codeMode"] != 8)) { showInfo(LANG("试运行自定义操作功能只适用于执行JavaScript和刷新页面操作。", "The trial run custom action function is only applicable to run JavaScript and refresh page operations.")); } else { trailElement(app._data.nowNode, 1); } } } catch (e) { showError(LANG("试运行功能只能在任务设计阶段,Chrome浏览器打开时使用!", "The trial run function can only be used when designing tasks and opening in Chrome browser!")); } e.stopPropagation(); //防止冒泡 } //箭头点击事件 function arrowClick(e) { if (option != 0) { app._data.nowArrow = {"position": this.getAttribute('position'), "pId": this.getAttribute('pId'), "num": 0}; } toolBoxKernel(e); } //增加元素函数 function addElement(op, param) { option = op; if (option == 1) { //打开网页选项 title = LANG("打开网页", "Open Page"); } else { title = $(".options")[option - 1].innerHTML; //获取新增操作名称 } toolBoxKernel(null, param); } // 工具箱操作函数 function toolBoxKernel(e, param = null) { if (option == 13) { //调整锚点 // let tarrow = DeepClone(app.$data.nowArrow); // refresh(); // app._data.nowArrow =tarrow; } else if (option == 11) { //复制操作 if (nowNode == null) { e.stopPropagation(); //防止冒泡 } else if (nowNode.getAttribute("dataType") > 0) { showError(LANG("循环和判断、条件分支不可复制!", "Cannot copy loop, if and condition!")); e.stopPropagation(); //防止冒泡 } else { let position = parseInt(nowNode.getAttribute('position')); let pId = nowNode.getAttribute('pId'); let tt = nodeList[nodeList[actionSequence[pId]]["sequence"][position]]; //在相应位置添加新元素 t = DeepClone(tt); //浅复制元素 let l = nodeList.length; t.index = l; nodeList.push(t); let position2 = parseInt(app._data.nowArrow['position']); let pId2 = app._data.nowArrow['pId']; nodeList[actionSequence[pId2]]["sequence"].splice(position2 + 1, 0, t.index); //在相应位置添加新元素 refresh(); //重新渲染页面 app._data.nowArrow = {"position": t["position"], "pId": t["parentId"], "num": 0}; $("#" + t["id"]).click(); //复制后点击复制后的元素 e.stopPropagation(); //防止冒泡 } } else if (option == 10) { //剪切操作 if (nowNode == null) { e.stopPropagation(); //防止冒泡 } else if ($(nowNode).is(".branch")) { showError(LANG("判断分支不可移动!", "Cannot move condition branch!")); e.stopPropagation(); //防止冒泡 } else { let position = parseInt(nowNode.getAttribute('position')); let pId = nowNode.getAttribute('pId'); let position2 = parseInt(app._data.nowArrow['position']); let pId2 = app._data.nowArrow['pId']; let id = nowNode.getAttribute('data'); let pidt = pId2; let move = true; while (pidt != 0) { if (pidt == id) { move = false; break; } pidt = nodeList[actionSequence[pidt]]["parentId"]; } if (move) //如果自己要移动到自己节点里就不允许移动 { let element = nodeList[actionSequence[pId]]["sequence"].splice(position, 1); //在相应位置删除元素 if (pId == pId2 && position < position2) //如果要移动的位置属于同一层并且是从前往后移动,注意需要控制数组插入位置向前错位 { position2--; } // console.log(element); nodeList[actionSequence[pId2]]["sequence"].splice(position2 + 1, 0, element[0]); //在相应位置添加新元素 refresh(); //重新渲染页面 // console.log(nodeList[element[0]]); app._data.nowArrow = { "position": nodeList[element[0]]["position"], "pId": nodeList[element[0]]["parentId"], "num": 0 }; $("#" + nodeList[element[0]]["id"]).click(); } else { showError(LANG("自己不能移动到自己的节点里!", "Cannot move inside self!")); } if (e != null) { e.stopPropagation(); //防止冒泡 } } } else if (option > 0) { //新增操作 let l = nodeList.length; let nt = null; let nt2 = null; if (option == 2 || option == 7) { //点击元素或移动到元素操作的名称更改 let l = 6; if (option == 2) { title = LANG("点击", "Click "); } else { title = LANG("移动到", "Move to "); l = 5; } try { content = param["content"]; } catch { content = LANG("元素", " Element"); } let str = content.trim(); if (str == "") { title += LANG("元素", "Element"); } else { if (window.location.href.indexOf("_CN") != -1) { if (str.length > l) { str = str.substring(0, l) + "..."; } } else { if (str.length > l + 7) { str = str.substring(0, l + 7) + "..."; } } title += str; } } let t = { id: 0, index: l, parentId: 0, type: 0, option: option, title: title, sequence: [], isInLoop: false, }; nodeList.push(t); if (option == 8) //循环 { t["type"] = 1; } else if (option == 9) //判断 { t["type"] = 2; // 增加两个分支 nt = { id: 0, parentId: 0, index: l + 1, type: 3, option: 10, title: LANG("无条件", "No Condition"), sequence: [], isInLoop: false, }; nt2 = { id: 0, parentId: 0, index: l + 2, type: 3, option: 10, title: LANG("无条件", "No Condition"), sequence: [], isInLoop: false, }; t["sequence"].push(nt.index); t["sequence"].push(nt2.index); nodeList.push(nt) nodeList.push(nt2); addParameters(nt); //增加选项的默认参数 addParameters(nt2); //增加选项的默认参数 } let position = parseInt(app._data.nowArrow['position']); let pId = app._data.nowArrow['pId']; nodeList[actionSequence[pId]]["sequence"].splice(position + 1, 0, t.index); //在相应位置添加新元素 refresh(); //重新渲染页面 //下面是确定添加元素之后下一个要插入的节点的位置 app._data.nowArrow = {"position": t["position"], "pId": t["parentId"], "num": 0}; addParameters(t); //增加选项的默认参数 if (param != null) { modifyParameters(t, param); } debuggable = false; if (option == 8) //循环情况下应插入在循环里面 { app._data.nowArrow = {"position": -1, "pId": t["id"], "num": 0}; $("#" + t["id"]).click(); } else if (option == 9) //判断插入到第一个判断条件中 { app._data.nowArrow = {"position": -1, "pId": nt["id"], "num": 0}; $("#" + nt["id"]).click(); } else { $("#" + t["id"]).click(); } if (e != null) e.stopPropagation(); //防止冒泡 option = 0; return t; } option = 0; } $(".options").mousedown(function () { option = parseInt(this.getAttribute("data")); title = this.innerHTML; if (option >= 10 && option <= 12 && (nowNode == null || nowNode.getAttribute("id") == 0)) { showError(LANG("目前未选中元素。", "No element selected。")); } else if (option == 12) { deleteElement(); $(".options")[12].click(); } }); function arrowDragOver(e) { e.preventDefault(); app._data.nowArrow = {"position": this.getAttribute('position'), "pId": this.getAttribute('pId'), "num": 0}; // console.log("dragover", app._data.nowArrow, nowNode); } function elementDragEnd(e) { // e.preventDefault(); // console.log("dragend"); if (nowNode != null) { nowNode.style.borderColor = "skyblue"; } nowNode = this; vueData.nowNodeIndex = actionSequence[this.getAttribute("data")]; this.style.borderColor = "blue"; handleElement(); //处理元素 option = 10; //剪切元素操作 toolBoxKernel.call(this, e); e.stopPropagation(); } function arrowDblClick(e) { option = 13; //调整锚点操作 app._data.nowArrow = {"position": this.getAttribute('position'), "pId": this.getAttribute('pId'), "num": 0}; toolBoxKernel.call(this, e); e.stopPropagation(); } function arrowDragEnd(e) { option = 13; //调整锚点操作 toolBoxKernel.call(this, e); e.stopPropagation(); } function optionDragEnd(e) { option = parseInt(this.getAttribute("data")); toolBoxKernel.call(this, e); e.stopPropagation(); } function bindEvents() { // 清空原来的listener然后再添加新的listener //以下绑定了左右键的行为 let rect = document.getElementsByClassName('clk'); for (let i = 0, rule; rule = rect[i++];) { rule.removeEventListener('mousedown', elementMousedown); rule.addEventListener('mousedown', elementMousedown); rule.removeEventListener('click', elementClick); rule.addEventListener('click', elementClick); rule.removeEventListener('dblclick', elementDblClick); rule.addEventListener('dblclick', elementDblClick); rule.removeEventListener('dragend', elementDragEnd); rule.addEventListener('dragend', elementDragEnd); } let arr = document.getElementsByClassName('arrow'); for (let i = 0, rule; rule = arr[i++];) { rule.removeEventListener('click', arrowClick); rule.addEventListener('click', arrowClick); rule.removeEventListener('mousedown', arrowMouseDown); rule.addEventListener('mousedown', arrowMouseDown); rule.removeEventListener('dblclick', arrowDblClick); rule.addEventListener('dblclick', arrowDblClick); rule.removeEventListener('dragover', arrowDragOver); rule.addEventListener('dragover', arrowDragOver); rule.removeEventListener('dragend', arrowDragEnd); rule.addEventListener('dragend', arrowDragEnd); } let branch = document.getElementsByClassName('branchAdd'); for (let i = 0, rule; rule = branch[i++];) { rule.removeEventListener('click', branchClick); rule.addEventListener('click', branchClick); } let options = document.getElementsByClassName("options"); for (let i = 0, rule; rule = options[i++];) { rule.removeEventListener('dragend', optionDragEnd); rule.addEventListener('dragend', optionDragEnd); } } //重新画图 function refresh(nowArrowReset = true) { $("#0").empty(); $("#0").append(`

`); actionSequence.splice(0); queue.splice(0); let idd = 1; queue.push(0); actionSequence.push(0); while (queue.length != 0) { let nd = queue.shift(); //取出父元素并建立对子元素的链接 for (let i = 0; i < nodeList[nd].sequence.length; i++) { nodeList[nodeList[nd].sequence[i]].parentId = nodeList[nd].id; nodeList[nodeList[nd].sequence[i]]["position"] = i; nodeList[nodeList[nd].sequence[i]].id = idd++; //检测元素是否位于循环内 if (nodeList[nd].option == 8 || nodeList[nd].isInLoop) { nodeList[nodeList[nd].sequence[i]].isInLoop = true; } else { nodeList[nodeList[nd].sequence[i]].isInLoop = false; } queue.push(nodeList[nd].sequence[i]); actionSequence.push(nodeList[nd].sequence[i]); } } if (nowArrowReset) //如果要重置锚点位置 { app._data.nowArrow = {"position": nodeList[0].sequence.length - 1, "pId": 0, "num": 0}; //设置默认要添加的位置是元素流程最开头处 } //第一个元素不渲染 for (let i = 1; i < actionSequence.length; i++) { let parentId = nodeList[actionSequence[i]]["parentId"]; $("#" + parentId).append(newNode(nodeList[actionSequence[i]])); } bindEvents(); } function deleteElement() { if (nowNode == null || nowNode.getAttribute("id") == 0) { showError(LANG("当前未选中元素!", "No element is selected now!")); return; } // if (nodeList[actionSequence[nowNode.getAttribute("data")]]["option"] == 1) { // showError("打开网页操作不可删除!"); // return; // } let position = parseInt(nowNode.getAttribute('position')); let pId = nowNode.getAttribute('pId'); let tnode = nodeList[actionSequence[pId]]["sequence"].splice(position, 1); //在相应位置删除元素 //循环的标记已经被删除的元素,因为删除循环后,循环内的元素也会 let queue = new Array(); queue.push(tnode[0]); while (queue.length > 0) { let index = queue.shift(); nodeList[index]["id"] = -1; //标记服务已被删除 for (let i = 0; i < nodeList[index]["sequence"].length; i++) { queue.push(nodeList[index]["sequence"][i]); } } app._data["nowNode"] = null; app._data["nodeType"] = 0; vueData.nowNodeIndex = 0; if (nowNode.getAttribute("datatype") == 3) { //如果删掉的是条件分支的话 pId = nowNode.parentNode.parentNode.getAttribute('pId'); position = nowNode.parentNode.parentNode.getAttribute('position'); } app.$data.nowArrow = {position: position - 1, "pId": pId, "num": 0}; //删除元素后锚点跳转到当前元素的上一个节点 refresh(false); //重新渲染页面 nowNode = null; //取消选择 } document.getElementById("flowchart_graph").oncontextmenu = function (e) { // 创建一个包含删除选项的右键菜单 let contextMenu = document.createElement("div"); contextMenu.id = "contextMenu"; contextMenu.innerHTML = `
${LANG("试运行", "Trial Run")}
${LANG("复制元素", "Copy Element")}
${LANG("剪切元素", "Move Element")}
${LANG("删除元素(双击)", "Delete Element (Double Click)")}
`; if (nowNode.getAttribute("datatype") == 3) { //如果删掉的是条件分支的话 contextMenu.innerHTML += `
${LANG("前移", "Move Up")}
${LANG("后移", "Move Down")}
`; // Add 前移 functionality contextMenu.children[4].addEventListener('click', function () { let conditionId = parseInt(nowNode.getAttribute('pid')); let position = parseInt(nowNode.getAttribute('position')); if (position > 0) { nodeList[actionSequence[conditionId]]["sequence"][position] = nodeList[actionSequence[conditionId]]["sequence"][position - 1]; nodeList[actionSequence[conditionId]]["sequence"][position - 1] = actionSequence[parseInt(nowNode.getAttribute('data'))]; refresh(); app._data.nowArrow = {"position": -1, "pId": 0, "num": 0}; $("#0").click(); e.stopPropagation(); //防止冒泡 } }); // Add 后移 functionality contextMenu.children[5].addEventListener('click', function () { let conditionId = parseInt(nowNode.getAttribute('pid')); let position = parseInt(nowNode.getAttribute('position')); if (position < nodeList[actionSequence[conditionId]]["sequence"].length - 1) { nodeList[actionSequence[conditionId]]["sequence"][position] = nodeList[actionSequence[conditionId]]["sequence"][position + 1]; nodeList[actionSequence[conditionId]]["sequence"][position + 1] = actionSequence[parseInt(nowNode.getAttribute('data'))]; refresh(); app._data.nowArrow = {"position": -1, "pId": 0, "num": 0}; $("#0").click(); e.stopPropagation(); //防止冒泡 } }); } // 设置右键菜单的样式 contextMenu.style.position = "absolute"; contextMenu.style.backgroundColor = "rgb(248, 249, 250)"; contextMenu.style.left = event.clientX + "px"; contextMenu.style.top = event.clientY + "px"; contextMenu.style.width = LANG("180px", "250px"); // 添加删除元素的功能 contextMenu.children[3].addEventListener("dblclick", function () { // myElement.remove(); // 删除元素 deleteElement(); contextMenu.remove(); // 删除右键菜单 }); // Add Test Run functionality contextMenu.children[0].addEventListener('click', function () { elementDblClick(e); contextMenu.remove(); // Remove the context menu }); // Add copy functionality contextMenu.children[1].addEventListener('click', function () { option = 11; //复制元素操作 showInfo(LANG("复制成功,请点击流程图中相应位置箭头以粘贴操作。", "Copy successfully, please click the arrow in the flow chart to paste.")); contextMenu.remove(); // Remove the context menu }); // Add cut functionality contextMenu.children[2].addEventListener('click', function () { option = 10; //剪切元素操作 showInfo(LANG("剪切成功,请点击流程图中相应位置箭头以粘贴操作。", "Cut successfully, please click the arrow in the flow chart to paste.")); contextMenu.remove(); // Remove the context menu }); // 将右键菜单添加到文档中 document.body.appendChild(contextMenu); } //屏蔽右键菜单 //删除元素 function inputDelete(e) { if (e.keyCode == 46) { e.stopPropagation(); //输入框按delete应该正常运行 //Electron中如果有showError或者confirm,执行后会卡死输入框,所以最好不要用 } }