转载

JS组件系列——Bootstrap Table 表格行拖拽(二:多行拖拽)

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

一、效果展示

1、拖动前

JS组件系列——Bootstrap Table 表格行拖拽(二:多行拖拽)

2、拖动中

JS组件系列——Bootstrap Table 表格行拖拽(二:多行拖拽)

3、拖动后

JS组件系列——Bootstrap Table 表格行拖拽(二:多行拖拽)

4、撤销回到拖动前状态

JS组件系列——Bootstrap Table 表格行拖拽(二:多行拖拽)

二、需求分析

通过上篇我们知道,如果要实现拖拽,必须要有一个可以拖拽的标签,或者叫容器,比如上篇里面的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; }; 页面整个js代码 

还是重点看看部分代码

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,而不是选中的行,奈何多个选中的行无法用某一个容器包起来。暂时没找到合适的解决方案。先这样吧,等以后想到好的方案了再优化吧。

正文到此结束
Loading...