path_tree.js 23 KB

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