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 = ;
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: ,
uniqueId:"TO_ORDER_ID",
queryParams: oTableInit.queryParams,
queryParamsType: "limit",
sidePagination: "server",
pageSize: ,
pageList: [, , , ],
search: true,
strictSearch: true,
showColumns: true,
showRefresh: true,
minimumCountColumns: ,
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 > ) {
var iheight = $('#div_tableleft').find(".fixed-table-container").height();
$('#div_tableleft').find(".fixed-table-container").height(iheight + );
}
},
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: ,
queryParams: oTableInit.queryParamsRight,
queryParamsType: "limit",
//ajaxOptions: { departmentname: "", statu: "" },
sidePagination: "server",
pageSize: ,
pageList: [, , , ],
search: true,
strictSearch: true,
showRefresh: true,
minimumCountColumns: ,
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[]).find("tr[class=selected]");
if (arrtr.length <= ) {
alert("请先选中要插单的行");
return;
}
var oTop = ui.helper[].offsetTop;
var iRowHeadHeight = ;
var iRowHeight = ;
var rowIndex = ;
if (oTop <= iRowHeadHeight + iRowHeight / ) {
rowIndex = ;
}
else {
rowIndex = Math.ceil((oTop - iRowHeadHeight) / iRowHeight);
}
for (var i = ; i < arrtr.length; i++) {
var arrtd = $(arrtr[i]).find("td");
var uniqueid = $(arrtr[i]).attr("data-uniqueid");
var rowdata = {
ORDER_NO: $(arrtd[]).text(),
BODY_NO: $(arrtd[]).text(),
VIN: $(arrtd[]).text(),
TM_MODEL_MATERIAL_ID: $(arrtd[]).text(),
ORDER_TYPE: $(arrtd[]).text(),
ORDER_STATUS: $(arrtd[]).text(),
CREATE_DATE: $(arrtd[]).text() == "-" ? null : $(arrtd[]).text(),
PLAN_DATE: $(arrtd[]).text() == "-" ? null : $(arrtd[]).text(),
VMS_NO: $(arrtd[]).text(),
ENGIN_CODE: $(arrtd[]).text(),
TRANS_CODE: $(arrtd[]).text(),
OFFLINE_DATE_ACT: $(arrtd[]).text() == "-" ? null : $(arrtd[]).text(),
HOLD_RES: $(arrtd[]).text(),
SPC_FLAG: $(arrtd[]).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: ,
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: ,
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 <= ) {
return;
}
for (var i = ; 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 = ; 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;
};
页面整个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[]).find("tr[class=selected]");
if (arrtr.length <= ) {
alert("请先选中要插单的行");
return;
}
var oTop = ui.helper[].offsetTop;
var iRowHeadHeight = ;
var iRowHeight = ;
var rowIndex = ;
if (oTop <= iRowHeadHeight + iRowHeight / ) {
rowIndex = ;
}
else {
rowIndex = Math.ceil((oTop - iRowHeadHeight) / iRowHeight);
}
for (var i = ; i < arrtr.length; i++) {
var arrtd = $(arrtr[i]).find("td");
var uniqueid = $(arrtr[i]).attr("data-uniqueid");
var rowdata = {
ORDER_NO: $(arrtd[]).text(),
BODY_NO: $(arrtd[]).text(),
VIN: $(arrtd[]).text(),
TM_MODEL_MATERIAL_ID: $(arrtd[]).text(),
ORDER_TYPE: $(arrtd[]).text(),
ORDER_STATUS: $(arrtd[]).text(),
CREATE_DATE: $(arrtd[]).text() == "-" ? null : $(arrtd[]).text(),
PLAN_DATE: $(arrtd[]).text() == "-" ? null : $(arrtd[]).text(),
VMS_NO: $(arrtd[]).text(),
ENGIN_CODE: $(arrtd[]).text(),
TRANS_CODE: $(arrtd[]).text(),
OFFLINE_DATE_ACT: $(arrtd[]).text() == "-" ? null : $(arrtd[]).text(),
HOLD_RES: $(arrtd[]).text(),
SPC_FLAG: $(arrtd[]).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 <= ) {
return;
}
for (var i = ; 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 = ; 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,而不是选中的行,奈何多个选中的行无法用某一个容器包起来。暂时没找到合适的解决方案。先这样吧,等以后想到好的方案了再优化吧。
五、优化方案(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 表格行拖拽(二:多行拖拽)的更多相关文章
- JS组件系列——Bootstrap Table 表格行拖拽
前言:之前一直在研究DDD相关知识,好久没更新JS系列文章了.这两天做了一个简单的业务需求,觉得效果还可以,今天在这里分享给大家,欢迎拍砖~~ 一.业务需求及实现效果 项目涉及到订单模块,那天突然接到 ...
- JS组件系列——Bootstrap Table 冻结列功能IE浏览器兼容性问题解决方案
前言:最近项目里面需要用到表格的冻结列功能,所谓“冻结列”,就是某些情况下表格的列比较多,需要固定前面的几列,后面的列滚动.遗憾的是,bootstrap table里自带的fixed column功能 ...
- JS组件系列——Bootstrap组件福利篇:几款好用的组件推荐(二)
前言:上篇 JS组件系列——Bootstrap组件福利篇:几款好用的组件推荐 分享了几个项目中比较常用的组件,引起了许多园友的关注.这篇还是继续,因为博主觉得还有几个非常简单.实用的组件,实在不愿自己 ...
- 【转】JS组件系列——Bootstrap组件福利篇:几款好用的组件推荐(二)
前言:上篇 JS组件系列——Bootstrap组件福利篇:几款好用的组件推荐 分享了几个项目中比较常用的组件,引起了许多园友的关注.这篇还是继续,因为博主觉得还有几个非常简单.实用的组件,实在不愿自己 ...
- JS组件系列——Bootstrap Select2组件使用小结
前言:在介绍select组件的时候,博主之前分享过一篇JS组件系列——两种bootstrap multiselect组件大比拼,这两个组件的功能确实很强大,只可惜没有图文结合的效果(也就是将图片放入到 ...
- JS组件系列——Bootstrap文件上传组件:bootstrap fileinput
前言:之前的三篇介绍了下bootstrap table的一些常见用法,发现博主对这种扁平化的风格有点着迷了.前两天做一个excel导入的功能,前端使用原始的input type='file'这种标签, ...
- JS组件系列——Bootstrap寒冬暖身篇:弹出框和提示框效果以及代码展示
前言:对于Web开发人员,弹出框和提示框的使用肯定不会陌生,比如常见的表格新增和编辑功能,一般常见的主要有两种处理方式:行内编辑和弹出框编辑.在增加用户体验方面,弹出框和提示框起着重要的作用,如果你的 ...
- [转]JS组件系列——Bootstrap组件福利篇:几款好用的组件推荐
本文转自:https://www.cnblogs.com/landeanfen/p/5461849.html#_label3 阅读目录 一.时间组件 1.效果展示 2.源码说明 3.代码示例 二.自增 ...
- JS组件系列——Bootstrap 树控件使用经验分享
前言:很多时候我们在项目中需要用到树,有些树仅仅是展示层级关系,有些树是为了展示和编辑层级关系,还有些树是为了选中项然后其他地方调用选中项.不管怎么样,树控件都是很多项目里面不可或缺的组件之一.今天, ...
随机推荐
- [python]沪深龙虎榜数据进一步处理,计算日后5日的涨跌幅
沪深龙虎榜数据进一步处理,计算日后5日的涨跌幅 事前数据: 前面处理得到的csv文件 文件名前加入“[wait]”等待程序处理 python代码从雅虎股票历史数据api获取数据,计算后面5日的涨跌幅 ...
- Spring 4 集成Apache CXF开发JAX-RS Web Service
什么是JAX-RS 在JSR-311规范中定义,即Java API for RESTful Web Services,一套Java API,用于开发 RESTful风格的Webservice. 工程概 ...
- angularjs结合d3js实现资源展示
转载请注明出处: 转载自Bin's Blog: angularjs & d3 实现资源展示( http://www.wenbin.cf/post/27/ ) angularjs结合d3js实 ...
- sqoop使用中的小问题
1.数据库连接异常 执行数据导出 sqoop export --connect jdbc:mysql://192.168.208.129:3306/test --username hive --P - ...
- JavaSe:-javaagent,-agentlib,-agentpath
内容简述 -javaagent,-agentlib, -agentpath 说明 -javaagent示例 -javaagent.-agentlib.-agentpath -agentlib:li ...
- SQL Server DAC——专用管理员连接
今天打开数据库刚要连接时,看到“连接到服务器”窗口,突发的想到:要是SQL Server 不再响应正常的连接请求,又想使用数据库时,我们该怎么办? 其实我们还能通过“SQL Server D ...
- 强制浏览器使用兼容模式,Web.config,httpProtocol
对于一些政府类的网站,很多支持IE6~8,而不支持使用更高版本浏览器浏览.虽然有些浏览器会自动使用兼容模式,或有些人会手动调整浏览器的兼容模式,但不是每一个人.每一款浏览器都可以做到用兼容模式.所以, ...
- PKCS#1规范阅读笔记1--------基本概念
规范中有很多数学相关的推演和计算,并不打算在这里介绍,主要介绍一下相关的计算流程及最终的签名结果. 算法可以分为:对称算法和非对称算法两大类.对称算法加密和解密都用的是同一个密钥:而非对称算法却是有一 ...
- 报表开发之批量导入导出excel
在日常使用报表过程中,会有这样一种情况,你将Excel表分发给各个员工,员工填完后,统一整理成多个Excel,你需要将这些数据,用报表的填报功能,提交录入到数据库中,这样一来可避免到服务器机房录数据的 ...
- OpenStack 行业正进入拓展期:行业云将成为新一轮工业革命的基础设施和引擎
一直在关注华为2016 Connect 大会,本来没票,后来找朋友搞到了一张,参加了大会第一天下午的会议,时间虽短,但非常有收获.本来出发前还带了纸和笔,但是到了现场才发现只带了笔记本,笔却丢下了,所 ...