于2012年5月14日编辑: 我终于能够跳出懒惰并准备分享实际的EXCEL LIKE JQGRID implementation。在少数浏览器上,单元格选择可能在jsfiddle上很奇怪,但它应该在你的开发盒上正常工作。玩得开心!!!!
于2011年9月13日编辑: 这是我第一次使用JQGrid。我正在使用ver 4.1.2。我花了几个星期的时间来整理像网格一样的excel,最大的挑战是找到关于如何使用JQGrid的正确信息。我目前的设置有很多ajax更新和图像库以及jqgrid格式化程序的使用,但我在这里放的主要代码是能够使用JQgrid与服务器端分页,excel像复制粘贴和其他一些功能的演示jqGrid的。这只是我回馈我从这个社区得到的所有帮助的方式。
对于刚刚跳到JQGrid的人,您很快就会发现在jqgrid中使用Textarea存在一些挑战。你可以找到一些解决方案here。
原帖:
只是在提出我的问题之前给予一点更新......
我已经能够在我正在使用的jqgrid上提出一些额外的功能(经过许多论坛之后),包括:从Excel到jqgrid来回复制粘贴,在keypress和dblclick上编辑单元格,使用鼠标选择(从此处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>
非常感谢提前!
答案 0 :(得分:2)
一种可能性是将最后一个值存储为单元格的属性,可以使用以下
来完成$('#' + 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保留在网格中。
答案 1 :(得分:1)
而不是jqgrid特定的实现,一种方法是使用一个辅助jqgrid实例,它不绑定到任何可见的html元素。
在任何被归类为提交的操作中,辅助实例被设置为当前(未更改的)jqgrid,并且主实例(即实际显示的实例)是辅助实例的副本,并且需要进行所需的更改。
然后撤消操作所需要的只是将辅助实例复制回主显示的实例,因此不需要了解具体的提交操作。
还可以轻松扩展到多个撤消操作。可能是一种资源匮乏。
答案 2 :(得分:0)
2^99
在给定代码中缺少var autocomp = new AutoCompleteRequest();
函数或类的这一行,请查看此内容。