JS組件Bootstrap Table表格多行拖拽效果實(shí)現(xiàn)代碼
前言:前天剛寫了篇JS組件Bootstrap Table表格行拖拽效果,今天接到新的需要,需要在之前表格行拖拽的基礎(chǔ)上能夠同時(shí)拖拽選中的多行。用了半天時(shí)間研究了下,效果是出來(lái)了,但是感覺(jué)不盡如人意。先把它分享出來(lái),以后想到更好的辦法再優(yōu)化吧。
一、效果展示
1、拖動(dòng)前
2、拖動(dòng)中
3、拖動(dòng)后
4、撤銷回到拖動(dòng)前狀態(tài)
二、需求分析
通過(guò)上篇我們知道,如果要實(shí)現(xiàn)拖拽,必須要有一個(gè)可以拖拽的標(biāo)簽,或者叫容器,比如上篇里面的tr就是一個(gè)拖拽的容器,那么如果要實(shí)現(xiàn)選擇行的拖拽,那么博主的第一反應(yīng)是將選中的行放到一個(gè)容器里面,比如放到一個(gè)div中,然后注冊(cè)這個(gè)div的可拖拽,可是實(shí)際情況是,tr是在table里面的標(biāo)簽,如果將tr用div包起來(lái),勢(shì)必將table里面的樣式打亂,這個(gè)界面就真的是亂掉了。很顯然,這條路走不通。然后通過(guò)谷歌瀏覽器審核元素知道,用Bootstrap table生成的表格tr的父級(jí)元素實(shí)際上是tbody,于是在想是否可以注冊(cè)tbody的拖拽,實(shí)踐證明,此法可行。于是就此開(kāi)干。
三、代碼示例
cshtm的代碼就不再重復(fù),和上篇相同。我們重點(diǎn)來(lái)看看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, }); }); //表格相關(guān)事件和方法 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: '訂單號(hào)' }, { field: 'BODY_NO', title: '車身號(hào)' }, { field: 'VIN', title: 'VIN碼' }, { field: 'TM_MODEL_MATERIAL_ID', title: '整車編碼' }, { field: 'ORDER_TYPE', title: '訂單類型' }, { field: 'ORDER_STATUS', title: '訂單狀態(tài)' }, { field: 'CREATE_DATE', title: '訂單導(dǎo)入時(shí)間' }, { field: 'PLAN_DATE', title: '訂單計(jì)劃上線日期' }, { field: 'VMS_NO', title: 'VMS號(hào)' }, { field: 'ENGIN_CODE', title: '發(fā)動(dòng)機(jī)號(hào)' }, { field: 'TRANS_CODE', title: '變速箱號(hào)' }, { field: 'OFFLINE_DATE_ACT', title: '實(shí)際下線日期' }, { field: 'HOLD_RES', title: 'hold理由' }, { field: 'SPC_FLAG', title: '特殊標(biāo)記' }, ], 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: '訂單號(hào)' }, { field: 'BODY_NO', title: '車身號(hào)' }, { field: 'VIN', title: 'VIN碼' }, { field: 'TM_MODEL_MATERIAL_ID', title: '整車編碼' }, { field: 'ORDER_TYPE', title: '訂單類型' }, { field: 'ORDER_STATUS', title: '訂單狀態(tài)' }, { field: 'CREATE_DATE', title: '訂單導(dǎo)入時(shí)間' }, { field: 'PLAN_DATE', title: '訂單計(jì)劃上線日期' }, { field: 'VMS_NO', title: 'VMS號(hào)' }, { field: 'ENGIN_CODE', title: '發(fā)動(dòng)機(jī)號(hào)' }, { field: 'TRANS_CODE', title: '變速箱號(hào)' }, { field: 'OFFLINE_DATE_ACT', title: '實(shí)際下線日期' }, { field: 'HOLD_RES', title: 'hold理由' }, { field: 'SPC_FLAG', title: '特殊標(biāo)記' }, ], 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("請(qǐng)先選中要插單的行"); 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) { //配置參數(shù) var temp = { //這里的鍵的名字和控制器的變量名必須一直,這邊改動(dòng),控制器也需要改成一樣的 limit: params.limit, //頁(yè)面大小 offset: params.offset, //頁(yè)碼 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) { //配置參數(shù) var temp = { //這里的鍵的名字和控制器的變量名必須一直,這邊改動(dòng),控制器也需要改成一樣的 limit: params.limit, //頁(yè)面大小 offset: params.offset, //頁(yè)碼 strBodyno: "", strVin: "", strOrderno: "", strEngincode: "", strOrderstatus: 5, strTranscode: "", strVms: "", strCarcode: "", strImportStartdate: "", strImportEnddate: "", strSendStartdate: "", strSendEnddate: "", }; return temp; }; return oTableInit; }; //頁(yè)面按鈕初始化事件 var ButtonInit = function () { var oInit = new Object(); var postdata = {}; oInit.Init = function () { //查詢點(diǎn)擊事件 $("#btn_query").click(function () { $("#tb_order_left").bootstrapTable('refresh'); }); //重置點(diǎn)擊事件 $("#btn_reset").click(function () { $(".container-fluid").find(".form-control").val(""); $("#tb_order_left").bootstrapTable('refresh'); }); //插單操作點(diǎn)擊事件 $("#btn_insertorder").click(function () { }); //撤銷操作點(diǎn)擊事件 $("#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--; //重新注冊(cè)可拖拽 m_oTable.InitDrag(); //m_oTable.InitDrop(); }); }; return oInit; };
還是重點(diǎn)看看部分代碼
1、注冊(cè)左邊可拖拽
$('#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) { } });
這里代碼很簡(jiǎn)單,主要做了兩件事:
(1)注冊(cè)tbody的可拖拽,同樣適用的JQuery UI的draggable事件。
(2)在開(kāi)始拖拽前,保存兩邊表格的數(shù)據(jù),用于還原的操作。
2、注冊(cè)右邊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("請(qǐng)先選中要插單的行"); 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(); } });
這里代碼和之前有點(diǎn)變化
(1)注冊(cè)#div_tableright div[class=fixed-table-container]標(biāo)簽的droppable,這個(gè)標(biāo)簽是Bootstrap Table表格初始化后自動(dòng)生成的,為什么不直接注冊(cè)表格#tb_order_right的droppable,是因?yàn)檫@個(gè)標(biāo)簽作用域太小,會(huì)導(dǎo)致拖過(guò)來(lái)的tbody捕捉不到drop事件。而注冊(cè)表格外部的#div_tableright div[class=fixed-table-container]這個(gè)div標(biāo)簽可以解決問(wèn)題。
(2)通過(guò)var arrtr = $(ui.helper[0]).find("tr[class=selected]");找到拖過(guò)來(lái)tbody里面選中的行,然后將數(shù)據(jù)取出依次插入右邊表格,左邊表格則依次刪除行數(shù)據(jù)。
(3)這里有點(diǎn)麻煩的是找drop的位置,我們知道,要想將左邊選中的行放到右邊指定的位置,那么就得得到當(dāng)前鼠標(biāo)drop的位置,這里通過(guò)ui.helper[0].offsetTop屬性來(lái)獲得鼠標(biāo)的Y軸位置,通過(guò)計(jì)算得到要插入的位置。
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--; //重寫注冊(cè)可拖拽 m_oTable.InitDrag(); //m_oTable.InitDrop(); });
撤銷操作和之前也基本相同。
四、總結(jié)
效果是完成了,美中不足的是每次拖過(guò)去的都是整個(gè)tbody,而不是選中的行,奈何多個(gè)選中的行無(wú)法用某一個(gè)容器包起來(lái)。暫時(shí)沒(méi)找到合適的解決方案。先這樣吧,等以后想到好的方案了再優(yōu)化吧。
五、優(yōu)化方案
1、注冊(cè)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();這樣在拖動(dòng)的時(shí)候就看不到未選中的行了。
2、精準(zhǔn)定位到右邊表格指定位置:
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('請(qǐng)先選中要插單的行'); return; } var oTop = ui.helper[0].offsetTop; //因?yàn)楸砀衩啃械母叨瓤赡懿灰恢?,所以這里取插入行位置的辦法是:取右邊表格的行高依次累加,計(jì)算行索引。 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;//這里參數(shù)用來(lái)定義拖動(dòng)到右邊表格最下面的情況,這時(shí)沒(méi)有進(jìn)入到此條件判斷里面。 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(); } }); };
因?yàn)槊恳恍械男懈卟淮_定,是由行里面的數(shù)據(jù)動(dòng)態(tài)撐出來(lái)的,所以這里也動(dòng)態(tài)計(jì)算drop的位置。
至此,這個(gè)小的功能基本告一段落,基本的效果和待優(yōu)化點(diǎn)也完成了。
如果大家還想深入學(xué)習(xí),可以點(diǎn)擊這里進(jìn)行學(xué)習(xí),再為大家附兩個(gè)精彩的專題:Bootstrap學(xué)習(xí)教程 Bootstrap實(shí)戰(zhàn)教程
以上就是本文的全部?jī)?nèi)容,希望本文所述對(duì)大家學(xué)習(xí)javascript程序設(shè)計(jì)有所幫助。
- Vue Element Sortablejs實(shí)現(xiàn)表格列的拖拽案例詳解
- 利用Vue-draggable組件實(shí)現(xiàn)Vue項(xiàng)目中表格內(nèi)容的拖拽排序
- vue.draggable實(shí)現(xiàn)表格拖拽排序效果
- 基于Vue實(shí)現(xiàn)可以拖拽的樹(shù)形表格實(shí)例詳解
- Elementui表格組件+sortablejs實(shí)現(xiàn)行拖拽排序的示例代碼
- JS組件Bootstrap Table表格行拖拽效果實(shí)現(xiàn)代碼
- javascript實(shí)現(xiàn)表格排序 編輯 拖拽 縮放
- js 表格排序(編輯+拖拽+縮放)
- js 表格拖拽效果實(shí)例代碼 (IE only)
- vue+element-ui+sortable.js實(shí)現(xiàn)表格拖拽功能
相關(guān)文章
Javascript-Mozilla和IE中的一個(gè)函數(shù)直接量的問(wèn)題
Javascript-Mozilla和IE中的一個(gè)函數(shù)直接量的問(wèn)題...2007-01-01JavaScript中while循環(huán)的基礎(chǔ)使用教程
這篇文章主要給大家介紹了關(guān)于JavaScript中while循環(huán)的基礎(chǔ)使用教程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用JavaScript具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08JavaScript使用位運(yùn)算符判斷奇數(shù)和偶數(shù)的方法
這篇文章主要介紹了JavaScript使用位運(yùn)算符判斷奇數(shù)和偶數(shù)的方法,涉及javascript位運(yùn)算的使用技巧,需要的朋友可以參考下2015-06-06JavaScript進(jìn)階(一)變量聲明提升實(shí)例分析
這篇文章主要介紹了JavaScript變量聲明提升,結(jié)合實(shí)例形式分析了JavaScript變量聲明提升相關(guān)原理、用法與操作注意事項(xiàng),需要的朋友可以參考下2020-05-05