一、业务需求及实现效果
项目涉及到订单模块,那天突然接到一个需求,说是两种不同状态的订单之间要实现插单的效果,页面上呈现方式是:左右两个Table,左边Table里面是状态为1的订单,右边Table里面是状态为2订单,左边Table里面的行数据拖动到右边Table里面指定行的位置,拖动完成后,左边表格减少一行,右边表格增加一行。除此之外,还需要撤销操作(相当于Ctrl + Z操作),能够返回到上一步的状态。可能描述会让大家模拟两可,反正已经实现了,先来看看效果图吧。
1、先看看拖动之前的效果
2、这是拖动左边表格行数据的效果
3、拖动一行完成之后表格数据的效果
4、第二次、第三次拖动完成后效果
5、右边表格上面撤销操作点击效果
6、多次点击撤销,表格回到初始状态
二、代码示例
接到需求的第一感觉是应该上Bootstrap table api里面找一下,毕竟开源的力量是强大的,或许有相关的示例呢。经过一番查找,很可惜,Bootstrap Table没有这种两张表格之间的操作。想想其实也可以理解,Bootstrap Table是针对某个动态表格数据绑定的,它的侧重点是表格内部的功能,比如表格内部行的拖拽排序(Reorder Rows)有很好的解决方案,对于像博主这样的特殊需求,似乎也应该自己去实现。
1、需求分析
既然决定自己去写,开始分析需求,似乎这个操作里面比较困难的是拖拽效果,说到拖拽效果,原来使用JsPlumb的时候那使用太多了,于是就想到了我们神奇的JQuery UI里面的draggable.js 和droppable.js。拖拽的问题解决了,那么还有一个难点,就是撤销操作怎么办?我们知道Ctrl+z的意思是还原,什么叫还原?就是返回到上一步的操作,那么前提是要能够保存上一步的状态,说到保存某一步的状态,博主就知道怎么做了,需要一个全局变量Json,里面要有三个键值对,分别是当前步骤的索引、左边表格的数据、右边表格的数据。似乎也不太难嘛,就此着手,开干。
2、代码示例
2.1 cshtml页面代码
<html>
<head>
<meta name=\"viewport\" content=\"width=device-width\" />
<title>@ViewBag.Title</title>
@Styles.Render(\"~/Content/css\")
@Styles.Render(\"~/Content/table-css\")
@Scripts.Render(\"~/bundles/jquery\")
@Scripts.Render(\"~/bundles/knockout\")
@Scripts.Render(\"~/bundles/bootstrap\")
@Scripts.Render(\"~/bundles/bootstrap-table\")
@RenderSection(\"Scripts\", false)
</head>
<body>
@RenderBody()
</body>
</html>
@{
ViewBag.Title = \"订单插单\";
Layout = \"~/Views/Shared/_Layout.cshtml\";
}
@Scripts.Render(\"~/bundles/Order/InsertOrder\")
@Styles.Render(\"~/bundles/Order/css\")
@Scripts.Render(\"~/Content/bootstrap/datepicker/js\")
@Styles.Render(\"~/Content/bootstrap/datepicker/css\")
<script src=\"~/Content/jquery-ui-1.11.4.custom/jquery-ui.min.js\"></script>
<div class=\"panel-body\" style=\"padding-bottom:0px;\">
<div class=\"panel panel-default\" style=\"margin-bottom:0px;\">
<div class=\"panel-heading\">查询条件</div>
<div class=\"panel-body container-fluid\">
<div class=\"row\">
<div class=\"col-md-3\">
<label for=\"txt_search_ordernumber\" class=\"col-sm-4 control-label\" style=\"margin-top:6px;\">订单号</label>
<span class=\"col-sm-8\">
<input type=\"text\" class=\"form-control\" id=\"txt_search_ordernumber\">
</span>
</div>
<div class=\"col-md-3\">
<label for=\"txt_search_bodynumber\" class=\"col-sm-3 control-label\" style=\"margin-top:6px;\">车身号</label>
<span class=\"col-sm-8\">
<input type=\"text\" class=\"form-control\" id=\"txt_search_bodynumber\">
</span>
</div>
<div class=\"col-md-3\">
<label for=\"txt_search_vinnumber\" class=\"col-sm-4 control-label\" style=\"margin-top:6px;\">VIN码</label>
<span class=\"col-sm-8\">
<input type=\"text\" class=\"form-control\" id=\"txt_search_vinnumber\">
</span>
</div>
<div class=\"col-md-3\">
<label for=\"txt_search_engin_code\" class=\"col-sm-4 control-label\" style=\"margin-top:6px;\">发动机号</label>
<span class=\"col-sm-8\">
<input type=\"text\" class=\"form-control\" id=\"txt_search_engin_code\">
</span>
</div>
</div>
<div class=\"collapse\" id=\"div_more_search\">
<div class=\"row\" style=\"margin-top:15px;\">
<div class=\"col-md-3\">
<label for=\"txt_search_import_startdate\" class=\"col-sm-4 control-label\" style=\"margin-top:6px;\">导入时间</label>
<span class=\"col-sm-8\">
<input type=\"text\" class=\"form-control datetimepicker\" readonly id=\"txt_search_import_startdate\">
</span>
</div>
<div class=\"col-md-3\">
<label for=\"txt_search_import_enddate\" class=\"col-sm-3 control-label\" style=\"margin-top:6px;\">至</label>
<span class=\"col-sm-8\">
<input type=\"text\" class=\"form-control datetimepicker\" readonly id=\"txt_search_import_enddate\">
</span>
</div>
<div class=\"col-md-3\">
<label for=\"txt_search_send_startdate\" class=\"col-sm-4 control-label\" style=\"margin-top:6px;\">下发时间</label>
<span class=\"col-sm-8\">
<input type=\"text\" class=\"form-control datetimepicker\" readonly id=\"txt_search_send_startdate\">
</span>
</div>
<div class=\"col-md-3\">
<label for=\"txt_search_send_enddate\" class=\"col-sm-4 control-label\" style=\"margin-top:6px;\">至</label>
<span class=\"col-sm-8\">
<input type=\"text\" class=\"form-control datetimepicker\" readonly id=\"txt_search_send_enddate\">
</span>
</div>
</div>
<div class=\"row\" style=\"margin-top:15px;\">
<div class=\"col-md-3\">
<label for=\"txt_search_carcode\" class=\"col-sm-4 control-label\" style=\"margin-top:6px;\">整车编码</label>
<span class=\"col-sm-8\">
<input type=\"text\" class=\"form-control\" id=\"txt_search_carcode\">
</span>
</div>
<div class=\"col-md-3\">
<label for=\"txt_search_vms\" class=\"col-sm-3 control-label\" style=\"margin-top:6px;\">VMS号</label>
<span class=\"col-sm-8\">
<input type=\"text\" class=\"form-control\" id=\"txt_search_vms\">
</span>
</div>
<div class=\"col-md-3\">
<label for=\"txt_search_trans_code\" class=\"col-sm-4 control-label\" style=\"margin-top:6px;\">变速箱号</label>
<span class=\"col-sm-8\">
<input type=\"text\" class=\"form-control\" id=\"txt_search_trans_code\">
</span>
</div>
</div>
</div>
<div class=\"row\" style=\"float:right;margin-right:50px;margin-top:13px;\">
<div>
<button type=\"button\" id=\"btn_query\" class=\"btn btn-primary\" style=\"margin-right:20px;width:100px;\">查询</button>
<button type=\"submit\" id=\"btn_reset\" class=\"btn btn-default\" style=\"margin-right:20px;width:100px;\">重置</button>
</div>
</div>
</div>
</div>
<div class=\"collapse_div_outside\">
<div class=\"collapse_div_inside\"></div>
<span id=\"span_collapse\" href=\"#div_more_search\" class=\"collapse_div_inside_ele\">展开<label class=\"glyphicon glyphicon-menu-down\"></label></span>
</div>
</div>
@*<div id=\"toolbar_left\" class=\"btn-group\">
</div>*@
<div id=\"toolbar_right\" class=\"btn-group\">
<button id=\"btn_cancel\" type=\"button\" class=\"btn btn-default\">
<span class=\"glyphicon glyphicon-backward aria-hidden=\"true\"></span>撤销
</button>
<button id=\"btn_insertorder\" type=\"button\" class=\"btn btn-default\">
<span class=\"glyphicon glyphicon-plus\" aria-hidden=\"true\"></span>插单
</button>
</div>
<div class=\"panel-body\" style=\"padding-top:0px;\">
<div id=\"div_tableleft\" class=\"col-md-6\">
<table id=\"tb_order_left\"></table>
</div>
<div id=\"div_tableright\" class=\"col-md-6\">
<table id=\"tb_order_right\"></table>
</div>
</div>
2.2 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();
}
});
//初始化右边表格
$(\'#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();
}
});
};
//注册表格行的draggable事件
oTableInit.InitDrag = function () {
$(\'#tb_order_left tr\').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) {
}
});
};
//注册右边表格的droppable事件
oTableInit.InitDrop = function () {
$(\"#tb_order_right\").droppable({
drop: function (event, ui) {
var arrtd = $(ui.helper[0]).find(\"td\");
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: $(ui.helper[0]).attr(\"data-uniqueid\")
};
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);
}
//插入右边表格指定位置行数据
$(\"#tb_order_right\").bootstrapTable(\"insertRow\", { index: rowIndex, row: rowdata });
$(\'#tb_order_left\').bootstrapTable(\"removeByUniqueId\", $(ui.helper[0]).attr(\"data-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_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();
});
//搜索栏展开收起点击事件
$(\"#span_collapse\").click(function () {
if ($(this).text() == \"收起\") {
$(this).html(\'展开<label class=\"glyphicon glyphicon-menu-down\"></label>\');
$(\"#div_more_search\").collapse(\'hide\');
}
else {
$(this).html(\'收起<label class=\"glyphicon glyphicon-menu-up\"></label>\');
$(\"#div_more_search\").collapse(\'show\')
}
});
};
return oInit;
};
我们重点来看几个地方的代码:
2.2.1 左边表格加载成功之后执行表格行的可拖拽。
$(\'#tb_order_left tr\').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) {
}
});
在draggable的start事件中,我们将拖拽之前的左右表格中的数据全部保存到arrdata变量中,i_statuindex这个全局变量用于记录当前这一步的索引,用于撤销操作。
2.2.2 右边表格在加载成功之后注册表格的droppable事件
$(\"#tb_order_right\").droppable({
drop: function (event, ui) {
var arrtd = $(ui.helper[0]).find(\"td\");
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: $(ui.helper[0]).attr(\"data-uniqueid\")
};
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);
}
$(\"#tb_order_right\").bootstrapTable(\"insertRow\", { index: rowIndex, row: rowdata });
$(\'#tb_order_left\').bootstrapTable(\"removeByUniqueId\", $(ui.helper[0]).attr(\"data-uniqueid\"));
oTableInit.InitDrag();
}
});
在drop事件时,取到当前拖过来的行数据,计算当前鼠标所在的位置,在右边表格指定位置插入拖过来的行数据。然后删除左边表格拖过来的行数据。
2.2.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();
});
撤销操作主要是通过全局变量arrdata里面的索引判断撤销到哪一步,然后根据索引取出当前步骤的左右表格数据,依次向两表格插入数据,然后i_statuindex依次递减,直至等于零,由于左边表格行数据全部重写加载过,所以需要重新注册可拖拽事件。就是这么简单的三步就能实现想要的效果,是不是很简单~~
以上就是本文的全部内容,希望对大家的学习有所帮助。
本文地址:https://www.stayed.cn/item/4333
转载请注明出处。
本站部分内容来源于网络,如侵犯到您的权益,请 联系我