rbac组件权限分配之二级菜单


示例图:

 

 rbac/urls.py

from django.urls import re_path
from rbac.views import role
from rbac.views import user
from rbac.views import menu

urlpatterns = [
    # 角色管理
    re_path(r'^role/list/$', role.role_list, name="role_list"),
    re_path(r'^role/add/$', role.role_add, name="role_add"),
    re_path(r'^role/edit/(?P\d+)/$', role.role_edit, name="role_edit"),
    re_path(r'^role/del/(?P\d+)/$', role.role_del, name="role_del"),
    # 用户管理
    re_path(r'^user/list/$', user.user_list, name="user_list"),
    re_path(r'^user/add/$', user.user_add, name="user_add"),
    re_path(r'^user/edit/(?P\d+)/$', user.user_edit, name="user_edit"),
    re_path(r'^user/del/(?P\d+)/$', user.user_del, name="user_del"),
    re_path(r'^user/reset/password/(?P\d+)/$', user.user_reset_pwd, name="user_reset_pwd"),
    # 一级菜单管理
    re_path(r'^menu/list/$', menu.menu_list, name="menu_list"),
    re_path(r'^menu/add/$', menu.menu_add, name="menu_add"),
    re_path(r'^menu/edit/(?P\d+)/$', menu.menu_edit, name="menu_edit"),
    re_path(r'^menu/del/(?P\d+)/$', menu.menu_del, name="menu_del"),
    # 二级菜单管理
    re_path(r'^second/menu/add/(?P\d+)/$', menu.second_menu_add, name="second_menu_add"),
    re_path(r'^second/menu/edit/(?P\d+)/$', menu.second_menu_edit, name="second_menu_edit"),
    re_path(r'^second/menu/del/(?P\d+)/$', menu.second_menu_del, name="second_menu_del"),
]

rbac/forms/base.py

from django import forms


class BootStrapModelForm(forms.ModelForm):
    """用于将全部字段设置为bootstrap样式"""
    def __init__(self, *args, **kwargs):
        super(BootStrapModelForm, self).__init__(*args, **kwargs)
        for name, field in self.fields.items():
            field.widget.attrs["class"] = "form-control"

rbac/forms/menu.py

from rbac import models
from rbac.forms.base import BootStrapModelForm


class SecondMenuModelForm(BootStrapModelForm):
    class Meta:
        model = models.Permission
        exclude = ["pid"]

rbac/views/menu.py

from django.shortcuts import render, redirect, HttpResponse
from rbac import models
from rbac.forms.menu import SecondMenuModelForm
from rbac.service.urls import memory_reverse


def menu_list(request):
    """
    菜单和权限列表
    :param request:
    :return:
    """
    menus = models.Menu.objects.all().order_by("pk")
    # menu_list.html的菜单名称是a标签,此a标签的href在当前url后面加了mid参数
    # 取到该参数后,传给模板,模板与所循环一级菜单的id进行if比较,如果相等,那么加上'active'表示选中
    menu_id = request.GET.get("mid")  # 用户选择的一级菜单
    second_menu_id = request.GET.get("sid")  # 用户选择的二级菜单
    # 如果一级菜单的id不存在,那么将menu_id设置为None,menu_id控制二级菜单的'新建'按钮
    if not models.Menu.objects.filter(id=menu_id).exists():
        menu_id = None
    # 如果一级菜单id存在,那么将二级菜单的数据信息查询出来传给模板;如果一级菜单id为空,查询到的数据
    # 是不能作为二级菜单的url,所以还需要做一个判断,即一级菜单为空时,二级菜单数据信息为一个空列表
    # 那么二级菜单展示也是为空
    if menu_id:
        second_menus = models.Permission.objects.filter(menu_id=menu_id)  # 根据一级菜单的id查询二级菜单
    else:
        second_menus = []
    return render(
        request,
        "rbac/menu_list.html",
        {
            "menus": menus,
            "mid": menu_id,
            "second_menus": second_menus,
            "sid": second_menu_id,
        }
    )


def second_menu_add(request, menu_id):
    """
    添加二级菜单
    :param request:
    :param menu_id: 已选择的一级菜单id(用于设置默认一级菜单进行展示)
    :return:
    """
    menu_obj = models.Menu.objects.filter(id=menu_id).first()
    if request.method == "GET":
        form = SecondMenuModelForm(initial={"menu": menu_obj})
        return render(request, "rbac/change.html", {"form": form})
    form = SecondMenuModelForm(data=request.POST)
    if form.is_valid():
        form.save()
        return redirect(memory_reverse(request, "rbac:menu_list"))
    return render(request, "rbac/change.html", {"form": form})


def second_menu_edit(request, pk):
    """
    编辑二级菜单
    :param request:
    :param pk:
    :return:
    """
    obj = models.Permission.objects.filter(id=pk).first()
    if not obj:
        return HttpResponse("二级菜单不存在...")
    if request.method == "GET":
        form = SecondMenuModelForm(instance=obj)
        return render(request, "rbac/change.html", {"form": form})
    form = SecondMenuModelForm(instance=obj, data=request.POST)
    if form.is_valid():
        form.save()
        return redirect(memory_reverse(request, "rbac:menu_list"))
    return render(request, "rbac/change.html", {"form": form})


def second_menu_del(request, pk):
    """
    删除一级菜单
    :param request:
    :param pk:
    :return:
    """
    url = memory_reverse(request, "rbac:menu_list")
    if request.method == "GET":
        return render(request, "rbac/delete.html", {"cancel_url": url})
    models.Permission.objects.filter(id=pk).delete()
    return redirect(url)

rbac/templates/rbac/menu_list.html

{% extends 'layout.html' %}

{% load rbac %}

{% block css %}
    <style>
        {# 选中样式css #}
        tr.active {
            border-left: 3px solid #fdc00f;
        }
    style>
{% endblock %}

{% block content %}
    <div class="luffy-container">
        {# 一级菜单 #}
        <div class="col-md-3">
            <div class="panel panel-default">
                
                <div class="panel-heading">
                    <i class="fa fa-book" aria-hidden="true">i>
                    一级菜单
                    <a href="{% memory_url request 'rbac:menu_add' %}" class="right btn btn-success btn-xs"
                       style="padding: 2px 8px; margin: -3px;">
                        <i class="fa fa-plus-circle" aria-hidden="true">i>
                        新建
                    a>
                div>

                
                <table class="table">
                    <thead>
                    <tr>
                        <th>名称th>
                        <th>图标th>
                        <th>选项th>
                    tr>
                    thead>
                    <tbody>
                    {% for menu in menus %}
                        {# safe也可以转换类型,将数值类型转为了字符串类型 #}
                        <tr class="{% if menu.id|safe == mid %}active{% endif %}">
                            <td>
                                <a href="?mid={{ menu.id }}">{{ menu.title }}a>
                            td>
                            <td>
                                <i class="fa {{ menu.icon }}" aria-hidden="true">i>
                            td>
                            <td>
                                <a style="color: #333333;" href="{% memory_url request 'rbac:menu_edit' pk=menu.id %}">
                                    <i class="fa fa-edit" aria-hidden="true">i>a>
                                <a style="color: #d9534f;" href="{% memory_url request 'rbac:menu_del' pk=menu.id %}"><i
                                        class="fa fa-trash-o">i>a>
                            td>
                        tr>
                    {% endfor %}
                    tbody>
                table>
            div>
        div>
        {# 二级菜单 #}
        <div class="col-md-4">
            <div class="panel panel-default">
                
                <div class="panel-heading">
                    <i class="fa fa-cog" aria-hidden="true">i>
                    二级菜单
                    {# 如果选中了一级菜单,那么就显示新建按钮 #}
                    {% if mid %}
                        <a href="{% memory_url request 'rbac:second_menu_add' menu_id=mid %}" class="right btn btn-success btn-xs"
                           style="padding: 2px 8px; margin: -3px;">
                            <i class="fa fa-plus-circle" aria-hidden="true">i>
                            新建
                        a>
                    {% endif %}
                div>

                
                <table class="table">
                    <thead>
                    <tr>
                        <th>名称th>
                        {# 让code和url两个信息显示在同一列,这样就不会因为信息过长而影响整个页面展示 #}
                        <th>CODE&URLth>
                        <th>选项th>
                    tr>
                    thead>
                    <tbody>
                    {% for menu in second_menus %}
                        {# 如果选中,那么加active让其显示默认选中效果 #}
                        <tr class="{% if menu.id|safe == sid %}active{% endif %}">
                            {# 合并两列单元格 #}
                            <td rowspan="2">
                                {# sid代表二级菜单的id,url如此设计能让后端获取到二级菜单id然后传给模板,模板根据判断进行默认选中效果的展示 #}
                                <a href="?mid={{ mid }}&sid={{ menu.id }}">{{ menu.title }}a>
                            td>
                            <td>{{ menu.name }}td>
                            <td>
                                <a style="color: #333333;" href="{% memory_url request 'rbac:second_menu_edit' pk=menu.id %}">
                                    <i class="fa fa-edit" aria-hidden="true">i>a>
                                <a style="color: #d9534f;" href="{% memory_url request 'rbac:second_menu_del' pk=menu.id %}"><i
                                        class="fa fa-trash-o">i>a>
                            td>
                        tr>
                        {# 因为是两行,所以这里也需要加一个active #}
                        <tr class="{% if menu.id|safe == sid %}active{% endif %}">
                            {# 合并两行单元格,并将上边距设置为0,这样就不会有一条横线了 #}
                            <td colspan="2" style="border-top: 0;">{{ menu.url }}td>
                        tr>
                    {% endfor %}
                    tbody>
                table>
            div>
        div>
    div>
{% endblock %}