【原】无脑操作:EasyUI Tree实现左键只选择叶子节点、右键浮动菜单实现增删改
Easyui中的Tree组件使用频率颇高,经常遇到的需求如下:
1、在树形结构上,只有叶子节点才能被选中,其他节点不能被选中;
2、在叶子节点上右键出现浮动菜单实现新增、删除、修改操作;
3、在非叶子节点上右键出现浮动菜单实现新增、修改操作。
------------------------------------------------------------------------------------------------------------------
实现方法如下:
1、搭建测试环境(可以参考前文:【原】无脑操作:IDEA + maven + SpringBoot + JPA + EasyUI实现CRUD及分页)
2、建库建表
DROP TABLE biz_department;
CREATE TABLE biz_department
(
departmentid INT AUTO_INCREMENT PRIMARY KEY COMMENT '部门编号',
departmentpid INT NOT NULL COMMENT '部门父编号',
departmentname VARCHAR(10) NOT NULL COMMENT '部门名称'
) ENGINE=INNODB COMMENT='部门信息';
INSERT INTO biz_department VALUES
(NULL, 0, '总部'),
(NULL, 1, '上海分公司'), (NULL, 1, '安徽分公司'),
(NULL, 3, '合肥办事处'), (NULL, 3, '铜陵办事处');
3、创建实体类 Department.java
@Entity
@Table(name = "biz_department")
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "departmentid")
private Integer departmentid;
@Column(name = "departmentpid")
private Integer departmentpid;
@Column(name = "departmentname")
private String departmentname; public Department() {
} public Department(Integer departmentpid, String departmentname) {
this.departmentpid = departmentpid;
this.departmentname = departmentname;
} public Integer getDepartmentid() {
return departmentid;
} public void setDepartmentid(Integer departmentid) {
this.departmentid = departmentid;
} public Integer getDepartmentpid() {
return departmentpid;
} public void setDepartmentpid(Integer departmentpid) {
this.departmentpid = departmentpid;
} public String getDepartmentname() {
return departmentname;
} public void setDepartmentname(String departmentname) {
this.departmentname = departmentname;
}
}
4、编写DAO接口 DepartmentDao.java
/**
* 因为需要使用分页和条件查询,所以从JpaRepository接口 和 JpaSpecificationExecutor接口继承
*/
public interface DepartmentDao extends JpaRepository<Department, Integer>, JpaSpecificationExecutor<Department> { }
5、编写工具类 TreeNode.java 和 TreeUtil.java
/**
* EasyUI Tree的封装类
*/
public class TreeNode {
private Integer id; // 节点的 id
private Integer parentId; // 父节点id,java生成树时使用
private String text; // 显示的节点文字。
private String iconCls; // 节点图标样式 "iconCls":"icon-save", "iconCls":"icon-ok", 等
private String state; // 节点状态, 'open' 或 'closed',默认是 'open'。当设为 'closed' 时,此节点有子节点,并且将从远程站点加载它们。
private String flag; // 节点类型
private Integer trueId; // 应用系统真实 id
private boolean checked; // 指示节点是否被选中。
private LinkedHashMap<?, ?> attributes; // 给一个节点追加的自定义属性。
private List<TreeNode> children; // 定义了一些子节点的节点数组。
private String url; // 扩展属性url public Integer getTrueId() {
return trueId;
} public void setTrueId(Integer trueId) {
this.trueId = trueId;
} public String getFlag() {
return flag;
} public void setFlag(String flag) {
this.flag = flag;
} public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public Integer getParentId() {
return parentId;
} public void setParentId(Integer parentId) {
this.parentId = parentId;
} public String getText() {
return text;
} public void setText(String text) {
this.text = text;
} public String getIconCls() {
return iconCls;
} public void setIconCls(String iconCls) {
this.iconCls = iconCls;
} public String getState() {
return state;
} public void setState(String state) {
this.state = state;
} public boolean isChecked() {
return checked;
} public void setChecked(boolean checked) {
this.checked = checked;
} public LinkedHashMap<?, ?> getAttributes() {
return attributes;
} public void setAttributes(LinkedHashMap<?, ?> attributes) {
this.attributes = attributes;
} public List<TreeNode> getChildren() {
return children;
} public void setChildren(List<TreeNode> children) {
this.children = children;
} public String getUrl() {
return url;
} public void setUrl(String url) {
this.url = url;
}
}
/**
* 树工具类
*/
public class TreeUtil {
/**
* Tree装配方法
*
* @param tempTreeNodes
* @param treeNodes
* @return
*/
public static List<TreeNode> Assemble(List<TreeNode> tempTreeNodes, List<TreeNode> treeNodes) {
if (tempTreeNodes != null) {
Map<Integer, TreeNode> map = new LinkedHashMap<>();
for (TreeNode tn : tempTreeNodes) {
map.put(tn.getId(), tn);
} TreeNode treeNode;
TreeNode pTreeNode;
for (Integer id : map.keySet()) {
treeNode = map.get(id);
if (treeNode.getParentId() == 0) {
treeNodes.add(treeNode);
} else {
pTreeNode = map.get(treeNode.getParentId());
List<TreeNode> children = pTreeNode.getChildren();
if (children != null) {
children.add(treeNode);
} else {
children = new ArrayList();
children.add(treeNode);
pTreeNode.setChildren(children);
}
}
}
} return treeNodes;
}
}
6、规划控制器 DepartmentController.java
@Controller
@RequestMapping("/department")
public class DepartmentController {
@Autowired
private DepartmentDao departmentDao; @RequestMapping("/view")
public String view() {
// 跳转至【资源管理】页面
return "department";
} @RequestMapping("/tree")
@ResponseBody
public String tree() {
List<Department> list = departmentDao.findAll();
List<TreeNode> tempTreeNodes = new ArrayList();
List<TreeNode> treeNodes = new ArrayList(); // 组装Easyui的Tree必须要有id、parentId、text属性,转换之
for (Department department : list) {
TreeNode tempTreeNode = new TreeNode();
tempTreeNode.setId(department.getDepartmentid());
tempTreeNode.setParentId(department.getDepartmentpid());
tempTreeNode.setText(department.getDepartmentname());
tempTreeNodes.add(tempTreeNode);
} return JSONObject.toJSON(TreeUtil.Assemble(tempTreeNodes, treeNodes)).toString();
} @RequestMapping("/saveNode")
@ResponseBody
public Map<String, Object> saveNode(Integer departmentpid, String departmentname) {
Department model = new Department();
model.setDepartmentpid(departmentpid);
model.setDepartmentname(departmentname); Map<String, Object> resultMap = new HashMap<>();
departmentDao.save(model);
resultMap.put("success", true);
return resultMap;
} @RequestMapping("/updateNode")
@ResponseBody
public Map<String, Object> updateNode(Department model) {
Map<String, Object> resultMap = new HashMap<>();
departmentDao.save(model);
resultMap.put("success", true);
return resultMap;
} @RequestMapping("/deleteNode")
@ResponseBody
public Map<String, Object> deleteNode(Integer departmentid) {
Map<String, Object> resultMap = new HashMap<>();
departmentDao.deleteById(departmentid);
resultMap.put("success", true);
return resultMap;
}
}
7、编写前端代码
HTML页面:department.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>测试Tree功能</title>
<link rel="stylesheet" type="text/css" href="../easyui/themes/default/easyui.css">
<link rel="stylesheet" type="text/css" href="../easyui/themes/icon.css">
<script type="text/javascript" src="../easyui/jquery.min.js"></script>
<script type="text/javascript" src="../easyui/jquery.easyui.min.js"></script>
<script type="text/javascript" src="../easyui/locale/easyui-lang-zh_CN.js"></script>
<script type="text/javascript" src="../biz/department.js"></script>
</head>
<body>
<!-- 部门树 -->
<ul id="deptTree" class="easyui-tree"></ul>
<!-- 叶子节点右键菜单 -->
<div id="leaf" class="easyui-menu" style="width: 120px;">
<div onclick="addNode()" iconcls="icon-add">新增节点</div>
<div onclick="removeNode()" iconcls="icon-remove">删除节点</div>
<div onclick="updateNode()" iconcls="icon-edit">编辑节点</div>
</div>
<!-- 非叶子节点右键菜单 -->
<div id="parentNode" class="easyui-menu" style="width: 120px;">
<div onclick="addNode()" iconcls="icon-add">新增节点</div>
<div onclick="updateNode()" iconcls="icon-edit">编辑节点</div>
</div>
<!-- 节点内容对话框 -->
<div id="info" class="easyui-dialog" style="width:300px; height: 120px;" closed=true>
<form id="treefrm" method="post">
<input type="hidden" name="departmentid">
<table style="margin: auto;" cellspacing="10">
<tr>
<td>部门名称</td>
<td><input class="easyui-textbox" name="departmentname" value="" data-options="required:true"></td>
</tr>
</table>
<div style="text-align: center; bottom: 15px; margin-top: 10px;">
<a id="btnSave" class="easyui-linkbutton"
data-options="iconCls:'icon-save'">保存</a>
<a id="btnCancel" class="easyui-linkbutton"
data-options="iconCls:'icon-cancel'">取消</a>
</div>
</form>
</div>
</body>
</html>
对应JS文件:department.js
// 记录添加还是修改
var flag;
// 临时存储选中节点数据
var tempNode; // 页面加载
$(function () {
// 菜单树绑定数据
$('#deptTree').tree({
url: '/department/tree',
animate: true,
lines: true,
onBeforeSelect: function (node) {
// onBeforeSelect事件:节点被选中前触发,返回false则取消选择动作
if (!$(this).tree('isLeaf', node.target)) {
// 不是叶子节点,则不能选中
return false;
}
},
onClick: function (node) {
// alert(node.target.innerText);
},
onContextMenu: function (e, node) {
// 记录选中的节点,为后续增删改操作提供节点数据
tempNode = node; // 阻止右键默认事件
e.preventDefault(); // 判断该结点有没有父结点
var root = $(this).tree('getParent', node.target);
// 没有父节点则为根结点,可以新增、编辑,不可以删除
if (root == null) {
// 如果是根节点,则可以新增、编辑,不可以删除
$('#parentNode').menu('show', {
left: e.pageX,
top: e.pageY
});
} if ($(this).tree('isLeaf', node.target)) {
// 如果是叶子节点,则可以新增、编辑和删除
$('#leaf').menu('show', {
left: e.pageX,
top: e.pageY
});
} else {
// 如果不是叶子节点,则可以新增、编辑,不可以删除
$('#parentNode').menu('show', {
left: e.pageX,
top: e.pageY
});
}
}
}); // 保存按钮押下处理
$('#btnSave').click(function () {
var tempdata, tempurl, tempmsg; if (flag == 'add') {
tempurl = 'saveNode';
tempmsg = '添加成功!';
tempdata = {
departmentpid: tempNode.id,
departmentname: $('#treefrm').find('input[name=departmentname]').val()
};
} else if (flag == 'edit') {
tempurl = 'updateNode';
tempmsg = '编辑成功!';
tempdata = {
departmentid: $('#treefrm').find('input[name=departmentid]').val(),
departmentpid: $('#deptTree').tree('getParent', tempNode.target).id,
departmentname: $('#treefrm').find('input[name=departmentname]').val()
};
} $.ajax({
type: 'post',
async: true,
url: tempurl,
data: tempdata,
dataType: 'json',
success: function (result) {
// 树重新加载
$('#deptTree').tree('reload'); $.messager.show({
title: '提示信息',
msg: tempmsg
});
},
error: function (result) {
// 请求失败时执行该函数
$.messager.show({
title: '错误信息',
msg: result.msg
});
}
}); $('#treefrm').form('clear');
$('#info').dialog('close');
}); // 取消按钮押下处理
$('#btnCancel').click(function () {
$('#treefrm').form('clear');
$('#info').dialog('close');
});
}); // 新增节点
var addNode = function () {
flag = 'add';
// 清空表单数据
$('#treefrm').form('clear');
// 打开dialog
$('#info').dialog('open').dialog('setTitle', '新增');
}; // 编辑节点
var updateNode = function () {
flag = 'edit';
// 清空表单数据
$('#treefrm').form('clear');
$('#treefrm').form('load', {
departmentid: tempNode.id,
departmentname: tempNode.text
});
// 打开dialog
$('#info').dialog('open').dialog('setTitle', '编辑');
}; // 删除节点
var removeNode = function () {
// 前台删除
$('#deptTree').tree('remove', tempNode.target); // 后台删除
$.ajax({
type: "post",
async: true, // 异步请求(同步请求将会锁住浏览器,用户其他操作必须等待请求完成才可以执行)
url: "deleteNode",
data: {departmentid: tempNode.id},
dataType: "json", // 返回数据形式为json
success: function (result) {
// 请求成功时执行该函数内容,result即为服务器返回的json对象
$.messager.show({
title: '提示信息',
msg: '删除成功!'
});
},
error: function (result) {
// 请求失败时执行该函数
$.messager.show({
title: '错误信息',
msg: result.msg
});
}
});
};
8、运行效果





【原】无脑操作:EasyUI Tree实现左键只选择叶子节点、右键浮动菜单实现增删改的更多相关文章
- EasyUI Combotree只选择叶子节点
EasyUI Combotree的方法拓展自Combo和Tree.而Tree有一个onBeforSelect事件来帮助我们实现只选择叶子节点的功能. Tree事件需要 'node' 参数,它包括下列属 ...
- 【原】无脑操作:IDEA + maven + Shiro + SpringBoot + JPA + Thymeleaf实现基础授权权限
上一篇<[原]无脑操作:IDEA + maven + Shiro + SpringBoot + JPA + Thymeleaf实现基础认证权限>介绍了实现Shiro的基础认证.本篇谈谈实现 ...
- 【原】无脑操作:IDEA + maven + Shiro + SpringBoot + JPA + Thymeleaf实现基础认证权限
开发环境搭建参见<[原]无脑操作:IDEA + maven + SpringBoot + JPA + Thymeleaf实现CRUD及分页> 需求: ① 除了登录页面,在地址栏直接访问其他 ...
- 【原】无脑操作:express + MySQL 实现CRUD
基于node.js的web开发框架express简单方便,很多项目中都在使用.这里结合MySQL数据库,实现最简单的CRUD操作. 开发环境: IDE:WebStorm DB:MySQL ------ ...
- Jquery EasyUI Combotree只能选择叶子节点且叶子节点有多选框
Jquery EasyUI Combotree只能选择叶子节点且叶子节点有多选框 Jquery EasyUI Combotree单选框,Jquery EasyUI Combotree只能选择叶子节点 ...
- 【原】无脑操作:eclipse + maven搭建SSM框架
网上看到一些Spring + Spring MVC + MyBatis框架的搭建教程,不是很详细或是时间久远了,自己动手整一个简单无脑的! 0.系统环境 1)Windows 10 企业版 2)JDK ...
- 【原】无脑操作:ElasticSearch学习笔记(01)
开篇来自于经典的“保安的哲学三问”(你是谁,在哪儿,要干嘛) 问题一.ElasticSearch是什么?有什么用处? 答:截至2018年12月28日,从ElasticSearch官网(https:// ...
- easyui Tree模拟级联勾选cascadeCheck,节点选择,父节点自动选中,节点取消,父节点自动取消选择,节点选择,所有子节点全部选择,节点取消,所有子节点全部取消勾选
最近项目中用到easyui tree,发现tree控件的cascadeCheck有些坑,不像miniui 的tree控件,级联勾选符合业务需求,所以就自己重新改写了onCheck事件,符合业务需求.网 ...
- EasyUI Combotree 只允许选择 叶子节点
$("#SDID").combotree({ url: '/Ajax/GetDeptTree.aspx?level=4&pid=-1', onSelect: functio ...
随机推荐
- 结合JDK源码看设计模式——原型模式
定义: 指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.不需要知道任何创建的细节,不调用构造函数适用场景: 类初始化的时候消耗较多资源 new产生的对象需要非常繁琐的过程 构造函数比较 ...
- Vue基本使用
Vue.js是一款流行的开源JavaScript前端框架,旨在更好地组织与简化Web开发.Vue所关注的核心是MVC模式中的视图层,同时,它也能方便地获取数据更新,并通过组件内部特定的方法实现视图与模 ...
- sql server2005安装时报 ‘服务无法启动’
SQL server服务无法启动的原因分析: 在安装SQL 2005标准版(不多于四个CPU)和企业版(无限制)时,CPU的总核数必须是2的n次方.即核心数为1,2,4,8,16,32依次类推.因BL ...
- Android进阶之光-第1章-Android新特性-读书笔记
第 1 章 Android 新特性 1.1 Android 5.0 新特性 1.1.1 Android 5.0 主要新特性 1. 全新的 Material Design 新风格 Material De ...
- ubuntu 16.04安装perf
ljc@ubuntu:~$ perf 程序“perf”尚未安装. 您可以使用以下命令安装: sudo apt install linux-tools-common ljc@ubuntu:~$ sudo ...
- 团队选题报告(bull beer)
一.团队成员及分工 团队名称:bull beer 团队成员: 黄文东:选题报告word撰写 沈培:原型设计,博客撰写,ppt制作 邓泽中:住院 刘帅:查找相关资料 二.选题报告内容 项目名称:学费管理 ...
- 从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 二十║Vue基础终篇:传值+组件+项目说明
缘起 新的一天又开始啦,大家也应该看到我的标题了,是滴,Vue基础基本就到这里了,咱们回头看看这一路,如果你都看了,并且都会写了,那么现在你就可以自己写一个Demo了,如果再了解一点路由,ajax请求 ...
- BannerDemo【图片轮播图控件】
版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 这里简单记录下一个开源库youth5201314/banner的运用.具体用法请阅读<youth5201314/banner& ...
- vue.js移动端配置flexible.js
前言 最近在用vue做移动端项目,网上找了一些移动端适配的方案,个人觉得手淘团队flexible.js还是比较容易上手,在这里做下总结. 主体 flexible.js适配方案采用rem布局,根据屏幕分 ...
- 【憩园】C#并发编程之异步编程(三)
写在前面 本篇是异步编程系列的第三篇,本来计划第三篇的内容是介绍异步编程中常用的几个方法,但是前两篇写出来后,身边的朋友总是会有其他问题,所以决定再续写一篇,作为异步编程(一)和异步编程(二)的补 ...