lowerCodeHelper

低代码平台助手

当前为 2023-12-01 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name lowerCodeHelper
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.2
  5. // @description 低代码平台助手
  6. // @author Ziker
  7. // @match https://ops.iyunquna.com/63008/*
  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. getCurrentAppIndex() {
  34. const nodes = document.getElementsByClassName("bar-tab");
  35. for (let i = 0; i < nodes.length; i++) {
  36. if (nodes[i].className.indexOf("bar-tab-selected") > 0) {
  37. return i;
  38. }
  39. }
  40. return -1;
  41. },
  42. getCurrentAppId() {
  43. const nodes = document.getElementsByClassName("tab-content-presentation-components");
  44. let className = nodes[this.getCurrentAppIndex()].className;
  45. return className.substring(className.indexOf("theia-tab-") + "theia-tab-".length);
  46. },
  47. sleep(duration) {
  48. return new Promise(resolve => {
  49. setTimeout(resolve, duration)
  50. })
  51. },
  52. showToast(msg, duration) {
  53. // 显示提示
  54. duration = isNaN(duration) ? 3000 : duration;
  55. const m = document.createElement('div');
  56. m.innerHTML = msg;
  57. m.style.cssText = "display: flex;justify-content: center;align-items: center;width:60%; min-width:180px; " +
  58. "background:#000000; opacity:0.98; height:auto;min-height: 50px;font-size:25px; color:#fff; " +
  59. "line-height:30px; text-align:center; border-radius:4px; position:fixed; top:85%; left:20%; z-index:999999;";
  60. document.body.appendChild(m);
  61. setTimeout(function () {
  62. const d = 0.5;
  63. m.style.webkitTransition = '-webkit-transform ' + d + 's ease-in, opacity ' + d + 's ease-in';
  64. m.style.opacity = '0';
  65. setTimeout(function () {
  66. document.body.removeChild(m)
  67. }, d * 1000);
  68. }, duration);
  69. },
  70. copyToClipboard(text) {
  71. // 创建一个临时的 textarea 元素
  72. let textarea = document.createElement('textarea');
  73. // 设置 textarea 的值为要拷贝的文本内容
  74. textarea.value = text;
  75. // 将 textarea 添加到文档中
  76. document.body.appendChild(textarea);
  77. // 选中 textarea 的内容
  78. textarea.select();
  79. // 执行复制操作
  80. document.execCommand('copy');
  81. // 移除临时的 textarea 元素
  82. document.body.removeChild(textarea);
  83. this.showToast("已拷贝 " + text)
  84. },
  85. formatString(str, len, padding) {
  86. const diff = len - str.toString().length;
  87. if (diff > 0) {
  88. return padding.repeat(diff) + str;
  89. } else {
  90. return str;
  91. }
  92. }
  93. }
  94. })(window);
  95.  
  96.  
  97. (function () {
  98. let historyTrace = ''
  99. 'use strict';
  100. jq(document).ready(function () {
  101. // 监听api tab变动
  102. waitObserve(".tabs-bar", () => {
  103. // 应用接口面板
  104. const appPanelTag = ".tab-content-presentation-components.theia-tab-" + window.pageHelper.getCurrentAppId();
  105. // 监听属性面板变动
  106. waitObserve(appPanelTag + " .p-8", () => {
  107. const p8 = document.querySelector(appPanelTag + " .p-8");
  108. const panel = p8.querySelector(".ant-form.ant-form-vertical.yqn-form");
  109. if (nonNull(panel) && isNull(panel.querySelector(".customScriptInfo"))) {
  110. const div = document.createElement("div");
  111. div.className = "customScriptInfo";
  112. panel.appendChild(div)
  113. // 获取当前NodeName
  114. const childProcessNode = panel.querySelector("#code");
  115. let nodeName = nonNull(childProcessNode) ? childProcessNode.value : p8.querySelector(".dashboard-code span").textContent
  116. // 获取当前节点信息
  117. getNodeInfo(nodeName, node => {
  118. // 处理信息
  119. const divNode = document.querySelector(appPanelTag + " .customScriptInfo");
  120. // 清空显示
  121. divNode.innerHTML = ""
  122. // 脚本
  123. if (nonNull(node.scriptId)) {
  124. divNode.appendChild(createButton("脚本:" + node.scriptId, () => window.pageHelper.copyToClipboard(node.scriptId)))
  125. }
  126. // 入参
  127. let id = node.inputScriptId;
  128. const inputScriptIds = node.inputScriptIds;
  129. if (nonNull(inputScriptIds)) {
  130. id = nonNull(id) ? id : inputScriptIds.example;
  131. id = nonNull(id) ? id : inputScriptIds.record;
  132. id = nonNull(id) ? id : inputScriptIds.recordList;
  133. id = nonNull(id) ? id : inputScriptIds.condition;
  134. id = nonNull(id) ? id : inputScriptIds.id;
  135. }
  136. if (isNull(id) && nonNull(node.dslBulidData)) {
  137. id = node.dslBulidData.dslScriptId;
  138. }
  139. if (nonNull(id)) {
  140. divNode.appendChild(createButton("入参:" + id, () => window.pageHelper.copyToClipboard(id)))
  141. }
  142. // 条件
  143. const executeScriptId = node.executeScriptId;
  144. if (nonNull(executeScriptId)) {
  145. divNode.appendChild(createButton("条件:" + executeScriptId, () => window.pageHelper.copyToClipboard(executeScriptId)))
  146. }
  147. // 校验
  148. const assertScriptId = node.assertScriptId;
  149. if (nonNull(assertScriptId)) {
  150. divNode.appendChild(createButton("校验:" + assertScriptId, () => window.pageHelper.copyToClipboard(assertScriptId)))
  151. }
  152. })
  153. }
  154. })
  155.  
  156. // 监听执行历史面板变动
  157. const executeHistoryBodyTag = appPanelTag + " .test-split-item.test-split-item-right .ant-table-body tbody";
  158. waitObserve(executeHistoryBodyTag, () => {
  159. const lines = document.querySelectorAll(executeHistoryBodyTag + " .ant-table-row.ant-table-row-level-0");
  160. if (nonNull(lines) && isNull(lines[0].querySelector(".customer-button-div"))) {
  161. const div = document.createElement("div");
  162. lines[0].appendChild(div)
  163. div.className = "customer-button-div";
  164. div.style.display = "none"
  165. getExecuteHistory(content => {
  166. for (let i = 0; i < content.length; i++) {
  167. const tds = lines[i].querySelectorAll("td");
  168. const actionCol = tds[tds.length - 1];
  169. const detailButton = actionCol.querySelectorAll("button")[0];
  170.  
  171. detailButton.style.display = "none"
  172. actionCol.insertBefore(createButton("RT:" + content[i].rt, () => {
  173. detailButton.click()
  174. historyTrace = tds[3].innerText
  175. }), detailButton)
  176. }
  177. })
  178. }
  179. })
  180. })
  181.  
  182. // 监听body变动
  183. waitObserve("body", () => {
  184. // 监听执行日志流程图变动
  185. const processPanelTag = ".node-bpmn #canvas .bjs-container .djs-container .viewport .layer-base";
  186. waitObserve(processPanelTag, () => {
  187. const processPanel = document.querySelector(processPanelTag);
  188. const rtDiv = processPanel.querySelector(".customer-rt");
  189. if (isNull(rtDiv)) {
  190. const div = document.createElement("div");
  191. processPanel.appendChild(div)
  192. div.className = "customer-rt";
  193. div.style.display = "none"
  194. getByTraceId(apiNodeLogList => {
  195. for (let i = 0; i < apiNodeLogList.length; i++) {
  196. const log = apiNodeLogList[i];
  197. const textNode = processPanel.querySelector("[data-element-id='" + log.nodeId + "'] text");
  198. if (isNull(textNode)) {
  199. continue
  200. }
  201. const tspan = textNode.querySelector("tspan");
  202. if (nonNull(tspan)) {
  203. const rtNode = tspan.cloneNode(true);
  204. textNode.appendChild(rtNode)
  205. rtNode.setAttribute("x", "65")
  206. rtNode.setAttribute("y", "15")
  207. rtNode.innerHTML = window.pageHelper.formatString(log.rt, 4, '&nbsp;&nbsp;')
  208. }
  209. }
  210. })
  211. }
  212. }, false)
  213. })
  214. })
  215.  
  216. function nonNull(o) {
  217. return o !== null && o !== undefined;
  218. }
  219.  
  220. function isNull(o) {
  221. return o === null || o === undefined;
  222. }
  223.  
  224. // 等待出现并监听变化
  225. function waitObserve(visibleTag, fun, attributes = true) {
  226. window.pageHelper.waitElementVisible(visibleTag, 0, () => {
  227. new MutationObserver(function (mutationsList) {
  228. fun()
  229. }).observe(document.querySelector(visibleTag), {
  230. attributes: attributes,
  231. childList: true,
  232. subtree: true,
  233. characterData: true
  234. })
  235. }).then(r => {
  236. })
  237. }
  238.  
  239. // 创建按钮
  240. function createButton(name, listener) {
  241. const button = document.createElement("button")
  242. button.type = "button"
  243. button.id = name
  244. button.className = "ant-btn ant-btn-link perf-tracked yqn-button yqn-link-no-padding customer-button"
  245. button.onclick = listener
  246. const span = document.createElement("span")
  247. span.textContent = name
  248. button.appendChild(span)
  249. return button;
  250. }
  251.  
  252. // 拿流程信息
  253. function getNodeInfo(nodeName, fuc) {
  254. getNodeInfoByApiMode(0, nodeName, fuc)
  255. }
  256.  
  257. const addressArr = ['', '/process', '/mq', '/job'];
  258.  
  259. // 拿流程信息
  260. function getNodeInfoByApiMode(apiMode, nodeName, fuc) {
  261. jq.ajax({
  262. url: 'https://gw-ops.iyunquna.com/api/42080/api' + addressArr[apiMode] + '/details_composer?guid=6f87e073-1da1-4017-b2de-c109abcd6d123',
  263. method: 'POST',
  264. xhrFields: {
  265. withCredentials: true
  266. },
  267. crossDomain: true,
  268. contentType: 'application/json',
  269. data: JSON.stringify({
  270. "header": {
  271. "xSourceAppId": "63008",
  272. "guid": "6f87e073-1da1-4017-b2de-c109abcd6d123",
  273. "lang": "zh",
  274. "timezone": "Asia/Shanghai"
  275. },
  276. "model": {
  277. "id": window.pageHelper.getCurrentAppId(),
  278. "appId": new URLSearchParams(window.location.href.split('?')[1]).get('appId')
  279. }
  280. }),
  281. success: function (response) {
  282. if (response.code === 200) {
  283. if (response.data === null && apiMode === 0) {
  284. getNodeInfoByApiMode(apiMode + 1, nodeName, fuc)
  285. getNodeInfoByApiMode(apiMode + 2, nodeName, fuc)
  286. getNodeInfoByApiMode(apiMode + 3, nodeName, fuc)
  287. return
  288. }
  289. let processDefine = JSON.parse(response.data.processDefine);
  290. for (let i = 0; i < processDefine.nodes.length; i++) {
  291. if (processDefine.nodes[i].code === nodeName) {
  292. fuc(processDefine.nodes[i])
  293. break
  294. }
  295. }
  296. }
  297. },
  298. error: function (xhr, status, error) {
  299. console.log('Request failed:', error);
  300. }
  301. });
  302. }
  303.  
  304. // 拿历史执行数据
  305. function getExecuteHistory(fuc) {
  306. jq.ajax({
  307. url: 'https://gw-ops.iyunquna.com/api/42086/apiLog/list?guid=c74610c3-b840-446f-a452-f678b32a39e8',
  308. method: 'POST',
  309. xhrFields: {
  310. withCredentials: true
  311. },
  312. crossDomain: true,
  313. contentType: 'application/json',
  314. data: JSON.stringify({
  315. "header": {
  316. "xSourceAppId": "63008",
  317. "guid": "6f87e073-1da1-4017-b2de-c109abcd6d123",
  318. "lang": "zh",
  319. "timezone": "Asia/Shanghai"
  320. },
  321. "model": {
  322. "apiId": window.pageHelper.getCurrentAppId(),
  323. "appId": new URLSearchParams(window.location.href.split('?')[1]).get('appId')
  324. }
  325. }),
  326. success: function (response) {
  327. if (response.code === 200 && nonNull(response.data) && nonNull(response.data.content)) {
  328. fuc(response.data.content)
  329. }
  330. },
  331. error: function (xhr, status, error) {
  332. console.log('Request failed:', error);
  333. }
  334. });
  335. }
  336.  
  337. const apiType = ['api', 'process', 'mq', 'job'];
  338.  
  339. // 拿 trace 执行数据
  340. function getByTraceId(fuc) {
  341. return getByTraceIdByApiMode(0, fuc)
  342. }
  343.  
  344. // 拿 trace 执行数据
  345. function getByTraceIdByApiMode(apiMode, fuc) {
  346. jq.ajax({
  347. url: 'https://gw-ops.iyunquna.com/api/42086/apiLog/getByTraceId?guid=3862879a-b379-4171-9644-289ee637dae0',
  348. method: 'POST',
  349. xhrFields: {
  350. withCredentials: true
  351. },
  352. crossDomain: true,
  353. contentType: 'application/json',
  354. data: JSON.stringify({
  355. "header": {
  356. "xSourceAppId": "63008",
  357. "guid": "6f87e073-1da1-4017-b2de-c109abcd6d123",
  358. "lang": "zh",
  359. "timezone": "Asia/Shanghai"
  360. },
  361. "model": {
  362. "appId": new URLSearchParams(window.location.href.split('?')[1]).get('appId'),
  363. "env": "qa4",
  364. "traceIdLike": historyTrace,
  365. "apiId": window.pageHelper.getCurrentAppId(),
  366. "testCaseId": null,
  367. "apiTypeCode": apiMode === 0 ? "api" : apiType[apiMode]
  368. }
  369. }),
  370. success: function (response) {
  371. if (response.code === 200 && nonNull(response.data) && nonNull(response.data.apiNodeLogList) && response.data.apiNodeLogList.length !== 0) {
  372. fuc(response.data.apiNodeLogList)
  373. } else if (apiMode === 0) {
  374. getByTraceIdByApiMode(1, fuc)
  375. getByTraceIdByApiMode(2, fuc)
  376. getByTraceIdByApiMode(3, fuc)
  377. }
  378. },
  379. error: function (xhr, status, error) {
  380. console.log('Request failed:', error);
  381. }
  382. });
  383. }
  384. })();
  385.