最近做一个项目,项目中开始使用的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;