由于项目上的需要,业务人员希望系统可以像Excel一样撤销,更方便编辑,因此花了点儿时间研究怎么在EasyUI的datagrid表格上实现类似Excel的撤销操作。在网上很容易找到了Undo.js库,这是一个6年前写出来的库,源码行数不多,查看起来也比较容易,看了下作者提供的两个Demo感觉比较适用,并且结合例子也很好做开发,所以就用它来做表格撤销操作的功能。其中最核心的就是堆栈的构造以及undo及redo方法的操作。代码是在官方DataGrid的“Cell Editing in DataGrid”的Demo上进行整合,考虑了删除行,插入行的操作,代码比较简单,就不多说了。
工具:
1. easyui 1.5.1
2. undo.js
具体实现:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>演示整合DataGrid与Undo.js</title>
<script src="../../undo.js"></script>
<link href="../easyui/themes/metro/easyui.css" rel="stylesheet" type="text/css">
<script type="text/javascript" src="../jquery.min.js"></script>
<script type="text/javascript" src="../easyui/jquery.easyui.js"></script>
<script type="text/javascript" src="../easyui/datagrid-cellediting.js"></script>
</head>
<body>
<div id="tool">
<button class="appendRow" href="#">AppendRow</button>
<button class="insertRow" href="#">InsertRow</button>
<button class="deleteRow" href="#">DeleteRow</button>
<button class="undo" href="#">Undo</button>
<button class="redo" href="#">Redo</button>
<button class="save" href="#">Save<span class="dirty">*</span></button>
</div>
<table id="dg" title="演示整合DataGrid与Undo.js" data-options="fit:true,toolbar:'#tool',rownumbers:true,fitColumns:true">
<thead>
<tr>
<th data-options="field:'itemid',width:100">Item ID</th>
<th data-options="field:'productid',width:100,editor:'text'">Product</th>
<th data-options="field:'listprice',width:80,align:'right',editor:{type:'numberbox',options:{precision:1}}">List Price</th>
<th data-options="field:'unitcost',width:80,align:'right',editor:'numberbox'">Unit Cost</th>
<th data-options="field:'attr1',width:200,editor:'text'">Attribute</th>
<th data-options="field:'status',width:60,align:'center',editor:{type:'checkbox',options:{on:'P',off:''}}">Status</th>
<th data-options="field:'apt1',width:100,editor:'text'">Apt1</th>
<th data-options="field:'takeOffTime1',width:80,editor:'text'">Take Off Time1</th>
<th data-options="field:'deskAtTime1',width:80,editor:'text'">Desk At Time1</th>
<th data-options="field:'apt2',width:100,editor:'text'">Apt2</th>
<th data-options="field:'takeOffTime2',width:80,editor:'text'">Take Off Time2</th>
<th data-options="field:'deskAtTime2',width:80,editor:'text'">Desk At Time2</th>
<th data-options="field:'apt3',width:100,editor:'text'">Apt3</th>
</tr>
</tr>
</thead>
</table>
<script type="text/javascript">
var old_value=null;
var new_value=null;
var cust_field=null;
var row_index=null;
$(function() {
var stack = new Undo.Stack(),
//现有纪录的编辑命令
EditCommand = Undo.Command.extend({
constructor: function(index,field,oldValue ,newValue) {
this.index=index;
this.field= field;
this.oldValue=oldValue;
this.newValue=newValue;
},
execute: function() {
},
undo: function() {
var data= $("#dg").datagrid("getData");
data.rows[this.index][this.field]=this.oldValue;
$("#dg").datagrid("loadData",data);
},
redo: function() {
var data= $("#dg").datagrid("getData");
data.rows[this.index][this.field]=this.newValue;
$("#dg").datagrid("loadData",data);
}
}),
//对表格增加行,删除行的命令
OperationCommand = Undo.Command.extend({
constructor: function(index,oldRow,newRow) {
this.index=index;
this.oldRow=oldRow;
this.newRow=newRow;
},
execute: function() {
},
undo: function() {
var data= $("#dg").datagrid("getData").rows;
if(this.newRow){
//insertRow || appendRow
data.splice(this.index, 1);
}else{
//deleteRow
data.splice(this.index, 0, this.oldRow);
}
$("#dg").datagrid("loadData",data);
},
redo: function() {
var data= $("#dg").datagrid("getData").rows;
if(this.newRow){
//insertRow || appendRow
data.splice(this.index, 0, this.newRow);
}else{
//deleteRow
data.splice(this.index, 1);
}
$("#dg").datagrid("loadData",data);
}
});
stack.changed = function() {
stackUI();
};
var undo = $(".undo"),
redo = $(".redo"),
dirty = $(".dirty");
function stackUI() {
//控制undo,redo,按钮是否可用。
undo.attr("disabled", !stack.canUndo());
redo.attr("disabled", !stack.canRedo());
dirty.toggle(stack.dirty());
}
stackUI();
//添加undo,redo,save按钮的click事件
$(document.body).delegate(".undo, .redo, .save", "click", function() {
$("#dg").datagrid("acceptChanges");
var what = $(this).attr("class");
stack[what]();
if(what=="save"){
alert("Submit All Changes;");
}
return false;
});
//添加appendRow,insertRow,deleteRow的click事件
$(document.body).delegate(".appendRow, .insertRow, .deleteRow", "click", function() {
var what = $(this).attr("class");
if (what == "appendRow") {
var length=$("#dg").datagrid("getData").rows.length;
var appendRow= {};
$("#dg").datagrid("appendRow",{});
$("#dg").datagrid("scrollTo",length);
stack.execute(new OperationCommand(length,null,appendRow));
} else if(what == "insertRow"){
var insertIndex= 3; //insert to line 3
var insertRow= {};
$("#dg").datagrid("insertRow",{
index: insertIndex,
row: insertRow
});
stack.execute(new OperationCommand(insertIndex,null,insertRow));
} else {
var deleteIndex= 3; //delete line 3
var deleteRow= $("#dg").datagrid("getData").rows[deleteIndex];
$("#dg").datagrid("deleteRow",deleteIndex);
stack.execute(new OperationCommand(deleteIndex,deleteRow,null));
}
return false;
});
//定义datagrid的事件
var dg = $('#dg').datagrid({
data: data,
onClickCell:function(index, field, value){
old_value=value;
cust_field = field;
row_index= index;
},
onAfterEdit:function(index, row, changes){
if (changes.hasOwnProperty(cust_field) && changes[cust_field]!=old_value) {
stack.execute(new EditCommand(index,cust_field,old_value,changes[cust_field]));
old_value = changes[cust_field];
}
}
});
//datagrid 列编辑扩展
dg.datagrid('enableCellEditing').datagrid('gotoCell', {
index: 0,
field: 'productid'
});
});
var data = [
{"productid":"FI-SW-01","productname":"Koi","unitcost":10.00,"status":"P","listprice":36.50,"attr1":"Large","itemid":"EST-1","apt1":"北京","takeOffTime1":"1010","deskAtTime1":"1350","apt2":"上海","takeOffTime2":"1710","deskAtTime2":"1859","apt3":"深圳"},
{"productid":"K9-DL-01","productname":"Dalmation","unitcost":12.00,"status":"P","listprice":18.50,"attr1":"Spotted Adult Female","itemid":"EST-10","apt1":"北京","takeOffTime1":"1010","deskAtTime1":"1350","apt2":"上海","takeOffTime2":"1710","deskAtTime2":"1859","apt3":"深圳"},
{"productid":"RP-SN-01","productname":"Rattlesnake","unitcost":12.00,"status":"P","listprice":38.50,"attr1":"Venomless","itemid":"EST-11","apt1":"北京","takeOffTime1":"1010","deskAtTime1":"1350","apt2":"上海","takeOffTime2":"1710","deskAtTime2":"1859","apt3":"深圳"},
{"productid":"RP-SN-01","productname":"Rattlesnake","unitcost":12.00,"status":"N","listprice":26.50,"attr1":"Rattleless","itemid":"EST-12","apt1":"北京","takeOffTime1":"1010","deskAtTime1":"1350","apt2":"上海","takeOffTime2":"1710","deskAtTime2":"1859","apt3":"深圳"},
{"productid":"RP-LI-02","productname":"Iguana","unitcost":12.00,"status":"N","listprice":35.50,"attr1":"Green Adult","itemid":"EST-13","apt1":"北京","takeOffTime1":"1010","deskAtTime1":"1350","apt2":"上海","takeOffTime2":"1710","deskAtTime2":"1859","apt3":"深圳"},
{"productid":"FL-DSH-01","productname":"Manx","unitcost":12.00,"status":"P","listprice":158.50,"attr1":"Tailless","itemid":"EST-14","apt1":"北京","takeOffTime1":"1010","deskAtTime1":"1350","apt2":"上海","takeOffTime2":"1710","deskAtTime2":"1859","apt3":"深圳"},
{"productid":"FL-DSH-01","productname":"Manx","unitcost":12.00,"status":"P","listprice":83.50,"attr1":"With tail","itemid":"EST-15","apt1":"北京","takeOffTime1":"1010","deskAtTime1":"1350","apt2":"上海","takeOffTime2":"1710","deskAtTime2":"1859","apt3":"深圳"},
{"productid":"FL-DLH-02","productname":"Persian","unitcost":12.00,"status":"N","listprice":23.50,"attr1":"Adult Female","itemid":"EST-16","apt1":"北京","takeOffTime1":"1010","deskAtTime1":"1350","apt2":"上海","takeOffTime2":"1710","deskAtTime2":"1859","apt3":"深圳"},
{"productid":"FL-DLH-02","productname":"Persian","unitcost":12.00,"status":"P","listprice":89.50,"attr1":"Adult Male","itemid":"EST-17","apt1":"北京","takeOffTime1":"1010","deskAtTime1":"1350","apt2":"上海","takeOffTime2":"1710","deskAtTime2":"1859","apt3":"深圳"},
{"productid":"AV-CB-01","productname":"Amazon Parrot","unitcost":92.00,"status":"N","listprice":63.50,"attr1":"Adult Male","itemid":"EST-18","apt1":"北京","takeOffTime1":"1010","deskAtTime1":"1350","apt2":"上海","takeOffTime2":"1710","deskAtTime2":"1859","apt3":"深圳"}
];
</script>
</body>
</html>
效果图:
参考地址:
1. undo.js: https://github.com/jzaefferer/undo
2. EasyUI: http://www.jeasyui.com