首頁  >  文章  >  web前端  >  JS元件Bootstrap Table表格多行拖曳效果實作程式碼_javascript技巧

JS元件Bootstrap Table表格多行拖曳效果實作程式碼_javascript技巧

WBOY
WBOY原創
2016-05-16 15:27:171447瀏覽

前言:前天剛寫了篇JS組件Bootstrap Table表格行拖曳效果,今天接到新的需要,需要在之前表格行拖曳的基礎上能夠同時拖拽選中的多行。花了半天研究了下,效果是出來了,但感覺不盡人意。先把它分享出來,以後再想到更好的方法再優化。

一、效果展示

1、拖曳前

2、拖曳中

3、拖曳後

4、撤銷回到拖曳前狀態

二、需求分析
透過上篇我們知道,如果要實現拖曳,必須要有一個可以拖曳的標籤,或者叫容器,比如上篇裡面的tr就是一個拖曳的容器,那麼如果要實現選擇行的拖拽,那麼部落客的第一個反應是將選取的行放到一個容器裡面,例如放到一個div中,然後註冊這個div的可拖曳,可是實際情況是,tr是在table裡面的標籤,如果將tr用div包起來,勢必將table裡面的樣式打亂,這個介面就真的是亂掉了。很顯然,這條路走不通。然後透過Google瀏覽器審核元素知道,用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() == "-" &#63; null : $(arrtd[7]).text(),
   PLAN_DATE: $(arrtd[8]).text() == "-" &#63; 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() == "-" &#63; 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() == "-" &#63; null : $(arrtd[7]).text(),
   PLAN_DATE: $(arrtd[8]).text() == "-" &#63; 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() == "-" &#63; 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() == "-" &#63; null : $(arrtd[7]).text(),
   PLAN_DATE: $(arrtd[8]).text() == "-" &#63; 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() == "-" &#63; 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的位置。

至此,這個小的功能基本上告一段落,基本的效果和待優化點也完成了。

原始碼下載:Bootstrap Table表格多行拖曳效果

以上就是本文的全部內容,希望本文所述對大家學習javascript程式設計有所幫助。

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn