EasyUI 树菜单
通过ssm框架项目实现EasyUI 的树菜单的单选,复选,异步加载树,同步加载树和树权限控制等功能。
本章知识点
效果图:
需求:通过SSM框架,实现EasyUI 树菜单的单选,多选,异步加载,同步加载的功能
技术:Spring,SpringMVC,Mybatis,EasyUI
明说:使用EasyUI-Tree,必须严格遵守它的规则,如异步加载树节点的 id,异步加载树返回值的格式等。如果按照其规则来做,你会发现 EasyUI 很简单。反之到处都是吭!
源码:见文章底部
场景:树菜单,在电商中很场景。笔者是在电商公司上班,类目树菜单随处可见。比如给广告员设置类目级别,刊登商品选择类目加载对应的产品规格参数等等
项目结构:
初始化静态树
大部分的功能,并非一步完成。都是从最基础的功能开始。这里是EasyUI-Tree 基础结构
-
根目录
-
关闭状态的子目录
- ITDragon
- 博客
-
默认展开的子目录
- 欢迎
- You!
- 你是最棒的!
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 getCategoryList(int parentId);
/**
* 一次全部加载所有树节点
*/
List 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;
@Service
public class CategoryServiceImpl implements CategoryService {
@Autowired
private CategoryMapper categoryMapper;
@Override
public List getCategoryList(int parentId) {
// 1. 创建查询条件
CategoryExample example = new CategoryExample();
Criteria criteria = example.createCriteria();
criteria.andParentIdEqualTo(parentId); // 查询父节点下的所有子节点
criteria.andStatusEqualTo(0); // 查询未删除状态的菜单
// TODO 权限拦截
// 2. 根据条件查询
List list = categoryMapper.selectByExample(example);
List 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 getCategoryList() {
// 1. 创建查询条件
CategoryExample example = new CategoryExample();
Criteria criteria = example.createCriteria();
criteria.andStatusEqualTo(0); // 查询未删除状态的菜单
// TODO 权限拦截
// 2. 根据条件查询
List list = categoryMapper.selectByExample(example);
List 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;
@Controller
public 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 getAsyncCatList(@RequestParam(value="id",defaultValue="0") int parentId) {
List results = categoryService.getCategoryList(parentId);
return results;
}
@RequestMapping("/sync")
@ResponseBody
private List getSyncCatList() {
List 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"%>
EasyUI-Tree
创建静态树菜单
-
父节点
- 子节点一
- 子节点二
使用方法
ul 标签 定义 class="easyui-tree"
EasyUI 树菜单教程
EasyUI 窗口教程
创建异步树菜单
创建异步树菜单
创建思路
一:初始加载一级类目菜单,通过点击一级类目菜单再查询其子节点菜单
二:类目表设计实例,一级类目的parentId为0,子节点类目的parentId是父节点类目的id
三:返回数据结构类型只要满足EasyUI的规范即可
创建异步树多选菜单
创建异步树多选菜单
注意
若采用异步树加载菜单,会出现勾选父节点。保存后只打印了父节点信息,未打印子节点(因为子节点都没有加载)
解决思路
让业务每个都点开(不合实际);本章节采用同步加载的方式;你们有没有更好的办法?
EasyUI 采用同步加载教程
树菜单权限管理:
业务逻辑:需要一张用户组管理表,设置当前登录用户所属组。
后台逻辑:树菜单表新增字段permission用来匹配用户所属组,说简单点就是多了一层查询条件。
说明:
① tree.jsp 除了EasyUI-Tree 的知识点外,还涉及了一点点窗口的知识
使用 javascript 创建窗口(window)
$('#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/ITDragonBl...
逆向工程:
https://github.com/ITDragonBl...
最后,EasyUI 树菜单到这里就结束了,感谢大家的阅读。觉得不错的可以点个赞!