JS組件Bootstrap Table表格多行拖拽效果實現(xiàn)代碼
前言:前天剛寫了篇JS組件Bootstrap Table表格行拖拽效果,今天接到新的需要,需要在之前表格行拖拽的基礎(chǔ)上能夠同時拖拽選中的多行。用了半天時間研究了下,效果是出來了,但是感覺不盡如人意。先把它分享出來,以后想到更好的辦法再優(yōu)化吧。
一、效果展示
1、拖動前

2、拖動中

3、拖動后

4、撤銷回到拖動前狀態(tài)

二、需求分析
通過上篇我們知道,如果要實現(xiàn)拖拽,必須要有一個可以拖拽的標簽,或者叫容器,比如上篇里面的tr就是一個拖拽的容器,那么如果要實現(xiàn)選擇行的拖拽,那么博主的第一反應(yīng)是將選中的行放到一個容器里面,比如放到一個div中,然后注冊這個div的可拖拽,可是實際情況是,tr是在table里面的標簽,如果將tr用div包起來,勢必將table里面的樣式打亂,這個界面就真的是亂掉了。很顯然,這條路走不通。然后通過谷歌瀏覽器審核元素知道,用Bootstrap table生成的表格tr的父級元素實際上是tbody,于是在想是否可以注冊tbody的拖拽,實踐證明,此法可行。于是就此開干。
三、代碼示例
cshtm的代碼就不再重復(fù),和上篇相同。我們重點來看看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: '訂單號'
},
{
field: 'BODY_NO',
title: '車身號'
}, {
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)入時間'
},
{
field: 'PLAN_DATE',
title: '訂單計劃上線日期'
},
{
field: 'VMS_NO',
title: 'VMS號'
},
{
field: 'ENGIN_CODE',
title: '發(fā)動機號'
},
{
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: '訂單狀態(tài)'
},
{
field: 'CREATE_DATE',
title: '訂單導(dǎo)入時間'
},
{
field: 'PLAN_DATE',
title: '訂單計劃上線日期'
},
{
field: 'VMS_NO',
title: 'VMS號'
},
{
field: 'ENGIN_CODE',
title: '發(fā)動機號'
},
{
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) { //配置參數(shù)
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) { //配置參數(shù)
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)在開始拖拽前,保存兩邊表格的數(shù)據(jù),用于還原的操作。
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,是因為這個標簽作用域太小,會導(dǎo)致拖過來的tbody捕捉不到drop事件。而注冊表格外部的#div_tableright div[class=fixed-table-container]這個div標簽可以解決問題。
(2)通過var arrtr = $(ui.helper[0]).find("tr[class=selected]");找到拖過來tbody里面選中的行,然后將數(shù)據(jù)取出依次插入右邊表格,左邊表格則依次刪除行數(shù)據(jù)。
(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();
});
撤銷操作和之前也基本相同。
四、總結(jié)
效果是完成了,美中不足的是每次拖過去的都是整個tbody,而不是選中的行,奈何多個選中的行無法用某一個容器包起來。暫時沒找到合適的解決方案。先這樣吧,等以后想到好的方案了再優(yōu)化吧。
五、優(yōu)化方案
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;//這里參數(shù)用來定義拖動到右邊表格最下面的情況,這時沒有進入到此條件判斷里面。
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();
}
});
};
因為每一行的行高不確定,是由行里面的數(shù)據(jù)動態(tài)撐出來的,所以這里也動態(tài)計算drop的位置。
至此,這個小的功能基本告一段落,基本的效果和待優(yōu)化點也完成了。
如果大家還想深入學(xué)習(xí),可以點擊這里進行學(xué)習(xí),再為大家附兩個精彩的專題:Bootstrap學(xué)習(xí)教程 Bootstrap實戰(zhàn)教程
以上就是本文的全部內(nèi)容,希望本文所述對大家學(xué)習(xí)javascript程序設(shè)計有所幫助。
- Vue Element Sortablejs實現(xiàn)表格列的拖拽案例詳解
- 利用Vue-draggable組件實現(xiàn)Vue項目中表格內(nèi)容的拖拽排序
- vue.draggable實現(xiàn)表格拖拽排序效果
- 基于Vue實現(xiàn)可以拖拽的樹形表格實例詳解
- Elementui表格組件+sortablejs實現(xiàn)行拖拽排序的示例代碼
- JS組件Bootstrap Table表格行拖拽效果實現(xiàn)代碼
- javascript實現(xiàn)表格排序 編輯 拖拽 縮放
- js 表格排序(編輯+拖拽+縮放)
- js 表格拖拽效果實例代碼 (IE only)
- vue+element-ui+sortable.js實現(xiàn)表格拖拽功能
相關(guān)文章
前端終止請求的3種方式總結(jié)(ajax、axios)
這篇文章主要給大家總結(jié)介紹了關(guān)于前端終止請求的3種方式,其中包括ajax、axios的相關(guān)資料, 取消請求在前端有時候會用到,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2023-09-09
寫出更好的JavaScript程序之undefined篇(中)
前一篇我介紹了幾種廣為使用的利用undefined這個概念值的辦法,這一篇我會介紹一些不太常見的辦法,其中還包括一個很巧妙的,我個人覺得很值得推廣的辦法。2009-11-11
javascript模擬post提交隱藏地址欄的參數(shù)
想要隱藏地址欄的參數(shù),就只能用javascript模擬post提交,下面是示例代碼,需要的朋友可以看看2014-09-09
JavaScript實現(xiàn)背景圖像切換3D動畫效果示例詳解
這篇文章主要為大家介紹了JavaScript實現(xiàn)背景圖像切換3D動畫效果示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-09-09
JavaScript Cookie的讀取和寫入函數(shù)
Cookie的讀取和寫入實現(xiàn)函數(shù)。2009-12-12

