问题的提出:作者在从事“住房公积金管理系统”的开发过程中,曾经遇到如下用户需求:1、公积金管理中心及其下属缴存单位要使用不同的管理功能模块;2、同一单位的操作员,可以通过级别来控制其对某些功能模块的使用,以此实现其业务的分工及其责任的界定;3、操作员级别的界定因单位不同,具体要求也有差别,因此,对此操作权限的控制要具有充分的灵活性和随意性;
思路:1、将系统的所有功能模块封装在系统菜单中,以便统一控制;2、所有需要控制的功能菜单项,都能通过操作员权限开关进行设置;3、增加、删除或修改菜单项相关属性后,能自动变更控制项目,实现其自适应性。
实现过程如下:
1. 数据结构
l 创建操作员表 T_operators
字段名称 |
数据类型 |
说明 |
Oper_id |
Char(3) |
操作员编号 3位字符 |
Oper_name |
Char(8) |
操作员姓名 8位字符 |
: |
|
|
: |
|
|
Flag_power |
Char(60) |
操作员权限60位字符,分别对应于60个功能菜单项的执行开关(1开 0关) |
l 创建菜单项控制表 T_menu
字段名称 |
数据类型 |
说明 |
menu_id |
Integer |
菜单项目编号 整数型 |
menu_name |
Char(30) |
菜单项目名称 30位字符 |
Menu_flag |
integer |
菜单项的执行开关(1开 0关) |
2. 创建系统菜单 m_frame (如图)
假设“支取申请”功能仅限于由缴存单位使用,即需要纳入功能控制范围,则设置其Tag属性为1(整数型,对应于T_menu表的“menu_id”字段,在系统菜单中,该值不能重复),而“表格打印”项属于公共使用功能,不需纳入功能控制范围,因此,不设置Tag属性。
3. 创建全局函数F_fillmenu(menu sysmenu)
参数:sysmenu menu对象型
功能:根据系统菜单,填充菜单控制表T_menu。仅将菜单项的Tag值为数值型的菜单项插入该表,因此对不需要功能控制的菜单项,可不设置其Tag属性。
源码如下:
定义全局变量:
Boolean Gb_init=TURE //控制“菜单控制表T_menu”的初始化
Datastore ds_power
定义局部变量:
String ls_opid,mysmenu,ls_name,ls_powerno,ls_syntax,ls_error
Integer nrow,li_opid,i,j,curlevel,sublevel,pos,CurRow
Boolean access
if Gb_Init then //“菜单控制表T_menu”初始化
delete from T_menu ; //删除菜单控制表
if sqlca.sqlcode < 0 then
messagebox(“系统提示”,“删除菜单控制表错误!”)
return –1
else
commit;
endif
ds_power=create datastore
ds_power.DataObject="dw_menu" //菜单控制表t_menu 的数据窗口
ds_power.settransObject(sqlca)
ds_power.reset()
Gb_Init=False
end if
curlevel=upperBound(sysmenu.item)
for i= 1 to curLevel
ls_name= sysmenu.item[i].text
if ls_name='-' then //跳过菜单分割线
continue
end if
ls_powerno=sysmenu.item[i].tag
if IsNumber(ls_powerno) then//如果Tag属性值为数值型,则纳入功能控制
pos=integer(ls_powerno)
ls_name=sysmenu.item[i].text
for j=1 to ds_power.RowCount() //检查菜单项目编号是否重复
if ds_power.object.no[j]=pos then
messagebox("Error","菜单编号["+string(pos)+'__'+ls_name+"]重复!"+'~n~n'+ '此项将被忽略!')
exit
end if
next
if j>ds_power.RowCount() then //如果不重复,插入菜单控制表
CurRow=ds_power.insertRow(0)
ds_power.SetItem(CurRow,"menu_id",pos)
ds_power.SetItem(CurRow,"menu_flag",0)
ds_power.SetItem(CurRow,"Menu_Name",ls_name)
end if
end if
sublevel=upperBound(sysmenu.item[i].item) //检查子菜单项
if sublevel>0 then
Init=False
f_FillMenu(sysmenu.Item[i]) //递归调用
end if
Next
ds_power.update()
if sqlca.sqlcode < 0 then
messagebox(“系统提示”,“保存菜单控制表错误!”)
return –1
else
commit;
endif
return 0
4. 创建全局函数F_SetMenu(menu sysmenu)
参数:sysmenu menu对象型
功能:根据操作员的权限控制字符串,设置菜单项是否可用
源码如下:
定义全局变量:
string Gs_powerflag //操作员的权限控制字符串,第n位对应t_menu
//表中menu_id=n 的菜单项,值=1开启0关闭
定义局部变量:
String ls_opid,mysmenu,ls_name,ls_powerno
Integer nrow,li_opid,i,j,curlevel,sublevel,pos
Boolean access
curlevel=upperBound(mymenu.item)
for i= 1 to curLevel
ls_name= mymenu.item[i].text
if ls_name='-' then
continue
end if
access=TRUE
ls_powerno=mymenu.item[i].tag
if IsNumber(ls_powerno) then
pos=integer(ls_powerno)
if mid(gs_powerflag,pos,1)='0' then //值=1开启0关闭
access=False
end if
end if
ls_name=mymenu.item[i].text
if access then //设置具体菜单项的可用性
if mymenu.item[i].visible then
mymenu.item[i].enabled=TRUE
mymenu.item[i].ToolBarItemVisible=TRUE
else
mymenu.item[i].enabled=FALSE
mymenu.item[i].ToolBarItemVisible=FALSE
end if
else
if mymenu.item[i].Visible then
mymenu.item[i].enabled=FALSE
mymenu.item[i].ToolBarItemVisible=FALSE
end if
end if
sublevel=upperBound(myMenu.item[i].item)
if sublevel>0 then
F_SetMenu(MyMenu.Item[i]) //递归调用
end if
Next
return 0
5. 在系统中的具体应用
l 菜单项目设置
进入系统连接数据库成功后,出现操作员注册画面,登录成功后,从操作员表T_operators中,取出权限字段内容存入全局变量Gs_powerFlag,调用F_SetMenu()进行菜单项目设置即可。在操作员注册窗口中具体实现如下:
menu menu_system
menu_system=w_frame.menuid //w_frame为系统主窗口 MdiHelp!
:
操作员登录处理 …
:
F_SetMenu(menu_system) //菜单项目设置
l 操作员权限设置(如图)
左边为操作员列表(T_operators)数据窗口dw_1,右边为菜单控制项目(T_menu)数据窗口dw_2,其中T_menu.menu_flag字段设置为CheckBox风格,左边选中操作员后,取出其权限控制串T_operators.flag_power,设置菜单控制项目的开关字段T_menu.menu_flag,再对该字段重新修改后,单击“保存”按钮,改写操作员的T_operators.Flag_power字段。上图中的T_menu.menu_flag的设置,对应于T_operators.flag_power的第18—26位为:011011011。
其中:Dw_1.RowFocusChanged事件的编码如下:
int ls_opid,li_row,li_right,i,j,li_currow
string ls_powerflag
li_currow=dw_1.getrow()
ls_powerflag = dw_1.GetItemstring(li_currow, "flag_power")
li_row= dw_2.Retrieve()
gs_powerflag=ls_powerflag
for i=1 to li_row
li_right=integer(mid(ls_powerflag,i,1))
dw_2.setitem( i,"flag",li_right)
next
“保存”按钮clicked事件的编码如下:
String ls_user, ls_pass,ls_power
Int i, rcnt, grow,iops
IF dw_1.ModifiedCount() < 1 and dw_2.modifiedCount()<1 then Return
grow = dw_1.GetRow()
rcnt = dw_2.RowCount()
ls_power=Fill("0",100) //"000...0" len=100
FOR i = 1 TO rcnt
ipos=dw_2.GetItemNumber(i, "no") //菜单项编号
ls_flag=string(dw_2.GetItemNumber(i, "flag"))
ls_power=Replace(ls_power,ipos,1,ls_flag)
NEXT
dw_1.SetItem(grow,"PowerFlag",ls_power)
dw_1.update()
IF Sqlca.sqlcode <> 0 THEN
Rollback;
MessageBox("系统提示", "保存操作员失败!")
ELSE
Commit;
END IF
本程序在Windows 2000/PowerBuilder 8.0下调试通过