最近做一个项目,项目中开始使用的TreeTable的一个纯js插件。也许是对这个封装的js不熟悉,不管怎么调试,出来的效果总是不太理想。没得办法,最后想起来easyUI对树形表格的展示效果还不错。
于是就根据easyUI官方最新的Demo做了下面的案例:
上面依次是TreeGrid同步加异步请求接口且数据未分页、同步加异步请求接口数据分页、同步请求接口未分页。
为啥要分上面三种呢?因为上面三种方式,请求接口、返回数据,以及携带的参数都不一样。
一、
同步请求接口未分页
后台接口对应的Model:
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* *生成Easyui treeGrid的方法
*/
public class BimPlanComponentTreeGrid implements Serializable{
private static final long serialVersionUID = 1L;
private String id;
private String planName;
private Date planStartTime;
private Date planEndTime;
private Integer planTimeLimit;
private String planTimeLimitUnit;
private Double planPercentDone;
private Integer planPriority;
private String planParentId;
private Integer planIndex;
private String planResource;
private String planRemark;
private String planFileId;
private String planState;
private String state="open";
private List<BimPlanComponentTreeGrid> children=new ArrayList<BimPlanComponentTreeGrid>();
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPlanName() {
return planName;
}
public void setPlanName(String planName) {
this.planName = planName;
}
public Date getPlanStartTime() {
return planStartTime;
}
public void setPlanStartTime(Date planStartTime) {
this.planStartTime = planStartTime;
}
public Date getPlanEndTime() {
return planEndTime;
}
public void setPlanEndTime(Date planEndTime) {
this.planEndTime = planEndTime;
}
public Integer getPlanTimeLimit() {
return planTimeLimit;
}
public void setPlanTimeLimit(Integer planTimeLimit) {
this.planTimeLimit = planTimeLimit;
}
public String getPlanTimeLimitUnit() {
return planTimeLimitUnit;
}
public void setPlanTimeLimitUnit(String planTimeLimitUnit) {
this.planTimeLimitUnit = planTimeLimitUnit;
}
public Double getPlanPercentDone() {
return planPercentDone;
}
public void setPlanPercentDone(Double planPercentDone) {
this.planPercentDone = planPercentDone;
}
public Integer getPlanPriority() {
return planPriority;
}
public void setPlanPriority(Integer planPriority) {
this.planPriority = planPriority;
}
public String getPlanParentId() {
return planParentId;
}
public void setPlanParentId(String planParentId) {
this.planParentId = planParentId;
}
public Integer getPlanIndex() {
return planIndex;
}
public void setPlanIndex(Integer planIndex) {
this.planIndex = planIndex;
}
public String getPlanResource() {
return planResource;
}
public void setPlanResource(String planResource) {
this.planResource = planResource;
}
public String getPlanRemark() {
return planRemark;
}
public void setPlanRemark(String planRemark) {
this.planRemark = planRemark;
}
public String getPlanFileId() {
return planFileId;
}
public void setPlanFileId(String planFileId) {
this.planFileId = planFileId;
}
public String getPlanState() {
return planState;
}
public void setPlanState(String planState) {
this.planState = planState;
}
public List<BimPlanComponentTreeGrid> getChildren() {
return children;
}
public void setChildren(List<BimPlanComponentTreeGrid> children) {
this.children = children;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
特别注意的是:实体类中必须包含state、children这两个属性。其中state=”closed”时表示此时的节点是叶级节点,即它是有子级节点;当state=”open”时表示此时的节点是子级节点,他没有子级节点。children这个属性是当前实体类集合。所以这里首先就想到需要使用到递归。
递归数组:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.mss.shtoone.bim.model.BimPlanComponentTreeGrid;
public class BimPlanComponentTreeGridList{
List<BimPlanComponentTreeGrid> returnList = new ArrayList<BimPlanComponentTreeGrid>();
/**
* 根据父节点的ID获取所有子节点
* @param list 分类表
* @param typeId 传入的父节点ID
* @return String
*/
public List<BimPlanComponentTreeGrid> getChildTreeObjects(List<BimPlanComponentTreeGrid> list,String praentId) {
List<BimPlanComponentTreeGrid> returnList = new ArrayList<BimPlanComponentTreeGrid>();
for (Iterator<BimPlanComponentTreeGrid> iterator = list.iterator(); iterator.hasNext();) {
BimPlanComponentTreeGrid t = (BimPlanComponentTreeGrid) iterator.next();
// 一、根据传入的某个父节点ID,遍历该父节点的所有子节点
if (t.getPlanParentId()==praentId) {
recursionFn(list, t);
returnList.add(t);
}
}
return returnList;
}
/**
* 递归列表
* @param list
* @param TreeObject
*/
private void recursionFn(List<BimPlanComponentTreeGrid> list, BimPlanComponentTreeGrid t) {
List<BimPlanComponentTreeGrid> childList = getChildList(list, t);// 得到子节点列表
t.setChildren(childList);
if(childList.size()>0){
t.setState("closed");
}else{
t.setState("open");
}
for (BimPlanComponentTreeGrid tChild : childList) {
if (hasChild(list, tChild)) {// 判断是否有子节点
//returnList.add(TreeObject);
Iterator<BimPlanComponentTreeGrid> it = childList.iterator();
while (it.hasNext()) {
BimPlanComponentTreeGrid n = (BimPlanComponentTreeGrid) it.next();
recursionFn(list, n);
}
}
}
}
// 得到子节点列表
private List<BimPlanComponentTreeGrid> getChildList(List<BimPlanComponentTreeGrid> list, BimPlanComponentTreeGrid t) {
List<BimPlanComponentTreeGrid> tlist = new ArrayList<BimPlanComponentTreeGrid>();
Iterator<BimPlanComponentTreeGrid> it = list.iterator();
while (it.hasNext()) {
BimPlanComponentTreeGrid n = (BimPlanComponentTreeGrid) it.next();
if (n.getPlanParentId()!=null ) {
if(n.getPlanParentId().equals(t.getId())){
tlist.add(n);
}
}
}
return tlist;
}
// 判断是否有子节点
private boolean hasChild(List<BimPlanComponentTreeGrid> list, BimPlanComponentTreeGrid t) {
return getChildList(list, t).size() > 0 ? true : false;
}
// 本地模拟数据测试
public static void main(String[] args) {
long start = System.currentTimeMillis();
List<BimPlanComponentTreeGrid> TreeObjectList = new ArrayList<BimPlanComponentTreeGrid>();
BimPlanComponentTreeGridList mt = new BimPlanComponentTreeGridList();
List<BimPlanComponentTreeGrid> ns=mt.getChildTreeObjects(TreeObjectList,null);
for (BimPlanComponentTreeGrid m : ns) {
System.out.println(m.getPlanName());
System.out.println(m.getChildren());
}
long end = System.currentTimeMillis();
System.out.println("用时:" + (end - start) + "ms");
}
}
后台通过ssh取出来的集合,通过
new BimPlanComponentTreeGridList().getChildTreeObjects(list,null); //list为从后台取出来的数据集。(这里是取出所有的数据,无论子父关系,全都交给treeGrid)
前段js展示:
<div class="easyui-layout" fit="true" id="treeGrid" style="height:500px;"> <table id="treeGridTableDynamic" fit="true" class="easyui-treegrid" ></table> </div>
---------------------------
$("#treeGridTable").treegrid({ animate:true, idField: 'id', treeField:"planName", title:"同步[未分页]", nowrap: false, animate: true, method: "GET", iconCls: 'icon-save', rownumbers: true, collapsible: true, loadMsg: "数据加载中,请稍后...", fitColumns: true, fit:true, url: url, lines: true, //加树形条 collapsible:true,//是否可折叠 showFooter:false,//是否使用页脚 columns: [[ { field:"id", title: "id", hidden: true }, { field:"xx", title: "项目名称", width: 200 }, { field:"xx", title: "开始时间", align: "center",formatter:dataFormat.formatShort }, { field:"xx", title: "终止时间", align: "center",formatter:dataFormat.formatShort } ]], onBeforeLoad: function(row,param){ }, onAfterEdit:function(row,changes){ //alert(row.xx); }, onBeforeExpand: function(row){ //alert(row.xx); } });
以上便完成了treeGrid的同步表格的数据展示,结果发现点击第一级请求第二节点时,浏览器崩了,原因是数据量比较多,所以就有了以下的考虑。
同步加载树形结构的第一级,当点击树形表格数据的文件图标时,异步获取他的子级数据。
二、同步加异步请求接口未分页
$("#treeGridTableDynamic").treegrid({ animate:true, idField: 'id', treeField:"planName", title:"同步加异步[未分页]", nowrap: false, animate: true, method: "GET", iconCls: 'icon-save', rownumbers: true, collapsible: true, loadMsg: "数据加载中,请稍后...", fitColumns: true, fit:true, url: url, lines: true, //加树形条 collapsible:true,//是否可折叠 showFooter:false,//是否使用页脚 checkbox: true, columns: [[ { field:"id", title: "id", hidden: true }, { field:"planName", title: "项目名称", width: 200 }, { field:"planStartTime", title: "开始时间", align: "center",formatter:dataFormat.formatShort }, { field:"planEndTime", title: "终止时间", align: "center",formatter:dataFormat.formatShort } ]], onBeforeLoad: function(row,param){ if (!row) { // load top level rows param.id = 0; // set id=0, indicate to load new page rows }else{ //这里进行异步请求 paramsModel.param1=""; paramsModel.param2=""; paramsModel.param3=""; paramsModel.param4=""; paramsModel.param5=""; paramsModel.param6=""; paramsModel.param7=""; paramsModel.param8=""; paramsModel.param9=row.id; paramsModel.param10=""; paramsModel.page=1; paramsModel.rows=10; var requestNew = JSON.stringify(paramsModel); requestNew = escape(encodeURIComponent(requestNew)); //处理中文乱码之类 $(this).treegrid("options").url =APIURL+"rest/getConstructionPlanTreeGridDynamic/"+requestNew+"/"+Key+".json"; } }, onAfterEdit:function(row,changes){ //alert(row.planName); }, onBeforeExpand: function(row){ //alert(row.planName); } })
此时 onBeforeLoad方法中row,在首次加载树形表格时,我们给他的一个参数=0,所以后台必然有一个当参数=0的时候请求他最高一级节点,即父节点,我们
把父节点的parentid=null。而第二次当我们点击树形表格数据的文件图标时,我们后台把父节点的id==parentid作为查询条件返回,即可。StringBuffer sql=new StringBuffer(); List<BimPlanComponent> planFileList=new ArrayList<BimPlanComponent>(); if(StringUtil.isNotEmpty(paramsModel.getParam9())){ if(paramsModel.getParam9().equalsIgnoreCase("0")){ sql.append("select a.id,planName,planStartTime,planEndTime,planTimeLimit,planTimeLimitUnit,planPercentDone,planPriority,planParentId,planIndex,planResource,planRemark,planFileId,planState "); sql.append("from BIM_ConstructionPlan as a inner join BIM_ConstructionPlanFile as b on a.planFileId=b.id "); sql.append(" inner join t_s_depart as c on b.departId=c.id where 1=1 and a.planParentId is NULL "); sql.append(sqlWhere.toString()); List<Object[]> list=findListbySql(sql.toString()); if(list.size()>0){ for(int i=0;i<list.size();i++){ Object[] obj=list.get(i); BimPlanComponent plan=new BimPlanComponent(); plan.setId(String.valueOf(obj[0])); plan.setPlanName(StringUtil.isEmptyObject(obj[1], "")); plan.setPlanStartTime(GetDate.StrConvertDateShort(StringUtil.isEmptyObject(obj[2],""))); plan.setPlanEndTime(GetDate.StrConvertDateShort(StringUtil.isEmptyObject(obj[3],""))); plan.setPlanTimeLimit(Integer.parseInt(StringUtil.isEmptyObject(obj[4],null))); plan.setPlanTimeLimitUnit(StringUtil.isEmptyObject(obj[5],null)); plan.setPlanPercentDone(Double.parseDouble(StringUtil.isEmptyObject(obj[6], null))); plan.setPlanPriority(Integer.parseInt(StringUtil.isEmptyObject(obj[7],null))); plan.setPlanParentId(StringUtil.isEmptyObject(obj[8],null)); plan.setPlanIndex(Integer.parseInt(StringUtil.isEmptyObject(obj[9],null))); plan.setPlanResource(StringUtil.isEmptyObject(obj[10],null)); plan.setPlanRemark(StringUtil.isEmptyObject(obj[11],null)); plan.setPlanFileId(StringUtil.isEmptyObject(obj[12],null)); plan.setPlanState(StringUtil.isEmptyObject(obj[13],null)); Long longCount=getCountForJdbc("select count(*) from BIM_ConstructionPlan where planParentId='"+plan.getId()+"'"); if(longCount>0){ plan.setState("closed"); }else{ plan.setState("open"); } planFileList.add(plan); } } }else{ //清空sql sql.setLength(0); sql.append("select a.id,planName,planStartTime,planEndTime,planTimeLimit,planTimeLimitUnit,planPercentDone,planPriority,planParentId,planIndex,planResource,planRemark,planFileId,planState "); sql.append("from BIM_ConstructionPlan as a inner join BIM_ConstructionPlanFile as b on a.planFileId=b.id "); sql.append(" inner join t_s_depart as c on b.departId=c.id where 1=1 and a.planParentId='"+paramsModel.getParam9()+"' "); sql.append(sqlWhere.toString()); List<Object[]> list=findListbySql(sql.toString()); if(list.size()>0){ for(int i=0;i<list.size();i++){ Object[] obj=list.get(i); BimPlanComponent plan=new BimPlanComponent(); plan.setId(String.valueOf(obj[0])); plan.setPlanName(StringUtil.isEmptyObject(obj[1], "")); plan.setPlanStartTime(GetDate.StrConvertDateShort(StringUtil.isEmptyObject(obj[2],""))); plan.setPlanEndTime(GetDate.StrConvertDateShort(StringUtil.isEmptyObject(obj[3],""))); plan.setPlanTimeLimit(Integer.parseInt(StringUtil.isEmptyObject(obj[4],null))); plan.setPlanTimeLimitUnit(StringUtil.isEmptyObject(obj[5],null)); plan.setPlanPercentDone(Double.parseDouble(StringUtil.isEmptyObject(obj[6], null))); plan.setPlanPriority(Integer.parseInt(StringUtil.isEmptyObject(obj[7],null))); plan.setPlanParentId(StringUtil.isEmptyObject(obj[8],null)); plan.setPlanIndex(Integer.parseInt(StringUtil.isEmptyObject(obj[9],null))); plan.setPlanResource(StringUtil.isEmptyObject(obj[10],null)); plan.setPlanRemark(StringUtil.isEmptyObject(obj[11],null)); plan.setPlanFileId(StringUtil.isEmptyObject(obj[12],null)); plan.setPlanState(StringUtil.isEmptyObject(obj[13],null)); Long longCount=getCountForJdbc("select count(*) from BIM_ConstructionPlan where planParentId='"+plan.getId()+"'"); if(longCount>0){ plan.setState("closed"); }else{ plan.setState("open"); } planFileList.add(plan); } } } } return planFileList;
特别注意每一级节点都要通过数据来判断他的state属性
第一种方式和第二种方式最终返回都是List集合,然后通过GSON格式化成json数据,丢给treeGrid处理。
虽然第二种方式解决了浏览器被程序卡死的情况。并且每次点击父节点请求子节点时,都只是请求当前父节点的
下一级。但是如果父节点的数据很多的话,我们就要一直拉动滚动条,这样肯定用户使用不方便。
怎么办呢?于是分页
三、同步加异步请求接口分页
首先通过第二个,我们知道了分页的对象是什么?
对,是父节点,也就是parent=null的那一级。而父节点下面的所有子节点都不进行分页
所以我们前端,只需要加上分页属性即可
$("#treeGridPageTable").treegrid({ animate:true, idField: 'id', treeField:"planName", title:"测试TreeGrid[分页]", nowrap: true, animate: true, method: "GET", iconCls: 'icon-save', collapsible: true, loadMsg: "数据加载中,请稍后...", rownumbers: true, fitColumns: true, fit:true, url: url, lines: true, //加树形条 pagination: true, pageSize: 5, pageList: [5,20,30], collapsible:false,//是否可折叠 showFooter:false,//是否使用页脚 striped: true, collapsible:true,//是否可折叠 columns: [[ { field:"id", title: "id", hidden: true }, { field:"planName", title: "项目名称", width: 200 }, { field:"planStartTime", title: "开始时间", align: "center",formatter:dataFormat.formatShort }, { field:"planEndTime", title: "终止时间", align: "center",formatter:dataFormat.formatShort } ]], onBeforeLoad: function(row,param){ if (!row) { // load top level rows param.id = 0; // set id=0, indicate to load new page rows }else{ //这里进行异步请求 paramsModel.param1=""; paramsModel.param2=""; paramsModel.param3=""; paramsModel.param4=""; paramsModel.param5=""; paramsModel.param6=""; paramsModel.param7=""; paramsModel.param8=""; paramsModel.param9=row.id; paramsModel.param10=""; paramsModel.page=1; paramsModel.rows=10; var requestNew = JSON.stringify(paramsModel); requestNew = escape(encodeURIComponent(requestNew)); //处理中文乱码之类 $(this).treegrid("options").url =BIMAPIURL+"rest/getConstructionPlanTreeGridDynamic/"+requestNew+"/"+BIMKey+".json"; } } })
那么后台的json数据自然需要rows和total两个属性StringBuffer sql=new StringBuffer(); List<BimPlanComponent> planFileList=new ArrayList<BimPlanComponent>(); if(StringUtil.isNotEmpty(paramsModel.getParam9())){ if(paramsModel.getParam9().equals("0")){ sql.append("select a.id,planName,planStartTime,planEndTime,planTimeLimit,planTimeLimitUnit,planPercentDone,planPriority,planParentId,planIndex,planResource,planRemark,planFileId,planState "); sql.append("from BIM_ConstructionPlan as a inner join BIM_ConstructionPlanFile as b on a.planFileId=b.id "); sql.append(" inner join t_s_depart as c on b.departId=c.id where 1=1 and a.planParentId is NULL"); //这里给其添加条件 sql.append(sqlWhere.toString()); List<Map<String, Object>> mapList=findForJdbc(sql.toString(),paramsModel.getPage(),paramsModel.getRows()); //查询出数据的条数 //不用分页 if(mapList.size()>0){ for(Map<String, Object> map:mapList){ BimPlanComponent plan=new BimPlanComponent(); plan.setId(String.valueOf(map.get("id"))); plan.setPlanName(StringUtil.isEmptyObject(map.get("planName"), null)); plan.setPlanStartTime(GetDate.StrConvertDateShort(StringUtil.isEmptyObject(map.get("planStartTime"),""))); plan.setPlanEndTime(GetDate.StrConvertDateShort(StringUtil.isEmptyObject(map.get("planEndTime"),""))); plan.setPlanTimeLimit(Integer.parseInt(StringUtil.isEmptyObject(map.get("planTimeLimit"),null))); plan.setPlanTimeLimitUnit(StringUtil.isEmptyObject(map.get("planTimeLimitUnit"),null)); plan.setPlanPercentDone(Double.parseDouble(StringUtil.isEmptyObject(map.get("planPercentDone"),null))); plan.setPlanPriority(Integer.parseInt(StringUtil.isEmptyObject(map.get("planPriority"),null))); plan.setPlanParentId(StringUtil.isEmptyObject(map.get("planParentId"),null)); plan.setPlanIndex(Integer.parseInt(StringUtil.isEmptyObject(map.get("planIndex"),null))); plan.setPlanResource(StringUtil.isEmptyObject(map.get("planResource"),null)); plan.setPlanRemark(StringUtil.isEmptyObject(map.get("planRemark"),null)); plan.setPlanFileId(StringUtil.isEmptyObject(map.get("planFileId"),null)); plan.setPlanState(StringUtil.isEmptyObject(map.get("planState"),null)); //设置state Long tempLong=getCountForJdbc("select count(*) from BIM_ConstructionPlan where planParentId='"+plan.getId()+"'"); if(tempLong>0){ plan.setState("closed"); }else{ plan.setState("open"); } planFileList.add(plan); } } }else{ sql.setLength(0); //清空sql } } Map<String, Object> map = new HashMap<String, Object>(); if(planFileList.size()>0){ map.put("total", dataCount); map.put("rows", planFileList); }else{ map.put("total", 0); map.put("rows", "[]"); } return map;