DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> JavaScript入門知識 >> 關於JavaScript >> JS組件Bootstrap Table表格多行拖拽效果實現代碼
JS組件Bootstrap Table表格多行拖拽效果實現代碼
編輯:關於JavaScript     

前言:前天剛寫了篇JS組件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,而不是選中的行,奈何多個選中的行無法用某一個容器包起來。暫時沒找到合適的解決方案。先這樣吧,等以後想到好的方案了再優化吧。 

五、優化方案

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

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

源碼下載:Bootstrap Table表格多行拖拽效果

如果大家還想深入學習,可以點擊這裡進行學習,再為大家附兩個精彩的專題:Bootstrap學習教程 Bootstrap實戰教程

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

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved