您的位置:首页 > 编程语言 > Java开发

EXTJS实现的TREEGRID(后台java,框架SpringMVC)

2013-07-22 14:07 369 查看


EXTJS实现的TREEGRID(后台java,框架SpringMVC)

一。一些废话

近日来,有几个项目用到了EXTJS作为Web前端。也看到了一些童鞋苦苦在网上寻觅树形列表控件,碰巧项目中用到了一种,现在给大家分享一下。

二。前端

前端代码,用的是JS。主要就是指定数据读取地址,然后定义列表表头。这里实例化TreeGrid对象

Js代码



// function loadGrid(json) 方法中的内容,由第三个代码片段,取得表头方法调用

// 等号左边,定义了一个对象,相当于var a=,这里用的是命名空间statisticBaseSummary,定义一个属于这个命名空间的对象

// 等号右边实例一个TreeGrid,在ExtJs4以前的版本都是在Ext.ux.tree命名空间下,ExtJs4以及日后版本全部整理到了Ext.Forml的空间下

// 并且名称发生了变化

statisticBaseSummary.gridPanel = new Ext.ux.tree.TreeGrid({

autoScroll : 'auto',// 滚动条

baseParams: statisticBaseSummary.parameter,// 请求树形数据参数

columnLines: true,// 斑马线

columns: Ext.util.JSON.decode(createDynamicGrid(fieldsArr, headerArr, headerHiddenArr, fieldsWidthArr, fieldsAlingArr)),// 动态列,如果不用动态列可以直接定义,关于动态列的做法另开博文介绍

tbar : statisticBaseSummary_tbar,

region : 'center',

loadMask : true,// 如何加载

useArrows: true,

enableSort : false,

animate: true,

enableDD: true,

containerScroll: true,

requestMethod : 'POST',// 请求方式

// 这里发现没有用到store对象,直接定义了一个服务器请求地址(statisticBaseSummary.reportUrl,这个是在文件头部定义的静态常量,value是url地址,比如:path + '/getTreeGridInfo'等等)

// 我们可以看看TreeGrid的源码发现,虽然没有定义store对象但是在源码中确实实例了一个store对象,可以想象,我们应该可以用store来处理数据请求

// 不过设立了dataUrl这种方式虽然有数据处理局限性,但是目前我还是建议用3.x版本的童鞋都采用这种方式

dataUrl: statisticBaseSummary.reportUrl,

listeners: {

// 数据返回到页面之后触发这个事件

load : function(node) {

// 监听你想的处理,比如load的等待画面隐藏之类的

}

}

});

下面这个方法,是做一个列头出来。

Js代码



/** 基本信息-数据列 */

function createDynamicGrid(fieldsArr, headerArr, headerHiddenArr, fieldsWidthArr, fieldsAlingArr) {

var statisticColumns = "[";

for (var i = 0; i < fieldsArr.length; i++) {

statisticColumns += "{header:\"<div style='text-align:center'>" + headerArr[i] + "</div>\", width:" + fieldsWidthArr[i] + ", dataIndex:'" + fieldsArr[i] + "', hidden:" + headerHiddenArr[i] + ", align:'" + fieldsAlingArr[i] + "'}";

if (i < fieldsArr.length - 1) {

statisticColumns += ",";

}

}

statisticColumns += "]";

return statisticColumns;

}

下面这个方法是上面的方法所需的参数,是异步取得,但是一定要在new gridtree之前取得。

Js代码



/** 取得表头 */

function getData() {

Share.AjaxRequest({

method: 'POST',

url: homePage.homePageHeaderUrl, // 设定Ajax请求数据源路径

showMsg : false,

params : homePage.parameter,

callback : function(json) {

//var obj = Ext.util.JSON.decode(request.responseText);

var obj = json.o;

homePage.headerMsg = obj;

loadGrid(json); // 这个loadGrid方法就是上面的构造树形列表的方法

}

});

}

三。后端

相对前端来说,后端动态取得一个可以让TreeGrid能够识别的数据则是关键的地方。后端我用的是JAVA,框架是SpringMVC。数据总体来说是一个List(里面放着Map)。

(一) 我们首先需要分析,官方给的例子的JSON数据是什么样子的。

Js代码



[{

// 表头第一列,该列也是树形列(aList:把这一列的数据作为aList),可以看图一

task:'Project: Shopping',

// 表头第二列,从这列开始就是与第一列的每一个节点所对应的数据字段了(把从第二以及以后列的数据作为bList)

duration:13.25,

user:'Tommy Maintz',// 表头第三列

iconCls:'task-folder',// 节点图标样式,与数据无关

expanded: true,//是否可以展开,与数据无关

// 接下来就是上面那个节点task:'Project: Shopping'的子节点以及子节点对应的数据

children:[{

// 表头第一列,该列也是树形列(aList:把这一列的数据作为aList),可以看图一

task:'Housewares',

// 表头第二列,从这列开始就是与第一列的每一个节点所对应的数据字段了(把从第二以及以后列的数据作为bList)

duration:1.25,

user:'Tommy Maintz',// 表头第三列

iconCls:'task-folder',// 节点图标样式,与数据无关

// 接下来就是上面那个节点task:'Project: Shopping'的子节点以及子节点对应的数据

children:[{

task:'Kitchen supplies',

duration:0.25,

user:'Tommy Maintz',

leaf:true,

iconCls:'task'

}

// 下面的代码我就略去了,上面的编码已经说明了Treegrid的数据格式

// 首先task这个属性是固定的

.......

]}

}]

(二)看过数据格式了,那么我们分析一下。

从这段代码可以看出,数据属性格式,一共分为三部分,一个是节点,一个是节点对应的数据,一个是节点对应的子节点以及子节点的数据。请往下看

第一部分(节点):

task:树形的节点,这个请固定不要更改

iconCls:树形节点的icon

expanded:树形节点是否可以展开

第二部分(节点对应的数据):

duration、user:这是数据表示列,样例中是有两个,但是我们可以定义很多列,当然你需要表示的列为准

第三部分(子节点以及节点对应数据)

children:隶属于该节点的下属节点以及节点数据。

针对于第三部分来说,我们可以发现,children中的结构实际上就是包含了上述这三部分属性内容。如果这块比较难以理解的话,我们可以倒过来想。有一个这样的叶子节点A,这种节点是一个没有子节点的节点,那么A的children属性是没有内容的是null,B节点是A节点的父节点,那么B节点的children属性内容就是A。这样大家明了了吧,如果再进一步,假设C节点是B节点的父呢?C节点的children属性内容就是B,那么C-B-A就构成了三级树形结构,其中这三个节点的属性字段都是一致的只是内容不同。这里大家会问,如果B节点的子节点是A和D呢,那么B的children属性值就是A+D,储存形式是:chidren.add(A),chidren.add(D)。这样一来B的子节点就是A和D了。

(三)如何实现。

首先,我们要定义一个List allList;

其次,我们要定义一个Model,这里我们定义为HashMap rootMap;

最后,我们要有两个中间交换变量,一个是childrenList,childrenMap;

那么我们现在从数据库检索出来两个List:

1.aList:代表树形节点数据,里面的Model一定会有这样几种字段:

parentId(该节点的父节点id)

id(该节点id,一般为主键)

name(该节点名称)

type(是否为叶子节点)

level(节点等级)

weight(权重,一般用于排序来用)

这些字段,我们只用前4个即可。

2.bList:代表对应树形节点的数据,这里面的Model一般是我们自定义的,且一定有一个或者多个字段与树形节点对应。

接下来我们谈谈如何打造出样例中的数据。

其实就是将aList与bList结构逻辑化为样例中的数据格式,就是讲这两个List置换成rootMap放到allList中。

Java代码



// 我们需要两个方法,一个是调用,一个是执行转换

public ArrayList result() {

// 。。。。。前面的代码省略

// 先定义一个返回List,就是最终我们需要的Treegrid默认的数据格式

ArrayList allList = new ArrayList<HashMap<String, Object>>();

// 定义一个根Map,这个Map直接被allList.add(rootMap)这个List中

HashMap rootMap = new HashMap();

// 定义子List

ArrayList childrenList= new ArrayList<HashMap<String, Object>>();

// 定义子Map,这个Map直接被allList.add(rootMap)这个List中

HashMap childrenMap = new HashMap();

// 下面是aList, bList的定义,大家可以更改

List<HashMap> bList = dao.getBList();

HashMap<String, Object> bResult = new HashMap<String, Object>();

String temp = "";

for (Iterator iterator = bList.iterator(); iterator.hasNext();) {

HashMap rs = (HashMap) iterator.next();

ModelB modelB= new ModelB();

temp = rs.get("ID") == null ? "" : rs.get("ID").toString();// 对应的树形节点ID

modelB.setFiled1(Integer.parseInt(rs.get("filed1") == null ? "0" : rs.get("filed1").toString()));

modelB.setFiled2(Integer.parseInt(rs.get("filed2") == null ? "0" : rs.get("filed2").toString()));

modelB.setFiled3(Integer.parseInt(rs.get("filed3") == null ? "0" : rs.get("filed3").toString()));

bResult.put(temp, modelB);

}

ArrayList aList = dao.getAList();

Iterator iterator = aList.iterator();

if (iterator.hasNext()) {

HashMap tempMap = (tempMap)iterator.next();

String id = <span style="font-size: 1em; line-height: 1.5;">tempMap</span><span style="font-size: 1em; line-height: 1.5;">.get("ID");</span>

String parentId = <span style="font-size: 1em; line-height: 1.5;">tempMap</span><span style="font-size: 1em; line-height: 1.5;">.get("parentId");</span>

String name = <span style="font-size: 1em; line-height: 1.5;">tempMap</span><span style="font-size: 1em; line-height: 1.5;">.get("name");//第一条树形结构数据,一定要是根节点。比如总计、合计等。也就是说在aList取得的语句中要排序</span>

rootMap.put("task", name);

rootMap.put("expanded", "true");

run(iterator, rootMap, null, bResult, childrenList)

}

// 调用执行转换方法返回追最终的List

allList.add(rootMap);

// 返回查询的List

return allList;

}

/**

* Treegrid数据转换

*

* @param iterator:节点记录

* @param parent:父节点对象

* @param last:子节点对象

* @param bResult:节点对应的数据

* @param childrenList:子节点集合

* @return

*/

public void run(Iterator iterator, HashMap parent, HashMap last, HashMap bResult, ArrayList childrenList) {

// 如果last(这个对象是子节点对象,包括子节点和对应的数据,第一次进到这个方法的时候为null)非空,则将last放到parent中

if (last!= null) parent.put("children", last);

if (childrenList== null) childrenList= new ArrayList<HashMap<String, Object>>();

while (iterator.hasNext()) {

// 得到节点

HashMap rs = (HashMap) iterator.next();

// 声明一个ic,这个对象就是子节点对象,包括节点和节点对应的数据

HashMap ic = new HashMap();

String id = <span style="font-size: 1em; line-height: 1.5;">rs</span><span style="font-size: 1em; line-height: 1.5;">.get("ID");</span>

String parentId = <span style="font-size: 1em; line-height: 1.5;">rs</span><span style="font-size: 1em; line-height: 1.5;">.get("parentId");</span>

String name = <span style="font-size: 1em; line-height: 1.5;">rs</span><span style="font-size: 1em; line-height: 1.5;">.get("name");</span>

ic.put("id", id);

ic.put("parentId", parentId);

ic.put("task", name);

ic.put("expanded", "true");

// 利用节点ID从bResult中寻找是否有对应该节点的数据,如果有的话则将节点对应的数据put到ic中

if (bResult.containsKey(ic.get("id")))

ic.put("children", bResult.get(ic.get("id")));

else

ic.put("children", "");

// 下面的代码就很重要了,是一个递归调用自身的逻辑

// 首先判断节点对应的数据是否存在,如果为null则代表该节点为根节点

if (last == null) {

// 将根节点对应的数据put到父节点对象中

childrenList.add(ic);

parent.put("children", childrenList);

// 我们得到的ic子节点对象就变为下一次循环的父节点对象了

last = ic;

// 根节点记录处理结束,进行下一节点处理,就是走上面的那个while循环的处理。

} else {

// 如果当前子节点对象ic所对应的父节点与前一个子节点对象last所对应的节点记录一致

// 代表当前子节点为前一节点子节点

while (ic != null && ic.get("parentId").equals(last.get("id")))

ic = run(iterator, last, ic, bResult, null);

// 如果当前子节点对象ic所对应的父节点与父节点对象parent所对应的节点记录一致

// 代表当前子节点与前一节点同为parent的子节点

if (ic != null && ic.get("parentId").equals(parent.get("id"))) {

childrenList.add(ic);

parent.put("children", childrenList);

last = ic;

} else

return ic;

}

}

以上代码则完成了aList,bList的转换,返回结果为Treegrid能够解析的数据,接下来就是返回给页面端的Controller.java

介绍这个文件主要就是告诉大家,利用SpringMVC框架直接返回List对象给TreeGrid控件。

Java代码



/** Report列表数据 */

@RequestMapping(value = "statisticSummarizing", method = RequestMethod.POST)

@ResponseBody

public ListstatisticSummarizing(ExtPager pager, HttpServletRequest request,

@RequestParam(required = false, defaultValue = "") HashMap<String, String> queryParam) {

//清除空字符串

Criteria criteria = new Criteria(request, queryParam, dataDialect);

if (pager.getLimit() != null && pager.getStart() != null) {

criteria.setSplit(pager);

}

//这里返回了List容器对象,这个对象直接会被TreeGrid控件解析

return statisticManager.statisticSummarizing(criteria);

}

四。总结

以上就是EXTJS实现的TREEGRID的具体步骤,如有不明请留言。

五。整个代码

Js代码



<%@ page language="java" contentType="text/html; charset=UTF-8"%>

<%@ page import="java.util.*;"%>

<%@ include file="/WEB-INF/views/commons/taglibs.jsp"%>

<div id="${param.id}_div"></div>

<script type="text/javascript">

Ext.ns("Ext.Conac.homePage");

homePage = Ext.Conac.homePage;

homePage = {

homePageHeaderUrl : ctx + "/homepage/homePageHeader",

homePageDataUrl : ctx + "/homepage/homePageData",

zhuanSongPage : ctx + "/instauration/zhuanSongPage",

parameter : new Object(),

headerMsg : '',

reportType : 'summary', // 统计报告类型:简表:simple,汇总:summary

reportFlag : '2',

searchFlag : '0',

textFlag : '0',

treeParentId : '',

treeGroupId : '1',

treeGroupName : 'XXXXXX',

headerMsg : '',

type : '1' // 统计表查询功能

};

/** Search页面 */

homePage.searchPanel = new Ext.form.FormPanel({

region : 'north',

margins:'1 1 1 1',

border:false,

height: 60,

layout : 'absolute',

defaults: {

style: {

margin: '0 0 0 10px'

}

},

items: [{

xtype: 'checkbox',

name :'searchFlag',

boxLabel: '<b>点击这里搜索</b>',

listeners :{

check : function(chkBox,checked){

var f = homePage.searchPanel.findById('homePage-fieldset1');

var c = homePage.searchPanel.findById('homePage-container1');

if(checked){

f.setVisible(true);

homePage.searchPanel.setHeight(170);

c.setPosition(0,145);

}else{

homePage.searchPanel.getForm().reset();

f.setVisible(false);

homePage.searchPanel.setHeight(60);

c.setPosition(0, 35);

}

if(homePage.myPanel){

homePage.myPanel.doLayout(false);

}

}

}

},{

id:'homePage-fieldset1',

xtype: 'fieldset',

height: 130,

layout: 'absolute',

hidden: true,

border: false,

x: 30,

y: 25,

items: [{

xtype: 'label',

text: 't1:',

x: 5,

y: 5

},{

xtype: 'label',

text: 't2:',

x: 5,

y: 30

},{

xtype: 'label',

text: 't3:',

x: 5,

y: 50

},{

xtype: 'textfield',

name:'certId',

maxLength: 12,

maxLength: 12,

width: 225,

x: 75,

y: 0

},{

xtype: 'textfield',

name:'unitName',

maxLength: 50,

width: 225,

x: 75,

y: 25

},{

id:'holdUnit',

xtype: 'textfield',

name:'holdUnit',

editable: false,

width: 225,

x: 75,

y: 50

},{

xtype: 'button',

width: 50,

iconCls:'tick',

text: '选择',

x: 300,

y: 50,

handler :function(){

Share.SelectJbdwCategory.show('holdUnit', 'jbdwId');

}

},{

xtype: 'button',

width: 50,

iconCls:'query',

text: '搜索',

x: 160,

y: 77,

handler :function(){

var params = homePage.searchPanel.getForm().getValues();

homePage.parameter["flag"] = params.flag; // 0: ;1:

homePage.parameter["certId"] = params.certId;

homePage.parameter["unitName"] = params.unitName;

homePage.parameter["holdUnit"] = params.holdUnit;

callReport();

}

},{

xtype: 'radio',

name : 'flag',

checked :true,

inputValue: '0',

boxLabel: '不搜索子节点',

x: 370,

y: 50

},{

xtype: 'radio',

name : 'flag',

inputValue: '1',

boxLabel: '搜索子节点',

x: 470,

y: 50

},{

id : 'jbdwId',

xtype: 'hidden',

name:'jbdwId'

}

]

},{

xtype: 'container',

id : 'homePage-container1',

html: "<div style=\"color:blue;\"><a href=\"#\" onclick=\"Share.openMorePage('/homepage/quickAnnualCheck','快速年检')\"><b>${annualCheckYear}t4</b></a></div>",

x: 0,

y: 35

}

]

});

/** Report报表 */

homePage.reportGrid = new Ext.FormPanel({

layout: 'fit',

border:false,

region : 'center'

});

/** 最外层布局 */

homePage.myPanel = new Ext.Panel({

renderTo : '${param.id}' + '_div',

layout: 'border',

border:false,

items: [homePage.searchPanel, homePage.reportGrid],

height : index.tabPanel.getInnerHeight() - 1

});

/** 基本信息-数据列 */

function createDynamicGrid(fieldsArr, headerArr, headerHiddenArr, fieldsWidthArr, fieldsAlingArr) {

var statisticColumns = "[";

for (var i = 0; i < fieldsArr.length; i++) {

statisticColumns += "{header:\"<div style='text-align:center'>" + headerArr[i] + "</div>\", width:" + fieldsWidthArr[i] + ", dataIndex:'" + fieldsArr[i] + "', hidden:" + headerHiddenArr[i] + ", align:'" + fieldsAlingArr[i] + "'}";

if (i < fieldsArr.length - 1) {

statisticColumns += ",";

}

}

statisticColumns += "]";

return statisticColumns;

}

/** report表单展示 */

function loadGrid(json) {

waiting = Ext.Msg.wait('正在处理,请稍等...', '', '');

var fieldsArr = new Array();

var headerArr = new Array();

var headerHiddenArr = new Array();

var fieldsWidthArr = new Array();

var fieldsAlingArr = new Array();

var records = json.rows; // 将Json数据过滤得到每一行数据

// 将过滤过的Json的行数分进行如下操作

for(var i=0;i<records.length;i++){

// 取得每一个域名种类

var record = records[i];

fieldsArr[i] = record.name;

headerArr[i] = record.text;

headerHiddenArr[i] = record.hidden;

fieldsWidthArr[i] = record.width;

fieldsAlingArr[i] = record.alignment;

}

/** Report顶部工具条 */

homePage.tbar = new Ext.Toolbar({

height: 26,

items: ["<div id='_tbar_item'>t5</div>"]

});

/** Report中央布局如果已经渲染关闭数据加载窗口 */

if (homePage.reportGrid) {

/** Report顶部工具条-信息 */

if (waiting != null) {

waiting.hide();

}

}

/** Report中央布局 */

homePage.gridPanel = new Ext.ux.tree.TreeGrid({

enableSort : false,

autoScroll : 'auto',

baseParams: homePage.parameter,

columns: Ext.util.JSON.decode(createDynamicGrid(fieldsArr, headerArr, headerHiddenArr, fieldsWidthArr, fieldsAlingArr)),

tbar : homePage.tbar,

region : 'center',

loadMask : true,

useArrows: true,

autoScroll: true,

animate: true,

enableDD: true,

containerScroll: true,

border: false,

requestMethod : 'POST',

dataUrl: homePage.homePageDataUrl,

listeners: {

load : function(node) {

if (node.isFirst) {

if (waiting != null) {

waiting.hide();

}

}

}

}

});

homePage.reportGrid.add(homePage.gridPanel);

homePage.reportGrid.doLayout(false);

}

/** 取得表头 */

function getData() {

Share.AjaxRequest({

method: 'POST',

url: homePage.homePageHeaderUrl, // 设定Ajax请求数据源路径

showMsg : false,

params : homePage.parameter,

callback : function(json) {

//var obj = Ext.util.JSON.decode(request.responseText);

var obj = json.o;

homePage.headerMsg = obj;

loadGrid(json);

}

});

}

/** 确定按钮操作 */

function callReport() {

homePage.reportGrid.remove(homePage.gridPanel);

homePage.parameter["start"] = '0'; // 初期表示数据

homePage.parameter["limit"] = homePage.pageSize; // 统计表查询功能

// 取得表头

getData();

}

callReport();

//转送

function openZhuanSong(sydwName, applyId) {

homePage.zhuansongWindow = new Ext.Window({

title : '转送',

pageY : 100,

width :370,

height : 180,

plain : true,

border : false,

modal : true,

resizable : false,

autoScroll : true,

autoLoad : {

url : homePage.zhuanSongPage,

params : {

sydwName : sydwName,

applyId : applyId

},

method: 'GET',

scripts : true,

nocache : true

}

});

homePage.zhuansongWindow.show();

}

</script>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐