# bootstrap table 重构 queryParams

queryParams:function(params){
   let filter = JSON.parse(params.filter);
   let op = JSON.parse(params.op);
   
   // 重构搜索条件
   filter['game.game_mode'] = 0;
   filter['game.play_mode'] = 0;
   op['game.game_mode'] = '=';
   op['game.play_mode'] = '=';
   params.filter = JSON.stringify(filter);
   params.op = JSON.stringify(op);
   console.log(params);
   return params;
}

# table 页脚统计和按钮复制到剪贴板等小功能

  1. 给 Table.api.init 内增加 showFooter:true 配置,开启页脚
  2. columns {field} 增加 footerFormatter:totalTextFormatter,作为统计表头
  3. 需要统计的列 添加代码 footerFormatter:function (rows){
    return sumFormatter(rows,this.field)
    }
  4. 添加 js 方法和监听滚动条代码 (如果有滚动条可使页脚跟随滚动条滚动)
  5. 进阶 通过监听 checkbox, 页面只统计勾选的数据,用到 checkData 和 table.bootstrapTable ('resetFooter')
define(['jquery', 'bootstrap', 'backend', 'table', 'form','layui'], function ($, undefined, Backend, Table, Form) {
    
    var Controller = {
        index: function () {
            // 初始化表格参数配置
            Table.api.init({
                showFooter:true,   // 此处配置项有效
                extend: {}
            });
            var table = $("#table");
            // 修改弹出框大小
            $('.btn-edit').data('area',["75%","75%"])
            // 初始化表格
            table.bootstrapTable({
                columns: [
                    [
                        {checkbox: true},
                        {field: 'id', title: __('Id')},
                       {field: 'operate', title: __('Operate'), table: table,
                          buttons: [
                             {
                                name: 'pwd',
                                text: __('Copy Account'),
                                icon: 'fa fa-copy',
                                classname: 'btn btn-xs btn-primary btn-ajax',
                                url: 'auth/admin/pwd?u={accname}',
                                success: function (data, ret) {
                                   let info = '后台地址:' + data.url + '\n账号:' + data.username + '\n密码:' +  data.pass;
                                   // 使用方法
                                   var ct =new copy_txt();
                                   ct.copy(info);// 把 "xxx" 复制到粘贴板
                                },
                                error: function (data, ret) {
                                   console.log(data, ret);
                                   Layer.alert(ret.msg);
                                   return false;
                                },
                                visible: function (row) {
                                   // 返回 true 时按钮显示,返回 false 隐藏
                                   if (row.account){
                                      return true;
                                   }else{
                                      return false;
                                   }
                                }
                             }
                          ], events: Table.api.events.operate, formatter: Table.api.formatter.operate},
                        {field: 'userName', title: __('Nickname'), operate: 'LIKE',footerFormatter:totalTextFormatter},
                        {field: 'balance', title: __('Balance'),operate:false,operate:false,footerFormatter:function(rows){
                                return sumFormatter(rows,this.field)
                            }},
                        {field: 'frozen', title: __('Frozen Balance'),operate:false,operate:false,footerFormatter:function(rows){
                                return sumFormatter(rows,this.field)
                            }},
                    ]
                ]
            });
            // 监听 checkbox
            table.on('check.bs.table uncheck.bs.table check-all.bs.table uncheck-all.bs.table post-body.bs.table', function (e) {
                checkData = Table.api.selecteddata(table);
                table.bootstrapTable('resetFooter');
            })
            // 可以隐藏某一个列
            if (!Config.show) {
                table.bootstrapTable('hideColumn','statmidday.platformFee');
                table.bootstrapTable('hideColumn','platformFee');
                table.bootstrapTable('hideColumn','frozenPlatformFee');
            }
            // 修改表格内的按钮
            table.on('post-body.bs.table',function () {
                $('.btn-editone').data('area',["75%","75%"])
            })
            var copy_txt=function(){// 无组件复制到剪贴板
                var _this =this;
                this.copy=function(txt){
                    $("#input_copy_txt_to_board").val(txt);// 赋值
                    $("#input_copy_txt_to_board").removeClass("hide");// 显示
                    $("#input_copy_txt_to_board").focus();// 取得焦点
                    $("#input_copy_txt_to_board").select();// 选择
                    document.execCommand("Copy");
                    $("#input_copy_txt_to_board").addClass("hide");// 隐藏
                }
                //-----------
                let html ='<textarea class="hide" id="input_copy_txt_to_board" /></textarea>';// 添加一个隐藏的元素 可换行
                //let html ='<input type class="hide" id="input_copy_txt_to_board" value="" />';// 添加一个隐藏的元素
                $("body").append(html);
            }
            // 监听滚动条
            $(".fixed-table-body").on("scroll",function(){
                var sl=this.scrollLeft;
                $(this).next()[0].scrollLeft = sl;
            })
            // 为表格绑定事件
            Table.api.bindevent(table);
        },
        // 页面弹出编辑框,提交修改后台刷新当前页面
        keepamount: function () {
            Form.api.bindevent("form[role='form']", function(res){
                parent.location.reload();// 这里刷新父页面,可以换其他代码
            });
        },
        api: {
            bindevent: function () {
                Form.api.bindevent($("form[role=form]"));
            }
        }
    };
    return Controller;
});
// 页脚统计 js begin
function totalTextFormatter() {
    return '总计:';
}
let checkData = [];
function sumFormatter(rows,value,format= false) {
    if (checkData.length) {
        rows = checkData;
    }
    var sum = 0;
    for (var i=0;i<rows.length;i++) {
        sum += Number(rows[i][value])
    }
    let result = 0;
    if (format){
        result = sum.toString();
    }else {
        result = toDecimal2(sum)
    }
    return result;
}
function toDecimal2(x) {
    var f = Math.round(x * 100) / 100;
    var s = f.toString();
    var rs = s.indexOf('.');
    if (rs < 0) {
        rs = s.length;
        s += '.';
    }
    while (s.length <= rs + 2) {
        s += '0';
    }
    return s;
}
// 页脚统计 js end

# 进阶 根据 checkbox 勾选,只页脚统计,勾选内容

通过 checkData 和 table.bootstrapTable ('resetFooter'); 实现

// 监听 checkbox
table.on('check.bs.table uncheck.bs.table check-all.bs.table uncheck-all.bs.table post-body.bs.table', function (e) {
    checkData = Table.api.selecteddata(table);
    table.bootstrapTable('resetFooter');
})
let checkData = [];
function sumFormatter(rows,value,format= false) {
    if (checkData.length) {
        rows = checkData;
    }
    var sum = 0;
    for (var i=0;i<rows.length;i++) {
        sum += Number(rows[i][value])
    }
    let result = 0;
    if (format){
        result = sum.toString();
    }else {
        result = toDecimal2(sum)
    }
    return result;
}

注意
public/assets/libs/bootstrap-table/dist/bootstrap-table.js 中的 resetFooter 默认不开放
需要修改代码 大致 3016 行,把 resetFooter ,追加写在末尾
var allowedMethods = [... 省略其他方法名,'resetFooter']
改好后需要压缩 js, 并且复制到同目录下的 bootstrap-table.min.js 中
最后执行
php think min -m backend -r all

# table 页脚 bug - 页脚统计与列无法对齐

看起来就像页脚,每个单元格都多了一个像素,整的对不齐
后来我就找到这个页脚生成的 js, 做了些修改
public/assets/libs/bootstrap-table/dist/bootstrap-table.js 差不多在 2288 行
下面代码 注释的就是原来的 上面那行是我修改的

$footerTd.eq(i).find(".fht-cell").width($this[0].getBoundingClientRect().width - 1)
//$footerTd.eq(i).find(".fht-cell").width($this.innerWidth())

改好后需要压缩 复制到同目录下的 bootstrap-table.min.js
在线压缩工具
所以最后还要执行
php think min -m backend -r all

# table 页脚 bug - 页脚和固定列 css 问题

页脚和固定列同时开启时,固定列会多一块,可以用 css 样式去掉

<style>
.fixed-table-container .fixed-columns {
        height: calc(100% - 116px) !important;
    }
</style>
#table 开启固定列
table.bootstrapTable({
                    url: $.fn.bootstrapTable.defaults.extend.index_url,
                    pk: 'id',
                    sortName: 'id',
                    searchFormVisible: true,
                    toolbar: '#toolbar',
                    fixedColumns: true, //开启固定列
                    fixedNumber: 2,     //左边列固定
                    #省略..
                    })

# public/assets/libs/bootstrap-table/dist/bootstrap-table.js

BootstrapTable.prototype.fitFooter = function () {
        //....
        //todo $this.innerWidth () 这个版本 js 无法获取精度数字 改好后压缩下 bootstrap-table.min.js
        this.$body.find('>tr:first-child:not(.no-records-found) > *').each(function (i) {
            var $this = $(this);
            $footerTd.eq(i).find(".fht-cell").width($this[0].getBoundingClientRect().width - 1)
            //$footerTd.eq(i).find(".fht-cell").width($this.innerWidth())
        });
    };

# tab 分页 多表 table

<div class="panel panel-default panel-intro">
    {:build_heading()}
    <ul class="nav nav-tabs main">
        {foreach name="typeList" item="vo" index="i"}
        <li {if $i==1}class="active"{/if}><a href="#{$key}" data-toggle="tab">{$vo}</a></li>
        {/foreach}
    </ul>
    <div class="panel-body">
        <div id="myTabContent" class="tab-content">
            <div class="tab-pane fade active in" id="one">
                <div class="widget-body no-padding">
                    <div id="toolbar" class="toolbar">
                        {:build_toolbar('refresh,add,delete')}
                    </div>
                    <table id="table" class="table table-striped table-bordered table-hover" 
                           data-operate-edit=false
                           width="100%">
                    </table>
                </div>
            </div>
            <div class="tab-pane fade" id="two">
                <div class="widget-body no-padding">
                    <div id="toolbar2" class="toolbar">
                        {:build_toolbar('refresh,add,delete')}
                    </div>
                    <table id="table2" class="table table-striped table-bordered table-hover"
                           data-operate-edit=false
                           width="100%">
                    </table>
                </div>
            </div>
        </div>
    </div>
</div>

对应 js

index: function () {
            Table.api.init();
            // 绑定事件
            $('.main a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
                var panel = $($(this).attr("href"));
                console.log(panel)
                if (panel.size() > 0) {
                    Controller.table[panel.attr("id")].call(this);
                    $(this).on('click', function (e) {
                        console.log(1)
                        $($(this).attr("href")).find(".btn-refresh").trigger("click");
                    });
                }
                // 移除绑定的事件
                $(this).unbind('shown.bs.tab');
            });
            // 必须默认触发 shown.bs.tab 事件
            $('ul.nav-tabs li.active a[data-toggle="tab"]').trigger("shown.bs.tab");
        },
        table: {
            one: function () {
                var table = $("#table");
                // 初始化表格
                table.bootstrapTable({
                    url: '',
                    extend: {
                        index_url: '',
                        add_url: '',
                        del_url: '',
                        table: '',
                    },
                    pk: 'id',
                    sortName: 'weigh',
                    toolbar:'#toolbar',
                    columns: [
                        [
                            {checkbox: true},
                            {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
                        ]
                    ],
                    sidePagination: "client", // 客户端分页
                });
                // 为表格绑定事件
                Table.api.bindevent(table);
            },
            two: function () {
                var table = $("#table2");
                // 初始化表格
                table.bootstrapTable({
                    url: '',
                    extend: {
                        index_url: '',
                        add_url: ',
                        del_url: '',
                        table: '',
                    },
                    pk: 'id',
                    sortName: 'weigh',
                    toolbar:'#toolbar2',
                    columns: [
                        [
                            {checkbox: true},
                            {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
                        ]
                    ],
                    sidePagination: "client", // 客户端分页
                });
                // 为表格绑定事件
                Table.api.bindevent(table);
            },
        },

# btn-group 按钮组 dropdown

当按钮太多时,把几个按钮合并到按钮组中

buttons: [
    {
        name: '',
        text: '',
        title: '',
        classname: 'btn btn-xs btn-info btn-dialog',
        icon: 'fa fa-list-alt',
        url: '',
        extend: 'data-area=\'["650px", "450px"]\'',
        dropdown:__('Operate'), // 需要放一起的按钮都加上 dropdown
        callback: function (data) {
        },
        visible: function (row) {
            // 返回 true 时按钮显示,返回 false 隐藏
            return true;
        }
    }
]

由于 btn-group 按钮组 显示的不太友好 自己瞎搞调整了下 css 样式

.dropdown-menu.pull-right {
        right: auto;
        top: auto;
        margin-top: 24px;
    }
    .dropdown-menu > li > a {
        text-align: left;
    }
    .btn-group {
        position: inherit;
        display: inline-flex;
    }

3-1

# btn-group 多个按钮组中间有个逗号

require-table.js html.unshift (dropdownHtml) 修改 html.unshift (dropdownHtml.join (' '))

if (!$.isEmptyObject(dropdowns)) {
                    var dropdownHtml = [];
                    $.each(dropdowns, function (i, j) {
                        dropdownHtml.push('<div class="btn-group"><button type="button" class="btn btn-primary dropdown-toggle btn-xs" data-toggle="dropdown">' + i + '</button><button type="button" class="btn btn-primary dropdown-toggle btn-xs" data-toggle="dropdown"><span class="caret"></span></button><ul class="dropdown-menu dropdown-menu-right"><li>' + j.join('</li><li>') + '</li></ul></div>');
                    });
                    html.unshift(dropdownHtml.join(' '));
                }
                return html.join(' ');

# 表格显示列,全选与全不选

参考链接
https://ask.fastadmin.net/question/9304.html

index: function () {
    // 初始化表格参数配置
    
    var table = $("#table");
    table.on('post-common-search.bs.table', function (e, settings, json, xhr) {
        $("ul.dropdown-menu", settings.$toolbar).prepend("<li role='menucheckall'><label><input type='checkbox' checked> 全选/反选</label></li>");
        $(document).on("click", "ul.dropdown-menu li[role='menucheckall'] input", function (e) {
            e.stopPropagation();
            var that = settings;
            var checked = $(this).prop('checked');
            $("ul.dropdown-menu li[role='menuitem'] input").each(function () {
                that.toggleColumn($(this).val(), checked, false);
                that.trigger('column-switch', $(this).data('field'), checked);
                $(this).prop("checked", checked);
            });
        });
    });
            // 为表格绑定事件
    Table.api.bindevent(table);
}

# 单日期 day 自定义搜索

bootstrap table 的搜索样式 单个日期搜索
参考链接
https://ask.fastadmin.net/question/28226.html

[
    {field: 'day', title: __('Day'), addclass:'datetimepicker', data: 'data-date-format="YYYYMMDD"'},
]

显示有 bug

.bootstrap-table .form-commonsearch .form-group {
    white-space: normal;
}

# bulid_checkboxs 增加全选 / 反选

bulid_checkboxs 构建的多选框 没有全选 / 反选
这里通过在数组开头增加 ['' => ' 全选 / 反选 ']
bulid_checkboxs ('status []',['' => ' 全选 / 反选 ',... 其他元素])
再配合 js 达成 全选 / 反选的效果

// 初始化 checkbox 是否勾选全选 / 反选
            function init(name) {
                let elem = $('input[name="' + name + '"]');
                // 当前列 checkbox 除了全选 / 反选的 元素数量
                let len = elem.length - 1;
                let chk = $('input[name="' + name + '"]:checked');
                let chklen = chk.length;
                // 排除全选
                if (elem.first().prop("checked")) {
                    chklen = chk.length - 1;
                }
                
                if (len == chklen && chklen != 0){
                    elem.first().prop("checked",true);
                }else {
                    elem.first().prop("checked",false);
                }
            }
            
            init('status');
            $(":checkbox").on('click',function(){
                // 获取当前 name
                let name = $(this).attr('name');
                let elem = $('input[name="' + name + '"]');
                // 当前列 checkbox 除了全选 / 反选的 元素数量
                let len = elem.length - 1;
                // 判断全选 / 反选
                let checked = $(this).prop('checked');
                let key = $(this).val();
                if (key == '') {
                    if (checked) {
                        elem.prop("checked",true);
                    } else {
                        elem.prop("checked",false);
                    }
                }
                // 如果有一个未选中则取消全选按钮的选中状态 / 全都选择设置全选按钮为选中状态
                let chk = $('input[name="' + name + '"]:checked');
                let chklen = chk.length;
                // 排除全选
                if (elem.first().prop("checked")) {
                    chklen = chk.length - 1;
                }
                if (len == chklen && chklen != 0){
                    elem.first().prop("checked",true);
                }else {
                    elem.first().prop("checked",false);
                }
            })

如果不选 checkbox ,php 后台接收参数不是 [] 而是 [0 => '']
可用 array_filter 过滤
status=arrayfilter(status = array_filter(status);

# 实用的表格列表字段显示优化,采用 cookie 记忆隐藏字段功能。

参考链接
https://ask.fastadmin.net/article/9464.html

define(['jquery', 'bootstrap', 'backend', 'table', 'form','cookie'], function ($, undefined, Backend, Table, Form) {
    var Controller = {
        index: function () {
            // 初始化表格参数配置
    
            var table = $("#table");
    
            // 选择隐藏字段事件,使用cookie存入
            table.on('column-switch.bs.table', function (e, json) {
                var myColumns = [];
                $.each(table.bootstrapTable('getHiddenColumns'), function (i, item) {
                    myColumns.push(item.field);
                });
                let cookieName = 'fa_<!--swig0-->' //fa_\{%table%\} 反斜杆去掉
                $.cookie(cookieName, JSON.stringify(myColumns), {expires: 365, path: '/'});
            });
            if ($.cookie(cookieName)) {
                //读取cookie隐藏字段
                $.each(JSON.parse($.cookie(cookieName)), function (i, item) {
                    table.bootstrapTable('hideColumn', item);
                });
            }
            // 为表格绑定事件
            Table.api.bindevent(table);
        }
    }
})

# 记录当前窗体内的确认框触发

$('.btn-release').on('click',function () {
                event.preventDefault();
                Layer.confirm(__('Confirm Release?'), {
                    title: __('Confirm Release?'),
                }, function (index) {
                    Fast.api.ajax({
                        url: "",
                    }, function (data) {
                        Layer.closeAll('dialog'); // 关闭确认框
                        Fast.api.close(1); // 关闭窗体并回传数据
                        parent.$(".btn-refresh").trigger("click"); // 触发窗体的父页面刷新
                    },function () {
                    });
                }, function (index) {
                });
            });

# 自定义搜索 (下拉框搜索)

fastAdmin 有个开发实例的插件,包括一些实用的技巧,如下

var table = $("#table");
            // 在普通搜索渲染后
            table.on('post-common-search.bs.table', function (event, table) {
                var form = $("form", table.$commonsearch);
                $("input[name='bankID']", form).addClass("selectpage").data("source", "bank/bank/index").data("primaryKey", "id").data("field", "account").data("orderBy", "id desc");
                Form.events.cxselect(form);
                Form.events.selectpage(form);
            });
            let col = [
                {checkbox: true},
                {field: 'id', title: __('Id')},
                {field: 'bankID', title: __('Account'),visible: false},
                {field: 'bank.account', title: __('Account'), operate: false},
            ]

# 固定表格高度

<table data-height="500"></table>

table.bootstrapTable({
    ...
    height: 500,
    ...
})

# 添加表格显示列的全选反选

// 添加表格显示列的全选反选
            table.on('post-common-search.bs.table', function (e, settings, json, xhr) {
                $("ul.dropdown-menu", settings.$toolbar).prepend("<li role='menucheckall'><label><input type='checkbox' checked> 全选/反选</label></li>");
                $(document).on("click", "ul.dropdown-menu li[role='menucheckall'] input", function (e) {
                    e.stopPropagation();
                    var that = settings;
                    var checked = $(this).prop('checked');
                    $("ul.dropdown-menu li[role='menuitem'] input").each(function () {
                        that.toggleColumn($(this).val(), checked, false);
                        that.trigger('column-switch', $(this).data('field'), checked);
                        $(this).prop("checked", checked);
                    });
                });
            });

# 表格导出插件,日期自动转换问题

参考地址 https://www.cnblogs.com/darkestinblack/p/12557433.html

exportOptions: {
            // 省略
            mso: {
            //cell td 实例
            //row td 所在行
            //col td 所在列
            onMsoNumberFormat: function (cell, row, col) {
                return '\\@';
            }
        },
    },

# 非 weigh 字段如何排序

对应 js

// 初始化表格参数配置
            Table.api.init({
                showFooter:true,   // 此处配置项有效
                extend: {
                    // 添加排序 url
                    dragsort_url: 'ajax/weigh',
                    ...
                }
            });
            // 重新定义排序字段
            Table.config.dragsortfield = 'sort';

修改 application/admin/controller/Ajax.php 中的 weigh 方法

/**
     * 通用排序
     */
    public function weigh()
    {
        ...
        // 限制更新的字段
        $field = in_array($field, ['weigh','sort']) ? $field : 'weigh';
    }

# 搜索加载完成触发 tab 状态分页,并查询状态

table.on('post-common-search.bs.table', function (event, table) {
                $('ul.nav-tabs li a[data-value="0"]').trigger('click');
                $(".form-commonsearch select[name=status]").val("0");
});

# 表格列样式 cellStyle

{field: 'field', title: __('field'),operate:false,cellStyle: function (value, row, index) {
                                return {css: {"background-color": "rgb(24,188,156)"}};
                            }},

# 表格公共搜索,带默认值,隐藏对应搜索框

// 在普通搜索渲染后
            table.on('post-common-search.bs.table', function (event, table) {
                var form = $("form", table.$commonsearch);
                var arr = ['day','type'];
                for (let item of table.columns) {
                    if (arr.indexOf(item.field) >= 0 && item.defaultValue.length) {
                        $("input[name='" + item.field + "']", form).parent().parent().addClass('hide');
                    }
                }
            });