tree_table.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  1. /*
  2. * Created by MaiXinRong on 2017/2/9.
  3. * 项目管理 专用树结构
  4. */
  5. (function($) {
  6. var _setting = {
  7. tree: {
  8. id: 'id',
  9. pid: 'parentId',
  10. nid: 'nextId',
  11. btnColumn: 1,
  12. iconCol: 'projType'
  13. },
  14. columns: [
  15. {
  16. head: '工程列表',
  17. data: 'name',
  18. static: false,
  19. width: '100%',
  20. event: {
  21. getText: null,
  22. getIcon: null
  23. }
  24. }
  25. ],
  26. dataTemp: {
  27. id: -1,
  28. parentId: -1,
  29. nextId: -1
  30. },
  31. viewEvent: {
  32. beforeSelect: null,
  33. onSelectNode: null
  34. }
  35. };
  36. var Node = (function () {
  37. function Node(tree, data) {
  38. this.parent = null;
  39. this.nextSibling = null;
  40. this.children = [];
  41. this.tree = tree;
  42. this.data = data;
  43. this.setting = tree.setting;
  44. this.expanded = true;
  45. this.row = null;
  46. this.expandBtn = null;
  47. }
  48. Node.prototype.firstChild = function() {
  49. return (this.children.length === 0) ? null : this.children[0];
  50. };
  51. Node.prototype.lastChild = function () {
  52. return (this.children.length === 0) ? null : this.children[this.children.length - 1];
  53. }
  54. Node.prototype.deepestRow = function () {
  55. return (this.children.length === 0) ? this.row : this.lastChild().deepestRow();
  56. }
  57. Node.prototype.addChild = function (child, childNext) {
  58. if (childNext){
  59. this.children.push(child);
  60. } else {
  61. if (this.childIndex(childNext) > -1){
  62. this.children.splice(this.childIndex(childNext) - 1, 0, child);
  63. } else {
  64. this.children.push(child);
  65. }
  66. }
  67. return child;
  68. };
  69. Node.prototype.childIndex = function (child) {
  70. return this.children.indexOf(child);
  71. };
  72. Node.prototype.depth = function () {
  73. return this.parent ? this.parent.depth() + 1 : 0;
  74. };
  75. Node.prototype.domId = function () {
  76. return this.data ? this.tree.domId + '_' + this.data[this.setting.tree.id] : '';
  77. };
  78. Node.prototype.expand = function (bool) {
  79. this.expanded = bool;
  80. _view._refreshTreeBtn(this);
  81. if (this.expanded) {
  82. _view._showNodes(this.children);
  83. } else {
  84. _view._hideNodes(this.children);
  85. }
  86. };
  87. Node.prototype.preSibling = function () {
  88. var iIndex = this.parent.childIndex(this);
  89. if (iIndex === -1){
  90. return null;
  91. } else {
  92. return iIndex > 0 ? this.parent.children[iIndex-1] : null;
  93. }
  94. };
  95. Node.prototype.setParent = function (parent) {
  96. if (parent && this.parent !== parent) {
  97. this.parent = parent;
  98. this.data[this.setting.tree.pid] = this.pid();
  99. }
  100. };
  101. Node.prototype.setNextSibling = function (nextSibling) {
  102. if (this.nextSibling !== nextSibling) {
  103. this.nextSibling = nextSibling;
  104. this.data[this.setting.tree.nid] = this.nid();
  105. }
  106. }
  107. Node.prototype.id = function () {
  108. return this.data ? this.data[this.setting.tree.id] : -1;
  109. };
  110. Node.prototype.pid = function () {
  111. return this.parent ? this.parent.id() : -1;
  112. };
  113. Node.prototype.nid = function () {
  114. return this.nextSibling ? this.nextSibling.id() : -1;
  115. };
  116. Node.prototype.propertyJoin = function (dataName) {
  117. return this.parent ? this.parent.propertyJoin(dataName) + ';' + this.data[dataName] : this.data[dataName];
  118. }
  119. return Node;
  120. })();
  121. var Tree = (function () {
  122. function Tree(obj, setting) {
  123. this._root = new Node(this);
  124. this.treeObj = obj;
  125. this.domId = obj.attr('id');
  126. this.treeHeadObj = _view._makeTableHead(this.treeObj, setting);
  127. this.treeBodyObj = _view._makeTableBody(this.treeObj);
  128. this.setting = setting;
  129. var _maxNodeId = 0;
  130. this.newNodeId = function (id) {
  131. if (arguments.length > 0){
  132. _maxNodeId = (id > _maxNodeId) ? id : _maxNodeId;
  133. } else {
  134. _maxNodeId += 1;
  135. return _maxNodeId;
  136. }
  137. };
  138. this.maxNodeId = function (){
  139. return _maxNodeId;
  140. }
  141. };
  142. Tree.prototype.firstNode = function (){
  143. return this._root.firstChild();
  144. };
  145. Tree.prototype.traverseDF = function(callback){
  146. var recurse = function (node) {
  147. var i;
  148. if (node !== this._root) {
  149. callback(node);
  150. }
  151. for (i = 0; i < node.children.length; i++){
  152. recurse(node.children[i]);
  153. }
  154. }
  155. recurse(this._root);
  156. };
  157. Tree.prototype.findNode = function (id){
  158. var treenode = null,
  159. callback = function (node) {
  160. if (node.data && node.data[node.setting.tree.id] === id){
  161. treenode = node;
  162. }
  163. };
  164. this.traverseDF.call(this, callback);
  165. return treenode;
  166. };
  167. Tree.prototype.findNodeByDomId = function (domId) {
  168. var treenode = null,
  169. callback = function (node) {
  170. if (node.domId === domId) {
  171. treenode = node;
  172. }
  173. };
  174. this.traverseDF.call(this, callback);
  175. return treenode;
  176. };
  177. Tree.prototype.removeNode = function (node) {
  178. var iIndex;
  179. if (node) {
  180. iIndex = node.parent.childIndex(node);
  181. if (iIndex > 0) {
  182. node.parent.children[iIndex - 1].setNextSibling(node.nextSibling);
  183. }
  184. node.parent.children.splice(iIndex, 1);
  185. }
  186. _view._removeNodesRowDom([node]);
  187. };
  188. Tree.prototype.loadData = function (arrData) {
  189. var i, that = this;
  190. var createTempNode = function (id, setting) {
  191. var tempData = {};
  192. tempData[setting.tree.id] = id;
  193. return new Node(that, tempData);
  194. };
  195. var loadNode = function (data, setting) {
  196. var node = that.findNode(data[setting.tree.id]) || null,
  197. parent = that.findNode(data[setting.tree.pid]) || null,
  198. next = that.findNode(data[setting.tree.nid]) || null,
  199. tempData;
  200. if (!node) {
  201. node = new Node(that, data);
  202. }
  203. node.data = data;
  204. that.newNodeId(node.id());
  205. if (!parent){
  206. if (data[setting.tree.pid] === -1) {
  207. parent = that._root;
  208. } else {
  209. parent = createTempNode(data[setting.tree.pid], setting);
  210. parent.parent = that._root;
  211. that._root.children.push(parent);
  212. }
  213. }
  214. if (node.parent && node.parent !== parent) {
  215. node.parent.children.splice(node.parent.childIndex(node), 1);
  216. }
  217. node.parent = parent;
  218. if (!next && data[setting.tree.nid] !== -1) {
  219. next = createTempNode(data[setting.tree.nid], setting);
  220. next.parent = parent;
  221. }
  222. node.nextSibling = next;
  223. if (parent.childIndex(node) === -1){
  224. if (!next){
  225. parent.children.push(node);
  226. } else if (parent.childIndex(next) === -1){
  227. parent.children.push(node);
  228. parent.children.push(next);
  229. } else {
  230. parent.children.splice(parent.childIndex(next), 0, node);
  231. }
  232. }
  233. };
  234. for (i = 0; i < arrData.length; i++){
  235. loadNode(arrData[i], this.setting);
  236. }
  237. };
  238. Tree.prototype.refreshNodesDom = function (nodes, recurse){
  239. var that = this;
  240. nodes.forEach(function (node) {
  241. if (node.row) {
  242. $('td', node.row).remove();
  243. _view._makeTableRowCells(node.row, node);
  244. } else if (node !== that._root) {
  245. _view._makeRowDom(that.treeBodyObj, node);
  246. }
  247. if (recurse) {
  248. that.refreshNodesDom(node.children, recurse);
  249. }
  250. })
  251. };
  252. Tree.prototype.refreshTreeDom = function () {
  253. this.refreshNodesDom(this._root.children, true);
  254. };
  255. Tree.prototype.addNodeData = function (data, parent, nextSibling) {
  256. var node = null;
  257. var pNode = parent ? parent : this._root;
  258. if (!nextSibling || (nextSibling.parent === pNode && pNode.childIndex(nextSibling) > -1)) {
  259. node = new Node(this, data);
  260. this.newNodeId(data[this.setting.tree.id]);
  261. node.row = _view._makeRowDom(this.treeBodyObj, node);
  262. this.move(node, pNode, nextSibling);
  263. }
  264. return node;
  265. }
  266. Tree.prototype.move = function(node, parent, nextSibling) {
  267. var iIndex = -1, pre;
  268. if (parent && (!nextSibling || (nextSibling.parent === parent && parent.childIndex(nextSibling) > -1))) {
  269. if (node) {
  270. if (node.parent) {
  271. iIndex = node.parent.childIndex(node);
  272. if (iIndex > 0) {
  273. node.parent.children[iIndex - 1].setNextSibling(node.nextSibling);
  274. }
  275. node.parent.children.splice(iIndex, 1);
  276. this.refreshNodesDom([node.parent], false);
  277. }
  278. if (nextSibling) {
  279. iIndex = parent.childIndex(nextSibling);
  280. if (iIndex > 0){
  281. pre = parent.children[iIndex - 1];
  282. pre.setNextSibling(node);
  283. parent.children.splice(iIndex - 1, 0, node);
  284. } else {
  285. parent.children.splice(0, 0, node);
  286. }
  287. } else {
  288. if (parent.children.length > 0){
  289. pre = parent.lastChild();
  290. pre.setNextSibling(node);
  291. }
  292. parent.children.push(node);
  293. }
  294. node.setParent(parent);
  295. node.setNextSibling(nextSibling);
  296. if (node.row) {
  297. if (pre) {
  298. _view._moveRowDom(node, pre.deepestRow());
  299. } else if (parent.id() !== -1) {
  300. _view._moveRowDom(node, parent.row);
  301. } else if (nextSibling) {
  302. _view._moveRowDomBefore(node, nextSibling.row);
  303. }
  304. }
  305. if (node.parent.row) {
  306. this.refreshNodesDom([node.parent], false);
  307. }
  308. }
  309. } else {
  310. this.e.throw('Error(information of moving node has mistake).');
  311. }
  312. };
  313. Tree.prototype.selected = (function () {
  314. var selectNode = null;
  315. var select = function (node) {
  316. if (arguments.length === 0) {
  317. return selectNode;
  318. } else {
  319. if (node) {
  320. if (selectNode && selectNode.row) {
  321. selectNode.row.removeClass('table-active');
  322. }
  323. selectNode = node;
  324. node.row.addClass('table-active');
  325. }
  326. }
  327. }
  328. return select;
  329. })();
  330. return Tree;
  331. })();
  332. _data = {
  333. clone: function (obj) {
  334. if (obj === null) return null;
  335. var o = _data.isArray(obj) ? [] : {};
  336. for (var i in obj) {
  337. o[i] = (obj[i] instanceof Date) ? new Date(obj[i].getTime()) : (typeof obj[i] === "object" ? _data.clone(obj[i]) : obj[i]);
  338. }
  339. return o;
  340. },
  341. isArray: function (arr) {
  342. return Object.prototype.toString.apply(arr) === "[object Array]";
  343. }
  344. },
  345. _event = {
  346. _selectProj: function (node) {
  347. if (node.setting.viewEvent.beforeSelect) {
  348. node.setting.viewEvent.beforeSelect(node.tree.selected());
  349. }
  350. node.tree.selected(node);
  351. if (node.setting.viewEvent.onSelectNode) {
  352. node.setting.viewEvent.onSelectNode(node);
  353. }
  354. }
  355. }
  356. _view = {
  357. _makeTableHeadCell: function (obj, col){
  358. var th;
  359. th = $('<th>');
  360. th.attr('width', col.width);
  361. th.text(col.head);
  362. obj.append(th);
  363. },
  364. _makeTableHead: function (obj, setting){
  365. var thead, tr, i;
  366. thead = $('<thead>');
  367. tr = $('<tr>');
  368. for (i = 0; i < setting.columns.length; i++){
  369. _view._makeTableHeadCell(tr, setting.columns[i]);
  370. }
  371. thead.append(tr);
  372. obj.append(thead);
  373. return thead;
  374. },
  375. _makeTableBody: function (obj){
  376. var tbody;
  377. tbody = $('<tbody>');
  378. obj.append(tbody);
  379. return tbody;
  380. },
  381. _makeTableRowCell: function (obj, node, columns, index) {
  382. var html = [], td, i, url;
  383. html.push('<td ');
  384. if (index === node.setting.tree.btnColumn){
  385. html.push('class=', 'in-', node.depth().toString(), '>');
  386. if (node.children.length !== 0) {
  387. html.push('<a href="#" class="tree-open" title="收起"><i class="fa fa-minus-square-o mr-1"></i></a>');
  388. } else {
  389. html.push('<a href="#" class="tree-open" title="收起"><i></i></a>');
  390. }
  391. } else {
  392. html.push('>');
  393. }
  394. if (columns.event.getIcon) {
  395. columns.event.getIcon(html, node);
  396. }
  397. if (columns.name !== '') {
  398. if (columns.event.getText) {
  399. columns.event.getText(html, node, node.data[columns.data]);
  400. } else {
  401. html.push(node.data[columns.name]);
  402. }
  403. }
  404. html.push('</td>');
  405. td = $(html.join(''));
  406. if (index === node.setting.tree.btnColumn) {
  407. node.expandBtn = $('.tree-open>.fa', td);
  408. node.expandBtn.bind('click', {node: node}, function(e){
  409. e.data.node.expand(!e.data.node.expanded);
  410. });
  411. node.icon = $('.tree-icon', td);
  412. }
  413. obj.append(td);
  414. return td;
  415. },
  416. _makeTableRowCells: function (obj, node) {
  417. for (i = 0; i < node.setting.columns.length; i++){
  418. _view._makeTableRowCell(obj, node, node.setting.columns[i], i);
  419. }
  420. },
  421. _makeRowDom: function (obj, node) {
  422. var tr, i;
  423. tr = $('<tr>');
  424. obj.append(tr);
  425. tr.attr('id', node.domId());
  426. node.row = tr;
  427. _view._makeTableRowCells(tr, node);
  428. tr.click(function(){
  429. _event._selectProj(node);
  430. });
  431. /*$(tr).draggable({
  432. helper: "clone",
  433. opacity: .75,
  434. refreshPositions: true, // Performance?
  435. revert: "invalid",
  436. revertDuration: 300,
  437. scroll: true
  438. });
  439. tr.droppable({
  440. drop: function (e, ui) {
  441. var target = node.tree.findNodeByDomId($(this).attr('id')),
  442. drag = node.tree.findNodeByDomId($(ui).attr('id'));
  443. tree.move(drag, target);
  444. }
  445. }); */
  446. return tr;
  447. },
  448. _moveRowDomBefore: function (node, destination) {
  449. var moveNodesRowDomBefore = function (nodes) {
  450. nodes.forEach(function (node) {
  451. node.row.insertBefore(destination);
  452. moveNodesRowDomBefore(node.children);
  453. });
  454. }
  455. moveNodesRowDomBefore([node]);
  456. _view._refreshNodesLevelCss([node]);
  457. },
  458. _moveRowDom: function (node, destination) {
  459. var pre = destination,
  460. moveNodeRowDom = function (node) {
  461. node.row.insertAfter(pre);
  462. pre = node.row;
  463. moveChildrenRowDom(node);
  464. },
  465. moveChildrenRowDom = function(node){
  466. var i;
  467. for (i = 0; i < node.children.length; i++){
  468. moveNodeRowDom(node.children[i]);
  469. }
  470. };
  471. moveNodeRowDom(node);
  472. _view._refreshNodesLevelCss([node]);
  473. },
  474. _refreshTreeBtn: function (node) {
  475. var i;
  476. if (node.children.length === 0) {
  477. node.expandBtn.hide();
  478. } else {
  479. node.expandBtn.show();
  480. if (node.expanded) {
  481. node.expandBtn.removeClass('fa-plus-square-o');
  482. node.expandBtn.addClass('fa-minus-square-o');
  483. } else {
  484. node.expandBtn.removeClass('fa-minus-square-o');
  485. node.expandBtn.addClass('fa-plus-square-o');
  486. }
  487. }
  488. },
  489. _refreshNodesLevelCss: function (nodes) {
  490. nodes.forEach(function(node){
  491. var td = $('td:eq(' + node.setting.tree.btnColumn.toString() + ')', node.row);
  492. td.attr('class', 'in-'+ node.depth().toString());
  493. _view._refreshNodesLevelCss(node.children);
  494. })
  495. },
  496. _hideNodes: function (nodes) {
  497. var i, node;
  498. for (i = 0; i < nodes.length; i++){
  499. node = nodes[i];
  500. node.row.hide();
  501. _view._hideNodes(node.children);
  502. }
  503. },
  504. _showNodes: function (nodes) {
  505. var i, node;
  506. for (i = 0; i < nodes.length; i++){
  507. node = nodes[i];
  508. node.row.show();
  509. if (node.expanded) {
  510. _view._showNodes(node.children);
  511. }
  512. }
  513. },
  514. _removeNodesRowDom: function (nodes) {
  515. var i, node;
  516. for (i = 0; i < nodes.length; i++){
  517. node = nodes[i];
  518. _view._removeNodesRowDom(node.children);
  519. if (node.row) {
  520. node.row.remove();
  521. }
  522. }
  523. }
  524. };
  525. $.fn.treeTable = {
  526. init: function(obj, setting, arrData){
  527. var _treeSetting, _tree;
  528. _treeSetting = _data.clone(_setting);
  529. $.extend(true, _treeSetting, setting)
  530. var _tree = new Tree(obj, _treeSetting);
  531. _tree.loadData(arrData);
  532. _tree.refreshTreeDom();
  533. return _tree;
  534. }
  535. }
  536. })(jQuery);