免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2422 | 回复: 0
打印 上一主题 下一主题

ExtJS专题-TreePanel(1)收藏 [复制链接]

论坛徽章:
59
2015七夕节徽章
日期:2015-08-24 11:17:25ChinaUnix专家徽章
日期:2015-07-20 09:19:30每周论坛发贴之星
日期:2015-07-20 09:19:42ChinaUnix元老
日期:2015-07-20 11:04:38荣誉版主
日期:2015-07-20 11:05:19巳蛇
日期:2015-07-20 11:05:26CU十二周年纪念徽章
日期:2015-07-20 11:05:27IT运维版块每日发帖之星
日期:2015-07-20 11:05:34操作系统版块每日发帖之星
日期:2015-07-20 11:05:36程序设计版块每日发帖之星
日期:2015-07-20 11:05:40数据库技术版块每日发帖之星
日期:2015-07-20 11:05:432015年辞旧岁徽章
日期:2015-07-20 11:05:44
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-06-17 16:57 |只看该作者 |倒序浏览

ExtJS专题-TreePanel(1)收藏
这次我们来学习下ExtJS中的树面板(TreePanel),很多人都说,只是为了树,都要好好学习ExtJS!可见大家对Tree的一往情深。从另一方面来说,Tree这种结构在实际项目中也的确用得相当广泛,所以我们很有必要研究一下它。这次我们完成的树大致上有这样的功能:它的节点是动态异步从后台(存储在数据库中一张电子产品分类表)加载的,节点之间可以拖曳,节点可以编辑,同时还支持右键菜单,而且,它能够和TabPanel结合构成经典的布局方式。呵呵,是不是很强大?!大家已经看到,我们组件的讲解是逐步递推的,所以我们这里也会用到刚学过的GridPanel和TabPanel这些实用的面板。我们看效果先:



好啦,洪哥,我们动手吧!

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

Insert title here

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

Ext.onReady(function() {
    Ext.QuickTips.init();// 浮动信息提示
    Ext.BLANK_IMAGE_URL = 'resources/images/default/s.gif';// 替换图片文件地址为本地

    // 创建一个简写
    var Tree = Ext.tree;
    // 定义根节点的Loader
    var treeloader = new Tree.TreeLoader({
            // dataUrl : 'tree.jsp'//这里可以不需要指定URL,在加载前事件响应里面设置
            });

    // 添加一个树形面板
    var treepanel = new Tree.TreePanel({
        // renderTo:"tree_div",//如果使用renderTo,则不能使用setRootNode()方法,需要在TreePanel中设置root属性。
        el : 'tree-panel',// 将树形添加到一个指定的div中,非常重要!
        region : 'west',
        title : '功能菜单',
        width : 200,
        minSize : 180,
        maxSize : 250,
        split : true,
        autoHeight : false,
        frame : true,// 美化界面
        // width : 200,//面板宽度
        // title : '可编辑和拖动的异步树',//标题
        // autoScroll : true, // 自动滚动
        enableDD : true,// 是否支持拖拽效果
        containerScroll : true,// 是否支持滚动条
        rootVisible : true, // 是否隐藏根节点,很多情况下,我们选择隐藏根节点增加美观性
        border : true, // 边框
        animate : true, // 动画效果
        loader : treeloader
            // 树加载
        });
    // 异步加载根节点
    var rootnode = new Tree.AsyncTreeNode({
                id : '0',
                text : '家电品牌总类',
                draggable : false,// 根节点不容许拖动
                expanded : true
            });

    // 为tree设置根节点
    treepanel.setRootNode(rootnode);

    // 响应加载前事件,传递node参数
    treepanel.on('beforeload', function(node) {
                treepanel.loader.dataUrl = 'tree.jsp?parentId=' + node.id; // 定义子节点的Loader
            });
    // 渲染树形
    treepanel.render();
    // 展开节点,第一个参数表示是否级联展开子节点
    rootnode.expand(true);

    // 设置树的点击事件
    function treeClick(node, e) {
        if (node.isLeaf()) {
            e.stopEvent();
            var n = contentPanel.getComponent(node.id);
            if (!n) {
                var n = contentPanel.add({
                            'id' : node.id,
                            'title' : node.text,
                            closable : true,
                            autoLoad : {
                                url : 'tabFrame.jsp?url=grid.html',
                                scripts : true
                            } // 通过autoLoad属性载入目标页,如果要用到脚本,必须加上scripts属性
                        });
            }
            contentPanel.setActiveTab(n);
        }
    }
    // 增加鼠标单击事件
    treepanel.on('click', treeClick);

    // 定义右键菜单
    var rightClick = new Ext.menu.Menu({
                id : 'rightClickCont',
                items : [{
                            id : 'rMenu1',
                            text : '添加节点',
                            // 增加菜单点击事件
                            handler : function() {
                                alert('添加节点的实现!');
                            }
                        }, {
                            id : 'rMenu2',
                            text : '编辑节点'
                        }, {
                            id : 'rMenu3',
                            text : '删除节点'
                        }]
            });
    // 增加右键点击事件
    treepanel.on('contextmenu', function(node, event) {// 声明菜单类型
                event.preventDefault();// 阻止浏览器默认右键菜单显示
                rightClick.showAt(event.getXY());// 取得鼠标点击坐标,展示菜单
            });

    /*
     * 设置tree的节点放置函数此函数有一个很重要的参数对象e e对象有三个重要的属性,分别为dropNode,target,point
     * 1.dropNode为在拖动时鼠标抓住的节点 2.target为将要放置在某处的节点
     * 3.point为被放置的状态,分别有append表示添加,above节点的上方,below节点的下方。
     *
     */
    treepanel.on('nodedrop', function(e) {

                if (e.point == 'append') {
                    alert('当前"' + e.dropNode.text + '"划到"' + e.target.text
                            + '"里面!');
                } else if (e.point == 'above') {
                    alert('当前"' + e.dropNode.text + '"放在了"' + e.target.text
                            + '"上面!');
                } else if (e.point == 'below') {
                    alert('当前"' + e.dropNode.text + '"放在了"' + e.target.text
                            + '"下面!');
                }
            });

    // 在原有的树形添加一个TreeEditor
    var treeEditer = new Tree.TreeEditor(treepanel, {
                allowBlank : false
            });
    /*
     * 为创建的treeEditer添加事件 有两个事件最为常用,一个为beforestartedit另一个为complete
     * 从名字就可以看出,beforestartedit事件是在编辑前的事件,因此可以通过它来判断那些节点可以编辑那些不可以。
     * complete为编辑之后的事件,在这里面可以添加很多事件,比如添加一个Ext.Ajax向后台传送修改的值等等。
     */
    treeEditer.on("beforestartedit", function(treeEditer) {
                var tempNode = treeEditer.editNode;// 将要编辑的节点
                if (tempNode.isLeaf()) {// 这里设定叶子节点才容许编辑
                    return true;
                } else {
                    return false;
                }
            });

    treeEditer.on("complete", function(treeEditer) {
                alert("被修改为" + treeEditer.editNode.text);
            });

    // (1)通过TabPanel控件的html属性配合实现。该方法是利用
    // html属性中包含的语法来调用另一个页面,具体见代码。
    // (2)通过TabPanel控件的autoLoad属性实现。该方法是利用autoLoad属性,它有很多参数,
    // 其中有两个比较重要,url表示要载入的文件,scripts表示载入的文件是否含有脚本,该属性相当重要,
    // 如果在新的页面中要创建Ext控件,必须指定该参数。该方法实现较前一个复杂,因为引入的文件不是一个完整的html文件,
    // 有可能只是内容的一部分,但是资源占用较少,而且载入速度较快(它有一个载入指示)

    // 添加第一个节点(html)
    treepanel.root.appendChild(new Ext.tree.TreeNode({
        id : 'htmlPanel',
        text : '通过html打开',
        listeners : {
            'click' : function(node, event) {
                event.stopEvent();
                var n = contentPanel.getComponent(node.id);
                if (!n) { // 判断是否已经打开该面板
                    n = contentPanel.add({
                        'id' : node.id,
                        'title' : node.text,
                        closable : true, // 通过html载入目标页
                        html : ''
                    });
                }
                contentPanel.setActiveTab(n);
            }
        }
    }));

    // 添加第二个节点(autoLoad)
    treepanel.root.appendChild(new Ext.tree.TreeNode({
                id : 'autoLoadPanel',
                text : '通过autoLoad打开',
                listeners : {
                    'click' : function(node, event) {
                        event.stopEvent();
                        var n = contentPanel.getComponent(node.id);
                        if (!n) { // //判断是否已经打开该面板
                            n = contentPanel.add({
                                        'id' : node.id,
                                        'title' : node.text,
                                        closable : true,
                                        autoLoad : {
                                            url : 'tabFrame.jsp?url=grid.html',
                                            scripts : true
                                        } // 通过autoLoad属性载入目标页,如果要用到脚本,必须加上scripts属性
                                    });
                        }
                        contentPanel.setActiveTab(n);
                    }
                }
            }));

    // 右边具体功能面板区
    var contentPanel = new Ext.TabPanel({
        region : 'center',
        enableTabScroll : true,
        activeTab : 0,
        items : [{
            id : 'homePage',
            title : '首页',
            autoScroll : true,
            html : 'Tree控件和TabPanel控件结合功能演示'
        }]
    });

    new Ext.Viewport({
                layout : 'border', // 使用border布局
                defaults : {
                    activeItem : 0
                },
                items : [treepanel, contentPanel]
            });

});

3.   再接着是tree.js中ExtJS的TreeLoader调用的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的代码:


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

package org.leno.javabean;

import java.sql.SQLException;
import org.apache.commons.dbcp.BasicDataSource;

public class DataSourceFactory {

    /**
     * @param args
     */
    private static BasicDataSource ds;

    public static BasicDataSource getDataSource() {
        if (ds == null) {
            ds = new BasicDataSource();
            ds.setDriverClassName("com.microsoft.jdbc.sqlserver.SQLServerDriver");
            ds.setUrl("jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=java28");
            ds.setUsername("sa");
            ds.setPassword("123");
            ds.setMaxActive(5);
        }
        return ds;

    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        try {
            System.out.println(DataSourceFactory.getDataSource()
                    .getConnection());
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

}
package org.leno.javabean;

/**
* @author leno
*定义树节点的属性,包括节点ID、Text、图标、是否为叶子节点、是否展开等。
*/
public class JSONTreeNode {

        private String id;            //ID
         private String text;          //节点显示
         private String cls;           //图标
         private boolean leaf;         //是否叶子
         private String href;          //链接
         private String hrefTarget;    //链接指向
         private boolean expandable;   //是否展开
         private String description;   //描述信息
         
        get/set……
}
package org.leno.javabean;

import java.sql.*;
import java.util.*;
import net.sf.json.JSONArray;

public class JSONTree {
private String parentId;
   
    public String getJSONString(){      
        Connection con =null;
        Statement st = null;
        ResultSet rs = null;
        List treeNodeArray = null;
        
        String SQLString = "SELECT * FROM Categories WHERE parentId="+this.parentId+" ORDER BY categoryId";   
            
        try
        {
            con = DataSourceFactory.getDataSource().getConnection();
            st = con.createStatement();
            //查找所有拥有下级类别的类别ID
            rs = st.executeQuery("SELECT parentId FROM Categories WHERE parentId>0 Group By parentId Order By parentId");            
            
            StringBuffer parentIDBuffer =new StringBuffer();
            parentIDBuffer.append("|");         
            while(rs.next())
            {
                parentIDBuffer.append(rs.getString("parentId"));
                parentIDBuffer.append("|");
            }
            //得到所有的parentcategoryId列表
            String parentIDString = parentIDBuffer.toString();         
            
            rs = st.executeQuery(SQLString);   
            treeNodeArray = new ArrayList();
            while(rs.next())
            {
                JSONTreeNode treeNode = new JSONTreeNode();
                String categoryId = rs.getString("categoryId");
                treeNode.setId(categoryId);
                treeNode.setText(rs.getString("categoryName"));
                treeNode.setDescription(rs.getString("description"));         
//                    treeNode.setHref("rightframe.jsp?categoryId="
//                      + rs.getString("categoryId").toString());
//                    treeNode.setHrefTarget("rightFrame");               
               
                if (parentIDString.indexOf("|"+categoryId+"|")>=0) //父节点
                    {
                        treeNode.setCls("folder");
                        treeNode.setLeaf(false);
                        treeNode.setExpandable(false);
                    }
                    else //子节点
                   {
                        treeNode.setCls("file");
                        treeNode.setLeaf(true);
                        treeNode.setExpandable(false);
                    }
                    treeNodeArray.add(treeNode);
            }
            
            JSONArray JsonArray = JSONArray.fromObject(treeNodeArray); //得到JSON数组     

            return JsonArray.toString();//返回JSON数据
        }
        catch(Exception e)
        {
            System.out.println("getJSONString() of JSONTree.java throws : "+e.toString());
            return "";
        }
        finally
        {
           try {
           if(con!=null&&!con.isClosed()){
                con.close();
           }
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        }
    }   


    public String getparentId() {
        return parentId;
    }

    public void setparentId(String parentId) {
        this.parentId = parentId;
    }

}

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

create table Categories
(
    categoryId int identity(1,1) primary key,
    categoryName varchar(30),
    description varchar(100),
    parentId int
)
insert into Categories values('电脑','关于电脑',0);
insert into Categories values('电视机','关于电视机',0);
insert into Categories values('数码相机','关于数码相机',0);
insert into Categories values('冰箱','关于冰箱',0);
insert into Categories values('联想电脑','关于联想电脑',1);
insert into Categories values('创维电视机','关于创维电视机',2);
insert into Categories values('索尼相机','关于索尼相机',3);
insert into Categories values('海尔冰箱','关于海尔冰箱',4);

insert into Categories values('旭日电脑','关于旭日电脑',5);
insert into Categories values('天逸电脑','关于天逸电脑',5);
insert into Categories values('创维液晶电视','关于创维液晶电视机',6);
insert into Categories values('索尼M23相机','关于索尼M23相机',7);
insert into Categories values('海尔G1冰箱','关于海尔G1冰箱',8);

select * from Categories
SELECT parentId FROM Categories WHERE parentId>0 Group By parentId Order By parentId

可能有人对ExtJS中树的节点属性不是很了解,所以我们要啰嗦一下。ExtJS只要求返回的数据格式类似下面这样即可:
[{'text':'welcome.html','id':'welcome.html','cls':'file',myPara:'myValue'},
{'text':'welcome2.html','id':'welcome2.html','leaf':true,'cls':'file','href':'welcome2.html'}]
这些数据是存储到一个数组中的,数组中的每一项代表一个节点,每一个节点都包含以下几个主要属性:
text:定义该节点显示的名称;
id:定义该节点的页面ID,便于document.getElementById方法获取该节点;
leaf:true或者false,定义该节点是否是叶子节点;
cls:定义该节点的class(显示的样式);
href:定义点击该节点后链接的页面;
另外你还可以为节点增加自定义的属性,方法如上面的myPara:'myValue'一样。ExtJS会自动将返回的数据解析成节点并正确显示到页面上。

好啦,今天我们TreePanel的学习就到这里,下次我们会学习FormPanel。祝愿大家学有所成。


本文来自CSDN博客,转载请标明出处:
http://blog.csdn.net/lenotang/archive/2008/10/19/3102727.aspx


http://www.javaeye.com/topic/207799


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/8463/showart_1967479.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP