做VFP项目,权限管理是绕不过的坎。10个用户以内还好,一个个配权限就行。50个用户呢?100个呢?每次来新人都要一个一个菜单勾权限,配置到怀疑人生。
今天我们聊聊VFP权限管理的两种模式——用户模式和角色组模式,从设计到代码一次性讲清楚。
一、核心原理:利用菜单的Skip属性
VFP菜单有个属性叫跳过(Skip),可以控制菜单项是否可用。祺佑框架的权限设计就是基于这个简单而强大的机制。
需要三张表
表名 | 用途 |
|---|---|
user_pass | 用户表(账号、密码等) |
Rights | 菜单表(description对应菜单名) |
RoleRight | 权限表(用户/组 + 菜单ID + 是否启用) |
💡 description相当于菜单设计器的提示(菜单名),但可以多个菜单名对应一个description。
二、用户模式:简单直接
思路:每个用户直接配置菜单权限,一人一套。
权限加载代码
TEXT TO lcSQLCmd NOSHOW TEXTMERGE select description, isnull(enabled, 0) enabled, userid from rights left join ( SELECT a1.userid, right_id, enabled FROM RoleRight inner join ( select id userid from user_pass where USER_PASS.id = 1 ) a1 on RoleRight.userid = a1.userid ) b1 on Rights.id = right_id ENDTEXT oDBSQLhelper = Newobject("MSSQLHelper", "MSSQLHelper.prg") If oDBSQLhelper.SQLQuery(lcSQLCmd, "main_menus") < 0 Messagebox(oDBSQLhelper.errmsg) Return.F. Endif用了左连接是因为菜单表随时会增加,这样查询就会将新增菜单项也全部展现,防止找不到权限。
关键技巧:纵向记录变横向属性
* 表变成对象(纵向记录变横向属性,方便菜单权限控制) Select main_menus oMenus = Createobject("empty") Scan AddProperty(oMenus, Alltrim(description), enabled) Endscan AddProperty(_screen, "oMenus", oMenus) && 挂到_screen全局可用这样_screen.oMenus.采购入库就是.T.或.F.,直接控制菜单的Skip属性。
用户模式优点:代码极少,简单高效。
用户模式缺点:人多时配置工作量巨大。
三、角色组模式:人多了就靠它
思路:把权限分配给"组",用户只需选择所属组,自动继承组权限。
数据表改动
user_pass表增加两个字段:
字段 | 类型 | 说明 |
|---|---|---|
u_lx | 逻辑型 | .T.=组,.F.=用户(默认.F.) |
roleid | 整型 | 所属组ID(默认0=没有所属组) |
组和用户在同一个表里,用u_lx字段区分。用户可以设置所属组,但组不可以选所属组。
自联接查询用户和组
Set Multilocks On * 用户列表(自联接,显示用户所属组名) TEXT TO lcSQLCmd noshow select a1.u_name, isnull(b1.u_name, '') rolename, a1.roleid, a1.id, u_lx from user_pass a1 left join ( select id, u_name from user_pass where u_lx = 1 ) b1 on a1.roleid = b1.id ENDTEXT oDBSQLhelper = Newobject("MSSQLHelper", "MSSQLHelper.prg") If oDBSQLhelper.SQLQuery(lcSQLCmd, "user_pass") < 0 Messagebox(oDBSQLhelper.errmsg) Return.F. Endif CursorSetProp("Buffering", 5, "user_pass") && 设置为表缓冲角色组合框设置
* 角色列表(追加空选项方便选择) TEXT TO lcSQLCmd NOSHOW textm SELECT 0 as id, '' as rolename union all SELECT id, u_name as rolename FROM user_pass WHERE u_lx = 1 ENDTEXT If oDBSQLhelper.SQLQuery(lcSQLCmd, "rolelist") < 0 Messagebox(oDBSQLhelper.errmsg) Return.F. Endif关键业务逻辑
1. 组合框显示/隐藏
This.Visible = !user_pass.u_lx && 如果是组,组合框隐藏掉2. 权限列表只读控制
* 用户没有所属组,可以编辑权限列表,否则只读 If user_pass.roleid == 0 IfInlist(Thisform.Opcode, 1, 2) This.ReadOnly = .F. This.column3.Enabled = .T. Else This.ReadOnly = .T. This.column3.Enabled = .F. Endif Else This.ReadOnly = .T. This.column3.Enabled = .F. Endif3. 保存时更新roleid
Tableupdate(1, .T., "user_pass") && 保存缓冲表 * 与DAL_CA共享链接 oDBSQLhelper = Newobject("MSSQLHelper", "MSSQLHelper.prg", "", Thisform.Oca.Datasource) TEXT TO lcSQLCmd NOSHOW TEXTMERGE UPDATE user_pass SET roleid = ?user_pass.roleid WHERE id = ?user_pass.id ENDTEXT If oDBSQLhelper.ExecuteSQL(lcSQLCmd) < 0 Messagebox(Thisform.Oca.msg) Return Endif四、前端加载权限:组优先
前端用户获取菜单权限的逻辑非常简单:
* 判定用户的roleid不为空,就使用roleid加载权限 * 否则使用用户id来加载权限 If !Empty(user_pass.roleid) nLoadId = user_pass.roleid && 用组ID加载 Else nLoadId = user_pass.id && 用用户ID加载 Endif五、进阶:Qiyu_Right权限类
祺佑框架还提供了Qiyu_RightManage权限管理类,支持更精细的权限控制:
属性 | 说明 |
|---|---|
isAdd | 新增权限 |
isEdit | 编辑权限 |
isDel | 删除权限 |
isCheck | 审核权限 |
isDee | 记账权限 |
isPrit | 打印权限 |
isGd1/isGd2/isGd3 | 自定义权限 |
权限控制两层:
1.控制界面控件的开闭— 表单控件的Enabled由权限属性决定
2.控制DAL层方法调用— DAL的Get/Add/Edit/Delete方法受isAdd/isEdit/isDel控制
子类示例:
* 由父类派生一个权限操作类 Define Class QX_SJQX As Qiyu_RightManage Of Locfile("Qiyu_RightManager.prg") * 钩子方法 - 用户自定义 Procedure GetRightStatus This.isadd = .f. This.isedit = .f. This.isdel = .f. EndProc EndDefine * 在表单的Init方法中使用 Thisform.oright = Newobject("qx_sjqx", "qx_sjqx.prg") Thisform.RightRefresh() && 权限刷新 Thisform.oright.SetRightStatus(Thisform) && 继承父类方法六、两种模式怎么选?
维度 | 用户模式 | 角色组模式 |
|---|---|---|
| 适合人数 | ≤10人 | 10人以上 |
| 配置工作量 | 每人一套权限 | 配好组,新人选组即可 |
| 灵活性 | ★★★★★ 个性定制 | ★★★ 组内统一 |
| 代码复杂度 | 简单 | 稍复杂(自联接+缓冲) |
| 维护成本 | 人员变动需逐个调整 | 加组/改组权限即可 |
我的建议:
- 小项目(内部几人用)→ 用户模式,够用就好
- 中大型项目 → 角色组模式,一劳永逸
- 可以先上用户模式,随时平滑升级到角色组模式(数据表结构兼容)
互动时间
你的VFP项目用的哪种权限模式?有没有踩过权限管理的坑?评论区聊聊!
觉得有用,点赞+收藏⭐ 下次开发权限直接抄作业!