作者: ecsun  链接:http://papa.javaeye.com/blog/231734  发表时间: 2008年08月22日

声明:本文系JavaEye网站发布的原创博客文章,未经作者书面许可,严禁任何网站转载本文,否则必将追究法律责任!

   三年前,我刚刚毕业,取得了第一份工作,在经过三个月的试用期以后,开始参考一个小项目的需求调研及前期的准备工作,由于公司人手有限,最终的编码实现,也落到了我身上.

 

   在做demo的时候,我遇到的第一个项工作,就是去实现一个无限的方便的树形结构,因为当时的项目,是建筑节能领域的一个新项目,公司在这方面,没有太多的沉淀,而公司已有的资源,是自己的一个开发平台,叫做MISD,不知道现在市场上还有没有个工具.MISD无法实现这样的结构,就只有能过编码来实现了.初出茅庐,经验不足,没有那么的考虑,于是拿tree maker一个多小时,把项目要用的几个树结构,做出来了,Ctrl+c,Ctrl+v,搞定~~拿去给经理一看:小伙子,手脚挺麻利的嘛~不错不错~~我先看看.

    心里挺乐~~真的挺乐,没几分钟,又被leader叫了过去,这树我在哪里加节点编辑啊?"要改代码啊."leader听了傻了,"那不行啊,客户不懂代码~",去写一个动态的吧,存数据....

 

    连静态的都是拿tree maker做的,动态的,我这怎么做啊?当时是真的不会做.于是到网上去找,还真不少.经过对比,在struts-menu里面,有一个数据库支持的树结构的例子,于是,按照文档,做了几天,还好,结构出来了.基本实现了项目所需要的功能,而这个项目,后来里面很多的树结构,都是通过我对struts-menu的修改实现的,效果还不错.

 

  这是我第一次写树结构的经历,整个过程中也是被leader叫去谈了好几次,好在最终没有给项目造成影响,保证了项目的按时完成,这套结构,后来也成为公司各个项目中使用的标准树结构,即使在离职到现在,这套结构,包括后来我搭建的项目开发基础框架,依然在原公司各个项目中发挥着重要的作用.

 

 

  我相信很多朋友和我一样,在刚开始做项目的时候,都或多或少的,被这种无限的树形结构所困扰.希望我的经历,可以给后来的朋友带来一些有用的参考价值.

 

  所谓无限的权形结构,最主要的方面,主要是可以无限的添加子结节,到底有没有人会无限的去添加子节点的,在我做过的项目中,不存在这样的问题,当节点添加的第四级的时候,已经很少有用户希望再点下去了,所以说,无限,只是客户或者leader希望服务于项目的树形结构更灵活而提出的一种需求,因为不管是客户也好,leader也好,在项目开始的时候,并不知道,未来需要多少级树形结构.

 

  设计这个的树形结构,最基础的,是数据库表的设计,要实现无限,数据库需要像这样的结构:

写道
create table tree(
id varchar(32) primary key,
name varchar(100) not null,
parent_id varchar(32)
);

 

其中最主要的是id和parent_id两上字段,它们定义了所有父节点与子节点之间的父子关系,这也是一种我们很常用的递归表结构设计,或者在ORM叫做自身双向一对多关系映射.现在oracle,SQLServer 2005都支持了递归SQL的查询.不过我们不常用.

 

有了这样的数据表结构,那么不管我们用什么技术去实现无限级树形结构,都是很容易的.

 

1.struts-menu.

 

   struts-menu从本质上来说,是一种taglib,作者把众多的脚本以及对树的操作,封装成简单的taglib,用户在生成树的时候,只需要简单的几句标签,就可以完成整个树形结构的生成,比如经典的struts-menu生成树形结构的页面代码,基本如下:

 

写道
<menu:useMenuDisplayer name="ListMenu" repository="repository">
<c:forEach var="parent" items="${parentMenus}">
<menu:displayMenu name="${parent.title}"> </menu:displayMenu>
</c:forEach>
</menu:useMenuDisplayer>

 

其它的,基本上是一个js的import,没有更多其它的代码,当然,我们需要提前将树结构数据放入request或session中.

 

关于struts-menu的详细实现,可以参考我写的另一篇博客:

 

http://papa.javaeye.com/admin/blogs/145554

 

 

2.DTree

 

 struts-menu 有一个很大的问题,就是速度,在界面上的反应速度,明显比较慢,当然,很多人从很多方面对struts-menu做了优化,但优化以后,个人觉得,还不是很理想,这个时候,我们就需要一种速度相对比较快的树形结构了,Dtree在方面,可以说做的相对比较好.同时,使用也很简单,Dtree可以使用和struts-menu一样的数据结构,只需要在里面加入一个很简单的随机数就可以了,在http://papa.javaeye.com/admin/blogs/145554有完整的描述 ,可以看到,如何将struts-menu的数据结构转化为Dtree的数据结构.

 

Dtree在页面中的展现,主要需要使用少量的javascript,这些代码,可以参考官方网站上的,也可以参考下面不太友好的实现.

 

写道
<script type="text/javascript">
<!--

d = new dTree('d');
<%
try{

List nodesCollection =(List)request.getAttribute("dTree");

Iterator it=nodesCollection.iterator();
while(it.hasNext()){
Map item=(Map) it.next();
%>
d.add(<%=item.get("dTree_id").toString()%>,<%=item.get("dTree_parentid").toString()%>,'<%=item.get("name").toString()%>','<%=item.get("location").toString()%>','','<%=item.get("target")%>');
<%
}
}catch(Exception e){
System.out.println(e.toString());
}
%>


document.write(d);

//-->
</script>

 

可以看到,dtree的实现,也是很简单的.

 

 

3.Ext Tree.

  

   从一年多以前,Ext 开始火爆,很多人开始使用ext tree ,也有很多人,因为ext tree 而开始使用ext,在这一块,在我的另一篇博客(三篇连载,请在博客中查询):

http://papa.javaeye.com/admin/blogs/157922

 

同时,在开源项目FaceYe中,使用了大量的Ext tree,包括带多选框的Ext tree 结构,可以到http://faceye.googlecode.com 下载FaceYe代码包并安装查看效果,在我的博客http://papa.javaeye.com中,对FaceYe有完整的描述 .

 

有完整的描述,Ext tree使用的数据结构,与struts-menu类似.

 

但需要注意的是,ext tree的加载速度并不太好.大的项目使用需要注意.

 

4.YUI tree.

 

   说了Ext tree,再说YUI tree,似乎有点多此一举,使用Ext的朋友,都知道Ext tree和 YUI tree可以说是师出同门,但Ext tree对YUI  tree做了比较多的扩展.也就是因为这些扩展,使得使用Ext tree 相对简单,而使用YUI tree或许多多少少有些不便.废话少说,先看一下如何展现YUI TREE:

 

 

写道
<script type="text/javascript">

(function(){
var tree;
function loadNodeData(node,fnLoadComplete){
var callback={
success:function(o){
if(o.responseText){
var result=YAHOO.lang.JSON.parse(o.responseText);
if(result){
for(var i=0;i<result.length;i++){
var tempNode=new YAHOO.widget.TextNode(result[i],node,false);
if(result[i].isLeaf){
tempNode.isLeaf=true;
}
}
}
tree.draw();
}
o.argument.fnLoadComplete();
},
failure:function(o){
o.argument.fnLoadComplete();
},
argument:{
'node':node,
'fnLoadComplete':fnLoadComplete
},
timeout:7000
};
var req=YAHOO.util.Connect.asyncRequest('POST', base.path+'security/treeAction!tree.do', callback,'nodeId='+node.data.id);
}

function init(){
tree=new YAHOO.widget.TreeView("tree-viewer");
tree.setDynamicLoad(loadNodeData,0);
initRoot();
tree.draw();
}
function initRoot(){
var root=tree.getRoot();
var callback={
success:function(o){
var result=YAHOO.lang.JSON.parse(o.responseText);
for(var i=0;i<result.length;i++){
var node=new YAHOO.widget.TextNode(result[i],root,false);
if(result[i].isLeaf){
node.isLeaf=true;
}
}
tree.draw();
},
failure:function(o){
}
};
var req=YAHOO.util.Connect.asyncRequest('POST', base.path+'security/treeAction!tree.do', callback);
}
YAHOO.util.Event.on(window,'load',function(e){
init();
});
})();

</script>

 

后台方面的代码,大多都大同小异,有兴趣的朋友参考FaceYe的实现了.

 

 



已有 0 人发表留言,猛击->>这里<<-参与讨论


JavaEye推荐