前言:前天刚写了篇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的位置。
至此,这个小的功能基本告一段落,基本的效果和待优化点也完成了。
以上就是本文的全部内容,希望本文所述对大家学习javascript程序设计有所帮助。
本文地址:https://www.stayed.cn/item/2724
转载请注明出处。
本站部分内容来源于网络,如侵犯到您的权益,请 联系我