javascript
JS组件系列——Bootstrap Table 表格行拖拽(二:多行拖拽)
前言:前天剛寫了篇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之后不能dropbreak;}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之后不能dropbreak;}i_statuindex--;//重寫注冊可拖拽 m_oTable.InitDrag();//m_oTable.InitDrop();});撤銷操作和之前也基本相同。
四、總結
效果是完成了,美中不足的是每次拖過去的都是整個tbody,而不是選中的行,奈何多個選中的行無法用某一個容器包起來。暫時沒找到合適的解決方案。先這樣吧,等以后想到好的方案了再優化吧。
?
五、優化方案(11月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的位置。
至此,這個小的功能基本告一段落,基本的效果和待優化點也完成了。
源碼下載
總結
以上是生活随笔為你收集整理的JS组件系列——Bootstrap Table 表格行拖拽(二:多行拖拽)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【译】CodeIgniter HMVC模
- 下一篇: LeetCode答案大全题(java版)