EnvironmentHelper.js 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253
  1. /**
  2. * 表达式编辑器的环境编辑公共类
  3. * @author hjh
  4. */
  5. (function () {
  6. /**判断当前页面是否是HRS运行界面 */
  7. function IsRunningPage() {
  8. let win = window;
  9. do {
  10. if (win.HrsPage != null) {
  11. return true;
  12. } else {
  13. win = win.parent;
  14. }
  15. } while (win !== window.top);
  16. return false;
  17. }
  18. function getQueryVariable(variable) {
  19. let query = decodeURI(window.location.search.substring(1));
  20. let vars = query.split("&");
  21. for (let i = 0; i < vars.length; i++) {
  22. let pair = vars[i].split("=");
  23. if (pair[0].toUpperCase() == variable.toUpperCase()) { return decodeURIComponent(pair[1]); }
  24. }
  25. return ('');
  26. }
  27. var g_needLoadzlExpressEditor = getQueryVariable("g_needLoadzlExpressEditor") == "true";
  28. //是否是运行界面,仅当非运行界面才加载monaco editor
  29. if (!IsRunningPage() || (typeof g_needLoadzlExpressEditor != 'undefined' && g_needLoadzlExpressEditor == true)) {
  30. //获取绝对路径
  31. let curWwwPath = window.document.location.href;
  32. let pathName = window.document.location.pathname;
  33. let pos = curWwwPath.indexOf(pathName);
  34. window.top.localhostPaht = curWwwPath.substring(0, pos);
  35. //引入monaco编辑器
  36. require.config({ paths: { vs: window.top.localhostPaht + '/lib/zlExpressEditor/monaco-editor/min/vs' } });
  37. require(['vs/editor/editor.main'], function () {
  38. delete define.amd;
  39. });
  40. }
  41. })();
  42. /**
  43. * 环境解析帮助JS
  44. * */
  45. class EnvironmentHelper {
  46. //资源节点类型
  47. static ResourceNodeType = {
  48. Path: "path",
  49. Group: "group",
  50. Property: "property",
  51. Method: "method",
  52. Class: "class"
  53. };
  54. /**
  55. * 属性的类型
  56. * */
  57. static PropertyType = {
  58. Number: "number",
  59. String: "string",
  60. Boolean: "boolean",
  61. Enum: "enum",
  62. Any: "any"
  63. };
  64. /**
  65. * 根据JS文件路径获取JS文件的内容
  66. * @param {any} url js文件的路径
  67. * @param {string} name 扩展的名字,名字不能与已有的重复,不然会后者覆盖前者
  68. * @param {string} parseCode 是否解析代码并返回节点JSON
  69. */
  70. static LoadExtraJsSource(url, name = 'global.js', parseCode = true) {
  71. let functionJson = null;
  72. $.ajax({
  73. type: "GET",
  74. dataType: "text",
  75. url: url,
  76. async: false,
  77. success: (res) => {
  78. //添加依赖库
  79. monaco.languages.typescript.javascriptDefaults.addExtraLib(res, name);
  80. if (parseCode) {
  81. let children = new JavaScriptCodeHelper(res).Parse();
  82. if (children != null && children.length > 0) {
  83. children = children.sort((a, b) => {
  84. return b.nodetype.localeCompare(a.nodetype);
  85. });
  86. //添加函数到资源树
  87. functionJson = { id: name, name: name, children: children, iconSkin: "namespace_icon" };
  88. }
  89. }
  90. }
  91. });
  92. return functionJson;
  93. }
  94. /**
  95. * 通过实例获取对象含有的方法与属性
  96. * @param {any} instance
  97. * @returns {Array, Array} {methods, properties}
  98. */
  99. static GetMethedsAndPropertiesWithInstance(instance) {
  100. var x = instance; // 子类的实例
  101. var methods = []; // 用于存储所有方法的数组
  102. var properties = []; // 用于存储所有方法的数组
  103. while (x) { // 循环直到 x 为 null
  104. var props = Object.getOwnPropertyNames(x); // 获取 x 上的所有属性名
  105. for (var prop of props) { // 遍历属性名
  106. try {
  107. if (typeof x[prop] === "function") { // 检查属性值是否是函数类型
  108. if (prop != "constructor" && methods.indexOf(prop) == -1) {
  109. methods.push(prop); // 添加到方法数组中
  110. }
  111. continue;
  112. }
  113. } catch (e) { }
  114. if (properties.indexOf(prop) == -1) {
  115. properties.push(prop);
  116. }
  117. }
  118. x = Object.getPrototypeOf(x); // 获取 x 的原型对象
  119. if (x.hasOwnProperty("__lookupGetter__") && x.hasOwnProperty("__lookupSetter__")) {
  120. break;
  121. }
  122. }
  123. methods = methods.sort();
  124. properties = properties.sort();
  125. return { methods, properties };
  126. }
  127. /**
  128. * 解析资源信息
  129. * @param {JSON} resource
  130. */
  131. static ParseResource(resource) {
  132. //解析资源信息,转为固定的对象
  133. let info = BaseResourceInfo.ParseResourceInfo(resource);
  134. return info;
  135. }
  136. /**
  137. * 提取叶子节点
  138. * @param {any} info
  139. */
  140. static ExtractLeafNodes(info) {
  141. let leafNodes = [];
  142. if (info.children != null) {
  143. for (let child of info.children) {
  144. leafNodes.push(...EnvironmentHelper.ExtractLeafNodes(child));
  145. }
  146. } else {
  147. leafNodes.push(info);
  148. }
  149. return leafNodes;
  150. }
  151. /**
  152. * 将资源信息转为JS代码
  153. * @param {any} resourceInfo
  154. */
  155. static ChangeToJsLib(resourceInfo) {
  156. let jscode = "";
  157. for (let r of resourceInfo) {
  158. //分组直接跳过
  159. if (r.nodetype === EnvironmentHelper.ResourceNodeType.Group) {
  160. jscode += EnvironmentHelper.ChangeToJsLib(r.children);
  161. } else {
  162. let lib = r.ChangeToJsLib();
  163. jscode += `let ${r.name} = ${lib.substr(r.name.length + 2)};`;
  164. }
  165. }
  166. return jscode;
  167. }
  168. /**
  169. * 将ID串的代码转为显示值[setValue的时候调用,匹配的ID是初始的,替换后的ID是合理化以后的]
  170. * @param {any} jsCode
  171. * @param {any} leafNodes
  172. */
  173. static ChangeCodeToDisplay(jsCode, leafNodes, _enumSettings) {
  174. if (jsCode == null) {
  175. jsCode = "";
  176. }
  177. //匹配选项内容
  178. let regex = /^\/\/选项内容_.*\n(.*)/gm;
  179. let match;
  180. let index = 0;
  181. while ((match = regex.exec(jsCode)) !== null) {
  182. //改变原Code[+1的原因是去掉\n]
  183. index = index + match[0].length + 1;
  184. let enumSetting = new EnumSetting();
  185. //选项的实际内容
  186. let code = match[1];
  187. let nameString = code.split("=")[0];
  188. enumSetting.name = nameString.substring(4).trim().substring(5);
  189. //除了“let name =”以后的内容
  190. code = code.substring(nameString.length + 1).trim();
  191. //对应的属性
  192. let property = null;
  193. if (code.indexOf(".includes(") > -1) {
  194. //包含与不包含
  195. if (code[0] == "!") {
  196. //是否是非包含
  197. enumSetting.selectedCondition = 3;
  198. code = code.substring(1);
  199. } else {
  200. enumSetting.selectedCondition = 2;
  201. }
  202. let splitResult = code.split(".includes(");
  203. //选中值
  204. enumSetting.items = JSON.parse(splitResult[0]);
  205. property = leafNodes.find(p => p._path_avaliableId == splitResult[1].substring(0, splitResult[1].length - 2));
  206. } else {
  207. let splitResult = [];
  208. //等于或不等于
  209. if (code.indexOf("==") > - 1) {
  210. enumSetting.selectedCondition = 0;
  211. splitResult = code.split("==");
  212. } else if (code.indexOf("!=") > - 1){
  213. enumSetting.selectedCondition = 1;
  214. splitResult = code.split("!=");
  215. } else {
  216. enumSetting.selectedCondition = 0;
  217. splitResult = code.split("//");
  218. splitResult = [splitResult[1], splitResult[0]];
  219. }
  220. property = leafNodes.find(p => p._path_avaliableId == splitResult[0].trim());
  221. //获取items
  222. //JSON.parse会自动去掉引号
  223. var selectedValue = JSON.parse(splitResult[1].trim().substring(0, splitResult[1].trim().length - 1));
  224. enumSetting.items.push(selectedValue);
  225. }
  226. //设置属性ID
  227. enumSetting.propertyId = property.id;
  228. _enumSettings[enumSetting.name] = enumSetting;
  229. }
  230. jsCode = jsCode.substr(index);
  231. for (let node of leafNodes) {
  232. //是否含有对应ID
  233. let index = jsCode.indexOf(node._path_avaliableId);
  234. if (index > -1) {
  235. //判断ID前面是否含有'.'
  236. if (index != 0) {
  237. //若前方是".",则说明匹配不完整,直接跳过
  238. if (jsCode[index - 1] == ".") {
  239. continue;
  240. }
  241. //若前方是一个有效的名称字符,则判断该匹配不完整,直接跳过
  242. if (EnvironmentHelper.IsValidChar(jsCode[index - 1])) {
  243. continue;
  244. }
  245. }
  246. if ((index + node._path_avaliableId.length) < jsCode.length - 1) {
  247. //若后方是一个连续的可用命名字符,则匹配不完整,跳过
  248. if (EnvironmentHelper.IsValidChar(jsCode[index + node._path_avaliableId.length])) {
  249. continue;
  250. }
  251. }
  252. jsCode = jsCode.replaceAll(node._path_avaliableId, node._path);
  253. } else if (node.nodetype == this.ResourceNodeType.Method) {
  254. //兼容以前的组件的中文方法名
  255. if (node._path_avaliableId.lastIndexOf(".") > 0) {
  256. var tempPath = node._path_avaliableId.substring(0, node._path_avaliableId.lastIndexOf(".")) + "." + this.NameFormatting(node.name);
  257. let index = jsCode.indexOf(tempPath);
  258. if (index > -1) {
  259. //判断ID前面是否含有'.'
  260. if (index != 0) {
  261. //若前方是".",则说明匹配不完整,直接跳过
  262. if (jsCode[index - 1] == ".") {
  263. continue;
  264. }
  265. //若前方是一个有效的名称字符,则判断该匹配不完整,直接跳过
  266. if (EnvironmentHelper.IsValidChar(jsCode[index - 1])) {
  267. continue;
  268. }
  269. }
  270. if ((index + tempPath.length) < jsCode.length - 1) {
  271. //若后方是一个连续的可用命名字符,则匹配不完整,跳过
  272. if (EnvironmentHelper.IsValidChar(jsCode[index + tempPath.length])) {
  273. continue;
  274. }
  275. }
  276. jsCode = jsCode.replaceAll(tempPath, node._path);
  277. }
  278. }
  279. }
  280. }
  281. return jsCode;
  282. }
  283. /**
  284. * 将代码的显示值转为ID代码串[GetValue的时候调用,匹配的ID是合理化以后的,返回的ID是初始的]
  285. * @param {any} jsCode
  286. * @param {any} leafNodes
  287. */
  288. static ChangeDisplayToCode(jsCode, leafNodes, _enumSettings) {
  289. if (jsCode == null) {
  290. return "";
  291. }
  292. //最初始的Jscode
  293. let preJscode = jsCode;
  294. for (let node of leafNodes) {
  295. let index = -1;
  296. while (true) {
  297. index += 1;
  298. //jsCode内是否含有该节点
  299. index = jsCode.indexOf(node._path, index);
  300. if (index > -1) {
  301. //判断该path是否处于引号内
  302. if (EnvironmentHelper.IsInsideQuote(jsCode, index)) {
  303. //处于引号内,则忽略
  304. continue;
  305. }
  306. //判断ID前面是否含有'.'
  307. if (index != 0) {
  308. //若前方是".",则说明匹配不完整,直接跳过
  309. if (jsCode[index - 1] == ".") {
  310. continue;
  311. }
  312. //若前方是一个有效的名称字符,则判断该匹配不完整,直接跳过
  313. if (EnvironmentHelper.IsValidChar(jsCode[index - 1])) {
  314. continue;
  315. }
  316. }
  317. //若后方是一个有效的名称字符,则判断该匹配不完整,直接跳过
  318. if (EnvironmentHelper.IsValidChar(jsCode[index + node._path.length])) {
  319. continue;
  320. }
  321. //替换一次
  322. jsCode = EnvironmentHelper.ReplaceString(jsCode, index, node._path, node._path_avaliableId);
  323. //含有则替换对应的Display与Id链
  324. //jsCode = jsCode.replaceAll(node._path, node._path_avaliableId);
  325. } else {
  326. //未找到对应的变量名,直接退出循环
  327. break;
  328. }
  329. }
  330. }
  331. try {
  332. if (_enumSettings != null) {
  333. for (var setting of Object.values(_enumSettings)) {
  334. if (preJscode.indexOf(`选项内容_${setting.name}`) > -1) {
  335. let propertyNode = leafNodes.find(p => p.id == setting.propertyId);
  336. let enumCode = EnvironmentHelper.GetEnumTruethCode(setting, leafNodes, propertyNode);
  337. jsCode = `//选项内容_${setting.name}\n${enumCode}\n${jsCode}`;
  338. }
  339. }
  340. }
  341. } catch (e) {
  342. console.error("ChangeDisplayToCode时,转选项内容的时候报错!");
  343. }
  344. return jsCode;
  345. }
  346. /**
  347. * 在指定的下标后,替换对应的字符串一次
  348. * @param {any} str
  349. * @param {any} index
  350. * @param {any} replacement
  351. * @returns
  352. */
  353. static ReplaceString(str, index, match, replacement) {
  354. const newStr = str.slice(0, index) + str.slice(index).replace(match, replacement);
  355. return newStr;
  356. }
  357. /**
  358. * 判断给定的索引是否在字符串的引号内。
  359. * @param {string} str - 要检查的字符串。
  360. * @param {number} index - 要检查的索引。
  361. * @returns {boolean} - 如果索引在引号内,则为true,否则为false。
  362. * @throws {Error} - 如果索引超出范围。
  363. */
  364. static IsInsideQuote(str, index) {
  365. if (index < 0 || index >= str.length) {
  366. throw new Error('索引超出范围');
  367. }
  368. const before = str.slice(0, index);
  369. let inside = false;
  370. let singleQuoteCount = 0;
  371. let doubleQuoteCount = 0;
  372. for (let i = 0; i < before.length; i++) {
  373. if (before[i] === '"' && before[i - 1] !== '\\') {
  374. doubleQuoteCount++;
  375. } else if (before[i] === "'" && before[i - 1] !== '\\') {
  376. singleQuoteCount++;
  377. }
  378. }
  379. if (singleQuoteCount % 2 !== 0 || doubleQuoteCount % 2 !== 0) {
  380. inside = true;
  381. }
  382. return inside;
  383. }
  384. /**
  385. * 判断一个字符是否是中文、数字、字母和下划线
  386. * @param {any} char
  387. */
  388. static IsValidChar(char) {
  389. if (char == null) {
  390. return false;
  391. }
  392. const reg = /^[\u4e00-\u9fa5_a-zA-Z0-9]+$/;
  393. return reg.test(char);
  394. }
  395. /**
  396. * 名称格式化,主要是要格式化成符合Js变量规则
  397. * @author 黄建华
  398. * @param {string} name
  399. * @returns {string}
  400. */
  401. static NameFormatting(name) {
  402. if (name === null || name === undefined) {
  403. return name;
  404. }
  405. //替换横杠到斜线
  406. let result = name.replace(/[\-@#%^&*()()!!~??<>《》/ ]/g, '_');
  407. if (/[0-9]/.test(name[0])) {
  408. //如果是以数字开头,则添加一个下划线
  409. result = "_" + result;
  410. }
  411. return result;
  412. }
  413. /**
  414. * 启用选项输入插件
  415. * 针对HRS的选项类型的输入
  416. * @param {any} editor
  417. */
  418. static EnableEnumInputPlugin(editor) {
  419. //用于监听双击事件,editor没有默认的双击事件
  420. var lastClick = 0;
  421. editor.onMouseDown(function (e) {
  422. var currentTime = new Date().getTime();
  423. var diffTime = currentTime - lastClick;
  424. if (diffTime < 200) { // 200毫秒内的两次点击被认为是双击
  425. var position = e.target.position;
  426. var model = editor.getModel();
  427. var word = model.getWordAtPosition(position);
  428. if (word) {
  429. if (word.word.startsWith("选项内容_")) {
  430. let name = word.word.substr(5, word.word.length - 5);
  431. if (enumSettings.hasOwnProperty(name)) {
  432. EnvironmentHelper.ShowEnumEditor(enumSettings[name], function (resultSetting) {
  433. enumSettings[resultSetting.name] = resultSetting;
  434. //如果名字改了
  435. if (resultSetting.name != name) {
  436. // 创建一个 Range 对象
  437. let range = editor.getSelection();
  438. // 新内容
  439. let text = "选项内容_" + resultSetting.name;
  440. // 创建一个操作对象
  441. let operation = {
  442. identifier: { major: 1, minor: 1 },
  443. range: range,
  444. text: text,
  445. forceMoveMarkers: true
  446. };
  447. // 执行替换操作
  448. model.pushEditOperations([], [operation], () => null);
  449. let decoration = EnvironmentHelper.lightingRange(
  450. editor,
  451. new monaco.Range(range.startLineNumber, range.startColumn, range.startLineNumber, range.startColumn + text.length),
  452. resultSetting.name
  453. );
  454. enumSettings[resultSetting.name].decoration = decoration;
  455. delete enumSettings[name];
  456. }
  457. });
  458. }
  459. }
  460. }
  461. //避免三击连续触发
  462. lastClick = 0;
  463. } else {
  464. lastClick = currentTime;
  465. }
  466. });
  467. //光标变化事件,当光标处于选项中的时候,不允许直接编辑
  468. editor.onDidChangeCursorPosition(function (e) {
  469. var word = editor.getModel().getWordAtPosition(e.position);
  470. //当光标位于选项内容最后的时候,不只读
  471. if (word && word.word.startsWith("选项内容_")) {
  472. editor.updateOptions({ readOnly: true });
  473. } else {
  474. editor.updateOptions({ readOnly: false });
  475. }
  476. });
  477. //选项内容的整体删除
  478. editor.onKeyDown(function (e) {
  479. if (e.code === 'Backspace' || e.code === 'Delete') {
  480. //当前焦点的位置
  481. var position = editor.getPosition();
  482. var model = editor.getModel();
  483. //获取当前焦点位置对应的词语
  484. var word = model.getWordAtPosition(position);
  485. if (word && word.word.startsWith("选项内容_")) {
  486. //对应的选项名称
  487. let name = word.word.substr(5, word.word.length - 5);
  488. editor.updateOptions({ readOnly: false });
  489. //如果是删除键,则整体删除
  490. var range = new monaco.Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn);
  491. var id = { major: 1, minor: 1 };
  492. var text = '';
  493. var op = { identifier: id, range: range, text: text, forceMoveMarkers: true };
  494. editor.executeEdits("my-source", [op]);
  495. e.preventDefault();
  496. //删除对应的Decoration颜色
  497. editor.getModel().deltaDecorations(enumSettings[name].decoration, []);
  498. //删除对应的选项配置
  499. delete enumSettings[name];
  500. }
  501. }
  502. });
  503. }
  504. /**
  505. * 显示枚举编辑器
  506. * @param {any} setting
  507. * @param {any} callback
  508. */
  509. static ShowEnumEditor(setting, callback) {
  510. var property = allLeafNodes.find(p => p.id == setting.propertyId);
  511. setting.options = property.propertyValue;
  512. setting.selItemMode = property.selItemMode;
  513. setting.showMode = property.showMode;
  514. layer.open({
  515. title: `选项编辑器-${property._path}`,
  516. btn: ["确定", "取消"],
  517. //area: ['400px', '520px'],//宽高
  518. type: 2,
  519. moveOut: true,
  520. content: './subview/enum_editor.html',
  521. success: function (layero, index) {
  522. //获取表达式编辑器窗体对象
  523. let iframeWin = window[layero.find('iframe')[0]['name']];
  524. //初始化编辑器
  525. iframeWin.Init(setting, enumSettings);
  526. this.iframeAuto(layero, index)
  527. },
  528. iframeAuto: function (layero, idnex) {
  529. let iframe_h = layer.getChildFrame("html", idnex).outerHeight()
  530. , n = $("#layui-layer" + idnex)
  531. , title_h = n.find(".layui-layer-title").outerHeight() || 0
  532. , btn_h = n.find(".layui-layer-btn").outerHeight() || 0;
  533. //外层layer高度等于容器高度加上头尾
  534. let layer_h = iframe_h + title_h + btn_h;
  535. let max_h = $(document).height();
  536. //如果高度超过max_h就设置为max_h,容器高度因为有头和尾,需要减去
  537. if (layer_h > max_h) {
  538. iframe_h = `calc(95vh - ${title_h + btn_h}px)`;
  539. layer_h = '95vh'
  540. }
  541. n.css({
  542. height: layer_h
  543. });
  544. n.find("iframe").css({
  545. height: iframe_h
  546. });
  547. this.reset(layero);
  548. },
  549. //重置layer的位置
  550. reset: function (layero) {
  551. var e = {};
  552. e.config = this;
  553. e.layero = layero;
  554. var g = $(window);
  555. var t = e.config,
  556. i = e.layero,
  557. n = [i.outerWidth(), i.outerHeight()],
  558. a = "object" == typeof t.offset;
  559. e.offsetTop = (g.height() - n[1]) / 2,
  560. e.offsetLeft = (g.width() - n[0]) / 2,
  561. 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),
  562. 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()),
  563. "min" === i.data("maxminStatus") && (e.offsetTop = g.height() - (i.find(u[1]).outerHeight() || 0), e.offsetLeft = i.css("left")),
  564. i.css({
  565. top: e.offsetTop,
  566. left: e.offsetLeft
  567. });
  568. },
  569. yes: function (index, layero) {
  570. let iframeWin = window[layero.find('iframe')[0]['name']];
  571. var enumSetting = iframeWin.GetEnumSetting();
  572. if (enumSetting) {
  573. callback(enumSetting);
  574. layer.close(index);
  575. }
  576. }
  577. });
  578. }
  579. /**
  580. * 高亮显示对应range的文本
  581. * @param {any} editor
  582. * @param {any} range
  583. * @returns ownerId
  584. */
  585. static lightingRange(editor, range) {
  586. return [];
  587. let decoration = editor.deltaDecorations(
  588. editor.getModel().getAllDecorations(),
  589. [{
  590. range: range,
  591. options: {
  592. isWholeLine: false,
  593. className: 'enum-content'
  594. }
  595. }]
  596. );
  597. return decoration;
  598. }
  599. /**
  600. * 将对应的选项转为可执行的代码Code
  601. * @param {any} setting enumSetting
  602. */
  603. static GetEnumTruethCode(setting, leafNodes, propertyNode) {
  604. if (!setting) {
  605. throw `选项设置不能为空!`;
  606. }
  607. //待组装的Code
  608. let code = `let 选项内容_${setting.name} = `;
  609. if (propertyNode.showMode === "选项值域选择") {
  610. if (propertyNode.selItemMode === "value") {
  611. code += JSON.stringify(setting.items[0]) + `;//${propertyNode._path_avaliableId}`;
  612. } else {
  613. code += JSON.stringify(setting.name) + `;//${propertyNode._path_avaliableId}`;
  614. }
  615. } else {
  616. switch (setting.selectedCondition) {
  617. case 0:
  618. //等于与不等于只有单选
  619. code += propertyNode._path_avaliableId + " == " + JSON.stringify(setting.items[0]) + ";";
  620. break;
  621. case 1:
  622. code += propertyNode._path_avaliableId + " != " + JSON.stringify(setting.items[0]) + ";";
  623. break;
  624. case 2:
  625. //包含与不包含对于数组而言
  626. code += `${JSON.stringify(setting.items)}.includes(${propertyNode._path_avaliableId});`
  627. break;
  628. case 3:
  629. code += `!${JSON.stringify(setting.items)}.includes(${propertyNode._path_avaliableId});`
  630. break;
  631. }
  632. }
  633. return code;
  634. }
  635. }
  636. /**
  637. * JavaScript的帮助类,主要是解析JavaScript代码,并提取其中的函数、变量、类等
  638. */
  639. class JavaScriptCodeHelper {
  640. /**
  641. * 传入的待解析的代码
  642. */
  643. SourceCode;
  644. /**
  645. * source中所有的注释
  646. */
  647. #Comments = [];
  648. /**
  649. * 当前解析的进度
  650. */
  651. #Index = 0;
  652. constructor(sourceCode) {
  653. this.SourceCode = sourceCode;
  654. if (window.acorn == null) {
  655. //引入acorn
  656. $.ajax({
  657. type: "GET",
  658. url: "./extra/acorn/acorn.min.js",
  659. async: false
  660. });
  661. }
  662. }
  663. /**
  664. * 解析JavaScript代码,并将内部的资源转为结构化的JSON
  665. */
  666. Parse() {
  667. if (this.SourceCode == null) {
  668. return;
  669. }
  670. //待返回的资源JSON
  671. let recource = [];
  672. //将代码转为ast
  673. let ast = window.acorn.parse(this.SourceCode, { ecmaVersion: "latest", onComment: this.#Comments });
  674. //遍历AST树
  675. ast.body.forEach(node => {
  676. if (node.type === "ClassDeclaration") {
  677. //解析Class
  678. let classJson = this.#AnalyzeClassAst(node);
  679. if (classJson != null) {
  680. recource.push(classJson);
  681. }
  682. } else if (node.type === "FunctionDeclaration") {
  683. //解析全局函数
  684. recource.push({
  685. id: node.id.name,
  686. name: node.id.name,
  687. _path: node.id.name,
  688. title: this.#GetNodeSource(node),
  689. iconSkin: "method_icon",
  690. nodetype: EnvironmentHelper.ResourceNodeType.Method
  691. });
  692. } else if (node.type === "VariableDeclaration") {
  693. node.declarations.forEach((declaration) => {
  694. //解析变量
  695. recource.push({
  696. id: declaration.id.name,
  697. name: declaration.id.name,
  698. _path: declaration.id.name,
  699. title: this.#GetNodeSource(declaration),
  700. iconSkin: "property_icon",
  701. nodetype: EnvironmentHelper.ResourceNodeType.Property
  702. });
  703. });
  704. }
  705. });
  706. return recource;
  707. }
  708. /**
  709. * 解析Class的AST树
  710. * @param {any} node class的ast节点
  711. */
  712. #AnalyzeClassAst(node) {
  713. let className = node.id.name;
  714. let classJson = { id: className, name: className, children: [], iconSkin: "class_icon", nodetype: EnvironmentHelper.ResourceNodeType.Class };
  715. for (const method of node.body.body) {
  716. //必须是静态非私有方法
  717. if (method.type === "MethodDefinition" && method.static === true && method.key.type !== "PrivateIdentifier") {
  718. let tempNode = {
  719. id: method.key.name,
  720. name: method.key.name,
  721. title: this.#GetNodeSource(method),
  722. _path: `${className}.${method.key.name}`,
  723. iconSkin: "method_icon",
  724. nodetype: EnvironmentHelper.ResourceNodeType.Method
  725. };
  726. //属性的Get方法是属性
  727. if (method.kind === "get") {
  728. tempNode.iconSkin = "property_icon";
  729. tempNode.nodetype = EnvironmentHelper.ResourceNodeType.Property;
  730. }
  731. classJson.children.push(tempNode);
  732. } else
  733. //必须是静态非私有的属性
  734. if (method.type === "PropertyDefinition" && method.static === true && method.key.type !== "PrivateIdentifier") {
  735. classJson.children.push({
  736. id: method.key.name,
  737. name: method.key.name,
  738. _path: `${className}.${method.key.name}`,
  739. title: this.#GetNodeSource(method),
  740. iconSkin: "property_icon",
  741. nodetype: EnvironmentHelper.ResourceNodeType.Property
  742. });
  743. }
  744. }
  745. //若是没有下级则直接隐藏
  746. if (classJson.children.length === 0) {
  747. return null;
  748. } else {
  749. classJson.children = classJson.children.sort((a, b) => {
  750. return b.nodetype.localeCompare(a.nodetype);
  751. });
  752. return classJson;
  753. }
  754. }
  755. /**
  756. * 获取节点的所有原文内容,包含头部注释,主要用于Title提示
  757. * @param {any} node
  758. */
  759. #GetNodeSource(node) {
  760. //1、判断该node是否存在头部注释
  761. let start = node.start;
  762. let end = node.end;
  763. //2、end去掉函数的实际内容
  764. let content = this.SourceCode.slice(node.start, node.end);
  765. end = start + content.indexOf("{");
  766. //当前节点所在行数
  767. let line = window.acorn.getLineInfo(this.SourceCode, node.start).line;
  768. //获取该节点之前所有的注释
  769. let comment = this.#Comments.filter(p => p.end < node.start);
  770. if (comment.length > 0) {
  771. //注释结束与哪一行
  772. let commentLine = window.acorn.getLineInfo(this.SourceCode, comment[comment.length - 1].end);
  773. //判断该注释是否在该节点的正上方
  774. if (line === commentLine.line + 1) {
  775. start = comment[comment.length - 1].start;
  776. }
  777. }
  778. return this.SourceCode.slice(start, end);
  779. }
  780. }
  781. /**
  782. * 资源父类
  783. */
  784. class BaseResourceInfo {
  785. id;
  786. //JS可用的变量ID,已经过格式化
  787. avaliableId;
  788. name;
  789. nodetype;
  790. title;
  791. children;
  792. //用于ztree的图标
  793. iconSkin;
  794. //用于显示的路径[name的路径]
  795. _path;
  796. //用于实际存储的路径
  797. _path_id;
  798. //转换后的合理化的ID路径
  799. _path_avaliableId;
  800. /**
  801. * 构造函数
  802. * @param {any} info
  803. * @param {any} parent
  804. */
  805. constructor(info, parent) {
  806. if (info.name === null || info.name === undefined) {
  807. throw `${info.id}的name为null或undefined!`;
  808. }
  809. this.name = EnvironmentHelper.NameFormatting(info.name);
  810. //ID为空的时候,以name作为ID
  811. if (info.id != null) {
  812. this.id = info.id;
  813. } else {
  814. this.id = this.name;
  815. }
  816. //ID格式化,主要为了符合JS的变量标准
  817. this.avaliableId = EnvironmentHelper.NameFormatting(this.id);
  818. this.nodetype = info.nodetype;
  819. this.title = info.note;
  820. this._path = parent?._path ?? "";
  821. this._path_id = parent?._path_id ?? "";
  822. this._path_avaliableId = parent?._path_avaliableId ?? "";
  823. if (this.nodetype != EnvironmentHelper.ResourceNodeType.Group) {
  824. if (this._path == "") {
  825. this._path = this.name;
  826. this._path_id = this.id;
  827. this._path_avaliableId = this.avaliableId;
  828. } else {
  829. this._path += `.${this.name}`;
  830. this._path_id += `.${this.id}`;
  831. this._path_avaliableId += `.${this.avaliableId}`;
  832. }
  833. }
  834. if (info.nodes != null && info.nodes.length > 0) {
  835. //下级节点
  836. this.children = BaseResourceInfo.ParseResourceInfo(info.nodes, this);
  837. }
  838. }
  839. /**
  840. * 解析资源信息
  841. * @param {any} resourceInfo
  842. * @param {any} parent
  843. * @returns
  844. */
  845. static ParseResourceInfo(resourceInfo, parent = null) {
  846. let resultResources = [];
  847. for (let i of resourceInfo) {
  848. let info = null;
  849. switch (i.nodetype) {
  850. case EnvironmentHelper.ResourceNodeType.Path:
  851. info = new PathResourceInfo(i, parent);
  852. break;
  853. case EnvironmentHelper.ResourceNodeType.Group:
  854. info = new GroupResourceInfo(i, parent);
  855. break;
  856. case EnvironmentHelper.ResourceNodeType.Property:
  857. info = new PropertyResourceInfo(i, parent);
  858. break;
  859. case EnvironmentHelper.ResourceNodeType.Method:
  860. info = new MethodResourceInfo(i, parent);
  861. break;
  862. }
  863. resultResources.push(info);
  864. }
  865. return resultResources;
  866. }
  867. /**
  868. * 转换为可运行的JS
  869. * */
  870. ChangeSubNodesToJsLib() {
  871. let lib = "";
  872. if (this.children != null) {
  873. for (let i of this.children) {
  874. lib += i.ChangeToJsLib() + ",";
  875. }
  876. }
  877. //去除最后一个逗号
  878. lib = lib.substr(0, lib.length - 1);
  879. return lib;
  880. }
  881. /**
  882. * 根据类型获取虚拟默认值
  883. * @param {any} type
  884. */
  885. GetDefaultValueByType(type) {
  886. switch (type) {
  887. case EnvironmentHelper.PropertyType.Any:
  888. case EnvironmentHelper.PropertyType.String:
  889. return "0";
  890. case EnvironmentHelper.PropertyType.Boolean:
  891. return false;
  892. case EnvironmentHelper.PropertyType.Number:
  893. return 0;
  894. default:
  895. return "0";
  896. }
  897. }
  898. /**
  899. * 获取value的显示字符串
  900. * @param {any} value
  901. * @param {any} type
  902. */
  903. GetValueDisplayString(value, type) {
  904. switch (type) {
  905. case EnvironmentHelper.PropertyType.Boolean:
  906. case EnvironmentHelper.PropertyType.Number:
  907. if (value === null || value === "") {
  908. return "null";
  909. }
  910. return value;
  911. default:
  912. return `"${BaseResourceInfo.EscapedString(value + "")}"`;
  913. }
  914. }
  915. /**
  916. * 转义字符串
  917. * @param {any} str
  918. * @returns
  919. */
  920. static EscapedString(str) {
  921. if (str == null) {
  922. return;
  923. }
  924. str = str.replaceAll("\"", "\\\"");
  925. str = str.replaceAll("'", "\\\'");
  926. return str;
  927. }
  928. /**
  929. * 获取ID链
  930. * */
  931. GetIdChain() {
  932. let id = "";
  933. if (this._parent != null) {
  934. id += this._parent.GetIdChain();
  935. }
  936. //仅当不是Group的时候参与路径计算
  937. if (this.nodetype != EnvironmentHelper.ResourceNodeType.Group) {
  938. if (id.length > 0) {
  939. id += ".";
  940. }
  941. id += this.id;
  942. }
  943. return id;
  944. }
  945. }
  946. /**
  947. * 分组资源
  948. */
  949. class GroupResourceInfo extends BaseResourceInfo {
  950. /**
  951. * 构造函数
  952. * @param {any} info
  953. * @param {any} parent
  954. */
  955. constructor(info, parent) {
  956. super(info, parent);
  957. this.iconSkin = "namespace_icon";
  958. }
  959. ChangeToJsLib() {
  960. return super.ChangeSubNodesToJsLib();
  961. }
  962. }
  963. /**
  964. * 路径资源
  965. */
  966. class PathResourceInfo extends BaseResourceInfo {
  967. constructor(info, parent) {
  968. //拼接路径
  969. super(info, parent);
  970. this.iconSkin = "class_icon";
  971. }
  972. ChangeToJsLib() {
  973. return `${this.name} : {${super.ChangeSubNodesToJsLib()}}`;
  974. }
  975. }
  976. /**
  977. * 属性资源
  978. */
  979. class PropertyResourceInfo extends PathResourceInfo {
  980. propertyType;
  981. propertyValue;
  982. selItemMode; //选择“选项明细”返回值的模式:value、 display
  983. showMode = "表达式编辑";
  984. /**
  985. * 构造函数
  986. * @param {JSON} info json
  987. * @param {BaseResourceInfo} parent 上级节点
  988. */
  989. constructor(info, parent) {
  990. super(info, parent);
  991. if (info.description) {
  992. this.propertyValue = info.description.value;
  993. this.propertyType = info.description.type;
  994. this.selItemMode = info.description.selItemMode;
  995. this.showMode = info.description.showMode;
  996. switch (this.propertyType) {
  997. case EnvironmentHelper.PropertyType.Boolean:
  998. this.iconSkin = "property_bool_icon";
  999. break;
  1000. case EnvironmentHelper.PropertyType.Number:
  1001. this.iconSkin = "property_number_icon";
  1002. break;
  1003. case EnvironmentHelper.PropertyType.Enum:
  1004. this.iconSkin = "property_enum_icon";
  1005. break;
  1006. case EnvironmentHelper.PropertyType.String:
  1007. this.iconSkin = "property_text_icon";
  1008. break;
  1009. default:
  1010. this.iconSkin = "property_icon";
  1011. break;
  1012. }
  1013. //没有传入默认值的情况下,根据类型虚拟一个默认值
  1014. if (this.propertyValue == null) {
  1015. this.propertyValue = this.GetDefaultValueByType(this.propertyType);
  1016. }
  1017. }
  1018. }
  1019. ChangeToJsLib() {
  1020. return `${this.name} : ${this.GetValueDisplayString(this.propertyValue, this.propertyType)}`;
  1021. }
  1022. }
  1023. /**
  1024. * 方法类型的资源管理
  1025. * */
  1026. class MethodResourceInfo extends PathResourceInfo {
  1027. returnValue;
  1028. returnType;
  1029. selItemMode; //选择“选项明细”返回值的模式:value、 display
  1030. showMode = "表达式编辑";
  1031. argsInfo;
  1032. //方法执行的主体,是个委托
  1033. content;
  1034. constructor(info, parent) {
  1035. super(info, parent);
  1036. if (info.description) {
  1037. this.returnValue = info.description.returnValue;
  1038. this.returnType = info.description.returnType;
  1039. this.selItemMode = info.description.selItemMode;
  1040. this.showMode = info.description.showMode;
  1041. if (this.returnValue == null) {
  1042. this.GetDefaultValueByType(this.returnType);
  1043. }
  1044. this.argsInfo = info.description.argsInfo;
  1045. this.content = info.description.content;
  1046. this.iconSkin = "method_icon";
  1047. }
  1048. }
  1049. ChangeToJsLib() {
  1050. let argsString = "";
  1051. let argsComment = "";
  1052. if (this.argsInfo != null) {
  1053. argsString = this.argsInfo.map(p => p["argName"]).join(",");
  1054. for (let arg of this.argsInfo) {
  1055. argsComment += `* @param {${arg.argType}} ${arg.argName} ${arg.argNote};
  1056. `;
  1057. }
  1058. }
  1059. //let value = this.returnValue;
  1060. //if (this.returnType == PropertyType.String) {
  1061. // value = `"${value}"`;
  1062. //}
  1063. let functionContent = "";
  1064. if (this.content != null) {
  1065. functionContent = this.content.toLocaleString();
  1066. if (functionContent.startsWith("function")) {
  1067. functionContent = functionContent.substr(8, functionContent.length - 8);
  1068. }
  1069. }
  1070. return `
  1071. /**
  1072. * ${this.title}
  1073. ${argsComment}*/
  1074. ${this.name}${functionContent}`;
  1075. }
  1076. }
  1077. class EnumSetting {
  1078. //选项配置的名称
  1079. name;
  1080. //对比方式[0:"==",1:"!=",2:"includes",3:"!includes"]
  1081. selectedCondition;
  1082. //对应的属性的id
  1083. propertyId;
  1084. //选项
  1085. items = [];
  1086. //编辑器的装饰颜色
  1087. decoration;
  1088. //选项的值取"value"还是"display"
  1089. selItemMode = "display";
  1090. //选项值域显示界面的显示模式:表达式编辑、选项值域选择
  1091. showMode = "表达式编辑";
  1092. }