zoukankan      html  css  js  c++  java
  • 二级动态菜单的功能

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

    {
        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 表。 一级菜单表:
      我们通过 一级菜单表, 来确定我们的 一级菜单应该有的样子

    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表模型

    然后他长这个样子:

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

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

    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进行,关联属于哪一个。一级菜单。

    大概的样子就是这样:

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

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

    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模块

    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 自定义模板语法
    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模块进行匹配时,确定的。 只有匹配成功的才会添加这个 键值对)

    
    
    到这里基本功能已经实现了; 还却一些。 比如我想要 的效果是: 当我点击一个 一级菜单时,其余的一级菜单 全部收起.(也就是为除点击菜单之外的 其余菜单下的 class="body" 的这个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="{% static 'plugins/bootstrap/css/bootstrap.css' %} "/>
        <link rel="stylesheet" href="{% static 'plugins/font-awesome/css/font-awesome.css' %} "/>
        <link rel="stylesheet" href="{% static 'css/commons.css' %} "/>
        <link rel="stylesheet" href="{% static 'css/nav.css' %} "/>
        <link rel="stylesheet" href="{% static 'rbac/css/rbac.css' %} "/>
    
    
    在中间需要的地方, 使用我们的自定义,模板语法:
    {% load rbac_tags %}   // 这一句放到,文件的首行或者首行下面就可以
        <div class="left-menu">
            <div class="menu-body">
                {% multi_menu request %}
            </div>
        </div>
    
    
    在 文件的最下方引入 js 文件:
    <script src="{% static 'js/jquery-3.3.1.min.js' %} "></script>
    <script src="{% static 'plugins/bootstrap/js/bootstrap.js' %} "></script>
    <script src="{% static 'rbac/js/rbac.js' %} "></script>
    
  • 相关阅读:
    #动态规划 0-1背包问题空间复杂度优化
    #动态规划 0-1背包问题思路概述
    #动态规划 LeetCode 337 打家劫舍 III
    #动态规划 LeetCode 213 打家劫舍 II
    #动态规划 LeetCode 198 打家劫舍
    #动态规划 LeetCode 63 不同路径 II
    #动态规划 LeetCode 62 不同路径
    #动态规划 LeetCode 279 完全平方数
    #动态规划 LeetCode 343 整数拆分
    #动态规划 LeetCode 64 最小路径和
  • 原文地址:https://www.cnblogs.com/chengege/p/10701763.html
Copyright © 2011-2022 走看看