Bootstrap教程- Table 表格行实现多行同时拖拽效果

本文转载于:猿2048网站➬Bootstrap教程- Table 表格行实现多行同时拖拽效果

一、效果展示

1、拖动前
2、拖动中
3、拖动后
4、撤销回到拖动前状态
二、需求分析

通过上篇我们知道,如果要实现拖拽,必须要有一个可以拖拽的标签,或者叫容器,比如上篇里面的tr就是一个拖拽的容器,那么如果要实现选择行的拖拽,那么博主的第一反应是将选中的行放到一个容器里面,比如放到一个div中,然后注册这个div的可拖拽,可是实际情况是,tr是在table里面的标签,如果将tr用div包起来,势必将table里面的样式打乱,这个界面就真的是乱掉了。很显然,这条路走不通。然后通过谷歌浏览器审核元素知道,用 Bootstrap  table生成的表格tr的父级元素实际上是tbody,于是在想是否可以注册tbody的拖拽,实践证明,此法可行。于是就此开干。

三、代码示例

cshtm的代码就不再重复,和上篇相同。我们重点来看看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(); if (data.total > 0 ) { var iheight = $( ' #div_tableleft ' ).find( " .fixed-table-container " ).height(); $( ' #div_tableleft ' ).find( " .fixed-table-container " ).height(iheight + 36 ); } }, onCheckAll: function (rows) { $( " #tb_order_left tbody tr " ).addClass( " selected " ); }, onUncheckAll: function (rows) { $( " #tb_order_left tbody tr " ).removeClass( " selected " ); } }); $( ' #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(); } }); }; oTableInit.InitDrag = function () { $( ' #tb_order_left tbody ' ).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) { } }); }; oTableInit.InitDrop = function () { $( " #div_tableright div[class=fixed-table-container] " ).droppable({ drop: function ( event , ui) { var arrtr = $(ui.helper[ 0 ]).find( " tr[class=selected] " ); if (arrtr.length <= 0 ) { alert( " 请先选中要插单的行 " ); return ; } 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); } for ( var i = 0 ; i < arrtr.length; i++ ) { var arrtd = $(arrtr[i]).find( " td " ); var uniqueid = $(arrtr[i]).attr( " data-uniqueid " ); 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: uniqueid }; $( " #tb_order_right " ).bootstrapTable( " insertRow " , { index: rowIndex++ , row: rowdata }); $( ' #tb_order_left ' ).bootstrapTable( " removeByUniqueId " , 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_insertorder " ).click(function () { }); // 撤销操作点击事件 $( " #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(); }); }; return oInit; };

还是重点看看部分代码

1、注册左边可拖拽

$('#tb_order_left tbody').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) {
            }
        });

这里代码很简单,主要做了两件事:

(1)注册tbody的可拖拽,同样适用的JQuery UI的draggable事件。

(2)在开始拖拽前,保存两边表格的数据,用于还原的操作。

2、注册右边drop

    $("#div_tableright div[class=fixed-table-container]").droppable({
            drop: function (event, ui) {
                var arrtr = $(ui.helper[0]).find("tr[class=selected]");
                if (arrtr.length <= 0) {
                    alert("请先选中要插单的行");
                    return;
                }
                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);
                }
                for (var i = 0; i < arrtr.length; i++) {
                    var arrtd = $(arrtr[i]).find("td");
                    var uniqueid = $(arrtr[i]).attr("data-uniqueid");
                    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: uniqueid

                    };
                    $("#tb_order_right").bootstrapTable("insertRow", { index: rowIndex++, row: rowdata });
                    $('#tb_order_left').bootstrapTable("removeByUniqueId", uniqueid);
                }
                
                
                oTableInit.InitDrag();
            }
        });

这里代码和之前有点变化

(1)注册#div_tableright div[class=fixed-table-container]标签的droppable,这个标签是Bootstrap Table表格初始化后自动生成的,为什么不直接注册表格#tb_order_right的droppable,是因为这个标签作用域太小,会导致拖过来的tbody捕捉不到drop事件。而注册表格外部的#div_tableright div[class=fixed-table-container]这个div标签可以解决问题。

(2)通过var arrtr = $(ui.helper[0]).find("tr[class=selected]");找到拖过来tbody里面选中的行,然后将数据取出依次插入右边表格,左边表格则依次删除行数据。

(3)这里有点麻烦的是找drop的位置,我们知道,要想将左边选中的行放到右边指定的位置,那么就得得到当前鼠标drop的位置,这里通过ui.helper[0].offsetTop属性来获得鼠标的Y轴位置,通过计算得到要插入的位置。

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();
            //m_oTable.InitDrop();
        });

撤销操作和之前也基本相同。

四、总结

效果是完成了,美中不足的是每次拖过去的都是整个tbody,而不是选中的行,奈何多个选中的行无法用某一个容器包起来。暂时没找到合适的解决方案。先这样吧,等以后想到好的方案了再优化吧。

五、优化方案(02月6日)

1、注册drap的方法

oTableInit.InitDrag = function () {
        $('#tb_order_left tbody').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);
                $(ui.helper[0]).find("tr[class!=selected]").remove();
            },
            stop: function (event, ui) {
            }
        });
    };
复制代码

增加了这一句$(ui.helper[0]).find("tr[class!=selected]").remove();这样在拖动的时候就看不到未选中的行了。

2、精准定位到右边表格指定位置:

oTableInit.InitDrop = function () {
        $("#div_tableright div[class=fixed-table-container]").droppable({
            drop: function (event, ui) {
                var arrtr = $(ui.helper[0]).find("tr[class=selected]");
                if (arrtr.length <= 0) {
                    toastr.warning('请先选中要插单的行');
                    return;
                }
                var oTop = ui.helper[0].offsetTop;
                //因为表格每行的高度可能不一致,所以这里取插入行位置的办法是:取右边表格的行高依次累加,计算行索引。
                var rowIndex = 0;
                var bIsBottom = true;
                var iRowHeadHeight = 40;
                var addHeight = iRowHeadHeight;
                var trs = $("#tb_order_right tbody tr");
                for (var i = 0; i < trs.length; i++) {
                    addHeight += $(trs[i]).height();
                    if (addHeight > oTop) {
                        rowIndex = i;
                        bIsBottom = false;//这里参数用来定义拖动到右边表格最下面的情况,这时没有进入到此条件判断里面。
                        break;
                    }
                }
                if (bIsBottom) {
                    rowIndex = trs.length;
                }

                for (var i = 0; i < arrtr.length; i++) {
                    var arrtd = $(arrtr[i]).find("td");
                    var uniqueid = $(arrtr[i]).attr("data-uniqueid");
                    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_NAME: $(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: uniqueid,
                        ORDER_STATUS:0

                    };
                    $("#tb_order_right").bootstrapTable("insertRow", { index: rowIndex++, row: rowdata });
                    $('#tb_order_left').bootstrapTable("removeByUniqueId", uniqueid);
                }

                oTableInit.InitDrag();

            }
        });
    };

因为每一行的行高不确定,是由行里面的数据动态撑出来的,所以这里也动态计算drop的位置。

至此,这个小的功能基本告一段落,基本的效果和待优化点也完成了。


更多专业前端知识,请上 【猿2048】www.mk2048.com

猜你喜欢

转载自www.cnblogs.com/qianduanwriter/p/11747491.html
今日推荐