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