rehacer deshacer comando jquery excel jqgrid

jquery - comando - JQGRID: cualquier forma fácil de implementar deshacer en excel como la implementación de jqGrid



deshacer y rehacer en excel (2)

Editado el 14/5/12: finalmente pude saltar de mi pereza y preparar esto para compartir la implementación real EXCEL LIKE JQGRID . La selección de celda puede funcionar de forma extraña en jsfiddle en algunos navegadores, pero debería funcionar normalmente en su caja de desarrollo. ¡¡¡¡Que te diviertas!!!!

Editado el 13/09/11: Este es mi primer uso de JQGrid. Estoy usando la versión 4.1.2. Pasé algunas semanas para armar la cuadrícula de Excel y el mayor desafío fue encontrar la información correcta sobre cómo usar JQGrid. Mi configuración actual tiene muchas actualizaciones ajax y galerías de imágenes y uso del formateador jqgrid, pero lo que he puesto aquí es el código principal para poder usar JQgrid con paginación del lado del servidor, excel como copiar y pegar y demostración de algunas otras características en jqgrid. Esta es solo mi manera de retribuir por toda la ayuda que obtuve de esta comunidad.

Para las personas que acaban de ingresar a JQGrid, pronto descubrirán que hay algún desafío al usar Textarea en jqgrid. puedes encontrar algunas soluciones aquí .

Publicación original:
Solo para dar una pequeña actualización antes de hacer mi pregunta ...

He podido encontrar algunas características adicionales en el jqgrid que estoy usando (después de pasar por muchos foros), entre ellas: copiar y pegar desde Excel a jqgrid, editar celda al presionar las teclas y hacer doble clic, copiar y pegar varias celdas de un bloque a otro en la misma cuadrícula usando la selección del mouse (desde aquí Usar Javascript para ''sumar celdas seleccionadas'' en IE6 )

La mayoría de las funciones de copiado funcionan en IE solo a partir de ahora. Guardo todos los cambios juntos en el botón "Guardar", haga clic para que todas las actualizaciones en las celdas estén en pantalla solo hasta que el usuario presione el botón "Guardar".

Aunque, las cosas todavía están en flujo en este momento, me gustaría tener el diseño de la implementación en papel ahora que más tarde. Estoy buscando una manera fácil de DESHACER solo el ÚLTIMO cambio. He estado pensando en utilizar los métodos "jQuery" de datos () y "removeData ()" para implementar esto, pero si hay algo que ya exista en jqgrid framework que me ayude, me gustaría saberlo. ¿¿Alguna sugerencia??

<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>

¡Muchas gracias de antemano!


En lugar de una implementación específica de jqgrid, una forma de abordar esto sería tener una instancia de jqgrid secundaria que no esté vinculada a ningún elemento html visible.

En cualquier clase operacional clasificada como una confirmación, la instancia secundaria se configura como la actual (sin modificar) jqgrid, y la instancia primaria (es decir, la que realmente se muestra) es un duplicado de la secundaria con el cambio requerido realizado.

Entonces, todo lo que se necesitaría para una operación de deshacer es duplicar la instancia secundaria nuevamente en la primaria mostrada, por lo tanto, no necesita ningún conocimiento de la acción de compromiso específica.

También se extendería fácilmente a múltiples operaciones de deshacer. Sin embargo, podría ser una fuente de recursos.


Una posibilidad es almacenar el último valor como un atributo de la celda que se puede hacer usando la siguiente

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

donde rowid es la fila en la que está trabajando y colIndex es el número de la columna en la que desea guardar el valor. Esto creará un atributo llamado lastval que se puede usar con su función de deshacer. El inconveniente de este enfoque es que toda la grilla se actualizará en una actualización y perderá los atributos almacenados en la grilla.

Suponiendo que esto sea aceptable, entonces puede guardar el último valor de cada celda usando

loadComplete: function() { $("#list").find("td").each(function(index, elem) { $(elem).attr(''lastval'', $(elem).html()); }); },

donde ''list'' es la identificación de jqGrid que creaste inicialmente.

Puedes actualizar el lastval como parte de una devolución previa u otra devolución de llamada, dependiendo de cómo quieras mantener el lastval.

Estoy seguro de que hay técnicas más eficientes para hacer lo anterior, pero con los valores que se pierden durante la actualización, no estoy seguro de que esto realmente ayude con lo que estás tratando de hacer. Un mejor enfoque sería almacenar estos atributos en cualquier parte del DOM o en el servidor. Sin embargo, si estoy leyendo los comentarios anteriores correctamente, desea mantener el últimoval en la cuadrícula.