├── DataTables Demo ├── json │ └── data.json ├── demo.html └── js │ └── dataTables.treeGrid.js ├── README.md └── dataTables.treeGrid.js /DataTables Demo/json/data.json: -------------------------------------------------------------------------------- 1 | {"draw":0,"recordsTotal":0,"recordsFiltered":0,"data":[{"remark":"网站建设、软件开发","deptId":5,"name":"同恒科技有限公司","nameEn":"TOHER","headMan":"李怀明","tel":"1372000000","address":"广州天河","functions":"网站建设、软件开发","children":[{"remark":"客服部","deptId":9,"name":"客服部","nameEn":"CustomerService","headMan":"杨雅雅","tel":"0663-8795555-4","address":"广州天河","functions":"主要负责客户的维护","children":[{"remark":"客服子部1","deptId":9,"name":"客服子部1","nameEn":"CustomerService","headMan":"111","tel":"0663-8795555-4","address":"广州天河","functions":"主要负责客户的维护","children":[]},{"remark":"客服子部2","deptId":9,"name":"客服子部2","nameEn":"CustomerService","headMan":"222","tel":"0663-8795555-4","address":"广州天河","functions":"主要负责客户的维护","children":[]}]},{"remark":"网站建设","deptId":8,"name":"网建部","nameEn":"WEB","headMan":"易强","tel":"06638795555","address":"广州天河","functions":"网站建设","children":[]},{"remark":"揭阳渔湖","deptId":7,"name":"营销部","nameEn":"Marketing","headMan":"袁希帆","tel":"06638795555","address":"揭阳渔湖","functions":"运营、销售","children":[]},{"remark":"揭阳","deptId":6,"name":"开发部","nameEn":"Development","headMan":"章浩滨","tel":"06638795555","address":"揭阳","functions":"技术开发","children":[]}]},{"remark":"揭阳渔湖","deptId":2,"name":"胜博科技有限公司","nameEn":"SBKJ","headMan":"陈树标","tel":"13822000000","address":"揭阳渔湖","functions":"通讯设备","children":[{"remark":"综合部","deptId":14,"name":"综合部","nameEn":"Comprehensive","headMan":"孙晓静","tel":"0663-8795555-2","address":"嘉盛华府","functions":"负责人事及财务","children":[]},{"remark":"通讯部","deptId":12,"name":"通讯部","nameEn":"Communication","headMan":"陈建生","tel":"0663-8795555-1","address":"嘉盛华府","functions":"移动业务外呼","children":[]}]}],"error":null} 2 | -------------------------------------------------------------------------------- /DataTables Demo/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | DataTables Demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
部门名称英文名称负责人部门电话部门地址主要职能
34 | 35 | 107 | 108 |
109 | 110 | 111 | 112 | 113 |
114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 插件介绍 2 | 针对DataTables写的树形表格插件([什么是DataTables? 可以点击访问官网了解](https://datatables.net/)) 3 | 在原DataTables基础上可以快速实现树形表格的渲染: 4 | 1、支持自定义展开/收缩 图标 5 | 2、支持自定义缩进距离 6 | 3、N层子集展开父级收缩 子集统一收缩; 7 | 8 | **[插件地址:https://github.com/lhmyy521125/dataTables.treeGrid](https://github.com/lhmyy521125/dataTables.treeGrid)** 9 | # 更新日志 10 | 2020-4-16:千呼万唤始出来,很多朋友再CSDN博客上反馈了插件的一些问题,博主的公司因为已经很少用dataTable(都使用VUE啦)所以很少去弄这款插件了,今天呢总算抽时间完善了这款插件,更新内容如下: 11 | - 1、解决dataTable reload() / draw() 时树形失效问题 12 | - 2、采用新的初始化方式,可以外部调用 expandAll() / collapseAll() 方法 13 | ```javascript 14 | //@example 15 | var table = $('#example').dataTable( { ... } ); 16 | var tree = new $.fn.dataTable.treeGrid( table ); 17 | tree.expandAll(); 18 | tree.collapseAll(); 19 | ``` 20 | - 3、更新后更容易对插件进行扩展,可以自定义自己需要实现的功能,参考expandAll() / collapseAll() 自己定义自己的方法,处理不同的需求 21 | 22 | 2019-5-8:很多朋友在博客私信说要一份DEMO,今天上传了DEMO样例仅供大家参考;注意要在WEB容器运行 23 | 24 | 2019-4-11:新增expandAll配置属性,true默认展开,false不展开不配置默认false 25 | 26 | 2018-10-11:当多层数据时,第一个子集未展开,第二个子集展开,点击父级收缩会出现死循环问题解决; 27 | 28 | 2018-10-11:多层子集收缩的时候会导致第二级以下的展开不会删除问题;解决方案采用递归方式改写收缩方法 29 | 30 | 31 | # 真实系统展现效果 32 | ![展现效果图](https://img-blog.csdnimg.cn/2019011717185479.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xobXl5NTIxMTI1,size_16,color_FFFFFF,t_70) 33 | # DEMO参考 34 | GitHub上DEMO的 运行效果,感兴趣的可以自行下载运行体验~~ 35 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200416152057214.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xobXl5NTIxMTI1,size_16,color_FFFFFF,t_70) 36 | # 使用方法 37 | 38 | ``` 39 | //注意自行下载 dataTables 40 | 41 | 42 | //引入我们写的dataTables Tree 插件 43 | 44 | ``` 45 | 46 | # DataTable 渲染JSON数据格式 47 | 48 | ``` 49 | // JSON对象数据应包含一个属性“children”作为子集 50 | { 51 | "data": 52 | [ 53 | { 54 | "name": "lhmyy521125", 55 | ... 56 | "children": [ 57 | { 58 | "name": "hello", 59 | ... 60 | } 61 | ] 62 | } 63 | ] 64 | } 65 | ``` 66 | # HTML数据格式(以DEMO截图代码为例) 67 | 68 | ``` 69 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 |
部门名称英文名称负责人部门电话部门地址主要职能
86 | 87 | 158 | 159 |
160 | 161 | 162 | 163 | 164 |
165 | 166 | 167 | 168 | 169 | ``` 170 | 171 | 如果该插件帮助到您,别忘记了点个 **star** 对我的支持~ 172 | -------------------------------------------------------------------------------- /dataTables.treeGrid.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @summary TreeGrid 3 | * @description TreeGrid extension for DataTable 4 | * @version 1.1.2 5 | * @file dataTables.treeGrid.js 6 | * 2020-04-16 更新日志 7 | * 1、解决dataTable reload() / draw() 时树形失效问题 8 | * 2、采用新的初始化方式,可以外部调用 expandAll() / collapseAll() 方法 9 | * @example 10 | * var table = $('#example').dataTable( { ... } ); 11 | * var tree = new $.fn.dataTable.treeGrid( table ); 12 | * tree.expandAll(); 13 | * 3、更新后更容易对插件进行扩展,可以自定义自己需要实现的功能,参考expandAll() / collapseAll() 14 | * 自己定义自己的方法,处理不同的需求 15 | */ 16 | (function (factory) { 17 | if (typeof define === 'function' && define.amd) { 18 | // AMD 19 | define(['jquery', 'datatables.net'], function ($) { 20 | return factory($, window, document); 21 | }); 22 | } else if (typeof exports === 'object') { 23 | // CommonJS 24 | module.exports = function (root, $) { 25 | if (!root) { 26 | root = window; 27 | } 28 | 29 | if (!$ || !$.fn.dataTable) { 30 | $ = require('datatables.net')(root, $).$; 31 | } 32 | 33 | return factory($, root, root.document); 34 | }; 35 | } else { 36 | // Browser 37 | factory(jQuery, window, document); 38 | } 39 | }(function ($, window, document) { 40 | 'use strict'; 41 | var DataTable = $.fn.dataTable; 42 | //定义全局 TR 子集 二维数组 43 | var treeGridRows = {}; 44 | var globalInit; 45 | var TreeGrid = function (dt, init) { 46 | var that = this; 47 | if (!(this instanceof TreeGrid)) { 48 | alert('TreeGrid warning: TreeGrid must be initialised with the "new" keyword.'); 49 | return; 50 | } 51 | 52 | if (init === undefined || init === true) { 53 | init = {}; 54 | } 55 | globalInit = init; 56 | var dtSettings = new $.fn.dataTable.Api(dt).settings()[0]; 57 | 58 | this.s = { 59 | dt: dtSettings 60 | }; 61 | 62 | if (dtSettings._oTreeGrid) { 63 | throw 'TreeGrid already initialised on this table'; 64 | } 65 | 66 | dtSettings._oTreeGrid = this; 67 | 68 | if (!dtSettings._bInitComplete) { 69 | dtSettings.oApi._fnCallbackReg(dtSettings, 'aoInitComplete', function () { 70 | that.fnConstruct(init); 71 | }, 'TreeGrid'); 72 | } else { 73 | this.fnConstruct(init); 74 | } 75 | }; 76 | 77 | $.extend(TreeGrid.prototype, { 78 | 79 | "fnConstruct": function (oInit) { 80 | var that = this; 81 | this.s = $.extend(true, this.s, TreeGrid.defaults, oInit); 82 | 83 | var settings = this.s.dt; 84 | var select = settings._select; 85 | var dataTable = $(settings.nTable).dataTable().api(); 86 | var sLeft = this.s.left; 87 | var sExpandAll = this.s.expandAll; 88 | var expandIcon = $(this.s.expandIcon); 89 | var collapseIcon = $(this.s.collapseIcon); 90 | 91 | // Expand TreeGrid 92 | dataTable.on('click', 'td.treegrid-control', function (e) { 93 | if (!$(this).html()) { 94 | return; 95 | } 96 | // record selected indexes 97 | var selectedIndexes = []; 98 | select && (selectedIndexes = dataTable.rows({selected: true}).indexes().toArray()); 99 | var rows = dataTable.rows(); 100 | var parentTr = getParentTr(e.target); 101 | var parentTrId = getTrId(); 102 | $(parentTr).attr('id', parentTrId); 103 | var row = dataTable.row(parentTr); 104 | var index = row.index(); 105 | var data = row.data(); 106 | 107 | var td = $(dataTable.cell(getParentTd(e.target)).node()); 108 | var paddingLeft = parseInt(td.css('padding-left'), 10); 109 | var layer = parseInt(td.find('span').css('margin-left') || 0, 10) / sLeft; 110 | var icon = collapseIcon.clone(); 111 | icon.css('marginLeft', layer * sLeft + 'px'); 112 | td.removeClass('treegrid-control').addClass('treegrid-control-open'); 113 | td.html('').append(icon); 114 | 115 | if (data.children && data.children.length) { 116 | var subRows = treeGridRows[parentTrId] = []; 117 | var prevRow = row.node(); 118 | data.children.forEach(function (item) { 119 | var newRow = dataTable.row.add(item); 120 | var node = newRow.node(); 121 | var treegridTd = $(node).find('.treegrid-control'); 122 | var left = (layer + 1) * sLeft; 123 | $(node).attr('parent-index', index); 124 | treegridTd.find('span').css('marginLeft', left + 'px'); 125 | treegridTd.next().css('paddingLeft', paddingLeft + left + 'px'); 126 | $(node).insertAfter(prevRow); 127 | prevRow = node; 128 | subRows.push(node); 129 | }); 130 | 131 | resetEvenOddClass(dataTable); 132 | select && setTimeout(function () { 133 | dataTable.rows(selectedIndexes).select(); 134 | }, 0); 135 | } 136 | }); 137 | // Collapse TreeGrid 138 | dataTable.on('click', 'td.treegrid-control-open', function (e) { 139 | var selectedIndexes = []; 140 | select && (selectedIndexes = dataTable.rows({selected: true}).indexes().toArray()); 141 | 142 | var parentTr = getParentTr(e.target); 143 | var parentTrId = $(parentTr).attr('id'); 144 | var td = $(dataTable.cell(getParentTd(e.target)).node()); 145 | var layer = parseInt(td.find('span').css('margin-left') || 0, 10) / sLeft; 146 | var icon = expandIcon.clone(); 147 | icon.css('marginLeft', layer * sLeft + 'px'); 148 | td.removeClass('treegrid-control-open').addClass('treegrid-control'); 149 | td.html('').append(icon); 150 | 151 | resetTreeGridRows(parentTrId); 152 | resetEvenOddClass(dataTable); 153 | 154 | select && setTimeout(function () { 155 | dataTable.rows(selectedIndexes).select(); 156 | }, 0); 157 | }); 158 | //dataTable init 处理 159 | dataTable.on('init.dt', function () { 160 | console.log('Table initialisation complete: ' + new Date().getTime()); 161 | //dataTable 初始化完成调用展开 162 | if(sExpandAll){ 163 | that.expandAll.call(that) 164 | } 165 | }); 166 | 167 | //dataTable draw 处理 168 | dataTable.on('draw.dt.DTFC', function () { 169 | that._fnDraw.call(that); 170 | }) 171 | 172 | var inProgress = false; 173 | // Check parents and children on select 174 | select && select.style === 'multi' && dataTable.on('select', function (e, dt, type, indexes) { 175 | if (inProgress) { 176 | return; 177 | } 178 | inProgress = true; 179 | indexes.forEach(function (index) { 180 | // Check parents 181 | selectParent(dataTable, index); 182 | // Check children 183 | selectChildren(dataTable, index); 184 | }); 185 | inProgress = false; 186 | }); 187 | 188 | // Check parents and children on deselect 189 | select && select.style === 'multi' && dataTable.on('deselect', function (e, dt, type, indexes) { 190 | if (inProgress) { 191 | return; 192 | } 193 | inProgress = true; 194 | indexes.forEach(function (index) { 195 | // Check parents 196 | deselectParent(dataTable, index); 197 | 198 | // Check children 199 | deselectChildren(dataTable, index); 200 | }); 201 | inProgress = false; 202 | }); 203 | }, 204 | /** 205 | * @returns {void} 206 | * @example 207 | * var table = $('#example').dataTable( { ... } ); 208 | * var tree = new $.fn.dataTable.treeGrid( table ); 209 | * tree.expandAll(); 210 | */ 211 | "expandAll": function () { 212 | console.log('expandAll: ' + new Date().getTime()); 213 | var that = this; 214 | this.s = $.extend(true, this.s, TreeGrid.defaults, globalInit); 215 | var dataTable = $(this.s.dt.nTable).dataTable().api(); 216 | var tds = $(dataTable.table().body()).find('td.treegrid-control'); 217 | tds.each(function (index, td) { 218 | _expandAll(that, null, td); 219 | }); 220 | }, 221 | 222 | "collapseAll":function(){ 223 | console.log('collapseAll: ' + new Date().getTime()); 224 | var that = this; 225 | this.s = $.extend(true, this.s, TreeGrid.defaults, globalInit); 226 | var dataTable = $(this.s.dt.nTable).dataTable().api(); 227 | var trs = $(dataTable.table().body()).find('tr'); 228 | trs.each(function (index, tr) { 229 | if(typeof($(tr).attr("parent-index"))=="undefined"){ 230 | var trid = $(tr).attr('id'); 231 | resetTreeGridRows(trid); 232 | } 233 | }); 234 | }, 235 | 236 | "_fnDraw": function () { 237 | console.log('_fnDraw: ' + new Date().getTime()); 238 | var that = this; 239 | this.s = $.extend(true, this.s, TreeGrid.defaults, globalInit); 240 | /* Draw callback function */ 241 | if (this.s.expandAll) { 242 | console.log('expandAll is True: ' + new Date().getTime()); 243 | var tds = $(dataTable.table().body()).find('td.treegrid-control'); 244 | tds.each(function (index, td) { 245 | _expandAll(that, null, td); 246 | }); 247 | } 248 | /* Event triggering */ 249 | $(this).trigger('draw.dtfc', { 250 | "left": this.s.left 251 | }); 252 | }, 253 | }); 254 | 255 | /** 256 | * 收缩展开处理 257 | * @param trId 258 | */ 259 | function resetTreeGridRows (trId) { 260 | var subRows = treeGridRows[trId]; 261 | if (subRows && subRows.length) { 262 | subRows.forEach(function (node) { 263 | var subTrId = $(node).attr('id'); 264 | if (treeGridRows[subTrId]) { 265 | resetTreeGridRows(subTrId); 266 | } 267 | dataTable.row($(node)).remove(); 268 | $(node).remove(); 269 | }); 270 | delete treeGridRows[trId]; 271 | $('#' + trId).find('.treegrid-control-open').each(function (i, td) { 272 | $(td).removeClass('treegrid-control-open').addClass('treegrid-control'); 273 | $(td).html('').append($(globalInit.expandIcon).clone()); 274 | }); 275 | } 276 | }; 277 | 278 | function resetEvenOddClass(dataTable) { 279 | var classes = ['odd', 'even']; 280 | $(dataTable.table().body()).find('tr').each(function (index, tr) { 281 | $(tr).removeClass('odd even').addClass(classes[index % 2]); 282 | }); 283 | }; 284 | 285 | function selectParent(dataTable, index) { 286 | var row = dataTable.row(index); 287 | var parentIndex = $(row.node()).attr('parent-index'); 288 | if (parentIndex !== null) { 289 | parentIndex = +parentIndex; 290 | var selector = '[parent-index="' + parentIndex + '"]'; 291 | var allChildRows = dataTable.rows(selector).nodes(); 292 | var selectedChildRows = dataTable.rows(selector, {selected: true}).nodes(); 293 | if (allChildRows.length === selectedChildRows.length) { 294 | var parentRow = dataTable.row(parentIndex, {selected: false}); 295 | parentRow.select(); 296 | if (parentRow.node()) { 297 | selectParent(dataTable, parentIndex); 298 | } 299 | } 300 | } 301 | } 302 | 303 | function selectChildren(dataTable, index) { 304 | var rows = dataTable.rows('[parent-index="' + index + '"]', {selected: false}); 305 | var childIndexes = rows.indexes().toArray(); 306 | if (childIndexes.length) { 307 | rows.select(); 308 | childIndexes.forEach(function (childIndex) { 309 | selectChildren(dataTable, childIndex); 310 | }); 311 | } 312 | } 313 | 314 | function deselectParent(dataTable, index) { 315 | var row = dataTable.row(index); 316 | var parentIndex = $(row.node()).attr('parent-index'); 317 | if (parentIndex !== null) { 318 | parentIndex = +parentIndex; 319 | var parentRow = dataTable.row(parentIndex, {selected: true}); 320 | parentRow.deselect(); 321 | if (parentRow.node()) { 322 | deselectParent(dataTable, parentIndex); 323 | } 324 | } 325 | } 326 | 327 | function deselectChildren(dataTable, index) { 328 | var rows = dataTable.rows('[parent-index="' + index + '"]', {selected: true}); 329 | var childIndexes = rows.indexes().toArray(); 330 | if (childIndexes.length) { 331 | rows.deselect(); 332 | childIndexes.forEach(function (childIndex) { 333 | deselectChildren(dataTable, childIndex); 334 | }); 335 | } 336 | } 337 | 338 | //默认展开方法 339 | function _expandAll(treeGrid, insertTr, tds) { 340 | var settings = treeGrid.s.dt; 341 | var select = settings._select; 342 | var dataTable = $(settings.nTable).dataTable().api(); 343 | var sLeft = treeGrid.s.left; 344 | 345 | var expandIcon = $(treeGrid.s.expandIcon); 346 | var collapseIcon = $(treeGrid.s.collapseIcon); 347 | if (!tds) { 348 | return 349 | } 350 | // 迭代存在子集的表格行 351 | var parentTr = getParentTr(tds); 352 | var parentTrId = getTrId(); 353 | 354 | var row = dataTable.row(parentTr); 355 | var index = row.index(); 356 | var data = row.data(); 357 | 358 | if (data.children && data.children.length) { 359 | //由于数据insertAfter插入问题,这里将JSON 倒序 360 | data.children.reverse(); 361 | $(parentTr).attr('id', parentTrId); 362 | // var td = $(dataTable.cell(getParentTd(tds)).node()); 363 | var td = $(tds); 364 | var paddingLeft = parseInt(td.css('padding-left'), 10); 365 | var layer = parseInt(td.find('span').css('margin-left') || 0, 10) / sLeft; 366 | var icon = collapseIcon.clone(); 367 | icon.css('marginLeft', layer * sLeft + 'px'); 368 | td.removeClass('treegrid-control').addClass('treegrid-control-open'); 369 | td.html('').append(icon); 370 | 371 | var subRows = treeGridRows[parentTrId] = []; 372 | var prevRow = row.node(); 373 | if (!insertTr) { 374 | insertTr = prevRow; 375 | } 376 | 377 | data.children.forEach(function (item) { 378 | var newRow = dataTable.row.add(item); 379 | var node = newRow.node(); 380 | var treegridTd = $(node).find('.treegrid-control'); 381 | var left = (layer + 1) * sLeft; 382 | $(node).attr('parent-index', index); 383 | treegridTd.find('span').css('marginLeft', left + 'px'); 384 | treegridTd.next().css('paddingLeft', paddingLeft + left + 'px'); 385 | // $(node).insertAfter(prevRow); 386 | $(node).insertAfter(insertTr); 387 | prevRow = node; 388 | subRows.push(node); 389 | //递归展开子集 当前插入行存在子集则递归 390 | var prevRowData = dataTable.row(prevRow).data(); 391 | if (prevRowData.children && prevRowData.children.length) { 392 | var prevTd = $(prevRow).find('td.treegrid-control'); 393 | _expandAll(treeGrid, $(node), prevTd); 394 | } 395 | 396 | }); 397 | select && setTimeout(function () { 398 | dataTable.rows(selectedIndexes).select(); 399 | }, 0); 400 | } 401 | }; 402 | 403 | function getTrId() { 404 | return 'tr-' + Date.now(); 405 | } 406 | 407 | function getParentTr(target) { 408 | return $(target).parents('tr')[0]; 409 | } 410 | 411 | function getParentTd(target) { 412 | return target.tagName === 'TD' ? target : $(target).parents('td')[0]; 413 | } 414 | 415 | TreeGrid.defaults = { 416 | "left": 12, 417 | "expandAll": false, 418 | "expandIcon": '+', 419 | "collapseIcon": '-', 420 | "fnDrawCallback": null 421 | }; 422 | 423 | TreeGrid.version = '1.1.2'; 424 | 425 | DataTable.Api.register('treeGrid()', function () { 426 | return this; 427 | }); 428 | 429 | $(document).on('init.dt.treeGrid', function (e, settings) { 430 | if (e.namespace !== 'dt') { 431 | return; 432 | } 433 | 434 | var init = settings.oInit.treeGrid; 435 | var defaults = DataTable.defaults.treeGrid; 436 | 437 | if (init || defaults) { 438 | var opts = $.extend({}, init, defaults); 439 | 440 | if (init !== false) { 441 | var treeGrid = new TreeGrid(settings, opts); 442 | } 443 | } 444 | }); 445 | 446 | $.fn.dataTable.TreeGrid = TreeGrid; 447 | $.fn.DataTable.TreeGrid = TreeGrid; 448 | 449 | return TreeGrid; 450 | })); 451 | -------------------------------------------------------------------------------- /DataTables Demo/js/dataTables.treeGrid.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @summary TreeGrid 3 | * @description TreeGrid extension for DataTable 4 | * @version 1.1.2 5 | * @file dataTables.treeGrid.js 6 | * 2020-04-16 更新日志 7 | * 1、解决dataTable reload() / draw() 时树形失效问题 8 | * 2、采用新的初始化方式,可以外部调用 expandAll() / collapseAll() 方法 9 | * @example 10 | * var table = $('#example').dataTable( { ... } ); 11 | * var tree = new $.fn.dataTable.treeGrid( table ); 12 | * tree.expandAll(); 13 | * 3、更新后更容易对插件进行扩展,可以自定义自己需要实现的功能,参考expandAll() / collapseAll() 14 | * 自己定义自己的方法,处理不同的需求 15 | */ 16 | (function (factory) { 17 | if (typeof define === 'function' && define.amd) { 18 | // AMD 19 | define(['jquery', 'datatables.net'], function ($) { 20 | return factory($, window, document); 21 | }); 22 | } else if (typeof exports === 'object') { 23 | // CommonJS 24 | module.exports = function (root, $) { 25 | if (!root) { 26 | root = window; 27 | } 28 | 29 | if (!$ || !$.fn.dataTable) { 30 | $ = require('datatables.net')(root, $).$; 31 | } 32 | 33 | return factory($, root, root.document); 34 | }; 35 | } else { 36 | // Browser 37 | factory(jQuery, window, document); 38 | } 39 | }(function ($, window, document) { 40 | 'use strict'; 41 | var DataTable = $.fn.dataTable; 42 | //定义全局 TR 子集 二维数组 43 | var treeGridRows = {}; 44 | var globalInit; 45 | var TreeGrid = function (dt, init) { 46 | var that = this; 47 | if (!(this instanceof TreeGrid)) { 48 | alert('TreeGrid warning: TreeGrid must be initialised with the "new" keyword.'); 49 | return; 50 | } 51 | 52 | if (init === undefined || init === true) { 53 | init = {}; 54 | } 55 | globalInit = init; 56 | var dtSettings = new $.fn.dataTable.Api(dt).settings()[0]; 57 | 58 | this.s = { 59 | dt: dtSettings 60 | }; 61 | 62 | if (dtSettings._oTreeGrid) { 63 | throw 'TreeGrid already initialised on this table'; 64 | } 65 | 66 | dtSettings._oTreeGrid = this; 67 | 68 | if (!dtSettings._bInitComplete) { 69 | dtSettings.oApi._fnCallbackReg(dtSettings, 'aoInitComplete', function () { 70 | that.fnConstruct(init); 71 | }, 'TreeGrid'); 72 | } else { 73 | this.fnConstruct(init); 74 | } 75 | }; 76 | 77 | $.extend(TreeGrid.prototype, { 78 | 79 | "fnConstruct": function (oInit) { 80 | var that = this; 81 | this.s = $.extend(true, this.s, TreeGrid.defaults, oInit); 82 | 83 | var settings = this.s.dt; 84 | var select = settings._select; 85 | var dataTable = $(settings.nTable).dataTable().api(); 86 | var sLeft = this.s.left; 87 | var sExpandAll = this.s.expandAll; 88 | var expandIcon = $(this.s.expandIcon); 89 | var collapseIcon = $(this.s.collapseIcon); 90 | 91 | // Expand TreeGrid 92 | dataTable.on('click', 'td.treegrid-control', function (e) { 93 | if (!$(this).html()) { 94 | return; 95 | } 96 | // record selected indexes 97 | var selectedIndexes = []; 98 | select && (selectedIndexes = dataTable.rows({selected: true}).indexes().toArray()); 99 | var rows = dataTable.rows(); 100 | var parentTr = getParentTr(e.target); 101 | var parentTrId = getTrId(); 102 | $(parentTr).attr('id', parentTrId); 103 | var row = dataTable.row(parentTr); 104 | var index = row.index(); 105 | var data = row.data(); 106 | 107 | var td = $(dataTable.cell(getParentTd(e.target)).node()); 108 | var paddingLeft = parseInt(td.css('padding-left'), 10); 109 | var layer = parseInt(td.find('span').css('margin-left') || 0, 10) / sLeft; 110 | var icon = collapseIcon.clone(); 111 | icon.css('marginLeft', layer * sLeft + 'px'); 112 | td.removeClass('treegrid-control').addClass('treegrid-control-open'); 113 | td.html('').append(icon); 114 | 115 | if (data.children && data.children.length) { 116 | var subRows = treeGridRows[parentTrId] = []; 117 | var prevRow = row.node(); 118 | data.children.forEach(function (item) { 119 | var newRow = dataTable.row.add(item); 120 | var node = newRow.node(); 121 | var treegridTd = $(node).find('.treegrid-control'); 122 | var left = (layer + 1) * sLeft; 123 | $(node).attr('parent-index', index); 124 | treegridTd.find('span').css('marginLeft', left + 'px'); 125 | treegridTd.next().css('paddingLeft', paddingLeft + left + 'px'); 126 | $(node).insertAfter(prevRow); 127 | prevRow = node; 128 | subRows.push(node); 129 | }); 130 | 131 | resetEvenOddClass(dataTable); 132 | select && setTimeout(function () { 133 | dataTable.rows(selectedIndexes).select(); 134 | }, 0); 135 | } 136 | }); 137 | // Collapse TreeGrid 138 | dataTable.on('click', 'td.treegrid-control-open', function (e) { 139 | var selectedIndexes = []; 140 | select && (selectedIndexes = dataTable.rows({selected: true}).indexes().toArray()); 141 | 142 | var parentTr = getParentTr(e.target); 143 | var parentTrId = $(parentTr).attr('id'); 144 | var td = $(dataTable.cell(getParentTd(e.target)).node()); 145 | var layer = parseInt(td.find('span').css('margin-left') || 0, 10) / sLeft; 146 | var icon = expandIcon.clone(); 147 | icon.css('marginLeft', layer * sLeft + 'px'); 148 | td.removeClass('treegrid-control-open').addClass('treegrid-control'); 149 | td.html('').append(icon); 150 | 151 | resetTreeGridRows(parentTrId); 152 | resetEvenOddClass(dataTable); 153 | 154 | select && setTimeout(function () { 155 | dataTable.rows(selectedIndexes).select(); 156 | }, 0); 157 | }); 158 | //dataTable init 处理 159 | dataTable.on('init.dt', function () { 160 | console.log('Table initialisation complete: ' + new Date().getTime()); 161 | //dataTable 初始化完成调用展开 162 | if(sExpandAll){ 163 | that.expandAll.call(that) 164 | } 165 | }); 166 | 167 | //dataTable draw 处理 168 | dataTable.on('draw.dt.DTFC', function () { 169 | that._fnDraw.call(that); 170 | }) 171 | 172 | var inProgress = false; 173 | // Check parents and children on select 174 | select && select.style === 'multi' && dataTable.on('select', function (e, dt, type, indexes) { 175 | if (inProgress) { 176 | return; 177 | } 178 | inProgress = true; 179 | indexes.forEach(function (index) { 180 | // Check parents 181 | selectParent(dataTable, index); 182 | // Check children 183 | selectChildren(dataTable, index); 184 | }); 185 | inProgress = false; 186 | }); 187 | 188 | // Check parents and children on deselect 189 | select && select.style === 'multi' && dataTable.on('deselect', function (e, dt, type, indexes) { 190 | if (inProgress) { 191 | return; 192 | } 193 | inProgress = true; 194 | indexes.forEach(function (index) { 195 | // Check parents 196 | deselectParent(dataTable, index); 197 | 198 | // Check children 199 | deselectChildren(dataTable, index); 200 | }); 201 | inProgress = false; 202 | }); 203 | }, 204 | /** 205 | * @returns {void} 206 | * @example 207 | * var table = $('#example').dataTable( { ... } ); 208 | * var tree = new $.fn.dataTable.treeGrid( table ); 209 | * tree.expandAll(); 210 | */ 211 | "expandAll": function () { 212 | console.log('expandAll: ' + new Date().getTime()); 213 | var that = this; 214 | this.s = $.extend(true, this.s, TreeGrid.defaults, globalInit); 215 | var dataTable = $(this.s.dt.nTable).dataTable().api(); 216 | var tds = $(dataTable.table().body()).find('td.treegrid-control'); 217 | tds.each(function (index, td) { 218 | _expandAll(that, null, td); 219 | }); 220 | }, 221 | 222 | "collapseAll":function(){ 223 | console.log('collapseAll: ' + new Date().getTime()); 224 | var that = this; 225 | this.s = $.extend(true, this.s, TreeGrid.defaults, globalInit); 226 | var dataTable = $(this.s.dt.nTable).dataTable().api(); 227 | var trs = $(dataTable.table().body()).find('tr'); 228 | trs.each(function (index, tr) { 229 | if(typeof($(tr).attr("parent-index"))=="undefined"){ 230 | var trid = $(tr).attr('id'); 231 | resetTreeGridRows(trid); 232 | } 233 | }); 234 | }, 235 | 236 | "_fnDraw": function () { 237 | console.log('_fnDraw: ' + new Date().getTime()); 238 | var that = this; 239 | this.s = $.extend(true, this.s, TreeGrid.defaults, globalInit); 240 | /* Draw callback function */ 241 | if (this.s.expandAll) { 242 | console.log('expandAll is True: ' + new Date().getTime()); 243 | var tds = $(dataTable.table().body()).find('td.treegrid-control'); 244 | tds.each(function (index, td) { 245 | _expandAll(that, null, td); 246 | }); 247 | } 248 | /* Event triggering */ 249 | $(this).trigger('draw.dtfc', { 250 | "left": this.s.left 251 | }); 252 | }, 253 | }); 254 | 255 | /** 256 | * 收缩展开处理 257 | * @param trId 258 | */ 259 | function resetTreeGridRows (trId) { 260 | var subRows = treeGridRows[trId]; 261 | if (subRows && subRows.length) { 262 | subRows.forEach(function (node) { 263 | var subTrId = $(node).attr('id'); 264 | if (treeGridRows[subTrId]) { 265 | resetTreeGridRows(subTrId); 266 | } 267 | dataTable.row($(node)).remove(); 268 | $(node).remove(); 269 | }); 270 | delete treeGridRows[trId]; 271 | $('#' + trId).find('.treegrid-control-open').each(function (i, td) { 272 | $(td).removeClass('treegrid-control-open').addClass('treegrid-control'); 273 | $(td).html('').append($(globalInit.expandIcon).clone()); 274 | }); 275 | } 276 | }; 277 | 278 | function resetEvenOddClass(dataTable) { 279 | var classes = ['odd', 'even']; 280 | $(dataTable.table().body()).find('tr').each(function (index, tr) { 281 | $(tr).removeClass('odd even').addClass(classes[index % 2]); 282 | }); 283 | }; 284 | 285 | function selectParent(dataTable, index) { 286 | var row = dataTable.row(index); 287 | var parentIndex = $(row.node()).attr('parent-index'); 288 | if (parentIndex !== null) { 289 | parentIndex = +parentIndex; 290 | var selector = '[parent-index="' + parentIndex + '"]'; 291 | var allChildRows = dataTable.rows(selector).nodes(); 292 | var selectedChildRows = dataTable.rows(selector, {selected: true}).nodes(); 293 | if (allChildRows.length === selectedChildRows.length) { 294 | var parentRow = dataTable.row(parentIndex, {selected: false}); 295 | parentRow.select(); 296 | if (parentRow.node()) { 297 | selectParent(dataTable, parentIndex); 298 | } 299 | } 300 | } 301 | } 302 | 303 | function selectChildren(dataTable, index) { 304 | var rows = dataTable.rows('[parent-index="' + index + '"]', {selected: false}); 305 | var childIndexes = rows.indexes().toArray(); 306 | if (childIndexes.length) { 307 | rows.select(); 308 | childIndexes.forEach(function (childIndex) { 309 | selectChildren(dataTable, childIndex); 310 | }); 311 | } 312 | } 313 | 314 | function deselectParent(dataTable, index) { 315 | var row = dataTable.row(index); 316 | var parentIndex = $(row.node()).attr('parent-index'); 317 | if (parentIndex !== null) { 318 | parentIndex = +parentIndex; 319 | var parentRow = dataTable.row(parentIndex, {selected: true}); 320 | parentRow.deselect(); 321 | if (parentRow.node()) { 322 | deselectParent(dataTable, parentIndex); 323 | } 324 | } 325 | } 326 | 327 | function deselectChildren(dataTable, index) { 328 | var rows = dataTable.rows('[parent-index="' + index + '"]', {selected: true}); 329 | var childIndexes = rows.indexes().toArray(); 330 | if (childIndexes.length) { 331 | rows.deselect(); 332 | childIndexes.forEach(function (childIndex) { 333 | deselectChildren(dataTable, childIndex); 334 | }); 335 | } 336 | } 337 | 338 | //默认展开方法 339 | function _expandAll(treeGrid, insertTr, tds) { 340 | var settings = treeGrid.s.dt; 341 | var select = settings._select; 342 | var dataTable = $(settings.nTable).dataTable().api(); 343 | var sLeft = treeGrid.s.left; 344 | 345 | var expandIcon = $(treeGrid.s.expandIcon); 346 | var collapseIcon = $(treeGrid.s.collapseIcon); 347 | if (!tds) { 348 | return 349 | } 350 | // 迭代存在子集的表格行 351 | var parentTr = getParentTr(tds); 352 | var parentTrId = getTrId(); 353 | 354 | var row = dataTable.row(parentTr); 355 | var index = row.index(); 356 | var data = row.data(); 357 | 358 | if (data.children && data.children.length) { 359 | //由于数据insertAfter插入问题,这里将JSON 倒序 360 | data.children.reverse(); 361 | $(parentTr).attr('id', parentTrId); 362 | // var td = $(dataTable.cell(getParentTd(tds)).node()); 363 | var td = $(tds); 364 | var paddingLeft = parseInt(td.css('padding-left'), 10); 365 | var layer = parseInt(td.find('span').css('margin-left') || 0, 10) / sLeft; 366 | var icon = collapseIcon.clone(); 367 | icon.css('marginLeft', layer * sLeft + 'px'); 368 | td.removeClass('treegrid-control').addClass('treegrid-control-open'); 369 | td.html('').append(icon); 370 | 371 | var subRows = treeGridRows[parentTrId] = []; 372 | var prevRow = row.node(); 373 | if (!insertTr) { 374 | insertTr = prevRow; 375 | } 376 | 377 | data.children.forEach(function (item) { 378 | var newRow = dataTable.row.add(item); 379 | var node = newRow.node(); 380 | var treegridTd = $(node).find('.treegrid-control'); 381 | var left = (layer + 1) * sLeft; 382 | $(node).attr('parent-index', index); 383 | treegridTd.find('span').css('marginLeft', left + 'px'); 384 | treegridTd.next().css('paddingLeft', paddingLeft + left + 'px'); 385 | // $(node).insertAfter(prevRow); 386 | $(node).insertAfter(insertTr); 387 | prevRow = node; 388 | subRows.push(node); 389 | //递归展开子集 当前插入行存在子集则递归 390 | var prevRowData = dataTable.row(prevRow).data(); 391 | if (prevRowData.children && prevRowData.children.length) { 392 | var prevTd = $(prevRow).find('td.treegrid-control'); 393 | _expandAll(treeGrid, $(node), prevTd); 394 | } 395 | 396 | }); 397 | select && setTimeout(function () { 398 | dataTable.rows(selectedIndexes).select(); 399 | }, 0); 400 | } 401 | }; 402 | 403 | function getTrId() { 404 | return 'tr-' + Date.now(); 405 | } 406 | 407 | function getParentTr(target) { 408 | return $(target).parents('tr')[0]; 409 | } 410 | 411 | function getParentTd(target) { 412 | return target.tagName === 'TD' ? target : $(target).parents('td')[0]; 413 | } 414 | 415 | TreeGrid.defaults = { 416 | "left": 12, 417 | "expandAll": false, 418 | "expandIcon": '+', 419 | "collapseIcon": '-', 420 | "fnDrawCallback": null 421 | }; 422 | 423 | TreeGrid.version = '1.1.2'; 424 | 425 | DataTable.Api.register('treeGrid()', function () { 426 | return this; 427 | }); 428 | 429 | $(document).on('init.dt.treeGrid', function (e, settings) { 430 | if (e.namespace !== 'dt') { 431 | return; 432 | } 433 | 434 | var init = settings.oInit.treeGrid; 435 | var defaults = DataTable.defaults.treeGrid; 436 | 437 | if (init || defaults) { 438 | var opts = $.extend({}, init, defaults); 439 | 440 | if (init !== false) { 441 | var treeGrid = new TreeGrid(settings, opts); 442 | } 443 | } 444 | }); 445 | 446 | $.fn.dataTable.TreeGrid = TreeGrid; 447 | $.fn.DataTable.TreeGrid = TreeGrid; 448 | 449 | return TreeGrid; 450 | })); 451 | --------------------------------------------------------------------------------