telwanggs

 

正文

前言:上篇  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来确定根节点。

有兴趣的园友可以自己试试。

四、总结

至此本文就结束了,这篇针对上篇做了一个补充,使得我们可以根据项目的需求自己选择用哪种方式,如果我们项目使用的是bootstrapTable作为数据展示的组件,可以考虑上述扩展;如果没有使用bootstrapTable,可以试试上篇的组件。如果你觉得本文能够帮助你,可以右边随意 打赏 博主,打赏后可以获得博主永久免费的技术支持。

本文原创出处:http://www.cnblogs.com/landeanfen/

分类:

技术点:

相关文章:

  • 2022-12-23
  • 2021-12-04
  • 2021-12-04
  • 2021-05-27
  • 2021-05-25
  • 2022-12-23
  • 2021-05-31
猜你喜欢
  • 2022-12-23
  • 2021-09-02
  • 2021-08-03
  • 2021-08-24
  • 2021-12-21
相关资源
相似解决方案