前两天用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>