ExpressEditor.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. /* eslint-disable*/
  2. /**
  3. * 可以确定本文件仅被表达式编辑器引用,并且编辑器永远只可能作为iframe弹窗出来,所以不会对全局造成干扰
  4. * 编辑器主体所需的js代码
  5. * @author hjh
  6. */
  7. //编辑器实体
  8. let editor;
  9. /**
  10. * 资源信息
  11. * */
  12. let AllReourceInfo = [];
  13. /**
  14. * 所有的叶子节点
  15. * */
  16. let allLeafNodes = [];
  17. /**
  18. * 表达式执行器
  19. * */
  20. let expressExecutor;
  21. /**
  22. * 函数资源树的ztree对象
  23. */
  24. let zlExpressEditor_FunctionTree;
  25. /**
  26. * 枚举选项的所有配置信息
  27. * key:name
  28. * value:setting
  29. */
  30. let enumSettings = {};
  31. /**
  32. * 选中项取值模式
  33. * * 例:"value" 或"display"
  34. */
  35. let selItemMode = "display";
  36. let showMode = "表达式编辑";
  37. /**左侧资源树的配置信息 */
  38. let setting = {
  39. data: {
  40. key: {
  41. title: "title"
  42. }
  43. },
  44. view: {
  45. fontCss: (treeId, treeNode) => {
  46. if (treeNode.title) {
  47. if (treeNode.title.toLowerCase().indexOf('@deprecated') > 0) {
  48. return { color: "silver" };
  49. }
  50. }
  51. }
  52. },
  53. callback: {
  54. //结构树单击事件
  55. onDblClick: function () {
  56. let selectid = $("#myTab").find(".active")[0].id;
  57. //获取当前焦点所在的树
  58. let ztree;
  59. if (selectid === "first-tab") {
  60. ztree = this.getZTreeObj("resource-tree");
  61. }
  62. else if (selectid === "second-tab") {
  63. ztree = this.getZTreeObj("function-tree");
  64. } else {
  65. ztree = this.getZTreeObj("parameter-tree");
  66. }
  67. //获取选中的节点
  68. let selectedNodes = ztree.getSelectedNodes();
  69. if (selectedNodes.length > 0) {
  70. let node = selectedNodes[0];
  71. //仅叶子节点可以插入
  72. if (node.children == null) {
  73. //判断是否枚举
  74. if (node.propertyType == EnvironmentHelper.PropertyType.Enum) {
  75. layer.open({
  76. title: `选项编辑器-${node._path}`,
  77. btn: ["确定", "取消"],
  78. //area: ['400px', '520px'],//宽高
  79. type: 2,
  80. moveOut: true,
  81. content: './subview/enum_editor.html',
  82. success: function (layero, index) {
  83. //获取表达式编辑器窗体对象
  84. let iframeWin = window[layero.find('iframe')[0]['name']];
  85. //初始化编辑器
  86. iframeWin.Init({
  87. name: node.name,
  88. propertyId: node.id,
  89. options: node.propertyValue,
  90. selItemMode: node.selItemMode,
  91. showMode: node.showMode
  92. }, window.enumSettings);
  93. this.iframeAuto(layero, index)
  94. },
  95. yes: function (index, layero) {
  96. let iframeWin = window[layero.find('iframe')[0]['name']];
  97. var enumSetting = iframeWin.GetEnumSetting();
  98. if (enumSetting) {
  99. //todo:考虑名字重复
  100. enumSettings[enumSetting.name] = enumSetting;
  101. var newRange = InsertValue(` 选项内容_` + enumSetting.name + " ");
  102. newRange.startColumn += 1;
  103. newRange.endColumn -= 1
  104. var decoration = EnvironmentHelper.lightingRange(editor, newRange, enumSetting.name);
  105. enumSettings[enumSetting.name].decoration = decoration;
  106. layer.close(index);
  107. }
  108. },
  109. iframeAuto: function (layero, idnex) {
  110. let iframe_h = layer.getChildFrame("html", idnex).outerHeight()
  111. , n = $("#layui-layer" + idnex)
  112. , title_h = n.find(".layui-layer-title").outerHeight() || 0
  113. , btn_h = n.find(".layui-layer-btn").outerHeight() || 0;
  114. //外层layer高度等于容器高度加上头尾
  115. let layer_h = iframe_h + title_h + btn_h;
  116. let max_h = $(document).height();
  117. //如果高度超过max_h就设置为max_h,容器高度因为有头和尾,需要减去
  118. if (layer_h > max_h) {
  119. iframe_h = `calc(95vh - ${title_h + btn_h}px)`;
  120. layer_h = '95vh'
  121. }
  122. n.css({
  123. height: layer_h
  124. });
  125. n.find("iframe").css({
  126. height: iframe_h
  127. });
  128. this.reset(layero);
  129. },
  130. //重置layer的位置
  131. reset: function (layero) {
  132. var e = {};
  133. e.config = this;
  134. e.layero = layero;
  135. var g = $(window);
  136. var t = e.config,
  137. i = e.layero,
  138. n = [i.outerWidth(), i.outerHeight()],
  139. a = "object" == typeof t.offset;
  140. e.offsetTop = (g.height() - n[1]) / 2,
  141. e.offsetLeft = (g.width() - n[0]) / 2,
  142. a ? (e.offsetTop = t.offset[0], e.offsetLeft = t.offset[1] || e.offsetLeft) : "auto" !== t.offset && ("t" === t.offset ? e.offsetTop = 0 : "r" === t.offset ? e.offsetLeft = g.width() - n[0] : "b" === t.offset ? e.offsetTop = g.height() - n[1] : "l" === t.offset ? e.offsetLeft = 0 : "lt" === t.offset ? (e.offsetTop = 0, e.offsetLeft = 0) : "lb" === t.offset ? (e.offsetTop = g.height() - n[1], e.offsetLeft = 0) : "rt" === t.offset ? (e.offsetTop = 0, e.offsetLeft = g.width() - n[0]) : "rb" === t.offset ? (e.offsetTop = g.height() - n[1], e.offsetLeft = g.width() - n[0]) : e.offsetTop = t.offset),
  143. t.fixed || (e.offsetTop = /%$/.test(e.offsetTop) ? g.height() * parseFloat(e.offsetTop) / 100 : parseFloat(e.offsetTop), e.offsetLeft = /%$/.test(e.offsetLeft) ? g.width() * parseFloat(e.offsetLeft) / 100 : parseFloat(e.offsetLeft), e.offsetTop += g.scrollTop(), e.offsetLeft += g.scrollLeft()),
  144. "min" === i.data("maxminStatus") && (e.offsetTop = g.height() - (i.find(u[1]).outerHeight() || 0), e.offsetLeft = i.css("left")),
  145. i.css({
  146. top: e.offsetTop,
  147. left: e.offsetLeft
  148. });
  149. }
  150. });
  151. } else {
  152. //如果是方法的话需要加入括号
  153. if (node.nodetype === EnvironmentHelper.ResourceNodeType.Method) {
  154. InsertValue(node._path + "()");
  155. } else {
  156. InsertValue(node._path);
  157. }
  158. }
  159. }
  160. }
  161. }
  162. }
  163. };
  164. /**
  165. * 加载左侧资源树
  166. * @param {any} $node
  167. * @param {any} json
  168. * @returns
  169. */
  170. function LoadTree($node, json) {
  171. let $tree = $.fn.zTree.init($node, setting, json);
  172. //默认展开第一个节点【当前组件】
  173. if ($tree.getNodes().length > 0) {
  174. $tree.expandNode($tree.getNodes()[0], true, true);
  175. }
  176. return $tree;
  177. }
  178. /**
  179. * 设置值到指定位置
  180. * @param {any} text 待插入的文本
  181. * @param {any} selection 要插入的位置,当selection为null的时候,插在光标处
  182. * @returns 返回插入内容的range
  183. */
  184. function InsertValue(text, selection = null) {
  185. if (selection == null) {
  186. //外部不指定位置,则插入到光标位置
  187. selection = editor.getSelection();
  188. }
  189. editor.pushUndoStop();
  190. editor.executeEdits('name-of-edit', [
  191. {
  192. range: selection,
  193. text: text
  194. },
  195. ]);
  196. editor.pushUndoStop();
  197. return editor.getSelection();
  198. }
  199. /**
  200. * 设置Js值
  201. * @param {any} jscode
  202. */
  203. function SetJsCode(jscode) {
  204. editor.getModel().setValue(EnvironmentHelper.ChangeCodeToDisplay(jscode, allLeafNodes, enumSettings));
  205. }
  206. /**
  207. * 获取编辑器代码
  208. * */
  209. function GetJsCode() {
  210. let jsCode = editor.getModel().getValue();
  211. //返回的表达式是ID串
  212. return EnvironmentHelper.ChangeDisplayToCode(jsCode, allLeafNodes, enumSettings);
  213. //return editor.getModel().getValue();
  214. }
  215. /**
  216. * 获取完成的js表达式代码
  217. * 返回一个JSON,有两个
  218. */
  219. function GetFullJsCode() {
  220. return {
  221. display: editor.getModel().getValue(),
  222. code: GetJsCode(),
  223. enumSettings: enumSettings
  224. };
  225. }
  226. /**
  227. * 写入输出的调试信息
  228. * @param {any} content 写入的内容
  229. * @param {any} level 写入的类型[警告/信息/错误]
  230. */
  231. function WriteDebugInfo(content, level = null) {
  232. $("#debug-message").text(content);
  233. }
  234. /**
  235. * 初始化编辑器
  236. * @param {any} node 原生HTML节点
  237. * @param {JSON} setting 配置信息
  238. */
  239. function InitEditor(setting) {
  240. //解析资源信息
  241. AllReourceInfo = EnvironmentHelper.ParseResource(setting.resource);
  242. //当不显示左侧资源树,或者三个资源列表一个都不是true的时候,则直接隐藏
  243. if (setting.showResource == false || !(setting.showPartResource != false || setting.showFunctionResource != false || setting.showParameterResource != false)) {
  244. $(".zl-side").hide();
  245. //隐藏展开按钮
  246. $(".zl-side-splitter").hide();
  247. $(".zl-main").css("width", "100%");
  248. } else {
  249. //显示部件资源树
  250. if (setting.showPartResource != false) {
  251. //绑定资源树
  252. LoadTree($("#resource-tree"), AllReourceInfo);
  253. //显示指定的页卡名称
  254. if (setting.partResourceTabName != null && setting.partResourceTabName != "") {
  255. $("#tabPartName").text(setting.partResourceTabName)
  256. }
  257. $("#myTab > li:nth-child(1)").show();
  258. } else {
  259. $("#myTab > li:nth-child(1)").hide();
  260. }
  261. //显示函数资源树
  262. if (setting.showFunctionResource != false) {
  263. zlExpressEditor_FunctionTree = LoadTree($("#function-tree"), null);
  264. } else {
  265. $("#myTab > li:nth-child(2)").hide()
  266. }
  267. //显示常量资源树
  268. if (setting.showParameterResource != false) {
  269. LoadTree($("#parameter-tree"), null);
  270. } else {
  271. $("#myTab > li:nth-child(3)").hide()
  272. }
  273. //选中第一个页卡
  274. $('#myTab a:visible:first').trigger("click");
  275. }
  276. //判断是否显示校验按钮
  277. if (setting.showCheckButton == false) {
  278. //隐藏校验按钮
  279. $(".editor-test-button").hide();
  280. $(".lay-message").hide();
  281. }
  282. //提取叶子节点
  283. for (let info of AllReourceInfo) {
  284. allLeafNodes.push(...EnvironmentHelper.ExtractLeafNodes(info));
  285. }
  286. //将所有的叶子节点按照文本的长度进行排序
  287. allLeafNodes = allLeafNodes.sort((a, b) => b._path.length - a._path.length);
  288. //todo: 理论上不应该不为空,忘了何时加的这个代码
  289. //将原可能的editor销毁
  290. if (editor != null) {
  291. try {
  292. editor.dispose();
  293. } catch (e) {
  294. }
  295. }
  296. //初始化资源树
  297. editor = monaco.editor.create(document.getElementById("editor"), {
  298. minimap: {
  299. enabled: false
  300. },
  301. language: setting.language ?? "javascript",
  302. readOnly: setting.readonly ?? false
  303. });
  304. if (setting.readonly != true) {
  305. //启用选项编辑插件
  306. EnvironmentHelper.EnableEnumInputPlugin(editor);
  307. }
  308. if (setting.promptWords) {
  309. // 注册自定义的智能提示词
  310. monaco.languages.registerCompletionItemProvider(setting.language ?? "javascript", {
  311. provideCompletionItems: function () {
  312. // 根据数组,创建提示
  313. const suggestions = setting.promptWords.map(word => ({
  314. label: word,
  315. kind: monaco.languages.CompletionItemKind.Text,
  316. insertText: word
  317. }));
  318. return { suggestions };
  319. },
  320. });
  321. }
  322. //设置默认值
  323. if (setting.value != null && setting.value !== "") {
  324. SetJsCode(setting.value);
  325. }
  326. //将资源转为JS代码
  327. let jscode = EnvironmentHelper.ChangeToJsLib(AllReourceInfo);
  328. //将JS代码添加库到monaco
  329. monaco.languages.typescript.javascriptDefaults.addExtraLib(jscode, 'resource.js');
  330. //初始化执行器
  331. expressExecutor = new ExpressExecutor(jscode, setting.returnType);
  332. if (setting.useCommonFunctions != false) {
  333. //启用基础函数库
  334. let resourceJson = EnvironmentHelper.LoadExtraJsSource("./extra/commonFunctions.js", "公共函数");
  335. if (zlExpressEditor_FunctionTree != null) {
  336. let commonFunctionsNode = zlExpressEditor_FunctionTree.addNodes(null, {
  337. id: "zlExpress_CommonFunctions",
  338. name: "通用函数"
  339. })[0];
  340. zlExpressEditor_FunctionTree.addNodes(commonFunctionsNode, resourceJson);
  341. }
  342. }
  343. //扩展文件
  344. if (setting.extension && setting.extension.length > 0) {
  345. if (zlExpressEditor_FunctionTree != null) {
  346. zlExpressEditor_FunctionTree.addNodes(null, {
  347. id: "zlExpress_extension",
  348. name: "扩展函数"
  349. });
  350. }
  351. //获取资源树中,扩展函数的节点
  352. let extionsionNode = zlExpressEditor_FunctionTree?.getNodeByParam("id", "zlExpress_extension", null);
  353. for (let extend of setting.extension) {
  354. if (extend.type === "code") {
  355. //直接通过代码进行装载
  356. monaco.languages.typescript.javascriptDefaults.addExtraLib(extend.jsCode, extend.name);
  357. } else if (extend.type === "instance") {
  358. if (setting.showFunctionResource != false) {
  359. var resource = EnvironmentHelper.GetMethedsAndPropertiesWithInstance(extend.jsCode);
  360. if (resource != null) {
  361. var resourceJson = { id: extend.name, name: extend.name, children: [], iconSkin: "class_icon" };
  362. for (var prop of resource.properties) {
  363. //解析变量
  364. resourceJson.children.push({
  365. id: prop,
  366. name: prop,
  367. _path: extend.name + "." + prop,
  368. title: prop,
  369. iconSkin: "property_icon",
  370. nodetype: EnvironmentHelper.ResourceNodeType.Property
  371. });
  372. }
  373. for (var prop of resource.methods) {
  374. resourceJson.children.push({
  375. id: prop,
  376. name: prop,
  377. _path: extend.name + "." + prop,
  378. title: prop,
  379. iconSkin: "method_icon",
  380. nodetype: EnvironmentHelper.ResourceNodeType.Method
  381. });
  382. }
  383. zlExpressEditor_FunctionTree?.addNodes(extionsionNode, resourceJson);
  384. }
  385. }
  386. } else {
  387. //通过文件进行装载
  388. if (!extend.filePath.startsWith("/")) {
  389. extend.filePath = "/" + extend.filePath;
  390. }
  391. //localhostPaht全局变量在EnvironmentHelper已被定义
  392. let path = window.top.localhostPaht + extend.filePath;
  393. //加载扩展JavaScript,并返回分析后的资源JSON
  394. let resourceJson = EnvironmentHelper.LoadExtraJsSource(path, extend.name, extend.addInResource == null ? true : extend.addInResource);
  395. if (resourceJson != null) {
  396. /*
  397. //当命名空间下只有一个class,并且这个Class的名字与命名空间相同,则隐藏命名空间
  398. if (resourceJson.children.length == 1 && resourceJson.children[0].name == resourceJson.name) {
  399. resourceJson = resourceJson.children[0];
  400. }
  401. */
  402. //将资源JSON放入资源树进行展示
  403. resourceJson.children.forEach((nitem) => {
  404. zlExpressEditor_FunctionTree?.addNodes(extionsionNode, nitem);
  405. });
  406. }
  407. }
  408. }
  409. }
  410. //校验按钮单击事件
  411. $("#editor-test").click(function () {
  412. let jsCode = editor.getModel().getValue();
  413. let result = expressExecutor.Test(jsCode, setting.returnType);
  414. let debugInfo;
  415. if (result.success) {
  416. debugInfo = `执行结果:${JSON.stringify(result.value)}`;
  417. } else {
  418. debugInfo = "执行出错:" + result.info;
  419. }
  420. WriteDebugInfo(debugInfo);
  421. });
  422. /**
  423. * 绑定搜索过滤
  424. */
  425. $(".search").keyup(function (e) {
  426. let searchText = $(this).val();
  427. //获取ztree树对象
  428. let ztreeObj = $.fn.zTree.getZTreeObj($(this).parents(".tab-pane").find(".ztree").attr("id"));
  429. if (searchText != null && searchText != "") {
  430. //隐藏所有节点
  431. ztreeObj.hideNodes(ztreeObj.transformToArray(ztreeObj.getNodes()));
  432. var pattern = new RegExp(searchText, "i");
  433. //需要显示的报表节点
  434. showNodes = ztreeObj.getNodesByFilter(p => (p.type != 0 && pattern.test(p.name)));
  435. if (showNodes && showNodes.length > 0) {
  436. ztreeObj.showNodes(showNodes);
  437. ztreeObj.showNodes(GetAllParentShowAbleNodes(showNodes));
  438. }
  439. } else {
  440. ztreeObj.showNodes(ztreeObj.transformToArray(ztreeObj.getNodes()));
  441. }
  442. });
  443. /**
  444. * 获取所有应该显示的父级节点
  445. * @param {any} nodes
  446. * @returns
  447. */
  448. function GetAllParentShowAbleNodes(nodes) {
  449. let Nodes = [];
  450. for (var i = nodes.length - 1; i >= 0; i--) {
  451. let node = nodes[i];
  452. Nodes.push(...GetParentNodes(node));
  453. }
  454. return Nodes;
  455. }
  456. /**
  457. * 获取父级节点
  458. * @param {any} node
  459. * @returns
  460. */
  461. function GetParentNodes(node) {
  462. let Nodes = [];
  463. let pNode = node.getParentNode();
  464. if (pNode != null) {
  465. Nodes.push(pNode);
  466. Nodes.push(...GetParentNodes(pNode));
  467. }
  468. return Nodes;
  469. }
  470. }
  471. // 使用.on()方法给每个tab绑定点击事件
  472. $(document).on('click', '#myTab a.nav-link', function (e) {
  473. e.preventDefault();
  474. // 移除所有tab的激活状态
  475. $('#myTab a').removeClass('active');
  476. // 给被点击的tab添加激活状态
  477. $(this).addClass('active');
  478. // 获取被点击的tab对应的内容的id
  479. var tabContentId = $(this).attr('href');
  480. // 隐藏所有的tab内容
  481. $('.tab-pane').removeClass('show active');
  482. // 显示被点击的tab对应的内容
  483. $(tabContentId).addClass('show active');
  484. });