二级菜单的功能优化---渐入佳境
功能1:点击其中之一的一级菜单展示其二级菜单,同时其他一级菜单的二级菜单都处于闭合状态
思路:使用jsDOM操作进行实现
HTML示例代码 <div class="multi-menu"> {% for menu in menu_list %} <div class="item"> <div class="title"><i class="fa {{ menu.icon }}"></i> {{ menu.name }}</div> <div class="body"> {% for child in menu.children %} <a href="{{ child.url }}">{{ child.title }}</a> {% endfor %} </div> </div> {% endfor %} </div>
js代码实现
$('.item .title').click(function () { $(this).next().removeClass('hide').parent().siblings().find('.body').addClass('hide') }) //1.先命中一级菜单title,触发点击事件 //2.保证自己没有被隐藏 //3.找到其他body使其隐藏,通过自己先找到父级parent,再找到所有的兄弟siblings的儿子,也就是body,命中后添加hide属性 //如此便实现了这个只显示当前菜单下的二级菜单功能
功能2:访问当前页面,默认选中对应页面展示菜单
思路:在访问的二级菜单url对应a标签中添加 class='active'
1.django中 templatetags文件下使用inclusion_tag进行自定义模板
#从二级菜单的数据结构中获取到需要使用的url,title进行模板渲染 """ { 1: { 'name': '信息管理', 'icon': 'fa-email', 'children': [{ 'title': '客户管理', 'url': '/customer/list/' }] }, } """ from django import template import re from permission import settings register = template.Library() #自定义模板 @register.inclusion_tag('menu.html') def menu(request): menu_dic = request.session.get(settings.MENU_SESSION_KEY) url = request.path_info #获取访问的url #获取到url和title进行模板渲染 for item in menu_dic.values(): #首先循环拿到item表示一级菜单id对应的所有内容 for i in item['children']: #循环item['children'] 要拿到i 表示二级菜单内的所有内容 if re.match("^{}$".format(i['url']),url): #将获取到url与session中的url进行正则匹配 i['class'] = 'active' #匹配成功后给二级菜单字典内添加一组键值对 class='active' break return {'menu_list':menu_dic.values()}
2.在对应的模板中直接渲染即可,参照功能1中的HTML代码
<a class="{{ child.class }}" href="{{ child.url }}">{{ child.title }}</a> 根据上面的HTML代码来看,在class=body 下的a标签中添加class='active'进行渲染即可.
***如此便实现了访问某个页面,默认选中对应页面的展示菜单***
功能3:访问某个页面,页面对应菜单的二级菜单展示,其他菜单的二级菜单默认关闭
思路:首先先隐藏所有的二级菜单,再根据访问的url展示对应的一级菜单下的二级菜单
#示例代码
from django import template import re from luffy_permission import settings register = template.Library() #自定义模板 @register.inclusion_tag('menu.html') def menu(request): menu_dic = request.session.get(settings.MENU_SESSION_KEY) url = request.path_info for item in menu_dic.values(): item['class'] = 'hide' #将所有的二级菜单隐藏 for i in item['children']: if re.match("^{}$".format(i['url']),url): i['class'] = 'active' item['class'] = '' #将匹配成功的二级菜单取消隐藏 break return {'menu_list':menu_dic.values()}
<div class="multi-menu"> {% for menu in menu_list %} <div class="item"> <div class="title"><i class="fa {{ menu.icon }}"></i> {{ menu.name }}</div> <div class="body {{ menu.class }}"> {% for child in menu.children %} <a class="{{ child.class }}" href="{{ child.url }}">{{ child.title }}</a> {% endfor %} </div> </div> {% endfor %} </div>
注:先在body中添加class='hide'属性,令所有的二级菜单隐藏,当访问的url页面匹配与session中的url匹配成功后在将 class='' 从而取消隐藏
功能4:固定菜单栏展示顺序,因为数据结构是字典(无序的)
思路:将menu表结构改变,添加一个字段weight = model.IntegerField(default=1),导入有序字典(from collections import OrderedDict),根据数值大小进行排序即可
models.py
class Menu(models.Model): """ 一级菜单 """ name = models.CharField(max_length=30,verbose_name='一级菜单名称') icon = models.CharField(max_length=56, verbose_name='图标', blank=True, null=True) weight = models.IntegerField(default=1) def __str__(self): return self.name #添加一个字段weight
rbac.py
from django import template import re from permission import settings from collections import OrderedDict register = template.Library() #自定义模板 @register.inclusion_tag('menu.html') def menu(request): menu_dic = request.session.get(settings.MENU_SESSION_KEY)
url = request.path_info order_dic = OrderedDict() #实例化一个有序字典 for key in sorted(menu_dic,key=lambda n:menu_dic[n]['weight'],reverse=True): order_dic[key] = item = menu_dic[key] #将根据weight字段排序后的menu_dic 赋值给order_dic item['class'] = 'hide' for i in item['children']: if re.match("^{}$".format(i['url']),url): i['class'] = 'active' item['class'] = '' break return {'menu_list':order_dic.values()} #再返回有序字典在模板上顺序渲染
功能5:非菜单权限归属问题 (二级菜单内各种时操作 默认选中并处于显示对应二级菜单)
思路:二级菜单下需要有操作的层级关系,也就是你伪三级菜单 ,更改表结构也就是 权限表(Permission)
步骤1:表结构设计 models.py
#参照上图所示
class Permission(models.Model): """ 权限表 menu_id 有的话 表示当前的权限是二级菜单 父权限 没有 是一个普通权限 parent_id 有的话 表示当前就是子权限 没有的话就是一个父权限 """ url = models.CharField(max_length=255,verbose_name='地址') title = models.CharField(max_length=25,verbose_name='标题') menu = models.ForeignKey('Menu',blank=True,null=True) parent = models.ForeignKey('self') #自关联 获取到层级结构
步骤2:查询和存放在session中的权限信息处理 service/permission.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from django.conf import settings 2 3 4 def init_permission(request,obj): 5 # 先查到需要的权限信息, 6 7 permission_query = obj.roles.all().filter(permissions__url__isnull=False).values('permissions__url', 8 'permissions__title', 9 'permissions__menu__name', 10 'permissions__menu__icon', 11 'permissions__menu__id', 12 'permissions__menu__weight', 13 'permissions__id', 14 'permissions__parent_id', 15 ).distinct() 16 # 存放权限信息列表 17 permission_list = [] 18 # 存放菜单信息的字典 19 menu_dic = {} 20 for item in permission_query: 21 # 将权限信息放到permission_list 22 permission_list.append({'url': item['permissions__url'], 23 'pid': item['permissions__parent_id'], 24 'id': item['permissions__id'] }) 25 26 #放入菜单信息 27 menu_id = item['permissions__menu__id'] 28 if not menu_id: 29 continue 30 if menu_id not in menu_dic: 31 menu_dic[menu_id] = { 32 'name': item['permissions__menu__name'], 33 'icon': item['permissions__menu__icon'], 34 'weight': item['permissions__menu__weight'], 35 'children': [{ 36 'title': item['permissions__title'], 37 'url': item['permissions__url'], 38 'id': item['permissions__id'], 39 }] 40 } 41 else: 42 menu_dic[menu_id]['children'].append({ 43 'title': item['permissions__title'], 44 'url': item['permissions__url'], 45 'id': item['permissions__id'], 46 }) 47 48 request.session[settings.PERMISSION_SESSION_KEY] = permission_list 49 request.session[settings.MENU_SESSION_KEY] = menu_dic
步骤3.中间件权限校验,处理访问url归属问题 middlewares/rbac.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from django.utils.deprecation import MiddlewareMixin 2 import re 3 from django.conf import settings 4 from django.shortcuts import HttpResponse,redirect,reverse 5 6 class RbacMiddleware(MiddlewareMixin): 7 8 def process_request(self,request): 9 #获取当前的url 10 url = request.path_info 11 12 #白名单 13 for i in settings.WHITE_LIST: 14 if re.match(i,url): 15 return 16 17 #获取权限信息 18 permission_list = request.session.get(settings.PERMISSION_SESSION_KEY) 19 if not permission_list: 20 return redirect(reverse('login')) 21 #权限校验 22 23 for item in permission_list: 24 # i 里面有{url pid id} 25 if re.match("^{}$".format(item['url']),url): 26 #要显示的二级菜单的id 27 pid = item.get('pid') 28 id = item.get('id') 29 #此步操作就是表示不管是pid还是id都是显示的二级菜单的id 30 if pid: 31 #当前访问的是子权限,找父权限显示 32 request.current_menu_id = pid 33 else: 34 #当前访问的是父权限,也就是二级菜单,找自己显示 35 request.current_menu_id = id 36 return 37 return HttpResponse('您没有访问的权限')
步骤4.自定义模板中二级菜单id和session中存的匹配成功
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from django import template import re from luffy_permission import settings from collections import OrderedDict register = template.Library() #自定义模板 @register.inclusion_tag('menu.html') def menu(request): menu_dic = request.session.get(settings.MENU_SESSION_KEY) url = request.path_info order_dic = OrderedDict() for i in sorted(menu_dic,key=lambda n:menu_dic[n]['weight'],reverse=True): order_dic[i] = item = menu_dic[i] # for item in order_dic.values(): item['class'] = 'hide' for i in item['children']: if i['id'] == request.current_menu_id:#session中的id和访问的二级菜单id匹配 i['class'] = 'active' item['class'] = '' break return {'menu_list':order_dic.values()}
功能6:路径导航(breadcrumb)
思路:先写个字典,字典中添加要显示的url和title,然后进行循环,具体逻辑代码展现
步骤1:将存放在session的权限信息从列表修改为字典,同时在权限信息中再添加title字段,确保能够展示,
permission_dic[item['permissions__id']] = ({'url': item['permissions__url'], 'pid': item['permissions__parent_id'], 'id': item['permissions__id'] , 'title': item['permissions__title'], }) #确保url和title字段存在,能够访问跳转和显示
步骤2:中间件中 middlewares/rbac.py
2-1添加一个列表套字典的数据结构并添加到request中
request.breadcrumb_list = [ {'title':'首页','url':'/index/'} ]
2-2 向breadcrumb_list中添加匹配到的pid/id对应的url和title主要是在访问的时候对应的将对二级菜单和三级菜单的url和标题展示
for item in permission_dic.values(): if re.match("^{}$".format(item['url']),url): pid = item.get('pid') id = item.get('id') #此步操作就是表示不管是pid还是id都是显示的二级菜单的id if pid: #当前访问的是子权限,找父权限显示 request.current_menu_id = pid #向路径导航列表中添加三级菜单的url和title request.breadcrumb_list.append( {'title': permission_dic[str(pid)]['title'], 'url': permission_dic[str(pid)]['url'] } ) # 向路径导航列表中添加二级菜单的url和title request.breadcrumb_list.append({'title': item['title'], 'url': item['url']}) else: #当前访问的是父权限,也就是二级菜单,找自己显示 request.current_menu_id = id request.breadcrumb_list.append({'title':item['title'],'url':item['url']})
步骤3 templatetags/inclusion_tag进行自定义模板rabc.py
@register.inclusion_tag('breadcrumb.html') def breadcrumb(request): return {'breadcrumb_list':request.breadcrumb_list} #将获取到的request.breadcrumb_list 交给'breadcrumb.html'渲染
步骤4:HTML模板渲染
<div> <ol class="breadcrumb no-radius no-margin" style="border-bottom: 1px solid #ddd;"> {% for breadcrumb in breadcrumb_list %} {% if forloop.last %} <li>{{ breadcrumb.title }}</li> {% else %} <li><a href="{{ breadcrumb.url }}">{{ breadcrumb.title }}</a></li> {% endif %} {% endfor %} </ol> </div>
#继承母版,单独写一个breadcrumb.html文件 #小小技术点:循环展示的时候,如果是最后一个无法访问,通过bootstrap 路径导航给li标签添加class='active'实现
功能7:权限控制到按钮级别
思路:根据用户是否有权限通过url反向解析的名称进行判断,有就通过过滤器显示,否则隐藏
步骤1:修改表结构,针对权限表(Permission)添加一个urlname字段,并将urls.py中url反向解析名称进行保存 例如: name='customer_list'
#更新的表结构models.py
添加以下字段
urlname = models.CharField(max_length=32,verbose_name='url别名')
步骤2:获取到需要的权限信息 service/permission.py权限初始化
permission_query = obj.roles.all().filter(permissions__url__isnull=False).values('permissions__url',
'permissions__title',
'permissions__urlname',#取到权限表的url别名
'permissions__menu__name',
'permissions__menu__icon',
'permissions__menu__id',
'permissions__menu__weight',
'permissions__id',
'permissions__parent_id',
'permissions__parent__urlname', #取到父权限和自己的别名两个
).distinct()
# 存放权限信息列表
permission_dic = {}
# 存放菜单信息的字典
menu_dic = {}
for item in permission_query:
# 将权限信息放到permission_list
permission_dic[item['permissions__urlname']] = ({'url': item['permissions__url'],
'pid': item['permissions__parent_id'],
'pname': item['permissions__parent__urlname'],
'id': item['permissions__id'] ,
'urlname': item['permissions__urlname'] ,
'title': item['permissions__title'],
})
步骤3.中间件中进行权限校验,使用权限表中的id和自关联的parent_id字段,匹配出对应的url和title
for item in permission_dic.values():
if re.match("^{}$".format(item['url']),url):
pid = item.get('pid')
pname = item.get('pname')
id = item.get('id')
if pid:
request.current_menu_id = pid
#向路径导航列表中添加二级菜单也就是父权限的url和title
request.breadcrumb_list.append(
{'title': permission_dic[pname]['title'],
'url': permission_dic[pname]['url']
}
)
# 向路径导航列表中添加三级菜单的url和title
request.breadcrumb_list.append({'title': item['title'], 'url': item['url']})
else:
#当前访问的是父权限,也就是二级菜单,找自己显示
request.current_menu_id = id
request.breadcrumb_list.append({'title':item['title'],'url':item['url']})#向路径导航中添加二级菜单
return
步骤4.在templatetags/rbac.py中配置过滤器预备模板渲染data
@register.filter()
def has_permission(request,urlname):
if urlname in request.session.get(settings.PERMISSION_SESSION_KEY):
return True
#通过url反向解析的名称有无进行判断,从而在展示模板中进行显示
步骤5.HTML模板渲染出权限对应的操作信息
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)