`
lizhenbin2010
  • 浏览: 99771 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

ExtJS实现树菜单,支持右击增删改查。

 
阅读更多

ExtJS树菜单很是强大,参照网上很多文章和说明,自己也写了一个。

我的JS都嵌入再JSP中。

1. JSP页面的的代码如下所示:

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">    
    <title>EXtJS异步加载树菜单</title>
	<link rel="stylesheet" type="text/css" href="././upload/extjs/resources/css/ext-all.css">	
	<script type="text/javascript" src="././upload/extjs/ext-base.js"></script>
	<script type="text/javascript" src="././upload/extjs/ext-all.js"></script>
	<script type="text/javascript" src="././upload/extjs/ext-basex.js"></script>
	<script type="text/javascript" src="././upload/extjs/ext-lang-zh_CN.js"></script>
  </head>
  
  <script type="text/javascript">
  	
  	Ext.onReady(function(){
 		
  		 Ext.QuickTips.init();// 浮动信息提示 
  		 
    	 Ext.BLANK_IMAGE_URL = '././upload/extjs/resources/images/default/s.gif';// 替换图片文件地址为本地
    	 
    	 var treeLoader = new Ext.tree.TreeLoader({ 
            //dataUrl : '././upload/tree/01-04-02.txt'//这里可以不需要指定URL,在加载前事件响应里面设置 
         });
         
         // 添加一个树形面板 
    	var treepanel = new Ext.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,// 美化界面 
	        autoScroll : true, // 自动滚动 
	        enableDD : true,// 是否支持拖拽效果 
	        containerScroll : true,// 是否支持滚动条 
	        rootVisible : true, // 是否隐藏根节点,很多情况下,我们选择隐藏根节点增加美观性 
	        border : true, // 边框 
	        animate : true, // 动画效果 
	        loader : treeLoader
        }); 
        
        // 异步加载根节点 
    	var rootnode = new Ext.tree.AsyncTreeNode({ 
            id : '0', 
            text : '异步加载树根节点', 
            draggable : false,// 根节点不容许拖动 
            expanded : true 
        }); 

	    // 为tree设置根节点 
	    treepanel.setRootNode(rootnode);
	    
	     // 响应加载前事件,传递node参数 
	    treepanel.on('beforeload', function(node) { 
			treepanel.loader.dataUrl = '././menu_jsonTreeNode.action?parentId='+node.id; // 定义子节点的Loader 
	    }); 
	    
	    // 渲染树形 
	    treepanel.render();
	     
	    // 展开节点,第一个参数表示是否级联展开子节点 
	    rootnode.expand(true);
	    
	    //设置树的点击事件,对数的叶子添加事件 
    	function treeClick(node, e) { 
	        if (node.isLeaf()) { 
	        	//alert(node.id);
	            e.stopEvent(); 
	            var n = contentPanel.getComponent(node.id); 
	            if (!n) { 
	                var n = contentPanel.add({ 
                        'id' : node.id, 
                        'title' : node.text, 
                        closable : true, 
                       	autoLoad : { 
                            url : '',  //载入页面的办法
                            scripts : true 
                        } // 通过autoLoad属性载入目标页,如果要用到脚本,必须加上scripts属性
	                 }); 
	        	} 
           		contentPanel.setActiveTab(n); 
        	} 
    	} 
	    // 增加鼠标单击事件 
	    treepanel.on('click', treeClick);
	    
/*
这里主要是对菜单的右键时间操作的响应分成两部分:1、对叶子节点的操作 2、对父节点的操作
*/
//=================================鼠标右键事件开始=====================
	      
	    // 增加右键点击事件 
	    treepanel.on('contextmenu', function(node, event) {// 声明菜单类型 
	      // 添加一个节点(叶子)
		  if(node.isLeaf()) {
		  	var chlidNodeClickMenu = new Ext.menu.Menu({ 
		        items : [
		        { 
                   text : '新增文件夹', 
                   iconCls : 'leaf',
                   // 增加菜单点击事件 
                   handler : function() {                   
	                   Ext.MessageBox.prompt("请输入增加菜单名称","",function(e,text){
	                   		if(e=="ok") {
	                   			Ext.Ajax.request({
	                   				async:false, //解决后台操作与页面异步的问题
	                   				url : '././json/menudml_addTreeNode.action?parentId='+node.id
	                   							+'&nodeName='+encodeURI(encodeURI(text)),
	                   				success : function(request) {
	                   					Ext.Msg.alert("提示:","增加菜单成功!");
	                   					treepanel.root.reload();
	                   					treepanel.root.expand(true, false);                					         				
	                   				},
	                   				failure : function(request) {
	                   					Ext.Msg.alert("提示:","增加菜单失败!");
	                   				}
	                   			});
	                   		}
	                   	});
	                 }
	               }, 
	               { 
	                   text : '修改菜单',
	                   iconCls : 'leaf',
	                   handler : function() {                   
	                   		Ext.MessageBox.prompt("请输入要修改菜单新名称","",function(e,text){
		                   		if(e=="ok") {
		                   			Ext.Ajax.request({
		                   				async:false, //解决后台操作与页面异步的问题
		                   				url : '././json/menudml_updateTreeNode.action?parentId='+node.id
		                   							+'&nodeName='+encodeURI(encodeURI(text)),
		                   				success : function(request) {
		                   					Ext.Msg.alert("提示:","菜单修改成功!");
		                   					treepanel.root.reload();
		                   					treepanel.root.expand(true, false);                					         				
		                   				},
		                   				failure : function(request) {
		                   					Ext.Msg.alert("提示:","菜单修改失败!");
		                   				}
		                   			});
		                   		}
	                   	   });
	                    }	                   
	               },
	               { 
	                   text : '删除菜单',
	                   iconCls : 'leaf',
	                   handler : function() {                   
	                   	  Ext.Msg.confirm("提示:","确定删除选定的记录信息?",function(e){
	                   		if(e=="yes") {
	                   			Ext.Ajax.request({
	                   				async:false, //解决后台操作与页面异步的问题
	                   				url : '././json/menudml_deleteTreeNode.action?parentId='+node.id,
	                   				success : function(request) {
	                   					Ext.Msg.alert("提示:","菜单删除成功!");
	                   					treepanel.root.reload();
	                   					treepanel.root.expand(true, false);                					         				
	                   				},
	                   				failure : function(request) {
	                   					Ext.Msg.alert("提示:","菜单删除失败!");
	                   				}
	                   			});
	                   		}
	                   	});
	                 }	                 
	              }] 
			 });
			 chlidNodeClickMenu.showAt(event.getPoint());//menu的showAt,不要忘记
			 		     	
	       }else if(!node.isLeaf() && node.parentNode!=null){
	         //对节点操作,针对父节点
	         var rootNodeClickMenu = new Ext.menu.Menu({
	         	items : [
		        { 
                   text : '新增子菜单', 
                   iconCls : 'folder',
                   handler:function(){
                   		Ext.MessageBox.prompt("请输入增加文件夹名称","",function(e,text){
	                   		if(e=="ok") {
	                   			Ext.Ajax.request({
	                   				async:false, //解决后台操作与页面异步的问题
	                   				url : '././json/menudml_addTreeNode.action?parentId='+node.id
	                   							+'&nodeName='+encodeURI(encodeURI(text)),
	                   				success : function(request) {
	                   					Ext.Msg.alert("提示:","增加菜单成功!");
	                   					treepanel.root.reload();
	                   					treepanel.root.expand(true, false);                					         				
	                   				},
	                   				failure : function(request) {
	                   					Ext.Msg.alert("提示:","增加菜单失败!");
	                   				}
	                   			});
	                   		}
	                   	});
                   }//在此略去
                 },
                 { 
                   text : '修改文件夹', 
                   iconCls : 'folder',
                   handler:function(){
                   		Ext.MessageBox.prompt("请输入要修改菜单新名称","",function(e,text){
		                   		if(e=="ok") {
		                   			Ext.Ajax.request({
		                   				async:false, //解决后台操作与页面异步的问题
		                   				url : '././json/menudml_updateTreeNode.action?parentId='+node.id
		                   							+'&nodeName='+encodeURI(encodeURI(text)),
		                   				success : function(request) {
		                   					Ext.Msg.alert("提示:","文件夹修改成功!");
		                   					treepanel.root.reload();
		                   					treepanel.root.expand(true, false);                					         				
		                   				},
		                   				failure : function(request) {
		                   					Ext.Msg.alert("提示:","文件夹修改失败!");
		                   				}
		                   			});
		                   		}
	                   	});
                   }//在此略去
                 },
                 { 
                   text : '删除文件夹', 
                   iconCls : 'folder',
                   handler:function(){
                   		Ext.Msg.confirm("提示:","确定删除选定的记录信息?",function(e){
	                   		if(e=="yes") {
	                   			Ext.Ajax.request({
	                   				async:false, //解决后台操作与页面异步的问题
	                   				url : '././json/menudml_deleteTreeRootNode.action?parentId='+node.id,
	                   				success : function(request) {
	                   					Ext.Msg.alert("提示:","文件夹删除成功!");
	                   					treepanel.root.reload();
	                   					treepanel.root.expand(true, false);                					         				
	                   				},
	                   				failure : function(request) {
	                   					Ext.Msg.alert("提示:","文件夹删除失败!");
	                   				}
	                   			});
	                   		}
	                   	});
                   }//在此略去
                 }]
	         });
	       	 rootNodeClickMenu.showAt(event.getPoint());//menu的showAt,不要忘记
	       }         
           event.preventDefault();// 阻止浏览器默认右键菜单显示 
		}); 
		
//=================================鼠标右键事件结束=====================
		
		/* 
    	 * 设置tree的节点放置函数此函数有一个很重要的参数对象e e对象有三个重要的属性,分别为dropNode,target,point 
	     * 1.dropNode为在拖动时鼠标抓住的节点 2.target为将要放置在某处的节点 
	     * 3.point为被放置的状态,分别有append表示添加,above节点的上方,below节点的下方。 
	     * 
	     */ 
	    treepanel.on('nodedrop', function(e) {
	     //实现
          }); 
   // 右边具体功能面板区 
	   var contentPanel = new Ext.TabPanel({ 
	       region : 'center', 
	       enableTabScroll : true, 
	       activeTab : 0, 
	       items : [{ 
	           id : 'homePage', 
	           title : '首页', 
	           autoScroll : true, 
	           html : '<div style="position:absolute;color:#ff0000;top:40%;left:40%;">Tree控件和TabPanel控件结合功能演示</div>' 
	       }] 
	   }); 

	    new Ext.Viewport({ 
	    
           layout : 'border', // 使用border布局 
           defaults : { 
               activeItem : 0 
           }, 
           items : [treepanel, contentPanel] 
       }); 
		
	});  
  </script>
  <body>
  	<div id="tree-panel"></div>
  </body>
</html>

2. 构造一棵树的POJO

package ext.util.tree;
/**
 * 定义一棵树的节点信息POJO
 * @author lizhenbin
 *
 */
public class TreeNode {
	
	private String id;
	private String text; //节点名称
	private boolean leaf; //是否叶子
	private String cls; //图标
	private String href; //链接
	private String hrefTarget; //链接指向
	private boolean expandable; //是否展开
	private String description; //描述信息
	
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getText() {
		return text;
	}
	public void setText(String text) {
		this.text = text;
	}
	public boolean isLeaf() {
		return leaf;
	}
	public void setLeaf(boolean leaf) {
		this.leaf = leaf;
	}
	public String getCls() {
		return cls;
	}
	public void setCls(String cls) {
		this.cls = cls;
	}
	public String getHref() {
		return href;
	}
	public void setHref(String href) {
		this.href = href;
	}
	public String getHrefTarget() {
		return hrefTarget;
	}
	public void setHrefTarget(String hrefTarget) {
		this.hrefTarget = hrefTarget;
	}
	public boolean isExpandable() {
		return expandable;
	}
	public void setExpandable(boolean expandable) {
		this.expandable = expandable;
	}
	public String getDescription() {
		return description;
	}
	public void setDescription(String description) {
		this.description = description;
	}

}

  3. action代码,这里我不想和框架的代码粘在一起,所有采用了原始的JDBC形式连接数据库操作

package ext.util.tree;

import java.net.URLDecoder;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import net.sf.json.JSONArray;

import com.opensymphony.xwork2.ActionSupport;
/**
 * 树菜单Action
 * 这里用JDBC的形式从数据库取值,方便移植
 * @author lizhenbin
 *
 */
public class TreeNodeAction extends ActionSupport {
	
	private String jsonString; //返回到页面的Json字符串
	private String parentId; //节点的父Id
	private String nodeName; //节点的名称
	private boolean success = true;

	/**
	 * ExtJS异步树加载数据的方式
	 * @return json数据
	 * @throws Exception
	 */
	public String jsonTreeNode() throws Exception {
		
		List<TreeNode> treeNodes = new ArrayList<TreeNode>();
		
		Connection conn = null;
		Statement stmt = null;
		ResultSet rs = null;
		Class.forName("oracle.jdbc.driver.OracleDriver");
		conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:ora10g", "weibo", "weibo");
		stmt = conn.createStatement();
		/**
		 * 查询所有节点的父ID
		 */
		int rootId = 0; //规定根的ID为0
		String sql = "select PARENTID from t_tree where parentid > "+rootId+" " +
				     "group by PARENTID " +
				     "order by PARENTID";
		rs = stmt.executeQuery(sql);
		StringBuffer parentIdBuffer = new StringBuffer();
		parentIdBuffer.append("|");
		while(rs.next()) {
			parentIdBuffer.append(rs.getString("PARENTID"));
			parentIdBuffer.append("|");
		}
		//得到所有的父Id的列表
		String pidStr = parentIdBuffer.toString();
		
		/**
		 * 计算下一级菜单的节点
		 */
		sql = "select * from t_tree where PARENTID = "+Integer.valueOf(this.parentId)+" order by NODEID";
		rs = stmt.executeQuery(sql);
		while(rs.next()) {			
			TreeNode treeNode = new TreeNode();
			String nodeId = rs.getString("NODEID");
			treeNode.setId(nodeId);
			treeNode.setText(rs.getString("NODENAME"));
			treeNode.setDescription(rs.getString("DESCRIPTION"));
			
			if(pidStr.indexOf("|"+nodeId+"|") >= 0) {
				//父节点
				treeNode.setCls("folder");
				treeNode.setLeaf(false);
				treeNode.setExpandable(false);
			}else{
				//子节点
				treeNode.setCls("file");
				treeNode.setLeaf(true);
				treeNode.setExpandable(false);
			}
			treeNodes.add(treeNode);
		}
		JSONArray jsonArray = JSONArray.fromObject(treeNodes);
		this.jsonString = jsonArray.toString();
		rs.close();
		stmt.close();
		conn.close();
		
		return "success";
	}

	/**
	 * 增加一个节点菜单节点
	 * @return
	 * @throws Exception
	 */
	public String addTreeNode() throws Exception{
		
		Connection conn = null;
		Statement stmt = null;
		Class.forName("oracle.jdbc.driver.OracleDriver");
		conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:ora10g", "weibo", "weibo");
		stmt = conn.createStatement();
		
		String nodeNameStr = URLDecoder.decode(this.nodeName, "utf-8"); //解决中文乱码
		String sql = "insert into t_tree(NODEID, NODENAME, DESCRIPTION, PARENTID) " +
				     "values(TREENODE_SEQ.Nextval, '"+nodeNameStr+"', null, "+Integer.valueOf(this.parentId)+")";
		int flag = stmt.executeUpdate(sql);
		if(flag <= 0)
			this.success = false;
		stmt.close();
		conn.close();
		return "success";
	}
	
	/**
	 * 修改节点菜单的名称
	 * @return
	 * @throws Exception
	 */
	public String updateTreeNode() throws Exception{
		
		Connection conn = null;
		Statement stmt = null;
		Class.forName("oracle.jdbc.driver.OracleDriver");
		conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:ora10g", "weibo", "weibo");
		stmt = conn.createStatement();
		
		String nodeNameStr = URLDecoder.decode(this.nodeName, "utf-8"); //解决中文乱码
		String sql = "update t_tree set NODENAME = '"+nodeNameStr+"' " +
				     "where NODEID = "+Integer.valueOf(this.parentId)+"";
		int flag = stmt.executeUpdate(sql);
		if(flag <= 0)
			this.success = false;
		stmt.close();
		conn.close();
		return "success";
		
	}
	
	/**
	 * 删除节点节点的信息
	 * @return
	 * @throws Exception
	 */
	public String deleteTreeNode() throws Exception{
		
		Connection conn = null;
		Statement stmt = null;
		Class.forName("oracle.jdbc.driver.OracleDriver");
		conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:ora10g", "weibo", "weibo");
		stmt = conn.createStatement();
		
		String sql = "delete from t_tree where NODEID = "+Integer.valueOf(this.parentId)+"";
		int flag = stmt.executeUpdate(sql);
		if(flag <= 0)
			this.success = false;
		stmt.close();
		conn.close();
		return "success";		
	}	
	
	/**
	 * 删除节父点节的信息包含删除所有的子节点(考虑之中)
	 * 目前只是支持,删除单个文件夹下得所有目录
	 * @return
	 * @throws Exception
	 */
	public String deleteTreeRootNode() throws Exception{
		
		Connection conn = null;
		Statement stmt = null;
		Class.forName("oracle.jdbc.driver.OracleDriver");
		conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:ora10g", "weibo", "weibo");
		stmt = conn.createStatement();
		/**
		 * 先删除所有的叶子节点
		 */
		String sql = "delete from t_tree where PARENTID = "+Integer.valueOf(this.parentId)+"";
		int childflag = stmt.executeUpdate(sql);
		/**
		 * 删除根节点
		 */
		sql = "delete from t_tree where NODEID = "+Integer.valueOf(this.parentId)+"";
		int rootflag = stmt.executeUpdate(sql);
		if(childflag <= 0 || rootflag <= 0)
			this.success = false;
		stmt.close();
		conn.close();
		return "success";		
	}	
	
	public String getJsonString() {
		return jsonString;
	}

	public void setJsonString(String jsonString) {
		this.jsonString = jsonString;
	}

	public String getParentId() {
		return parentId;
	}

	public void setParentId(String parentId) {
		this.parentId = parentId;
	}
	
	public String getNodeName() {
		return nodeName;
	}

	public void setNodeName(String nodeName) {
		this.nodeName = nodeName;
	}
	
	public boolean isSuccess() {
		return success;
	}

	public void setSuccess(boolean success) {
		this.success = success;
	}
}

 4. struts.xml的配置信息

<package name="default" namespace="/" extends="struts-default">
		
	<!-- ExtJS生产树的部分 -->
	<action name="menu_*" method="{1}" class="ext.util.tree.TreeNodeAction">
		<result name="success">/jsonvalue.jsp</result>
	</action>
</package>
<package name="weibo_json" namespace="/json"  extends="json-default">
		
	<!-- 树菜单的增删改操作 -->
	<action name="menudml_*" method="{1}" class="ext.util.tree.TreeNodeAction">
		<result type="json"></result>
	</action>
</package>

 5. 我这里的ajax请求,我返回的json数据都通过jsonvalue.jsp获取

<%@ page language="java"  pageEncoding="utf-8"%>
<%@taglib prefix="s" uri="/struts-tags" %>
<s:property value="jsonString" escape="false"/>

 6.数据库我使用的时oracle,建表信息如下:

-- Create table
create table T_TREE
(
  NODEID      NUMBER not null,
  NODENAME    VARCHAR2(50),
  DESCRIPTION VARCHAR2(255),
  PARENTID    NUMBER
)
tablespace WEIBO_DATA
  pctfree 10
  initrans 1
  maxtrans 255
  storage
  (
    initial 1M
    next 1M
    minextents 1
    maxextents unlimited
    pctincrease 0
  );
-- Create/Recreate primary, unique and foreign key constraints 
alter table T_TREE
  add constraint PK_T_TREE primary key (NODEID)
  using index 
  tablespace WEIBO_DATA
  pctfree 10
  initrans 2
  maxtrans 255
  storage
  (
    initial 1M
    next 1M
    minextents 1
    maxextents unlimited
    pctincrease 0
  );

  -- Create sequence 
create sequence TREENODE_SEQ
minvalue 1
maxvalue 999999999999999999999999999
start with 61
increment by 1
cache 20;

 

  • 大小: 9.9 KB
分享到:
评论
3 楼 zipmoon 2012-07-30  
新人 配置好了 不报错,什么都没有显示 博主给的第一个jsp页面没名字 希望配置再说的详细一些
2 楼 lizhenbin2010 2012-03-14  
在struts2.x里面有个jar包可以把你想要的数据转换成json数据,这个你需要在你的struts.xml里面配置,也就是吧你的命名空间设置为json-default就行。看我的就是这样:
<package name="mail_lzb" namespace="/json"  extends="json-default">
   <action>...</action>
</package>
加入的转换json jar包每个版本都有自己的对应包,你查查看对应你的就行,一般来说你下载struts文档里面就有。
1 楼 ywdhz 2012-03-09  
你的树是如何接受json 数据的

相关推荐

Global site tag (gtag.js) - Google Analytics