tree_table.js 17 KB

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