最近在研究easyuidatagrid 批量编辑和提交 使其可以动态显示所修改过的单元格(当修改过的单元格的值和最初的值不同时,更改颜色,并且忽略值类型,因为easyui编辑器会将所有修改过的值转为string类型)
我们先看一下效果图
easyui编辑时 都会更改值的类型 所以如过一个值是int 或其他类型的话。我们哪怕没有改变他的值 只是点击编辑在退出 easyui也会默认你是修改过的 所以我需要拿到第一次加载数据进行备份 将修改后的数据和初始数据对比 我们在代码在说
首先我们需要引入easyui
<!-- 引入JQuery -->
<script type="text/javascript" src="easyui/jquery.min.js"></script>
<!-- 引入EasyUI -->
<script type="text/javascript" src="easyui/jquery.easyui.min.js"></script>
<!-- 引入EasyUI的中文国际化js,让EasyUI支持中文 -->
<script type="text/javascript" src="easyui/locale/easyui-lang-zh_CN.js"></script>
<link rel="stylesheet" type="text/css" href="easyui/demo/demo.css">
<!-- 引入EasyUI的样式文件-->
<link rel="stylesheet" href="easyui/themes/default/easyui.css"
type="text/css" />
<!-- 引入EasyUI的图标样式文件-->
<link rel="stylesheet" href="easyui/themes/icon.css" type="text/css" />
创建一个table
<body>
<table id="dg"></table>
</body>
然后初始化datagrid
<script type="text/javascript">
//首先我们需要一下工具方法
//为数组添加一些方法
//去重
Array.prototype.unique = function() {
this.sort();
var re = [ this[0] ];
for (var i = 1; i < this.length; i++) {
if (this[i] !== re[re.length - 1]) {
re.push(this[i]);
}
}
return re;
}
//判断元素是否存在
Array.prototype.contains = function(obj) {
var i = this.length;
while (i--) {
if (this[i] === obj) {
return true;
}
}
return false;
}
//删除
Array.prototype.remove = function(val) {
var index = this.indexOf(val);
if (index > -1) {
this.splice(index, 1);
}
}
//判断元素是否存在
Array.prototype.contains = function(obj) {
var i = this.length;
while (i--) {
if (this[i] === obj) {
return true;
}
}
return false;
}
//对数组或集合的深拷贝
function objDeepCopy(source) {
var sourceCopy = source instanceof Array ? [] : {};
for ( var item in source) {
sourceCopy[item] = typeof source[item] === 'object' ? objDeepCopy(source[item])
: source[item];
}
return sourceCopy;
}
//定位
Array.prototype.indexOf = function(val) {
for (var i = 0; i < this.length; i++) {
if (this[i] == val) return i;
}
return -1;
};
//判断两个对象指定属性的值是否相等(忽略类型)
function isObjectValueEqual(obj1, obj2, arr) {
for (var i = 0; i < arr.length; i++) {
var propName = arr[i];
if (obj1[propName] != obj2[propName]) {
return false;
}
}
return true;
}
$(function() {
var dg = $("#dg");
//创建一个全局数组 我们方便称号取个名字叫:更新数组 里面存储修改后(这里的修改后指的是值有变动的而不是我开启编辑过的行)的行对象
var updatedrows = [];
/*
这里我先来科普下 最近使用easyui datagrid我发现 datagrid中存在一个元数据对象(我就这么称呼它吧) 就是页面第一次加载后的所获得的信息后
我们在页面上进行的任何修改 添加 删除 都会更改这个数据对象的值 我们无论通过何种方法或事件获取的数据都是该数据对象的一个引用 并不是一个新的对象
就是说我们对所获取的对象进行修改都是对数据对象的修改 所以说元数据对象并不等于初始化数据(初始化数据指我们页面加载所请求的数据)
*/
//创建一个全局对象(olddata) 我们方便称号取个名字叫:初始化数据对象 里面存储datagrid首次加载完数据时元数据对象的一个拷贝 (不要直接使用=号赋值)
var olddata;
//全局变量当前正在编辑的行号
var editIndex = undefined;
//我个人用来偷懒的 用来代替$('#dg').datagrid(xx,xx,xx....) 建议你们好好的写$('#dg').datagrid(xx,xx,xx....)
function mydg() {
var str = '(';
for (var i = 0; i < arguments.length; i++) {
if (typeof arguments[i] == "string") {
str += "'" + arguments[i] + "',"
} else if (typeof arguments[i] == "object") {
str += JSON.stringify(arguments[i]) + ","
} else {
str += +arguments[i] + ","
}
}
str = (str.substring(str.length - 1) == ',') ? str.substring(0,
str.length - 1) : str;
str += ')'
str = "$('#dg').datagrid" + str;
var retu = eval(str);
return retu;
}
//初始化我们的datagrid
$("#dg").datagrid(
{
//从远程站点请求数据的 URL。
url : '${pageContext.request.contextPath}/emp/getEmpAll',
//设置为 true,则只允许选中一行。(并不是指每行的复选框)
singleSelect : true,
// 如果设置为 true,当用户点击某一行时,则会选中/取消选中复选框。如果设置为 false 时,只有当用户点击了复选框时,才会选中/取消选中复选框。
checkOnSelect : false,
//如果设置为 true,点击复选框将会选中该行。如果设置为 false,选中该行将不会选中复选框。
selectOnCheck : false,
//使列自动展开/折叠以适应数据网格(datagrid)的宽度。
fitColumns : true,
//指示哪个字段是标识字段。
idField : 'empno',
//设置为 true,则在数据网格(datagrid)底部显示分页工具栏。
pagination : true,
fit : true,
fitColumn : false,
// 设置为 true,则把行条纹化。(即奇偶行使用不同背景色)
striped : true,
//
nowap : true,
border : true,
//标题文本
title : '员工管理',
rownumbers : true,
//当用户双击一个单元格时触发
onDblClickCell : onDblClickCell,
//该事件在完成编辑但是编辑器尚未销毁之前触发
onEndEdit : onEndEdit,
/*
* 当数据加载成功时触发 我们会用这个事件备份数据但是要注意这个事件并不是加载完数据第一个执行的方法
* field里有很多方法都会优先与onLoadSuccess之前执行,例如styler formatter 他们都有可能改变数据格式
* 我们可以利用这个特性来改变我们想要备份的数据格式
*/
onLoadSuccess : onLoadSuccess,
//当用户完成编辑一行时触发
onAfterEdit : onAfterEdit,
//发送加载数据的请求前触发,如果返回 false 加载动作就会取消。
onBeforeLoad : onBeforeLoad,
//从远程站点请求数据的 URL
//初始化列
columns : [ [
{
field : 'ck',
checkbox : true
},
{
field : 'empno',
title : '员工编号',
width : 100,
/*
* styler:单元格的样式函数,返回样式字符串来自定义该单元格的样式
* styler 方法会优先与onLoadSuccess事件执行前执行
* 另我们每次结束编辑模式之后都会重新调用styler方法
* 我们利用这个特性来达到每次修值后更改背景颜色
*/
styler : styler
},
{
field : 'deptno',
title : '员工部门',
width : 100,
styler : styler,
// formatter 方法会优先与onLoadSuccess事件执行前执行
formatter : function(value, row) {
//我们可以将数据改成我们想要备份的格式 注意这里会更改元数据对象
if (row.dept) {
row.deptno = row.dept.deptno;
row.dname = row.dept.dname;
delete row.dept.loc;
return row.dept.dname;
}
},
//指示编辑类型 要想开启编辑模式必须为field指定编辑类型
editor : {
type : 'combobox',
options : {
valueField : 'DEPTNO',
textField : 'DNAME',
method : 'get',
url : '${pageContext.request.contextPath}/dept/getDeptAlls'
}
}
}, {
field : 'ename',
title : '员工姓名',
width : 100,
styler : styler,
editor : {
type : 'validatebox',
options : {
required : false
}
}
}, {
field : 'hiredate',
title : '员工生日',
width : 100,
styler : styler,
editor : 'datebox'
}, {
field : 'sal',
title : '员工薪水',
width : 100,
align : 'right',
styler : styler,
editor : {
type : 'numberbox',
options : {
precision : 1
}
}
} ] ],
//头部工具栏
toolbar : [ {
iconCls : 'icon-add',
handler : append,
text : '添加'
}, '-', {
iconCls : 'icon-remove',
handler : removeit,
text : '删除'
}, '-', {
iconCls : 'icon-save',
handler : accept,
text : '保存'
}, '-', {
iconCls : 'icon-undo',
handler : reject,
text : '撤销'
} ]
});
/*
* 判断值是否被修改 如果被修改更改单元格的背景颜色
* 这里的思路是这样的 首先我们要进行判断 所修改的数据是否是初始化数据
如果是将修改后的值和初始化数据对象进行比较 返回颜色
如果不是初始化数据对象 则代表是新增加的行 直接返回颜色就可以了
*/
function styler(value, row, index) {
if (olddata) {
//这里是一个简单的算法 计算当前行是初始化数据的第几行
var Index = index - (mydg('getRows').length - olddata.length);
if (Index >= 0) {
for ( var aq in row) {
if (row[aq] == value) {
if (value != olddata[Index][aq]) {
return 'background-color:#ffee00;';
}
}
}
} else {
return 'background-color:#ffee00;';
}
}
}
/*定义一个方法 判断当前有没有正在编辑的行 如果没有 则返回true
* 如果有 则判断当前编辑的行是否有效 如果有效 则结束对改行的编辑 并更新全局变量的值 返回true
* 如果没效直接返回false
* */
function endEditing() {
if (editIndex == undefined) {
return true;
} else if (dg.datagrid("validateRow", editIndex)) {
dg.datagrid("endEdit", editIndex);
editIndex = undefined;
return true;
} else {
return false;
}
}
//双击单元格事件 开启行的编辑模式
function onDblClickCell(rowIndex, field, value) {
if (editIndex != rowIndex) {
if (endEditing()) {
mydg('beginEdit', rowIndex);
var rows = mydg('getRows');
var row = rows[rowIndex];
//取消标识符单元格的可编辑属性
var tt = mydg('getColumnOption', 'empno');
tt.editor = {};
//为下拉框赋值
//使单元获取焦点
var ed = mydg('getEditor', {
index : rowIndex,
field : field
});
if (ed) {
($(ed.target).data('textbox') ? $(ed.target).textbox(
'textbox') : $(ed.target)).focus();
}
editIndex = rowIndex;
}
}
}
//添加一行
function append() {
if (endEditing()) {
mydg('insertRow', {
index : 0, // index start with 0
row : {
}
});
var tt = mydg('getColumnOption', 'empno');
tt.editor = {
type : 'numberbox'
};
mydg('beginEdit', 0);
//使单元获取焦点
var ed = mydg('getEditor', {
index : 0,
field : 'empno'
});
if (ed) {
($(ed.target).data('textbox') ? $(ed.target).textbox(
'textbox') : $(ed.target)).focus();
}
editIndex = 0;
}
}
//onEndEdit:该事件在完成编辑但是编辑器尚未销毁之前触发;
function onEndEdit(rowIndex, row) {
var ed = $(this).datagrid('getEditor', {
index : rowIndex,
field : 'deptno'
})
var dept={};
row.dept=dept;
row.dname = $(ed.target).combobox('getText');
row.dept.dname = row.dname;
row.dept.deptno = row.deptno;
}
//发送加载数据的请求前触发,如果返回 false 加载动作就会取消。
function onBeforeLoad() {
olddata = null;
updatedrows = [];
}
//当数据加载成功时触发 用来备份原始数据
function onLoadSuccess() {
olddata = objDeepCopy(mydg('getData').rows);
}
// onAfterEdit:该事件在完成编辑并且编辑器销毁之后触发 判断数据是否被修改
function onAfterEdit(rowIndex, rowData, changes) {
var bl = false;
var Index;
//updatedrows
if (olddata) {
Index = rowIndex - (mydg('getRows').length - olddata.length);
//判断元数据是否被修改
if (Index >= 0) {
bl = isObjectValueEqual(rowData, olddata[Index], [ 'ename',
'hiredate', 'sal', 'deptno', 'dname' ]);
}
}
if (Index >= 0) {
if (!bl) {
if (!updatedrows.contains(rowData)) {
updatedrows.push(rowData)
}
} else {
updatedrows.remove(rowData)
}
}
}
//保存
/*datagrid当然也有获取updated的方法 不过我个人觉得这个方法实在太简单了。他会将你点击过编辑的行的都获取到 (新增加的没有)主要还是因为我们开启过编辑模式后 如何你的字段值有非string类型的它都会将它变为string类型的 当值类型改变他也会默认这行是修改过的。所以我们就用自己写好的updatedrows就可以了 */
function accept() {
if (endEditing()) {
var inserted = mydg('getChanges', "inserted");
var deleted = mydg('getChanges', "deleted");
if (inserted.length > 0 || updatedrows.length > 0
|| deleted.length > 0) {
var effectRow = new Object();
if (inserted.length) {
effectRow["inserted"] = JSON.stringify(inserted);
}
if (deleted.length) {
effectRow["deleted"] = JSON.stringify(deleted);
}
if (updatedrows.length) {
effectRow["updated"] = JSON.stringify(updatedrows);
}
$.post("${pageContext.request.contextPath}/emp/batchData",
effectRow, function(result) {
if (result.success){
$.messager.alert("提示", "提交成功!", "info");
$('#dg').datagrid('reload'); // reload the user data
} else {
$.messager.show({ // show error message
title: 'Error',
msg: result.msg
});
}
}, "JSON").error(function() {
$.messager.alert("提示", "提交错误了!");
});
} else {
$.messager.alert("提示", "请先编辑数据!!!", "info");
}
}
}
//删除
function removeit() {
//var Index = index - (mydg('getRows').length - olddata.length);getRowIndex
if (endEditing()) {
//id数组
var ids = [];
var rows = mydg('getChecked');
if (rows.length > 0) {
for (var i = 0; i < rows.length; i++) {
ids.push(rows[i].empno);
}
}
if (ids.length > 0) {
$.messager.confirm('批量删除确认', '你确定要删除编号为' + ids.join(',')
+ '的员工吗?', function(r) {
if (r) {
var rowarr=mydg('getData').rows;
for (var i = 0; i < ids.length; i++) {
var Index = rowarr.indexOf(rows[0]) - (mydg('getRows').length - olddata.length);
//判断元数据是否有数据被删除?
if (Index>=0) {
//删除元数据中被删除的数据
olddata.splice(Index,1);
//删除更新数组中被删除的数据 因为既然数据已经被删除我们就没必要在将该数据拿到后台进行更新操作了 直接删除就可以了
updatedrows.remove(rows[0])
}
//删除指定行
mydg('deleteRow', rowarr.indexOf(rows[0]));
rowarr=mydg('getData').rows;
}
}
});
} else {
$.messager.alert("提示", "请选择要删除的行", "error");
}
}
}
//撤销
function reject() {
mydg('rejectChanges')
updatedrows = [];
editIndex = undefined;
}
});
</script>
然后我们看看后台如何接受数据。我这里用的是springmvc
// 批量操作
@RequestMapping("/batchData")
@ResponseBody
public Msg batchData(String updated, String deleted, String inserted, HttpServletRequest request) {
Msg msg = null;
List<Emp> listUpdated = null;
List<Emp> listDeleted = null;
List<Emp> listInserted = null;
if (deleted != null) {
listDeleted = JSON.parseArray(deleted, Emp.class);
}
if (updated != null) {
listUpdated = JSON.parseArray(updated, Emp.class);
}
if (inserted != null) {
listInserted = JSON.parseArray(inserted, Emp.class);
}
System.out.println("-----------listUpdated---------------");
System.out.println(listUpdated);
System.out.println("-----------listInserted--------------");
System.out.println(listInserted);
System.out.println("-----------listDeleted---------------");
System.out.println(listDeleted);
if (server.batchData(listUpdated, listDeleted, listInserted)) {
msg = new Msg(true, "提交成功");
}else{
msg = new Msg(false, "提交失败");
}
msg = new Msg(true, "提交成功");
return msg;
}