一、如何通过代码实现导航栏菜单树
在前面,我们介绍了easyUI 的Tree lines 的使用,知道EasyUI 前框框架要生成对应的tree结构,就需要有对应的json数据格式,这一节我们主要解释一下怎么实现这个过程。主要的过程就是将数据库中的数据转变成为对应的json格式数据。
再次之前,还得再加上如何使用EasyUI快速搭建好我们的主界面。直接看一段代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用完整页面创建布局</title>
<link rel="stylesheet" type="text/css" href="../easyui/themes/default/easyui.css">
<link rel="stylesheet" type="text/css" href="../easyui/themes/icon.css">
<link rel="stylesheet" type="text/css" href="../easyui/demo/demo.css">
<script type="text/javascript" src="../easyui/jquery.min.js"></script>
<script type="text/javascript" src="../easyui/jquery.easyui.min.js"></script>
</head>
<body class="easyui-layout">
<div data-options="region:'north',title:'North Title',split:true" style="height:100px;"></div>
<div data-options="region:'south',title:'South Title',split:true" style="height:100px;"></div>
<div data-options="region:'west',title:'West',split:true,collapsible:false" style="width:15%;"></div>
<div data-options="region:'center',title:'center title'" style="padding:5px;background:#eee;">
sjsssfsj
</div>
</body>
</html>
界面效果:
在搭建好如上面所示的界面之后,就可以在West的div中生成对应的Tree树格式。
二、生成Tree Lines所需的json数据格式
这个时候,我们就需要再次对我们数据库中的权限表再次做一个解释,里面的字段设置的意义:
(1)权限表中authId与parentId的关系
parentId中的数据对应着的是authId。例如:权限管理这条数据。parentId=1,此时的1对应的是authId的1,说明此节点的父节点是authId=1的某系统。类似的,其他的菜单。parentId对应的就是父节点authId。
(2)authPath的用法。
在easyUI中的树的规范中,提到过一个自定义属性。将authPath放在自定义属性中,主要是为了页面中完成点击事件,实行跳转的功能。
例如这下面的一段代码:当页面加载的时候,调用Ajax请求数据库,获取到数据库中authId表的数据,封装成对应的json格式的数据。然后定义点击事件,通过点击事件,用js创建选项卡的方式,传入该节点的自定义属性中的页面的路径,完成打开本地写好的页面,完成该权限对应的操作。
$("#tree").tree({
lines:true,
url:'AuthServlet?action=menu&parentId=-1',
onLoadSuccess:function(){
$("#tree").tree('expandAll');
},
onClick:function(node){
console.log(node);
if(node.id==16){
logout();
}else if(node.id==15){
openPasswordModifyDialog();
}else if(node.attributes.authPath){
openTab(node);
}
}
});
//============================================================
function openTab(node){
if($("#tabs").tabs("exists",node.text)){
$("#tabs").tabs("select",node.text);
}else{
var content="<iframe frameborder=0 scrolling='auto' style='width:100%;height:100%' src="+node.attributes.authPath+"></iframe>"
$("#tabs").tabs("add",{
title:node.text,
iconCls:node.iconCls,
closable:true,
content:content
});
}
}
});
(3)state的用法。
通过观察,我们可以发现,当state的值为open的时候,对应带easyUI中的tree中的节点,是没有子节点的。当state的值为closed的时候,说明该条数据中对应的tree对应的节点是有子节点的。具体对应到封装json数据格式的时候,我们在算法中细讲。
(4)iconCls的使用。
这个要和easyUI中自定义的图标相联系。在引入easyUI的框架的时候,前面会引入这样的一样代码。
<link rel="stylesheet" type="text/css" href="easyui/themes/icon.css">
在对应的文件夹下面的css样式表中,定义了很多默认的图标样式,要使用自定义的样式就需要在里面自己做定义。如下面的代码:
.icon-home{
background:url('usericons/home.png') no-repeat center center;
}
.icon-permission{
background:url('usericons/permission.png') no-repeat center center;
}
.icon-student{
background:url('usericons/student.png') no-repeat center center;
}
.icon-course{
background:url('usericons/course.png') no-repeat center center;
}
.icon-item{
background:url('usericons/item.png') no-repeat center center;
}
.icon-userManage{
background:url('usericons/userManage.png') no-repeat center center;
}
.icon-roleManage{
background:url('usericons/roleManage.png') no-repeat center center;
}
.icon-menuManage{
background:url('usericons/menuManage.png') no-repeat center center;
}
.icon-modifyPassword{
background:url('usericons/modifyPassword.png') no-repeat center center;
}
.icon-exit{
background:url('usericons/exit.png') no-repeat center center;
}
是不是可以通过比较发现,这里定义的类名,和authId中存放的iconCls中的数据只有关联的。
三、如何通过代码实现
1、进入调试,进入到调用生成tree的json方法:传入的参数:
int userId:----当前用户的登录的用户的id
string parentId:-----传入的父节点的id,开始调用的时候为最高一级的父节点的id。
通过userId和parentId父节点生成json对应的数据格式的方法:
public JSONArray getAuthsByUserId(int userId,String parentId) {
JSONArray jsonArray=this.getAuthByParentId(userId, parentId);
for(int i=0;i<jsonArray.size();i++){
JSONObject jsonObject=jsonArray.getJSONObject(i);
if("open".equals(jsonObject.getString("state"))){
continue;
}else{
jsonObject.put("children", getAuthsByUserId(userId,jsonObject.getString("id")));
}
}
return jsonArray;
}
2、进入到getAutsByUserId()方法之后,第一次通过调用本类中的getAuthByParentId()方法获取ParentId为-1的,并且userId为当前用户的Id的json格式数据。
public JSONArray getAuthByParentId(int userId,String parentId){
JSONArray jsonArray=new JSONArray();
String sql="SELECT t_auth.authId,t_auth.authName,t_auth.authPath,t_auth.parentId,t_auth.authDescription,t_auth.state,t_auth.iconCls " +
"FROM t_roleuser LEFT JOIN t_role ON t_roleuser.roleId=t_role.roleId " +
"LEFT JOIN t_authrole ON t_role.roleId=t_authrole.roleId " +
"LEFT JOIN t_auth ON t_authrole.authId=t_auth.authId " +
"WHERE t_auth.parentId=? AND t_authrole.roleId=?";
Connection con=null;
PreparedStatement pstmt;
ResultSet rs;
try {
con = dataSource.getCurrentConnection();
pstmt = con.prepareStatement(sql);
pstmt.setString(1, parentId);
pstmt.setInt(2, userId);
rs=pstmt.executeQuery();
while(rs.next()){
JSONObject jsonObject=new JSONObject();
jsonObject.put("id", rs.getInt("authId"));
jsonObject.put("text", rs.getString("authName"));
if(!hasChildren(userId ,rs.getString("authId"))){
jsonObject.put("state", "open");
}else{
jsonObject.put("state", rs.getString("state"));
}
jsonObject.put("iconCls", rs.getString("iconCls"));
JSONObject attributeObject=new JSONObject();
attributeObject.put("authPath", rs.getString("authPath"));
jsonObject.put("attributes", attributeObject);
jsonArray.add(jsonObject);
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
dataSource.closeConnection(con);
}
return jsonArray;
}
3、顶用方法之后,在做是否有子节点判断的时候,有一个方法hasChildren(),判断该节点是否子节点,并为这个节点设置相应的状态。下面一段代码为hasChildren()方法的代码:
//判断是否有Children属性:
private boolean hasChildren(int userId,String parentId ){
String sql="SELECT t_auth.authId,t_auth.authName,t_auth.authPath,t_auth.parentId,t_auth.authDescription,t_auth.state,t_auth.iconCls " +
"FROM t_roleuser LEFT JOIN t_role ON t_roleuser.roleId=t_role.roleId " +
"LEFT JOIN t_authrole ON t_role.roleId=t_authrole.roleId " +
"LEFT JOIN t_auth ON t_authrole.authId=t_auth.authId " +
"WHERE t_auth.parentId=? AND t_authrole.roleId=?";
Connection con=null;
PreparedStatement pstmt;
ResultSet rs;
try {
con= dataSource.getCurrentConnection();
pstmt = con.prepareStatement(sql);
pstmt.setString(1, parentId);
pstmt.setInt(2, userId);
rs=pstmt.executeQuery();
return rs.next();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
dataSource.closeConnection(con);
}
return false;
}
4、回到通过servlet进入到dao层查询tree的json方法中,在方法中第一次调用getAuthsByUserId之后的到的json数据格式为:
[
{
"id": 1,
"text": "某系统",
"state": "closed",
"iconCls": "icon-home",
"attributes": {
"authPath": ""
}
}
]
第一次调用完getAuthsByUSerId方法之后的得到的json数据格式为tree中的最顶级的菜单。然后我们回到我们查询tree的json格式的函数,里面有个一For循环,通过循环每次返回的json格式的数据,通过判断json格式中的state的值是否为“closed”来判断是否有子节点,如果有子节点,则再次调用getAuthsByUserId方法获取该节点下的子节点的对应的json格式数据。实现该功能的代码我们在粘贴一遍,这段代码是我们第一次进入到dao层的方法中的一段,我们也可以看前面的方法。
for(int i=0;i<jsonArray.size();i++){
JSONObject jsonObject=jsonArray.getJSONObject(i);
if("open".equals(jsonObject.getString("state"))){
continue;
}else{
jsonObject.put("children", getAuthsByUserId(userId,jsonObject.getString("id")));
}
}
5、通过这样递归的方法之后,我们就会发现,我们最后得到的json 数据格式的就是我们所需要的数据格式,也就是我们最开始看到的json数据格式。成功之后,我们就会得到这样的效果: