扩展BootstrapTable的treegrid功能

来源:互联网 发布:python tuple 和 array 编辑:程序博客网 时间:2024/06/03 18:04

 

正文

前言:上篇  JS组件系列——自己动手封装bootstrap-treegrid组件 博主自己动手封装了下treegrid的功能,但毕竟那个组件只是一个单独针对树形表格做的,适用性还比较有限。关注博主的园友应该知道,博主的博客里面写了很多bootstrapTable的扩展,今天打算在直接在bootstrapTable的基础上扩展一个treegrid的功能,很多长期关注博主博客的园友一直在问我怎么在bootstrapTable里面直接使用treegrid的功能,所以今天还是带来点福利。有兴趣的可以捧个人场!

本文原创地址:http://www.cnblogs.com/landeanfen/p/6924895.html

回到顶部

一、效果预览

全部折叠

展开一级

全部展开

回到顶部

二、代码示例

怎么样?效果还行吧。给出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:">',                    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来确定根节点。

原创粉丝点击