EasyUI-DataGrid结合Undo.js实现类似Excel撤销操作
By: Date: 2017年4月7日 Categories: 程序 标签:

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

发表回复

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