path_tree.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  1. /**
  2. * 构建pathTree
  3. * 可动态加载子节点,要求子节点获取接口按/xxx/get-children定义
  4. * @param {Object} setting - 设置
  5. * @returns {PathTree}
  6. */
  7. 'use strict';
  8. const itemsPre = 'id_';
  9. const createNewPathTree = function (type, setting) {
  10. class BaseTree {
  11. /**
  12. * 构造函数
  13. */
  14. constructor (setting) {
  15. // 无索引
  16. this.datas = [];
  17. // 以key为索引
  18. this.items = {};
  19. // 以排序为索引
  20. this.nodes = [];
  21. // 树设置
  22. this.setting = JSON.parse(JSON.stringify(setting));
  23. }
  24. /**
  25. * 树结构根据显示排序
  26. */
  27. sortTreeNode () {
  28. const self = this;
  29. const addSortNodes = function (nodes) {
  30. for (let i = 0; i < nodes.length; i++) {
  31. self.nodes.push(nodes[i]);
  32. addSortNodes(self.getChildren(nodes[i]));
  33. }
  34. };
  35. self.nodes = [];
  36. addSortNodes(this.getChildren(null));
  37. }
  38. /**
  39. * 加载数据(初始化), 并给数据添加部分树结构必须数据
  40. * @param datas
  41. */
  42. loadDatas (datas) {
  43. // 清空旧数据
  44. this.items = {};
  45. this.nodes = [];
  46. // 加载全部数据
  47. for (const data of datas) {
  48. const keyName = itemsPre + data[this.setting.id];
  49. this.items[keyName] = JSON.parse(JSON.stringify(data));
  50. this.datas.push(this.items[keyName]);
  51. }
  52. this.sortTreeNode();
  53. for (const node of this.nodes) {
  54. const children = this.getChildren(node);
  55. node.expanded = children.length > 0;
  56. node.visible = true;
  57. }
  58. }
  59. /**
  60. * 根据id获取树结构节点数据
  61. * @param {Number} id
  62. * @returns {Object}
  63. */
  64. getItems (id) {
  65. return this.items[itemsPre + id];
  66. };
  67. /**
  68. * 查找node的parent
  69. * @param {Object} node
  70. * @returns {Object}
  71. */
  72. getParent (node) {
  73. return this.getItems(node[this.setting.pid]);
  74. };
  75. /**
  76. * 根据path查找完整节点
  77. * @param {Number} path
  78. */
  79. getFullPathNodes (path) {
  80. const self = this, ids = path.split('.');
  81. console.log(path);
  82. console.log(ids);
  83. if (ids.length > 0) {
  84. return this.nodes.filter((x) => {
  85. return ids.indexOf('' + x[self.setting.id]) >= 0;
  86. });
  87. } else {
  88. return [];
  89. }
  90. };
  91. /**
  92. * 查询node的已下载子节点
  93. * @param {Object} node
  94. * @returns {Array}
  95. */
  96. getChildren (node) {
  97. const setting = this.setting;
  98. const pid = node ? node[setting.id] : setting.rootId;
  99. const children = this.datas.filter(function (x) {
  100. return x[setting.pid] === pid;
  101. });
  102. children.sort(function (a, b) {
  103. return a.order - b.order;
  104. });
  105. return children;
  106. };
  107. /**
  108. * 查询node的已下载的全部后代
  109. * @param {Object} node
  110. * @returns {Array}
  111. */
  112. getPosterity (node) {
  113. const reg = new RegExp('^' + node.full_path + '.');
  114. return this.datas.filter(function (x) {
  115. return reg.test(x.full_path);
  116. })
  117. };
  118. /**
  119. * 查询node是否是父节点的最后一个子节点
  120. * @param {Object} node
  121. * @returns {boolean}
  122. */
  123. isLastSibling (node) {
  124. const siblings = this.getChildren(this.getParent(node));
  125. return node.order === siblings[siblings.length - 1].order;
  126. };
  127. /**
  128. * 刷新子节点是否可见
  129. * @param {Object} node
  130. * @private
  131. */
  132. _refreshChildrenVisible (node) {
  133. const children = this.getChildren(node);
  134. for (const child of children) {
  135. child.visible = node.expanded && node.visible;
  136. this._refreshChildrenVisible(child);
  137. }
  138. };
  139. /**
  140. * 设置节点是否展开, 并控制子节点可见
  141. * @param {Object} node
  142. * @param {Boolean} expanded
  143. */
  144. setExpanded (node, expanded) {
  145. node.expanded = expanded;
  146. this._refreshChildrenVisible(node);
  147. };
  148. /**
  149. * 提取节点key和索引数据
  150. * @param {Object} node - 节点
  151. * @returns {key}
  152. */
  153. getNodeKeyData (node) {
  154. const data = {};
  155. for (const key of this.setting.keys) {
  156. data[key] = node[key];
  157. }
  158. return data;
  159. };
  160. /**
  161. * 得到树结构构成id
  162. * @param node
  163. * @returns {*}
  164. */
  165. getNodeKey (node) {
  166. return node[this.setting.id];
  167. };
  168. }
  169. class MeasureTree extends BaseTree {
  170. addData (datas) {
  171. const loadedData = [];
  172. for (const data of datas) {
  173. let node = this.getItems(data[this.setting.id]);
  174. if (node) {
  175. for (const prop in node) {
  176. if (data[prop] !== undefined) {
  177. node[prop] = data[prop];
  178. }
  179. }
  180. loadedData.push(node);
  181. } else {
  182. const keyName = itemsPre + data[this.setting.id];
  183. const node = JSON.parse(JSON.stringify(data));
  184. this.items[keyName] = node;
  185. this.datas.push(node);
  186. node.expanded = false;
  187. node.visible = true;
  188. loadedData.push(node);
  189. }
  190. }
  191. this.sortTreeNode();
  192. for (const node of loadedData) {
  193. const children = this.getChildren(node);
  194. if (!node.expanded && children.length > 0) {
  195. node.expanded = true;
  196. this._refreshChildrenVisible(node);
  197. }
  198. }
  199. return loadedData;
  200. }
  201. removeData (datas) {
  202. datas.sort(function (a, b) {
  203. return b.level - a.level;
  204. });
  205. console.log(datas);
  206. const removeArrayData = function (array, data) {
  207. const index = array.indexOf(data);
  208. array.splice(index, 1);
  209. };
  210. for (const data of datas) {
  211. const node = this.getItems(data[this.setting.id]);
  212. if (node && this.getChildren(node).length === 0) {
  213. delete this.items[itemsPre + node[this.setting.id]];
  214. removeArrayData(this.datas, node);
  215. removeArrayData(this.nodes, node);
  216. }
  217. }
  218. };
  219. loadLeafData (data) {
  220. const datas = data instanceof Array ? data : [data];
  221. for (const d of datas) {
  222. let node = this.getItems(d[this.setting.id]);
  223. if (node && node.is_leaf) {
  224. for (const prop in node) {
  225. if (data[prop] !== undefined) {
  226. node[prop] = d[prop];
  227. }
  228. }
  229. }
  230. }
  231. };
  232. }
  233. class ActiveTree extends BaseTree {
  234. /**
  235. * 加载数据(动态),只加载不同部分
  236. * @param {Array} datas
  237. * @return {Array} 加载到树的数据
  238. * @privateA
  239. */
  240. _loadData (datas) {
  241. const loadedData = [];
  242. for (const data of datas) {
  243. let node = this.getItems(data[this.setting.id]);
  244. if (node) {
  245. for (const prop in node) {
  246. if (data[prop] !== undefined) {
  247. node[prop] = data[prop];
  248. }
  249. }
  250. loadedData.push(node);
  251. } else {
  252. const keyName = itemsPre + data[this.setting.id];
  253. const node = JSON.parse(JSON.stringify(data));
  254. this.items[keyName] = node;
  255. this.datas.push(node);
  256. node.expanded = false;
  257. node.visible = true;
  258. loadedData.push(node);
  259. }
  260. }
  261. this.sortTreeNode();
  262. for (const node of loadedData) {
  263. const children = this.getChildren(node);
  264. if (!node.expanded && children.length > 0) {
  265. node.expanded = true;
  266. this._refreshChildrenVisible(node);
  267. }
  268. }
  269. return loadedData;
  270. };
  271. /**
  272. * 以下方法需等待响应, 通过callback刷新界面
  273. */
  274. /**
  275. * 加载子节点
  276. * @param {Object} node
  277. * @param {function} callback
  278. */
  279. loadChildren (node, callback) {
  280. const self = this;
  281. const url = this.setting.preUrl ? this.setting.preUrl + '/get-children' : 'get-children';
  282. console.log(url);
  283. postData(url, this.getNodeKeyData(node), function (data) {
  284. self._loadData(data);
  285. callback();
  286. });
  287. };
  288. }
  289. class LedgerTree extends BaseTree {
  290. /**
  291. * 加载数据(动态),只加载不同部分
  292. * @param {Array} datas
  293. * @return {Array} 加载到树的数据
  294. * @privateA
  295. */
  296. _updateData (datas) {
  297. const loadedData = [];
  298. for (const data of datas) {
  299. let node = this.getItems(data[this.setting.id]);
  300. if (node) {
  301. for (const prop in node) {
  302. if (data[prop] !== undefined) {
  303. node[prop] = data[prop];
  304. }
  305. }
  306. loadedData.push(node);
  307. }
  308. }
  309. for (const node of loadedData) {
  310. const children = this.getChildren(node);
  311. node.expanded = children.length > 0 && children[0].visible;
  312. }
  313. this.sortTreeNode();
  314. return loadedData;
  315. };
  316. /**
  317. * 加载数据(动态),只加载不同部分
  318. * @param {Array} datas
  319. * @return {Array} 加载到树的数据
  320. * @privateA
  321. */
  322. _loadData (datas) {
  323. const loadedData = [];
  324. for (const data of datas) {
  325. let node = this.getItems(data[this.setting.id]);
  326. if (node) {
  327. for (const prop in node) {
  328. if (data[prop] !== undefined) {
  329. node[prop] = data[prop];
  330. }
  331. }
  332. loadedData.push(node);
  333. } else {
  334. const keyName = itemsPre + data[this.setting.id];
  335. const node = JSON.parse(JSON.stringify(data));
  336. this.items[keyName] = node;
  337. this.datas.push(node);
  338. node.expanded = false;
  339. node.visible = true;
  340. loadedData.push(node);
  341. }
  342. }
  343. this.sortTreeNode();
  344. for (const node of loadedData) {
  345. const children = this.getChildren(node);
  346. if (!node.expanded && children.length > 0) {
  347. node.expanded = true;
  348. this._refreshChildrenVisible(node);
  349. }
  350. }
  351. return loadedData;
  352. };
  353. /**
  354. * 清理数据(动态)
  355. * @param datas
  356. * @private
  357. */
  358. _freeData (datas) {
  359. const removeArrayData = function (array, data) {
  360. const index = array.indexOf(data);
  361. array.splice(index, 1);
  362. };
  363. for (const data of datas) {
  364. const node = this.getItems(data[this.setting.id]);
  365. if (node) {
  366. delete this.items[itemsPre + node[this.setting.id]];
  367. removeArrayData(this.datas, node);
  368. removeArrayData(this.nodes, node);
  369. }
  370. }
  371. };
  372. /**
  373. * 加载需展开的数据
  374. * @param {Array} datas
  375. * @returns {Array}
  376. * @private
  377. */
  378. _loadExpandData (datas) {
  379. const loadedData = [], existData = [], expandData = [];
  380. for (const data of datas) {
  381. let node = this.getItems(data[this.setting.id]);
  382. if (node) {
  383. existData.push(node);
  384. } else {
  385. const keyName = itemsPre + data[this.setting.id];
  386. const node = JSON.parse(JSON.stringify(data));
  387. this.items[keyName] = node;
  388. this.datas.push(node);
  389. node.expanded = false;
  390. node.visible = true;
  391. loadedData.push(node);
  392. }
  393. }
  394. this.sortTreeNode();
  395. for (const node of loadedData) {
  396. const children = this.getChildren(node);
  397. if (!node.expanded && children.length > 0) {
  398. node.expaned = true;
  399. this._refreshChildrenVisible(node);
  400. }
  401. }
  402. for (const node of existData) {
  403. const children = this.getChildren(node);
  404. if (!node.expanded && children.length > 0) {
  405. node.expanded = children.length > 0;
  406. this._refreshChildrenVisible(node);
  407. expandData.push(node);
  408. }
  409. }
  410. return [loadedData, expandData];
  411. };
  412. /**
  413. * 以下方法需等待响应, 通过callback刷新界面
  414. */
  415. /**
  416. * 加载子节点
  417. * @param {Object} node
  418. * @param {function} callback
  419. */
  420. loadChildren (node, callback) {
  421. const self = this;
  422. const url = this.setting.preUrl ? this.setting.preUrl + '/get-children' : 'get-children';
  423. console.log(url);
  424. postData(url, this.getNodeKeyData(node), function (data) {
  425. self._loadData(data);
  426. callback();
  427. });
  428. };
  429. /**
  430. * 树结构基本操作
  431. * @param {String} url - 请求地址
  432. * @param {Object} node - 操作节点
  433. * @param {String} type - 操作类型
  434. * @param {function} callback - 界面刷新
  435. */
  436. baseOperation (url, node, type, callback) {
  437. const self = this;
  438. const data = {
  439. id: node[this.setting.id],
  440. postType: type
  441. };
  442. postData(url, data, function (datas) {
  443. const result = {};
  444. if (datas.update) {
  445. result.update = self._updateData(datas.update);
  446. }
  447. if (datas.create) {
  448. result.create = self._loadData(datas.create);
  449. }
  450. if (datas.delete) {
  451. result.delete = self._freeData(datas.delete);
  452. }
  453. callback(result);
  454. });
  455. };
  456. /**
  457. * 节点数据编辑
  458. * @param {String} url - 请求地址
  459. * @param {Array|Object} updateData - 需更新的数据
  460. * @param {function} callback - 界面刷新
  461. */
  462. update (url, updateData, callback) {
  463. const self = this;
  464. postData(url, updateData, function (datas) {
  465. const result = self._updateData(datas);
  466. callback(result);
  467. }, function () {
  468. if (updateData instanceof Array) {
  469. const result = [];
  470. for (const data of updateData) {
  471. result.push(self.getItems(data[self.setting.id]));
  472. }
  473. callback(result)
  474. } else {
  475. callback([self.getItems(updateData[self.setting.id])]);
  476. }
  477. });
  478. };
  479. /**
  480. * 复制粘贴整块(目前仅可粘贴为后项)
  481. * @param {String} url - 请求地址
  482. * @param {Object} node - 操作节点
  483. * @param {Array} block - 被复制整块的节点列表
  484. * @param {function} callback - 界面刷新
  485. */
  486. pasteBlock (url, node, block, callback) {
  487. const self = this;
  488. const data = {
  489. id: node[self.setting.id],
  490. block: block
  491. };
  492. postData(url, data, function (datas) {
  493. const result = {};
  494. if (datas.update) {
  495. result.update = self._updateData(datas.update);
  496. }
  497. if (datas.create) {
  498. result.create = self._loadData(datas.create);
  499. }
  500. if (datas.delete) {
  501. result.delete = self._freeData(datas.delete);
  502. }
  503. callback(result);
  504. });
  505. };
  506. /**
  507. * 提交数据
  508. * @param {String} url - 请求地址
  509. * @param {Object} node - 当前选中节点
  510. * @param {Object} data - 提交的数据
  511. * @param {function} callback - 界面刷新
  512. */
  513. postData (url, node, data, callback) {
  514. const self = this;
  515. data.id = node[self.setting.id];
  516. postData(url, data, function (datas) {
  517. const result = {};
  518. if (datas.update) {
  519. result.update = self._updateData(datas.update);
  520. }
  521. if (datas.create) {
  522. result.create = self._loadData(datas.create);
  523. }
  524. if (datas.delete) {
  525. result.delete = self._freeData(datas.delete);
  526. }
  527. if (datas.expand) {
  528. const [create, update] = self._loadExpandData(datas.expand);
  529. result.create = result.create.concat(create);
  530. result.expand = update;
  531. }
  532. callback(result);
  533. });
  534. };
  535. batchInsert (url, node, data, callback) {
  536. const self = this;
  537. data.id = node[self.setting.id];
  538. postData(url, data, function (datas) {
  539. const result = {};
  540. if (datas.update) {
  541. result.update = self._updateData(datas.update);
  542. }
  543. if (datas.create) {
  544. result.create = self._loadData(datas.create);
  545. }
  546. if (datas.delete) {
  547. result.delete = self._freeData(datas.delete);
  548. }
  549. callback(result);
  550. });
  551. };
  552. }
  553. if (type === 'base') {
  554. return new BaseTree(setting);
  555. } else if (type === 'active') {
  556. return new ActiveTree(setting);
  557. } else if (type === 'ledger') {
  558. return new LedgerTree(setting);
  559. } else if (type === 'measure') {
  560. return new MeasureTree(setting);
  561. }
  562. };
  563. const treeCalc = {
  564. getMaxLevel: function (tree) {
  565. return Math.max.apply(Math, tree.datas.map(function(o) {return o.level}));
  566. },
  567. calculateNode: function (tree, node, calcFields) {
  568. const children = tree.getChildren(node);
  569. if (children.length > 0) {
  570. const gather = children.reduce(function (rst, x) {
  571. const result = {};
  572. const fieldCalc = function (field) {
  573. if (rst[field]) {
  574. result[field] = x[field] ? rst[field] + x[field] : rst[field];
  575. } else {
  576. result[field] = x[field] ? x[field] : undefined;
  577. }
  578. }
  579. for (const cf of calcFields) {
  580. fieldCalc(cf);
  581. }
  582. return result;
  583. });
  584. for (const cf of calcFields) {
  585. if (gather[cf]) {
  586. node[cf] = gather[cf];
  587. }
  588. }
  589. }
  590. },
  591. calculateLevelNode: function (tree, level, calcFields) {
  592. const nodes = tree.datas.filter((n) => { return n.level === level });
  593. for (const node of nodes) {
  594. this.calculateNode(tree, node, calcFields);
  595. }
  596. },
  597. calculateAll: function (tree, calcFields) {
  598. for (let i = this.getMaxLevel(tree); i >= 0; i--) {
  599. this.calculateLevelNode(tree, i, calcFields);
  600. }
  601. },
  602. calculateParent: function (tree, node, calcFields) {
  603. const nodes = tree.getFullPathNodes(node.full_path);
  604. nodes.sort((a, b) => {
  605. return b.level - a.level;
  606. });
  607. for (const n of nodes) {
  608. this.calculateNode(tree, n, calcFields);
  609. }
  610. return nodes;
  611. }
  612. }