二级菜单效果图
一、把一级菜单从权限表里抽离出来,单独创建一个表
rbac/models
Menuclass Menu(models.Model): """ 菜单表 """ title = models.CharField(verbose_name='一级菜单的名称', max_length=32) icon = models.CharField(verbose_name='图标', max_length=32, null=True, blank=True) def __str__(self): return self.title
Permission
class Permission(models.Model): """ 权限表 """ title = models.CharField(verbose_name='标题', max_length=32) url = models.CharField(verbose_name='含正则的URL', max_length=128) menu = models.ForeignKey(verbose_name='所属菜单', to=Menu, null=True, blank=True, help_text='null表示不是菜单,非null表示是二级菜单', on_delete=models.CASCADE ) def __str__(self): return self.title
二、修改初始化权限
rbac/service/init_permission.py
思路:- 获取一级菜单和二级菜单的信息
- 找出有menu_id的菜单(可以做二级菜单的)
- 将一级菜单的id作为key,values还是一个字典,里面储存一级菜单的标题、图标和二级菜单。
代码:
from permission_learn import settingsdef init_permission(current_user, request): """ 用户权限的初始化 :param current_user: 当前登录用户 :param request: :return: """ permission_menu_queryset = current_user.roles.filter(permissions__isnull=False).values( 'permissions__id', 'permissions__title', 'permissions__url', 'permissions__menu_id', # + 'permissions__menu__title', # + 'permissions__menu__icon', # + ).distinct() menu_dict = {} permission_list = [] for item in permission_menu_queryset: permission_list.append(item['permissions__url']) menu_id = item['permissions__menu_id'] if not menu_id: continue second_menu = { 'title': item['permissions__title'], 'url': item['permissions__url']} if menu_id in menu_dict: menu_dict[menu_id]['second_menu'].append(second_menu) else: menu_dict[menu_id] = { 'title': item['permissions__menu__title'], 'icon': item['permissions__menu__icon'], 'second_menu': [second_menu, ] } request.session[settings.PERMISSION_SESSION_KEY] = permission_list request.session[settings.MENU_SESSION_KEY] = menu_dict"""客户列表 /customer/list/ 1 ForeignKey --> 1 客户管理 fa-hdd-o 添加客户 /customer/add/ null 编辑客户 /customer/edit/(?P\d+)/ null 删除客户 /customer/del/(?P \d+)/ null"""
三、渲染到模板
rbac/templatetags/rbac.py
import refrom collections import OrderedDictfrom django.conf import settingsfrom django.template import Libraryregister = Library()@register.inclusion_tag('rbac/multi_menu.html')def multi_menu(request): menu_dict = request.session[settings.MENU_SESSION_KEY] # 对字典的key进行排序。得到的结果是只包含Key的列表,类似这样的 [1,2,3] key_list = sorted(menu_dict) # 空的有序字典 ordered_dict = OrderedDict() # 有序字典,按照我们想要的顺序展示 current_path = request.path for key in key_list: menu = menu_dict[key] # {'title':'客户管理','icon':'fa fa-book','second_menu':[二级菜单1,二级菜单2,...]} menu['class'] = 'hide' # 隐藏二级菜单 for second_menu in menu['second_menu']: regex = '^%s$' % second_menu['url'] if re.match(regex, current_path): second_menu['class'] = 'active' menu['class'] = '' # 显示点中的二级菜单 ordered_dict[key] = menu context = { 'menus': ordered_dict } return context
需要注意的是对字典的key进行排序得到的结果是只包含Key的列表,menu_dict的key是menu_id,最后得到这样的 [1,2,3...]的结果
通过templatestag渲染二级菜单到模板:rbac/templates/rbac/multi_menu.html
layout.html页面只需要把一级菜单的templates替换成二级菜单的就行
- {% menu request %} + {% multi_menu request %}
js代码:rbac/static/rbac/js/rbac.js
$('.multi-menu .title').click(function () { $(this).next().toggleClass('hide');});
toggleClass检查每个元素中指定的类。如果不存在则添加类,如果已设置则删除之。这就是所谓的切换效果。
css代码:rbac/static/rbac/css/rbac.css
'''.multi-menu .item {}.multi-menu .item > .title { padding: 10px 5px;border-bottom: 1px solid #dddddd;cursor: pointer;color: #333;display: block;background: #efefef;background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #efefef), color-stop(1, #fafafa));background: -ms-linear-gradient(bottom, #efefef, #fafafa);background: -o-linear-gradient(bottom, #efefef, #fafafa);filter: progid:dximagetransform.microsoft.gradient(startColorStr='#e3e3e3', EndColorStr='#ffffff');-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fafafa',EndColorStr='#efefef')";box-shadow: inset 0 1px 1px white;}.multi-menu .item > .body { border-bottom: 1px solid #dddddd;}.multi-menu .item > .body a { display: block;padding: 5px 20px;text-decoration: none;border-left: 2px solid transparent;font-size: 13px;}.multi-menu .item > .body a:hover { border-left: 2px solid #2F72AB;}.multi-menu .item > .body a.active { border-left: 2px solid #2F72AB;}'''
中间件不需要改动