ExpressEditor.html 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8" />
  5. <title></title>
  6. <!--jquery-->
  7. <script src="./extra/jquery/jquery.min.js"></script>
  8. <!--ztree-->
  9. <link href="./extra/ztree/css/zTreeStyle/zTreeStyle.css" rel="stylesheet" />
  10. <script src="./extra/ztree/js/jquery.ztree.all.min.js"></script>
  11. <script src="./extra/ztree/js/jquery.ztree.exhide.min.js"></script>
  12. <script src="extra/layer/layer.min.js"></script>
  13. <!--zlExpressEditor-->
  14. <link href="./css/ExpressEditor.css" rel="stylesheet" />
  15. <script src="./monaco-editor/min/vs/loader.min.js"></script>
  16. <script src="./js/EnvironmentHelper.js"></script>
  17. <script src="./js/ExpressEditor.js"></script>
  18. <script src="./js/ExpressExecutor.js"></script>
  19. <script>
  20. //测试代码
  21. (function () {
  22. //编辑器配置信息
  23. let setting = {
  24. //初始化的时候需要设置的值
  25. "value": "",
  26. //编辑器语言
  27. "language": "javascript",
  28. //是否只读
  29. "readonly": false,
  30. //是否显示左侧资源树
  31. "showResource": true,
  32. //显示资源树的部件页卡
  33. "showPartResource": true,
  34. //左侧资源树的第一个页卡的名称
  35. "partResourceTabName": "部件",
  36. //显示函数资源树页卡
  37. "showFunctionResource": true,
  38. //显示参数资源树部件页卡
  39. "showParameterResource": true,
  40. //是否显示校验按钮
  41. "showCheckButton": false,
  42. //是否开起缩略图
  43. "minimap": false,
  44. //表达式返回值类型
  45. "returnType": EnvironmentHelper.PropertyType.Any,
  46. //资源信息[第一个页卡,可能没有]
  47. "resource": [
  48. {
  49. //必填,且必须满足JS的变量定义规则
  50. "id": "id",
  51. //显示名,必填,用于在表达式编辑器显示用的名称
  52. "name": "部件1",
  53. /**
  54. 节点类型
  55. path:表示参与路径提示(如例子中的“属性”);
  56. group:表示不参与路径(如例子中的“方法”);
  57. property:表示该节点是个属性;
  58. method:表示该节点表示一个方法;
  59. property和method只能出现在叶子节点;
  60. */
  61. "nodetype": EnvironmentHelper.ResourceNodeType.Path,
  62. "nodes": [
  63. {
  64. "id": "properties",
  65. "name": "属性",
  66. "nodetype": EnvironmentHelper.ResourceNodeType.Path,
  67. "nodes": [
  68. {
  69. "id": "P_BJID",
  70. "name": "部件ID",
  71. "nodetype": EnvironmentHelper.ResourceNodeType.Property,
  72. //描述信息,当用户鼠标悬停或进行“.”操作的时候出现提示
  73. "note": "描述信息",
  74. //根据nodetype的不同,有不同的description
  75. "description": {
  76. //默认值,进行表达式校验的时候使用的值,可以不指定,而后系统以给定类型虚拟一个默认值进行校验
  77. "value": 123,
  78. //必填:属性(property)的数据类型,用于校验返回结果类型
  79. "type": EnvironmentHelper.PropertyType.Number,
  80. }
  81. },
  82. {
  83. "id": "P_BJMC",
  84. "name": "部件名称",
  85. "nodetype": "property",
  86. "note": "描述信息",
  87. "description": {
  88. "value": "text",
  89. "type": "string",
  90. }
  91. },
  92. {
  93. "id": "P_CS",
  94. "name": "选项测试",
  95. "nodetype": EnvironmentHelper.ResourceNodeType.Property,
  96. "note": "描述信息",
  97. "description": {
  98. "value": [
  99. { display: "枚举Key1", value: "枚举Value1" },
  100. { display: "枚举Key2", value: "枚举Value2" },
  101. { display: "枚举Key3", value: "枚举Value3" }
  102. ],
  103. "type": EnvironmentHelper.PropertyType.Enum,
  104. "selItemMode": "value",//或:display(选择“选项明细”返回name)
  105. "showMode": "表达式编辑"//或:选项值域选择(不显示运算符)
  106. }
  107. },
  108. {
  109. "id": "P_CS2",
  110. "name": "选项测试2",
  111. "nodetype": EnvironmentHelper.ResourceNodeType.Property,
  112. "note": "描述信息",
  113. "description": {
  114. "value": function (pid) {
  115. //xxxx
  116. return [
  117. { display: 1, value: 1 },
  118. { display: 2, value: 2 },
  119. { display: 3, value: 2 }
  120. ];
  121. },
  122. "type": EnvironmentHelper.PropertyType.Enum,
  123. "selItemMode": "value",//或:display(选择“选项明细”返回name)
  124. "showMode": "表达式编辑"//或:选项值域选择(不显示运算符)
  125. }
  126. }
  127. ]
  128. },
  129. {
  130. "id": "methods",
  131. "name": "方法",
  132. //group:表示该节点仅用于分组,即弹出式表达式的左侧树形界面的分组,在实际使用中,并不参数路径精算。例如本例中,是直接通过"部件1.getSelection()"进行访问该方法,绕开“方法”两个字
  133. "nodetype": "group",
  134. "nodes": [
  135. {
  136. "id": "getSelection",
  137. "name": "获取焦点行",
  138. "nodetype": "method",
  139. "note": "描述信息",
  140. "description": {
  141. "content": function () { },
  142. //方法的默认返回值,若没有返回值,则根据返回类型(returnType)虚拟一个默认返回值
  143. "returnValue": "text",
  144. //返回类型
  145. "returnType": "string",
  146. //参数信息,数组类型,每个参数按给定顺序输入
  147. "argsInfo": [{
  148. //参数名
  149. "argName": "arg1",
  150. //参数的类型
  151. "argType": "string",
  152. //参数的描述信息,在用户输入参数的时候进行提示
  153. "argNote": "第1个参数的描述信息"
  154. },
  155. {
  156. "argName": "arg2",
  157. "argType": "number",
  158. "argNote": "第2个参数的描述信息"
  159. }]
  160. }
  161. }
  162. ]
  163. }
  164. ]
  165. }
  166. ],
  167. //扩展的其他函数方法
  168. "extension": [
  169. {
  170. //名称需要唯一
  171. "name": "ComponentBase",
  172. //通过文件的方式装载
  173. "type": "file",
  174. //基于跟目录的绝对路径的JS文件[文件路径必须使用正斜杠]
  175. "filePath": "/component/ComponentBase.js",
  176. //是否加入左侧资源树[可以不传,默认是true]
  177. "addInResource": true
  178. },
  179. {
  180. //名称需要唯一
  181. "name": "own",
  182. //通过实例的方式装载
  183. "type": "instance",
  184. //实例对象
  185. "instance": new Object()
  186. },
  187. {
  188. //名称需要唯一
  189. "name": "selfDefined",
  190. //直接通过写代码引用
  191. "type": "code",
  192. //基于跟目录的绝对路径的JS文件[文件路径必须使用正斜杠]
  193. "jsCode": "var _self = new TableControl();"
  194. }
  195. ],
  196. //是否启用公共函数库,公共函数库的路径在"./extra/CommonFunctions.js",该文件内定义的所有方法都将被添加到控件的智能提示库
  197. "useCommonFunctions": true,
  198. //扩展参数[参数页卡],配置信息与上方资源信息(resource)相视,但不允许出现method类型的节点
  199. "parameters": [
  200. {
  201. "id": "id",
  202. "name": "系统参数",
  203. "nodetype": "group",
  204. "nodes": [{
  205. "id": "id1",
  206. "name": "系统参数1",
  207. "nodetype": "property",
  208. "description": {
  209. "value": "text",
  210. "type": "string",
  211. }
  212. }, {
  213. "id": "id2",
  214. "name": "系统参数2",
  215. "nodetype": "property",
  216. "description": {
  217. "value": "text",
  218. "type": "string",
  219. }
  220. }],
  221. },
  222. {
  223. "id": "id",
  224. "name": "环境参数",
  225. "nodetype": "group",
  226. "nodes": [{
  227. "id": "id1",
  228. "name": "环境参数1",
  229. "nodetype": "property",
  230. "description": {
  231. "value": "text",
  232. "type": "string",
  233. }
  234. }],
  235. }
  236. ],
  237. //提示词(用于自动补全的词语)
  238. "promptWords":["测试1","麻醉中"]
  239. }
  240. window.onload = function () {
  241. init_demo = function () {
  242. InitEditor(setting);
  243. }
  244. }
  245. })();
  246. function init_demo() {
  247. }
  248. </script>
  249. </head>
  250. <body>
  251. <div class="container-fluid">
  252. <div class="row mt-1">
  253. <div class="zl-side">
  254. <ul class="nav nav-tabs" id="myTab" role="tablist">
  255. <li class="nav-item">
  256. <a class="nav-link active" id="first-tab" data-toggle="tab" href="#first" role="tab" aria-controls="first" aria-selected="true">
  257. <h6 id="tabPartName">部件</h6>
  258. </a>
  259. </li>
  260. <li class="nav-item">
  261. <a class="nav-link" id="second-tab" data-toggle="tab" href="#second" role="tab" aria-controls="second" aria-selected="false">
  262. <h6>函数</h6>
  263. </a>
  264. </li>
  265. <li class="nav-item">
  266. <a class="nav-link" id="third-tab" data-toggle="tab" href="#third" role="tab" aria-controls="third" aria-selected="false">
  267. <h6>参数</h6>
  268. </a>
  269. </li>
  270. </ul>
  271. <div class="tab-content" id="myTabContent">
  272. <div class="tab-pane fade show active" id="first" role="tabpanel" aria-labelledby="first-tab">
  273. <div class="row" style="height:40px;">
  274. <div class="col col-12">
  275. <div class="app-property-search">
  276. <div style="display:flex;">
  277. <i style="display: flex; align-content: center; justify-content: center; flex-wrap: wrap;">
  278. <svg style="width:20px;height:20px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  279. <circle cx="11" cy="11" r="8"></circle>
  280. <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
  281. </svg>
  282. </i>
  283. <input type="text" autocomplete="off" class="search" name="search" placeholder="输入名称进行搜索..." value="" />
  284. </div>
  285. </div>
  286. </div>
  287. </div>
  288. <div class="row" style="height:calc(100% - 40px)">
  289. <div class="col-12 nav-height-item">
  290. <div id="resource-tree" class="ztree"></div>
  291. </div>
  292. </div>
  293. </div>
  294. <div class="tab-pane fade" id="second" role="tabpanel" aria-labelledby="tabpanel-tab">
  295. <div class="row" style="height:40px;">
  296. <div class="col col-12">
  297. <div class="app-property-search">
  298. <div>
  299. <i style="display: flex; align-content: center; justify-content: center; flex-wrap: wrap;">
  300. <svg style="width:20px;height:20px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  301. <circle cx="11" cy="11" r="8"></circle>
  302. <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
  303. </svg>
  304. </i>
  305. <input type="text" autocomplete="off" class="search" name="search" placeholder="输入名称进行搜索..." value="" />
  306. </div>
  307. </div>
  308. </div>
  309. </div>
  310. <div class="row" style="height:calc(100% - 40px)">
  311. <div class="col-12 nav-height-item">
  312. <div id="function-tree" class="ztree"></div>
  313. </div>
  314. </div>
  315. </div>
  316. <div class="tab-pane fade" id="third" role="tabpanel" aria-labelledby="tabpanel-tab">
  317. <div class="row" style="height:40px;">
  318. <div class="col col-12">
  319. <div class="app-property-search">
  320. <div>
  321. <i style="display: flex; align-content: center; justify-content: center; flex-wrap: wrap;">
  322. <svg style="width:20px;height:20px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  323. <circle cx="11" cy="11" r="8"></circle>
  324. <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
  325. </svg>
  326. </i>
  327. <input type="text" autocomplete="off" class="search" name="search" placeholder="输入名称进行搜索..." value="" />
  328. </div>
  329. </div>
  330. </div>
  331. </div>
  332. <div class="row" style="height:calc(100% - 40px)">
  333. <div class="col-12 nav-height-item">
  334. <div id="parameter-tree" class="ztree"></div>
  335. </div>
  336. </div>
  337. </div>
  338. </div>
  339. </div>
  340. <div class="zl-side-splitter side-splitter-tb">
  341. <button class="zl-side-btn-svg" type="button" aria-expanded="true" title="折叠">
  342. <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" width="8px" height="24px" viewBox="0 0 8 24" enable-background="new 0 0 8 24" xml:space="preserve"><g opacity="0.8"> <polygon fill="#231F20" points="2,12 6,8 6,16 " /></g></svg>
  343. </button>
  344. </div>
  345. <div class="zl-main nav-height-editor">
  346. <div class="editor-btn px-3 editor-test-button" style="text-align: right;">
  347. <button class="btn btn-info btn-sm btn-editor" id="editor-test">校验</button>
  348. </div>
  349. <div id="editor" class="editor-style"></div>
  350. <div class="lay-message">
  351. <div class="lay-message-title" id="messageTitle">
  352. <span class="message-title-name">输出信息:</span>
  353. <span class="message-title-icon"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" class="message-icon" x="0px" y="0px" width="8px" height="24px" viewBox="0 0 8 24" enable-background="new 0 0 8 24" xml:space="preserve"><g opacity="0.8"> <polygon fill="#231F20" points="2,12 6,8 6,16 "></polygon></g></svg></span>
  354. </div>
  355. <div class="lay-message-value" id="debug-message">
  356. </div>
  357. </div>
  358. </div>
  359. </div>
  360. <div class="row">
  361. <div class="col-sm-12 mt-1">
  362. <div id="text">
  363. </div>
  364. </div>
  365. </div>
  366. </div>
  367. <script>
  368. (function () {
  369. //消息折叠展开
  370. $('#messageTitle').click(function (e) {
  371. if ($(".lay-message").hasClass("message-close")) {
  372. $(".lay-message").removeClass("message-close");
  373. } else {
  374. $(".lay-message").addClass("message-close");
  375. }
  376. });
  377. //分割功能
  378. Divsions({
  379. dDom: document.querySelector('.zl-side-splitter'),
  380. direction: 'vertical',
  381. foldDir: 'left',
  382. minSize: [{
  383. direction: 'prev',
  384. minisize: 144,
  385. opt: 'fold'
  386. }],
  387. //拖动结束后执行
  388. dropEnd() {
  389. if (editor) {
  390. editor.layout();
  391. }
  392. }
  393. })
  394. // 分割功能
  395. function Divsions(opt) {
  396. let dDom = opt.dDom // 分割线条
  397. let initX = 0; // 设置偏移量
  398. let fx = opt.direction // 设置分割方向 vertical(左右) crosswise(上下分割)
  399. let minsize = opt.minSize // 设置限制[{direction: 'prev', minisize: 200, opt:'fold'}]
  400. let prSize = null // 记录原始大小
  401. let foldDir = opt.foldDir // 折叠方向
  402. let dropEnd = opt.dropEnd;
  403. dDom.addEventListener('mousedown', BindDivisionClickEvent);
  404. dDom.querySelector('button').addEventListener('click', function (e) {
  405. e.stopPropagation()
  406. if (e.button !== 0) return
  407. Flod();
  408. });
  409. /**绑定分割条的事件(左右) */
  410. function BindDivisionClickEvent(e) {
  411. e.preventDefault()
  412. if (e.button !== 0) return
  413. initX = fx === 'vertical' ? e.clientX : e.clientY;
  414. fx === 'vertical' ? dDom.prOffsetLeft = dDom.offsetLeft : dDom.prOffsetTop = dDom.offsetTop
  415. dDom.style.position = 'relative';
  416. document.addEventListener('mousemove', BindDivsionMoveEvent);
  417. document.addEventListener('mouseup', BindDivsionUpMouseEvent);
  418. }
  419. function BindDivsionMoveEvent(e) {
  420. if (e.button !== 0) return
  421. e.preventDefault();
  422. if (!dDom) return;
  423. fx === 'vertical' ? dDom.style.left = (e.clientX - initX) + 'px' : dDom.style.top = (e.clientY - initX) + 'px';
  424. }
  425. function BindDivsionUpMouseEvent(e) {
  426. document.removeEventListener('mousemove', BindDivsionMoveEvent);
  427. document.removeEventListener('mouseup', BindDivsionUpMouseEvent);
  428. CalcDomWidth();
  429. typeof dropEnd === 'function' && dropEnd()
  430. }
  431. /**计算dom 宽度 */
  432. function CalcDomWidth() {
  433. CalcMiniSize()
  434. dDom.style.position = ''; // 清除分割条的定位样式
  435. dDom.style[fx === 'vertical' ? 'left' : 'top'] = '';
  436. }
  437. /**设置大小 */
  438. function setSize(size) {
  439. fx === 'vertical' ? (function () {
  440. dDom.previousElementSibling.style.width = size.prev; // 设置前一个的dom宽度
  441. dDom.nextElementSibling.style.width = size.next; // 设置后者的宽度
  442. })() : (function () {
  443. dDom.previousElementSibling.style.height = size.prev; // 设置前一个的dom宽度
  444. dDom.nextElementSibling.style.height = size.next; // 设置后者的宽度
  445. })()
  446. }
  447. /**获取上一级的大小 */
  448. function getPrevSize() {
  449. let cx = fx === 'vertical' ? dDom.offsetLeft : dDom.offsetTop; // 当前鼠标距离左边框距离
  450. if ((fx === 'vertical' ? dDom.prOffsetLeft : dDom.prOffsetTop) === cx) return; // 未发生位置偏移时,停止操作
  451. return cx
  452. }
  453. /**获取总宽度 */
  454. function getAllSize() {
  455. return fx === 'vertical' ? dDom.parentElement.clientWidth : dDom.parentElement.clientHeight;
  456. }
  457. /**折叠 */
  458. function Flod() {
  459. if (dDom.classList.contains('is-flod')) {
  460. foldDir === 'left' || foldDir === 'top' ? dDom.previousElementSibling.style.display = '' : dDom.nextElementSibling.style.display = '';
  461. setSize(prSize)
  462. dDom.classList.remove('is-flod')
  463. } else {
  464. prSize = {
  465. prev: fx === 'vertical' ? dDom.previousElementSibling.style.width : dDom.previousElementSibling.style.height,
  466. next: fx === 'vertical' ? dDom.nextElementSibling.style.width : dDom.nextElementSibling.style.height
  467. }
  468. // 折叠
  469. // 获取折叠方向
  470. if (foldDir === 'left' || foldDir === 'top') {
  471. dDom.previousElementSibling.style.display = 'none';
  472. dDom.nextElementSibling.style[fx === 'vertical' ? 'width' : 'height'] = 'calc(100% - 10px)'
  473. } else {
  474. dDom.nextElementSibling.style.display = 'none';
  475. dDom.previousElementSibling.style[fx === 'vertical' ? 'width' : 'height'] = 'calc(100% - 10px)'
  476. }
  477. dDom.classList.add('is-flod')
  478. }
  479. if (editor) {
  480. editor.layout();
  481. console.log("enter");
  482. }
  483. }
  484. /**计算最小问题 */
  485. function CalcMiniSize() {
  486. let pSize = getPrevSize()
  487. if (pSize === undefined) return
  488. let aSize = getAllSize()
  489. let nSize = aSize - pSize
  490. let psize = getMinSize('prev') // 获取前一个dom的最小值
  491. let nsize = getMinSize('next') // 获取后一个dom的最小值
  492. let pState = psize && pSize < psize.minisize && psize.opt
  493. let nState = nsize && nSize < nsize.minisize && nsize.opt
  494. // 折叠
  495. if (pState === 'fold' || nState === 'fold') {
  496. Flod()
  497. } else {
  498. dDom.previousElementSibling.style.display === 'none' ? dDom.previousElementSibling.style.display = '' : '';
  499. dDom.nextElementSibling.style.display === 'none' ? dDom.nextElementSibling.style.display = '' : '';
  500. let f = (pSize / aSize).toFixed(2) // 算出比例
  501. // 剩下则是正常执行
  502. setSize({
  503. prev: `calc(${f * 100}% - 5px)`,
  504. next: `calc(${(1 - f) * 100}% - 5px)`
  505. })
  506. dDom.classList.remove('is-flod')
  507. }
  508. }
  509. /**获取最小宽度 */
  510. function getMinSize(d) {
  511. return minsize.find(a => a.direction === d);
  512. }
  513. }
  514. })()
  515. </script>
  516. </body>
  517. </html>