最新提示:该项目由于属于毕业设计,所以存在着比较多的不足,所以读者可以参考下设计思路即可,请不要直接沿用该设计
最近一直在思考,如何设计一个毕业论文呢?后台就回想起自己以前大学做的项目,每次都需要开发一个后台网站,及其繁琐,后来就慢慢有了个想法,开发一个网站固件,可以动态配置,于是就动手设计了起来...
经过一个多月的研究,还系统也初步完善了起来。为了让开发更加人性化,这里面加入了代码生成器模块(详情http://blog.csdn.net/u010469003/article/details/46358859),以及自动生成权限信息模块。项目github地址:https://github.com/luohong19910602/springmvc_mybatis。
项目架构设计:spring + spring mvc + mybatis + liger ui + jsp等。
为了进一步优化查询用户权限系统模块,这里面使用如下sql对查询用户权限进行优化。
优化查询用户权限sql #第一步,查询出用户角色所关联的权限 SELECT sys_privilege.id, sys_user.id, sys_user.user_name, sys_privilege.privilege_name, sys_privilege.privilege_url, sys_privilege.privilege_name, sys_privilege.privilege_parent_id FROM sys_user, sys_role, sys_privilege, sys_role_user, sys_role_privilege WHERE sys_user.id = sys_role_user.user_id AND sys_role_user.role_id = sys_role.id AND sys_role.id = sys_role_privilege.role_id AND sys_role_privilege.privilege_id = sys_privilege.id UNION #第二步,查询出用户自己的权限 #第三步,两者合并起来就是用户所具备的权限 SELECT sys_privilege.id, sys_user.id, sys_user.user_name, sys_privilege.privilege_name, sys_privilege.privilege_url, sys_privilege.privilege_name, sys_privilege.privilege_parent_id FROM sys_user,sys_privilege,sys_user_privilege where sys_user.id = sys_user_privilege.user_id AND sys_user_privilege.privilege_id=sys_privilege.id;这里面将用户权限划分为两个部分,第一部分是用户关联角色具备的权限,另外一部分是用户的个人权限,合并起来得到用户的全部权限。同理,用户的菜单也可以使用同样的优化技术。具体的数据库设计大家可以观察下面的数据库部分,发现菜单与权限模块的表设计是一样的。
CREATE TABLE `sys_menu` ( `id` varchar(50) NOT NULL, `menu_parent_id` varchar(50) DEFAULT NULL, `menu_name` varchar(50) DEFAULT NULL, `menu_desc` varchar(50) DEFAULT NULL, `menu_created_time` varchar(50) DEFAULT NULL, `menu_creator` varchar(50) DEFAULT NULL, `menu_updated_time` varchar(50) DEFAULT NULL, `menu_updator` varchar(50) DEFAULT NULL, `menu_del_flag` int(1) DEFAULT '0', `menu_pic` varchar(100) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; create table sys_resource( id varchar(50) primary key, resource_name varchar(50) not null, resource_url varchar(50), resource_desc varchar(50), resource_menu_id varchar(50), resource_created_time varchar(50), resource_creator varchar(50), resource_updated_time varchar(50), resource_updator varchar(50), resource_del_flag int(1) default 0, foreign key (resource_menu_id) references sys_menu(id) on delete cascade on update cascade ); create table sys_user( id varchar(50) primary key, user_name varchar(50), user_login_name varchar(50), user_password varchar(50), user_tel varchar(50), user_qq varchar(20), user_email varchar(50), user_blog varchar(50), user_address varchar(200), user_current_address varchar(200), user_birthday varchar(20), user_login_count int(11) default 0, user_updated_time varchar(50), user_updator varchar(50), user_created_time varchar(50), user_creator varchar(50), user_del_flag int(1) default 0, user_super_user_flag int(1) default 0 ); --用户可以访问的url create table sys_user_resource( id varchar(50) primary key, user_resource_user_id varchar(50) not null, user_resource_url varchar(200) not null, foreign key (user_resource_user_id) references sys_user(id) on delete cascade on update cascade ); --角色 create table sys_role( id varchar(50) primary key, role_name varchar(50) not null, role_parent_id varchar(50), role_updated_time varchar(50), role_updator varchar(50), role_created_time varchar(50), role_creator varchar(50), role_del_flag int(1) default 0 ); create table sys_role_menu( id varchar(50) primary key, role_id varchar(50), menu_id varchar(50), resource_id varchar(50), foreign key (role_id) references sys_role(id) on delete cascade on update cascade, foreign key (menu_id) references sys_menu(id) on delete cascade on update cascade, foreign key (resource_id) references sys_resource(id) on delete cascade on update cascade ); drop table sys_privilege; create table sys_privilege( id varchar(50) primary key, privilege_name varchar(50) not null, privilege_url varchar(200), privilege_desc varchar(200), privilege_parent_id varchar(50), privilege_created_time varchar(50), privilege_creator varchar(50), privilege_updated_time varchar(50), privilege_updator varchar(50), privilege_del_flag int(1) default 0, foreign key (privilege_parent_id) references sys_privilege(id) on delete cascade on update cascade ); create table sys_role_privilege( id varchar(50) primary key, role_id varchar(50), privilege_id varchar(50), foreign key (role_id) references sys_role(id) on delete cascade on update cascade, foreign key (privilege_id) references sys_privilege(id) on delete cascade on update cascade ); create table sys_role_user( id varchar(50) primary key, role_id varchar(50), user_id varchar(50), foreign key (role_id) references sys_role(id) on delete cascade on update cascade, foreign key (user_id) references sys_user(id) on delete cascade on update cascade ); create table sys_user_privilege( id varchar(50) primary key, user_id varchar(50), privilege_id varchar(50), foreign key (user_id) references sys_user(id) on delete cascade on update cascade, foreign key (privilege_id) references sys_privilege(id) on delete cascade on update cascade ); create table sys_user_menu( id varchar(50) primary key, user_id varchar(50), menu_id varchar(50), resource_id varchar(50), foreign key (user_id) references sys_user(id) on delete cascade on update cascade, foreign key (menu_id) references sys_menu(id) on delete cascade on update cascade, foreign key (resource_id) references sys_resource(id) on delete cascade on update cascade );
所以通常而言,超级管理员属于开发阶段的开发人员使用。每当一个新的功能加入时,就需要配置一个权限,然后将权限关联到相关菜单下面。
使用技术: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>
<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;">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>恩,代码就补贴那么多了,需要源码的直接发我邮箱即可,有时间我直接把源码发你...哈哈,有兴趣的,可以自己完善代码。
下面我们看看工程跑起来的相关界面,目前处于开发中,所以还有几个模块在开发中,需要源码的,可以联系我