最近一直在思考,如何设计一个毕业论文呢?后台就回想起自己以前大学做的项目,每次都需要开发一个后台网站,及其繁琐,后来就慢慢有了个想法,开发一个网站固件,可以动态配置,于是就动手设计了起来...
通用权限设计
每一个网站,都有着相似的后台界面,现在比较流行的DWZ、LIGERUI、EXTJS等js框架,都提供了相似的界面。同时,后台网站的安全也是非常重要的。那么,为了开发上的方便,很有必要将一个后台网站做成一个通用的网站固件。这样子带来的好处:可以动态配置后台网站,极大简便了重复造轮子的功夫。
主要内容
界面组件
授权和验证权限
界面组件主要指的是用户可以访问的菜单与资源
授权主要指的是授予用户可以访问的url资源链接
验证权限程序验证用户的请求是否合法,如果不合法,那么程序将不允许用户进行访问
下面先抽象出几个实体
- 菜单:就是用于导航与分类的界面组件,通常情况下,一个菜单可以拥有多个子菜单与资源。比如“基础服务菜单”,包含了“权限管理”、“角色管理”、“用户管理”、“菜单管理”四个资源,也简称为四个链接
- 资源:菜单下面的叶子节点,也是一个界面组件,代表一个可以访问的url链接
- 权限:用于形成网站可访问的资源集,一个集合内的权限,形成一个包,权限之间存在着层次关系。比如“用户管理权限”,包含了“添加用户”、“删除用户”、“添加用户”、“列出全部用户”四个权限
- 用户:该网站的后台用户。
- 角色:每个用户可以拥有自己的菜单与权限,所以每次添加一个新用户,都需要配置菜单、权限信息,这样子操作比较繁琐,所以抽象出一个角色实体,用于划分用户所属的类别。角色可以拥有多个子角色、用户、菜单、权限
实体之间的ER关系
- 菜单与资源:1-N关系,也就是一个菜单可以拥有多个资源。
- 菜单与菜单自身:1-N
- 菜单与用户:N-N,也就是一个菜单可以被多名用户访问,一个用户可以访问多个菜单
- 角色与用户:N-N,也就是一个角色可以拥有多个用户,一个用户可以同时属于多个角色
- 角色与权限:N-N,也就是一个角色可以拥有多个权限,一个权限可以同时属于多个角色
- 角色与菜单:N-N,也就是一个角色可以拥有多个菜单,一个菜单可以同时属于多个角色
- 用户与权限:N-N,也就是一个用户可以拥有多个权限,一个权限可以同时被多个用户访问
- 权限与权限自身: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 = [ { text: '基础',isexpand:false, children: [ {url:"demos/base/resizable.htm",text:"改变大小"}, {url:"demos/base/drag.htm",text:"拖动"}, {url:"demos/base/drag2.htm",text:"拖动2"}, {url:"demos/base/dragresizable.htm",text:"拖动并改变大小"}, {url:"demos/base/tip.htm",text:"气泡"}, {url:"demos/base/tip2.htm",text:"气泡2"} ] } ]; </span>
通过观察,我们可以使用下面的接口来模拟
<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 [email protected] * * */ 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 [email protected] * */ 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 [email protected] * */ 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 [email protected] * */ 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>
恩,代码就补贴那么多了,需要源码的直接发我邮箱即可,有时间我直接把源码发你...哈哈,有兴趣的,可以自己完善代码。
下面我们看看工程跑起来的相关界面,目前处于开发中,所以还有几个模块在开发中,需要源码的,可以联系我