请选择 进入手机版 | 继续访问电脑版
MSIPO技术圈 首页 IT技术 查看内容

Layui之动态树 左侧树形菜单栏 详细全面

2023-07-13

⭐ฅʕ•̫͡•ʔฅ本期看点:该篇是运用Layui框架来编写后台树形菜单栏,并且结合MySql来编写完成

目录

一.效果图

二.具体步骤

 2.1 数据库 

 2.2 树形导航栏

    第一个类:Treevo

             第二个类:BuildTree:

2.3  Dao方法

        2.3.1 basedao

                 2.3.2 Dao类

2.4  后台Servlet

2.5 前台代码


一.效果图

Layui就一般都为这种水墨风,哈哈,大概就是图片的左侧的样子

二.具体步骤

 2.1 数据库 

        需要准备的数据:首先准备好需要展示在树形菜单中的数据。这些数据应该包含节点的id、父节点id、节点名称等信息,以便构建树形结构。

 2.2 树形导航栏

 写一个java文件,里面包含了所有关于树形导航栏的方法和属性,把关于导航栏的单独用一个类写出,这样会更加清晰明了

    第一个类:Treevo:

        这个里面定义了许多关于父节点以及子节点的属性,可以帮助我们在将数据库的平级数据转换成父子关系的数据 ,以及定义了两个属性是否有父节点,子节点,将来要进行判断

package com.zking.util;

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();
	}

}

第二个类:BuildTree:

        这个类里面定义了两个循环,一个循环可视为外层循环,即可定义为将所有的数据循环一遍,接着第二个循环可视为内层循环,将第二个循环的父id和第一个循环的id进行判断,如果相等就说明 他们是父子关系,并且在这里还指定了默认最高节点也就是父节点,这个可根据自己的数据库里的来进行变化

package com.zking.util;

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("0");
			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) {

			String pid = children.getParentId();
			if (pid == null || idParam.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;
				}
			}

		}
		return topNodes;
	}

}

2.3  Dao方法

        2.3.1 basedao

package com.yinzi.utils;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import com.yinzi.utils.DBAccess;
import com.yinzi.utils.PageBean;

public class BaseDao<T> {
	/**
	 * 带分页的模糊查询
	 * @param c
	 * @param sql
	 * @param pagebean
	 * @return
	 * @throws Exception
	 */
	public List<T> executeQuery(Class c ,String sql,PageBean pagebean) throws Exception{
		//创建集合保存数据
		List<T> list = new ArrayList<>();
		//获取连接
		Connection conn =null;
		//执行SQL语句
		PreparedStatement ps = null;
		//结果集对象
		ResultSet rs =null;
		
		//判断是否需要分页
		if(pagebean!=null && pagebean.isPagination()) {//如果需要分页,就拼接SQL语句
			String Countsql=getCountsql(sql);//获取总数量
			conn = DBAccess.getConnection();
			ps = conn.prepareStatement(Countsql);//执行改变后的SQL语句
			rs = ps.executeQuery();
			if(rs.next()) {
				pagebean.setTotal(rs.getObject("n").toString());
			}
			
			String pagesql=getPagesql(sql,pagebean);
			conn = DBAccess.getConnection();
			ps = conn.prepareStatement(pagesql);//执行改变后的SQL语句
			rs = ps.executeQuery();
		}else {//否则不需要,就按照原sql语句执行
			conn = DBAccess.getConnection();
			ps = conn.prepareStatement(sql);
			rs = ps.executeQuery();
		}
		
		//循环遍历数据
		while(rs.next()) {
			T t = (T) c.newInstance();
			//拿到所有的属性
			Field[] fields = c.getDeclaredFields();
			for (Field field : fields) {
				//打开访问权限
				field.setAccessible(true);
				field.set(t,rs.getObject(field.getName()));
			}
			list.add(t);
		}
		return list;
	}
	/**
	 * 最终SQL语句
	 * @param sql
	 * @param pagebean
	 * @return
	 */
	private String getPagesql(String sql, PageBean pagebean) {
		return sql+" limit "+pagebean.getStartIndex()+","+pagebean.getRows();
	}
	/**
	 * 总记录数
	 * @param sql
	 * @return
	 */
	private String getCountsql(String sql) {
		return "select count(1) as n from ("+sql+") t";
		
	}
	
	
	
	
	
	/**
	 *  增删改的方法
	 * @param sql sql语句
	 * @param t 实体类
	 * @param attr 实体类对应的属性
	 * @return
	 * @throws Exception 
	 */
	public int excuteUpdate(String sql ,T t,String[] attr) throws Exception {
		Connection conn=DBAccess.getConnection();//连接数据库
		PreparedStatement ps=conn.prepareStatement(sql);//执行sql语句
		//循环拿到属性
		for (int i = 0; i < attr.length; i++) {
			//一切反射从获取类类开始
			Field f = t.getClass().getDeclaredField(attr[i]);
			//打开访问权限
			f.setAccessible(true);
			ps.setObject(i+1, f.get(t));
		}
		
		return ps.executeUpdate();
		
	}
	
	
	
	
	
	
	
}

2.3.2 Dao类

        这个类里面首先做了一个查询所有获取所有的数据,接着 写一个方法将这些平级的数据转换成父子关系的数据,还调用了BuildTree里面的方法

package com.yinzi.dao;

import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.yinzi.entity.Permission;
import com.yinzi.utils.BaseDao;
import com.yinzi.utils.PageBean;
import com.zking.util.BuildTree;
import com.zking.util.TreeVo;

public class PermissionDao extends BaseDao<Permission>{
	/**
	 * 查詢所有
	 * @param permission
	 * @param pagebean
	 * @return
	 * @throws Exception
	 */
	public List<Permission> list(Permission permission,PageBean pagebean) throws Exception {
		String sql="select * from t_easyui_permission";
		return super.executeQuery(Permission.class, sql, pagebean);
	}
	
	
	
	//将这些平级数据转换成父子关系的数据
	public List<TreeVo<Permission>> menus(Permission permission,PageBean pagebean) throws Exception{
		//创建一个父子关系的集合  TreeVo
		List<TreeVo<Permission>> tvList = new ArrayList<TreeVo<Permission>>();
		//获取平级数据
		List<Permission> list = this.list(permission, pagebean);
		for (Permission per : list) {
			TreeVo<Permission> tv=new TreeVo<>();
			//将per对象转成tv对象,因为tv对象才有children
			tv.setId(per.getId()+"");
			tv.setText(per.getName());
			tv.setParentId(per.getPid()+"");
			tvList.add(tv);
		}
		return BuildTree.buildList(tvList, "0");//这个地方填写一级菜单的id
	}
	
	
	

	
	
	
}

2.4  后台Servlet

package com.yinzi.Servlet;

import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.yinzi.dao.PermissionDao;
import com.yinzi.entity.Permission;
import com.zking.framework.ActionSupport;
import com.zking.framework.ModelDriver;
import com.zking.util.ResponseUtil;
import com.zking.util.TreeVo;

public class PermissionAction extends ActionSupport implements ModelDriver<Permission>{
	private Permission permission=new Permission();
	private PermissionDao pd=new PermissionDao();
	
	public void menus(HttpServletRequest req, HttpServletResponse resp) throws Exception {
		//调用方法
		List<TreeVo<Permission>> menus = pd.menus(null, null);
		//回显给前台
		ResponseUtil.writeJson(resp, menus);
	}
	
	@Override
	public Permission getModel() {
		return permission;
	}

}

2.5 前台代码

        首先样式小编是复制的Layui的模式,然后在ajax里面进行了拼接循环,最后输出即可

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html >
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>后台首页</title>
<link rel="stylesheet"
	href="${pageContext.request.contextPath}/static/js/layui/css/layui.css"></link>
<script type="text/javascript"
	src="${pageContext.request.contextPath}/static/js/layui/layui.js"></script>
</head>
<body>
<div class="layui-layout layui-layout-admin">
  <div class="layui-header">
    <div class="layui-logo layui-hide-xs layui-bg-black">layout demo</div>
    <!-- 头部区域(可配合layui 已有的水平导航) -->
    <ul class="layui-nav layui-layout-left">
      <!-- 移动端显示 -->
      <li class="layui-nav-item layui-show-xs-inline-block layui-hide-sm" lay-header-event="menuLeft">
        <i class="layui-icon layui-icon-spread-left"></i>
      </li>
      <!-- Top导航栏 -->
      <li class="layui-nav-item layui-hide-xs"><a href="">nav 1</a></li>
      <li class="layui-nav-item layui-hide-xs"><a href="">nav 2</a></li>
      <li class="layui-nav-item layui-hide-xs"><a href="">nav 3</a></li>
      <li class="layui-nav-item">
        <a href="javascript:;">nav groups</a>
        <dl class="layui-nav-child">
          <dd><a href="">menu 11</a></dd>
          <dd><a href="">menu 22</a></dd>
          <dd><a href="">menu 33</a></dd>
        </dl>
      </li>
    </ul>
    <!-- 个人头像及账号操作 -->
    <ul class="layui-nav layui-layout-right">
      <li class="layui-nav-item layui-hide layui-show-md-inline-block">
        <a href="javascript:;">
          <img src="//tva1.sinaimg.cn/crop.0.0.118.118.180/5db11ff4gw1e77d3nqrv8j203b03cweg.jpg" class="layui-nav-img">
          tester
        </a>
        <dl class="layui-nav-child">
          <dd><a href="">Your Profile</a></dd>
          <dd><a href="">Settings</a></dd>
          <dd><a href="login.jsp">Sign out</a></dd>
        </dl>
      </li>
      <li class="layui-nav-item" lay-header-event="menuRight" lay-unselect>
        <a href="javascript:;">
          <i class="layui-icon layui-icon-more-vertical"></i>
        </a>
      </li>
    </ul>
  </div>
  
  <div class="layui-side layui-bg-black">
    <div class="layui-side-scroll">
      <!-- 左侧导航区域(可配合layui已有的垂直导航) -->
      <ul id="menu" class="layui-nav layui-nav-tree" lay-filter="menu">
        <li class="layui-nav-item layui-nav-itemed">
          <a class="" href="javascript:;">menu group 1</a>
          <dl class="layui-nav-child">
            <dd><a href="javascript:;">menu 1</a></dd>
            <dd><a href="javascript:;">menu 2</a></dd>
            <dd><a href="javascript:;">menu 3</a></dd>
            <dd><a href="">the links</a></dd>
          </dl>
        </li>
        <li class="layui-nav-item">
          <a href="javascript:;">menu group 2</a>
          <dl class="layui-nav-child">
            <dd><a href="javascript:;">list 1</a></dd>
            <dd><a href="javascript:;">list 2</a></dd>
            <dd><a href="">超链接</a></dd>
          </dl>
        </li>
        <li class="layui-nav-item"><a href="javascript:;">click menu item</a></li>
        <li class="layui-nav-item"><a href="">the links</a></li>
      </ul>
    </div>
  </div>
  
  <div class="layui-body">
    <!-- 内容主体区域 -->
    <div style="padding: 15px;">内容主体区域。记得修改 layui.css 和 js 的路径</div>
  </div>
  
  <div class="layui-footer">
    <!-- 底部固定区域 -->
            底部固定区域
  </div>
</div>
<script>
//JS 
layui.use(['element', 'layer', 'util'], function(){
  var element = layui.element ,
  layer = layui.layer,
  util = layui.util,
  $ = layui.$;

  $.ajax({
	  url:"${pageContext.request.contextPath}/permission.action?methodName=menus",
	  dataType:"json",
	  success:function(data){
		  console.log(data)
		//定义一个字符串进行拼接
		var htmlStr="";
		//遍历数据
		$.each(data,function(i,n){
			//拼接
			htmlStr+='<li class="layui-nav-item layui-nav-itemed">';
			htmlStr+='<a class="" href="javascript:;">'+n.text+'</a>';
			//如果其存在孩子,就在进行循环
			 if(n.hasChildren){
				var children =n.children;
				//因为这个只需要循环一次,所以放在循环外面
				htmlStr+='<dl class="layui-nav-child">';
				$.each(children,function(index,node){
					//这个需要循环多次
					htmlStr+='<dd><a href="javascript:;">'+node.text+'</a></dd>';
				})
				htmlStr+='</dl>';
			} 
			
			htmlStr+='</li>';
		})
		  $("#menu").html(htmlStr);
		//渲染
		element.render('menu');	
	  }
  });
		
		
  });
  
  
</script>
</body>
</html>

今天的分享的案例就到这啦,有不懂的宝子,评论区留言,尽情期待哦~~

相关阅读

热门文章

    手机版|MSIPO技术圈 皖ICP备19022944号-2

    Copyright © 2024, msipo.com

    返回顶部