EasyUI-DataGrid结合Undo.js实现类似Excel撤销操作

由于项目上的需要,业务人员希望系统可以像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>

效果图:
EasyUI datagrid整合undo.js实现表格撤销

参考地址:
1. undo.js: https://github.com/jzaefferer/undo
2. EasyUI: http://www.jeasyui.com

发表评论

电子邮件地址不会被公开。 必填项已用*标注