前言
Tree组件的话来做权限方面的很好,基于树组件的话分为两种,静态树和动态树,静态树的话代码比较冗余死板,动态树配合ajax+json实现从数据库调用显示方便灵活。
动态树
使用树控件先定义一个空ul元素中并使用Javascript加载数据。
<ul id="tt"></ul>
- 创建js文件进行json数据加载
$('#tt').tree({
url:'tree_data.json'
});
- 使用树组件就要遵循别人提供的规则。
- 因为是基于动态树案例要进行数据库访问所以配合自定义mvc框架我们必须导入依赖架包。
思路和流程
首先我们的菜单栏是存储在数据库中的,我们要配合mvc框架+ajax实现数据渲染,关键点在于menuid和parentid字段,这里就涉及到了权限的方面,在java代码中可以根据menuid来逻辑进行权限的控制,比如有的人可以看到所有菜单,有的只能看到部分菜单具体请往下看。
简单的权限关系理解,这里简单的画一下。
前端步骤:
(1)前端导入软件包。
(2)引入css样式
(3)引入图标样式
(4)引入jquery,因为easyui的核心库是基于jQuery的
(5)引入easyui的核心js库
(6)自定义js文件并引入,主要提供访问路径执行数据访问层进行数据渲染
后端流程概述:
代码实现案例
- index界面文件引入的顺序,以及隐藏域的传值需要注意。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<script type="text/javascript"
src="${pageContext.request.contextPath }/static/js/index.js"></script>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>主界面</title>
<!--全局样式 -->
<link rel="stylesheet" type="text/css"
href="${pageContext.request.contextPath }/static/js/jquery-easyui-1.5.1/themes/default/easyui.css">
<!--定义图标 -->
<link rel="stylesheet" type="text/css"
href="${pageContext.request.contextPath }/static/js/jquery-easyui-1.5.1/themes/default/easyui.css">
<script type="text/javascript"
src="${pageContext.request.contextPath }/static/js/jquery-easyui-1.5.1/jquery.min.js"></script>
<!--组件库源码的js文件 -->
<script type="text/javascript"
src="${pageContext.request.contextPath }/static/js/jquery-easyui-1.5.1/jquery.easyui.min.js"></script>
<!-- 自定义 js-->
<script type="text/javascript"
src="${pageContext.request.contextPath }/static/js/index.js"></script>
</head>
<body class="easyui-layout">
<input type="hidden" id="ctx"value="${pageContext.request.contextPath }">
<!-- 头部 -->
<div data-options="region:'north',border:false"
style="height: 60px; background: #B3DFDA; padding: 10px">管理系统</div>
<!-- 菜单管理 -->
<div data-options="region:'west',split:true,title:'West'"
style="width: 150px; padding: 10px;">
<ul id="tt"></ul>
</div>
<!-- 内容块 -->
<div
data-options="region:'east',split:true,collapsed:true,title:'East'"
style="width: 100px; padding: 10px;">east region</div>
<!-- 底部权限 -->
<div data-options="region:'south',border:false"
style="height: 50px; background: #A9FACD; padding: 10px;">south
region</div>
<div data-options="region:'center',title:'Center'"></div>
</body>
</html>
- 自定义的js文件提供jquery加载的资源路径。
$(function(){
$('#tt').tree({
//这是请求路径
url:$("#ctx" ).val()+'/menu.action?methodName=menuNodes'
});
})
- TreeVo树格式的实体类提供json数据格式的转载。
package com.liyingdong.vo;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class TreeVo<T> {
/**
* 节点ID
*/
private String id;
/**
* 显示节点文本
*/
private String text;
/**
* 节点状态,open closed
*/
private Map<String, Object> state;
/**
* 节点是否被选中 true false
*/
private boolean checked = false;
/**
* 节点属性
*/
private Map<String, Object> attributes;
/**
* 节点的子节点
*/
private List<TreeVo<T>> children = new ArrayList<TreeVo<T>>();
/**
* 父ID
*/
private String parentId;
/**
* 是否有父节点
*/
private boolean hasParent = false;
/**
* 是否有子节点
*/
private boolean hasChildren = false;
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 Map<String, Object> getState() {
return state;
}
public void setState(Map<String, Object> state) {
this.state = state;
}
public boolean isChecked() {
return checked;
}
public void setChecked(boolean checked) {
this.checked = checked;
}
public Map<String, Object> getAttributes() {
return attributes;
}
public void setAttributes(Map<String, Object> attributes) {
this.attributes = attributes;
}
public List<TreeVo<T>> getChildren() {
return children;
}
public void setChildren(List<TreeVo<T>> children) {
this.children = children;
}
public boolean isHasParent() {
return hasParent;
}
public void setHasParent(boolean isParent) {
this.hasParent = isParent;
}
public boolean isHasChildren() {
return hasChildren;
}
public void setChildren(boolean isChildren) {
this.hasChildren = isChildren;
}
public String getParentId() {
return parentId;
}
public void setParentId(String parentId) {
this.parentId = parentId;
}
public TreeVo(String id, String text, Map<String, Object> state, boolean checked, Map<String, Object> attributes,
List<TreeVo<T>> children, boolean isParent, boolean isChildren, String parentID) {
super();
this.id = id;
this.text = text;
this.state = state;
this.checked = checked;
this.attributes = attributes;
this.children = children;
this.hasParent = isParent;
this.hasChildren = isChildren;
this.parentId = parentID;
}
public TreeVo() {
super();
}
}
数据访问层MenuDao
思路:
- Menu的格式是不满足easyui的tree组件的展示的数据格式的
- 实现,将将List《Menu》得到的单个Menu转成treevo,将treevo加入到nodes
package com.liyingdong.dao;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import com.liyingdong.entity.Menu;
import com.liyingdong.util.BaseDao;
import com.liyingdong.util.BuildTree;
import com.liyingdong.util.PageBean;
import com.liyingdong.vo.TreeVo;
public class MenuDao extends BaseDao<Menu>{
public List<Menu> list(Menu menu,PageBean pageBean) throws InstantiationException, IllegalAccessException, SQLException{
String sql="select * from t_easyui_menu ";
return executeQuery(sql,Menu.class, pageBean);
}
public List<TreeVo<Menu>> menuNode(Menu menu,PageBean pageBean) throws InstantiationException, IllegalAccessException, SQLException{
//1获取到数据库的数据
List<Menu> list = this.list(menu, pageBean);
//通过工具类完成指定格式的输出
//2 定义容器
List<TreeVo<Menu>> nodes=new ArrayList<TreeVo<Menu>>();
//声明对象在循环外面节约jvm的资源
TreeVo treevo=null;
for (Menu p : list) {
//实例化treevo对象
treevo=new TreeVo<>();
//设置id
treevo.setId(p.getMenuid());
// text相当于permission中的name
treevo.setText(p.getMenuname());
// 设置父id
treevo.setParentId(p.getParentid());
// 加入到集合
nodes.add(treevo);
}
//将集合转为条件符合的父子结构返回出去
return BuildTree.buildList(nodes,"-1");
}
}
BuildTree类
这里buildList方法提供了父子分层的一个原理逻辑,带有两个参数,(需要关系分层的集合,指定id),灵活的实现了指定需要显示的父层。
实现原理:双重for循环,外层取父id,内层实现遍历id比对,符合条件则将当前对象加入到父id的子项中,时间复杂度呈线性关系,取决于集合的未知因素的多少来定,所以是O(n)的时间复杂度。
package com.liyingdong.util;
import com.liyingdong.vo.TreeVo;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class BuildTree {
/**
* 默认-1为顶级节点
* @param nodes
* @param <T>
* @return
*/
public static <T> TreeVo<T> build(List<TreeVo<T>> nodes) {
if (nodes == null) {
return null;
}
//存放顶级节点(二级节点)容器
List<TreeVo<T>> topNodes = new ArrayList<TreeVo<T>>();
for (TreeVo<T> children : nodes) {
String pid = children.getParentId();
if (pid == null || "0".equals(pid)) {
topNodes.add(children);
//如果是顶级节点,那么结束当前循环,继续下一次的循环
continue;
}
for (TreeVo<T> parent : nodes) {
String id = parent.getId();
if (id != null && id.equals(pid)) {
parent.getChildren().add(children);
children.setHasParent(true);
parent.setChildren(true);
continue;
}
}
}
//考虑到数据库表中没有顶级节点(一级节点)
//
TreeVo<T> root = new TreeVo<T>();
if (topNodes.size() == 1) {
//数据库有顶级节点这条数据
root = topNodes.get(0);
} else {
//数据库中没有顶级节点,就进行创建一个
root.setId("000");
root.setParentId("-1");
root.setHasParent(false);
root.setChildren(true);
root.setChecked(true);
root.setChildren(topNodes);
root.setText("顶级节点");
Map<String, Object> state = new HashMap<>(16);
state.put("opened", true);
root.setState(state);
}
return root;
}
/**
* 指定idparam为顶级节点
* @param nodes
* @param idParam
* @param <T>
* @return
*/
public static <T> List<TreeVo<T>> buildList(List<TreeVo<T>> nodes, String idParam) {
// 判断非空
if (nodes == null) {
return null;
}
//存放顶级节点(二级节点)的容器
List<TreeVo<T>> topNodes = new ArrayList<TreeVo<T>>();
// 外层循环
for (TreeVo<T> children : nodes) {
// 获取到当前对象的父id
String pid = children.getParentId();
// 判断id是否等于传进来的父id(根据数据库来判断)
if (pid == null || idParam.equals(pid)) {
// 条件成立加入到当前二级节点下面
topNodes.add(children);
// 跳过
continue;
}
// 内层循环
for (TreeVo<T> parent : nodes) {
// 获取当前对象的id
String id = parent.getId();
// 如果当前对象id等于父id就进来
if (id != null && id.equals(pid)) {
// 获取到二级节点的子节点容器加当前对象
parent.getChildren().add(children);
// 有父节点
children.setHasParent(true);
// 有子节点
parent.setChildren(true);
// 跳过
continue;
}
}
}
// 返回存储好的树结构
return topNodes;
}
}
业务类MenuAction
这一步就是将获取到的数据进行响应并输出到浏览器中,它不用进行结果码的跳转因为它里面有一个转发输出的操作。
package com.liyingdong.web;
import java.sql.SQLException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.liyingdong.dao.MenuDao;
import com.liyingdong.entity.Menu;
import com.liyingdong.framework.ActionSupport;
import com.liyingdong.framework.ModelDriver;
import com.liyingdong.util.ResponseUtil;
public class MenuAction extends ActionSupport implements ModelDriver<Menu>{
private Menu menu=new Menu();
private MenuDao menuDao=new MenuDao();
@Override
public Menu getModel() {
// TODO Auto-generated method stub
return menu;
}
public String menuNodes(HttpServletRequest req,HttpServletResponse resp) {
try {
//方法内部直接进行了输出到浏览器上
ResponseUtil.writeJSON(resp, menuDao.menuNode(null, null));
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
ResponseUtil类
主要功能是进行响应,把传过来的对象转换为json格式的字符串,并且输出到浏览器上。
package com.liyingdong.util;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
public class ResponseUtil {
public static void write(HttpServletResponse response,Object o)throws Exception{
response.setContentType("text/html;charset=utf-8");
PrintWriter out=response.getWriter();
out.println(o.toString());
out.flush();
out.close();
}
public static void writeJSON(HttpServletResponse response,Object o)throws Exception{
// 进行响应的字符编码
response.setContentType("text/html;charset=utf-8");
// ObjectMapper可以方便的将模型对象转换为JSON,或者JSON生成相应的模型类
ObjectMapper om=new ObjectMapper();
// writeValuesAsString的方法就可以把对像转化成json字符串
String jsonstr= om.writeValueAsString(o);
// 获取到写流
PrintWriter out=response.getWriter();
// 输出到浏览器。
out.println(jsonstr.toString());
// 刷新
out.flush();
out.close();
// 关闭流
}
}
实现效果
今天就分享到这了,有不懂之处欢迎评论区留言,拜拜。