zTree实现树形结构菜单

举报
别团等shy哥发育 发表于 2023/02/05 16:56:09 2023/02/05
【摘要】 @[toc] 一、简介zTree 是一个依靠 jQuery 实现的多功能 “树插件”。优异的性能、灵活的配置、多种功能的组合是 zTree 最大优点。官方文档:http://www.treejs.cn/v3/api.php功能很强大,API都是中文的,但是在样式上面稍有欠缺,且容易受到开发环境版本的影响。 二、前端渲染效果 三、实现步骤 1、数据库表结构 2、引入zTree插件<link r...

@[toc]

一、简介

zTree 是一个依靠 jQuery 实现的多功能 “树插件”。优异的性能、灵活的配置、多种功能的组合是 zTree 最大优点。
官方文档:http://www.treejs.cn/v3/api.php

功能很强大,API都是中文的,但是在样式上面稍有欠缺,且容易受到开发环境版本的影响。

二、前端渲染效果

在这里插入图片描述

三、实现步骤

1、数据库表结构

在这里插入图片描述

在这里插入图片描述

2、引入zTree插件

<link rel="stylesheet"
href="/ccms/commons/jslib/ztreeV3.5.15/css/zTreeStyle/zTreeStyle.css" type="text/css">
<script type="text/javascript"
	src="/ccms/commons/jslib/ztreeV3.5.15/jquery.ztree.all-3.5.js"></script>
	<script type="text/javascript"
		src="/ccms/commons/jslib/js-gmxt-define/ztreeTool.js"></script>

3、树形结构实体类SysModule

省略get和set方法

public class SysModule {
	/**模板编码*/
	private String moduleCode;  
	/**模板名称*/
	private String moduleName;	
	/**模板路径*/
	private String modulePath;	
	/**父级模板编号*/
	private String parentCode;	
	/**是否为叶子节点*/
	private int isLeaf;			
	/**同级排序编号*/
	private int sortNumber;
}

树形结构辅助类:

package com.ccms.tools;

import java.util.List;

public class Tree {
	
	private String id;

	private String pId;

	private String name;

	private boolean isParent;

	private boolean open;

	private boolean checked;

	private boolean nocheck;

	private List<Tree> children;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public boolean getParent() {
		return isParent;
	}

	public void setParent(boolean isParent) {
		this.isParent = isParent;
	}

	public boolean isOpen() {
		return open;
	}

	public void setOpen(boolean open) {
		this.open = open;
	}

	public boolean isChecked() {
		return checked;
	}

	public void setChecked(boolean checked) {
		this.checked = checked;
	}

	public List<Tree> getChildren() {
		return children;
	}

	public void setChildren(List<Tree> children) {
		this.children = children;
	}

	public boolean isNocheck() {
		return nocheck;
	}

	public void setNocheck(boolean nocheck) {
		this.nocheck = nocheck;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getpId() {
		return pId;
	}

	public void setpId(String pId) {
		this.pId = pId;
	}

	private String attributes;

	public void setAttributes(String attributes) {
		this.attributes = attributes;
	}

	public String getAttributes() {
		return attributes;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		final Tree other = (Tree) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}

	
	

}

4、表示层代码

<div class="col-sm-3">
	<ul id="protree" class="ztree" style="background: #fff"></ul>
</div>

5、js渲染部分

1、树初始化配置

var setting = {
			check : {
				enable : true,
				autoCheckTrigger : true,	//触发事件回调函数
				chkStyle : "checkbox",		//勾选框类型:checkbox
				chkboxType : {
					"Y" : "s",	//checkbox被勾选后,s标识操作会影响父级节点
					"N" : "ps"
				}
			},
			data : {
				simpleData : {
					enable : false,
				}
			},
			edit : {
				enable : true,
				showRemoveBtn : false,
				showRenameBtn : false,
				drag : {
					autoExpandTrigger : true,
					prev : false,
					inner : true,
					next : false
				}
			},
			callback : {
				onClick : zTreeOnClick,
				onDrop : zTreeOnDrop,
				beforeDrop : zTreeBeforeDrop
			}
		};
//节点点击事件
		function zTreeOnClick(event, treeId, treeNode) {
			// 隐藏添加功能模块表单
			$('#addwin').hide();
			if (isSelected("protree")) {
				if (getSelected("protree").id != "null") {
					// 显示修改功能模块表单
					$('#upwin').show();
					// 在修改功能模块表单中,绑定修改前的数据
					loadSingleData(getSelected("protree").id);
				} else {
					$("#addwin").hide();
					$("#upwin").hide();
				}
			}
		}
		//加载单条数据
		function loadSingleData(id) {
			$.ajax({
				url : '/ccms/module/getSingleData',
				dataType : 'json',
				data : {
					id : id
				},
				type : 'post',
				success : function(data) {
					$("#pname_edit").val(data.moduleData.parentModuleName);
					$("#moduleCode_edit").val(data.moduleData.moduleCode);
					$("#moduleName_edit").val(data.moduleData.moduleName);
					$("#modulePath_edit").val(data.moduleData.modulePath);
					$("#parentCode_edit").val(data.moduleData.parentCode);
					$("#isLeaf_edit>input[name='isLeaf']:checked").prop(
							'checked', false);
					$("#isLeaf" + data.moduleData.isLeaf + "_edit").prop(
							'checked', true);
					$("#sortNumber_edit").val(data.moduleData.sortNumber);
					if (data.moduleData.moduleCode == '0') {
						$("#moduleName_edit").attr('disabled', true);
						$("#moduleCode_edit").attr('disabled', true);
						$("#modulePath_edit").attr('disabled', true);
						$("#parentCode_edit").attr('disabled', true);
						$("#isLeaf_edit input[name='isLeaf']").attr('disabled',
								true);
						$("#sortNumber_edit").attr('disabled', true);
					} else {
						$("#moduleName_edit").attr('disabled', false);
						$("#moduleCode_edit").attr('disabled', false);
						$("#modulePath_edit").attr('disabled', false);
						$("#parentCode_edit").attr('disabled', false);
						$("#isLeaf_edit input[name='isLeaf']").attr('disabled', false);
						$("#sortNumber_edit").attr('disabled', false);
					}
				},
				error : function() {
					swal('系统提示', '抱歉,数据加载失败。', 'info');
				}
			});
		}
function zTreeOnDrop(event, treeId, treeNodes, targetNode, moveType) {
			var selfId = treeNodes[0].id;
			var parentId = targetNode.id;

			$.ajax({
				url : '/ccms/manager/ModuleController/changeJoin',
				dataType : 'json',
				data : {
					selfId : selfId,
					parentId : parentId
				},
				type : 'post',
				success : function(jsonData) {
					if (jsonData.rtnCode = 0) {
						swal('系统提示', "修改层级关系成功", 'info');
						loadProTree();
					}

				},
				error : function() {
					swal('系统提示', "修改层级关系失败~~", 'info');
				}
			});

		};

		function zTreeBeforeDrop(treeId, treeNodes, targetNode, moveType) {

			if (jsonToObj(targetNode.attributes).isLeaf == '1') {
				swal('系统提示', "目标节点已是末节点,不可进行拖拽~~", 'info');
				return false;
			} else {
				return true;

			}
		};

2、加载数据树

//加载数据树
		function loadProTree() {
			$.ajax({
				url : '/ccms/module/getTreeList',
				type : 'post',
				async : 'true',
				cache : false,
				data : {},
				dataType : 'json',
				success : function(data) {
					$.fn.zTree.init($("#protree"), setting, data.zNodes);
				}
			});
		}

4、控制器关键代码

 // 获取module树
    @RequestMapping(value = "/getTreeList", method = { RequestMethod.GET, RequestMethod.POST })
    @ResponseBody
    public Map<String, Object> getTreeList(HttpServletRequest req, HttpServletResponse resp)
            throws UnsupportedEncodingException, IOException {
        List<Tree> tree = moduleService.getProtocolTree();
        Map<String, Object> result = new HashMap<String, Object>();
        result.put("zNodes", tree);
        return result;
    }
// 根据id获取单条数据
    @RequestMapping(value = "/getSingleData", method = { RequestMethod.GET, RequestMethod.POST })
    @ResponseBody
    public Map<String, Object> getSingleData(String id, HttpServletRequest req, HttpServletResponse resp)
            throws UnsupportedEncodingException, IOException {
        SysModule info = moduleService.getSingleDate(id);
        Map<String, Object> result = new HashMap<String, Object>();
        result.put("moduleData", info);
        return result;
    }

5、业务逻辑层代码:

public interface ModuleService {
    //加载module树
    public List<Tree> getProtocolTree();

    // 查询是否有重复值,存在 exit,不存在 no
    public String isExistName(String moduleName, String moduleCode);
    //根据id获取module
    public SysModule getSingleDate(String moduleCode);
}

实现类:

package com.ccms.service.impl;

import com.ccms.dao.ModuleDao;
import com.ccms.pojo.SysModule;
import com.ccms.service.ModuleService;
import com.ccms.tools.Tree;
import com.ccms.tools.UUIDGenerator;
import org.json.simple.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
@Service("moduleService")
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT)
public class ModuleServiceImpl implements ModuleService {

    @Autowired
    ModuleDao moduleDao;


    //加载module树
    @Override
    public List<Tree> getProtocolTree() {
        List<String> attributesList = new ArrayList<String>();
        attributesList.add("isLeaf");
        // 获取功能模块的所有父节点
        List<SysModule> parentModuleLst = moduleDao.getParentTree();
        List<Tree> parentTreesLst = new ArrayList<Tree>();
        // 将List<SysModule>类型转换成List<Tree>
        for (SysModule module : parentModuleLst) {
            Tree rootTreeData = new Tree();
            rootTreeData.setId(module.getModuleCode());
            rootTreeData.setName(module.getModuleName());
            rootTreeData.setOpen(true);
            rootTreeData.setChecked(false);
            rootTreeData.setParent(false);
            rootTreeData.setNocheck(false);
            JSONObject attributesJO = new JSONObject();
            attributesJO.put("isLeaf", module.getIsLeaf());
            rootTreeData.setAttributes(attributesJO.toString());
            parentTreesLst.add(rootTreeData);
        }
        if (parentTreesLst != null) {
            // 遍历List<Tree>类型对象parentTreesLst
            for (Tree parentData : parentTreesLst) {
                // 获取当前父节点模块编号
                String moduleCode = parentData.getId();
                // 根据当前父节点模块编号,获取其所包含的功能模块
                List<SysModule> list = moduleDao.getTreeList(moduleCode);
                List<Tree> treeList = new ArrayList<Tree>();
                // 将List<SysModule>类型转换成List<Tree>
                for (SysModule sysModule : list) {
                    Tree tempTreeData = new Tree();
                    tempTreeData.setId(sysModule.getModuleCode());
                    tempTreeData.setName(sysModule.getModuleName());
                    tempTreeData.setOpen(true);
                    tempTreeData.setChecked(false);
                    tempTreeData.setParent(false);
                    tempTreeData.setNocheck(false);
                    JSONObject attributesJO = new JSONObject();
                    attributesJO.put("isLeaf", sysModule.getIsLeaf());
                    tempTreeData.setAttributes(attributesJO.toString());
                    List childList = new ArrayList<Tree>();
                    childList = getChildTreeById(sysModule.getModuleCode());
                    tempTreeData.setChildren(childList);
                    treeList.add(tempTreeData);
                }
                parentData.setChildren(treeList);
            }
        }
        return parentTreesLst;
    }
    // 查询是否有重复值,存在 exit,不存在 no
    @Override
    public String isExistName(String moduleName, String moduleCode) {
        String result;
        int i = moduleDao.existSameModuleName(moduleName, moduleCode);
        if (i > 0) {
            result = "exit";
        } else {
            result = "no";
        }
        return result;
    }

    //根据id获取module
    @Override
    public SysModule getSingleDate(String moduleCode) {
        return moduleDao.getSingleDataById(moduleCode);
    }
   

    // 根据父节点CodeId获取子节点数据列表
    private List getChildTreeById(String codeId) {
        List<Tree> treeList = new ArrayList<Tree>();
        List<SysModule> list = moduleDao.getTreeList(codeId);
        for (SysModule sysModule : list) {
            Tree tempTreeData = new Tree();
            tempTreeData.setId(sysModule.getModuleCode());
            tempTreeData.setName(sysModule.getModuleName());
            tempTreeData.setOpen(true);
            tempTreeData.setChecked(false);
            tempTreeData.setParent(false);
            tempTreeData.setNocheck(false);
            JSONObject attributesJO = new JSONObject();
            attributesJO.put("isLeaf", sysModule.getIsLeaf());
            tempTreeData.setAttributes(attributesJO.toString());
            List childList = new ArrayList<Tree>();
            childList = getChildTreeById(sysModule.getModuleCode());
            tempTreeData.setChildren(childList);
            treeList.add(tempTreeData);
        }
        return treeList;
    }
}

6、数据访问层代码:

package com.ccms.dao;

import com.ccms.pojo.SysModule;
import org.apache.ibatis.annotations.*;

import java.util.List;

public interface ModuleDao {
    // 查询所有模块
    @Select("select * from sys_module order by sortNumber asc")
    public List<SysModule> getAllModule();

    // 查询用户可以操作的模块编号集合
    @Select("select distinct moduleCode from sys_role_module where roleCode "
            + "in (select roleCode from sys_user_role where userCode=#{userCode})")
    List<String> getmoduleCodes(@Param("userCode") String userCode);

    // 获取树形结构所有父节点
    @Select("select * from sys_module where parentCode is null order by sortNumber asc")
    public List<SysModule> getParentTree();

    // 根据功能模块编号,获取其所包含的功能模块
    @Select("select * from sys_module where parentCode = #{moduleCode} order by sortNumber asc")
    public List<SysModule> getTreeList(@Param("moduleCode") String moduleCode);

    // 根据id查询重复name
    @Select("select ifnull(count(*),0) from sys_module where moduleName=#{moduleName} and moduleCode <> #{moduleCode}")
    public int existSameModuleName(@Param("moduleName") String moduleName, @Param("moduleCode") String moduleCode);

    // 根据功能模块编号获取数据
    @Select("select cm.*, (case when cm.parentCode is null then '无' " + " else  pm.moduleName end) as parentModuleName"
            + " from sys_module cm" + " left join sys_module pm" + " on cm.parentCode=pm.moduleCode"
            + " where cm.moduleCode= #{moduleCode}")
    public SysModule getSingleDataById(@Param("moduleCode") String moduleCode);

}

四、碰到的bug及解决方案

1、指定结点选中无效

描述checkNode(treeNode,checked,checkTypeFlag,callbackFlag)方法不能指定某个结点选中
解决
若想在树加载之后设置指定的Id选中,请将ajax的async的值设为false,即同步加载(也就是说先要等树加载完再指定结点选中,要不会出现未知错误,很难发现)

2、mybatis多对多关系的处理较为麻烦

解决请点击这里:https://blog.csdn.net/qq_43753724/article/details/115205164

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。