EasyUI 树菜单
来源:互联网 发布:尤克里里教程软件 编辑:程序博客网 时间:2024/06/08 08:05
EasyUI 树菜单
通过ssm框架项目实现EasyUI 的树菜单的单选,复选,异步加载树,同步加载树和树权限控制等功能。
本章知识点
效果图:
需求:通过SSM框架,实现EasyUI 树菜单的单选,多选,异步加载,同步加载的功能
技术:Spring,SpringMVC,Mybatis,EasyUI
明说:使用EasyUI-Tree,必须严格遵守它的规则,如异步加载树节点的 id,异步加载树返回值的格式等。如果按照其规则来做,你会发现 EasyUI 很简单。反之到处都是吭!
源码:见文章底部
场景:树菜单,在电商中很场景。笔者是在电商公司上班,类目树菜单随处可见。比如给广告员设置类目级别,刊登商品选择类目加载对应的产品规格参数等等
项目结构:
初始化静态树
大部分的功能,并非一步完成。都是从最基础的功能开始。这里是EasyUI-Tree 基础结构
<ul class="easyui-tree"> <li> <span>根目录</span> <ul> <li data-options="state:'closed'"> <span>关闭状态的子目录</span> <ul> <li>ITDragon</li> <li>博客</li> </ul> </li> <li> <span>默认展开的子目录</span> <ul> <li>欢迎</li> <li>You!</li> </ul> </li> <li>你是最棒的!</li> </ul> </li></ul>
Maven Web项目实战
项目框架结构是:Spring,SpringMVC,Mybatis。 没有其他的额外配置,都是基础的整合配置。这里就不贴代码。读者也可以直接从github上clone下来(sql文件也在项目中)。
POJO层
本章的主角,类目实体类 Category.java
package com.itdragon.pojo;import java.util.Date;public class Category { private Integer id; private String name; private Integer isLeaf; private Integer parentId; private Date createddate; private Date updateddate; private Integer status; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name == null ? null : name.trim(); } public Integer getIsLeaf() { return isLeaf; } public void setIsLeaf(Integer isLeaf) { this.isLeaf = isLeaf; } public Integer getParentId() { return parentId; } public void setParentId(Integer parentId) { this.parentId = parentId; } public Date getCreateddate() { return createddate; } public void setCreateddate(Date createddate) { this.createddate = createddate; } public Date getUpdateddate() { return updateddate; } public void setUpdateddate(Date updateddate) { this.updateddate = updateddate; } public Integer getStatus() { return status; } public void setStatus(Integer status) { this.status = status; }}
按照EasyUI规范封装的Tree节点实体类 EUTreeNode.java
package com.itdragon.common.pojo;/** * 树的数据格式(Tree Data Format) * 每个节点可以包括下列属性: * id:节点的 id,它对于加载远程数据很重要。 * text:要显示的节点文本。 * state:节点状态,'open' 或 'closed',默认是 'open'。当设置为 'closed' 时,该节点有子节点,并且将从远程站点加载它们。 * checked:指示节点是否被选中。 * attributes:给一个节点添加的自定义属性。 * children:定义了一些子节点的节点数组 * * 这里先封装常用的 id,text,state */public class EUTreeNode { private long id; private long parentId; private String text; private String state; public long getId() { return id; } public void setId(long id) { this.id = id; } public long getParentId() { return parentId; } public void setParentId(long parentId) { this.parentId = parentId; } public String getText() { return text; } public void setText(String text) { this.text = text; } public String getState() { return state; } public void setState(String state) { this.state = state; }}
说明:
① Category.java 属性 createdDate 和 updatedDate 的类型都是java.util.Date。实际上也可以是 String 类型,这样可以在显示(日期格式化),排序,筛选时减少很多工作量。
② 这里的 Category.java,CategoryExample.java,CategoryMapper.java,CategoryMapper.xml 是通过 Mybatis 提供的逆向工程自动生成的。文章底部会提供链接。
Service 层
提供查询类目的接口 CategoryService.java 感觉怪怪的 -.-||
package com.itdragon.service;import java.util.List;import com.itdragon.common.pojo.EUTreeNode;public interface CategoryService { /** * 通过父节点,异步加载树菜单 * @param parentId */ List<EUTreeNode> getCategoryList(int parentId); /** * 一次全部加载所有树节点 */ List<EUTreeNode> getCategoryList();}
类目接口的实现类 CategoryServiceImpl.java
package com.itdragon.service.impl;import java.util.ArrayList;import java.util.List;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import com.itdragon.common.pojo.EUTreeNode;import com.itdragon.mapper.CategoryMapper;import com.itdragon.pojo.Category;import com.itdragon.pojo.CategoryExample;import com.itdragon.pojo.CategoryExample.Criteria;import com.itdragon.service.CategoryService;@Servicepublic class CategoryServiceImpl implements CategoryService { @Autowired private CategoryMapper categoryMapper; @Override public List<EUTreeNode> getCategoryList(int parentId) { // 1. 创建查询条件 CategoryExample example = new CategoryExample(); Criteria criteria = example.createCriteria(); criteria.andParentIdEqualTo(parentId); // 查询父节点下的所有子节点 criteria.andStatusEqualTo(0); // 查询未删除状态的菜单 // TODO 权限拦截 // 2. 根据条件查询 List<Category> list = categoryMapper.selectByExample(example); List<EUTreeNode> resultList = new ArrayList<>(); // 3. 把列表转换成 EasyUI Tree 需要的json格式 for (Category category : list) { EUTreeNode node = new EUTreeNode(); node.setId(category.getId()); node.setText(category.getName()); node.setState(category.getIsLeaf() == 1?"open":"closed"); resultList.add(node); } // 4. 返回结果 return resultList; } @Override public List<EUTreeNode> getCategoryList() { // 1. 创建查询条件 CategoryExample example = new CategoryExample(); Criteria criteria = example.createCriteria(); criteria.andStatusEqualTo(0); // 查询未删除状态的菜单 // TODO 权限拦截 // 2. 根据条件查询 List<Category> list = categoryMapper.selectByExample(example); List<EUTreeNode> resultList = new ArrayList<>(); // 3. 把列表转换成 EasyUI Tree 需要的json格式 for (Category category : list) { EUTreeNode node = new EUTreeNode(); node.setId(category.getId()); node.setText(category.getName()); node.setState(category.getIsLeaf() == 1?"open":"closed"); node.setParentId(category.getParentId()); resultList.add(node); } // 4. 返回结果 return resultList; }}
说明:树菜单的权限拦截,并没有提供代码,是考虑到涉及其他实体类。其实有了思路,其他的都好说………………好吧!我承认自己懒=。=
Controller 层
用于页面跳转的 PageController.java
package com.itdragon.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;@Controllerpublic class PageController { @RequestMapping("/") public String showIndex() { return "tree"; } @RequestMapping("/{page}") public String showpage(@PathVariable String page) { return page; }}
负责加载类目树菜单的 CategoryController.java
package com.itdragon.controller;import java.util.List;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.ResponseBody;import com.itdragon.common.pojo.EUTreeNode;import com.itdragon.service.CategoryService;@Controller@RequestMapping("/category")public class CategoryController { @Autowired private CategoryService categoryService; /** * http://www.jeasyui.net/plugins/185.html * 当展开一个关闭的节点时,如果该节点没有子节点加载,它将通过上面定义的 URL 向服务器发送节点的 id 值作为名为 'id' 的 http 请求参数,以便检索子节点。 * 所以这里的参数value必须是id,若是其他值则接收不到。缺省值是0,表示初始化一级菜单。 * * @param parentId * @return */ @RequestMapping("/async") @ResponseBody private List<EUTreeNode> getAsyncCatList(@RequestParam(value="id",defaultValue="0") int parentId) { List<EUTreeNode> results = categoryService.getCategoryList(parentId); return results; } @RequestMapping("/sync") @ResponseBody private List<EUTreeNode> getSyncCatList() { List<EUTreeNode> results = categoryService.getCategoryList(); return results; }}
说明:这里的@RequestParam(value=”id”,defaultValue=”0”),value值必须是id,不能是其他值。
Views视图层
演示EasyUI-Tree 类目树菜单的 tree.jsp
<%@ 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>EasyUI-Tree</title><link rel="stylesheet" type="text/css" href="js/jquery-easyui-1.4.1/themes/default/easyui.css" /><link rel="stylesheet" type="text/css" href="js/jquery-easyui-1.4.1/themes/icon.css" /><script type="text/javascript" src="js/jquery-easyui-1.4.1/jquery.min.js"></script><script type="text/javascript" src="js/jquery-easyui-1.4.1/jquery.easyui.min.js"></script><script type="text/javascript" src="js/jquery-easyui-1.4.1/locale/easyui-lang-zh_CN.js"></script></head><body class="easyui-layout"> <div data-options="region:'west',title:'EasyUI 树菜单',split:true" style="width:205px;"> <ul id="menu" class="easyui-tree" style="margin-top: 10px;margin-left: 5px;"> <li> <span>EasyUI</span> <ul> <li>静态树</li> <li>结构为ul li 标签</li> <li>ul定义class为easyui-tree</li> </ul> </li> <li> <span>本章知识点</span> <ul> <li>创建静态树菜单</li> <li>创建异步树菜单</li> <li>创建异步树多选菜单</li> <li>树菜单权限管理</li> </ul> </li> </ul> </div> <div id="content" region="center" title="ITDragon博客" style="padding:5px;"> <span> <h3>创建静态树菜单</h3> <ul id="" class="easyui-tree"> <li> <span>父节点</span> <ul> <li>子节点一</li> <li>子节点二</li> </ul> </li> </ul> <h4>使用方法</h4> <p>ul 标签 定义 class="easyui-tree"</p> <a href="http://www.jeasyui.net/plugins/185.html">EasyUI 树菜单教程 </a> <br/> <a href="http://www.jeasyui.net/plugins/180.html">EasyUI 窗口教程 </a> </span> <hr/> <span> <h3>创建异步树菜单</h3> <a href="javascript:void(0)" class="easyui-linkbutton selectCategory">创建异步树菜单</a> <input type="hidden" name="categoryId" style="width: 280px;"></input> <br/> <h4>创建思路</h4> <p>一:初始加载一级类目菜单,通过点击一级类目菜单再查询其子节点菜单</p> <p>二:类目表设计实例,一级类目的parentId为0,子节点类目的parentId是父节点类目的id</p> <p>三:返回数据结构类型只要满足EasyUI的规范即可</p> </span> <hr/> <span> <h3>创建异步树多选菜单</h3> <a href="javascript:void(0)" class="easyui-linkbutton selectMoreCategory">创建异步树多选菜单</a> <input type="hidden" name="categoryIds" style="width: 280px;"></input> <br/> <h4>注意</h4> <p>若采用异步树加载菜单,会出现勾选父节点。保存后只打印了父节点信息,未打印子节点(因为子节点都没有加载)</p> <h4>解决思路</h4> <p>让业务每个都点开(不合实际);本章节采用同步加载的方式;你们有没有更好的办法?</p> <a href="http://www.jeasyui.net/tutorial/57.html"> EasyUI 采用同步加载教程 </a> </span> <hr/> <span> <h3>树菜单权限管理:</h3> <p>业务逻辑:需要一张用户组管理表,设置当前登录用户所属组。</p> <p>后台逻辑:树菜单表新增字段permission用来匹配用户所属组,说简单点就是多了一层查询条件。</p> </span> </div> <script type="text/javascript"> $(function(){ initAsyncCategory (); initMoreSyncCategory (); }); // 异步加载树菜单 function initAsyncCategory (){ $(".selectCategory").each(function(i,e){ var _ele = $(e); _ele.after("<span style='margin-left:10px;'></span>"); // 避免被按钮遮住 _ele.unbind('click').click(function(){ $("<div>").html("<ul>").window({ // 使用 javascript 创建窗口(window) width:'500', height:"450", modal:true, closed:true, iconCls:'icon-save', title:'异步树菜单', onOpen : function(){ // 窗口打开后执行 var _win = this; $("ul",_win).tree({ url:'/category/async', // 采用异步加载树节点,返回数据的格式要满足EasyUI Tree 的要求 animate:true, onClick:function(node){ // 树菜单点击后执行 if($(this).tree("isLeaf",node.target)){ // 如果该节点是叶节点就填写到categoryId中,并关闭窗口 _ele.parent().find("[name=categoryId]").val(node.id); _ele.next().text(node.text).attr("categoryId",node.id); $(_win).window('close'); } } }); }, onClose : function(){ // 窗口关闭后执行 $(this).window("destroy"); } }).window('open'); // 使用 javascript 打开窗口(window) }); }); } // 同步加载复选树菜单 function initMoreSyncCategory (){ $(".selectMoreCategory").each(function(i,e){ var _ele = $(e); _ele.after("<span style='margin-left:10px;'></span>"); _ele.unbind('click').click(function(){ $("<div>").html("<ul id='moreItemCat'>").window({ // 使用 javascript 创建窗口(window) width:'500', height:"450", modal:true, closed:true, iconCls:'icon-save', title:'多选树菜单,关闭窗口后保存数据', onOpen : function(){ // 窗口打开后执行 var _win = this; $("ul",_win).tree({ url:'/category/sync', // 采用同步的方式加载所有树节点 animate:true, checkbox:true, // js 声明树菜单可以复选 loadFilter: function(rows){ return convert(rows); } }); }, onClose : function(){ // 窗口关闭后执行 var nodes = $("#moreItemCat").tree('getChecked'); var categoryIds = ''; var categoryTexts = ''; for(var i = 0; i < nodes.length; i++){ if ('' != categoryIds) { categoryIds += ','; categoryTexts += ' , '; } categoryIds += nodes[i].id; categoryTexts += nodes[i].text; } _ele.parent().find("[name=categoryIds]").val(categoryIds); _ele.next().text(categoryTexts).attr("categoryId",categoryTexts); $(this).window("destroy"); } }).window('open'); // 使用 javascript 打开窗口(window) }); }); } // 官方提供的 js 解析 json 代码 function convert(rows){ function exists(rows, parentId){ for(var i=0; i<rows.length; i++){ if (rows[i].id == parentId) return true; } return false; } var nodes = []; for(var i=0; i<rows.length; i++){ // get the top level nodes var row = rows[i]; if (!exists(rows, row.parentId)){ nodes.push({ id:row.id, text:row.text, state:row.state }); } } var toDo = []; for(var i=0; i<nodes.length; i++){ toDo.push(nodes[i]); } while(toDo.length){ var node = toDo.shift(); // the parent node for(var i=0; i<rows.length; i++){ // get the children nodes var row = rows[i]; if (row.parentId == node.id){ var child = {id:row.id,text:row.text,state:row.state}; if (node.children){ node.children.push(child); } else { node.children = [child]; } toDo.push(child); } } } return nodes; } </script></body></html>
说明:
① tree.jsp 除了EasyUI-Tree 的知识点外,还涉及了一点点窗口的知识
- 使用 javascript 创建窗口(window)
<div id="win"></div>$('#win').window({ width:600, height:400, modal:true});
- 打开和关闭窗口(window)
$('#win').window('open'); // open a window$('#win').window('close'); // close a window
② tree.jsp 主要包含了单选异步加载树菜单和多选同步加载树菜单两大知识点,所以内容较长,请耐心阅读。
③ 若异步加载树菜单,支持多选,会出现子节点没有打印的问题
总结
- 如何初始化静态的树菜单。
- 如何实现异步加载树菜单,单选后显示在页面上。
- 如何实现同步加载树菜单,多选后显示在页面上。
- 树菜单表的设计思路。
源码:
https://github.com/ITDragonBlog/daydayup/tree/master/EasyUI/EasyUI-tree
逆向工程:
https://github.com/ITDragonBlog/daydayup/tree/master/mybatis/generatorSqlmapCustom
最后,EasyUI 树菜单到这里就结束了,感谢大家的阅读。觉得不错的可以点个赞!
- EasyUI 树菜单
- EasyUI菜单
- EasyUI 树型菜单 异步加载树
- EasyUI动态展示菜单树是否选中
- easyui tree异步树菜单联动
- easyui menu tree 实现左侧菜单树
- easyUI动态生成多级菜单树
- easyui 生成 多级 菜单树 01
- easyui 生成 菜单结构树 02
- easyui 右键菜单实例
- jquery easyui 右键菜单
- jQuery easyui 树形菜单
- Easyui 二级菜单
- jQuery easyUI 树形菜单
- EasyUI 菜单与按钮
- EasyUI 树形菜单
- EasyUI 树形菜单网格
- EasyUI 菜单与按钮
- greenDAO 官方替换数据库框架ObjectBox 学习 写记录1
- 周鸿祎与小蓝单车有何仇怨,称:这个CEO一直有人品问题
- 闲来麻将源码下载
- Qt 维护工具MaintenanceTool.exe 使用
- 从零开始学习区块链(1)
- EasyUI 树菜单
- Qml实用技巧:将样式style从对象中独立出来,可使多个按钮加载同一个样式
- [oracle]pl/sql in/out变量的使用和过程中过程的调用
- Kivy 中文教程 实例入门 简易画板 (Simple Paint App):2. 实现绘图功能
- PAT:B1012. 数字分类 (20)
- lambda
- Router的使用
- 获取跳转菜单的值
- idea 注册