JS 组件系列之BootstrapTable的treegrid功能
程序员文章站
2022-04-10 10:14:02
上篇给大家介绍了js 组件系列之 bootstrap treegrid 组件封装过程,下面重点给大家介绍js 组件系列之bootstraptable的treegrid功能,...
上篇给大家介绍了js 组件系列之 bootstrap treegrid 组件封装过程,下面重点给大家介绍js 组件系列之bootstraptable的treegrid功能,需要的的朋友一起学习吧!
一、效果预览
全部折叠
展开一级
全部展开
二、代码示例
怎么样?效果还行吧。给出js的源码供大家参考。
(function ($) { 'use strict'; var sprintf = function (str) { var args = arguments, flag = true, i = 1; str = str.replace(/%s/g, function () { var arg = args[i++]; if (typeof arg === 'undefined') { flag = false; return ''; } return arg; }); return flag ? str : ''; }; var getfieldindex = function (columns, field) { var index = -1; $.each(columns, function (i, column) { if (column.field === field) { index = i; return false; } return true; }); return index; }; var calculateobjectvalue = function (self, name, args, defaultvalue) { var func = name; if (typeof name === 'string') { var names = name.split('.'); if (names.length > 1) { func = window; $.each(names, function (i, f) { func = func[f]; }); } else { func = window[name]; } } if (typeof func === 'object') { return func; } if (typeof func === 'function') { return func.apply(self, args); } if (!func && typeof name === 'string' && sprintf.apply(this, [name].concat(args))) { return sprintf.apply(this, [name].concat(args)); } return defaultvalue; }; var getitemfield = function (item, field) { var value = item; if (typeof field !== 'string' || item.hasownproperty(field)) { return item[field]; } var props = field.split('.'); for (var p in props) { value = value[props[p]]; } return value; }; var getparent = function (node, source, field) { var data = []; var items = $.grep(source, function (item, index) { return node.parentid == item[field]; }); $.each(items, function (index, item) { data.splice(0, 0, item); var child = getparent(item, source, field); $.each(child, function (i, n) { data.splice(0, 0, n); }); }); return data; }; var getchild = function (node, source, field) { var data = []; var items = $.grep(source, function (item, index) { return item.parentid == node[field]; }); $.each(items, function (index, item) { data.push(item); var child = getchild(item, source, field); $.each(child, function (i, n) { data.push(n); }); }); return data; }; //调用bootstraptable组件的构造器得到对象 var bootstraptable = $.fn.bootstraptable.constructor, _initdata = bootstraptable.prototype.initdata, _initpagination = bootstraptable.prototype.initpagination, _initbody = bootstraptable.prototype.initbody; //重写bootstraptable的initdata方法 bootstraptable.prototype.initdata = function () { _initdata.apply(this, array.prototype.slice.apply(arguments)); var that = this; if (that.options.treeview && this.data.length > 0) { var rows = []; var roots = $.grep(this.data, function (row, index) { return row.level == that.options.treerootlevel; }); $.each(roots, function (index, item) { rows.push(item); var child = getchild(item, that.data, that.options.treeid); $.each(child, function (i, n) { if (that.options.treecollapseall) { n.hidden = true; } rows.push(n); }); }); that.options.data = that.data = rows; } }; //重写bootstraptable的initpagination方法 bootstraptable.prototype.initpagination = function () { //理论情况下,treegrid是不支持分页的,所以默认分页参数为false this.options.pagination = false; //调用“父类”的“虚方法” _initpagination.apply(this, array.prototype.slice.apply(arguments)); }; //重写bootstraptable的initbody方法 bootstraptable.prototype.initbody = function (fixedscroll) { var that = this, html = [], data = this.getdata(); this.trigger('pre-body', data); this.$body = this.$el.find('tbody'); if (!this.$body.length) { this.$body = $('<tbody></tbody>').appendto(this.$el); } if (!this.options.pagination || this.options.sidepagination === 'server') { this.pagefrom = 1; this.pageto = data.length; } for (var i = this.pagefrom - 1; i < this.pageto; i++) { var key, item = data[i], style = {}, csses = [], data_ = '', attributes = {}, htmlattributes = []; if (item.hidden) continue; style = calculateobjectvalue(this.options, this.options.rowstyle, [item, i], style); if (style && style.css) { for (key in style.css) { csses.push(key + ': ' + style.css[key]); } } attributes = calculateobjectvalue(this.options, this.options.rowattributes, [item, i], attributes); if (attributes) { for (key in attributes) { htmlattributes.push(sprintf('%s="%s"', key, escapehtml(attributes[key]))); } } if (item._data && !$.isemptyobject(item._data)) { $.each(item._data, function (k, v) { if (k === 'index') { return; } data_ += sprintf(' data-%s="%s"', k, v); }); } html.push('<tr', sprintf(' %s', htmlattributes.join(' ')), sprintf(' id="%s"', $.isarray(item) ? undefined : item._id), sprintf(' class="%s"', style.classes || ($.isarray(item) ? undefined : item._class)), sprintf(' data-index="%s"', i), sprintf(' data-uniqueid="%s"', item[this.options.uniqueid]), sprintf('%s', data_), '>' ); if (this.options.cardview) { html.push(sprintf('<td colspan="%s">', this.header.fields.length)); } if (!this.options.cardview && this.options.detailview) { html.push('<td>', '<a class="detail-icon" href="javascript:" rel="external nofollow" >', sprintf('<i class="%s %s"></i>', this.options.iconsprefix, this.options.icons.detailopen), '</a>', '</td>'); } $.each(this.header.fields, function (j, field) { var text = '', value = getitemfield(item, field), type = '', cellstyle = {}, id_ = '', class_ = that.header.classes[j], data_ = '', rowspan_ = '', title_ = '', column = that.columns[getfieldindex(that.columns, field)]; if (!column.visible) { return; } style = sprintf('style="%s"', csses.concat(that.header.styles[j]).join('; ')); value = calculateobjectvalue(column, that.header.formatters[j], [value, item, i], value); if (item['_' + field + '_id']) { id_ = sprintf(' id="%s"', item['_' + field + '_id']); } if (item['_' + field + '_class']) { class_ = sprintf(' class="%s"', item['_' + field + '_class']); } if (item['_' + field + '_rowspan']) { rowspan_ = sprintf(' rowspan="%s"', item['_' + field + '_rowspan']); } if (item['_' + field + '_title']) { title_ = sprintf(' title="%s"', item['_' + field + '_title']); } cellstyle = calculateobjectvalue(that.header, that.header.cellstyles[j], [value, item, i], cellstyle); if (cellstyle.classes) { class_ = sprintf(' class="%s"', cellstyle.classes); } if (cellstyle.css) { var csses_ = []; for (var key in cellstyle.css) { csses_.push(key + ': ' + cellstyle.css[key]); } style = sprintf('style="%s"', csses_.concat(that.header.styles[j]).join('; ')); } if (item['_' + field + '_data'] && !$.isemptyobject(item['_' + field + '_data'])) { $.each(item['_' + field + '_data'], function (k, v) { if (k === 'index') { return; } data_ += sprintf(' data-%s="%s"', k, v); }); } if (column.checkbox || column.radio) { type = column.checkbox ? 'checkbox' : type; type = column.radio ? 'radio' : type; text = [that.options.cardview ? '<div class="card-view">' : '<td class="bs-checkbox">', '<input' + sprintf(' data-index="%s"', i) + sprintf(' name="%s"', that.options.selectitemname) + sprintf(' type="%s"', type) + sprintf(' value="%s"', item[that.options.idfield]) + sprintf(' checked="%s"', value === true || (value && value.checked) ? 'checked' : undefined) + sprintf(' disabled="%s"', !column.checkboxenabled || (value && value.disabled) ? 'disabled' : undefined) + ' />', that.header.formatters[j] && typeof value === 'string' ? value : '', that.options.cardview ? '</div>' : '</td>' ].join(''); item[that.header.statefield] = value === true || (value && value.checked); } else { value = typeof value === 'undefined' || value === null ? that.options.undefinedtext : value; var indent, icon; if (that.options.treeview && column.field == that.options.treefield) { var indent = item.level == that.options.level ? '' : sprintf('<span style="margin-left: %spx;"></span>', (item.level - that.options.treerootlevel) * 15); var child = $.grep(data, function (d, i) { return d.parentid == item[that.options.treeid] && !d.hidden; }); icon = sprintf('<span class="tree-icon %s" style="cursor: pointer; margin: 0px 5px;"></span>', child.length > 0 ? that.options.expandicon : that.options.collapseicon); //icon = sprintf('<span class="tree-icon %s" style="cursor: pointer; margin: 0px 5px;"></span>', child.length > 0 ? that.options.expandicon : ""); } text = that.options.cardview ? ['<div class="card-view">', that.options.showheader ? sprintf('<span class="title" %s>%s</span>', style, getpropertyfromother(that.columns, 'field', 'title', field)) : '', sprintf('<span class="value">%s</span>', value), '</div>' ].join('') : [sprintf('<td%s %s %s %s %s %s>', id_, class_, style, data_, rowspan_, title_), indent, icon, value, '</td>' ].join(''); if (that.options.cardview && that.options.smartdisplay && value === '') { text = ''; } } html.push(text); }); if (this.options.cardview) { html.push('</td>'); } html.push('</tr>'); } if (!html.length) { html.push('<tr class="no-records-found">', sprintf('<td colspan="%s">%s</td>', this.$header.find('th').length, this.options.formatnomatches()), '</tr>'); } this.$body.html(html.join('')); if (!fixedscroll) { this.scrollto(0); } this.$body.find('> tr[data-index] > td').off('click dblclick').on('click dblclick', function (e) { var $td = $(this), $tr = $td.parent(), item = that.data[$tr.data('index')], index = $td[0].cellindex, field = that.header.fields[that.options.detailview && !that.options.cardview ? index - 1 : index], column = that.columns[getfieldindex(that.columns, field)], value = getitemfield(item, field); if ($td.find('.detail-icon').length) { return; } that.trigger(e.type === 'click' ? 'click-cell' : 'dbl-click-cell', field, value, item, $td); that.trigger(e.type === 'click' ? 'click-row' : 'dbl-click-row', item, $tr); if (e.type === 'click' && that.options.clicktoselect && column.clicktoselect) { var $selectitem = $tr.find(sprintf('[name="%s"]', that.options.selectitemname)); if ($selectitem.length) { $selectitem[0].click(); } } }); this.$body.find('> tr[data-index] > td > .detail-icon').off('click').on('click', function () { debugger; var $this = $(this), $tr = $this.parent().parent(), index = $tr.data('index'), row = data[index]; if ($tr.next().is('tr.detail-view')) { $this.find('i').attr('class', sprintf('%s %s', that.options.iconsprefix, that.options.icons.detailopen)); $tr.next().remove(); that.trigger('collapse-row', index, row); } else { $this.find('i').attr('class', sprintf('%s %s', that.options.iconsprefix, that.options.icons.detailclose)); $tr.after(sprintf('<tr class="detail-view"><td colspan="%s">%s</td></tr>', $tr.find('td').length, calculateobjectvalue(that.options, that.options.detailformatter, [index, row], ''))); that.trigger('expand-row', index, row, $tr.next().find('td')); } that.resetview(); }); this.$body.find('> tr[data-index] > td > .tree-icon').off('click').on('click', function (e) { debugger; e.stoppropagation(); var $this = $(this), $tr = $this.parent().parent(), index = $tr.data('index'), row = data[index]; var icon = $(this); var child = getchild(data[index], data, that.options.treeid); $.each(child, function (i, c) { $.each(that.data, function (index, item) { if (item[that.options.treeid] == c[that.options.treeid]) { item.hidden = icon.hasclass(that.options.expandicon); that.uncheck(index); return; } }); }); if (icon.hasclass(that.options.expandicon)) { icon.removeclass(that.options.expandicon).addclass(that.options.collapseicon); } else { icon.removeclass(that.options.collapseicon).addclass(that.options.expandicon); } that.options.data = that.data; that.initbody(true); }); this.$selectitem = this.$body.find(sprintf('[name="%s"]', this.options.selectitemname)); this.$selectitem.off('click').on('click', function (event) { event.stopimmediatepropagation(); var $this = $(this), checked = $this.prop('checked'), row = that.data[$this.data('index')]; if (that.options.maintainselected && $(this).is(':radio')) { $.each(that.options.data, function (i, row) { row[that.header.statefield] = false; }); } row[that.header.statefield] = checked; if (that.options.singleselect) { that.$selectitem.not(this).each(function () { that.data[$(this).data('index')][that.header.statefield] = false; }); that.$selectitem.filter(':checked').not(this).prop('checked', false); } that.updateselected(); that.trigger(checked ? 'check' : 'uncheck', row, $this); }); $.each(this.header.events, function (i, events) { if (!events) { return; } if (typeof events === 'string') { events = calculateobjectvalue(null, events); } var field = that.header.fields[i], fieldindex = $.inarray(field, that.getvisiblefields()); if (that.options.detailview && !that.options.cardview) { fieldindex += 1; } for (var key in events) { that.$body.find('tr').each(function () { var $tr = $(this), $td = $tr.find(that.options.cardview ? '.card-view' : 'td').eq(fieldindex), index = key.indexof(' '), name = key.substring(0, index), el = key.substring(index + 1), func = events[key]; $td.find(el).off(name).on(name, function (e) { var index = $tr.data('index'), row = that.data[index], value = row[field]; func.apply(this, [e, value, row, index]); }); }); } }); this.updateselected(); this.resetview(); this.trigger('post-body'); }; //给组件增加默认参数列表 $.extend($.fn.bootstraptable.defaults, { treeview: false,//treeview视图 treefield: "id",//treeview视图字段 treeid: "id", treerootlevel: 0,//根节点序号 treecollapseall: false,//是否全部展开 collapseicon: "glyphicon glyphicon-chevron-right",//折叠样式 expandicon: "glyphicon glyphicon-chevron-down"//展开样式 }); })(jquery);
组件的使用如下:
1、首先引用这个js文件。
2、然后初始化组件
$('#tb').bootstraptable({ url: actionurl + 'getmenulist', toolbar: '#toolbar', sidepagination: 'client', pagination: false, treeview: true, treeid: "id", treefield: "name", treerootlevel: 1, clicktoselect: true,//collapseicon: "glyphicon glyphicon-triangle-right",//折叠样式 //expandicon: "glyphicon glyphicon-triangle-bottom"//展开样式 });
treeview:true表示启用树表格模式;
treeid:'id'表示每一行tree的id;
treefield:'name'表示要对那一列进行展开;
treerootlevel:1表示树根的级别。
还有一个地方需要注意,要建立记录之间的父子级关系,必然后有一个parentid的概念,所以在从后端返回的结果集里面,每条记录势必有一个parentid的属性,如果是根节点,parentid为null。比如我们后台得到的结果集的json格式如下:
[{id: 1, name: "系统设置", url: null, parentid: null, level: 1, createtime: null, status: 1, sortorder: 1,…}, {id: 2, name: "菜单管理", url: "/systems/menu/index", parentid: 1, level: 2, createtime: null, status: 1,…}, {id: 3, name: "订单管理", url: null, parentid: null, level: 1, createtime: "2017-05-31 17:05:27",…}, {id: 4, name: "基础数据", url: null, parentid: null, level: 1, createtime: "2017-05-31 17:05:55",…}, {id: 5, name: "新增订单", url: "/order/add", parentid: 3, level: 2, createtime: "2017-05-31 17:07:03",…}]
三、组件需要完善的地方
上述封装给大家提供一个扩展bootstraptable组件treeview功能,还有很多地方需要完善,比如:
1、我们的叶子节点前面的图标可以去掉;
2、增加展开所有、折叠所有的功能;
3、level字段可以去掉,通过parentid为null来确定根节点。
有兴趣的小伙伴可以自己试试。
四、总结
至此本文就结束了,这篇针对上篇做了一个补充,使得我们可以根据项目的需求自己选择用哪种方式,如果我们项目使用的是bootstraptable作为数据展示的组件,可以考虑上述扩展;如果没有使用bootstraptable,可以试试上篇的组件。
推荐阅读
-
JS 组件系列之 bootstrap treegrid 组件封装过程
-
JS组件系列之MVVM组件构建自己的Vue组件
-
JS 组件系列之Bootstrap Table的冻结列功能彻底解决高度问题
-
JS 组件系列之Bootstrap Table 冻结列功能IE浏览器兼容性问题解决方案
-
JS 组件系列之 bootstrap treegrid 组件封装过程
-
JS组件系列之MVVM组件构建自己的Vue组件
-
JS 组件系列之BootstrapTable的treegrid功能
-
JS 组件系列之Bootstrap Table的冻结列功能彻底解决高度问题
-
JS 组件系列之Bootstrap Table 冻结列功能IE浏览器兼容性问题解决方案
-
JS 组件系列之BootstrapTable的treegrid功能