一.設(shè)計(jì)思想:
1.布局css+div:好處就不多說(shuō)了
2.利用ajax,當(dāng)點(diǎn)擊樹(shù)中某個(gè)節(jié)點(diǎn)時(shí)才生成其子節(jié)點(diǎn),這樣能避免了客戶端一次性加載整棵樹(shù),執(zhí)行速度快
3.樹(shù)的生成用java控制而不是像Dtree的實(shí)現(xiàn)是用javascript實(shí)現(xiàn),dtree實(shí)現(xiàn)的js代碼看了你都會(huì)頭暈,而且這種樹(shù)是一次性加載,如果你想利用異步請(qǐng)求(ajax)獲得一個(gè)樹(shù),那么實(shí)現(xiàn)起來(lái)相當(dāng)麻煩。因?yàn)楫惒秸?qǐng)求后執(zhí)行是在服務(wù)器端執(zhí)行的,js又不能在服務(wù)器端執(zhí)行。但如果數(shù)是用java實(shí)現(xiàn)就不一樣了,呵呵。
4.本程序?qū)崿F(xiàn)分四層:視圖層tree.jsp,treeAction.jsp(可以在通過(guò)控制層實(shí)現(xiàn));業(yè)務(wù)層com.tree.biz.TreeBiz;數(shù)據(jù)持久層用hibernate,數(shù)據(jù)庫(kù)層用mysql
二。數(shù)據(jù)設(shè)計(jì):
很明顯,運(yùn)用表內(nèi)關(guān)聯(lián)是不錯(cuò)的選擇,有多種實(shí)現(xiàn)方式,設(shè)計(jì)了個(gè)parid字段用于標(biāo)識(shí)父級(jí)id(也可以通過(guò)編號(hào)來(lái)控制,01,01001,01002的形式,具體根據(jù)項(xiàng)目的需求來(lái)定)建庫(kù)腳本如下:
************************************數(shù)據(jù)腳本 start********************************
?
create database if not exists `template`;
USE `template`;
?
DROP TABLE IF EXISTS `tree_demo`;
CREATE TABLE `tree_demo` (
? `id` bigint(20) NOT NULL auto_increment,
? `item_num` varchar(20) default NULL,
? `item_name` varchar(50) default NULL,
? `item_parId` bigint(20) default '0',
? PRIMARY KEY? (`id`)
) TYPE=MyISAM;
?
insert into `tree_demo` values
(1,'','一級(jí)菜單1',0),
(2,'','一級(jí)菜單2',0),
(3,'','一級(jí)菜單3',0),
(4,'','一級(jí)菜單4',0),
(5,'','一級(jí)菜單5',0),
(6,'','二級(jí)菜單11',1),
(7,'','二級(jí)菜單12',1),
(8,'','二級(jí)菜單13',1),
(9,'','三級(jí)菜單121',7),
(10,'','三級(jí)菜單122',7),
(11,'','三級(jí)菜單123',7),
(12,'','三級(jí)菜單124',7),
(13,'','二級(jí)菜單21',2),
(14,'','二級(jí)菜單22',2);
*************************************數(shù)據(jù)腳本 end******************************************
三,持久層實(shí)現(xiàn),我是在myeclipse6.0中實(shí)現(xiàn)的,所以pojo類(lèi)和dao的生成很簡(jiǎn)單,文件名是com.tree.hibernate.TreeDemo;com.tree.hibernate.TreeDemoDAO;
四,業(yè)務(wù)邏輯層:com.tree.biz.TreeBiz;代碼如下
****************************com.tree.biz.TreeBiz???? start**************************************
package com.tree.biz;
import java.util.List;
import com.tree.hibernate.TreeDemo;
import com.tree.hibernate.TreeDemoDAO;
public class TreeBiz {
?private TreeDemoDAO dao;
?public TreeBiz(){
??dao = new TreeDemoDAO() ;
?}
?public TreeDemo getTreeDemoById(Long id){
??return dao.findById(id) ;
?}
?
?public List getChlids(TreeDemo treeNode){
??List li = null ;
??li = dao.findByItemParId(treeNode!=null?treeNode.getId():0) ;
??return li ;
?}
}
*************************************com.tree.biz.TreeBiz???? end***********************************
五,視圖層
1.css的實(shí)現(xiàn)代碼
***********************************************tree.css??????? start*******************************
TABLE{
?border:0px;
?margin:0px;
?padding:0px;
?font-size:12px;
}
DIV{
?border:0px;
?margin:0px;
?padding:0px;?
}
.treeClass{
?height:16px;??
?width:200px;????
?font-size:12px;
}
******************************************tree.css???? end**********************************
?2.ajax.js實(shí)現(xiàn)
******************************************ajax.js??????? start**********************************
// JavaScript Document
var divObject ;
function createXMLHttpRequest() {
??//alert("1");
?if (window.ActiveXObject) {
??return? new ActiveXObject("Microsoft.XMLHTTP");
?} else {
??if (window.XMLHttpRequest) {
??return new XMLHttpRequest();
??}
?}
}
function sendRequest(url,showAreaId){//第1個(gè)是提交的url,第2個(gè)是定義要將服務(wù)器返回的信息顯示在哪個(gè)id域里
?var xmlHttp = createXMLHttpRequest();
?url += "&sessionId="+parseInt(Math.random()*(10000000));
?//alert(url);
?xmlHttp.onreadystatechange = function(){
??if(xmlHttp.readyState==4){
???if(xmlHttp.status==200){
????//alert(xmlHttp.responseText);
????document.getElementById(showAreaId).innerHTML=xmlHttp.responseText;
???}else{
????alert('請(qǐng)求的頁(yè)面異常'+xmlHttp.responseText);
????return;
???}
??}??
?};?
?
?xmlHttp.open("GET",url, false);??
?xmlHttp.send(null);
}
******************************************ajax.js??????? end***********************************
?3.tree.jsp,這個(gè)頁(yè)面用于顯示樹(shù)
******************************************tree.jsp??????start***********************************
<%@ page language="java" import="java.util.*" pageEncoding="gb2312"%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
? <head>
??? <title>純jsp 動(dòng)態(tài)tree</title>
?
?<link rel="stylesheet" type="text/css" href="../../css/tree.css">
?
?<script type="text/javascript" src="../../js/ajax.js"></script>
?<script type="text/javascript">
??function treeAcion(nodeId,level){//id:父節(jié)點(diǎn)id ; level:節(jié)點(diǎn)層次
???var node = document.getElementById('child'+nodeId);?
???var varImg = document.getElementById("varImg"+nodeId);??
???if(node.style.display == "none"){
????sendRequest("treeAction.jsp?id="+nodeId+"&level="+level,"child"+nodeId);//向頁(yè)面treeAction.jsp異步請(qǐng)求節(jié)點(diǎn)的下級(jí)節(jié)點(diǎn),并將下級(jí)節(jié)點(diǎn)顯示在id為"child"+nodeId的層里
????if(node.innerHTML != ""){??//有下級(jí)時(shí)才顯示??
?????node.style.display = "" ;
?????varImg.innerHTML="<img? src='../images/o.gif'/>" ;//更改節(jié)點(diǎn)前的圖片
????}????
???}else{
????node.style.display = "none" ;
????varImg.innerHTML="<img? src='../images/c.gif'/>";//更改節(jié)點(diǎn)前的圖片
???}???
???return false ;
??}
?</script>
? </head>
?
? <body>
??? <div id="node0" class="treeClass"><a href="#" onclick="return treeAcion('0','0')" >根接點(diǎn)</a></div>
??? <div id="child0" style="display:none;">??
???? ?
??? </div>
<!--這里我通過(guò)js事件來(lái)生成一級(jí)根目錄,如果想在服務(wù)器端生成這個(gè)樹(shù),則把這段代碼換成java實(shí)現(xiàn),具體參照treeAction.jsp-->
??? <script type="text/javascript">
??? ?treeAcion('0','0');
??? </script>
? </body>
</html>
******************************************tree.jsp????????? end***********************************
4.treeAction.jsp 用于生成指定節(jié)點(diǎn)的下級(jí)節(jié)點(diǎn),可以根據(jù)需要用servlet,或struts來(lái)做控制層實(shí)現(xiàn)
******************************************treeAction.jsp??????start***********************************
<%@ page language="java" import="java.util.*" pageEncoding="gb2312"%>
<jsp:directive.page import="com.tree.biz.TreeBiz"/>
<jsp:directive.page import="com.tree.hibernate.TreeDemo;"/>
<%
?try{
??TreeBiz treeBiz = new TreeBiz() ;
??Long id = Long.valueOf(request.getParameter("id")) ;//父節(jié)點(diǎn)id
??int level = Integer.parseInt(request.getParameter("level")) ;??//父節(jié)點(diǎn)層次
??int curLevel = level+1 ;//計(jì)算當(dāng)前節(jié)點(diǎn)層次
??String space = "" ;?????????????//菜單前豎虛線數(shù)目?
??for(int i= 1 ; i<curLevel ; i++){//根據(jù)級(jí)次生成豎虛線數(shù)目???
???space +="<td width='19' nowrap><img src='../images/g.gif'></td> " ;
??}??
??List<TreeDemo> liChild = treeBiz.getChlids(treeBiz.getTreeDemoById(id));//定義要取得的直屬下級(jí)列表
??for(int i=0 ; i<liChild.size() ; i++){
???TreeDemo treeNode = liChild.get(i) ;
???String imgSrc = "" ;//圖標(biāo)名稱
???if(treeBiz.getChlids(treeNode).size() == 0){//不存在下級(jí)菜單
????if(i==liChild.size()-1){ //到了最后一個(gè)菜單
?????imgSrc = "e.gif" ;
????}else{
?????imgSrc = "l.gif" ;
????}????
???}else{??????????//存在下級(jí)菜單
????imgSrc = "c.gif" ;
???}
%>
???<div id="node<%=treeNode.getId()%>"? class="treeClass">
???<table cellspacing="0" cellpadding="0" align="center" border="0" height="100%" width="100%">
????<tr>
?????<%=space %>
?????<td width='19' nowrap id="varImg<%=treeNode.getId()%>"><img? src="../images/<%=imgSrc%>"/></td>
?????<td nowrap>
??????<a href="#" onclick="return treeAcion('<%=treeNode.getId()%>',<%=curLevel %>);"><%=treeNode.getItemName()%></a>
?????</td>
????</tr>
???</table>
???</div>
???<div id="child<%=treeNode.getId()%>" style="display:none;"></div><%???
??}
??
?}catch(Exception e){
??out.println("<font color='red'>出錯(cuò)了:</font>"+e.getMessage());
?}
%>
******************************************treeAction.jsp????????? end*********************************
?
總結(jié):寫(xiě)一個(gè)demo因該越簡(jiǎn)單越好,特別是實(shí)現(xiàn)上用視圖層加數(shù)據(jù)庫(kù)層,這樣看著也清爽些,不用在各個(gè)層之間轉(zhuǎn)來(lái)轉(zhuǎn)去,呵呵。我們還可以在這個(gè)樹(shù)上增加右鍵功能,這樣可以通過(guò)右鍵直接給指定的節(jié)點(diǎn)增加下級(jí)節(jié)點(diǎn),效果和效率都不錯(cuò)。另外asp的實(shí)現(xiàn)思想也一樣,兩個(gè)頁(yè)面就行。代碼量70行左右就能實(shí)現(xiàn)了。