请稍等 ...
×

采纳答案成功!

向帮助你的同学说点啥吧!感谢那些助人为乐的人

希望老师把根据权限动态加载菜单功能完善下

正在回答 回答被采纳积分+3

3回答

Jimin 2017-12-16 10:26:18

你好,动态处理菜单的代码写好了。现在具体说一下:

1、前提要求及设定:

1)由于目前项目使用的模板最多适合展示两层,因此我们这里要求配置权限模块时最多两级(尽管代码里是支持无限层级的,代码那样实现是为了支撑特别复杂的情形,并且容易扩展)

2)要求每个模块下最多设置一个菜单权限点,相当于映射到菜单时,代表菜单每一项点击时最多只有一个url被响应(如果配置多个,我们代码里只会选择第一项)

3)同一个菜单,如果有下级菜单,那么当前层级就不再配置菜单权限点了。同理,如果一个菜单配置了菜单权限点,就不再去关心他的下级菜单了,因为这个菜单被选择时,已经要做跳转操作了。这里主要是方便前端ui展示及配合ui模板处理,太复杂的情况另说。

2、接下来说下后端代码,

SysUserController.java下包装一个查询菜单接口:

@RequestMapping("/menu.json")
@ResponseBody
public JsonData menuTree() {
   List<AclModuleLevelDto> aclModuleLevelDtoList = sysTreeService.userAclTree(RequestHolder.getCurrentUser().getId());
   return JsonData.success(sysMenuService.changeTreeToMenu(aclModuleLevelDtoList));
}

userAclTree方法是现成的,适配成一个简单的menu结构返回

使用到的适配方法和类如下:

@Getter
@Setter
public class Menu {

   private Integer id;

   // 菜单展示的名称
   private String name;

   // 菜单点击跳转的链接
   private String url;

   // 下级菜单, 如果存在url里,就不存在下级菜单了
   private List<Menu> subList = Lists.newArrayList();

   public void addSubMenu(Menu menu) {
       if (menu != null) {
           subList.add(menu);
       }
   }
}

@Service
public class SysMenuService {

   public List<Menu> changeTreeToMenu(List<AclModuleLevelDto> aclModuleLevelDtoList) {
       List<Menu> menuList = Lists.newArrayList();
       if (CollectionUtils.isEmpty(aclModuleLevelDtoList)) {
           return menuList;
       }

       // 处理菜单
       // 由于样式只适合最多两级,因此这里假设只有两层,尽管实现时层级是不限制多少级的
       for (AclModuleLevelDto firstLevel : aclModuleLevelDtoList) {
           Menu firstLevelMenu = new Menu();
           firstLevelMenu.setId(firstLevel.getId());
           firstLevelMenu.setName(firstLevel.getName());

           boolean needAddFirstMenu = false; // 是否需要增加一级菜单

           // 首先检查menu下是否已配置了有菜单的权限,如果有,就直接使用对应菜单的权限点的url作为点击后跳转的路径
           if (CollectionUtils.isNotEmpty(firstLevel.getAclList())) {
               for (AclDto aclDto : firstLevel.getAclList()) {
                   if (aclDto.getType() == 1 && StringUtils.isNotBlank(aclDto.getUrl())) { // 类型为菜单, 且配置了url
                       // 正常每个模块下应该只配置一个菜单的权限点,如果有多个,那是配置的有问题
                       firstLevelMenu.setUrl(aclDto.getUrl());
                       needAddFirstMenu = true;
                       break;
                   }
               }
           }

           // 目前这个菜单还没有点击跳转的url, 而且下面还有子模块时,继续尝试处理下一层级
           if(StringUtils.isBlank(firstLevelMenu.getUrl()) && CollectionUtils.isNotEmpty(firstLevel.getAclModuleList())) {
               for (AclModuleLevelDto sendLevel : firstLevel.getAclModuleList()) {
                   Menu secondLevelMenu = new Menu();
                   secondLevelMenu.setId(sendLevel.getId());
                   secondLevelMenu.setName(sendLevel.getName());
                   boolean needAddSecondMenu = false; // 是否需要增加二级菜单

                   if (CollectionUtils.isNotEmpty(sendLevel.getAclList())) {
                       for (AclDto aclDto : sendLevel.getAclList()) {
                           if (aclDto.getType() == 1 && StringUtils.isNotBlank(aclDto.getUrl())) { // 类型为菜单, 且配置了url
                               // 正常每个模块下应该只配置一个菜单的权限点,如果有多个,那是配置的有问题
                               secondLevelMenu.setUrl(aclDto.getUrl());
                               needAddFirstMenu = true;
                               needAddSecondMenu = true;
                               break;
                           }
                       }
                   }
                   if (needAddSecondMenu) { // 需要增加二级菜单
                       firstLevelMenu.addSubMenu(secondLevelMenu);
                   }
               }
           }

           if (needAddFirstMenu) { // 需要增加一级菜单
               menuList.add(firstLevelMenu);
           }
       }
       return menuList;
   }
}

接口返回示例:

https://img1.sycdn.imooc.com//szimg/5a3481620001ef0210721322.jpg

接下来,处理一下前端的渲染

涉及到菜单的渲染,只要调整admin.jsp就可以了

首先是增加菜单渲染的模板,这里包含三个主要是因为菜单分三种情况:

1)菜单只有一个层级,点击就进行页面跳转的,对应模板:urlMenuTemplate

2)菜单包含两个层级,第一层点击时打开第二层菜单,第二层点击时进行页面跳转。

第一层对应模板:emptyMenuTemplate

第二层对应模板:secondMenuTemplate

<script id="secondMenuTemplate" type="x-tmpl-mustache">
<b class="arrow"></b>
<ul class="submenu">
{{#subList}}
<li class="">
   <a class="popstyle" href="{{url}}" target="_blank">
       <i class="menu-icon fa fa-caret-right"></i>
       {{name}}
   </a>
   <b class="arrow"></b>
</li>
{{/subList}}
</ul>
</script>

<script id="urlMenuTemplate" type="x-tmpl-mustache">
<a class="popstyle" href="{{url}}" target="_blank">
   <i class="menu-icon fa fa-tachometer"></i>
   {{name}}
</a>
<b class="arrow"></b>
</script>

<script id="emptyMenuTemplate" type="x-tmpl-mustache">
<a href="#" class="dropdown-toggle">
   <i class="menu-icon fa fa-desktop"></i>
   <span class="menu-text"> {{name}} </span>
   <b class="arrow fa fa fa-caret-right"></b>
</a>
</script>

有了模板接下来就是修改页面的展示部分了

首先去掉页面上之前写死的菜单部分:

<ul class="nav nav-list">。。。(省略之前的菜单内容) </ul>  

换成 

<ul class="nav nav-list"></ul>

接下来是进行页面的实际渲染,admin.jsp页面最底下的的JavaScript替换成这段就可以了。

<script>
   $(document).ready(function () {
       $(function () {
           var emptyMenuTemplate =  $('#emptyMenuTemplate').html();
           var urlMenuTemplate =  $('#urlMenuTemplate').html();
           var secondMenuTemplate =  $('#secondMenuTemplate').html();
           $.ajax({
               url: "/sys/user/menu.json",
               success : function (result) {
                   if(result.ret) {
                       var menuList = result.data;
                       $(menuList).each(function (i, firstMenu) {
                           if(firstMenu.url) { // 如果首层就有url的话
                               var appendFirst = Mustache.render(urlMenuTemplate, firstMenu);
                               $(".nav-list").append('<li class="">' + appendFirst + "</li>");
                           }  else { // 如果是两层的话
                               var appendFirst = Mustache.render(emptyMenuTemplate, firstMenu);
                               var appendSend = Mustache.render(secondMenuTemplate, {
                                   subList: firstMenu.subList
                               });
                               $(".nav-list").append('<li class="">' + appendFirst + appendSend + "</li>");
                           }
                           handleCommonBehavior();
                       });
                   } else {
                       showMessage("加载菜单", result.msg, false);
                   }
               }
           })
       });

       function handleCommonBehavior() {
           $(".popstyle").removeAttr("target");
           $(".popstyle").each(function () {
               var $this = $(this);
               tmp = $this.attr("href");
               $this.attr("data", tmp);
               $this.attr("href", "javascript:void(0)");
           });

           $(".popstyle").click(function () {
               var $this = $(this);
               $("iframe").attr(
                   'src',
                   $this.attr("data")
               );
           });
       }

       $(".direct").click(function () {
           var $this = $(this);
           $("iframe").attr(
                   'src',
                   $this.attr("data-value")
           );
       });
   });
</script>

这样完成之后,在加载这个页面时,就会去后台请求实时的菜单数据,来进行动态渲染啦~

祝你学习愉快~

4 回复 有任何疑惑可以回复我~
  • 老师,你都已经实现动态菜单了,把 建表 的 sql和 mapper.xml也一起分享一下吧!
    回复 有任何疑惑可以回复我~ 2019-10-20 15:04:39
  • Jimin 回复 慕UI4845773 #2
    课程提供的springmvc版本的源码里有
    回复 有任何疑惑可以回复我~ 2019-10-23 22:27:29
慕UI4845773 2019-10-20 15:04:12

老师,你都已经实现动态菜单了,把 建表 的 sql和 mapper.xml也一起分享一下吧!

1 回复 有任何疑惑可以回复我~
  • Jimin #1
    课程springmvc版本的源码里提供了
    回复 有任何疑惑可以回复我~ 2019-10-23 22:28:37
  • coding-149的代码仓库中没看到菜单相关的代码。是不是我找错了。
    回复 有任何疑惑可以回复我~ 2023-01-15 11:12:28
Jimin 2017-12-13 13:57:10

你好,权限菜单的生成接口在课程里有实现,只是没做到页面上,你是指在页面上看动态的权限菜单吗?

0 回复 有任何疑惑可以回复我~
  • 提问者 无敌威威 #1
    是的,辛苦老师啦
    回复 有任何疑惑可以回复我~ 2017-12-13 14:54:00
  • 菜单权限怎么拿到前面不同的图标,老师你的图标都是相同
    回复 有任何疑惑可以回复我~ 2019-05-11 16:49:02
  • 图标可以前端自己给个算法,这东西完全看实现者设计了,实在不行,可以给权限模块存储时加上一项图标
    回复 有任何疑惑可以回复我~ 2019-05-11 20:45:23
问题已解决,确定采纳
还有疑问,暂不采纳
意见反馈 帮助中心 APP下载
官方微信