zoukankan      html  css  js  c++  java
  • 扩展BootstrapTable的treegrid功能

    扩展BootstrapTable的treegrid功能

    正文

    前言:上篇  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/

  • 相关阅读:
    【已解决】github中git push origin master出错:error: failed to push some refs to
    好记心不如烂笔头,ssh登录 The authenticity of host 192.168.0.xxx can't be established. 的问题
    THINKPHP 5.0目录结构
    thinkphp5.0入口文件
    thinkphp5.0 生命周期
    thinkphp5.0 架构
    Django template
    Django queryset
    Django model
    Python unittest
  • 原文地址:https://www.cnblogs.com/telwanggs/p/7111945.html
Copyright © 2011-2022 走看看