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、運行效果