path_tree.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636
  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 () {
  30. const self = this;
  31. const addSortNodes = function (nodes) {
  32. for (let i = 0; i < nodes.length; i++) {
  33. self.nodes.push(nodes[i]);
  34. nodes[i].children = self.getChildren(nodes[i]);
  35. addSortNodes(nodes[i].children);
  36. }
  37. };
  38. self.nodes = [];
  39. addSortNodes(this.getChildren(null));
  40. }
  41. /**
  42. * 加载数据(初始化), 并给数据添加部分树结构必须数据
  43. * @param datas
  44. */
  45. loadDatas (datas) {
  46. // 清空旧数据
  47. this.items = {};
  48. this.nodes = [];
  49. // 加载全部数据
  50. for (const data of datas) {
  51. const keyName = itemsPre + data[this.setting.id];
  52. this.items[keyName] = JSON.parse(JSON.stringify(data));
  53. this.datas.push(this.items[keyName]);
  54. }
  55. this.sortTreeNode();
  56. for (const node of this.nodes) {
  57. node.expanded = node.children.length > 0;
  58. node.visible = true;
  59. }
  60. }
  61. /**
  62. * 根据id获取树结构节点数据
  63. * @param {Number} id
  64. * @returns {Object}
  65. */
  66. getItems (id) {
  67. return this.items[itemsPre + id];
  68. };
  69. /**
  70. * 查找node的parent
  71. * @param {Object} node
  72. * @returns {Object}
  73. */
  74. getParent (node) {
  75. return this.getItems(node[this.setting.pid]);
  76. };
  77. /**
  78. * 根据path查找完整节点
  79. * @param {Number} path
  80. */
  81. getFullPathNodes (path) {
  82. const self = this, ids = path.split('.');
  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 = node.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 = node.children;
  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. if (node[this.setting.pid] !== this.setting.rootId) {
  215. const parent = this.items[itemsPre + node[this.setting.pid]];
  216. removeArrayData(parent.children, node);
  217. }
  218. removeArrayData(this.datas, node);
  219. removeArrayData(this.nodes, node);
  220. }
  221. }
  222. };
  223. loadLeafData (data) {
  224. const datas = data instanceof Array ? data : [data];
  225. for (const d of datas) {
  226. let node = this.getItems(d[this.setting.id]);
  227. if (node && node.is_leaf) {
  228. for (const prop in node) {
  229. if (data[prop] !== undefined) {
  230. node[prop] = d[prop];
  231. }
  232. }
  233. }
  234. }
  235. };
  236. }
  237. class ActiveTree extends BaseTree {
  238. /**
  239. * 加载数据(动态),只加载不同部分
  240. * @param {Array} datas
  241. * @return {Array} 加载到树的数据
  242. * @privateA
  243. */
  244. _loadData (datas) {
  245. const loadedData = [];
  246. for (const data of datas) {
  247. let node = this.getItems(data[this.setting.id]);
  248. if (node) {
  249. for (const prop in node) {
  250. if (data[prop] !== undefined) {
  251. node[prop] = data[prop];
  252. }
  253. }
  254. loadedData.push(node);
  255. } else {
  256. const keyName = itemsPre + data[this.setting.id];
  257. const node = JSON.parse(JSON.stringify(data));
  258. this.items[keyName] = node;
  259. this.datas.push(node);
  260. node.expanded = false;
  261. node.visible = true;
  262. loadedData.push(node);
  263. }
  264. }
  265. this.sortTreeNode();
  266. for (const node of loadedData) {
  267. const children = node.children;
  268. if (!node.expanded && children.length > 0) {
  269. node.expanded = true;
  270. this._refreshChildrenVisible(node);
  271. }
  272. }
  273. return loadedData;
  274. };
  275. /**
  276. * 以下方法需等待响应, 通过callback刷新界面
  277. */
  278. /**
  279. * 加载子节点
  280. * @param {Object} node
  281. * @param {function} callback
  282. */
  283. loadChildren (node, callback) {
  284. const self = this;
  285. const url = this.setting.preUrl ? this.setting.preUrl + '/get-children' : 'get-children';
  286. console.log(url);
  287. postData(url, this.getNodeKeyData(node), function (data) {
  288. self._loadData(data);
  289. callback();
  290. });
  291. };
  292. }
  293. class LedgerTree extends BaseTree {
  294. /**
  295. * 加载数据(动态),只加载不同部分
  296. * @param {Array} datas
  297. * @return {Array} 加载到树的数据
  298. * @privateA
  299. */
  300. _updateData (datas) {
  301. const loadedData = [];
  302. for (const data of datas) {
  303. let node = this.getItems(data[this.setting.id]);
  304. if (node) {
  305. for (const prop in node) {
  306. if (data[prop] !== undefined) {
  307. node[prop] = data[prop];
  308. }
  309. }
  310. loadedData.push(node);
  311. }
  312. }
  313. for (const node of loadedData) {
  314. const children = this.getChildren(node);
  315. node.expanded = children.length > 0 && children[0].visible;
  316. }
  317. this.sortTreeNode();
  318. return loadedData;
  319. };
  320. /**
  321. * 加载数据(动态),只加载不同部分
  322. * @param {Array} datas
  323. * @return {Array} 加载到树的数据
  324. * @privateA
  325. */
  326. _loadData (datas) {
  327. const loadedData = [];
  328. for (const data of datas) {
  329. let node = this.getItems(data[this.setting.id]);
  330. if (node) {
  331. for (const prop in node) {
  332. if (data[prop] !== undefined) {
  333. node[prop] = data[prop];
  334. }
  335. }
  336. loadedData.push(node);
  337. } else {
  338. const keyName = itemsPre + data[this.setting.id];
  339. const node = JSON.parse(JSON.stringify(data));
  340. this.items[keyName] = node;
  341. this.datas.push(node);
  342. node.expanded = false;
  343. node.visible = true;
  344. loadedData.push(node);
  345. }
  346. }
  347. this.sortTreeNode();
  348. for (const node of loadedData) {
  349. const children = node.children;
  350. if (!node.expanded && children.length > 0) {
  351. node.expanded = true;
  352. this._refreshChildrenVisible(node);
  353. }
  354. }
  355. return loadedData;
  356. };
  357. /**
  358. * 清理数据(动态)
  359. * @param datas
  360. * @private
  361. */
  362. _freeData (datas) {
  363. const removeArrayData = function (array, data) {
  364. const index = array.indexOf(data);
  365. array.splice(index, 1);
  366. };
  367. for (const data of datas) {
  368. const node = this.getItems(data[this.setting.id]);
  369. if (node) {
  370. delete this.items[itemsPre + node[this.setting.id]];
  371. if (node[this.setting.pid] !== this.setting.rootId) {
  372. const parent = this.getItems(node[this.setting.pid]);
  373. removeArrayData(parent.children, node);
  374. }
  375. removeArrayData(this.datas, node);
  376. removeArrayData(this.nodes, node);
  377. }
  378. }
  379. };
  380. /**
  381. * 加载需展开的数据
  382. * @param {Array} datas
  383. * @returns {Array}
  384. * @private
  385. */
  386. _loadExpandData (datas) {
  387. const loadedData = [], existData = [], expandData = [];
  388. for (const data of datas) {
  389. let node = this.getItems(data[this.setting.id]);
  390. if (node) {
  391. existData.push(node);
  392. } else {
  393. const keyName = itemsPre + data[this.setting.id];
  394. const node = JSON.parse(JSON.stringify(data));
  395. this.items[keyName] = node;
  396. this.datas.push(node);
  397. node.expanded = false;
  398. node.visible = true;
  399. loadedData.push(node);
  400. }
  401. }
  402. this.sortTreeNode();
  403. for (const node of loadedData) {
  404. const children = node.children();
  405. if (!node.expanded && children.length > 0) {
  406. node.expaned = true;
  407. this._refreshChildrenVisible(node);
  408. }
  409. }
  410. for (const node of existData) {
  411. const children = node.children();
  412. if (!node.expanded && children.length > 0) {
  413. node.expanded = children.length > 0;
  414. this._refreshChildrenVisible(node);
  415. expandData.push(node);
  416. }
  417. }
  418. return [loadedData, expandData];
  419. };
  420. /**
  421. * 以下方法需等待响应, 通过callback刷新界面
  422. */
  423. /**
  424. * 加载子节点
  425. * @param {Object} node
  426. * @param {function} callback
  427. */
  428. loadChildren (node, callback) {
  429. const self = this;
  430. const url = this.setting.preUrl ? this.setting.preUrl + '/get-children' : 'get-children';
  431. console.log(url);
  432. postData(url, this.getNodeKeyData(node), function (data) {
  433. self._loadData(data);
  434. callback();
  435. });
  436. };
  437. /**
  438. * 树结构基本操作
  439. * @param {String} url - 请求地址
  440. * @param {Object} node - 操作节点
  441. * @param {String} type - 操作类型
  442. * @param {function} callback - 界面刷新
  443. */
  444. baseOperation (url, node, type, callback) {
  445. const self = this;
  446. const data = {
  447. id: node[this.setting.id],
  448. postType: type
  449. };
  450. postData(url, data, function (datas) {
  451. const result = {};
  452. if (datas.update) {
  453. result.update = self._updateData(datas.update);
  454. }
  455. if (datas.create) {
  456. result.create = self._loadData(datas.create);
  457. }
  458. if (datas.delete) {
  459. result.delete = self._freeData(datas.delete);
  460. }
  461. callback(result);
  462. });
  463. };
  464. /**
  465. * 节点数据编辑
  466. * @param {String} url - 请求地址
  467. * @param {Array|Object} updateData - 需更新的数据
  468. * @param {function} callback - 界面刷新
  469. */
  470. update (url, updateData, callback) {
  471. const self = this;
  472. postData(url, updateData, function (datas) {
  473. const result = self._updateData(datas);
  474. callback(result);
  475. }, function () {
  476. if (updateData instanceof Array) {
  477. const result = [];
  478. for (const data of updateData) {
  479. result.push(self.getItems(data[self.setting.id]));
  480. }
  481. callback(result)
  482. } else {
  483. callback([self.getItems(updateData[self.setting.id])]);
  484. }
  485. });
  486. };
  487. /**
  488. * 复制粘贴整块(目前仅可粘贴为后项)
  489. * @param {String} url - 请求地址
  490. * @param {Object} node - 操作节点
  491. * @param {Array} block - 被复制整块的节点列表
  492. * @param {function} callback - 界面刷新
  493. */
  494. pasteBlock (url, node, block, callback) {
  495. const self = this;
  496. const data = {
  497. id: node[self.setting.id],
  498. block: block
  499. };
  500. postData(url, data, function (datas) {
  501. const result = {};
  502. if (datas.update) {
  503. result.update = self._updateData(datas.update);
  504. }
  505. if (datas.create) {
  506. result.create = self._loadData(datas.create);
  507. }
  508. if (datas.delete) {
  509. result.delete = self._freeData(datas.delete);
  510. }
  511. callback(result);
  512. });
  513. };
  514. /**
  515. * 提交数据
  516. * @param {String} url - 请求地址
  517. * @param {Object} node - 当前选中节点
  518. * @param {Object} data - 提交的数据
  519. * @param {function} callback - 界面刷新
  520. */
  521. postData (url, node, data, callback) {
  522. const self = this;
  523. data.id = node[self.setting.id];
  524. postData(url, data, function (datas) {
  525. const result = {};
  526. if (datas.update) {
  527. result.update = self._updateData(datas.update);
  528. }
  529. if (datas.create) {
  530. result.create = self._loadData(datas.create);
  531. }
  532. if (datas.delete) {
  533. result.delete = self._freeData(datas.delete);
  534. }
  535. if (datas.expand) {
  536. const [create, update] = self._loadExpandData(datas.expand);
  537. result.create = result.create.concat(create);
  538. result.expand = update;
  539. }
  540. callback(result);
  541. });
  542. };
  543. batchInsert (url, node, data, callback) {
  544. const self = this;
  545. data.id = node[self.setting.id];
  546. postData(url, data, function (datas) {
  547. const result = {};
  548. if (datas.update) {
  549. result.update = self._updateData(datas.update);
  550. }
  551. if (datas.create) {
  552. result.create = self._loadData(datas.create);
  553. }
  554. if (datas.delete) {
  555. result.delete = self._freeData(datas.delete);
  556. }
  557. callback(result);
  558. });
  559. };
  560. }
  561. if (type === 'base') {
  562. return new BaseTree(setting);
  563. } else if (type === 'active') {
  564. return new ActiveTree(setting);
  565. } else if (type === 'ledger') {
  566. return new LedgerTree(setting);
  567. } else if (type === 'measure') {
  568. return new MeasureTree(setting);
  569. }
  570. };
  571. const treeCalc = {
  572. getMaxLevel: function (tree) {
  573. return Math.max.apply(Math, tree.datas.map(function(o) {return o.level}));
  574. },
  575. calculateNode: function (tree, node, calcFields) {
  576. const children = tree.getChildren(node);
  577. if (children.length > 0) {
  578. const gather = children.reduce(function (rst, x) {
  579. const result = {};
  580. const fieldCalc = function (field) {
  581. if (rst[field]) {
  582. result[field] = x[field] ? rst[field] + x[field] : rst[field];
  583. } else {
  584. result[field] = x[field] ? x[field] : undefined;
  585. }
  586. }
  587. for (const cf of calcFields) {
  588. fieldCalc(cf);
  589. }
  590. return result;
  591. });
  592. for (const cf of calcFields) {
  593. if (gather[cf]) {
  594. node[cf] = gather[cf];
  595. }
  596. }
  597. }
  598. },
  599. calculateLevelNode: function (tree, level, calcFields) {
  600. const nodes = tree.datas.filter((n) => { return n.level === level });
  601. for (const node of nodes) {
  602. this.calculateNode(tree, node, calcFields);
  603. }
  604. },
  605. calculateAll: function (tree, calcFields) {
  606. for (let i = this.getMaxLevel(tree); i >= 0; i--) {
  607. this.calculateLevelNode(tree, i, calcFields);
  608. }
  609. },
  610. calculateParent: function (tree, node, calcFields) {
  611. const nodes = tree.getFullPathNodes(node.full_path);
  612. nodes.sort((a, b) => {
  613. return b.level - a.level;
  614. });
  615. for (const n of nodes) {
  616. this.calculateNode(tree, n, calcFields);
  617. }
  618. return nodes;
  619. }
  620. }