这是一个添加用户的,界面。 这个功能不是一个菜单选项。 他在 用户管理-->客户列表 页面中的一个功能。
但是当我点击的时候, 左侧的信息,全部收缩起来了。 我觉得应该时要展开这个一级标签,并且还要有 选中 客户列表的这样一个提示。
so 又有了我问题, 想想解决办法:
思路是这样子的:
- 登录,做权限和菜单的初始化。
- 获取菜单信息
{
1: {'title': '用户管理',
'icon': 'fa-envira',
'children': [{'id': 1, 'title': '客户列表', 'url': '/customer/list/'}]
},
2: {'title': '信息管理',
'icon': 'fa-black-tie',
'children': [{'id': 7, 'title': '账单列表', 'url': '/payment/list/'}]
}
}
- 获取权限信息( 进行更改之后的数据结构 )
[
{'id': 1, 'url': '/customer/list/', 'paren_id': None}, # 客户列表: 可以作为子菜单的
{'id': 2, 'url': '/customer/add/', 'paren_id': 1}, # 添加客户: 不可
{'id': 3, 'url': '/customer/edit/(?P<cid>\\d+)/', 'paren_id': 1}, # 编辑客户: 不可
{'id': 4, 'url': '/customer/del/(?P<cid>\\d+)/', 'paren_id': 1},
{'id': 5, 'url': '/customer/import/', 'paren_id': 1},
{'id': 6, 'url': '/customer/tpl/', 'paren_id': 1},
{'id': 7, 'url': '/payment/list/', 'paren_id': None}, # 账单列表: 可以作为子菜单的
{'id': 8, 'url': '/payment/add/', 'paren_id': 7},
{'id': 9, 'url': '/payment/edit/(?P<pid>\\d+)/', 'paren_id': 7},
{'id': 10, 'url': '/payment/del/(?P<pid>\\d+)/', 'paren_id': 7}
]
(解释:我们在循环这个,权限信息列表的时候。 使用当前访问的url 和 列表字典中的 url 进行匹配。 如果他的 parent_id 为null。那么这个 'id': 1 就是需要显示被选中的子菜单。 如果他的 parent_id 不为null,那么这个 'paren_id': 1 关联的 "id":1 的这条权限,就是需要显示被选中的子菜单。 这个 id 或者 parent_id 是需要传给inclusion_tag进行使用的。)( 因为做了所属关系,所以这里 不管是 id 还是 parent_id 都是 子菜单的主键 id)
- 再次进行访问。
- 中间件中进行权限的校验(根据权限信息 进行校验)
获取id 或 parent_id (应该呗选中的,可以做菜单的 权限id)。 然后传递给 inclusion_tag
- 模板中使用 inclusion_tag 生成动态菜单 (根据菜单信息,进行动态生成)
这里在做 显示被选中的菜单的时候, 就不能再只去判断,url 是否匹配。 而是要判断,于当前url 相匹配的,他的id, 与权限列表中id或parent_id 相等的那个。 权限子菜单,要被选中。
1. 当点击某个不能成为权限的菜单时,指定一个可以成为菜单的权限。让其默认的选中以及展开。( so 我们需要,为这个权限做一个归属。)
还是依然对,数据库进行 开刀:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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) parent_Permission = models.ForeignKey(verbose_name="关联权限", help_text="对于非菜单权限,需要确定当前权限归属于哪一个,父权限", to="Permission", null=True, blank=True, on_delete=models.CASCADE,related_name="parents") def __str__(self): return self.title
so 添加完成后,的这个表。 应该是这个样子的。
2. 对登录的时候, session保存的值进行,一番修改:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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__id", "permissions__url", "permissions__title", "permissions__pid_id", "permissions__menu_id", "permissions__menu__icon", "permissions__menu__title", ).distinct() # 获取权限 和 菜单信息。 权限放在权限列表,菜单放在菜单列表 menu_dict = {} permission_list = [] # 这里就不再是一个 单纯的列表! 而是套嵌字典的列表 for item in permission_queryset: url_dict = { "id": item.get("permissions__id"), "url": item.get("permissions__url"), "paren_id": item.get("permissions__pid_id") } permission_list.append(url_dict) menu_id = item.get("permissions__menu_id") if not menu_id: continue node = {"id": item.get("permissions__id"), "title": item.get("permissions__title"), "url": item.get("permissions__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] } request.session[settings.PERMISSIONS_SESSION_KEY] = permission_list request.session[settings.MENU_SESSION_KEY] = menu_dict
3. 然后是,中间件部分的,处理:
这里 我们需要读取出,当前访问的url (通过 request.path_info)。 然后与权限列表中的所有url 进行匹配。
匹配成功之后。这一步很重要:我们需要一个参数( 一个应该被选中的id 或 parent_id),传递给 inclusion_tag。 如何传递呢?
request.current_selected_permission = item.get("paren_id") or item.get("id")
在匹配成功之后,进行这一步操作。 在 request 对象中,添加一个属性current_selected_permission 他的值是 tem.get("paren_id") or item.get("id")
这个 参数的作用就是,判断。那一个子菜单应该默认被选中 或者 被展开。 or 的语法 不做多余解释。
4. 最后就是 自定义的 模板语法了:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
@register.inclusion_tag("rbac/multi_menu.html") def multi_menu(request): ''' 创建二级菜单 :return: ''' current_selected_permission = request.current_selected_permission 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: # 循环有序字典列表 val = menu_dict[key] # 得到每一个字典 val["class"] = "hide" # 添加一个 class:hide 键值. 控制标签的显示隐藏 for per in val["children"]: # 循环 当前字典(菜单) 下的 子菜单 if per.get("id") == current_selected_permission: per["class"] = "active" # 匹配成功 为当前子菜单添加 class:active 类属性(表示被选中的) val["class"] = "" # 当前一级菜单的 class:"" 跟改为空, 覆盖掉hide(不隐藏) ordered_dict[key] = val # 最终将设置好的每一个字典。 添加到有序字典当中 return {"ordered_dict": ordered_dict, "path_info": path_info}
这里做个什么改动呢:
就只有一点, 将原来判断, 当前访问的 url 与 每一个子菜单的url 进行匹配的操作。
改成了 per.get("id") == current_selected_permission 当前访问的url 的 id 或他的 parent_id 与 保存的 应该展开显示的 子菜单url的 主键id。进行匹配。
匹配成功的这条子菜单权限 url 自己添加 class="active" 并且他的父级 删除掉 hide "class" = ""。