WxPython跨平台开发框架之动态菜单的管理和功能权限的控制
在一个业务管理系统中,如果我们需要实现权限控制功能,我们需要定义好对应的权限功能点,然后在前端界面中对界面元素的可用性和功能点进行绑定,这样就可以在后台动态分配权限进行动态控制了,一般来说,权限功能点是针对角色进行控制的,也就是简称RBAC(Role Based Access Control)。对于登录系统后的用户,对用户的菜单(工具栏)、界面操作按钮的权限进行动态化的绑定和统一处理的操作过程,这样对于我们界面,只需要约定一些规则即可实现比较弹性化的操作,非常方便。本篇随笔介绍WxPython跨平台开发框架之动态菜单的管理和功能权限的控制。
1、权限管理系统的相关资源
权限管理系统主要的功能包括有:用户管理、组织机构管理、功能管理、角色管理和权限分配管理、菜单管理、系统类型管理、登录日志管理、操作日志管理、系统黑白名单管理等功能模块。对于每新增一个系统,我们只需要在权限管理系统中增加一个系统类型定义,以及相关的功能、菜单数据即可,非常方便管理。下图是一个简化的权限管理系统中涉及到角色相关资源的信息。

菜单资源和权限功能点是基于不同系统前端定义的资源,因此可以一套系统管理多个终端的菜单、功能点,以便实现更好的控制。
权限系统分了两级管理员用户:超级管理员和公司管理员。超级管理员可以管理整个集团或者整个系统的人员和相关信息(包括组织机构、角色、登陆日志、操作日志等信息的分级);公司管理员可以管理分子公司、事业单位处室/局级这样的组织机构的人员和相关信息。
分级管理组织机构、角色、用户等相关数据,能够减少管理员的相关工作,提高工作效率,并能增强权限管理系统对权限的控制和资源分配等管理,提高用户的认同感。
1)功能点定义和管理
权限功能点的管理就是对TB_Function的表的管理操作,这个表是我们定义用于系统控制的功能点。
权限功能点的管理为了展示它的树状结果,包括树列表的管理和明细列表的管理,如下图所示。

我们为了方便,在开始的时候,创建功能点的时候,一般通过批量添加的方式快速添加,如下界面所示。

这样系统会根据主控制标识,为各个操作(增加、删除、修改等)增加对应的操作标识。

为对应角色添加相关的操作功能点

2)菜单资源的定义和管理
一般的业务系统,需要对菜单进行动态配置管理,通过后台菜单的配置和权限的指定,能够实现菜单的动态加载和权限验证。
因此菜单也是权限分配的一部分,为了有效管理菜单资源,我们把菜单放到权限管理系统中进行管理控制,可根据用户权限进行动态控制显示。
菜单的管理界面如下所示。

菜单信息,包括名称和相关的图标定义等,这些需要再前端界面中构建工具栏显示用到的资源,而窗体类型则是定义我们需要动态展示的菜单对象的模块名和类名称。

菜单定义好后,为了和实际用户进行关联,那么需要为角色添加相关的访问菜单,从而实现用户菜单的动态关联。

我们在开发初期,模拟定义了静态的菜单资源的信息,如下所示。
class ToolbarUtil:
"""工具栏菜单的创建类""" @staticmethod
def create_tools():
"""创建工具栏菜单嵌套集合"""
menus = [
MenuInfo(
id="01",
label="用户管理",
icon="user",
path="views.frm_user.FrmUser",
),
MenuInfo(
id="02",
label="组织机构管理",
icon="organ",
path="views.frm_ou.FrmOU",
),
...............
]
return menus
那么我们有了动态定义的菜单和动态分配的功能后,我们就可以根据用户ID(角色ID)从后端系统接口中获得对应的菜单列表,然后统一展示即可。
@staticmethod
def create_tools_dynamic():
"""动态创建工具栏菜单""" # 同步获取菜单信息
menus = api_menu.get_all_nodes_by_user_sync(
settings.CurrentUser.id, settings.SystemType
)
# 定义字段映射
field_mapping = {"name": "label", "winformtype": "path", "icon": "icon"}
newmenus = map_with_dynamic_alias(menus, MenuInfo, field_mapping) return newmenus
这样我们在主窗体界面中,构建菜单的函数如下所示。
def _create_toolbar(self, d="H"):
"""创建工具栏"""
agwStyle = aui.AUI_TB_TEXT | aui.AUI_TB_OVERFLOW if d.upper() in ["V", "VERTICAL"]:
agwStyle = aui.AUI_TB_TEXT | aui.AUI_TB_VERTICAL tb = aui.AuiToolBar(
self, -1, wx.DefaultPosition, wx.DefaultSize, agwStyle=agwStyle
)
tb.SetToolBitmapSize(wx.Size(16, 16)) # 动态创建工具栏
toolbars = ToolbarUtil.create_tools_dynamic() for item in toolbars:
tool_id = wx.NewIdRef()
bitmap = get_bitmap(item.icon)
tb.AddSimpleTool(
tool_id=tool_id,
label=item.label,
bitmap=bitmap,
short_help_string=item.tips if item.tips else item.label,
) # 绑定事件
self.Bind(
wx.EVT_TOOL,
partial(self.on_tool_event, item), # 这里传递菜单信息
id=tool_id,
) # 增加系统常用按钮
tb.AddSeparator()
tb.AddSimpleTool(
self.id_close_all, "关闭所有", images.delete_all.Bitmap, "关闭所有页面"
)
tb.AddSimpleTool(self.id_quit, "退出", images.close.Bitmap, "退出程序") self.Bind(
wx.EVT_TOOL, lambda event: EventPub.show_about_dialog(), id=self.id_about
)
self.Bind(
wx.EVT_TOOL, lambda event: EventPub.close_all_page(), id=self.id_close_all
)
tb.Realize()
return tb
动态构建部分菜单后,并加上一些额外的操作功能项目即可。
要动态构建视图的实例,我们需要用到importlib库来导入模块,然后在模块中获得对应类型进行构造处理即可。
import importlib
import wx """要自动导入一个给定的类(比如 "views.testaui_panel.DocumentPanel")并在 Python 中使用它,
可以利用 importlib 库来动态地导入模块和类。importlib 允许你在运行时加载模块,
而不需要在代码中显式地使用 import 语句。""" def dynamic_import(class_path: str):
# 拆分字符串为模块路径和类名
# print(f"Dynamic import: {class_path}")
module_path, class_name = class_path.rsplit(".", 1) # 动态导入模块
module = importlib.import_module(module_path) # 获取类对象
class_obj = getattr(module, class_name) # 返回类对象
return class_obj
例如可以通过下面类似的代码实现对话框展示或者窗口的展示。
def show_window(class_path: str, parent=None, **kwargs):
"""根据路径和基类对象,显示窗口"""
window_class = dynamic_import(class_path)
window = window_class(parent, **kwargs)
# 判断传入的窗口是 wx.Dialog 还是 wx.Panel
if isinstance(window, wx.Dialog):
# 如果是 wx.Dialog,调用 ShowModal
result = window.ShowModal()
print(f"Dialog closed with result: {result}")
window.Destroy() # 在对话框关闭后销毁
elif isinstance(window, wx.Panel):
# 如果是 wx.Panel,调用 Show
window.Show()
print("Panel shown")
else:
print("Unknown window type")
2、功能点和按钮的控制处理
在上面的菜单资源中,直接和角色关联,在用户登录系统后,自动构建对应的菜单(工具栏)显示,而对于功能点和按钮的关联控制处理,我们也是可以采用类似的方式处理的。
首先我们在当前终端的全局设置对象里面定义好拥有的功能点对象,如下代码所示。

我们在用户成功登录系统(认证用户通过)后,获取用户的详细信息,以及相关联的资源(包括菜单、功能点、角色列表)等信息进行全局存储,方便在用到的地方进行调用判断,如下代码所示。
async def SetLoginInfo(self):
"""设置登录信息"""
userid = settings.AccessTokenResult.userid
res = await api_user.Get(userid)
if res.success:
settings.CurrentUser = res.result
await self.GetSystemType()
await self.GetFunctionsByUser(userid)
await self.GetRolesByUser(userid) EventPub.user_info_loaded() # 发布用户信息加载完成事件
我们前面随笔介绍过,一般列表窗体是继承一个统一的基类的。
在我的WxPython跨平台开发框架中,我们对于常规窗体列表界面做了抽象处理,一般绝大多数的逻辑封装在基类上,基类提供一些可重写的函数给子类实现弹性化的处理。
如下是基类窗体和其他窗体之间的集成关系。

由于基类是通过泛型类型的定义的,因此可以对子类的相关逻辑进行统一抽象处理,以便实现常规功能的控制(包括新增、编辑、删除、批量添加、打印、导入、导出等)。
如对于客户信息的列表窗体,我们的视图类如下所示((通过代码生成工具Database2Sharp生成即可,之前随笔《在自家的代码生成工具中,增加对跨平台WxPython项目的前端代码生成,简直方便的不得了》介绍过)。

我们可以看到,其中model是由子类传入的一个对象类型,那么我们在父类也可以统一进行获取它的名称进行处理即可。
在BaseListFrame类里面,我们定义一个判断是否有对应功能点的函数,如下所示。
def HasAction(self, action_id: str, entity_name: str = None) -> bool:
"""判断权限缓存是否存在指定操作功能id :param action_id: 操作功能id, 如Add,Edit,Delete, Import,Export等
:param entity_name: 实体名称, 默认根据self.model的名称获取并替换后缀Dto,如Customer,Product等
:return: 存在返回True,否则返回False
"""
if not action_id:
return False if not entity_name:
# 根据self.model的名称获取并替换后缀Dto
entity_name = self.model.__qualname__.replace("Dto", "") # 判断权限缓存是否存在类似"Customer:Add"格式的字符串
result = f"{entity_name}:{action_id}" in settings.FunctionDict
return result
这样我们约定了模块和功能点的名称前缀后,就可以通用的处理判断了。

由于我们常规的新增、编辑、删除、导出等操作由父类统一生成标准的按钮,那么我们就可以根据是否有某些功能的标识进行构建了,如下代码所示。
def _CreateCommonButtons(self, pane: wx.Window):
"""父类窗口统一创建通用按钮"""
# 增加按钮
btns_sizer = wx.BoxSizer(wx.HORIZONTAL) button_list = []
# 设置图标和位置
if EventFlags.EVT_Search & self.EVT_FLAGS:
btn_search = ControlUtil.create_button(
pane,
"查询",
"search",
handler=self._on_first_page,
is_async=True,
id=wx.ID_FIND,
)
button_list.append(btn_search)
if self.has_add:
btn_add = ControlUtil.create_button(
pane, "新增", "add", handler=self.OnAdd, is_async=True
)
button_list.append(btn_add)
if self.has_edit:
btn_edit = ControlUtil.create_button(
pane, "编辑", "edit", handler=self.OnEdit, is_async=True
)
button_list.append(btn_edit)
if self.has_delete:
btn_delete = ControlUtil.create_button(
pane, "批量删除", "delete", handler=self.OnDelete, is_async=True
)
button_list.append(btn_delete)
if self.has_export:
btn_export = ControlUtil.create_button(
pane, "导出Excel", "xls", handler=self.OnExport, is_async=True
)
button_list.append(btn_export) # 将按钮添加到按钮组中
for btn in button_list:
btns_sizer.Add(btn, 0, wx.ALL, 3) return btns_sizer
我们这里使用了辅助类创建按钮 ControlUtil.create_button 方便控制相关的内容。类似的右键菜单我们也可以如此操作,判断权限是否拥有再构建即可。
def _on_showmenu(self, event: wx.grid.GridEvent) -> None:
"""父类窗体的右键菜单处理""" # 创建右键菜单对象
menu: wx.Menu = wx.Menu() if self.has_add:
ControlUtil.create_menu(
self, menu, "新增", icon_name="add", handler=self._OnMenuAdd
)
if self.has_edit:
ControlUtil.create_menu(
self, menu, "编辑", icon_name="edit", handler=self._OnMenuEdit
)
if self.has_delete:
ControlUtil.create_menu(
self, menu, "删除选中行", icon_name="delete", handler=self._OnMenuDelete
) if self.has_export:
ControlUtil.create_menu(
self, menu, "导出Excel", icon_name="xls", handler=self._OnMenuExport
) ............

这些都是父类窗体,对通用操作的权限判断和创建处理,如果对于子类窗体,我们也可以使用这些判断标识来增加一些额外的操作按钮或者菜单的。
如对于字典模块列表界面中,判读它是否有批量添加的操作权限,并添加相关的功能入口。如下所示。
def CreateCustomMenus(self, parent_menu: wx.Menu) -> None:
"""子类重写该方法,创建自定义菜单"""
# 父类已创建默认菜单,这里添加自定义菜单
if self.has_batch_add:
ControlUtil.create_menu(
self,
parent_menu,
"批量添加字典",
"batch_add",
handler=self.OnMenuBatchAdd,
)
对于查看/编辑/新增的窗体,它们也是有一个通用的编辑对话框基类的,因此也可以和列表的方式实现同样的功能控制,这里不在赘述。
以上就是对于登录系统后的用户,对用户的菜单(工具栏)、界面操作按钮的权限进行动态化的绑定和统一处理的操作过程,这样对于我们界面,只需要约定一些规则即可实现比较弹性化的操作,非常方便。
WxPython跨平台开发框架之动态菜单的管理和功能权限的控制的更多相关文章
- .NET快速信息化系统开发框架 V3.2 -> WinForm“组织机构管理”界面组织机构权限管理采用新的界面,操作权限按模块进行展示
对于某些大型的企业.信息系统,涉及的组织机构较多,模块多.操作权限也多,对用户或角色一一设置模块.操作权限等比较繁琐.我们可以直接对某一组织机构进行权限的设置,这样设置后,同一组织机构的用户就可以拥有 ...
- SharePoint2013 显示网站菜单中设计管理器功能
当部署完SharePoint2013后,并创建了对应的网站集,就开始试图去按照企业VI(Visual Identity)来定制站点的布局.色彩.字体等等的页面元素.可是,在站点的设置菜单中,默认没有“ ...
- winform快速开发平台 -> 通用权限管理之动态菜单
这几个月一直忙APP的项目,没来得及更新项目,想想该抽出时间整理一下开发思路,跟大家分享,同时也希望得到宝贵的建议. 先说一下我们的权限管理的的设计思路,首先一个企业信息化管理系统一定会用到权限管理, ...
- ASP。使用依赖注入的asp.net Core 2.0用户角色库动态菜单管理
下载source code - 2.2 MB 介绍 在开始这篇文章之前,请阅读我的前一篇文章: 开始使用ASP.NET Core 2.0身份和角色管理 在上一篇文章中,我们详细讨论了如何使用ASP.N ...
- 循序渐进VUE+Element 前端应用开发(3)--- 动态菜单和路由的关联处理
在我开发的很多系统里面,包括Winform混合框架.Bootstrap开发框架等系列产品中,我都倾向于动态配置菜单,并管理对应角色的菜单权限和页面权限,实现系统对用户权限的控制,菜单一般包括有名称.图 ...
- C#开发微信门户及应用(6)--微信门户菜单的管理操作
前面几篇继续了我自己对于C#开发微信门户及应用的技术探索和相关的经验总结,继续探索微信API并分享相关的技术,一方面是为了和大家对这方面进行互动沟通,另一方面也是专心做好微信应用的底层技术开发,把基础 ...
- WIN 下的超动态菜单(一)
WIN 下的超动态菜单(一)介绍 WIN 下的超动态菜单(二)用法 WIN 下的超动态菜单(三)代码 作者:黄山松,发表于博客园:http://www.cnblogs.com/tomview/ ...
- RDIFramework.NET ━ 9.6 模块(菜单)管理 ━ Web部分
RDIFramework.NET ━ .NET快速信息化系统开发框架 9.6 模块(菜单)管理 -Web部分 模块(菜单)管理是整个框架的核心,主要面向系统管理人员与开发人员,对普通用户建议不要授 ...
- [ionic开源项目教程] - 手把手教你使用移动跨平台开发框架Ionic开发一个新闻阅读APP
前言 这是一个系列文章,从环境搭建开始讲解,包括网络数据请求,将持续更新到项目完结.实战开发中遇到的各种问题的解决方案,也都将毫无保留的分享给大家. 关注订阅号:TongeBlog ,查看移动端跨平台 ...
- 移动跨平台开发框架Ionic开发一个新闻阅读APP
移动跨平台开发框架Ionic开发一个新闻阅读APP 前言 这是一个系列文章,从环境搭建开始讲解,包括网络数据请求,将持续更新到项目完结.实战开发中遇到的各种问题的解决方案,也都将毫无保留的分享给大家. ...
随机推荐
- Machine Learning Week_4 Neural Networks: Representation
目录 0 Neural Networks: Representation 1 Motivations 1.1 Non-linear Hypotheses 1.2 Neurons and the Bra ...
- JavaScript对象获取属性的方法(.和[]方式)
js对象获取属性有两种方法:1.通过.的方式 2. 通过[]方式 // 通过.方式获取属性值,key是静态的 var aa = {name: "zhang", age: 18}; ...
- 微信H5分享外部链接,缩略图不显示
可关注微信公众号酒酒酒酒查看原文: 前言:最近做了一款推广茶的APP软件,展厅.产品需要分享功能:从APP内分享到H5网页:微信内打开H5网页,点击微信内右上角三个点,可再次分享: 注意:大多数情况下 ...
- npm安装html2canvas依赖报错 npm ERR! Unexpected token < in JSON at position 0 while parsing near '<!DOCTYPE html> npm ERR! <htm...'
今天安装某个依赖时发现npm ERR! 可我是正常操作啊,也没有升级啥的,咋就安装不了了? npm install --save html2canvas 报错信息如下: npm ERR! Unexpe ...
- 深入解析C#异步编程:await 关键字背后的实现原理
C# 异步编程中 await 实现原理详解 在C#中,async 和 await 关键字用于编写异步代码.本文将详细介绍 await 的实现原理,包括状态机的生成.回调函数的注册和触发等关键步骤. 1 ...
- 服务端SSE数据代理与基于fetch的EventSource实现
服务端SSE数据代理与基于fetch的EventSource实现 Server-Sent Events(SSE)是一种由服务器单向推送实时更新到客户端的方案,基本原理是客户端通过HTTP请求打开与服务 ...
- Windows远程登录到VirtualBox安装的Ubuntu11.10
一.环境 本地:Windows xp 虚拟机:VirtualBox 虚拟机的操作系统:Linux-Ubuntu11.10 二.目的 在Windows下通过SecureCRT(putty也可)远程登录到 ...
- 缓存之ehcache 之使用
1. EHCache 的特点,是一个纯Java ,过程中(也可以理解成插入式)缓存实现,单独安装Ehcache ,需把ehcache-X.X.jar 和相关类库方到classpath中.如项目已安装了 ...
- uni-app 蓝牙扫码适配
1.前言 蓝牙设备扫码的效率要高于手机摄像头 App需要进行对蓝牙扫码枪进行适配才能正常使用蓝牙设备枪,并兼容之前的摄像头扫码 适配的关键在于:扫码枪进行扫码时,需要对其进行事件监听,并拿到条码的值 ...
- js模拟点击下载文件到本地
function fake_click(obj) { var ev = document.createEvent("MouseEvents"); ev.initMouseEvent ...