【原】无脑操作:EasyUI Tree实现左键只选择叶子节点、右键浮动菜单实现增删改

Easyui中的Tree组件使用频率颇高,经常遇到的需求如下:

1、在树形结构上,只有叶子节点才能被选中,其他节点不能被选中;

2、在叶子节点上右键出现浮动菜单实现新增、删除、修改操作;

3、在非叶子节点上右键出现浮动菜单实现新增、修改操作。

------------------------------------------------------------------------------------------------------------------

实现方法如下:

1、搭建测试环境(可以参考前文:【原】无脑操作:IDEA + maven + SpringBoot + JPA + EasyUI实现CRUD及分页

2、建库建表

 1 DROP TABLE biz_department;
 2 CREATE TABLE biz_department
 3 (
 4     departmentid INT AUTO_INCREMENT PRIMARY KEY COMMENT '部门编号',
 5     departmentpid INT NOT NULL COMMENT '部门父编号',
 6     departmentname VARCHAR(10) NOT NULL COMMENT '部门名称'
 7 ) ENGINE=INNODB COMMENT='部门信息';
 8 INSERT INTO biz_department VALUES
 9 (NULL, 0, '总部'),
10 (NULL, 1, '上海分公司'), (NULL, 1, '安徽分公司'),
11 (NULL, 3, '合肥办事处'), (NULL, 3, '铜陵办事处');

3、创建实体类 Department.java

 1 @Entity
 2 @Table(name = "biz_department")
 3 public class Department {
 4     @Id
 5     @GeneratedValue(strategy = GenerationType.IDENTITY)
 6     @Column(name = "departmentid")
 7     private Integer departmentid;
 8     @Column(name = "departmentpid")
 9     private Integer departmentpid;
10     @Column(name = "departmentname")
11     private String departmentname;
12 
13     public Department() {
14     }
15 
16     public Department(Integer departmentpid, String departmentname) {
17         this.departmentpid = departmentpid;
18         this.departmentname = departmentname;
19     }
20 
21     public Integer getDepartmentid() {
22         return departmentid;
23     }
24 
25     public void setDepartmentid(Integer departmentid) {
26         this.departmentid = departmentid;
27     }
28 
29     public Integer getDepartmentpid() {
30         return departmentpid;
31     }
32 
33     public void setDepartmentpid(Integer departmentpid) {
34         this.departmentpid = departmentpid;
35     }
36 
37     public String getDepartmentname() {
38         return departmentname;
39     }
40 
41     public void setDepartmentname(String departmentname) {
42         this.departmentname = departmentname;
43     }
44 }

4、编写DAO接口 DepartmentDao.java

1 /**
2  * 因为需要使用分页和条件查询,所以从JpaRepository接口 和 JpaSpecificationExecutor接口继承
3  */
4 public interface DepartmentDao extends JpaRepository<Department, Integer>, JpaSpecificationExecutor<Department> {
5 
6 }

5、编写工具类 TreeNode.java 和  TreeUtil.java

  1 /**
  2  * EasyUI Tree的封装类
  3  */
  4 public class TreeNode {
  5     private Integer id;                      // 节点的 id
  6     private Integer parentId;                // 父节点id,java生成树时使用
  7     private String text;                    // 显示的节点文字。
  8     private String iconCls;                 // 节点图标样式    "iconCls":"icon-save",  "iconCls":"icon-ok", 等
  9     private String state;                   // 节点状态, 'open' 或 'closed',默认是 'open'。当设为 'closed' 时,此节点有子节点,并且将从远程站点加载它们。
 10     private String flag;                    // 节点类型
 11     private Integer trueId;                 // 应用系统真实 id
 12     private boolean checked;                // 指示节点是否被选中。
 13     private LinkedHashMap<?, ?> attributes; // 给一个节点追加的自定义属性。
 14     private List<TreeNode> children;        // 定义了一些子节点的节点数组。
 15     private String url;                     // 扩展属性url
 16 
 17     public Integer getTrueId() {
 18         return trueId;
 19     }
 20 
 21     public void setTrueId(Integer trueId) {
 22         this.trueId = trueId;
 23     }
 24 
 25     public String getFlag() {
 26         return flag;
 27     }
 28 
 29     public void setFlag(String flag) {
 30         this.flag = flag;
 31     }
 32 
 33     public Integer getId() {
 34         return id;
 35     }
 36 
 37     public void setId(Integer id) {
 38         this.id = id;
 39     }
 40 
 41     public Integer getParentId() {
 42         return parentId;
 43     }
 44 
 45     public void setParentId(Integer parentId) {
 46         this.parentId = parentId;
 47     }
 48 
 49     public String getText() {
 50         return text;
 51     }
 52 
 53     public void setText(String text) {
 54         this.text = text;
 55     }
 56 
 57     public String getIconCls() {
 58         return iconCls;
 59     }
 60 
 61     public void setIconCls(String iconCls) {
 62         this.iconCls = iconCls;
 63     }
 64 
 65     public String getState() {
 66         return state;
 67     }
 68 
 69     public void setState(String state) {
 70         this.state = state;
 71     }
 72 
 73     public boolean isChecked() {
 74         return checked;
 75     }
 76 
 77     public void setChecked(boolean checked) {
 78         this.checked = checked;
 79     }
 80 
 81     public LinkedHashMap<?, ?> getAttributes() {
 82         return attributes;
 83     }
 84 
 85     public void setAttributes(LinkedHashMap<?, ?> attributes) {
 86         this.attributes = attributes;
 87     }
 88 
 89     public List<TreeNode> getChildren() {
 90         return children;
 91     }
 92 
 93     public void setChildren(List<TreeNode> children) {
 94         this.children = children;
 95     }
 96 
 97     public String getUrl() {
 98         return url;
 99     }
100 
101     public void setUrl(String url) {
102         this.url = url;
103     }
104 }
 1 /**
 2  * 树工具类
 3  */
 4 public class TreeUtil {
 5     /**
 6      * Tree装配方法
 7      *
 8      * @param tempTreeNodes
 9      * @param treeNodes
10      * @return
11      */
12     public static List<TreeNode> Assemble(List<TreeNode> tempTreeNodes, List<TreeNode> treeNodes) {
13         if (tempTreeNodes != null) {
14             Map<Integer, TreeNode> map = new LinkedHashMap<>();
15             for (TreeNode tn : tempTreeNodes) {
16                 map.put(tn.getId(), tn);
17             }
18 
19             TreeNode treeNode;
20             TreeNode pTreeNode;
21             for (Integer id : map.keySet()) {
22                 treeNode = map.get(id);
23                 if (treeNode.getParentId() == 0) {
24                     treeNodes.add(treeNode);
25                 } else {
26                     pTreeNode = map.get(treeNode.getParentId());
27                     List<TreeNode> children = pTreeNode.getChildren();
28                     if (children != null) {
29                         children.add(treeNode);
30                     } else {
31                         children = new ArrayList();
32                         children.add(treeNode);
33                         pTreeNode.setChildren(children);
34                     }
35                 }
36             }
37         }
38 
39         return treeNodes;
40     }
41 }

6、规划控制器 DepartmentController.java

 1 @Controller
 2 @RequestMapping("/department")
 3 public class DepartmentController {
 4     @Autowired
 5     private DepartmentDao departmentDao;
 6 
 7     @RequestMapping("/view")
 8     public String view() {
 9         // 跳转至【资源管理】页面
10         return "department";
11     }
12 
13     @RequestMapping("/tree")
14     @ResponseBody
15     public String tree() {
16         List<Department> list = departmentDao.findAll();
17         List<TreeNode> tempTreeNodes = new ArrayList();
18         List<TreeNode> treeNodes = new ArrayList();
19 
20         // 组装Easyui的Tree必须要有id、parentId、text属性,转换之
21         for (Department department : list) {
22             TreeNode tempTreeNode = new TreeNode();
23             tempTreeNode.setId(department.getDepartmentid());
24             tempTreeNode.setParentId(department.getDepartmentpid());
25             tempTreeNode.setText(department.getDepartmentname());
26             tempTreeNodes.add(tempTreeNode);
27         }
28 
29         return JSONObject.toJSON(TreeUtil.Assemble(tempTreeNodes, treeNodes)).toString();
30     }
31 
32     @RequestMapping("/saveNode")
33     @ResponseBody
34     public Map<String, Object> saveNode(Integer departmentpid, String departmentname) {
35         Department model = new Department();
36         model.setDepartmentpid(departmentpid);
37         model.setDepartmentname(departmentname);
38 
39         Map<String, Object> resultMap = new HashMap<>();
40         departmentDao.save(model);
41         resultMap.put("success", true);
42         return resultMap;
43     }
44 
45     @RequestMapping("/updateNode")
46     @ResponseBody
47     public Map<String, Object> updateNode(Department model) {
48         Map<String, Object> resultMap = new HashMap<>();
49         departmentDao.save(model);
50         resultMap.put("success", true);
51         return resultMap;
52     }
53 
54     @RequestMapping("/deleteNode")
55     @ResponseBody
56     public Map<String, Object> deleteNode(Integer departmentid) {
57         Map<String, Object> resultMap = new HashMap<>();
58         departmentDao.deleteById(departmentid);
59         resultMap.put("success", true);
60         return resultMap;
61     }
62 }

7、编写前端代码

HTML页面:department.html

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>测试Tree功能</title>
 6     <link rel="stylesheet" type="text/css" href="../easyui/themes/default/easyui.css">
 7     <link rel="stylesheet" type="text/css" href="../easyui/themes/icon.css">
 8     <script type="text/javascript" src="../easyui/jquery.min.js"></script>
 9     <script type="text/javascript" src="../easyui/jquery.easyui.min.js"></script>
10     <script type="text/javascript" src="../easyui/locale/easyui-lang-zh_CN.js"></script>
11     <script type="text/javascript" src="../biz/department.js"></script>
12 </head>
13 <body>
14 <!-- 部门树 -->
15 <ul id="deptTree" class="easyui-tree"></ul>
16 <!-- 叶子节点右键菜单 -->
17 <div id="leaf" class="easyui-menu" style="width: 120px;">
18     <div onclick="addNode()" iconcls="icon-add">新增节点</div>
19     <div onclick="removeNode()" iconcls="icon-remove">删除节点</div>
20     <div onclick="updateNode()" iconcls="icon-edit">编辑节点</div>
21 </div>
22 <!-- 非叶子节点右键菜单 -->
23 <div id="parentNode" class="easyui-menu" style="width: 120px;">
24     <div onclick="addNode()" iconcls="icon-add">新增节点</div>
25     <div onclick="updateNode()" iconcls="icon-edit">编辑节点</div>
26 </div>
27 <!-- 节点内容对话框 -->
28 <div id="info" class="easyui-dialog" style="width:300px; height: 120px;" closed=true>
29     <form id="treefrm" method="post">
30         <input type="hidden" name="departmentid">
31         <table style="margin: auto;" cellspacing="10">
32             <tr>
33                 <td>部门名称</td>
34                 <td><input class="easyui-textbox" name="departmentname" value="" data-options="required:true"></td>
35             </tr>
36         </table>
37         <div style="text-align: center; bottom: 15px; margin-top: 10px;">
38             <a id="btnSave" class="easyui-linkbutton"
39                data-options="iconCls:'icon-save'">保存</a>
40             <a id="btnCancel" class="easyui-linkbutton"
41                data-options="iconCls:'icon-cancel'">取消</a>
42         </div>
43     </form>
44 </div>
45 </body>
46 </html>

对应JS文件:department.js

  1 // 记录添加还是修改
  2 var flag;
  3 // 临时存储选中节点数据
  4 var tempNode;
  5 
  6 // 页面加载
  7 $(function () {
  8     // 菜单树绑定数据
  9     $('#deptTree').tree({
 10         url: '/department/tree',
 11         animate: true,
 12         lines: true,
 13         onBeforeSelect: function (node) {
 14             // onBeforeSelect事件:节点被选中前触发,返回false则取消选择动作
 15             if (!$(this).tree('isLeaf', node.target)) {
 16                 // 不是叶子节点,则不能选中
 17                 return false;
 18             }
 19         },
 20         onClick: function (node) {
 21             // alert(node.target.innerText);
 22         },
 23         onContextMenu: function (e, node) {
 24             // 记录选中的节点,为后续增删改操作提供节点数据
 25             tempNode = node;
 26 
 27             // 阻止右键默认事件
 28             e.preventDefault();
 29 
 30             // 判断该结点有没有父结点
 31             var root = $(this).tree('getParent', node.target);
 32             // 没有父节点则为根结点,可以新增、编辑,不可以删除
 33             if (root == null) {
 34                 // 如果是根节点,则可以新增、编辑,不可以删除
 35                 $('#parentNode').menu('show', {
 36                     left: e.pageX,
 37                     top: e.pageY
 38                 });
 39             }
 40 
 41             if ($(this).tree('isLeaf', node.target)) {
 42                 // 如果是叶子节点,则可以新增、编辑和删除
 43                 $('#leaf').menu('show', {
 44                     left: e.pageX,
 45                     top: e.pageY
 46                 });
 47             } else {
 48                 // 如果不是叶子节点,则可以新增、编辑,不可以删除
 49                 $('#parentNode').menu('show', {
 50                     left: e.pageX,
 51                     top: e.pageY
 52                 });
 53             }
 54         }
 55     });
 56 
 57     // 保存按钮押下处理
 58     $('#btnSave').click(function () {
 59         var tempdata, tempurl, tempmsg;
 60 
 61         if (flag == 'add') {
 62             tempurl = 'saveNode';
 63             tempmsg = '添加成功!';
 64             tempdata = {
 65                 departmentpid: tempNode.id,
 66                 departmentname: $('#treefrm').find('input[name=departmentname]').val()
 67             };
 68         } else if (flag == 'edit') {
 69             tempurl = 'updateNode';
 70             tempmsg = '编辑成功!';
 71             tempdata = {
 72                 departmentid: $('#treefrm').find('input[name=departmentid]').val(),
 73                 departmentpid: $('#deptTree').tree('getParent', tempNode.target).id,
 74                 departmentname: $('#treefrm').find('input[name=departmentname]').val()
 75             };
 76         }
 77 
 78         $.ajax({
 79             type: 'post',
 80             async: true,
 81             url: tempurl,
 82             data: tempdata,
 83             dataType: 'json',
 84             success: function (result) {
 85                 // 树重新加载
 86                 $('#deptTree').tree('reload');
 87 
 88                 $.messager.show({
 89                     title: '提示信息',
 90                     msg: tempmsg
 91                 });
 92             },
 93             error: function (result) {
 94                 // 请求失败时执行该函数
 95                 $.messager.show({
 96                     title: '错误信息',
 97                     msg: result.msg
 98                 });
 99             }
100         });
101 
102         $('#treefrm').form('clear');
103         $('#info').dialog('close');
104     });
105 
106     // 取消按钮押下处理
107     $('#btnCancel').click(function () {
108         $('#treefrm').form('clear');
109         $('#info').dialog('close');
110     });
111 });
112 
113 // 新增节点
114 var addNode = function () {
115     flag = 'add';
116     // 清空表单数据
117     $('#treefrm').form('clear');
118     // 打开dialog
119     $('#info').dialog('open').dialog('setTitle', '新增');
120 };
121 
122 // 编辑节点
123 var updateNode = function () {
124     flag = 'edit';
125     // 清空表单数据
126     $('#treefrm').form('clear');
127     $('#treefrm').form('load', {
128         departmentid: tempNode.id,
129         departmentname: tempNode.text
130     });
131     // 打开dialog
132     $('#info').dialog('open').dialog('setTitle', '编辑');
133 };
134 
135 // 删除节点
136 var removeNode = function () {
137     // 前台删除
138     $('#deptTree').tree('remove', tempNode.target);
139 
140     // 后台删除
141     $.ajax({
142         type: "post",
143         async: true,           // 异步请求(同步请求将会锁住浏览器,用户其他操作必须等待请求完成才可以执行)
144         url: "deleteNode",
145         data: {departmentid: tempNode.id},
146         dataType: "json",      // 返回数据形式为json
147         success: function (result) {
148             // 请求成功时执行该函数内容,result即为服务器返回的json对象
149             $.messager.show({
150                 title: '提示信息',
151                 msg: '删除成功!'
152             });
153         },
154         error: function (result) {
155             // 请求失败时执行该函数
156             $.messager.show({
157                 title: '错误信息',
158                 msg: result.msg
159             });
160         }
161     });
162 };

8、运行效果

posted @ 2018-07-26 10:16  {name:"代码屠夫"}  阅读(1826)  评论(2编辑  收藏  举报