tree_table.js 17 KB

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