http://blog.csdn.net/lenotang/article/details/3102727


 1.      首先还是主要的显示页面tree.html,这里有两个地方要注意一下,一个是我们引用的JS如果采用GBK的默认编码,浏览器会显示未结束的字符串常量的错误,所以我们一般会修改JS文件的编码方式为UTF-8,或者在导入JS时加上编码字符集。第二个是我们要定义一个显示TreePanelDIV

2.      然后是我们的主体JS文件,tree.js,为了凸显主题,这个我已经做了必要的简化,还做了详细的注释,大家好好看一下。 


  1. Ext.onReady(function() { 
  2.     Ext.QuickTips.init();// 浮动信息提示 
  3.     Ext.BLANK_IMAGE_URL = 'resources/images/default/s.gif';// 替换图片文件地址为本地 
  4.   
  5.     // 创建一个简写 
  6.     var Tree = Ext.tree; 
  7.     // 定义根节点的Loader 
  8.     var treeloader = new Tree.TreeLoader({ 
  9.             // dataUrl : 'tree.jsp'//这里可以不需要指定URL,在加载前事件响应里面设置 
  10.             }); 
  11.   
  12.     // 添加一个树形面板 
  13.     var treepanel = new Tree.TreePanel({ 
  14.         // renderTo:"tree_div",//如果使用renderTo,则不能使用setRootNode()方法,需要在TreePanel中设置root属性。 
  15.         el : 'tree-panel',// 将树形添加到一个指定的div中,非常重要! 
  16.         region : 'west'
  17.         title : '功能菜单'
  18.         width : 200, 
  19.         minSize : 180, 
  20.         maxSize : 250, 
  21.         split : true
  22.         autoHeight : false
  23.         frame : true,// 美化界面 
  24.         // width : 200,//面板宽度 
  25.         // title : '可编辑和拖动的异步树',//标题 
  26.         // autoScroll : true, // 自动滚动 
  27.         enableDD : true,// 是否支持拖拽效果 
  28.         containerScroll : true,// 是否支持滚动条 
  29.         rootVisible : true// 是否隐藏根节点,很多情况下,我们选择隐藏根节点增加美观性 
  30.         border : true// 边框 
  31.         animate : true// 动画效果 
  32.         loader : treeloader 
  33.             // 树加载 
  34.         }); 
  35.     // 异步加载根节点 
  36.     var rootnode = new Tree.AsyncTreeNode({ 
  37.                 id : '0'
  38.                 text : '家电品牌总类'
  39.                 draggable : false,// 根节点不容许拖动 
  40.                 expanded : true 
  41.             }); 
  42.   
  43.     // 为tree设置根节点 
  44.     treepanel.setRootNode(rootnode); 
  45.   
  46.     // 响应加载前事件,传递node参数 
  47.     treepanel.on('beforeload'function(node) { 
  48.                 treepanel.loader.dataUrl = 'tree.jsp?parentId=' + node.id; // 定义子节点的Loader 
  49.             }); 
  50.     // 渲染树形 
  51.     treepanel.render(); 
  52.     // 展开节点,第一个参数表示是否级联展开子节点 
  53.     rootnode.expand(true); 
  54.   
  55.     // 设置树的点击事件 
  56.     function treeClick(node, e) { 
  57.         if (node.isLeaf()) { 
  58.             e.stopEvent(); 
  59.             var n = contentPanel.getComponent(node.id); 
  60.             if (!n) { 
  61.                 var n = contentPanel.add({ 
  62.                             'id' : node.id, 
  63.                             'title' : node.text, 
  64.                             closable : true
  65.                             autoLoad : { 
  66.                                 url : 'tabFrame.jsp?url=grid.html'
  67.                                 scripts : true 
  68.                             } // 通过autoLoad属性载入目标页,如果要用到脚本,必须加上scripts属性 
  69.                         }); 
  70.             } 
  71.             contentPanel.setActiveTab(n); 
  72.         } 
  73.     } 
  74.     // 增加鼠标单击事件 
  75.     treepanel.on('click', treeClick); 
  76.   
  77.     // 定义右键菜单 
  78.     var rightClick = new Ext.menu.Menu({ 
  79.                 id : 'rightClickCont'
  80.                 items : [{ 
  81.                             id : 'rMenu1'
  82.                             text : '添加节点'
  83.                             // 增加菜单点击事件 
  84.                             handler : function() { 
  85.                                 alert('添加节点的实现!'); 
  86.                             } 
  87.                         }, { 
  88.                             id : 'rMenu2'
  89.                             text : '编辑节点' 
  90.                         }, { 
  91.                             id : 'rMenu3'
  92.                             text : '删除节点' 
  93.                         }] 
  94.             }); 
  95.     // 增加右键点击事件 
  96.     treepanel.on('contextmenu'function(node, event) {// 声明菜单类型 
  97.                 event.preventDefault();// 阻止浏览器默认右键菜单显示 
  98.                 rightClick.showAt(event.getXY());// 取得鼠标点击坐标,展示菜单 
  99.             }); 
  100.   
  101.     /* 
  102.      * 设置tree的节点放置函数此函数有一个很重要的参数对象e e对象有三个重要的属性,分别为dropNode,target,point 
  103.      * 1.dropNode为在拖动时鼠标抓住的节点 2.target为将要放置在某处的节点 
  104.      * 3.point为被放置的状态,分别有append表示添加,above节点的上方,below节点的下方。 
  105.      * 
  106.      */ 
  107.     treepanel.on('nodedrop'function(e) { 
  108.   
  109.                 if (e.point == 'append') { 
  110.                     alert('当前"' + e.dropNode.text + '"划到"' + e.target.text 
  111.                             + '"里面!'); 
  112.                 } else if (e.point == 'above') { 
  113.                     alert('当前"' + e.dropNode.text + '"放在了"' + e.target.text 
  114.                             + '"上面!'); 
  115.                 } else if (e.point == 'below') { 
  116.                     alert('当前"' + e.dropNode.text + '"放在了"' + e.target.text 
  117.                             + '"下面!'); 
  118.                 } 
  119.             }); 
  120.   
  121.     // 在原有的树形添加一个TreeEditor 
  122.     var treeEditer = new Tree.TreeEditor(treepanel, { 
  123.                 allowBlank : false 
  124.             }); 
  125.     /* 
  126.      * 为创建的treeEditer添加事件 有两个事件最为常用,一个为beforestartedit另一个为complete 
  127.      * 从名字就可以看出,beforestartedit事件是在编辑前的事件,因此可以通过它来判断那些节点可以编辑那些不可以。 
  128.      * complete为编辑之后的事件,在这里面可以添加很多事件,比如添加一个Ext.Ajax向后台传送修改的值等等。 
  129.      */ 
  130.     treeEditer.on("beforestartedit"function(treeEditer) { 
  131.                 var tempNode = treeEditer.editNode;// 将要编辑的节点 
  132.                 if (tempNode.isLeaf()) {// 这里设定叶子节点才容许编辑 
  133.                     return true
  134.                 } else { 
  135.                     return false
  136.                 } 
  137.             }); 
  138.   
  139.     treeEditer.on("complete"function(treeEditer) { 
  140.                 alert("被修改为" + treeEditer.editNode.text); 
  141.             }); 
  142.   
  143.     // (1)通过TabPanel控件的html属性配合<iframe>实现。该方法是利用 
  144.     // html属性中包含<iframe>的语法来调用另一个页面,具体见代码。 
  145.     // (2)通过TabPanel控件的autoLoad属性实现。该方法是利用autoLoad属性,它有很多参数, 
  146.     // 其中有两个比较重要,url表示要载入的文件,scripts表示载入的文件是否含有脚本,该属性相当重要, 
  147.     // 如果在新的页面中要创建Ext控件,必须指定该参数。该方法实现较前一个复杂,因为引入的文件不是一个完整的html文件, 
  148.     // 有可能只是内容的一部分,但是资源占用较少,而且载入速度较快(它有一个载入指示) 
  149.   
  150.     // 添加第一个节点(html) 
  151.     treepanel.root.appendChild(new Ext.tree.TreeNode({ 
  152.         id : 'htmlPanel'
  153.         text : '通过html打开'
  154.         listeners : { 
  155.             'click' : function(node, event) { 
  156.                 event.stopEvent(); 
  157.                 var n = contentPanel.getComponent(node.id); 
  158.                 if (!n) { // 判断是否已经打开该面板 
  159.                     n = contentPanel.add({ 
  160.                         'id' : node.id, 
  161.                         'title' : node.text, 
  162.                         closable : true// 通过html载入目标页 
  163.                         html : '<iframe scrolling="auto" frameborder="0" width="100%" height="100%" src="grid.html"></iframe>' 
  164.                     }); 
  165.                 } 
  166.                 contentPanel.setActiveTab(n); 
  167.             } 
  168.         } 
  169.     })); 
  170.   
  171.     // 添加第二个节点(autoLoad) 
  172.     treepanel.root.appendChild(new Ext.tree.TreeNode({ 
  173.                 id : 'autoLoadPanel'
  174.                 text : '通过autoLoad打开'
  175.                 listeners : { 
  176.                     'click' : function(node, event) { 
  177.                         event.stopEvent(); 
  178.                         var n = contentPanel.getComponent(node.id); 
  179.                         if (!n) { // //判断是否已经打开该面板 
  180.                             n = contentPanel.add({ 
  181.                                         'id' : node.id, 
  182.                                         'title' : node.text, 
  183.                                         closable : true
  184.                                         autoLoad : { 
  185.                                             url : 'tabFrame.jsp?url=grid.html'
  186.                                             scripts : true 
  187.                                         } // 通过autoLoad属性载入目标页,如果要用到脚本,必须加上scripts属性 
  188.                                     }); 
  189.                         } 
  190.                         contentPanel.setActiveTab(n); 
  191.                     } 
  192.                 } 
  193.             })); 
  194.   
  195.     // 右边具体功能面板区 
  196.     var contentPanel = new Ext.TabPanel({ 
  197.         region : 'center'
  198.         enableTabScroll : true
  199.         activeTab : 0, 
  200.         items : [{ 
  201.             id : 'homePage'
  202.             title : '首页'
  203.             autoScroll : true
  204.             html : '<div style="position:absolute;color:#ff0000;top:40%;left:40%;">Tree控件和TabPanel控件结合功能演示</div>' 
  205.         }] 
  206.     }); 
  207.   
  208.     new Ext.Viewport({ 
  209.                 layout : 'border'// 使用border布局 
  210.                 defaults : { 
  211.                     activeItem : 0 
  212.                 }, 
  213.                 items : [treepanel, contentPanel] 
  214.             }); 
  215.   
  216. }); 

 

3.   再接着是tree.jsExtJSTreeLoader调用的tree.jsp,在目录树上点击TreeNode后会加载下一级节点。tree.jsp负责TreeNode点击后,传回由下一级节点构造的JSON数据,也就是前台树异步请求访问的后台WEB组件。它会调用JAVABEAN操作数据库,得到每个节点的子节点数据。这里由于我们后台需要返回给客户端JSON格式的数据,也就是需要操作JSON数据格式。所以我们首先要下载JSON-lib,地址:http://json-lib.sourceforge.net/打开网址后,首页上有一段话: 
 Json-lib requires (at least) the following dependencies in your classpath: 
      jakarta commons-lang 2.3 
      jakarta commons-beanutils 1.7.0 
      jakarta commons-collections 3.2 
      jakarta commons-logging 1.1.1 
      ezmorph 1.0.4 
   
需要下载上述jar文件,配合JSON-lib 一起使用。
 
   commons 
下载地址:http://commons.apache.org/ 
   ezmorph 
下载地址:
http://ezmorph.sourceforge.net 
   
或者,到 http://www.docjar.com 搜索下载。
 
   JSON
的用法,我们已经提过多次,大家可参考相关文档。看看tree.jsp的代码:

 

<%@ page language="java" pageEncoding="utf-8"%>

<jsp:useBean class="org.leno.javabean.JSONTree" id="JSONTree"></jsp:useBean>

<%

    String parentId = "";

    if (request.getParameter("parentId") != null) {

        parentId = request.getParameter("parentId").toString();

    }

    JSONTree.setparentId(parentId);

%>

<%=JSONTree.getJSONString()%>

 

4.       后台用到的JAVA,这里包括访问数据库的数据源工厂类DataSourceFactory(这里用到了DBCP连接池,大家要记得导入连接sqlserver 2000数据库和dbcp连接池的相关JAR包!),定义树节点的属性,包括节点IDText、图标、是否为叶子节点、是否展开等的类JSONTreeNode,还有类似DAO能够封装数据访问和格式转换细节的JSONTree

 

  1.   
  2. package org.leno.javabean; 
  3.   
  4. import java.sql.SQLException; 
  5. import org.apache.commons.dbcp.BasicDataSource; 
  6.   
  7. public class DataSourceFactory { 
  8.   
  9.     /** 
  10.      * @param args 
  11.      */ 
  12.     private static BasicDataSource ds; 
  13.   
  14.     public static BasicDataSource getDataSource() { 
  15.         if (ds == null) { 
  16.             ds = new BasicDataSource(); 
  17.             ds.setDriverClassName("com.microsoft.jdbc.sqlserver.SQLServerDriver"); 
  18.             ds.setUrl("jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=java28"); 
  19.             ds.setUsername("sa"); 
  20.             ds.setPassword("123"); 
  21.             ds.setMaxActive(5); 
  22.         } 
  23.         return ds; 
  24.   
  25.     } 
  26.   
  27.     public static void main(String[] args) { 
  28.         // TODO Auto-generated method stub 
  29.         try { 
  30.             System.out.println(DataSourceFactory.getDataSource() 
  31.                     .getConnection()); 
  32.         } catch (SQLException e) { 
  33.             // TODO Auto-generated catch block 
  34.             e.printStackTrace(); 
  35.         } 
  36.   
  37.     } 
  38.   
  39. package org.leno.javabean; 
  40.   
  41. /** 
  42.  * @author leno 
  43.  *定义树节点的属性,包括节点ID、Text、图标、是否为叶子节点、是否展开等。 
  44.  */ 
  45. public class JSONTreeNode { 
  46.   
  47.         private String id;            //ID 
  48.          private String text;          //节点显示 
  49.          private String cls;           //图标 
  50.          private boolean leaf;         //是否叶子 
  51.          private String href;          //链接 
  52.          private String hrefTarget;    //链接指向 
  53.          private boolean expandable;   //是否展开 
  54.          private String description;   //描述信息 
  55.          
  56.         get/set…… 
  57. package org.leno.javabean; 
  58.   
  59. import java.sql.*; 
  60. import java.util.*; 
  61. import net.sf.json.JSONArray; 
  62.   
  63. public class JSONTree { 
  64. private String parentId; 
  65.     
  66.     public String getJSONString(){      
  67.         Connection con =null
  68.         Statement st = null
  69.         ResultSet rs = null
  70.         List<JSONTreeNode> treeNodeArray = null
  71.         
  72.         String SQLString = "SELECT * FROM Categories WHERE parentId="+this.parentId+" ORDER BY categoryId";    
  73.             
  74.         try 
  75.         { 
  76.             con = DataSourceFactory.getDataSource().getConnection(); 
  77.             st = con.createStatement(); 
  78.             //查找所有拥有下级类别的类别ID 
  79.             rs = st.executeQuery("SELECT parentId FROM Categories WHERE parentId>0 Group By parentId Order By parentId");            
  80.             
  81.             StringBuffer parentIDBuffer =new StringBuffer(); 
  82.             parentIDBuffer.append("|");         
  83.             while(rs.next()) 
  84.             { 
  85.                 parentIDBuffer.append(rs.getString("parentId")); 
  86.                 parentIDBuffer.append("|"); 
  87.             } 
  88.             //得到所有的parentcategoryId列表 
  89.             String parentIDString = parentIDBuffer.toString();         
  90.             
  91.             rs = st.executeQuery(SQLString);    
  92.             treeNodeArray = new ArrayList<JSONTreeNode>(); 
  93.             while(rs.next()) 
  94.             { 
  95.                 JSONTreeNode treeNode = new JSONTreeNode(); 
  96.                 String categoryId = rs.getString("categoryId"); 
  97.                 treeNode.setId(categoryId); 
  98.                 treeNode.setText(rs.getString("categoryName")); 
  99.                 treeNode.setDescription(rs.getString("description"));          
  100. //                    treeNode.setHref("rightframe.jsp?categoryId=" 
  101. //                      + rs.getString("categoryId").toString()); 
  102. //                    treeNode.setHrefTarget("rightFrame");               
  103.                 
  104.                 if (parentIDString.indexOf("|"+categoryId+"|")>=0//父节点 
  105.                     { 
  106.                         treeNode.setCls("folder"); 
  107.                         treeNode.setLeaf(false); 
  108.                         treeNode.setExpandable(false); 
  109.                     } 
  110.                     else //子节点 
  111.                    { 
  112.                         treeNode.setCls("file"); 
  113.                         treeNode.setLeaf(true); 
  114.                         treeNode.setExpandable(false); 
  115.                     } 
  116.                     treeNodeArray.add(treeNode); 
  117.             } 
  118.             
  119.             JSONArray JsonArray = JSONArray.fromObject(treeNodeArray); //得到JSON数组     
  120.   
  121.             return JsonArray.toString();//返回JSON数据 
  122.         } 
  123.         catch(Exception e) 
  124.         { 
  125.             System.out.println("getJSONString() of JSONTree.java throws : "+e.toString()); 
  126.             return ""
  127.         } 
  128.         finally 
  129.         { 
  130.            try { 
  131.            if(con!=null&&!con.isClosed()){ 
  132.                 con.close(); 
  133.            } 
  134.         } catch (SQLException e) { 
  135.             // TODO Auto-generated catch block 
  136.             e.printStackTrace(); 
  137.         } 
  138.         } 
  139.     }   
  140.   
  141.   
  142.     public String getparentId() { 
  143.         return parentId; 
  144.     } 
  145.   
  146.     public void setparentId(String parentId) { 
  147.         this.parentId = parentId; 
  148.     } 
  149.   

 

5.      最后是我们的数据库脚本script.sql,大家用SQLServer 2000数据库创建一个java28database,然后执行下列脚本即可。