ソースを参照

Add Tree Dispaly Column

Ywk 9 年 前
コミット
04ae8a87e9

+ 1 - 0
.gitignore

@@ -29,3 +29,4 @@ nbproject
 .DS_Store
 .DS_Store
 .localized
 .localized
 Icon
 Icon
+.vscode

+ 7 - 0
docs/_i18n/en/documentation/table-options.md

@@ -144,6 +144,13 @@ The table options are defined in `jQuery.fn.bootstrapTable.defaults`.
         <td>Key in incoming json containing rows data list.</td>
         <td>Key in incoming json containing rows data list.</td>
     </tr>
     </tr>
     <tr>
     <tr>
+        <td>totalField</td>
+        <td>data-total-field</td>
+        <td>String</td>
+        <td>'total'</td>
+        <td>Key in incoming json containing  "total" data .</td>
+    </tr>
+    <tr>
         <td>ajax</td>
         <td>ajax</td>
         <td>data-ajax</td>
         <td>data-ajax</td>
         <td>Function</td>
         <td>Function</td>

+ 179 - 165
src/bootstrap-table.js

@@ -1596,57 +1596,131 @@
         this.updatePagination(event);
         this.updatePagination(event);
     };
     };
 
 
-    BootstrapTable.prototype.initBody = function (fixedScroll) {
-        var that = this,
+    BootstrapTable.prototype.initRow = function(item, i, data, parentDom) {
+        var that=this,
+            key,
             html = [],
             html = [],
-            data = this.getData();
+            style = {},
+            csses = [],
+            data_ = '',
+            attributes = {},
+            htmlAttributes = [];
 
 
-        this.trigger('pre-body', data);
+        if ($.inArray(item, this.hiddenRows) > -1) {
+            return;
+        }
 
 
-        this.$body = this.$el.find('>tbody');
-        if (!this.$body.length) {
-            this.$body = $('<tbody></tbody>').appendTo(this.$el);
+        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]);
+            }
         }
         }
 
 
-        //Fix #389 Bootstrap-table-flatJSON is not working
+        attributes = calculateObjectValue(this.options,
+            this.options.rowAttributes, [item, i], attributes);
 
 
-        if (!this.options.pagination || this.options.sidePagination === 'server') {
-            this.pageFrom = 1;
-            this.pageTo = data.length;
+        if (attributes) {
+            for (key in attributes) {
+                htmlAttributes.push(sprintf('%s="%s"', key, escapeHTML(attributes[key])));
+            }
         }
         }
 
 
-        for (var i = this.pageFrom - 1; i < this.pageTo; i++) {
-            var key,
-                item = data[i],
-                style = {},
-                csses = [],
+        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="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, that.options.escape),
+                value = '',
+                type = '',
+                cellStyle = {},
+                id_ = '',
+                class_ = that.header.classes[j],
                 data_ = '',
                 data_ = '',
-                attributes = {},
-                htmlAttributes = [];
+                rowspan_ = '',
+                colspan_ = '',
+                title_ = '',
+                column = that.columns[j];
 
 
-            if ($.inArray(item, this.hiddenRows) > -1) {
-                continue;
+            if (that.fromHtml && typeof value_ === 'undefined') {
+                return;
             }
             }
 
 
-            style = calculateObjectValue(this.options, this.options.rowStyle, [item, i], style);
+            if (!column.visible) {
+                return;
+            }
 
 
-            if (style && style.css) {
-                for (key in style.css) {
-                    csses.push(key + ': ' + style.css[key]);
-                }
+            if (that.options.cardView && (!column.cardVisible)) {
+                return;
             }
             }
 
 
-            attributes = calculateObjectValue(this.options,
-                this.options.rowAttributes, [item, i], attributes);
+            style = sprintf('style="%s"', csses.concat(that.header.styles[j]).join('; '));
 
 
-            if (attributes) {
-                for (key in attributes) {
-                    htmlAttributes.push(sprintf('%s="%s"', key, escapeHTML(attributes[key])));
+            // 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('; '));
             }
             }
 
 
-            if (item._data && !$.isEmptyObject(item._data)) {
-                $.each(item._data, function (k, v) {
+            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
                     // ignore data-index
                     if (k === 'index') {
                     if (k === 'index') {
                         return;
                         return;
@@ -1655,160 +1729,100 @@
                 });
                 });
             }
             }
 
 
-            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 (column.checkbox || column.radio) {
+                type = column.checkbox ? 'checkbox' : type;
+                type = column.radio ? 'radio' : type;
 
 
-            if (this.options.cardView) {
-                html.push(sprintf('<td colspan="%s"><div class="card-views">', this.header.fields.length));
-            }
+                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('');
 
 
-            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>');
-            }
+                item[that.header.stateField] = value === true || (value && value.checked);
+            } else {
+                value = typeof value === 'undefined' || value === null ?
+                    that.options.undefinedText : value;
+
+                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_),
+                    value,
+                    '</td>'
+                ].join('');
 
 
-            $.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;
+                // 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>';
                 }
                 }
+            }
 
 
-                if (!column.visible) {
-                    return;
-                }
+            html.push(text);
+        });
 
 
-                if (that.options.cardView && !column.cardVisible) {
-                    return;
-                }
+        if (this.options.cardView) {
+            html.push('</div></td>');
+        }
+        html.push('</tr>');
 
 
-                style = sprintf('style="%s"', csses.concat(that.header.styles[j]).join('; '));
+        return html.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('; '));
-                }
+    BootstrapTable.prototype.initBody = function (fixedScroll) {
+        var that = this,
+            html = [],
+            data = this.getData();
 
 
-                value = calculateObjectValue(column,
-                    that.header.formatters[j], [value_, item, i], value_);
+        this.trigger('pre-body', data);
 
 
-                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);
-                    });
-                }
+        this.$body = this.$el.find('>tbody');
+        if (!this.$body.length) {
+            this.$body = $('<tbody></tbody>').appendTo(this.$el);
+        }
 
 
-                if (column.checkbox || column.radio) {
-                    type = column.checkbox ? 'checkbox' : type;
-                    type = column.radio ? 'radio' : type;
+        //Fix #389 Bootstrap-table-flatJSON is not working
 
 
-                    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;
-
-                    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_),
-                        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>';
-                    }
-                }
+        if (!this.options.pagination || this.options.sidePagination === 'server') {
+            this.pageFrom = 1;
+            this.pageTo = data.length;
+        }
 
 
-                html.push(text);
-            });
+        var trFragments = $(document.createDocumentFragment());
+        var hasTr;
 
 
-            if (this.options.cardView) {
-                html.push('</div></td>');
+        for (var i = this.pageFrom - 1; i < this.pageTo; i++) {
+            var item = data[i];
+            var tr = this.initRow(item, i, data, trFragments);
+            hasTr = hasTr || !!tr;
+            if (tr&&tr!==true) {
+                trFragments.append(tr);
             }
             }
-
-            html.push('</tr>');
         }
         }
 
 
         // show no records
         // show no records
-        if (!html.length) {
-            html.push('<tr class="no-records-found">',
-                sprintf('<td colspan="%s">%s</td>',
-                    this.$header.find('th').length, this.options.formatNoMatches()),
+        if (!hasTr) {
+            trFragments.append('<tr class="no-records-found">'+
+                sprintf('<td colspan="%s">%s</td>'+
+                    this.$header.find('th').length, this.options.formatNoMatches())+
                 '</tr>');
                 '</tr>');
         }
         }
 
 
-        this.$body.html(html.join(''));
+        this.$body.html(trFragments);
 
 
         if (!fixedScroll) {
         if (!fixedScroll) {
             this.scrollTo(0);
             this.scrollTo(0);

ファイルの差分が大きいため隠しています
+ 1 - 0
src/extensions/tree-column/bootstrap-table-tree-column.css


+ 130 - 0
src/extensions/tree-column/bootstrap-table-tree-column.js

@@ -0,0 +1,130 @@
+/**
+ * @author: KingYang
+ * @webSite: https://github.com/kingyang
+ * @version: v1.0.0
+ */
+
+! function ($) {
+
+    'use strict';
+
+    $.extend($.fn.bootstrapTable.defaults, {
+        treeShowField: null,
+        idField: 'id',
+        parentIdField: 'pid',
+        treeVerticalcls: 'vertical',
+        treeVerticalLastcls: 'vertical last',
+        treeSpacecls: 'space',
+        treeNodecls: 'node',
+        treeCellcls: 'treenode',
+        treeTextcls: 'text',
+        onTreeFormatter: function (row) {
+            var that = this,
+                options = that.options,
+                level = row._level || 0,
+                plevel = row._parent && row._parent._level || 0,
+                paddings = [];
+            for (var i = 0; i < plevel; i++) {
+                paddings.push('<i class="' + options.treeVerticalcls + '"></i>');
+                paddings.push('<i class="' + options.treeSpacecls + '"></i>');
+            }
+
+            for (var i = plevel; i < level; i++) {
+                if (row._last && i === (level - 1)) {
+                    paddings.push('<i class="' + options.treeVerticalLastcls + '"></i>');
+                } else {
+                    paddings.push('<i class="' + options.treeVerticalcls + '"></i>');
+                }
+                paddings.push('<i class="' + options.treeNodecls + '"></i>');
+            }
+            return paddings.join('');
+        }, onGetNodes: function (row, data) {
+            var that = this;
+            var nodes = [];
+            $.each(data, function (i, item) {
+                if (row[that.options.idField] === item[that.options.parentIdField]) {
+                    nodes.push(item);
+                }
+            });
+            return nodes;
+        },
+        onCheckLeaf: function (row, data) {
+            if (row.isLeaf !== undefined) {
+                return row.isLeaf;
+            }
+            return !row._nodes || !row._nodes.length;
+        }, onCheckRoot: function (row, data) {
+            var that = this;
+            return !row[that.options.parentIdField];
+        }
+    });
+
+    var BootstrapTable = $.fn.bootstrapTable.Constructor,
+        _initRow = BootstrapTable.prototype.initRow,
+        _initHeader = BootstrapTable.prototype.initHeader;
+
+    BootstrapTable.prototype.initHeader = function () {
+        var that = this;
+        _initHeader.apply(that, Array.prototype.slice.apply(arguments));
+        var treeShowField = that.options.treeShowField;
+        if (treeShowField) {
+            $.each(this.header.fields, function (i, field) {
+                if (treeShowField === field) {
+                    that.treeEnable = true;
+                    var _formatter = that.header.formatters[i];
+                    var _class = [that.options.treeCellcls];
+                    if (that.header.classes[i]) {
+                        _class.push(that.header.classes[i].split('"')[1] || '');
+                    }
+                    that.header.classes[i] = 'class="' + _class.join(' ') + '"';
+                    that.header.formatters[i] = function (value, row, index) {
+                        var colTree = [that.options.onTreeFormatter.apply(that, [row])];
+                        colTree.push('<span class="' + that.options.treeTextcls + '">');
+                        if (_formatter) {
+                            colTree.push(_formatter.apply(this, Array.prototype.slice.apply(arguments)));
+                        } else {
+                            colTree.push(value);
+                        }
+                        colTree.push('</span>');
+                        return colTree.join('');
+                    };
+                    return false;
+                }
+            });
+        }
+    };
+
+    var initNode = function (item, idx, data, parentDom) {
+        var that = this;
+        var nodes = that.options.onGetNodes.apply(that, [item, data]);
+        item._nodes = nodes;
+        parentDom.append(_initRow.apply(that, [item, idx, data, parentDom]));
+        var len = nodes.length - 1;
+        for (var i = 0; i <= len; i++) {
+            var node = nodes[i];
+            node._level = item._level + 1;
+            node._parent = item;
+            if (i === len)
+                node._last = 1;
+            initNode.apply(that, [node, $.inArray(node, data), data, parentDom]);
+        }
+    };
+
+
+    BootstrapTable.prototype.initRow = function (item, idx, data, parentDom) {
+        var that = this;
+        if (that.treeEnable) {
+            if (that.options.onCheckRoot.apply(that, [item, data])) {
+                if (item._level === undefined) {
+                    item._level = 0;
+                }
+                initNode.apply(that, [item, idx, data, parentDom]);
+                return true;
+            }
+            return false;
+
+        }
+        return _initRow.apply(that, Array.prototype.slice.apply(arguments));
+    };
+
+} (jQuery);

+ 43 - 0
src/extensions/tree-column/bootstrap-table-tree-column.less

@@ -0,0 +1,43 @@
+ .table:not(.table-condensed) > tbody > tr > td.treenode {
+     padding-top: 0;
+     padding-bottom: 0;
+     border-bottom: solid #fff 1px;
+ }
+ 
+ .table:not(.table-condensed) > tbody > tr:last-child > td.treenode {
+     border-bottom: none;
+ }
+ 
+ .treenode {
+     .text {
+         float: left;
+         display: block;
+         padding-top: 6px;
+         padding-bottom: 6px;
+     }
+     .vertical,
+     .vertical.last {
+         float: left;
+         display: block;
+         width: 1px;
+         border-left: dashed silver 1px;
+         height: 38px;
+         margin-left: 8px;
+     }
+     .vertical.last {
+         height: 15px;
+     }
+     
+     .space,
+     .node {
+         float: left;
+         display: block;
+         width: 15px;
+         height: 5px;
+         margin-top: 15px;
+     }
+     
+     .node {
+         border-top: dashed silver 1px;
+     }
+ }

+ 17 - 0
src/extensions/tree-column/extension.json

@@ -0,0 +1,17 @@
+{
+  "name": "Tree column",
+  "version": "1.0.0",
+  "description": "Plugin to  support display tree data column.",
+  "url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/tree-column",
+  "example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/tree-column.html",
+
+  "plugins": [{
+    "name": "bootstrap-table-reorder-rows",
+    "url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/tree-column"
+  }],
+
+  "author": {
+    "name": "KingYang",
+    "image": "https://avatars3.githubusercontent.com/u/1540211"
+  }
+}

BIN
src/extensions/tree-column/icon.png