关于bootstrap-table-tree封装

本文介绍了如何使用Python Django结合Bootstrap封装tree表格,详细讲述了如何处理parentId以构建树形结构,并通过AJAX获取后台数据。注意,该树形表格不支持分页,更多资源与文章由作者提供。
部署运行你感兴趣的模型镜像
(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 items = $.grep(source, function (item, index) { return item.parentId == node[field]; });
        return items;
    };

    var getAllChild = function (node, source, field) {
        var data = [];
        var g=function(child){
            $.each(child, function (i, n) { data.push(n); var subChild = getChild(n, source, field); if(subChild!=null && subChild.length>0){ g(subChild); } });
        }
        var child = getChild(node, source, field);
        g(child);
        return data;
    };

    //调用bootstrapTable组件的构造器得到对象
    var BootstrapTable = $.fn.bootstrapTable.Constructor,
        _initData = BootstrapTable.prototype.initData,
        _initPagination = BootstrapTable.prototype.initPagination;

    //重写bootstrapTable的initData方法
    BootstrapTable.prototype.initData = function () {
        _initData.apply(this, Array.prototype.slice.apply(arguments));
        var that = this;
        //初始化数据,添加level,isLeaf 属性
        if (that.options.treeView && this.data.length > 0) {
            var rows = [],levelStep=1;

            var roots = $.grep(this.data, function (row, index) { return row.parentId == null; });
            var g=function(child){ var childLevel=that.options.treeRootLevel+levelStep; $.each(child, function (i, n) { n.level=childLevel; if (that.options.treeCollapseAll) { n.hidden = true; } var subChild = getChild(n, that.data, that.options.treeId); if(subChild==null || subChild.length==0){ n.isLeaf=true; } rows.push(n); if(subChild!=null && subChild.length>0){ levelStep++; g(subChild); }else{ levelStep=1; } }); }
            $.each(roots, function (index, item) { item.level=that.options.treeRootLevel; var child = getChild(item, that.data, that.options.treeId); if(child==null || child.length==0){ item.isLeaf=true; } rows.push(item); g(child); });
            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的initRow方法

    BootstrapTable.prototype.initRow = function(item, i, data, parentDom) {
        var that=this,
            key,
            html = [],
            style = {},
            csses = [],
            data_ = '',
            attributes = {},
            htmlAttributes = [];

        if ($.inArray(item, this.hiddenRows) > -1) {
            return;
        }

        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) { // ignore data-index 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"><div class="card-views">', this.header.fields.length));
        }

        if (!this.options.cardView && this.options.detailView) {
            html.push('<td>', '<a class="detail-icon" href="#">', 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, that.options.escape), value = '', type = '', cellStyle = {}, id_ = '', class_ = that.header.classes[j], data_ = '', rowspan_ = '', colspan_ = '', title_ = '', column = that.columns[j]; if (that.fromHtml && typeof value_ === 'undefined') { return; } if (!column.visible) { return; } if (that.options.cardView && (!column.cardVisible)) { return; } if (column.escape) { value_ = escapeHTML(value_); } style = sprintf('style="%s"', csses.concat(that.header.styles[j]).join('; ')); // handle td's id and class 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 + '_colspan']) { colspan_ = sprintf(' colspan="%s"', item['_' + field + '_colspan']); } if (item['_' + field + '_title']) { title_ = sprintf(' title="%s"', item['_' + field + '_title']); } cellStyle = calculateObjectValue(that.header, that.header.cellStyles[j], [value_, item, i, field], 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('; ')); } value = calculateObjectValue(column, that.header.formatters[j], [value_, item, i], value_); if (item['_' + field + '_data'] && !$.isEmptyObject(item['_' + field + '_data'])) { $.each(item['_' + field + '_data'], function(k, v) { // ignore data-index 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 = [sprintf(that.options.cardView ? '<div class="card-view %s">' : '<td class="bs-checkbox %s">', column['class'] || ''), '<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 && 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; //渲染tree展开图标,下面text中添加了indent和icon。 var indent, icon; if (that.options.treeView && column.field == that.options.treeField) { var indent = item.level == that.options.treeRootLevel ? '' : 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); if(item.isLeaf){ icon = sprintf('<span class="tree-icon %s" style="cursor: pointer; margin: 0px 5px;"></span>', that.options.leafIcon); } } //end 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 %s>', id_, class_, style, data_, rowspan_, colspan_, title_), indent,icon, value, '</td>' ].join(''); // Hide empty data on Card view when smartDisplay is set to true. if (that.options.cardView && that.options.smartDisplay && value === '') { // Should set a placeholder for event binding correct fieldIndex text = '<div class="card-view"></div>'; } } html.push(text); });

        if (this.options.cardView) {
            html.push('</div></td>');
        }
        html.push('</tr>');

        return html.join(' ');
    };
    //重写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);
        }

        //Fix #389 Bootstrap-table-flatJSON is not working

        if (!this.options.pagination || this.options.sidePagination === 'server') {
            this.pageFrom = 1;
            this.pageTo = data.length;
        }

        var trFragments = $(document.createDocumentFragment());
        var hasTr;

        for (var i = this.pageFrom - 1; i < this.pageTo; i++) {
            var item = data[i];
            if (item.hidden) continue;//hidden属性,当前行不渲染
            var tr = this.initRow(item, i, data, trFragments);
            hasTr = hasTr || !!tr;
            if (tr&&tr!==true) { trFragments.append(tr); }
        }

        // show no records
        if (!hasTr) { trFragments.append('<tr class="no-records-found">' + sprintf('<td colspan="%s">%s</td>', this.$header.find('th').length, this.options.formatNoMatches()) + '</tr>'); }

        this.$body.html(trFragments);

        if (!fixedScroll) { this.scrollTo(0); }

        // click to select by column
        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, fields = that.getVisibleFields(), field = fields[that.options.detailView && !that.options.cardView ? index - 1 : index], column = that.columns[getFieldIndex(that.columns, field)], value = getItemField(item, field, that.options.escape); 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, field); // if click to select - then trigger the checkbox/radio click 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(); // #144: .trigger('click') bug } } });

        this.$body.find('> tr[data-index] > td > .detail-icon').off('click').on('click', function () { var $this = $(this), $tr = $this.parent().parent(), index = $tr.data('index'), row = data[index]; // Fix #980 Detail view, when searching, returns wrong row // remove and update if ($tr.next().is('tr.detail-view')) { $this.find('i').attr('class', sprintf('%s %s', that.options.iconsPrefix, that.options.icons.detailOpen)); that.trigger('collapse-row', index, row); $tr.next().remove(); } 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"></td></tr>', $tr.find('td').length)); var $element = $tr.next().find('td'); var content = calculateObjectValue(that.options, that.options.detailFormatter, [index, row, $element], ''); if($element.length === 1) { $element.append(content); } that.trigger('expand-row', index, row, $element); } that.resetView(); return false; });
        //treeicon点击事件
        this.$body.find('> tr[data-index] > td > .tree-icon').off('click').on('click', function (e) { e.stopPropagation(); var $this = $(this), $tr = $this.parent().parent(), index = $tr.data('index'), row = data[index]; var icon = $(this); if(icon.hasClass(that.options.expandIcon)){ //展开状态 icon.removeClass(that.options.expandIcon).addClass(that.options.collapseIcon); var child = getAllChild(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 = true; return; } }); }); }else{ icon.removeClass(that.options.collapseIcon).addClass(that.options.expandIcon); 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 = false; return; } }); }); } that.options.data = that.data; that.initBody(true); });
        //end

        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; } // fix bug, if events is defined with namespace 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:not(.no-records-found)').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', data);
    };


    /**
     * 展开所有树节点
     */
    BootstrapTable.prototype.expandAllTree = function ()
    { var that=this; var roots = $.grep(this.data, function (row, index) { return row.parentId == null; }); $.each(roots, function (index, item) { var child = getAllChild(item, that.options.data, that.options.treeId); $.each(child, function (i, n) { n.hidden=false; }); }); that.initBody(true); }
    /**
     * 闭合所有树节点
     */
    BootstrapTable.prototype.collapseAllTree = function ()
    { var that=this; var roots = $.grep(this.data, function (row, index) { return row.parentId == null; }); $.each(roots, function (index, item) { var child = getAllChild(item, that.options.data, that.options.treeId); $.each(child, function (i, n) { n.hidden=true; }); }); that.initBody(true); }

    //给组件增加默认参数列表
    $.extend($.fn.bootstrapTable.defaults, { treeView: false,//treeView视图 treeField: "id",//treeView视图字段 treeId: "id", treeRootLevel: 1,//根节点序号 treeCollapseAll: true,//是否全部展开,默认不展开 collapseIcon: "glyphicon glyphicon-chevron-right",//折叠样式 expandIcon: "glyphicon glyphicon-chevron-down",//展开样式 leafIcon:"glyphicon glyphicon-leaf"//叶子节点样式 });
    $.fn.bootstrapTable.methods.push('expandAllTree', 'collapseAllTree');
})(jQuery);

上边是已经封装好的tree,感谢某大神 原创出处:http://www.cnblogs.com/landeanfen/
我之前踩了不会少坑,因为毕设要做,所以自己摸索了下,这里写图片描述
看图,我使用后效果


这个是需要引入,这里通过ajax获取后台值,但需要转为json一次,看下边data

  <link href="../static/css/bootstrap-table.min.css" rel="stylesheet" type="text/css"/>
    <link href="../static/css/bootstrap.css" rel="stylesheet" type="text/css"/>
    <script type="text/javascript" src="../static/js/jquery-3.2.1.min.js"></script>
    <script type="text/javascript" src="../static/js/bootstrap.min.js"></script>
    <script type="text/javascript" src="../static/js/bootstrap-table.min.js"></script>
    <script type="text/javascript" src="../static/js/上边的.js"></script>

我现在分享出自己的代码,

var tree;
$(function () {
    var oTable = new TableInit();
    oTable.Init();
    $("#expandAllTree").on('click', function () {
        if ($("#expandAllTree").val() == '展开所有') {
            $('#tree_table').bootstrapTable("expandAllTree")//展开\
            $("#expandAllTree").val('折叠所有')
        } else {
            $('#tree_table').bootstrapTable("collapseAllTree")//折叠
            $("#expandAllTree").val('展开所有')
        }
    })

});


var TableInit = function () {

    var oTableInit = new Object();
    //初始化Table
    oTableInit.Init = function () {
        $.ajax({
            url: '/fault_list',
            method: 'get',
            dataType: 'json',
            async: false,
            success: function (data) {
                tree = JSON.stringify(data)
            }
        })
        $('#tree_table').bootstrapTable({
            class: 'table table-hover table-bordered',
            data: eval(tree),
            pagination: false,
            treeView: true,
            striped: true,   //是否各行渐变色
            bordered: true,  //是否显示边框
            treeId: "id",
            treeField: "events",
            columns: [{
                align: 'center',
                title: '编号',
                formatter: function (value, row, index) {
                    return index + 1;
                }
            }, {
                field: 'id',
                visible: false,
            }, {
                align: 'left',
                field: 'events',
                title: '事件',
                editable: {
                    type: 'text',
                    validate: function (data, value, row) {
                        if ($.trim(data) == '') return '请输入事件';
                    }
                }
            }, {
                align: 'center',
                field: 'type',
                title: '事件类型',
                // editable: {
                //     type: 'select',
                //     source: [{value: "T", text: "T"}, {value: "M", text: "M"}, {value: "X", text: "X"}]
                //
                // }
            }, {
                align: 'center',
                field: 'describe',
                title: '描述',
                editable: {
                    type: 'text',
                    validate: function (data) {
                        if ($.trim(data) == '') return '请输入描述';
                    }
                }
            }, {
                align: 'center',
                field: 'signature',
                title: '唯一特征码',
            }, {
                align: 'right',
                title: '操作',
                formatter: function (value, row) {
                    a = '<button class="btn btn-primary"  data-toggle="modal" data-target="#addeventModal"' +
                        ' onclick="update_top(' + row.id + ')" >添加子事件</button> '
                    if (row.type == 'X') {
                        a = ""
                    }
                    b = '<button class="btn btn-danger"   data-toggle="modal" data-target="#delModal"' +
                        ' onclick="delete_eve(' + row.id + ')"' +
                        ' >删除</button> '
                    return a + b
                }
            },
            ],
            onEditableSave: function (field, row, oldValue, $el) {
                $.ajax({
                    type: "POST",
                    url: "/fault_upd/",
                    data: row,
                    success: function (data) {
                        if (data == "ok") {
                            toastr.success('修改成功');
                        } else {
                            toastr.error('修改失败');
                        }
                    },
                    error: function () {
                        toastr.error('后台error');
                    },
                });
            }
        });
    }
    return oTableInit;
};

看看后台

这里写图片描述
后台使用python,django实现,前台要什么就传什么,自己要去处理下parentId,,它是根据parentId处理树的,而且树不支持分页的,其他也就没什么了,有问题可以评论
更多资源,更多文章由吾爱小白提供(是我啦)

您可能感兴趣的与本文相关的镜像

Python3.9

Python3.9

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值