• 技术文章 >web前端 >js教程

    JS组件Bootstrap Table表格多行拖拽效果实现代码

    高洛峰高洛峰2017-01-04 10:54:17原创936
    前言:前天刚写了篇JS组件Bootstrap Table表格行拖拽效果,今天接到新的需要,需要在之前表格行拖拽的基础上能够同时拖拽选中的多行。用了半天时间研究了下,效果是出来了,但是感觉不尽如人意。先把它分享出来,以后想到更好的办法再优化吧。

    一、效果展示

    1、拖动前

    2016110316252934.png

    2、拖动中

    2016110316252934.png

    3、拖动后

    2016110316252934.png

    4、撤销回到拖动前状态

    2016110316252934.png

    二、需求分析
    通过上篇我们知道,如果要实现拖拽,必须要有一个可以拖拽的标签,或者叫容器,比如上篇里面的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,而不是选中的行,奈何多个选中的行无法用某一个容器包起来。暂时没找到合适的解决方案。先这样吧,等以后想到好的方案了再优化吧。

    五、优化方案

    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的位置。

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

    更多JS组件Bootstrap Table表格多行拖拽效果实现代码相关文章请关注PHP中文网!

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
    上一篇:如何提高javascript加载速度 下一篇:JS组件Bootstrap Table表格行拖拽效果实现代码
    VIP课程(WEB全栈开发)

    相关文章推荐

    • 【活动】充值PHP中文网VIP即送云服务器• 为什么需要性能监控?聊聊Node.js性能监控• 4个Angular单元测试编写的小技巧,快来看看!• 一文带你了解npm的原理• 深入解析npm的包管理机制• 聊聊npm配置国内镜像(淘宝镜像)
    1/1

    PHP中文网