首页 > 代码库 > 通用权限设计(springmvc + mybatis + ligerui)

通用权限设计(springmvc + mybatis + ligerui)

最近一直在思考,如何设计一个毕业论文呢?后台就回想起自己以前大学做的项目,每次都需要开发一个后台网站,及其繁琐,后来就慢慢有了个想法,开发一个网站固件,可以动态配置,于是就动手设计了起来...

通用权限设计

       每一个网站,都有着相似的后台界面,现在比较流行的DWZ、LIGERUI、EXTJS等js框架,都提供了相似的界面。同时,后台网站的安全也是非常重要的。那么,为了开发上的方便,很有必要将一个后台网站做成一个通用的网站固件。这样子带来的好处:可以动态配置后台网站,极大简便了重复造轮子的功夫。

主要内容

       界面组件
       授权和验证权限

       界面组件主要指的是用户可以访问的菜单与资源
       授权主要指的是授予用户可以访问的url资源链接
       验证权限程序验证用户的请求是否合法,如果不合法,那么程序将不允许用户进行访问

下面先抽象出几个实体

  • 菜单:就是用于导航与分类的界面组件,通常情况下,一个菜单可以拥有多个子菜单与资源。比如“基础服务菜单”,包含了“权限管理”、“角色管理”、“用户管理”、“菜单管理”四个资源,也简称为四个链接
  • 资源:菜单下面的叶子节点,也是一个界面组件,代表一个可以访问的url链接
  • 权限:用于形成网站可访问的资源集,一个集合内的权限,形成一个包,权限之间存在着层次关系。比如“用户管理权限”,包含了“添加用户”、“删除用户”、“添加用户”、“列出全部用户”四个权限
  • 用户:该网站的后台用户。
  • 角色:每个用户可以拥有自己的菜单与权限,所以每次添加一个新用户,都需要配置菜单、权限信息,这样子操作比较繁琐,所以抽象出一个角色实体,用于划分用户所属的类别。角色可以拥有多个子角色、用户、菜单、权限


实体之间的ER关系

  1. 菜单与资源:1-N关系,也就是一个菜单可以拥有多个资源。
  2. 菜单与菜单自身:1-N
  3. 菜单与用户:N-N,也就是一个菜单可以被多名用户访问,一个用户可以访问多个菜单
  4. 角色与用户:N-N,也就是一个角色可以拥有多个用户,一个用户可以同时属于多个角色
  5. 角色与权限:N-N,也就是一个角色可以拥有多个权限,一个权限可以同时属于多个角色
  6. 角色与菜单:N-N,也就是一个角色可以拥有多个菜单,一个菜单可以同时属于多个角色
  7. 用户与权限:N-N,也就是一个用户可以拥有多个权限,一个权限可以同时被多个用户访问
  8. 权限与权限自身:1-N

初始化信息



       一个网站的初始化信息,也代表最基本的信息。应该包含的内容如下:
一个“基础服务”菜单,该菜单下面拥有四个资源,分别是:菜单管理、角色管理、用户管理、权限管理。这几个资源,分别代表了网站固件的一个基本模块。

  • 菜单管理:用于管理该后台网站的菜单信息,包含了添加菜单、删除菜单、更新菜单、列出全部菜单等功能
  • 角色管理:用于管理该后台网站的角色信息,包含了添加角色、删除角色、更新角色、列出全部角色、管理角色下的用户、管理角色可以访问的权限、管理角色可以访问的菜单
  • 用户管理:用户管理该后台网站的用户信息,包含了添加用户、删除用户、更新用户、流出全部用户、配置用户的角色、权限、菜单
  • 权限管理:用户管理该后台网站的权限信息,包含了添加权限、删除权限、更新权限、列出全部权限


超级管理员

       为了让方便考虑,需要一个用户,具备访问该网站的全部功能模块,这个用户我们称之为超级管理员。在初始化一个后台网站时,超级管理员需要做的事情如下:
       1、添加权限
       2、配置网站的菜单信息
       3、添加角色
       4、添加用户
       5、配置用户的菜单、权限

       

      所以通常而言,超级管理员属于开发阶段的开发人员使用。每当一个新的功能加入时,就需要配置一个权限,然后将权限关联到相关菜单下面


使用技术:spring mvc + mybatis + ligerui + javascript + css + jsp + html + mysql


下面看看工程的包组织,这里使用了按照模块划分来组织包。

技术分享


下面是jsp的组织,同样采用按照模块来组织,并且将所有的jsp界面放在了WEB-INF下面,可以确保文件的相关安全

技术分享


在开发过程中,最主要的是抽象与设计架构,所以这里主要做了一个树组件的抽象。也就是将数据库的数据,变成js框架中的组件。于是设计了一个liger ui的树组件

下面先看看liger ui树组件的数据

<span style="font-size:18px;">var indexdata = 
http://www.mamicode.com/[>
通过观察,我们可以使用下面的接口来模拟

<span style="font-size:18px;">package net.itaem.view;
import java.util.List;  
  
/**  
 * liger ui中Tree模型  
 * 
 * 菜单模型使用树的结构进行组织
 * 一个ITreeModel有两个形态,一个是Menu,一个Leaf
 * 遍历节点的时候,会递归遍历结点
 * 
 * 这个接口主要用来描述菜单的一些相关操作
 * 每一个后台框架都应该实现该接口,然后对外体现出一致性
 * 目前框架提供了LigerUI Tree的实现
 * 
 * <br>
 * 如果用户如果感兴趣,可以提供一个DWZ, EXTJS的实现
 * 在判断一个结点是否是同一个结点,这里使用结点ID来判断
 * 为了配置菜单的显示顺序,这里需要默认指定一个排序方式
 * 
 * @see LigerUiTree
 * 
 * @date 2014-08-19 10:17 am  
 * @author 骆宏  
 * @email 846705189@qq.com  
 *   
 * */  
public interface ITreeModel {  
    /**  
     * 定义一个字符串,用来表示树模型中的菜单节点  
     * */  
    String MENU = "menu";  
      
    /**  
     * 定义一个字符串,用来表示数模型中的叶子节点  
     * */  
    String LEAF = "leaf";  
    
    /**  
     * 返回当前菜单的结构层次  
     * @return 返回菜单的结构层次  
     *         如果是叶子节点,那么返回0  
     * */  
    public int level();  
      
    /**  
     * 返回当前节点的所有子节点,子节点包括了子菜单以及叶子节点  
     * @return 返回当前菜单的全部子节点  
     * */  
    public List<ITreeModel> children();  
      
    /**  
     * 返回当前节点的父节点  
     * @return 返回当前节点的父亲节点,如果没有父亲节点,返回null  
     * */  
    public ITreeModel parent();  
      
    /**  
     * 返回当前节点的节点类型,这里的节点类型一共有两种,一种是菜单,另外一种是叶子节点  
     * @return 节点类型  
     * @see ITreeModel#MENU  
     * @see ITreeModel#LEAF  
     * */  
    public String nodeType();  
      
    /**  
     * 返回当前节点的url  
     * @return 当前节点的url  
     * */  
    public String url();  
      
    /**  
     * 放回当前节点的id  
     * @return 当前节点id  
     * */  
    public String id();  
      
    /**  
     * 返回节点的名字  
     * @return 节点名字  
     * */  
    public String name();  
      
    /**  
     * 当前节点如果是菜单,那么该菜单默认是否展开呢?如果是返回true,代表展开;否则,代表不展开  
     * @return 返回菜单节点的展开状态  
     * */  
    public boolean isexpand();  
      
    /**  
     * 设置菜单名字  
     * @param name  
     * */  
    public void setName(String name);  
      
    /**  
     * 设置菜单url  
     * @param url  
     * */  
    public void setUrl(String url);  
      
    /**  
     * 设置菜单展开状态  
     * @param isexpend  
     * */  
    public void setIsexpand(boolean isexpand);  
      
    /**  
     * 设置父节点  
     * @param parent  
     * */  
    public void setParent(ITreeModel parent);  
      
    /**  
     * 设置孩子节点  
     * @param children  
     * */  
    public void setChildren(List<ITreeModel> children);  
      
    /**  
     * 设置节点id  
     * @param id  
     * */  
    public void setId(String id);  
      
    /**  
     * 返回该节点的json数据,包含该节点下面的所有子节点  
     * @return 返回当前节点的json数据  
     * */  
    public String toTreeJson();  
      
    /**  
     * 返回从根节点到当前节点的所有节点集合,包含当前节点
     * 该集合的第一个元素为最大根节点,第二个元素为第二个根结点,依次类推 
     * @return 返回根节点到当前节点的集合  
     * */  
    public List<ITreeModel> route();  
    
    /**  
     * 返回当前结点下面的第position结点
     * @return 返回以当前节点子根节点的子树  
     * */  
    public ITreeModel subTree(int position);  
    
    /**
     * 添加子节点,添加到结点的结尾处
     * @param subTree 要添加的子节点
     * */
    public boolean addSubTree(ITreeModel subTree);
    
    /**
     * 在指定position添加结点
     * @param position 下标
     * @param subTree 要添加的子节点
     * */
    public void addSubTree(int position, ITreeModel subTree);
    
    /**
     * 删除子节点
     * @param subTree 要删除的结点
     * */
    public boolean deleteSubTree(ITreeModel subTree);
    
    /**
     * 删除子节点
     * @param position 要删除的结点在子节点中的位置
     * */
    public boolean deleteSubTree(int position);
    
    /**
     * 判断类型
     * @return
     * */
    public boolean isMenu();
    
    /**
     * 判断类型
     * @return
     * */
    public boolean isLeaf();
    
    /**
     * 返回Menu的图片
     * @return
     * */
    public String pic();
    
    /**
     * 返回图标的图片
     * @param pic 图片url地址
     * */
    public void setPic(String pic);
}  </span>

下面提供liger ui组件的实现

<span style="font-size:18px;">package net.itaem.view.ligerui;
import java.util.ArrayList;
import java.util.List;

import net.itaem.view.ITreeModel;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

/**  
 * 这里是liger ui树的插件实现类
 * 
 * 在每次add、delete之后,都需要计算树的level
 * 
 * 
 * 
 * @author 骆宏  
 * @date 2014-08-19 19:39 am  
 * @email 846705189@qq.com  
 * */  
public class LigerUiTree implements ITreeModel{  

	//定义一个level,用来保存树的层次  
	private int level = 1;
	//定义一个url,用来保存当前节点的url  
	private String url;  
	//定义一个id,用来保存当前节点id  
	private String id;  
	//定义一个isexpend,用来保存节点展开状态  
	private boolean isexpand;  
	//定义一个name,用来保存节点的名称  
	private String name;  
	//定义一个parent,用来保存节点的父亲节点  
	private ITreeModel parent;  
	//定义一个children,用来保存当前节点的所有子节点  
	private List<ITreeModel> children = new ArrayList<ITreeModel>();
	//定义一个nodeType,用来保存结点类型
	private String nodeType = LEAF;
	//定义一个pic,用来保存图片的url地址
	private String pic;
	//用来缓存树的json数据
	private String jsonCache;
	//用来保存用户的操作状态,如果树已经构建好,并且没有删除、添加,那么继续使用jsonCache
	private boolean hasChange;

	public LigerUiTree(){  

	}  

	/**  
	 * 定义一个基本的构造方法,该构造方法的参数都不能为空  
	 * @param id 节点id  
	 * @param name 节点name  
	 * @param url 节点url  
	 * */  
	public LigerUiTree(String id, String name, String url){  
		if(id == null || name == null || url == null) throw new RuntimeException("id name url都不能为空");  

		this.id = id;  
		this.name = name;  
		this.url = url; 

		hasChange = true;
	}  

	public LigerUiTree(String id, String name, String url, ITreeModel parent) {  
		this(id, name, url);  
		this.parent = parent;
		hasChange = true;
	}  

	public LigerUiTree(String id, String name, String url, List<ITreeModel> children) {  
		this(id, name, url);  
		this.children = children;  
		hasChange = true;
	}  

	@Override  
	public void setUrl(String url) {  
		this.url = url;  
		hasChange = true;
	}  

	@Override  
	public void setId(String id) {  
		this.id = id;  
		hasChange = true;
	}  

	@Override  
	public void setIsexpand(boolean isexpand) {  
		this.isexpand = isexpand;  
		hasChange = true;
	}  

	@Override  
	public void setName(String name) {  
		this.name = name;  
		hasChange = true;
	}  

	@Override
	public void setParent(ITreeModel parent) {  
		this.parent = parent;  
		hasChange = true;
	}  

	/**  
	 * 这里同时会计算树的层次  
	 * 并且这里会同时维护parant - children之间的关联管理,也就是在设置当前节点的子节点时,同时会指点这些子节点的父亲节点为当前节点  
	 * */  
	@Override
	public void setChildren(List<ITreeModel> children) {  
		if(children == null || children.size() == 0) return;   //如果为null,do nothing

		this.children = children;  //设置当前结点的子节点为children
		int max = 0;
		ITreeModel child = null;
		for(int i=0; i < children.size(); i++){
			child = children.get(i);
			child.setParent(this);
			if(max < child.level()) max = child.level();
		}

		level += max;
		nodeType = MENU;	
		hasChange = true;
	}  

	@Override  
	public int level() {  
		//每次要计算树的高度,都必须遍历整棵树的,然后确定树的高度,由于树随时可以被改变,所以这里不适合使用缓存模式  
		return level;   
	}  

	@Override  
	public List<ITreeModel> children() {  
		return children;  
	}  

	@Override  
	public ITreeModel parent() {  
		return parent;  
	}  

	@Override  
	public String nodeType() {  
		return nodeType;
	}  

	@Override  
	public String url() {  
		return url;  
	}  

	@Override  
	public String id() {  
		return id;  
	}  

	@Override  
	public boolean isexpand() {  
		return isexpand;  
	}  

	@Override  
	public String name(){  
		return name;  
	}  

	@Override  
	public String toTreeJson() {  
		if(hasChange && jsonCache == null){  
			JSONObject json = new JSONObject();
			//生成这个节点的基本数据  
			json.put("text", name);  
			json.put("isexpand", isexpand);  
            json.put("id", id);
            json.put("icon", pic);
            
			if(url != null && !"".equals(url))
				json.put("url", url);  

			if(parent != null){  
				json.put("pid", parent.id());     
			}

			//生成这个节点的子菜单数据  
			JSONArray childrenJson = new JSONArray();  
			if(children != null && children.size() != 0){  
				for(ITreeModel child: children){  
					//让每个子menu递归的去生成json数据  
					childrenJson.add(toJson(child));  
				}  
				json.put("children", childrenJson);  
			}
			jsonCache = json.toString();
			return jsonCache;
		}else
			return jsonCache;
	}  

	/**  
	 * 递归入口  
	 * @see MenuVo#toJson()  
	 * */  
	private String toJson(ITreeModel tree){  
		JSONObject json = new JSONObject();  
		if(tree.children() != null && tree.children().size() != 0){  
			//生成这个菜单节点的基本数据  
			json.put("text", tree.name());  
			json.put("id", tree.id());
			json.put("icon", tree.pic());
			
			if(tree.parent() != null){  
				json.put("pid", tree.parent().id());  
			}  

			json.put("isexpand", tree.isexpand());  

			//生成这个菜单节点的子菜单数据  
			JSONArray childrenJson = new JSONArray();  
			if(tree.children() != null){  
				for(ITreeModel child: tree.children()){  
					//让每个子menu递归的去生成json数据  
					childrenJson.add(toJson(child));  
				}  
				json.put("children", childrenJson);  
			}  
		}else{   //这个节点不是菜单,是菜单下面的一个具体子节点,该节点已经没有子节点了  
			json.put("id", tree.id());  
			if(tree.parent() != null){  
				json.put("pid", tree.parent().id());  
			}  
			json.put("text", tree.name());  
			json.put("url", tree.url());  
			json.put("icon", tree.pic());
		}  
		return json.toString();  
	}  

	@Override  
	public List<ITreeModel> route() {  
		List<ITreeModel> route = new ArrayList<ITreeModel>();  
		ITreeModel current = this;  
		while(current != null){  
			route.add(current);  
			current = current.parent();  
		}  
		java.util.Collections.reverse(route);  
		return route;  
	}  

	@Override  
	public ITreeModel subTree(int position) {  
		if(position < 0) throw new RuntimeException("position 小于0");
		if(children != null && children.size() > 0 && position <= children.size()-1){
			return children.get(position);
		}
		return null;
	}  

	/**
	 * 生成hashCode
	 * */
	@Override  
	public int hashCode() {  
		return id.hashCode() * 37 + 5; 
	}  

	/**
	 * 比较两个菜单是否相等
	 * */
	@Override  
	public boolean equals(Object obj) {  
		return id.equals(((ITreeModel)obj).id()); 
	}  

	/**  
	 * 返回节点的基本信息  
	 * @return  
	 * */  
	@Override  
	public String toString() {  
		return "LigerUiTree [" + "id=" + id + ", name=" + name 
				+ ", level=" + level + ", url=" + url  
				+ ", nodeType=" + nodeType() + ", isexpand=" + isexpand + ",  pic=" + pic + "]";  
	}


	@Override
	public boolean addSubTree(ITreeModel subTree) {
		if(subTree == null) return false;
		nodeType = MENU;
		subTree.setParent(this);

		boolean addedFlag = children.add(subTree);

		calculateLevel0(subTree);

		hasChange = true;
		return addedFlag;
	}

	@Override
	public void addSubTree(int position, ITreeModel subTree) {
		if(position < 0 || position >= children.size()) return;
		children.add(position, subTree);

		calculateLevel0(subTree);
		if(children.size() > 0)
			nodeType = MENU;

		hasChange = true;
	}  

	/**
	 * 增加一个结点,计算level,分为四种情况
	 * */
	private void calculateLevel0(ITreeModel subTree){
		if(this.isLeaf() && subTree.isLeaf()){
			level = 2;
		}else if(this.isMenu() && subTree.isMenu()){
			level += subTree.level();
		}else if(this.isLeaf() && subTree.isMenu()){
			level = subTree.level() + 1;
		}else{
			//is menu, so add a new leaf, the level not change	
		}
	}


	@Override
	public boolean deleteSubTree(ITreeModel subTree) {
		boolean deletedFlag = false;
		for(int i=0; i<children.size(); i++){
			if(children.get(i).equals(subTree)){
				children.remove(i);
				break;
			}
		}

		if(children.size() > 0)
			nodeType = MENU;


		calculateLevel();

		hasChange = true;
		return deletedFlag;
	}

	@Override
	public boolean deleteSubTree(int position) {
		if(position < 0 || children == null || children.size() == 0 || position >= children.size()) return false;
		ITreeModel tree = children.remove(position);
		if(children.size() > 0) 
			nodeType = MENU;

		calculateLevel();

		hasChange = true;
		if(tree == null) return false;
		else return true;
	}


	/**
	 * 计算输的层次,每次删除一个结点,需要遍历当前所有子节点,看看当前的子节点中,最大的level,然后将这个值+1即可
	 * */
	private void calculateLevel(){
		//设置level,遍历所有的children树,然后取最大值  
		int max = -1;  

		for(int i=0; i<children.size(); i++){  
			children.get(i).setParent(this);   //维护parent-children的相互关联关系  
			if(children.get(i).level() > max) max = children.get(i).level();  
		}  

		//如果添加的节点都是叶子节点,那么当前层次为2  
		//否则计算最大的树层次 = 子节点最大的层次 + 1  
		if(max != -1){  
			level = max + 1;  
		}else{  
			level = 2;     
		}
	}


	@Override
	public boolean isMenu() {
		return nodeType.equals(ITreeModel.MENU);
	}

	@Override
	public boolean isLeaf() {
		return nodeType.equals(ITreeModel.LEAF);
	}

	@Override
	public String pic() {
		return pic;
	}

	@Override
	public void setPic(String pic) {
		this.pic = pic;
	}
}</span>

有了模型,下面只需要定义几个工具类,实现数据库对象与liger ui数据json转换即可

<span style="font-size:18px;">package net.itaem.view;

import net.itaem.menu.entity.Menu;
import net.itaem.privilege.entity.Privilege;
import net.itaem.role.entity.Role;

/**
 * 实现菜单、角色、权限-->Liger Ui Tree转换
 * 
 * @author luohong
 * @date 2014-12-24
 * @email 846705189@qq.com
 * */
public interface IToTree {

	/**
	 * 将Menu变成一个Tree
	 * @param menu
	 * @return
	 * */
	public ITreeModel menuToTree(Menu menu);
	
	/**
	 * 将Role变成一个Tree
	 * @param role
	 * @return
	 * */
	public ITreeModel roleToTree(Role role);
	
	/**
	 * 将一个Privilege变成一个Tree
	 * @param privilege
	 * @return
	 * */
	public ITreeModel privilegeToTree(Privilege privilege);
}
</span>
<span style="font-size:18px;"></span>
<pre name="code" class="html"><span style="font-size:18px;">package net.itaem.view.ligerui;
import java.util.ArrayList;
import java.util.List;

import net.itaem.view.ITreeModel;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

/**  
 * 这里是liger ui树的插件实现类
 * 
 * 在每次add、delete之后,都需要计算树的level
 * 
 * 
 * 
 * @author 骆宏  
 * @date 2014-08-19 19:39 am  
 * @email 846705189@qq.com  
 * */  
public class LigerUiTree implements ITreeModel{  

	//定义一个level,用来保存树的层次  
	private int level = 1;
	//定义一个url,用来保存当前节点的url  
	private String url;  
	//定义一个id,用来保存当前节点id  
	private String id;  
	//定义一个isexpend,用来保存节点展开状态  
	private boolean isexpand;  
	//定义一个name,用来保存节点的名称  
	private String name;  
	//定义一个parent,用来保存节点的父亲节点  
	private ITreeModel parent;  
	//定义一个children,用来保存当前节点的所有子节点  
	private List<ITreeModel> children = new ArrayList<ITreeModel>();
	//定义一个nodeType,用来保存结点类型
	private String nodeType = LEAF;
	//定义一个pic,用来保存图片的url地址
	private String pic;
	//用来缓存树的json数据
	private String jsonCache;
	//用来保存用户的操作状态,如果树已经构建好,并且没有删除、添加,那么继续使用jsonCache
	private boolean hasChange;

	public LigerUiTree(){  

	}  

	/**  
	 * 定义一个基本的构造方法,该构造方法的参数都不能为空  
	 * @param id 节点id  
	 * @param name 节点name  
	 * @param url 节点url  
	 * */  
	public LigerUiTree(String id, String name, String url){  
		if(id == null || name == null || url == null) throw new RuntimeException("id name url都不能为空");  

		this.id = id;  
		this.name = name;  
		this.url = url; 

		hasChange = true;
	}  

	public LigerUiTree(String id, String name, String url, ITreeModel parent) {  
		this(id, name, url);  
		this.parent = parent;
		hasChange = true;
	}  

	public LigerUiTree(String id, String name, String url, List<ITreeModel> children) {  
		this(id, name, url);  
		this.children = children;  
		hasChange = true;
	}  

	@Override  
	public void setUrl(String url) {  
		this.url = url;  
		hasChange = true;
	}  

	@Override  
	public void setId(String id) {  
		this.id = id;  
		hasChange = true;
	}  

	@Override  
	public void setIsexpand(boolean isexpand) {  
		this.isexpand = isexpand;  
		hasChange = true;
	}  

	@Override  
	public void setName(String name) {  
		this.name = name;  
		hasChange = true;
	}  

	@Override
	public void setParent(ITreeModel parent) {  
		this.parent = parent;  
		hasChange = true;
	}  

	/**  
	 * 这里同时会计算树的层次  
	 * 并且这里会同时维护parant - children之间的关联管理,也就是在设置当前节点的子节点时,同时会指点这些子节点的父亲节点为当前节点  
	 * */  
	@Override
	public void setChildren(List<ITreeModel> children) {  
		if(children == null || children.size() == 0) return;   //如果为null,do nothing

		this.children = children;  //设置当前结点的子节点为children
		int max = 0;
		ITreeModel child = null;
		for(int i=0; i < children.size(); i++){
			child = children.get(i);
			child.setParent(this);
			if(max < child.level()) max = child.level();
		}

		level += max;
		nodeType = MENU;	
		hasChange = true;
	}  

	@Override  
	public int level() {  
		//每次要计算树的高度,都必须遍历整棵树的,然后确定树的高度,由于树随时可以被改变,所以这里不适合使用缓存模式  
		return level;   
	}  

	@Override  
	public List<ITreeModel> children() {  
		return children;  
	}  

	@Override  
	public ITreeModel parent() {  
		return parent;  
	}  

	@Override  
	public String nodeType() {  
		return nodeType;
	}  

	@Override  
	public String url() {  
		return url;  
	}  

	@Override  
	public String id() {  
		return id;  
	}  

	@Override  
	public boolean isexpand() {  
		return isexpand;  
	}  

	@Override  
	public String name(){  
		return name;  
	}  

	@Override  
	public String toTreeJson() {  
		if(hasChange && jsonCache == null){  
			JSONObject json = new JSONObject();
			//生成这个节点的基本数据  
			json.put("text", name);  
			json.put("isexpand", isexpand);  
            json.put("id", id);
            json.put("icon", pic);
            
			if(url != null && !"".equals(url))
				json.put("url", url);  

			if(parent != null){  
				json.put("pid", parent.id());     
			}

			//生成这个节点的子菜单数据  
			JSONArray childrenJson = new JSONArray();  
			if(children != null && children.size() != 0){  
				for(ITreeModel child: children){  
					//让每个子menu递归的去生成json数据  
					childrenJson.add(toJson(child));  
				}  
				json.put("children", childrenJson);  
			}
			jsonCache = json.toString();
			return jsonCache;
		}else
			return jsonCache;
	}  

	/**  
	 * 递归入口  
	 * @see MenuVo#toJson()  
	 * */  
	private String toJson(ITreeModel tree){  
		JSONObject json = new JSONObject();  
		if(tree.children() != null && tree.children().size() != 0){  
			//生成这个菜单节点的基本数据  
			json.put("text", tree.name());  
			json.put("id", tree.id());
			json.put("icon", tree.pic());
			
			if(tree.parent() != null){  
				json.put("pid", tree.parent().id());  
			}  

			json.put("isexpand", tree.isexpand());  

			//生成这个菜单节点的子菜单数据  
			JSONArray childrenJson = new JSONArray();  
			if(tree.children() != null){  
				for(ITreeModel child: tree.children()){  
					//让每个子menu递归的去生成json数据  
					childrenJson.add(toJson(child));  
				}  
				json.put("children", childrenJson);  
			}  
		}else{   //这个节点不是菜单,是菜单下面的一个具体子节点,该节点已经没有子节点了  
			json.put("id", tree.id());  
			if(tree.parent() != null){  
				json.put("pid", tree.parent().id());  
			}  
			json.put("text", tree.name());  
			json.put("url", tree.url());  
			json.put("icon", tree.pic());
		}  
		return json.toString();  
	}  

	@Override  
	public List<ITreeModel> route() {  
		List<ITreeModel> route = new ArrayList<ITreeModel>();  
		ITreeModel current = this;  
		while(current != null){  
			route.add(current);  
			current = current.parent();  
		}  
		java.util.Collections.reverse(route);  
		return route;  
	}  

	@Override  
	public ITreeModel subTree(int position) {  
		if(position < 0) throw new RuntimeException("position 小于0");
		if(children != null && children.size() > 0 && position <= children.size()-1){
			return children.get(position);
		}
		return null;
	}  

	/**
	 * 生成hashCode
	 * */
	@Override  
	public int hashCode() {  
		return id.hashCode() * 37 + 5; 
	}  

	/**
	 * 比较两个菜单是否相等
	 * */
	@Override  
	public boolean equals(Object obj) {  
		return id.equals(((ITreeModel)obj).id()); 
	}  

	/**  
	 * 返回节点的基本信息  
	 * @return  
	 * */  
	@Override  
	public String toString() {  
		return "LigerUiTree [" + "id=" + id + ", name=" + name 
				+ ", level=" + level + ", url=" + url  
				+ ", nodeType=" + nodeType() + ", isexpand=" + isexpand + ",  pic=" + pic + "]";  
	}


	@Override
	public boolean addSubTree(ITreeModel subTree) {
		if(subTree == null) return false;
		nodeType = MENU;
		subTree.setParent(this);

		boolean addedFlag = children.add(subTree);

		calculateLevel0(subTree);

		hasChange = true;
		return addedFlag;
	}

	@Override
	public void addSubTree(int position, ITreeModel subTree) {
		if(position < 0 || position >= children.size()) return;
		children.add(position, subTree);

		calculateLevel0(subTree);
		if(children.size() > 0)
			nodeType = MENU;

		hasChange = true;
	}  

	/**
	 * 增加一个结点,计算level,分为四种情况
	 * */
	private void calculateLevel0(ITreeModel subTree){
		if(this.isLeaf() && subTree.isLeaf()){
			level = 2;
		}else if(this.isMenu() && subTree.isMenu()){
			level += subTree.level();
		}else if(this.isLeaf() && subTree.isMenu()){
			level = subTree.level() + 1;
		}else{
			//is menu, so add a new leaf, the level not change	
		}
	}


	@Override
	public boolean deleteSubTree(ITreeModel subTree) {
		boolean deletedFlag = false;
		for(int i=0; i<children.size(); i++){
			if(children.get(i).equals(subTree)){
				children.remove(i);
				break;
			}
		}

		if(children.size() > 0)
			nodeType = MENU;


		calculateLevel();

		hasChange = true;
		return deletedFlag;
	}

	@Override
	public boolean deleteSubTree(int position) {
		if(position < 0 || children == null || children.size() == 0 || position >= children.size()) return false;
		ITreeModel tree = children.remove(position);
		if(children.size() > 0) 
			nodeType = MENU;

		calculateLevel();

		hasChange = true;
		if(tree == null) return false;
		else return true;
	}


	/**
	 * 计算输的层次,每次删除一个结点,需要遍历当前所有子节点,看看当前的子节点中,最大的level,然后将这个值+1即可
	 * */
	private void calculateLevel(){
		//设置level,遍历所有的children树,然后取最大值  
		int max = -1;  

		for(int i=0; i<children.size(); i++){  
			children.get(i).setParent(this);   //维护parent-children的相互关联关系  
			if(children.get(i).level() > max) max = children.get(i).level();  
		}  

		//如果添加的节点都是叶子节点,那么当前层次为2  
		//否则计算最大的树层次 = 子节点最大的层次 + 1  
		if(max != -1){  
			level = max + 1;  
		}else{  
			level = 2;     
		}
	}


	@Override
	public boolean isMenu() {
		return nodeType.equals(ITreeModel.MENU);
	}

	@Override
	public boolean isLeaf() {
		return nodeType.equals(ITreeModel.LEAF);
	}

	@Override
	public String pic() {
		return pic;
	}

	@Override
	public void setPic(String pic) {
		this.pic = pic;
	}
}</span>


<span style="font-size:18px;"></span>
恩,代码就补贴那么多了,需要源码的直接发我邮箱即可,有时间我直接把源码发你...哈哈,有兴趣的,可以自己完善代码。


下面我们看看工程跑起来的相关界面,目前处于开发中,所以还有几个模块在开发中,需要源码的,可以联系我

技术分享

技术分享技术分享

通用权限设计(springmvc + mybatis + ligerui)