示例效果图:
app01/urls.py
from django.conf.urls import re_path from app01.views import customer from app01.views import payment from app01.views import account urlpatterns = [ re_path(r'^customer/list/$', customer.customer_list, name='customer_list'), # 把name全部加上 re_path(r'^customer/add/$', customer.customer_add, name='customer_add'), re_path(r'^customer/edit/(?P<cid>\d+)/$', customer.customer_edit, name='customer_edit'), re_path(r'^customer/del/(?P<cid>\d+)/$', customer.customer_del, name='customer_del'), re_path(r'^customer/import/$', customer.customer_import, name='customer_import'), re_path(r'^customer/tpl/$', customer.customer_tpl, name='customer_tpl'), re_path(r'^payment/list/$', payment.payment_list, name='payment_list'), re_path(r'^payment/add/$', payment.payment_add, name='payment_add'), re_path(r'^payment/edit/(?P<pid>\d+)/$', payment.payment_edit, name='payment_edit'), re_path(r'^payment/del/(?P<pid>\d+)/$', payment.payment_del, name='payment_del'), re_path(r'^login/', account.login, name='login'), re_path(r"^", account.login), ]
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'^multi/permissions/$', menu.multi_permissions, name="multi_permissions"), re_path(r'^multi/permissions/del/(?P<pk>\d+)/$', menu.multi_permissions_del, name="multi_permissions_del"), ]
rbac/forms/menu.py
from django import forms from rbac import models class MultiAddPermissionForm(forms.Form): title = forms.CharField( widget=forms.TextInput(attrs={"class": "form-control"}) ) url = forms.CharField( widget=forms.TextInput(attrs={"class": "form-control"}) ) name = forms.CharField( widget=forms.TextInput(attrs={"class": "form-control"}) ) menu_id = forms.ChoiceField( choices=[(None, "-----")], widget=forms.Select(attrs={"class": "form-control"}), required=False, ) pid_id = forms.ChoiceField( choices=[(None, "-----")], widget=forms.Select(attrs={"class": "form-control"}), required=False, ) def __init__(self, *args, **kwargs): super(MultiAddPermissionForm, self).__init__(*args, **kwargs) self.fields["menu_id"].choices += models.Menu.objects.values_list("id", "title") self.fields["pid_id"].choices += models.Permission.objects.filter( pid__isnull=True ).exclude(menu__isnull=True).values_list("id", "title") class MultiEditPermissionForm(forms.Form): id = forms.IntegerField( widget=forms.HiddenInput(), required=False, ) title = forms.CharField( widget=forms.TextInput(attrs={"class": "form-control"}) ) url = forms.CharField( widget=forms.TextInput(attrs={"class": "form-control"}) ) name = forms.CharField( widget=forms.TextInput(attrs={"class": "form-control"}) ) menu_id = forms.ChoiceField( choices=[(None, "-----")], widget=forms.Select(attrs={"class": "form-control"}), required=False, ) pid_id = forms.ChoiceField( choices=[(None, "-----")], widget=forms.Select(attrs={"class": "form-control"}), required=False, ) def __init__(self, *args, **kwargs): super(MultiEditPermissionForm, self).__init__(*args, **kwargs) self.fields["menu_id"].choices += models.Menu.objects.values_list("id", "title") self.fields["pid_id"].choices += models.Permission.objects.filter( pid__isnull=True ).exclude(menu__isnull=True).values_list("id", "title")
rbac/views/menu.py
from django.shortcuts import render, redirect, HttpResponse from rbac import models from rbac.forms.menu import MenuModelForm, SecondMenuModelForm, PermissionModelForm, MultiAddPermissionForm, \ MultiEditPermissionForm from rbac.service.urls import memory_reverse from rbac.service.routes import get_all_url_dict from collections import OrderedDict from django.forms import formset_factory def multi_permissions(request): """ 批量操作权限 :param request: :return: """ post_type = request.GET.get("type") generate_formset_class = formset_factory(MultiAddPermissionForm, extra=0) update_formset_class = formset_factory(MultiEditPermissionForm, extra=0) generate_formset = None update_formset = None if request.method == "POST" and post_type == "generate": # 批量添加 formset = generate_formset_class(data=request.POST) if formset.is_valid(): object_list = [] post_row_list = formset.cleaned_data has_error = False for i in range(0, formset.total_form_count()): row_dict = post_row_list[i] try: new_object = models.Permission(**row_dict) new_object.validate_unique() object_list.append(new_object) except Exception as e: formset.errors[i].update(e) generate_formset = formset has_error = True if not has_error: models.Permission.objects.bulk_create(object_list, batch_size=100) else: generate_formset = formset if request.method == "POST" and post_type == "update": # 批量更新 formset = update_formset_class(data=request.POST) if formset.is_valid(): post_row_list = formset.cleaned_data for i in range(0, formset.total_form_count()): row_dict = post_row_list[i] permission_id = row_dict.pop("id") try: row_object = models.Permission.objects.filter(id=permission_id).first() for k, v in row_dict.items(): setattr(row_object, k, v) row_object.validate_unique() row_object.save() except Exception as e: formset.errors[i].update(e) update_formset = formset else: update_formset = formset # 1 获取项目中所有的URL all_url_dict = get_all_url_dict() router_name_set = set(all_url_dict.keys()) # 2 获取数据库中所有的URL permissions = models.Permission.objects.all().values("id", "title", "name", "url", "menu_id", "pid_id") permission_dict = OrderedDict() for row in permissions: permission_dict[row["name"]] = row permission_name_set = set(permission_dict.keys()) for name, value in permission_dict.items(): router_row_dict = all_url_dict.get(name) if not router_row_dict: continue if value["url"] != router_row_dict["url"]: value["url"] = "路由和数据库中不一致..." # 3 应该添加、删除、修改的权限有哪些? # 3.1 计算出应该增加的name if not generate_formset: generate_name_list = router_name_set - permission_name_set # 添加 generate_formset = generate_formset_class( initial=[row_dict for name, row_dict in all_url_dict.items() if name in generate_name_list] ) # 3.2 计算出应该删除的name delete_name_list = permission_name_set - router_name_set # 删除 delete_row_list = [row_dict for name, row_dict in permission_dict.items() if name in delete_name_list] # 3.3 计算出应该更新的name if not update_formset: update_name_list = router_name_set & permission_name_set # 修改 update_formset = update_formset_class( initial=[row_dict for name, row_dict in permission_dict.items() if name in update_name_list] ) return render( request, "rbac/multi_permissions.html", { "generate_formset": generate_formset, "delete_row_list": delete_row_list, "update_formset": update_formset, } ) def multi_permissions_del(request, pk): """ 批量操作权限的页面删除 :param request: :param pk: :return: """ url = memory_reverse(request, "rbac:multi_permissions") if request.method == "GET": return render(request, "rbac/delete.html", {"cancel": url}) models.Permission.objects.filter(id=pk).delete() return redirect(url)
rbac/templates/rbac/multi_permissions.html
{% extends 'layout.html' %} {% block content %} <div class="luffy-container"> {# 添加 #} <form method="post" action="?type=generate"> {% csrf_token %} {{ generate_formset.management_form }} <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading"> <i class="fa fa-th-list" aria-hidden="true"></i> 待新建权限列表 <button href="#" class="right btn btn-primary btn-xs" style="padding: 2px 8px; margin: -3px;"> <i class="fa fa-save" aria-hidden="true"></i> 新建 </button> </div> <!-- Table --> <table class="table"> <thead> <tr> <th>序号</th> <th>名称</th> <th>URL</th> <th>别名</th> <th>菜单</th> <th>父权限</th> </tr> </thead> <tbody> {% for form in generate_formset %} <tr> <td>{{ forloop.counter }}</td> {% for field in form %} <td>{{ field }} <span style="color: red">{{ field.errors.0 }}</span></td> {% endfor %} </tr> {% endfor %} </tbody> </table> </div> </form> {# 删除 #} <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading"> <i class="fa fa-th-list" aria-hidden="true"></i> 待删除权限列表 </div> <!-- Table --> <table class="table"> <thead> <tr> <th>序号</th> <th>名称</th> <th>URL</th> <th>别名</th> <th>删除</th> </tr> </thead> <tbody> {% for form in delete_row_list %} <tr> <td>{{ forloop.counter }}</td> <td>{{ form.title }}</td> <td>{{ form.url }}</td> <td>{{ form.name }}</td> <td><a href="{% url 'rbac:multi_permissions_del' pk=form.id %}" style="color: #d9534f"> <i class="fa fa-trash-o"></i> </a></td> </tr> {% endfor %} </tbody> </table> </div> {# 更新 #} <form method="post" action="?type=update"> {% csrf_token %} {{ update_formset.management_form }} <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading"> <i class="fa fa-th-list" aria-hidden="true"></i> 待更新权限列表 <button href="#" class="right btn btn-primary btn-xs" style="padding: 2px 8px; margin: -3px;"> <i class="fa fa-save" aria-hidden="true"></i> 更新 </button> </div> <!-- Table --> <table class="table"> <thead> <tr> <th>序号</th> <th>名称</th> <th>URL</th> <th>别名</th> <th>菜单</th> <th>父权限</th> </tr> </thead> <tbody> {% for form in update_formset %} <tr> <td>{{ forloop.counter }}</td> {% for field in form %} {% if forloop.first %} {{ field }} {% else %} <td>{{ field }} <span style="color: red">{{ field.errors.0 }}</span></td> {% endif %} {% endfor %} </tr> {% endfor %} </tbody> </table> </div> </form> </div> {% endblock %}
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"> <!-- Default panel contents --> <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 --> <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"> <!-- Default panel contents --> <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 --> <table class="table"> <thead> <tr> <th>名称</th> {# 让code和url两个信息显示在同一列,这样就不会因为信息过长而影响整个页面展示 #} <th>CODE&URL</th> <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 class="col-md-5"> <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading"> <i class="fa fa-cogs" aria-hidden="true"></i> 权限 {# 如果选中了二级菜单,那么就显示新建按钮 #} <div class="btn-group right"> {% if sid %} <a href="{% memory_url request 'rbac:permission_add' second_menu_id=sid %}" 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 %} <a href="{% memory_url request 'rbac:multi_permissions' %}" class="btn btn-primary btn-xs" style="padding: 2px 8px; margin: -3px 0;"> <i class="fa fa-mail-forward" aria-hidden="true"></i> 批量操作 </a> </div> </div> <!-- Table --> <table class="table"> <thead> <tr> <th>名称</th> {# 让code和url两个信息显示在同一列,这样就不会因为信息过长而影响整个页面展示 #} <th>CODE&URL</th> <th>选项</th> </tr> </thead> <tbody> {% for menu in permissions %} <tr> {# 合并两列单元格 #} <td rowspan="2"> {{ menu.title }} </td> <td>{{ menu.name }}</td> <td> <a style="color: #333333;" href="{% memory_url request 'rbac:permission_edit' pk=menu.id %}"> <i class="fa fa-edit" aria-hidden="true"></i></a> <a style="color: #d9534f;" href="{% memory_url request 'rbac:permission_del' pk=menu.id %}"><i class="fa fa-trash-o"></i></a> </td> </tr> <tr> {# 合并两行单元格,并将上边距设置为0,这样就不会有一条横线了 #} <td colspan="2" style="border-top: 0;">{{ menu.url }}</td> </tr> {% endfor %} </tbody> </table> </div> </div> </div> </div> {% endblock %}
链接:https://pan.baidu.com/s/1VvxYPgmNJD-8MJUGnnEpxQ
提取码:abab