Extjs中可编辑表格,树,触发按钮和复选框的结合使用
1.数据源Store:
/*1.创建Record*/ var costRecord = new Ext.data.Record.create([ {name:"id",type:"int"}, {name:"xmid",type:"int"}, {name:"subject",type:"string"}, {name:"subjectId",type:"string"}, {name:"planCost",type:"string"}, {name:"actualCost",type:"string"} ]); /*2.创建Store*/ var costStore = new Ext.data.Store({ //注意JsonStore和Store的区别 url:"getProjectCost.eva?doType=getProjectCost", reader: new Ext.data.JsonReader({ root:"data" },costRecord) });
2.列ColumnModel:
var selectedCostRow;//定义全局变量,获取点击列表某一行时通过复选框获得的行号。 //复选框 var costSm = new Ext.grid.CheckboxSelectionModel(); /* costSm.handleMouseDown = Ext.emptyFn; */ //触发域 var costTrigger = new Ext.form.TriggerField({ emptyText:"请选择...", allowBlank:false, readOnly:true, onTriggerClick:function(e){ //单击触发按钮触发的事件 selectedCostRow = costSm.getSelected(); costTreeWin.show(); } }); var costCm = new Ext.grid.ColumnModel({ columns:[ costSm, //在列中加上定义的复选框 { header:"编号", dataIndex:"id", width:200, hidden:true },{ header:"项目编号", dataIndex:"xmid", width:200, hidden:true, editor:new Ext.form.TextField({ //此处加上可编辑文本框 id:"costXmid" }) },{ header:"支出内容(经济科目)", dataIndex:"subject", width:200, editor:costTrigger //此处加上触发域 },{ header:"经济科目ID", dataIndex:"subjectId", width:200, hidden:true },{ header:"计划支出数(单位:元)", dataIndex:"planCost", width:200, editor:new Ext.form.TextField({ allowBlank: false }) },{ header:"实际支出数(单位:元)", dataIndex:"actualCost", width:200, editor:new Ext.form.TextField({ allowBlank: false }) } ] });
注意:若加上了costSm.handleMouseDown = Ext.emptyFn; 则点击复选框才能选中,点击行不能选中。
若不加这句话,则点击行即可选中复选框!
3.可编辑表格面板:EditorGridPanel
/*4.创建表格面板*/ var costGrid = new Ext.grid.EditorGridPanel({ cm:costCm, sm:costSm, //表格中也必须加上定义的复选框才行 store:costStore, clicksToEdit:1, //设置点击几次才可以编程 stripeRows: true, //斑马线的表格 loadMask:{msg:'正在加载数据,请稍侯……'}, height:480, width:630, tbar:[ { text:"保存", iconCls:"save", handler:saveCost //点击调用保存方法 },{ text:"增加", iconCls:"add", handler:function(){ var newCost = new costRecord({ //此处创建新的Record id:-1, xmid:Ext.getCmp("costXmid").getValue(), subject:'', subjectId:'', planCost:'', actualCost:'' }); costGrid.stopEditing(); //关闭表格的编辑状态 costStore.add(newCost); //store.insert(0, newCost); //创建的Record插入store的第一行 //store.insert(store.getCount(), newCost); //创建的Record插入store的最后一行 costGrid.startEditing(0, 0); //激活第一行第一列的编辑状态 } },{ text:"删除", iconCls:"remove", handler:removeCost //点击调用删除方法 } ] }); /*创建保存收支明细方法,只保存修改过的信息*/ function saveCost(){ //只提交修改过的记录 var rcds = costStore.getModifiedRecords(); //获取修改过的记录集Records if(rcds&&rcds.length>0){ Ext.Msg.wait("正在保存..."); var rows=new Array(); for(var i=0;i<rcds.length;i++){ var rs = rcds[i]; //获取指定下标的某一行的记录Record var row=new Object(); var fields=rs.data; //获取该行记录的数据Data row = {id:fields.id,xmid:fields.xmid,subjectId:fields["subjectId"],planCost:fields["planCost"],actualCost:fields["actualCost"]}; rows.push(row); //注意此处获取属性方式:obj.id或者obj["id"] } //因为此处obj是data,这些id,name都是data的属性 //如果直接用record,则可以用它在Extjs中定义的get方法:rs.get("id") Ext.Ajax.request({ url: 'updateProjectCost.eva?doType=updateProjectCost', method:'POST', timeout:300000, success: result, failure: function(){Ext.Msg.alert('信息','未成功提交数据!'); }, params:{updateSets :Ext.encode(rows)} //将数组转化为JSON字符串!!! }); function result(response, options){ var result = Ext.util.JSON.decode(response.responseText); if(result.success){ Ext.Msg.hide(); Ext.Msg.show({ title:'成功', msg: '数据保存成功', buttons: Ext.Msg.OK, icon: Ext.MessageBox.INFO }); //保存成功后刷新修改过的脏数据。 costStore.rejectChanges(); costStore.reload(); }else{ Ext.Msg.hide(); Ext.Msg.alert('信息','保存数据未成功!'); } } } } /*创建删除收支信息的方法,删除复选框勾选的记录*/ function removeCost(){ var rcs = costGrid.getSelectionModel().getSelections(); if(!rcs||rcs.length<1){ Ext.Msg.alert("提示","请先选择要删除的行"); return; } else{ Ext.Msg.confirm("确认删除","请确认是否删除选中的项目支出条目?",function(btn){ if(btn == "yes"){//选中"是"的按钮 var ids = new Array(); for (var i = 0; i < rcs.length; i++) { ids.push(rcs[i].get("id")); } //异步发请求 Ext.Ajax.request({ url:"deleteCosts.eva?doType=deleteCosts", method:"POST", params:{costIds:ids.join(",")}, success:function(response,option){ var result = Ext.util.JSON.decode(response.responseText); if (result.success) { Ext.Msg.alert("成功","选中的项目支出条目已成功删除!"); costStore.rejectChanges(); costStore.reload(); } }, failure:function(response,option){ Ext.Msg.alert("失败","删除过程中发生错误!"); } }); } }); } }
注:
Ext中将数组转化为JSON字符串的方式:Ext.encode(array);
数组转json的数据如下:
[
{
"id": 1,
"xmid": 1,
"subject": "餐饮",
"subjectId": 1,
"planCost": 100,
"actualCost": 200
},
{
"id": 2,
"xmid": 2,
"subject": "医疗",
"subjectId": 2,
"planCost": 300,
"actualCost": 500
}
]
Servlet:处理传入的json数据,并保存:
if("updateProjectCost".equals(action)){
ProjectCostDao costDao = new ProjectCostDao();
String sData = request.getParameter("updateSets");
List<ProjectCost> projectCosts = JSON.parseArray(sData, ProjectCost.class);//将Json字符串转化为集合
boolean done = costDao.updateProjectCosts(projectCosts);
String str=null;
if(done){
str="{success:true}";
}else{
str="{success:false}";
}
response.setContentType("text/html;charset=UTF-8");
out=response.getWriter();
out.print(str);
out.close();
return;
}
注:用
FastJson的
parseArray(jsonStr, T.class)方法可直接
将符合设定的类的格式的JSON字符串转化成集合!
Dao:保存数据
/**
* 方法一:自己定义主键ID,对于新数据定义ID为-1,再根据ID判断是更新还是插入
*/
public boolean updateProjectCosts(List sets){
boolean done = false;
if(sets==null||sets.size()==0){
return true;
}
Session s = null;
try{
s = HibernateUtil.getSession();
s.beginTransaction();
for(int i=0;i<sets.size();i++){
ProjectCost cost = (ProjectCost) sets.get(i);
int id = cost.getId();
if(id<0){//新增
s.save(cost);
}else{
ProjectCost initCost = (ProjectCost) s.get(ProjectCost.class, id);
if (initCost != null) {
initCost.setXmid(cost.getXmid());
initCost.setCostSubject(cost.getCostSubject());
initCost.setPlanCost(cost.getPlanCost());
initCost.setActualCost(cost.getActualCost());
s.flush();
}
}
}
s.getTransaction().commit();
done = true;
}catch(Throwable e) {
logger.error(e.toString());
HibernateUtil.endSession(s);
}finally{
HibernateUtil.endSession(s);
}
return done;
}
/**
* 方法二:直接用saveOrUpdate,Hibernate会自动根据主键查询,有则更新,无则插入————更加方便快捷!
*/
public boolean updateProjectCosts(List<ProjectCost> projectCosts){
boolean done = false;
if(projectCosts==null||projectCosts.size()==0){
return true;
}
Session s = null;
try{
s = HibernateUtil.getSession();
s.beginTransaction();
for(int i=0;i<projectCosts.size();i++){
ProjectCost projectCost = projectCosts.get(i);
s.saveOrUpdate(projectCost);
}
s.getTransaction().commit();
done = true;
}catch(Throwable e) {
HibernateUtil.endSession(s);
}finally{
HibernateUtil.endSession(s);
}
return done;
}
直接用saveOrUpdate:
4.树面板TreePanel:注意查看下面标注的顺序
/*5.创建树形节点下拉选*/ //1.定义根节点 var costRoot = new Ext.tree.AsyncTreeNode({ id:'tree-root', text:"经济科目", expanded:true, //根节点默认展开(注意和TreePanel中的rootVisible:true的联系) draggable:false //根节点不可拖动 }); //2.定义节点数据加载器 var costLoader = new Ext.tree.TreeLoader({ dataUrl:'getTree.base?doType=getPTreeByGrade&onlyValid=1', //此处不是url,和Store的Proxy不同 baseParams :{pid:''}, baseAttrs:{uiProvider:Ext.tree.TreeCheckNodeUI} //必须有该项,不然树节点无法出现选择框 }); //3.定义数据加载前触发方法 costLoader.on("beforeload",function(treeLoader,node){ treeLoader.baseParams.pid = node.id; },this); //4.定义树形面板,用于显示数据 var costTree = new top.Ext.tree.TreePanel({ /**注意top!!!*/ root:costRoot, //根节点 loader:costLoader, //数据加载器 autoScroll:true, animate:false, enableDD:false, //不允许子节点拖动 rootVisible:true, //显示根节点 checkModel:"single", //复选框只能单选,多选为:multiple,级联:cascade onlyLeafCheckable:true, width:250, listeners:{"click":function(node){ if(!node.isLeaf()){ node.toggle(); //点击可展开可收缩 } }} }); //5.创建树节点窗口 var costTreeWin = new top.Ext.Window({ /**注意top!!!*/ title:"经济科目", layout:"fit", closeAction:"hide", modal:true, width:250, height:250, items:costTree, buttons:[ { text:"确定", handler:function(e){ var node = costTree.getChecked(); //获取被选中的树节点 if(!node || node.length < 1){ Ext.Msg.alert("系统提示","请选择经济科目!"); return; } var name = new Array(); var value = new Array(); for (var i = 0; i < node.length; i++) { name.push(node[i].text); value.push(node[i].id); } var data = selectedCostRow.data; //获取选择的行的Record的Data数据对象 data["subjectId"] = value.join(); /*data["subject"]= name.join(); —— —— 此处用这种方式给触发域赋值出错!!!必须用下面的方式!!! */ costTrigger.setValue(name.join()); costTreeWin.hide(); } },{ text:"取消", handler:function(){ costTreeWin.hide(); } } ] });
注意事项:
1.在可编辑表格面板中加触发域TriggerField,则关联的树面板TreePanel和树窗口Window在定义时必须用top置顶!!!否则无法在单元格中显示被选中的数据。
2.通过selectedRow.data获取选中的行所对应的Record的Data对象(注意该Data是Object对象!)
3.通过data["subjectId"] = value.join()方式,给该data对象中的各种属性赋值。(注意改变的实际是Store的值!!)
4.给TriggerField触发域占用的单元格赋值,必须用:costTrigger.setValue(name.join())
此处若还是用data["subject"] = name.join(),则会报错,原因尚未深究。
查询树节点的SQL:
SELECT *
FROM (SELECT BM, MC, PID, ISLEAF, LEVEL
FROM (SELECT BM, MC, PID, 0 AS ISLEAF
FROM BM_CONT
WHERE TABLE_BM = 'BM_GRADE'
AND YEAR = 2014
UNION
SELECT TO_CHAR(ID) AS BM,
FILENO AS MC,
GID AS PID,
1 AS ISLEAF
FROM POLICY)
CONNECT BY PRIOR BM = PID
START WITH PID IS NULL)
查到的树节点的数据:
(具体数据请查看附件!)
5.窗口Window:
/*6.创建收支明细窗口*/ var costWin = new Ext.Window({ title:"收支明细", frame:"fit", closeAction:"hide", modal:true, items:costGrid });
6.点击显示窗口时传递需要的参数:
function showCostWin(xmid){ Ext.getCmp("costXmid").setValue(xmid); costWin.show(); costStore.baseParams.xmid = xmid; costStore.load(); }
图示: