• Bootstrap学习-实现Table 表格行拖拽效果
  • 一毛哥 发表于 2016/2/22 15:24:00 | 分类标签: Bootstrap学习 table
  •  一、业务需求及实现效果

    项目涉及到订单模块,那天突然接到一个需求,说是两种不同状态的订单之间要实现插单的效果,页面上呈现方式是:左右两个Table,左边Table里面是状态为1的订单,右边Table里面是状态为2订单,左边Table里面的行数据拖动到右边Table里面指定行的位置,拖动完成后,左边表格减少一行,右边表格增加一行。除此之外,还需要撤销操作(相当于Ctrl + Z操作),能够返回到上一步的状态。

    二、代码示例

    初初接到需求,博主的第一感觉是应该上Bootstrap table api里面找一下,毕竟开源的力量是强大的,或许有相关的示例呢。经过一番查找,很可惜,Bootstrap Table没有这种两张表格之间的操作。想想其实也可以理解,Bootstrap Table是针对某个动态表格数据绑定的,它的侧重点是表格内部的功能,比如表格内部行的拖拽排序(Reorder Rows)有很好的解决方案,对于像博主这样的特殊需求,似乎也应该自己去实现。

    1、需求分析

    既然决定自己去写,博主开始分析需求,似乎这个操作里面比较困难的是拖拽效果,说到拖拽效果,原来使用JsPlumb的时候那使用太多了,于是就想到了我们神奇的JQuery UI里面的draggable.js 和droppable.js。拖拽的问题解决了,那么还有一个难点,就是撤销操作怎么办?我们知道Ctrl+z的意思是还原,什么叫还原?就是返回到上一步的操作,那么前提是要能够保存上一步的状态,说到保存某一步的状态,博主就知道怎么做了,需要一个全局变量Json,里面要有三个键值对,分别是当前步骤的索引、左边表格的数据、右边表格的数据。似乎也不太难嘛,博主就此着手,开干。

    2、代码示例

     2.1 cshtml页面代码
    <html>
    <head>
        <meta name="viewport" content="width=device-width" />
        <title>@ViewBag.Title</title>
        @Styles.Render("~/Content/css")
        @Styles.Render("~/Content/table-css")
        @Scripts.Render("~/bundles/jquery")
        @Scripts.Render("~/bundles/knockout")
        @Scripts.Render("~/bundles/bootstrap")
        @Scripts.Render("~/bundles/bootstrap-table")
        @RenderSection("Scripts", false)
    </head>
    <body>
        @RenderBody()
    </body>
    </html>
    @{
        ViewBag.Title = "订单插单";
        Layout = "~/Views/Shared/_Layout.cshtml";
    }
    
    @Scripts.Render("~/bundles/Order/InsertOrder")
    @Styles.Render("~/bundles/Order/css")
    @Scripts.Render("~/Content/bootstrap/datepicker/js")
    @Styles.Render("~/Content/bootstrap/datepicker/css")
    
    <script src="~/Content/jquery-ui-1.11.4.custom/jquery-ui.min.js"></script>
    
    <div class="panel-body" style="padding-bottom:0px;">
        
            <div class="panel panel-default" style="margin-bottom:0px;">
                <div class="panel-heading">查询条件</div>
                <div class="panel-body container-fluid">
                    <div class="row">
                        <div class="col-md-3">
                            <label for="txt_search_ordernumber" class="col-sm-4 control-label" style="margin-top:6px;">订单号</label>
                            <span class="col-sm-8">
                                <input type="text" class="form-control" id="txt_search_ordernumber">
                            </span>
                        </div>
                        <div class="col-md-3">
                            <label for="txt_search_bodynumber" class="col-sm-3 control-label" style="margin-top:6px;">车身号</label>
                            <span class="col-sm-8">
                                <input type="text" class="form-control" id="txt_search_bodynumber">
                            </span>
                        </div>
                        <div class="col-md-3">
                            <label for="txt_search_vinnumber" class="col-sm-4 control-label" style="margin-top:6px;">VIN码</label>
                            <span class="col-sm-8">
                                <input type="text" class="form-control" id="txt_search_vinnumber">
                            </span>
                        </div>
                        <div class="col-md-3">
                            <label for="txt_search_engin_code" class="col-sm-4 control-label" style="margin-top:6px;">发动机号</label>
                            <span class="col-sm-8">
                                <input type="text" class="form-control" id="txt_search_engin_code">
                            </span>
                        </div>
                    </div>
                    <div class="collapse" id="div_more_search">
                        <div class="row" style="margin-top:15px;">
                            <div class="col-md-3">
                                <label for="txt_search_import_startdate" class="col-sm-4 control-label" style="margin-top:6px;">导入时间</label>
                                <span class="col-sm-8">
                                    <input type="text" class="form-control datetimepicker" readonly id="txt_search_import_startdate">
                                </span>
                            </div>
                            <div class="col-md-3">
                                <label for="txt_search_import_enddate" class="col-sm-3 control-label" style="margin-top:6px;"></label>
                                <span class="col-sm-8">
                                    <input type="text" class="form-control datetimepicker" readonly id="txt_search_import_enddate">
                                </span>
                            </div>
                            <div class="col-md-3">
                                <label for="txt_search_send_startdate" class="col-sm-4 control-label" style="margin-top:6px;">下发时间</label>
                                <span class="col-sm-8">
                                    <input type="text" class="form-control datetimepicker" readonly id="txt_search_send_startdate">
                                </span>
                            </div>
                            <div class="col-md-3">
                                <label for="txt_search_send_enddate" class="col-sm-4 control-label" style="margin-top:6px;"></label>
                                <span class="col-sm-8">
                                    <input type="text" class="form-control datetimepicker" readonly id="txt_search_send_enddate">
                                </span>
                            </div>
                        </div>
    
                        <div class="row" style="margin-top:15px;">
                            <div class="col-md-3">
                                <label for="txt_search_carcode" class="col-sm-4 control-label" style="margin-top:6px;">整车编码</label>
                                <span class="col-sm-8">
                                    <input type="text" class="form-control" id="txt_search_carcode">
                                </span>
                            </div>
                            <div class="col-md-3">
                                <label for="txt_search_vms" class="col-sm-3 control-label" style="margin-top:6px;">VMS号</label>
                                <span class="col-sm-8">
                                    <input type="text" class="form-control" id="txt_search_vms">
                                </span>
                            </div>
                            <div class="col-md-3">
                                <label for="txt_search_trans_code" class="col-sm-4 control-label" style="margin-top:6px;">变速箱号</label>
                                <span class="col-sm-8">
                                    <input type="text" class="form-control" id="txt_search_trans_code">
                                </span>
                            </div>
                        </div>
                    </div>
    
                        <div class="row" style="float:right;margin-right:50px;margin-top:13px;">
                            <div>
                                <button type="button" id="btn_query" class="btn btn-primary" style="margin-right:20px;width:100px;">查询</button>
                                <button type="submit" id="btn_reset" class="btn btn-default" style="margin-right:20px;width:100px;">重置</button>
                            </div>
    
                        </div>
                    </div>
            </div>
    
        <div class="collapse_div_outside">
            <div class="collapse_div_inside"></div>
            <span id="span_collapse" href="#div_more_search" class="collapse_div_inside_ele">展开<label class="glyphicon glyphicon-menu-down"></label></span>
        </div>
    </div>
    
    @*<div id="toolbar_left" class="btn-group">
    </div>*@
    <div id="toolbar_right" class="btn-group">
        <button id="btn_cancel" type="button" class="btn btn-default">
            <span class="glyphicon glyphicon-backward aria-hidden="true"></span>撤销
        </button>
        <button id="btn_insertorder" type="button" class="btn btn-default">
            <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>插单
        </button>
    </div>
    <div class="panel-body" style="padding-top:0px;">
        <div id="div_tableleft" class="col-md-6">
            <table id="tb_order_left"></table>
        </div>
        <div id="div_tableright" class="col-md-6">
            <table id="tb_order_right"></table>
        </div>
    </div>

     2.2 js代码

    var i_statuindex = 0;
    //此数组用于保存撤销操作每一步的数据
    var arrdata = [];
    
    var m_oTable = null;
    
    $(function () {
        //1.初始化表格
        m_oTable = new TableInit();
        m_oTable.Init();
    
        //2.初始化按钮事件
        var oButtonInit = new ButtonInit();
        oButtonInit.Init();
    
        //3.日期控件的初始化
        $(".datetimepicker").datetimepicker({
            format: 'yyyy-mm-dd hh:ii',
            autoclose: true,
            todayBtn: true,
        });
    
    });
    
    //表格相关事件和方法
    var TableInit = function () {
        var oTableInit = new Object();
    
        oTableInit.Init = function () {
         //初始化左边表格
            $('#tb_order_left').bootstrapTable({
                url: '/api/OrderApi/get',
                method: 'get',
                striped: true,
                cache: false,
                striped: true,
                pagination: true,
                height: 600,
                uniqueId:"TO_ORDER_ID",
                queryParams: oTableInit.queryParams,
                queryParamsType: "limit",
                sidePagination: "server",
                pageSize: 10,
                pageList: [10, 25, 50, 100],
                search: true,
                strictSearch: true,
                showColumns: true,
                showRefresh: true,
                minimumCountColumns: 2,
                clickToSelect: true,
                columns: [{
                    checkbox: true
                },
                {
                    field: 'ORDER_NO',
                    title: '订单号'
                },
                {
                    field: 'BODY_NO',
                    title: '车身号'
                }, {
                    field: 'VIN',
                    title: 'VIN码'
                }, {
                    field: 'TM_MODEL_MATERIAL_ID',
                    title: '整车编码'
                },
                {
                    field: 'ORDER_TYPE',
                    title: '订单类型'
                },
                {
                    field: 'ORDER_STATUS',
                    title: '订单状态'
                },
                {
                    field: 'CREATE_DATE',
                    title: '订单导入时间'
                },
                {
                    field: 'PLAN_DATE',
                    title: '订单计划上线日期'
                },
                {
                    field: 'VMS_NO',
                    title: 'VMS号'
                },
                {
                    field: 'ENGIN_CODE',
                    title: '发动机号'
                },
                {
                    field: 'TRANS_CODE',
                    title: '变速箱号'
                },
                {
                    field: 'OFFLINE_DATE_ACT',
                    title: '实际下线日期'
                },
                {
                    field: 'HOLD_RES',
                    title: 'hold理由'
                },
                {
                    field: 'SPC_FLAG',
                    title: '特殊标记'
                },
                ],
                onLoadSuccess: function (data) {
                     //表格加载完成之后初始化拖拽
              oTableInit.InitDrag();
                }
            });
         //初始化右边表格
            $('#tb_order_right').bootstrapTable({
                url: '/api/OrderApi/get',
                method: 'get',
                toolbar: '#toolbar_right',
                striped: true,
                cache: false,
                striped: true,
                pagination: true,
                height: 600,
                queryParams: oTableInit.queryParamsRight,
                queryParamsType: "limit",
                //ajaxOptions: { departmentname: "", statu: "" },
                sidePagination: "server",
                pageSize: 10,
                pageList: [10, 25, 50, 100],
                search: true,
                strictSearch: true,
                showRefresh: true,
                minimumCountColumns: 2,
                columns: [
                {
                    field: 'ORDER_NO',
                    title: '订单号'
                },
                {
                    field: 'BODY_NO',
                    title: '车身号'
                }, {
                    field: 'VIN',
                    title: 'VIN码'
                }, {
                    field: 'TM_MODEL_MATERIAL_ID',
                    title: '整车编码'
                },
                {
                    field: 'ORDER_TYPE',
                    title: '订单类型'
                },
                {
                    field: 'ORDER_STATUS',
                    title: '订单状态'
                },
                {
                    field: 'CREATE_DATE',
                    title: '订单导入时间'
                },
                {
                    field: 'PLAN_DATE',
                    title: '订单计划上线日期'
                },
                {
                    field: 'VMS_NO',
                    title: 'VMS号'
                },
                {
                    field: 'ENGIN_CODE',
                    title: '发动机号'
                },
                {
                    field: 'TRANS_CODE',
                    title: '变速箱号'
                },
                {
                    field: 'OFFLINE_DATE_ACT',
                    title: '实际下线日期'
                },
                {
                    field: 'HOLD_RES',
                    title: 'hold理由'
                },
                {
                    field: 'SPC_FLAG',
                    title: '特殊标记'
                },
                ],
                onLoadSuccess: function (data) {
                    oTableInit.InitDrop();
                }
            });
        };
        //注册表格行的draggable事件
        oTableInit.InitDrag = function () {
            $('#tb_order_left tr').draggable({
                helper: "clone",
                start: function (event, ui) {
                    var old_left_data = JSON.stringify($('#tb_order_left').bootstrapTable("getData"));
                    var old_right_data = JSON.stringify($('#tb_order_right').bootstrapTable("getData"));
                    var odata = { index: ++i_statuindex, left_data: old_left_data, right_data: old_right_data };
                    arrdata.push(odata);
                },
                stop: function (event, ui) {
                    
                }
            });
        };
        //注册右边表格的droppable事件
        oTableInit.InitDrop = function () {
            $("#tb_order_right").droppable({
                drop: function (event, ui) {
                    var arrtd = $(ui.helper[0]).find("td");
                    var rowdata = {
                        ORDER_NO: $(arrtd[1]).text(),
                        BODY_NO: $(arrtd[2]).text(),
                        VIN: $(arrtd[3]).text(),
                        TM_MODEL_MATERIAL_ID: $(arrtd[4]).text(),
                        ORDER_TYPE: $(arrtd[5]).text(),
                        ORDER_STATUS: $(arrtd[6]).text(),
                        CREATE_DATE: $(arrtd[7]).text() == "-" ? null : $(arrtd[7]).text(),
                        PLAN_DATE: $(arrtd[8]).text() == "-" ? null : $(arrtd[8]).text(),
                        VMS_NO: $(arrtd[9]).text(),
                        ENGIN_CODE: $(arrtd[10]).text(),
                        TRANS_CODE: $(arrtd[11]).text(),
                        OFFLINE_DATE_ACT: $(arrtd[12]).text() == "-" ? null : $(arrtd[12]).text(),
                        HOLD_RES: $(arrtd[13]).text(),
                        SPC_FLAG: $(arrtd[14]).text(),
                        TO_ORDER_ID: $(ui.helper[0]).attr("data-uniqueid")
    
                    };
                    var oTop = ui.helper[0].offsetTop;
                    var iRowHeadHeight = 40;
                    var iRowHeight = 37;
                    var rowIndex = 0;
                    if (oTop <= iRowHeadHeight + iRowHeight / 2) {
                        rowIndex = 0;
                    }
                    else {
                        rowIndex = Math.ceil((oTop - iRowHeadHeight) / iRowHeight);
                    }
              //插入右边表格指定位置行数据
                    $("#tb_order_right").bootstrapTable("insertRow", { index: rowIndex, row: rowdata });
                    $('#tb_order_left').bootstrapTable("removeByUniqueId", $(ui.helper[0]).attr("data-uniqueid"));
                    oTableInit.InitDrag();
                }
            });
        };
    
        oTableInit.queryParams = function (params) {  //配置参数
            var temp = {   //这里的键的名字和控制器的变量名必须一直,这边改动,控制器也需要改成一样的
                limit: params.limit,   //页面大小
                offset: params.offset,  //页码
                strBodyno: $("#txt_search_bodynumber").val(),
                strVin: $("#txt_search_vinnumber").val(),
                strOrderno: $("#txt_search_ordernumber").val(),
                strEngincode: $("#txt_search_engin_code").val(),
                strOrderstatus: 0,
                strTranscode: $("#txt_search_trans_code").val(),
                strVms: $("#txt_search_vms").val(),
                strCarcode: $("#txt_search_carcode").val(),
                strImportStartdate: $("#txt_search_import_startdate").val(),
                strImportEnddate: $("#txt_search_import_enddate").val(),
                strSendStartdate: $("#txt_search_send_startdate").val(),
                strSendEnddate: $("#txt_search_send_enddate").val(),
    
            };
            return temp;
        };
    
        oTableInit.queryParamsRight = function (params) {  //配置参数
            var temp = {   //这里的键的名字和控制器的变量名必须一直,这边改动,控制器也需要改成一样的
                limit: params.limit,   //页面大小
                offset: params.offset,  //页码
                strBodyno: "",
                strVin: "",
                strOrderno: "",
                strEngincode: "",
                strOrderstatus: 5,
                strTranscode: "",
                strVms: "",
                strCarcode: "",
                strImportStartdate: "",
                strImportEnddate: "",
                strSendStartdate: "",
                strSendEnddate: "",
    
            };
            return temp;
        };
    
        return oTableInit;
    };
    
    //页面按钮初始化事件
    var ButtonInit = function () {
        var oInit = new Object();
        var postdata = {};
    
        oInit.Init = function () {
    
            //查询点击事件
            $("#btn_query").click(function () {
                $("#tb_order_left").bootstrapTable('refresh');
            });
    
            //重置点击事件
            $("#btn_reset").click(function () {
                $(".container-fluid").find(".form-control").val("");
                $("#tb_order_left").bootstrapTable('refresh');
            });
            //撤销操作点击事件
            $("#btn_cancel").click(function () {
                if (i_statuindex <= 0) {
                    return;
                }
                for (var i = 0; i < arrdata.length; i++) {
                    if (arrdata[i].index != i_statuindex) {
                        continue;
                    }
                    var arr_left_data = eval(arrdata[i].left_data);
                    var arr_right_data = eval(arrdata[i].right_data);
    
                    $('#tb_order_left').bootstrapTable('removeAll');
                    $('#tb_order_right').bootstrapTable('removeAll');
                    $('#tb_order_left').bootstrapTable('append', arr_left_data);
                    for (var x = 0; x < arr_right_data.length; x++) {
                        $("#tb_order_right").bootstrapTable("insertRow", { index: x, row: arr_right_data[x] });
                    }
                    
                    //$('#tb_order_right').bootstrapTable('append', arr_right_data);//append之后不能drop
                    break;
                }
                i_statuindex--;
    
                //重新注册可拖拽
                m_oTable.InitDrag();
                //m_oTable.InitDrop();
            });
    
            //搜索栏展开收起点击事件
            $("#span_collapse").click(function () {
                if ($(this).text() == "收起") {
                    $(this).html('展开<label class="glyphicon glyphicon-menu-down"></label>');
                    $("#div_more_search").collapse('hide');
                }
                else {
                    $(this).html('收起<label class="glyphicon glyphicon-menu-up"></label>');
                    $("#div_more_search").collapse('show')
                }
            });
        };
    
        return oInit;
    };

    我们重点来看几个地方的代码:

    2.2.1  左边表格加载成功之后执行表格行的可拖拽。

    $('#tb_order_left tr').draggable({
                helper: "clone",
                start: function (event, ui) {
                    var old_left_data = JSON.stringify($('#tb_order_left').bootstrapTable("getData"));
                    var old_right_data = JSON.stringify($('#tb_order_right').bootstrapTable("getData"));
                    var odata = { index: ++i_statuindex, left_data: old_left_data, right_data: old_right_data };
                    arrdata.push(odata);
                },
                stop: function (event, ui) {
                }
            });

    在draggable的start事件中,我们将拖拽之前的左右表格中的数据全部保存到arrdata变量中,i_statuindex这个全局变量用于记录当前这一步的索引,用于撤销操作。

    2.2.2 右边表格在加载成功之后注册表格的droppable事件

        $("#tb_order_right").droppable({
                drop: function (event, ui) {
                    var arrtd = $(ui.helper[0]).find("td");
                    var rowdata = {
                        ORDER_NO: $(arrtd[1]).text(),
                        BODY_NO: $(arrtd[2]).text(),
                        VIN: $(arrtd[3]).text(),
                        TM_MODEL_MATERIAL_ID: $(arrtd[4]).text(),
                        ORDER_TYPE: $(arrtd[5]).text(),
                        ORDER_STATUS: $(arrtd[6]).text(),
                        CREATE_DATE: $(arrtd[7]).text() == "-" ? null : $(arrtd[7]).text(),
                        PLAN_DATE: $(arrtd[8]).text() == "-" ? null : $(arrtd[8]).text(),
                        VMS_NO: $(arrtd[9]).text(),
                        ENGIN_CODE: $(arrtd[10]).text(),
                        TRANS_CODE: $(arrtd[11]).text(),
                        OFFLINE_DATE_ACT: $(arrtd[12]).text() == "-" ? null : $(arrtd[12]).text(),
                        HOLD_RES: $(arrtd[13]).text(),
                        SPC_FLAG: $(arrtd[14]).text(),
                        TO_ORDER_ID: $(ui.helper[0]).attr("data-uniqueid")
    
                    };
                    var oTop = ui.helper[0].offsetTop;
                    var iRowHeadHeight = 40;
                    var iRowHeight = 37;
                    var rowIndex = 0;
                    if (oTop <= iRowHeadHeight + iRowHeight / 2) {
                        rowIndex = 0;
                    }
                    else {
                        rowIndex = Math.ceil((oTop - iRowHeadHeight) / iRowHeight);
                    }
                    $("#tb_order_right").bootstrapTable("insertRow", { index: rowIndex, row: rowdata });
                    $('#tb_order_left').bootstrapTable("removeByUniqueId", $(ui.helper[0]).attr("data-uniqueid"));
                    oTableInit.InitDrag();
                }
            });

    在drop事件时,取到当前拖过来的行数据,计算当前鼠标所在的位置,在右边表格指定位置插入拖过来的行数据。然后删除左边表格拖过来的行数据。

    2.2.3 撤销操作代码

         //撤销操作点击事件
            $("#btn_cancel").click(function () {
                if (i_statuindex <= 0) {
                    return;
                }
                for (var i = 0; i < arrdata.length; i++) {
                    if (arrdata[i].index != i_statuindex) {
                        continue;
                    }
                    var arr_left_data = eval(arrdata[i].left_data);
                    var arr_right_data = eval(arrdata[i].right_data);
    
                    $('#tb_order_left').bootstrapTable('removeAll');
                    $('#tb_order_right').bootstrapTable('removeAll');
                    $('#tb_order_left').bootstrapTable('append', arr_left_data);
                    for (var x = 0; x < arr_right_data.length; x++) {
                        $("#tb_order_right").bootstrapTable("insertRow", { index: x, row: arr_right_data[x] });
                    }
                    //$('#tb_order_right').bootstrapTable('append', arr_right_data);//append之后不能drop
                    break;
                }
                i_statuindex--;
    
                //重写注册可拖拽
                m_oTable.InitDrag();
            });

    撤销操作主要是通过全局变量arrdata里面的索引判断撤销到哪一步,然后根据索引取出当前步骤的左右表格数据,依次向两表格插入数据,然后i_statuindex依次递减,直至等于零,由于左边表格行数据全部重写加载过,所以需要重新注册可拖拽事件。就是这么简单的三步就能实现想要的效果,是不是很简单~~

  • 请您注意

    ·自觉遵守:爱国、守法、自律、真实、文明的原则

    ·尊重网上道德,遵守《全国人大常委会关于维护互联网安全的决定》及中华人民共和国其他各项有关法律法规

    ·严禁发表危害国家安全,破坏民族团结、国家宗教政策和社会稳定,含侮辱、诽谤、教唆、淫秽等内容的作品

    ·承担一切因您的行为而直接或间接导致的民事或刑事法律责任

    ·您在编程中国社区新闻评论发表的作品,本网站有权在网站内保留、转载、引用或者删除

    ·参与本评论即表明您已经阅读并接受上述条款

  • 感谢本文作者
  • 作者头像
  • 昵称:一毛哥
  • 加入时间:2013/6/13 0:00:00
  • TA的签名
  • 这家伙很懒,虾米都没写
  • +进入TA的空间
  • 以下内容也很赞哦
分享按钮