二级动态菜单的功能

摘要:
要确定主菜单应该是什么样子,请使用classMenu(models.Model):mid=models。AutoField(primary_key=True)title=模型。CharField(verbose_name='主菜单标题',max_length=32)icon=型号。字符字段(verbose_name=“icon”,

二级动态菜单的实现, 我们可能需要一个  下方展示的这样的一种数据结构:

{
    1: {
        'title': '用户管理', 
        'icon': 'fa fa-envira', 
        'children': [
            {'title': '客户列表', 'url': '/customer/list/'}
        ]
    }, 
    2: {
        'title': '信息管理', 
        'icon': 'fa-black-tie', 
        'children': [
            {'title': '账单列表', 'url': '/payment/list/'}
        ]
    }
}

 OK 如何实现:
1. 数据库中 我们新添加一张表吧。 就叫 Menu 表。 一级菜单表:
  我们通过 一级菜单表, 来确定我们的 一级菜单应该有的样子

二级动态菜单的功能第1张二级动态菜单的功能第2张
class Menu(models.Model):
    mid = models.AutoField(primary_key=True)
    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
一级菜单, Menu表模型

然后他长这个样子:

二级动态菜单的功能第3张

so 一级菜单中,有 title 字段,表示菜单的名称。 icon字段,表示这个菜单的图标。

然后就是  一级菜单下的子菜单,  我们就修改一下  Permission表, 给他添加一个 ment 的外键。用来表示这个 url 可以被作为子菜单使用。
这样 就不需要,原来的表中 is_menu 表示是否可以成为菜单的选项了, 我们删掉他:

二级动态菜单的功能第4张二级动态菜单的功能第5张
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, on_delete=models.CASCADE)

    def __str__(self):
        return self.title
对Permission 权限表进行修改

这样我们就只需要,为 可以成为子菜单的 url进行,关联属于哪一个。一级菜单。

大概的样子就是这样:

二级动态菜单的功能第6张

 如果喜欢也可以,给子菜单添加icon。 为他也添加一个图标。

2. 数据库方面, 这就妥了。然后是 用户登录的时候。 我们要怎样 进行数据结构的编写了。就是 init_permission 初始化权限的阶段。 我们就要把 最初说的数据结构保存到session中。

二级动态菜单的功能第7张二级动态菜单的功能第8张
def init_permission(current_user, request):
    '''  二级菜单,实现
    :param current_user: 当前请求 用户对象
    :param request:  当前请求 数据
    :return:
    '''
    # 2. 权限 初始化
    # 根据当前用户信息,获取当前用户所拥有的所有的权限(queryset对象 是不能直接放入,session中的)
    permission_queryset = current_user.roles.filter(permissions__isnull=False).values("permissions__url","permissions__title","permissions__menu_id""permissions__menu__title", "permissions__menu__icon", ).distinct()

    # 获取权限 和 菜单信息。  权限放在权限列表,菜单放在菜单列表
    menu_dict = {}  # 菜单我们就需要一个字典了
    permission_list = []  # 所有的权限好说,还是放到列表中

    for item in permission_queryset:
        permission_list.append(item.get("permissions__url"))
        menu_id = item.get("permissions__menu_id")   #先取得当前这个url的父菜单的id
        if not menu_id:  # 如果为null的话,直接跳过。 说明这不是一个子菜单,也就不需要后续的操作了。
            continue

        node = {"title": item.get("permissions__title"), "url": item.get("permissions__url")}   # 获取到当前这个url的信息
        if menu_id in menu_dict:
            menu_dict[menu_id]["children"].append(node)
        else:
            menu_dict[menu_id] = {
                "title": item.get("permissions__menu__title"),
                "icon": item.get("permissions__menu__icon"),
                "children": [node]
            }
# 下面的解释,有一点复杂: 如果当前这个url 的menu_id 在 menu_dict字典中。那么就将当前这条url 添加到一级菜单的 "children" 列表中。也就是node
# 否则,就应该 以当前这条url 的menu_id为键。其中保存 一级菜单的title  和  icon字段信息。 并且"children"为键的列表中也要保存 node 这个参数代表的数据.
# 最终我们就能得到,最初我们想要的 数据结构
    request.session[settings.PERMISSIONS_SESSION_KEY] = permission_list
    request.session[settings.MENU_SESSION_KEY] = menu_dict
init_permission 中处理数据,并保存到session

3. ok 数据结构已经 搞定。 就剩下 改怎么渲染的问题了!  还是使用 inclusion_tag  进行渲染:

考虑到,字典是一个 无序的,存储容器。 那么我们将他转化成有序字典 就好了呀!

这里 需要使用到 re模块。 和OrderedDict模块

二级动态菜单的功能第9张二级动态菜单的功能第10张
from django.template import Library
from django.conf import settings
import re
from collections import OrderedDict

@register.inclusion_tag("rbac/multi_menu.html")
def multi_menu(request):
    '''
    创建二级菜单
    :return:
    '''
    '''
    {
        1: {
            'title': '用户管理', 
            'icon': 'fa fa-envira', 
            'children': [
                {'title': '客户列表', 'url': '/customer/list/'}
            ]
        }, 
        2: {
            'title': '信息管理', 
            'icon': 'fa-black-tie', 
            'children': [
                {'title': '账单列表', 'url': '/payment/list/'}
            ]
        }
    }
    我们可以对字典进行排序, 构成一个有序字典
    '''
    path_info = request.path_info
    menu_dict = request.session.get(settings.MENU_SESSION_KEY)  # 取出数据还是一样的操作

    key_list = sorted(menu_dict)  # 对字典的key 进行排序
    ordered_dict = OrderedDict()  # 创建一个空的 有序字典
    for key in key_list:   # 循环根据key 排序之后的大字典
        val = menu_dict[key]  # 得到其中的每一个小字典
        val["class"] = "hide"  # 添加一个 class:hide 键值. 控制标签的显示隐藏
        for per in val["children"]:  # 循环 当前字典(菜单) 下的 children 子菜单
            regex = "^%s$" % per["url"]   # 每一个子菜单中的url 添加 起始和终止符。 进行严格的匹配。
            if re.match(regex, request.path_info):  # 与当前访问的url:request.path_info  进行匹配
                per["class"] = "active"   # 匹配成功 为当前子菜单添加  class:active 类属性(表示被选中的)
                val["class"] = ""  # 当前一级菜单的 class:""  跟改为空, 不hide(不隐藏)
        ordered_dict[key] = val  # 最终将数据组织好的每一个小字典。 添加到有序的大字典当中
    return {"ordered_dict": ordered_dict}
rbac_tags 自定义模板语法
二级动态菜单的功能第11张二级动态菜单的功能第12张
a = OrderedDict(
    [
        ('1', {
            'title': '用户管理', 
            'icon': 'fa-envira', 
            'children': [
                {
                    'title': '客户列表', 
                    'url': '/customer/list/', 
                    'class': 'active'
                }
            ], 
            'class': ''
        }
         ), 
        ('2', {
            'title': '信息管理', 
            'icon': 'fa-black-tie', 
            'children': [
                {
                    'title': '账单列表', 
                    'url': '/payment/list/'
                }
            ], 
            'class': 'hide'
        }
         )
    ]
)
自定义模板语法中对字典进行处理,变成有序字典的结构 应该是这个样子

再看看 我们的这个 multi_menu.html 要怎么搞:

{#二级菜单#}
<div class="multi-menu">
    {% for item in ordered_dict.values %}
        <div class="item">
            <a class="title"><span class="icon-wrap"><i class="fa {{ item.icon }}"></i></span>{{ item.title }}</a>
            <div class="body {{ item.class }}">
                {% for per in item.children %}
                    <a class="{{ per.class }}" href="{{ per.url }}">{{ per.title }}</a>
                {% endfor %}
            </div>
        </div>
    {% endfor %}
</div>

使用了 两层 for 循环来做这件事情!

{{ item.class }} 这个变量就是 一级菜单中保存的 class键对应的 值。 用来控制 这个一级菜单下的 子菜单得,显示和隐藏;
{{ per.class }}  这个变量是循环 item.children 列表中,每个子菜单字典中,保存的 'class': 'active' , 用来表示当前这个子标签,是被选中的状态。
  (怎么确定的呢? 通过request.path_info 这个是发送到服务端的 当前访问的url。 我们和用户所有的权限, 使用re模块进行匹配时,确定的。 只有匹配成功的才会添加这个 键值对)


到这里基本功能已经实现了; 还却一些。 比如我想要 的效果是: 当我点击一个 一级菜单时,其余的一级菜单 全部收起.(也就是为除点击菜单之外的 其余菜单下的   的这个div添加上hide)

我们需要的就是 javascript 的东西了。 用 jQuery 来搞吧:

(function (jq) {
    jq('.multi-menu .title').click(function () {
        $(this).next().removeClass("hide");
        $(this).parent().siblings().children(".body").addClass('hide');
    });
})(jQuery);

 点击时 当前这个一级菜单,删除 hide。 使用js中的 排他思想,为除我之外其他兄弟,全部加上hide。

当然,我们应该,把这些所有的代码,都添加到。我的 rbac 组件当中。 其余地方使用时,只需要引用就可以。

在我们的 BASE.html 中添加,就可以所有的 继承 都使用到:
头部引入 css 的文件
    <link rel="stylesheet" href="http://t.zoukankan.com/{% static 'plugins/bootstrap/css/bootstrap.css' %} "/>
    <link rel="stylesheet" href="http://t.zoukankan.com/{% static 'plugins/font-awesome/css/font-awesome.css' %} "/>
    <link rel="stylesheet" href="http://t.zoukankan.com/{% static 'css/commons.css' %} "/>
    <link rel="stylesheet" href="http://t.zoukankan.com/{% static 'css/nav.css' %} "/>
    <link rel="stylesheet" href="http://t.zoukankan.com/{% static 'rbac/css/rbac.css' %} "/>


在中间需要的地方, 使用我们的自定义,模板语法:
{% load rbac_tags %}   // 这一句放到,文件的首行或者首行下面就可以
    <div class="left-menu">
        <div class="menu-body">
            {% multi_menu request %}
        </div>
    </div>


在 文件的最下方引入 js 文件:
<script src="http://t.zoukankan.com/{% static 'js/jquery-3.3.1.min.js' %} "></script>
<script src="http://t.zoukankan.com/{% static 'plugins/bootstrap/js/bootstrap.js' %} "></script>
<script src="http://t.zoukankan.com/{% static 'rbac/js/rbac.js' %} "></script>

免责声明:文章转载自《二级动态菜单的功能》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Unbutu下装oraclepostman使用csrftoken下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

scrapy抓取所有网站域名

需求分析 从一个门户网站出发,试图爬取该门户网站所有链接,如此递归下去,发现新域名则保存起来,每个域名网站只爬取一次。有了这些数据在通过外部DNS获得IP,就可以自己搭建DNS服务器了 创建项目 创建一个项目,名叫crawl_all_domainname scrapy startproject crawl_all_domainname 创建爬虫脚本doma...

操作系统/应用程序、操作中的“并发”、线程和进程,python中线程和进程(GIL锁),python线程编写+锁

并发编程前言:       1、网络应用            1)爬虫 直接应用并发编程;            2)网络框架 django flask tornado 源码-并发编程            3)socketserver 源码-并发编程       2、运维领域            1)自动化开发-运维开发(机器的批量管理,任务的批量执...

转载:Android应用的自动更新模块

软件的自动更新一般都与Splash界面绑定在一起, 由于需要维护的软件界面很复杂, 一个Activity中嵌入ViewPager, 并且逻辑比较复杂, 索性重新写一个Activity, 现在的软件都很流行使用Splash界面, 正好与自动更新配套在一起; 在这个自动更新Splash中, 使用到了 动画设置 ,SharedPerference ,pull解析...

解决URL中文乱码问题--对中文进行加密、解密处理

解决URL中文乱码问题--对中文进行加密、解密处理情景:在资源调度中,首先用户需要选择工作目标,然后跟据选择的工作目标不同而选择不同的账号和代理ip。处理过程如下:点击选择账号,在js中获取工作目标对工作目标进行两次编码(encodeURI(encodeURI(gzmb))),在后台,对工作目标进行解码,然后构建URL。       如下: 1 Strin...

Apache Rewrite url重定向功能的简单配置

1.Apache Rewrite的主要功能就是实现URL的跳转和隐藏真实地址,基于Perl语言的正则表达式规范。平时帮助我们实现拟静态,拟目录,域名跳转,防止盗链等2.Apache Rewrite的配置Apache下的Rewrite配置主要有两种,一种是针对整个apache服务器的配置,此种配置的Rewrite规则是直接在httpd.conf下书写。配置步骤...

Django实战(二)之模板语言

该实战教程基于菜鸟教程,菜鸟教程可参考:http://www.runoob.com/django/django-template.html 模板语法,每个框架都有其支持的模板语法,Django的模板语法在我看来与vue.js倒有一些相似处 ,比如它们的模板语法中参数为{{parm}}。 本篇所用到的例子,仍然基于实战(一) 在HelloWorld(该文件夹...