jquery - JQGRID:在 Excel 上实现撤消的任何简单方法,例如 jqGrid 实现

标签 jquery excel jqgrid

编辑于 2012 年 5 月 14 日: 我终于能够跳出我的懒惰,准备这个分享实际EXCEL LIKE JQGRID implementation 。在少数浏览器上的 jsfiddle 上,单元格选择可能会很奇怪,但在您的开发盒上应该可以正常工作。玩得开心!!!!

编辑于 2011 年 9 月 13 日: 这是我第一次使用 JQGrid。我使用的是 4.1.2 版本。我花了几周时间来整理类似 excel 的网格,最大的挑战是找到有关如何使用 JQGrid 的正确信息。我当前的设置有很多 ajax 更新和图像库以及 jqgrid 格式化程序的使用,但我在这里放置的是能够使用 JQgrid 与服务器端分页、excel 等复制粘贴以及其他一些功能演示的主要代码jqgrid。这只是我回馈从这个社区获得的所有帮助的方式。

对于刚接触 JQGrid 的人来说,您很快就会发现在 jqgrid 中使用 Textarea 存在一些挑战。你可以找到一些解决方案here .

原帖:
只是在提出我的问题之前提供一点更新......

我已经能够在我正在使用的 jqgrid 上提出一些附加功能(在浏览了许多论坛之后),包括:从 Excel 到 jqgrid 来回复制粘贴、在按键和 dblclick 上编辑单元格,使用鼠标选择将多个单元格从一个 block 复制并粘贴到同一网格上的另一个 block (从此处 Using Javascript to 'sum selected cells' in IE6 )

到目前为止,大多数复制粘贴功能仅适用于 IE。我在单击“保存”按钮时将所有更改一起保存,以便单元格上的所有更新仅在用户单击“保存”按钮之前显示在屏幕上。

尽管目前情况仍在不断变化,但我希望现在就将实现设计写在纸上,而不是稍后。我正在寻找一种简单的方法来撤消最后的更改。我一直在考虑使用 jQuery 的“data()”和“removeData()”方法来实现此目的,但如果 jqgrid 框架中已有任何内容可以提供帮助,我想知道。有什么建议吗??

<style type="text/css">
    .sel {background-color: #96B9DC !important; }
    .altered {}
</style>
<script type="text/javascript">
    var enableOnSelectEvent = false; // handle text selection
</script>
<div style="width:100%; background-color:#FFF; border:1px solid #000;"><input id="btnsavechanges" value="Save Changes" style="width:120px;" class="formbutton ui-corner-all" type="button" onclick="getChanges(); return false;" /></div>
<table id="grd_asset" width="100%" onSelectStart="return enableOnSelectEvent;"></table>
<div id="pfrmac" style='width:100%;'></div>
<input type="hidden" id="hidSelected" value="" />

<!-- copy content from the grid cells -->
<input type="hidden" id="hidCopiedText" value="" />

<!-- Start and End of cell selection -->
<input type="hidden" id="hidStartCell" value="" />
<input type="hidden" id="hidEndCell" value="" />

<!-- Start and End of last modified cell(s) -->
<input type="hidden" id="hidModStartCell" value="" />
<input type="hidden" id="hidModEndCell" value="" />

<script type="text/javascript">
    /*************************************************/
    /**************** Grid Utilities  ****************/
    /*************************************************/
    FnGrid = function () {
        this.GridColumns = function () {
            return assetGrid.jqGrid('getGridParam', 'colModel');
        }
        this.GetSelCells = function () {
            return assetGrid.find("td.sel");
        }
        this.ClearSelection = function () {
            assetGrid.find("td").removeClass("sel");
        }
        this.ClearSavedHistory = function () {
            assetGrid.removeData();
        }
        this.ClearMarkedChanges = function () {
            assetGrid.find("tr").removeClass("altered");
        }
        this.GetRowCells = function (cell) {
            return cell.parent().children("td")
        }
        this.GetRowId = function (cell) {
            var row = cell.closest('tr.jqgrow');
            return row.attr('id');
        }
        this.GetRowIndex = function (cell) {
            var cellrow = cell.parent();
            return cellrow.parent().children("tr").index(cellrow);
        }
        this.GetColIndex = function (cell) {
            return cell.parent().children("td").index(cell);
        }
        this.IsInEditMode = function () {
            var savedRows = assetGrid.getGridParam('savedRow');
            return (savedRows && savedRows.length > 0);
        }
        this.PutCellInEdit = function (cell, irow, icol, edit) {
            assetGrid.editCell(irow, icol, edit);
            // transfer focus to the input
            var inp = $(cell).children("input")
            if (inp && inp.length > 0) {
                $(inp[0]).val('');
                $(inp[0]).focus();
            }
        }
        this.HandleEditMode = function (cell, e) {
            var ctrl = e.ctrlKey;
            var alt = e.altKey;

            var keyCode = (e.keyCode ? e.keyCode : e.which);
            if (keyCode) {
                if (keyCode >= 32 && keyCode <= 126 && !ctrl && !alt) {
                    // switch the cell to edit mode if not already
                    if (!($(cell).hasClass("edit-cell"))) {
                        this.PutCellInEdit(cell, this.GetRowIndex($(cell)), this.GetColIndex($(cell)), true);                        }
                }
            }
            return true;
        }
        this.HandleInputNavigation = function (ele, evt) {
            evt = window.event || evt;

            switch (evt.keyCode) {
                // down arrow                   
                case 40:
                    if (!$(ele).parent().hasClass("altered"))
                        $(ele).parent().addClass("altered");

                    irow = this.GetRowIndex($(ele).parent());
                    icol = this.GetColIndex($(ele).parent())
                    var prevcell = irow + "," + icol;
                    $("#hidModStartCell").val(prevcell);
                    $("#hidModEndCell").val(prevcell);

                    downele = $(ele).parent()
                            .parent()
                            .next()
                            .children("td")[this.GetColIndex($(ele).parent())];

                    this.ClearSelection();
                    assetGrid.editCell(this.GetRowIndex($(downele)), this.GetColIndex($(downele)), true);
                    break;

                // up arrow                   
                case 38:
                    if (!$(ele).parent().hasClass("altered"))
                        $(ele).parent().addClass("altered");

                    irow = this.GetRowIndex($(ele).parent());
                    icol = this.GetColIndex($(ele).parent())
                    var prevcell = irow + "," + icol;
                    $("#hidModStartCell").val(prevcell);
                    $("#hidModEndCell").val(prevcell);

                    topele = $(ele).parent()
                            .parent()
                            .prev()
                            .children("td")[this.GetColIndex($(ele).parent())];

                    if (this.GetRowIndex($(topele)) <= 0) break;

                    this.ClearSelection();
                    assetGrid.editCell(this.GetRowIndex($(topele)), this.GetColIndex($(topele)), true);
                    break;
            }
        }
    }

    var autocomp = new AutoCompleteRequest();
    var lastSel = "";
    var assetGrid = $('#grd_asset');
    var start = null;
    var fnassetgrid = new FnGrid();
    var lastSel = -1;

    function selectTo(cell) {
        if (start == null)
            return;
        fnassetgrid.ClearSelection();
        var stop = $(cell);
        var tbl = start.closest("table");
        var rs = tbl.children("tbody").children("tr");
        var r0 = rs.index(start.parent()), c0 = fnassetgrid.GetColIndex(start);
        var r1 = rs.index(stop.parent()), c1 = fnassetgrid.GetColIndex(stop);
        var concat = "";
        for (var i = r0; i <= r1; i++) {
            var cells = $(rs.get(i)).children("td");
            var rowid = 0;
            for (var j = c0; j <= c1; j++) {
                var cell = $(cells.get(j));
                if (rowid == 0) rowid = fnassetgrid.GetRowId(cell);
                if (cell.is(":hidden")) continue;
                cell.addClass("sel");
                concat += assetGrid.getCell(rowid, j) + "\t";
            }
            if (concat.lastIndexOf("\t") == concat.length - 1)
                concat = concat.substring(0, concat.lastIndexOf("\t"));

            concat += escape("\r\n");
        }
        $("#hidSelected").val(concat.trim());
    }


    $(document).ready(function () {
        /*************************************************/
        /******************* THE GRID  *******************/
        /*************************************************/
        assetGrid.jqGrid({
            ajaxGridOptions: { contentType: "application/json; charset=utf-8", type: "POST" },
            url: '../api/yourservices.asmx/GetData',
            datatype: 'json',
            serializeGridData: function (postData) {
                if (postData.searchField === undefined) postData.searchField = null;
                if (postData.searchString === undefined) postData.searchString = null;
                if (postData.searchOper === undefined) postData.searchOper = null;
                if (postData.filters === undefined) postData.filters = null;
                return JSON.stringify(postData);
            },
            colNames: [' ', 'AssetId', 'Item#', 'Make', 'Description'],
            colModel: [
                { name: 'ctrls', width: 80, fixed: true, sortable: false, resize: false, formatter: 'actions',
                    formatoptions: { keys: true }
                },
                { name: 'AssetID', label: 'AssetID', width: 65, key: true, hidden: true },
                { name: 'Sequence', label: 'Item#', width: 50, align: "right", sorttype: 'int', sortable: true, editoptions: { dataEvents: [{ type: 'keydown', fn: function (e) { fnassetgrid.HandleInputNavigation(this, e); } }]} },
                { name: 'Make', label: 'Make', width: 105, editable: true, edittype: 'text', editoptions: {
                    size: 18,
                    dataEvents: [{
                        type: 'focus',
                        fn: function (e) {
                            $(this).autocomplete({
                                source: autocomp.source,
                                delay: autocomp.delay,
                                minLength: autocomp.minLength
                            });

                            $(this).bind("autocompleteopen", autocomp.open);
                            $(this).bind("autocompleteclose", autocomp.close);
                        }
                    }]
                }
                },
                { name: 'Description', label: 'Description', fixed: false, editable: true, edittype: 'textarea', unformat: unfrmttextarea, editoptions: { rows: "10", cols: "40"} }
            ],
            rowNum: 10, /* no of recs in a grid */
            width: 1330,
            rowList: [10, 20, 30], /* array to construct a select box element in the pager */
            pager: '#pfrmac',
            sortname: 'AssetID', /* initial sorting column */
            viewrecords: true,  /* display the number of total records on the pager bar */
            pginput: true,
            sortorder: "desc",
            cellEdit: true,
            shrinkToFit: true,
            jsonReader: {
                root: function (obj) { return obj.d.SearchResultSet; },
                page: function (obj) { return obj.d.PageNum; }, // current page of the query
                total: function (obj) { return obj.d.TotalPages; }, // total pages for the query
                records: function (obj) { return obj.d.TotalNoOfSearchResultItems; },
                id: "AssetID",
                repeatitems: false,
                userdata: function (obj) {
                    extendUserSession();
                    return { "Error": obj.d.Error, "SearchResultSet": obj.d.SearchResultSet }
                }
            },
            loadonce: false,
            caption: "Asset list",
            height: '100%',
            cellsubmit: 'clientArray',
            beforeEditCell: function (rowid, cellname, value, iRow, iCol) {
                enableOnSelectEvent = true;
            },
            beforeSaveCell: function (rowid, cellname, value, iRow, iCol) {
                savedrow = assetGrid.getGridParam('savedRow');
                if (savedrow && savedrow.length > 0) {
                    if (savedrow[0].id == iRow && savedrow[0].ic == iCol && savedrow[0].v != value) {
                        tr = $('#' + rowid);
                        if (tr && !tr.hasClass("altered")) {
                            tr.addClass("altered");
                            there_are_unsaved_changes = 1;
                        }
                    }
                }
            },
            afterSaveCell: function (rowid, cellname, value, iRow, iCol) {
                enableOnSelectEvent = false;
            },
            afterRestoreCell: function (rowid, value, iRow, iCol) {
                enableOnSelectEvent = false;
            },
            loadComplete: function (data) {
                if (assetGrid.getGridParam('userData').Error && assetGrid.getGridParam('userData').Error != '')
                    alert("Error: " + assetGrid.getGridParam('userData').Error);
            },
            gridComplete: function () {
                rowindex = 1;
                rows = assetGrid.find("tr");

                if (rows && rows.length > 1) {
                    for (i = 1; i < rows.length; i++) {
                        $(rows[i]).find("td").each(function (evt) {
                            evt = window.event || evt;

                            start = $(this);
                            colindex = fnassetgrid.GetColIndex(start);
                            if (colindex > 0) {
                                $(this).click(function () {
                                    if (!($(this).hasClass("edit-cell")))
                                        return false;
                                }).dblclick(function () {
                                    if (!($(this).hasClass("edit-cell"))) {
                                        fnassetgrid.PutCellInEdit(this, fnassetgrid.GetRowIndex($(this)), fnassetgrid.GetColIndex($(this)), true);
                                        return;
                                    }
                                    else
                                        return true;
                                }).mousedown(function () {
                                    if (fnassetgrid.IsInEditMode())
                                        return true;
                                    start = $(this);
                                    selectTo(this);
                                    return false;
                                }).mouseover(function () {
                                    if (fnassetgrid.IsInEditMode()) return true;
                                    selectTo(this);
                                }).mouseup(function () {
                                    if (fnassetgrid.IsInEditMode()) return true;
                                    selectTo(this);
                                    $("#hidEndCell").val(fnassetgrid.GetColIndex($(this)));
                                    start = null;
                                }).keypress(function (e) {
                                    fnassetgrid.HandleEditMode(this, e);
                                });
                            }
                        });
                        rowindex++;
                    }
                }
            }
        });

        function unfrmttextarea(cellvalue, options, cellobject) {
            return cellvalue;
        }

        $("body").mouseup(function () {
            start = null;
        });


        /*************************************************/
        /*********** Global KEYUP integration  ***********/
        /*************************************************/
        $(assetGrid).keyup(function (e) {
            var ctrl = e.ctrlKey
            var key = e.charCode || e.keyCode || 0;

            if ((ctrl && key == 88) /* CUT */ || (ctrl && key == 67) /* COPY */ || (ctrl && key == 86) /* PASTE */ || (ctrl && key == 90) /* UNDO */) {

                if ((ctrl && key == 88) /* CUT */ || (ctrl && key == 67) /* COPY */) {
                    if (fnassetgrid.IsInEditMode()) return true;
                    CopyToClipboard("hidSelected");

                    var selectedCells = fnassetgrid.GetSelCells();

                    if (selectedCells && selectedCells.length > 0) {
                        $("#hidStartCell").val(fnassetgrid.GetRowIndex($(selectedCells[0])) + "," + fnassetgrid.GetColIndex($(selectedCells[0])));
                        $("#hidEndCell").val(fnassetgrid.GetRowIndex($(selectedCells[selectedCells.length - 1])) + "," + fnassetgrid.GetColIndex($(selectedCells[selectedCells.length - 1])));
                        $("#hidCopiedText").val($("#hidSelected").val());
                    }
                    else {
                        $("#hidStartCell").val('');
                        $("#hidEndCell").val('');
                    }

                    if (ctrl && key == 88) /* CUT */{
                        assetGrid.find("td.sel").each(function () {
                            row = $(this).closest('tr.jqgrow');
                            rowId = row.attr('id');
                            assetGrid.setCell(rowId, (fnassetgrid.GridColumns())[fnassetgrid.GetColIndex($(this))].name, '', '', '', true);
                        });
                        fnassetgrid.ClearSelection();
                    }
                }
                else if (ctrl && key == 86) /* PASTE */{
                    var clipboardata = getClipboardData();
                    if (get_objtype(clipboardata) != "[object String]") {
                        alert("The data you are pasting either is empty or incompatible");
                        return false;
                    }

                    pasteinfo(assetGrid, clipboardata);
                }
                else if ((ctrl && key == 90) /* UNDO */) {
                // TBD : No jqgrid features available to get the help 
                }
                return false; // prevent bubbling
            }
            else
                return true; // let it bubble
        });
    });


    /********************************************************************/
    /*********** Method to retrieve and submit altered asset information ***********/
    /********************************************************************/
    function getChanges() {
        var editedxml = "<?xml version='1.0' encoding='utf-8' ?\>\n";
        editedxml += "<ASSETS>\n";
        assetGrid.find("tr.altered").each(function () {
            editedxml += "<ASSET>\n";
            $(this).children("td").each(function () {
                colindex = fnassetgrid.GetColIndex($(this));                    
                if (colindex > 0) {
                    editedxml += "<" + (fnassetgrid.GridColumns())[colindex].name.toUpperCase() + ">" + $(this).text().trim() + "</" + (fnassetgrid.GridColumns())[colindex].name.toUpperCase() + ">\n";
                }
            })
            editedxml += "</ASSET>\n";
        })
        editedxml += "</ASSETS>";

        fnassetgrid.ClearMarkedChanges();

        //TBD: submit XML to an AJAX service
    }


    var _browserPasteData = null;
    function getClipboardData() {
        if (_browserPasteData) // Safari/Chrome logic
            return _browserPasteData;
        if (window.clipboardData) // IE logic
        {
            return window.clipboardData.getData("Text");
        }
        else if (typeof (netscape) != "undefined") // Firefox logic
        {
            netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
            var clip = Components.classes["@mozilla.org/widget/clipboard;1"].createInstance(Components.interfaces.nsIClipboard);
            var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
            trans.addDataFlavor("text/unicode");
            clip.getData(trans, clip.kGlobalClipboard);
            var str = new Object();
            var len = new Object();
            trans.getTransferData("text/unicode", str, len);
            if (str)
                return str.value.QueryInterface(Components.interfaces.nsISupportsString).toString();
        }
        return null;
    }
    // In Safari/Chrome the clipboard data can only be accessed
    // from the onpaste event. In this sample the event is handled 
    // off the body element: <body onpaste="browserPaste(event)">
    function browserPaste(e) {
        _browserPasteData = e.clipboardData && e.clipboardData.getData ?
            e.clipboardData.getData('text/plain') : null;
    }

    function pasteinfo(objGrid, info) {
        selectedCells = fnassetgrid.GetSelCells();
        firstcell = $(selectedCells[0]);
        firstselectedcolindex = fnassetgrid.GetColIndex(firstcell);
        rowcellscount = fnassetgrid.GetRowCells(firstcell).length;

        if (firstselectedcolindex == 0) {
            alert("You cannot paste into an non-editable column");
            return false;
        }

        if (selectedCells && selectedCells.length > 0) {
            // if the clipboard info is from the asset grid
            if (info && info == $("#hidCopiedText").val()) {
                // get the index values of last copied source cell
                hidStartCell = -1;
                if ($("#hidStartCell").val() != '' && $("#hidStartCell").val().split(',').length > 1) {
                    hidStartCell = $("#hidStartCell").val().split(',')[1];
                }

                // if columns of source and dest do not match, throw warning
                if (firstselectedcolindex != hidStartCell) {
                    if (!confirm("The data you are pasting comes from a different set of \ncolumns than those that you are pasting into.\n\nAre you sure you want to paste into these columns?"))
                        return false;
                }
            }

            $("#hidModStartCell").val(fnassetgrid.GetRowIndex(firstcell) + "," + firstselectedcolindex);

            var prevcell = null;
            // remove the last "line break" and break clipboard info into lines
            datarows = unescape(info).replace(/\r\n$/, '').split("\r\n");
            if (datarows && datarows.length > 0) {
                currentrow = firstcell.parent();
                currentcell = firstcell;

                // if the source is a single cell, allow it to be pasted over multiple cells
                if (datarows.length == 1 && datarows[0].split("\t").length == 1) {
                    copydata = datarows[0].split("\t");

                    $.each(selectedCells, function (index, value) {
                        prevcell = $(value);
                        if (!prevcell.parent().hasClass("altered")) {
                            prevcell.parent().addClass("altered");
                            there_are_unsaved_changes = 1;
                        }
                        var rowId = prevcell.closest('tr.jqgrow').attr('id');
                        var icol = fnassetgrid.GetColIndex(prevcell);
                        assetGrid.setCell(rowId, (fnassetgrid.GridColumns())[icol].name, copydata[0], '', '', true);
                    });
                }
                else {
                    for (i = 0; i < datarows.length && currentrow.length > 0; ++i) {
                        if (datarows[i] == '') break;
                        // break each lines into columns
                        datarows[i] = datarows[i].split("\t");
                        var row = null;
                        var rowId = null;
                        var rowindex = null;
                        for (j = 0; j < datarows[i].length && currentcell.length > 0; ++j) {
                            // mark the row as altered
                            if (!currentcell.parent().hasClass("altered")) {
                                currentcell.parent().addClass("altered");
                                there_are_unsaved_changes = 1;
                            }
                            // for each outer iteration get the rowid
                            if (row == null) {
                                row = (currentcell).closest('tr.jqgrow');
                                rowId = row.attr('id');
                            }
                            var icol = fnassetgrid.GetColIndex(currentcell);
                            assetGrid.setCell(rowId, (fnassetgrid.GridColumns())[icol].name, datarows[i][j], '', '', true);
                            prevcell = currentcell;

                            // advance to the next visible cell -- only consider pasting into visible columns
                            do {
                                currentcell = currentcell.next();
                            }
                            while ((currentcell.length > 0) && currentcell.is(":hidden"))
                        }

                        currentrow = currentrow.next();
                        currentcell = $(currentrow.children("td")[firstselectedcolindex]);
                    }
                }
            }
        }

        if (prevcell.length > 0)
            $("#hidModEndCell").val(fnassetgrid.GetRowIndex(prevcell) + "," + fnassetgrid.GetColIndex(prevcell));
    }

</script>

非常感谢!

最佳答案

一种可能性是将最后一个值存储为单元格的属性,这可以使用以下代码来完成

$('#' + rowid + ' > td:eq(' + colIndex + ')').attr('lastval', valueToSave);

其中 rowid 是您正在处理的行,colIndex 是您希望保存值的列号。这将创建一个名为 lastval 的属性,可与您的撤消函数一起使用。这种方法的缺点是整个网格将在刷新时更新,并且您将丢失存储在网格中的属性。

假设这是可以接受的,那么您可以使用

保存每个单元格的最后一个值
loadComplete: function() {
    $("#list").find("td").each(function(index, elem) {
        $(elem).attr('lastval', $(elem).html());
    });
},

其中“list”是您最初创建的 jqGrid 的 ID。

您可以将lastval作为beforeSubmit或其他回调的一部分进行更新,具体取决于您想要如何维护lastval。

我确信有更有效的技术可以完成上述操作,但是随着刷新过程中值的丢失,我不确定这是否真的有助于您尝试做的事情。更好的方法是将这些属性存储在 DOM 中的其他位置或存储回服务器上。但是,如果我正确地阅读了上面的评论,那么您希望将 lastval 保留在网格本身中。

关于jquery - JQGRID:在 Excel 上实现撤消的任何简单方法,例如 jqGrid 实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7016109/

相关文章:

php - jQuery定位,PHP获取URL,图片总是加载不出来

jquery - 如何模仿 jquery 中的 marque 滚动行为?

jQuery 按数据顺序淡入淡出

excel - VBA循环工作表: Delete rows if cell doesn't contain

vba - Excel 查找数字组合的函数

jquery - Jqgrid表格编辑出错

javascript - jqGrid 滚动错误与大行

javascript - css 试图调整 jQuery datepicker 的大小

arrays - 在 EXCEL 中使用多个表的条件求和

php - jqgrid 编辑操作如何将变量发送到服务器?