前兩天用kendoUI的treeView做了一個給用戶角色分配菜單權限的功能,廢了老大的勁,寫來說明一下,
做出效果如圖:
業務流程:點擊”分配菜單權限“按鈕,彈出一個新的window,並顯示出所有的菜單選項,菜單分子父兩級,因為這里是修改權限,
顯示時,根據行選的用戶已有權限需要默認打鈎,點擊“提交查詢”按鈕,form提交,根據勾選情況,入庫。
下面說說一下實現代碼:
1,首先,自定義分配菜單權限按鈕,grid 右邊一欄為command,
command : [ { text : "分配菜單權限", click: function(e) { // e.target is the DOM element representing the button var tr = $(e.target).closest("tr"); // get the current table row (tr) // get the data bound to the current table row var data = this.dataItem(tr); showTree(data.id).data("kendoWindow").open();
}
}, { name : "edit", text : { edit : "信息修改", cancel : "關閉", update : "提交" } }, { name : "destroy", text : "刪除" } ],
解釋:通過var tr = $(e.target).closest("tr"); var data = this.dataItem(tr); 獲得的data對應每一行的一跳數據,格式是json,幾對應后台的JavaBean
並將沒一行的主鍵可獲取 以作為標示,這里data.id 即為點擊對應那一行的role的id
2,彈出窗口,展示菜單menu樹形結構: showTree(data.id).data("kendoWindow").open();
showTree是一個方法 如下:
function showTree(roleId){ return $("#menuTree").kendoWindow({ width: "300px", height : "300px", position: { top: 100, left: 500 }, title: "分配菜單權限", visible: false, content: 'role/menuTreePage.do?roleId='+roleId, activate : function(){ }, close : function(){ console.log("清除樹"); $("#treeview").data("kendoTreeView").destroy(); } }); }
menuTree 為在此grid 的jsp中定義的一個占位div
<div id="menuTree"></div>
即通過這個div生成一個彈窗kendoWindow,而這個彈窗中的內容實際上顯示的是另外一個jsp頁面rolemenu.jsp,content: 'role/menuTreePage.do?roleId='+roleId,
通過這個屬性的配置將另一個jsp中的內容包裹到kendoWindow中,rolemenu.jsp中真正的菜單menu的樹形結構
3,樹形結構的配置
<body> <div id="treeview" class="demo-section"></div> <form id="treeForm" action="<spring:url value='/role/assignMenus.do' />" method="post" onsubmit="return false;"> <input type="hidden" name="menuIds" id="result" /> <input type="hidden" name="roleId" id="roleId" value="${roleId}"/> <input id="formSub" type="submit" /> </form> <script type="text/javascript"> $(function() { var homogeneous = new kendo.data.HierarchicalDataSource({ transport : { read : { url : "role/menuTree.do", dataType : "json", data: function() { var uproleId= $("#roleId").val(); return {roleId:uproleId} } } }, schema : { model : { id : "id", hasChildren : "hasChildren", children:"items", expanded : true, checked : "checked" }, } }); $("#treeview").kendoTreeView({ //loadOnDemand : false,// 延遲加載,默認是true,這里設置false是因為在初始化閉合的時候,如果選中根元素,是不法獲取子節點id的 dataSource : homogeneous, dataTextField : "name", checkboxes : { checkChildren : true } }); var options = { success : function(data) { console.log(data); //location.href = "http://localhost:8085/csop_monitor/index.do"; $("#menuTree").data("kendoWindow").close(); } }; $("#formSub").click(function() { var result = $("#result").val(); var oldRs = $("#oldResult").val(); if(result == ""){ $("#menuTree").data("kendoWindow").close(); return false; }else{ $("#result").val(result); } //alert("re="+result); $("#treeForm").ajaxSubmit(options); }); // show checked node IDs on datasource change $("#treeview").data("kendoTreeView").dataSource.bind("change", getChangeValue); function getChangeValue(){ var checkedNodes = [], treeView = $("#treeview").data("kendoTreeView"), message; checkedNodeIds(treeView.dataSource.view(), checkedNodes); if (checkedNodes.length > 0) message = checkedNodes.join(","); $("#result").val(message); console.log("改變后result : " + $("#result").val()); } //function that gathers IDs of checked nodes function checkedNodeIds(nodes, checkedNodes) { for (var i = 0; i < nodes.length; i++) { if (nodes[i].checked) { console.log("checked node : " + nodes[i].id); checkedNodes.push(nodes[i].id); } if (nodes[i].hasChildren) { checkedNodeIds(nodes[i].children.view(), checkedNodes); } } } }); </script> </body>
treeView的完成,首先定義個個datasource,我這里定義了一個 HierarchicalDataSource,這個本身就是一個樹形結構的datasouce
值得注意的是schema的配置
schema : { model : { id : "id", hasChildren : "hasChildren", children:"items", expanded : true, checked : "checked" }, }
hasChildren,children,checked 都對應后台傳遞過來的javabean中的屬性
checked,hasChildren 為Boolean類型
children 如果有,會自動生成一個樹形,但是這個有一個問題就是JavaBean中必須定義為items,否則treeview找不到,我不知道是不是treeview那邊還需要配置一個屬性。
checked 由於我的樹形前有checkbox,如果未true,kendo也會自動選中,
然后就簡單了 ,同樣通過一個div來顯示treeview
<div id="treeview" class="demo-section"></div>
$("#treeview").kendoTreeView...
這樣就可以顯示了,后台對menus的操作等下再說
我這里還定義了一個form,用於提交,用戶進行選擇,操作之后的數據保存,通過tree的change事件保存下所選的menuIds,即可,
通過這個方法 getChangeValue,這里不再贅述。
最后:后台獲取menus
思路:首先抓取所有的menus,再通過roleID查詢這個角色所擁有的menu權限找到對應的menu,將checked 設為true
@RequestMapping("/menuTree.do") @ResponseBody public List<SysMenuDto> menuListTree(@RequestParam(value = "roleId", required = false) String roleId) { List<SysMenuDto> menuDtos = roleService.getRoleMenuTreeForUpdate(roleId); return menuDtos; }
@Override public List<SysMenuDto> getRoleMenuTreeForUpdate(String roleId) { Role role=roleDao.get(roleId, Role.class); List<SysMenu> roleRoleMenus = new ArrayList<SysMenu>(); Iterator<RoleMenuMap> it = role.getRoleMenuMaps().iterator(); while (it.hasNext()) { RoleMenuMap roleMenuMap = it.next(); roleRoleMenus.add(roleMenuMap.getMenu()); } List<SysMenu> menus=menuDao.listRootMenus(); List<SysMenuDto> menuDtos=new ArrayList<>(); for (SysMenu SysMenu : menus) { SysMenuDto menuDto=new SysMenuDto().convertToDto(SysMenu); if(roleRoleMenus.contains(SysMenu)){ menuDto.setChecked(true); }else{ menuDto.setChecked(false); } List<SysMenu> chiledMenus=SysMenu.getChildren(); if(chiledMenus!=null&&chiledMenus.size()>0){ for(int i=0;i<chiledMenus.size();i++){ SysMenu childMenu=chiledMenus.get(i); if(roleRoleMenus.contains(childMenu)){ menuDto.getItems().get(i).setChecked(true); }else{ menuDto.getItems().get(i).setChecked(false); } } } menuDtos.add(menuDto); } return menuDtos; }
menuDto
private String name; private String url; private String desc; private int order; private String createdTime; private String parentId; private String parentName; private List<SysMenuDto> items = new ArrayList<>(); private boolean hasChildren; private boolean checked; @Override public SysMenuDto convert(SysMenu o) { this.setId(o.getId()); this.setCreatedTime(DateUtils.date2String(o.getCreatedTime())); this.setDesc(o.getDesc()); this.setName(o.getName()); this.setUrl(o.getUrl()); this.setOrder(o.getOrder()); this.setParentId(o.getParent() == null ? null : o.getParent().getId()); this.setParentName(o.getParent() == null ? "無" : o.getParent().getName()); List<SysMenu> sysMenus = o.getChildren(); for (SysMenu sysMenu : sysMenus) { items.add(new SysMenuDto().convert(sysMenu)); } this.hasChildren = sysMenus != null && sysMenus.size() > 0 ? true : false; return this; } public SysMenu convertToEntity(SysMenu menu) { //menu.setId(this.getId()); menu.setDesc(this.desc); menu.setName(this.name); menu.setUrl(this.url); menu.setOrder(this.order); //menu.setParent(parent); //menu.setChildren(children); return menu; } //getter and setter .....
附:兩個完整jsp代碼
role.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <%@ taglib prefix='spring' uri="http://www.springframework.org/tags" %> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <head> <script src="<spring:url value='/resources/js/plugins/jquery.form.js' />"></script> <script type="text/javascript"> $(function() { var prefix = "role"; var dataSource = new kendo.data.DataSource({ type: "odata", pageSize : 20, serverPaging: true,//服務器端是否進行分頁查詢 serverFiltering: true, transport : { read : { url : prefix + '/showRoles.do', dataType : "json", }, update: { url: prefix + '/save.do', dataType: "json", contentType:"application/x-www-form-urlencoded", type : "post", }, destroy: { url: prefix + '/delete.do', contentType:"application/x-www-form-urlencoded", type : "post", }, create: { url: prefix + '/save.do', dataType: "json", contentType:"application/x-www-form-urlencoded", type : "post", }, parameterMap: function(data, type) { if (type !== "read" && data) { // return kendo.stringify(data); } return data; } }, schema : { model : { id : "id", fields : { id : {type : "string"}, name : {type : "string"}, status : { type : "string", defaultValue:"激活" }, desc : {type : "string"}, } } }, //after create update destory. reload the datasource(reflesh page) requestEnd: function(e) { var response = e.response; var type = e.type; if(type != "read"){ this.read(); } } }); $("#grid").kendoGrid({ dataSource : dataSource, sortable : false, selectable : "multiple",// 多選 height : 500, navigatable: true, editable: true, toolbar : [ { name : "create", text : "新增角色" } ], pageable : { pageSize : 20,// 一頁顯示多少行數據 previousNext : true,// 是否允許有上一頁、下一頁、首頁、尾頁摁扭 numeric : true,// 是否顯示翻頁處的頁數按鈕 buttonCount : 5,// 限制頁數按鈕的顯示個數 input : false,// 是否顯示輸入頁數的文本框 refresh : true,// 是否允許刷新頁面 pageSizes : true,// 是否允許調整一頁顯示的行數,可設置[5, 10, 15] messages : { display : "顯示 {0}-{1} 條數據 總共 {2} 條數據", empty : "沒有數據", itemsPerPage : "選擇顯示行數", refresh : "刷新", previous : "上一頁", next : "下一頁", last : "尾頁", first : "首頁" } }, columns : [ // 顯示列定義 { field : "name", width : 100, title : "角色名" }, { field : "desc", width : 120, title : "描述" }, { field : "status", width : 80, title : "啟動狀態", editor : activeDownEditor, template : "#=status#" }, { command : [ { text : "分配菜單權限", click: function(e) { // e.target is the DOM element representing the button var tr = $(e.target).closest("tr"); // get the current table row (tr) // get the data bound to the current table row var data = this.dataItem(tr); var menuIds = []; console.log(data); if(data.roleMenuMapDtos != null) for(var i = 0; i < data.roleMenuMapDtos.length; i++){ menuIds.push(data.roleMenuMapDtos[i].menu.id); } showTree(data.id, menuIds).data("kendoWindow").open(); } }, { name : "edit", text : { edit : "信息修改", cancel : "關閉", update : "提交" } }, { name : "destroy", text : "刪除" } ], title : "操作", width : "160px" } ], editable : {// 設置可以在列表中進行編輯數據 // 設置刪除時顯示的確認信息 confirmation : "您確定要進行刪除操作嗎?", destroy : true,// 不允許刪除 mode : "popup",// 設置編輯形式為彈出框(popup)還是在列表中(inline) //template: kendo.template($("#editTemplate").html())//設置彈出框中加載的內容,設置此項mode必須是popup }, groupable : false }); var data = [ { text: "激活", value: "激活" }, { text: "凍結", value: "凍結" } ]; function activeDownEditor(container, options){ // console.log(options); var status = options.model.status; var rolestatusDroplist=$('<input required data-text-field="text" data-value-field="value" data-bind="value:' + options.field + '"/>') .appendTo(container).kendoDropDownList({ dataTextField: "text", dataValueField: "value", dataSource : data }); rolestatusDroplist.data("kendoDropDownList").select(function(dataItem) { return dataItem.text === status; }); } function showTree(roleId, menuIds){ return $("#menuTree").kendoWindow({ width: "300px", height : "300px", position: { top: 100, left: 500 }, title: "分配菜單權限", visible: false, content: 'role/menuTreePage.do?roleId='+roleId, activate : function(){ $("#roleId").val(roleId); //var treeview = $("#treeview").data("kendoTreeView"); //console.log("role.reosurce : " + menuIds); //var nodes = treeview.dataSource.view(); //if(menuIds.length > 0) //checkedNodes(nodes, menuIds); //treeview.updateIndeterminate(); //if (checkedNodes.length > 0) // message = menuIds.join(","); // $("#oldResult").val(message); // console.log("roleId : " + $("#roleId").val() + " oldresult : " + $("#oldResult").val()); }, close : function(){ console.log("清除樹"); $("#treeview").data("kendoTreeView").destroy(); } }); } function checkedNodes(nodes, checkedNodesID) { //console.log("nodes=---"+nodes); for (var i = 0; i < checkedNodesID.length; i++) { for(var j = 0; j < nodes.length; j++){ //console.log("node id : " + nodes[j].id + " checked Id : " + checkedNodesID[i]); if(nodes[j].id == checkedNodesID[i]){ nodes[j].checked = true; console.log("node.id : " + nodes[j].id + " node.name : " + nodes[j].name + " is checked : " + nodes[j].checked); // console.log(nodes[j].name + " " + nodes[j].hasChildren); if(nodes[j].hasChildren) checkedNodes(nodes[j].children.view(),checkedNodesID); }else{ nodes[j].checked = false; } } } } /* function chilrenChecked(children){ for(var i = 0; i < children.length; i++){ children[i].checked = true; console.log("children name : " + children[i].name + " checked : " + children[i].checked); } }*/ //for rolemenu }); </script> </head> <body> <div id="menuTree"></div> <div id="example" class="k-content"> <div id="clientsDb"> <div id="grid" style="height: 380px"></div> </div> </div> <div id="dialog"></div> </body>
rolemenu.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <%@ taglib prefix='spring' uri="http://www.springframework.org/tags" %> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <head> <script src="<spring:url value='/resources/js/plugins/jquery.form.js' />"></script> </head> <body> <div id="treeview" class="demo-section"></div> <form id="treeForm" action="<spring:url value='/role/assignMenus.do' />" method="post" onsubmit="return false;"> <input type="hidden" name="menuIds" id="result" /> <input type="hidden" name="roleId" id="roleId" value="${roleId}"/> <input id="formSub" type="submit" /> </form> <script type="text/javascript"> $(function() { var homogeneous = new kendo.data.HierarchicalDataSource({ transport : { read : { url : "role/menuTree.do", dataType : "json", data: function() { var uproleId= $("#roleId").val(); return {roleId:uproleId} } } }, schema : { model : { id : "id", hasChildren : "hasChildren", children:"items", expanded : true, checked : "checked" }, } }); $("#treeview").kendoTreeView({ //loadOnDemand : false,// 延遲加載,默認是true,這里設置false是因為在初始化閉合的時候,如果選中根元素,是不法獲取子節點id的 dataSource : homogeneous, dataTextField : "name", checkboxes : { checkChildren : true } }); var options = { success : function(data) { console.log(data); //location.href = "http://localhost:8085/csop_monitor/index.do"; $("#menuTree").data("kendoWindow").close(); } }; $("#formSub").click(function() { var result = $("#result").val(); var oldRs = $("#oldResult").val(); if(result == ""){ $("#menuTree").data("kendoWindow").close(); return false; }else{ $("#result").val(result); } //alert("re="+result); $("#treeForm").ajaxSubmit(options); }); // show checked node IDs on datasource change $("#treeview").data("kendoTreeView").dataSource.bind("change", getChangeValue); function getChangeValue(){ //console.log("改變前result : " + $("#oldResult").val()); var checkedNodes = [], treeView = $("#treeview").data("kendoTreeView"), message; //console.log("treeView : " + treeView.dataSource.view().length > 0); checkedNodeIds(treeView.dataSource.view(), checkedNodes); //console.log(checkedNodes); if (checkedNodes.length > 0) message = checkedNodes.join(","); //alert("message="+message); $("#result").val(message); console.log("改變后result : " + $("#result").val()); } //function that gathers IDs of checked nodes function checkedNodeIds(nodes, checkedNodes) { for (var i = 0; i < nodes.length; i++) { // console.log(nodes[i]); // console.log(nodes[i].checked); if (nodes[i].checked) { console.log("checked node : " + nodes[i].id); checkedNodes.push(nodes[i].id); } if (nodes[i].hasChildren) { checkedNodeIds(nodes[i].children.view(), checkedNodes); } // console.log("node.id : " + nodes[i].id + " node.name : " + // nodes[i].name); } } }); </script> </body>