3.4 用户权限方案
在制作一些中小型软件时,用户权限是必可不少的,它可以限定用户的使用范围,对一些商业性比较强的企业来说,用户权限是一个非常好的信息保密及安全的方法,它可以将用户分为不同等级,用户只能对权限内的数据进行操作,范围以外的信息将完全被屏蔽。本节将对用户权限的制作过程进行详细的说明。
3.4.1 简单用户权限
简单用户权限是将每个用户对各窗体的操作权限进行设置,它只对各窗体是否可用进行设置,适用于一些权限比较低的应用程序。
1.方案分析
本方案是利用一个公共数组来记录登录用户对各窗体的使用权限,在用户调用窗体时,对相应的数组元素值进行判断,判断是否有权调用本窗体。下面是简单用户权限的流程图。如图3.42所示。
图3.42 简单用户权限的流程图
在制作简单用户权限时,主要有以下几个难点:
(1)如何用TCheckBox组件改变用户数据表中的权限。
(2)当修改登录用户权限时,如何自动更新数组内容。
2.实施过程
本方案以工资管理系统为例,对简单用户权限进行详细说明。
实例位置:光盘\mr\3\3.4\3.4.1\01
本方案实现的“权限设置”窗体效果如图3.43所示。
图3.43 “权限设置”窗体
在制作“用户权限”窗体之前,先要在数据库中制作一个用户表tb_OddPopedom_User,该用户表中记录了用户名、密码和各窗体的使用权限等。用户数据表的关系图如图3.44所示。
图3.44 tb_OddPopedom_User数据表的关系图
在tb_OddPopedom_User数据表中将权限字段名设为“P_”+数字的形式,是为了能够利用数字来修改或获取指定的权限值,以便对各窗体的调用情况进行控制。
下面就对“权限设置”窗体的制作过程进行一下详细的说明。在“权限设置”窗体中放置的TCheckBox组件的先后顺序要与主窗体中各子窗体的调用顺序相同,以防止在设置权限时出现错误。
在“权限设置”窗体显示时,首先要将用户数据表中的所有用户名添加到用户名下拉列表框中,并将当前登录的用户名显示在下拉列表文本框中,然后将当前登录用户的权限以TCheckBox组件进行显示。代码如下:
procedure TFrm_Popedom.FormShow(Sender: TObject);
begin
with DataModule1.ADOQuery1 do //将所有用户名添加到下拉列表中
begin
close;
SQL.Clear;
SQL.Add('select * from tb_OddPopedom_User');
Open;
ComboBox1.Clear;
while not Eof do
begin
ComboBox1.Items.Add(FieldByName('User_Name').AsString);
Next;
end;
end;
M_Popedum(Frm_Interface.Pop_Name,DataModule1.ADOQuery1); //显示当前用户的权限
ComboBox1.Text := Frm_Interface.Pop_Name; //将当前登录用户名显示在下拉列表的文本框中
end;
自定义函数M_Popedum()用于将当前所选择的用户权限按照一定的顺序用TCheckBox组件进行显示。代码如下:
procedure TFrm_Popedom.M_Popedum(SName : String; ADOQuer : TADOQuery);
var
i,p : Integer;
begin
p := 0;
with ADOQuer do //查找当前所选用户的信息
begin
close;
SQL.Clear;
SQL.Add('select * from tb_OddPopedom_User where User_Name='+''''+SName+'''');
Open;
for i:=0 to 12 do //将用户权限以TcheckBox组件进行显示
begin
(self.FindComponent('CheckBox'+inttostr(i+1)) as TCheckBox).Checked :=
FieldByName('P_'+IntToStr(i)).AsBoolean;
if FieldByName('P_'+IntToStr(i)).AsBoolean=False then
p := 1;
end;
if p=0 then //当用户权限全部为True时,全选复选框为选中状态
CheckBox14.Checked := True
else
CheckBox14.Checked := False;
end;
end;
M_Popedum()过程的参数说明如表3.12所示。
表3.12 M_Popedum()过程的参数说明
参数 |
说明 |
SName |
用户名 |
ADOQuer |
TADOQuery类型,用来查找SName用户名的信息 |
在“权限设置”窗体中,操作员可以通过用户名下拉列表动态显示所选用户的权限信息。可以在ComboBox1组件的OnChange事件实现。代码如下:
procedure TFrm_Popedom.ComboBox1Change(Sender: TObject);
begin
M_Popedum(ComboBox1.Text,DataModule1.ADOQuery1);
end;
在窗体中对各权限进行设置时,当权限为全选状态时,“全选”复选框为选中状态,当权限有一个为非选中状态时,“全选”复选框将自动变为非选中状态,那么,如何才能更简便的实现这一操作呢?可以在CheckBox1组件的单击事件中利用Sender对象来判断当前组件的Checked属性值。代码如下:
procedure TFrm_Popedom.CheckBox1Click(Sender: TObject);
begin
if TCheckBox(Sender).Checked = False then
CheckBox14.Checked := False; //全选复选框
end;
在其它TCheckBox组件(CheckBox14组件不设置)的对象编辑器的Events选项卡中的OnClick下拉列表中选择CheckBox1Click即可。
当选中“全选”复选框(CheckBox14)时,将使窗体中的所有TCheckBox组件设为可选状态,该操作可以在OnClick事件中完成,也可以在OnMouseUp事件中完成。代码如下:
procedure TFrm_Popedom.CheckBox14MouseUp(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
i : Integer;
t : Boolean;
begin
if CheckBox14.Checked then
t := True
else
t := False;
for i:=1 to 13 do
(self.FindComponent('CheckBox'+inttostr(i)) as TCheckBox).Checked := t;
end;
对各窗体的权限设置完成后,就可以单击“确定”按钮来进行保存,在对当前登录用户的权限进行修改后,将修改后的权限保存在公共数组APopedom中,以便在退出“权限设置”窗体后,可以直接对调用窗体的权限进行控制。代码如下:
procedure TFrm_Popedom.Button1Click(Sender: TObject);
var
i : Integer;
begin
with DataModule1.ADOQuery1 do
begin
close;
SQL.Clear;
SQL.Add('select * from tb_OddPopedom_User where User_Name='+''''
+Trim(ComboBox1.Text)+'''');
Open;
Edit;
for i:=0 to 12 do
begin
FieldByName('P_'+IntToStr(i)).AsBoolean := (self.FindComponent('CheckBox'+
inttostr(i+1)) as TCheckBox).Checked;
if Trim(ComboBox1.Text)=Frm_Interface.Pop_Name then //判断修改的是否为登录用户
Frm_Interface.APopedom[i] := FieldByName('P_'+IntToStr(i)).AsBoolean;
end;
Post;
end;
Close;
end;
当“权限设置”窗体制作完成后,如何根据用户权限对主窗体中的各个子窗体进行控制呢,下面对其进行详细的讲解。
在“用户登录”窗体的“登录”按钮中,当用户名和密码正确时,将登录用户的权限存入主窗体(Frm_Interface)中的公共数组APopedom中。部分代码如下:
If 用户名和密码正确 then
begin
for i:=0 to 12 do
begin
pp := 'P_'+IntToStr(i); //动态组合权限字段名称
Frm_Interface.APopedom[i] := DataModule1.ADO_User.fieldbyname(pp).AsVariant;
end;
Frm_Interface.Pop_Name := Trim(Edit1.Text);
self.Close;
end;
当进入主窗体后,在调用子窗体时,根据数组中各元素的值判断该窗体是否有权被调用。下面给出其中一个子窗体的调用代码,以供参考。
procedure TFrm_Interface.N10Click(Sender: TObject); //TMainMenu组件的子菜单项
begin
if APopedom[9]=True then
begin
Application.CreateForm(TFrm_ManageUser,Frm_ManageUser);
Frm_ManageUser.ShowModal;
Frm_ManageUser.Free;
end
else
showmessage('无权限进入本窗体');
end;
注意:数组标志号与权限字段和窗体的调用顺序必需相同,也就是将用户的权限信息存入数组时,存入顺序必需与窗体的调用顺序相同。
3.补充说明
在本方案中是用数组中的元素值分别在各窗体调用时进行判断,此方法必需在用户登录后,将该用户的权限存入到数组中,也可以直接在用户表tb_OddPopedom_User中查找当前登录用户的使用权限,对指定窗体的权限进行判断,可以定义一个自定义函数FindPart(),用来在tb_OddPopedom_User数据表中查找指定窗体的权限。代码如下:
function FindPart(UserName: String; N: Integer): Boolean;
begin
result := False;
with DataModule1.ADOQuery2 do
begin
Close;
SQL.Clear;
SQL.Add('select * from tb_OddPopedom_User where User_Name='+''''+UserName+''''
+' and P_'+IntToStr(N)+'='+''''+'1'+'''');
open;
if RecordCount>0 then
result := True;
end;
end;
FindPart()过程的参数说明如表3.13所示。
表3.13 FindPart()过程的参数说明
参数 |
说明 |
UserName |
当前登录的用户名称 |
N |
窗体所对应的权限号,也就是P_后面的编号值 |
3.4.2 模块式用户权限
模块式用户权限实际上就在数据库中设计一个数据表,该数据表记录了用户所涉及的所有权限,并以模块的形式进行保存,当添加一个新用户时,将该模块中的权限信息复制到用户权限表中,这样,便可以直接对用户权限进行设置。
1.方案分析
本方案是将用户的所有权限存放在一个模块数据表中,在添加用户时,将添加用户的编号与模块数据表中的信息组合成一个新表,在该表中可以对指定用户的权限进行设置,以及通过权限值对主窗体菜单的显示情况进行控制。下面是模块式用户权限的流程图。如图3.45所示。
图3.45 模块式用户权限的流程图
在制作模块式用户权限时,主要有以下几个难点:
(1)如何在TTreeView组件以层级的方式显示主窗体、子窗体,以及子窗体中的各组件。
(2)如何在TTreeView组件中添加复选框。
(3)如何在TTreeView组件中保存修改后的用户权限。
2.实施过程
本方案以进销存管理系统为例,以树型复选框的形式对用户的权限进行详细讲解。
实例位置:光盘\mr\3\3.4\3.4.2\01
本方案实现的“权限设置”窗体效果如图3.46所示。
图3.46 “权限设置”窗体
在设置“权限窗体”之前,先要在数据库中创建用户表(tb_Branch_User)、用户权限表(tb_Tree_Popedom)和权限模块表(tb_TemPop)。
交叉链接:tb_TemPop数据表已在3.1.3节中进行了说明,tb_Branch_User数据表已在3.2.3节的实施过程中进行了说明,在这里不作讲解。
下面只对用户权限表(tb_Tree_Popedom)进行说明,用户权限表的关系图如图3.47所示。
图3.47 tb_Tree_Popedom数据表的关系图
tb_Tree_Popedom数据表是用户编号与tb_TemPop数据表所生成的,在该表中记录了所有用户的权限,各用户的权限以用户编号进行区分,在添加用户时,同时在tb_Tree_Popedom数据表中添加权限。
下面就对“权限设置”窗体的制作过程进行详细的说明。
注意:本方案为了可以直接对当前所选用户的权限进行设置,在DataModule1窗体中用ADO_Pop(TADOQUery)组件一直与用户数据表tb_Branch_User进行连接,这样可以通过ADO_Pop组件直接获取当前操作用户的相关信息。
在图3.46中可以看出,在TreeView1组件中以复选框的形式来对各窗体及组件的权限进行设置,在Delphi的TreeView1组件中是没有复选框功能的,本方案是利用TreeView1组件的Images属性,将ImageList1组件中的图片显示在TreeView1组件上,并通过对ImageList1组件索引号的设置,改变在TreeView1组件中的样式。具体实现将在TreeView1组件的应用进行讲解。
在“权限设置”窗体显示时,可以在窗体的OnShow事件中,将当前所选用户的权限显示在TreeView1组件上。代码如下:
procedure TFrm_Popedom.FormShow(Sender: TObject);
begin
//在状态栏中显示当前操作的用户名
StatusBar1.Panels[0].Text := '正在被修改权限的用户:'+
DataModule1.ADO_user.FieldByName('User_Name').AsString;
TreeView1.Items.Clear;
myshow(DataModule1.ADOQuery2,'0',Nil);
end;
自定义函数myshow(),用于在TreeView1组件中显示当前用户权限中的根节点信息,也就是主窗体中的主菜单项。代码如下:
procedure TFrm_Popedom.myshow(ADOQuer: TADOQuery; S : String; N : TTreeNode);
var
i : integer;
TempNode:TTreeNode;
CRMRecord:PCRMRecord; //定义一个PCRMRecord结构的变量
begin
with ADOQuer do
begin
close;
SQL.Clear;
SQL.Add('select * from tb_Tree_Popedom where (Fu_ID ='+''''+S+''''+') and
(User_ID='+''''+Trim(DataModule1.ADO_user.FieldByName('User_ID').AsString)+
''''+')'); //查询用户权限表中当前用户Fu_ID字段为0的信息,也就是根节点信息
open;
end;
for i:=0 to ADOQuer.RecordCount-1 do //将所有根节点信息显示在TreeView1组件上
begin
TempNode := TreeView1.Items.AddChild(N,ADOQuer.FieldByName
('Tree_Name').AsString);
if ADOQuer.FieldByName('User_Pop').AsBoolean then
begin
TempNode.ImageIndex := 1; //当权限为1时,设置节点前面的图片为选中图片
end
else
begin
TempNode.ImageIndex := 0; //设置图片为非选中图片
end;
TempNode.SelectedIndex := TempNode.ImageIndex;
//用CRMRecord结构变量记录所有根节点的主ID号
New(CRMRecord);
CRMRecord.ID := ADOQuer.FieldByName('Zhu_ID').AsString;
TempNode.Data := CRMRecord;
//在根节点下,添加一个空的子节点,用于在根节点前显示一个扩展标记
TreeView1.Items.AddChild(TempNode,'');
ADOQuer.Next;
end;
end;
myshow()过程的参数说明如表3.14所示。
表3.14 myshow()过程的参数说明
参数 |
说明 |
ADOQuer |
TADOQuery类型,用于操作的数据表 |
S |
tb_Tree_Popedom表中Fu_ID字段的值 |
N |
TTreeNode类型,表示节点值,当N为Nil时,表示添加的是根节点,否则是N节点下的子节点 |
在使用PCRMRecord结构前,首先要在该窗体的单元中定义一个结构类型的指针,用于记录各节点名称在数据库中的主ID号。代码如下:
type
PCRMRecord = ^TCRMRecord;
TCRMRecord = record
ID:string;
end;
TFrm_Popedom = class(TForm)
在窗体显示时,TreeView1组件中只是添加了根节点,可以通过该组件的OnExpanding事件在扩展当前节点时,在该节点的下面添加相应的子节点。代码如下;
procedure TFrm_Popedom.TreeView1Expanding(Sender: TObject; Node: TTreeNode;
var AllowExpansion: Boolean);
begin
Node.DeleteChildren;
myshow(DataModule1.ADOQuery2,PCRMRecord(Node.data).ID,Node);
end;
在TreeView1组件中显示当前用户的所有权限信息后,就是对各权限进行控制,为了避免在单击节点扩展按钮或双击节点时,重新加载子节点,使子节点所设置的权限恢复为原始状态,可以在节点上右击鼠标,改变节点左边的图片样式,同时保存修改后的节点权限。这一过程可以在TreeView1组件的OnMouseUp事件中完成。代码如下:
procedure TFrm_Popedom.TreeView1MouseUp(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
Node:TTreeNode;
begin
if Button = mbRight then //如果单击鼠标右键
begin
Node := TreeView1.GetNodeAt(x,y); //获取当前节点的节点值
if Assigned(Node) then //如果节点不为空
begin
if Node.ImageIndex = 0 then //对图片的索引号进行切换,
begin
Node.ImageIndex := 1;
end
else
begin
Node.ImageIndex := 0;
end;
Node.SelectedIndex := Node.ImageIndex;
with DataModule1.ADOQuery2 do //下面对修改的权限进行保存
begin
close;
SQL.Clear;
//通过PCRMRecord(Node.data).ID获当前节点名在数据库中的主ID号
SQL.Add('select * from tb_Tree_Popedom where (zhu_id ='+''''+
PCRMRecord(Node.data).ID +''''+') and (User_ID='+''''+
Trim(DataModule1.ADO_user.FieldByName('User_ID').AsString)+''''+')');
open;
Edit;
if Node.ImageIndex=1 then //根据图片的索引号,设置当前节点的权限值
DataModule1.ADOQuery2.FieldByName('User_Pop').AsBoolean := True
else
DataModule1.ADOQuery2.FieldByName('User_Pop').AsBoolean := False;
Post;
end;
end;
end;
end;
如果是对当前登录用户的权限进行设置,那么在设置完成后,返回“操作员管理”窗体和主窗体时,应该根据修改后的权限重新进行显示,可以在“权限设置”窗体的OnClose事件中判断修改的是否为当前登录用户。代码如下:
procedure TFrm_Popedom.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
if DataModule1.ADO_user.FieldByName('User_Name').AsString=Frm_Main.User_Name then
begin
Frm_Main.MenuPop; //修改主窗体中各菜单的显示情况
//修改“操作员管理”窗体中各按钮的可用状态
Frm_Main.ModuleIfTrue(DataModule1.ADO_Pop,Frm_ManageUser);
end;
end;
自定义过程MenuPop和ModuleIfTrue()是在主窗体中定义的一个公共过程,用于根据权限改变主窗体及各子窗体中组件的显示情况,下面对这两个自定义过程进行详细说明。
自定义过程MenuPop是用MainMenu1组件中主菜单项中的Hint属性值,在tb_Tree_Popedom数据表进行查找,如果主菜单项的权限可用,则查找主菜单项下的各子菜单项,当子菜单项的权限不可用时,将子菜单项的Enabled属性设为False(不可用),否则将Enabled属性设为True(可用),如果主菜单项的权限为不可用,则将Enabled属性设为False。代码如下:
procedure TFrm_Main.MenuPop;
var
i,j : Integer;
F_ID : String;
begin
with DataModule1.ADO_Pop do
begin
Close;
SQL.Clear;
//在tb_Tree_Popedom表中查找当前登录用户Title字段为1(窗体信息)的所有信息
SQL.Add('Select * from tb_Tree_Popedom where User_ID='+''''+Frm_Main.Logon_ID
+''''+' and Title=1');
Open;
end;
DataModule1.ADO_Pop.First;
for i:=0 to MainMenu1.Items.Count-1 do //对主菜单项进行遍历
begin
while Not DataModule1.ADO_Pop.Eof do
begin
if MainMenu1.Items[i].Hint=DataModule1.ADO_Pop.FieldByName
('Tree_Name').AsString then //查找指定的主菜单项
begin
if DataModule1.ADO_Pop.FieldByName('User_Pop').AsBoolean=True then
begin //如果主菜单项的权限可用
F_ID := DataModule1.ADO_Pop.FieldByName('ZHu_ID').AsString;
MainMenu1.Items[i].Enabled := True;
with DataModule1.ADOQuery3 do //查找当前主菜单项下的所有子菜单项
begin
Close;
SQL.Clear;
SQL.Add('Select * from tb_Tree_Popedom where User_ID='+''''+
Frm_Main.Logon_ID+''''+' and Title=1 and Fu_ID='+''''+F_ID+'''');
Open;
end;
DataModule1.ADOQuery3.First;
for j:=0 to MainMenu1.Items[i].Count-1 do //遍历当前主菜单项的所有子菜单项
begin
while Not DataModule1.ADOQuery3.Eof do
begin
if DataModule1.ADOQuery3.FieldByName('Tree_Name').AsString=
MainMenu1.Items[i].Items[j].Hint then //如果查找到子菜单项
begin
//当子菜单项的权限为可用时,将子菜单项设为可用状态,否则设为不可用状态
if DataModule1.ADOQuery3.FieldByName('User_Pop').AsBoolean<>True then
MainMenu1.Items[i].Items[j].Enabled := False
else
MainMenu1.Items[i].Items[j].Enabled := True;
end;
DataModule1.ADOQuery3.Next;
end;
DataModule1.ADOQuery3.First;
end;
end
else //当主菜单项的权限为不可用时,将主菜单项设为不可用状态
MainMenu1.Items[i].Enabled := False;
end;
DataModule1.ADO_Pop.Next;
end;
DataModule1.ADO_Pop.First;
end;
end;
注意:在MainMenu1组件各菜单项的Hint属性中输入与当前菜单项名称相同的文本,而且该文本要与tb_TemPop数据表中的Tree_Name字段中的信息相对应,以便查找权限值。
自定义过程MenuPop在主窗体的显示事件(OnShow)中进行调用,用于在用户登录后,直接对主窗体的各菜单项的使用情况进行设置。代码如下:
procedure TFrm_Main.FormShow(Sender: TObject);
begin
//动态调用“登录”窗体
Application.CreateForm(TFrm_Logon,Frm_Logon);
Frm_Logon.ShowModal;
Frm_Logon.Free;
MenuPop;
end;
自定义过程ModuleIfTrue(),用于控制指定窗体中各组件的可用状态(必需是在tb_TemPop数据表中设定的组件)。代码如下:
procedure TFrm_Main.ModuleIfTrue(ADOQuer: TADOQuery; Frm: Tform);
var
i : Integer;
F_Com : TControl;
Z_ID : String;
begin
with ADOQuer do
begin
Close;
SQL.Clear;
//查找当前登录用户调用指定窗体在tb_Tree_Popedom数据表中的信息
SQL.Add('select * from tb_Tree_Popedom where User_ID='+''''+Frm_Main.Logon_ID+
''''+' and Tree_Name='+''''+Frm.Caption+'''');
OPen;
Z_ID := FieldByName('ZHu_ID').AsString; //获取当前窗体的主ID号
Close;
SQL.Clear;
//根据主ID号,查找当前窗体下的所有组件信息
SQL.Add('select * from tb_Tree_Popedom where User_ID='+''''+Frm_Main.Logon_ID
+''''+' and Fu_ID='+''''+Z_ID+'''');
Open;
end;
for i:=0 to Frm.ComponentCount-1 do //遍历所有组件
begin
if Frm.Components[i] is TControl then //是否为可视组件
begin
F_Com := TControl(Frm.Components[i]); //将组件名称存入到F_Com变量中
while Not ADOQuer.Eof do
begin
//如果当前组件在tb_Tree_Popedom数据表中
if F_Com.Hint = ADOQuer.FieldByName('Tree_Name').AsString then
//根据权限设置当前组件的可用状态
if ADOQuer.FieldByName('User_Pop').AsBoolean=False then
F_Com.Enabled := False
else
F_Com.Enabled := True;
ADOQuer.Next;
end;
ADOQuer.First;
end;
end;
end;
注意:自定义过程ModuleIfTrue()是根据各窗体的Caption属性值来查找该窗体中的组件并进行控制,所以,窗体的Caption属性值必需与tb_TemPop数据表中的Tree_Name字段值相同。
ModuleIfTrue()过程的参数说明如表3.15所示。
表3.15 ModuleIfTrue()函数的参数说明
参数 |
说明 |
ADOQuer |
TADOQuery类型,用于获取数据表中的信息 |
Frm |
窗体名称 |
在各子窗体的显示事件(OnShow)中,调用ModuleIfTrue()过程对当前窗体中的各组件的使用情况进行设置。代码如下:
procedure TFrm_ManageUser.FormShow(Sender: TObject);
begin
Frm_Main.ModuleIfTrue(DataModule1.ADO_Pop,Frm_ManageUser);
//其它操作代码
end;
3.补充说明
本方案中的用户权限模块表(tb_TemPop数据表)中的信息是以手动方式在数据库中进行输入的,可以在用户表中设置一个操作员用户,该用户进入程序后,可以直接通过“权限设置”窗体在各子窗体的节点下添加窗体中没有权限的组件,或是删除已设置权限的组件,这样,使用都可以动态控制各窗体内的按钮使用情况。
首先要在右击窗体节点时,弹出PopupMenu1组件,主要是在右击节点时,如何将焦点落在当前节点上,可以在TreeView1组件的OnMouseDown事件中完成。代码如下:
procedure TFrm_Popedom.TreeView1MouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
Node:TTreeNode;
begin
Node := TreeView1.GetNodeAt(X,Y);
Node.Selected := True;
Node.Focused := True;
end;
下面就是如何在鼠标右击窗体节点后,弹出一个快捷菜单,可以在TreeView1组件的OnContextPopup事件中完成。代码如下:
procedure TFrm_Popedom.TreeView1ContextPopup(Sender: TObject;
MousePos: TPoint; var Handled: Boolean);
begin
TreeView1.PopupMenu := Nil;
if TreeView1.Selected<>Nil then
if TreeView1.Selected.Level=1 then
begin
//保存当前节点的名称,用于搜所该节点下的所有子节点
NodeName := TreeView1.Selected.Text;
TreeView1.PopupMenu := PopupMenu1;
end
else
Exit;
end;
注意:使用OnContextPopup事件必需在窗体加添加一个TPopupMenu组件,并在TreeView1组件的PopupMenu属性中添加该组件。
为了方便添加各窗体的组件,在tb_TemPop数据表中多添加一个字段ControlName,用于记录各窗体或组件的Name属性值,这样就可以不用对组件的Hind属性进行设置,直接用组件的Name属性值在ControlName字段中进行查找。
接下来就是根据PopupMenu1组件中的“添加”和“修改”菜单项,在节点下添加或修改指定的节点信息,在单击“添加”菜单项时,将弹出“添加组件节点”窗体(Frm_AddNode),如图3.48所示。
图3.48 “添加组件节点”窗体
在该窗体中的下拉列表中保存当前节点窗体中没有设置权限的组件的Name属性名,可以在下拉列表中进行选择,在中文名文本框中输入该按钮的中文名称,单击“确定”按钮进行添加,在添加该节点时,tb_TemPop数据表中的Zhu_ID字段值是自动添加的,Fu_ID字段值是父节点的Zhu_ID字段信息等。也可以在该窗体中用TDBGrid组件显示已有权限的组件信息,防止按钮的中文名称出现重复。
为了可以直接从指定的窗体中获取各组件的信息,可以在“添加组件节点”窗体的OnShow事件中根据当前节点在数据库中的ControlName字段值(进货单录入窗体为Frm_StordOdd),动态创建窗体,并将窗体中的组件名称(Name属性)添加到下拉列表中。代码如下:
procedure TFrm_AddNode.FormShow(Sender: TObject);
begin
ComboBox1.Clear;
Edit1.Clear;
//公共变量FrmName用于在tb_TemPop数据表的字段ControlName中获取窗体名称,如:'Frm_StordOdd'
OpenFrom(FrmName);
end;
通过自定义过程OpenFrom()动态创建窗体,并将窗体中的组件名添加到下拉列表中。代码如下:
procedure OpenFrom(FName: String); //参数Fname记录了动态窗体的名称
var
MyClass : TComponentClass;
begin
MyClass := TComponentClass(GetClass('T'+FName));
Application.CreateForm(MyClass, TForm(FName));
//将动态打开窗体中的没有设置权限的组件添加到下拉列表中
TForm(FName).Free;
end;
删除按钮的权限,只需要在tb_TemPop数据表查找指定窗体的按钮名,并进行删除便可。
这里只是对用户自动添加和删除权限进行了简要说明。
3.4.3 角色用户权限
角色用户权限是将不同的用户名按照相同的权限分成不同的角色,通过对角色权限的设置,可以改变该角色下各用户的权限。
1.方案分析
本方案是将用户的所有权限放置在一个权限模块表中,并根据各角色的操作范围将相应的权限存放在角色权限表中,可以在一个角色中添加多个用户,并根据该角色权限对应用程序进行控制。下面是角色用户权限的流程图。如图3.49所示。
图3.49 角色用户权限的流程图
在制作角色用户权限时,主要有以下几个难点:
(1)如何将TreeView1组件中的节点添加到TreeView1组件中。
(2)如何将TreeView1组件根节点的所有节点添加到TreeView1组件中。
(3)如何将添加后的节点保存在数据库中。
(4)如何在TTreeView组件中删除一个节点或根节点下的所有节点。
2.实施过程
本方案以进销存管理系统为例,对角色权限的添加、删除及其设置进行详细的讲解。
实例位置:光盘\mr\3\3.4\3.4.3\01
本方案实现的“操作员权限”窗体效果如图3.50所示。
图3.50 “操作员权限”窗体
在对“操作员角色”窗体设置前,先要在数据库中创建角色信息表(tb_Part_Table)、用户登录表(tb_Part_User)、角色权限表(tb_Part_Module)和模块权限表(tb_Part_Tem)。
下面以数据表关系图的形式对各数据表进行分析。
角色信息表(tb_Part_Table)是记录用户所添中的角色编号和角色名称。角色信息表的关系图如图3.51所示。
图3.51 tb_Part_Table数据表的关系图
用户登录表(tb_Part_User)是角色编号与用户信息所生成的,用户登录表的关系图如图3.52所示。
图3.52 tb_Part_User数据表的关系图
模块权限表(tb_Part_Tem)记录了所有窗体及组件的权限信息,该表与3.1.3节中tb_TemPop数据表的应用相同,在这里不作讲解,模块权限表的关系图如图3.53所示。
图3.53 tb_Part_Tem数据表的关系图
角色权限表(tb_Part_Module)是角色编号与模块权限表所生成的,用户所添加的所有角色权限都保存在该表中。角色权限表的关系图如图3.54所示。
图3.54 tb_Part_Module数据表的关系图
通过对用户登录表和角色权限表的分析,可以看出各用户的权限,实际上就是所在角色的权限,而用户并没有实际意义上的权限。
下面对角色用户权限的制作进行详细的讲解。
首先对“操作员权限”窗体中主要组件的相关设置进行一下说明,如表3.16所示。
表3.16 各窗体的相关组件
对象名 |
对象类型 |
属性 |
值 |
描述 |
Panel2 |
TPanel |
Align |
alRight |
用于在窗体的左边存放角色和用户的相关信息 |
BevelOuter |
bvNone |
|||
Image3 |
TImage |
Align |
alClient |
在Panel2上显示背景图片 |
Panel3 |
TPanel |
Align |
alTop |
该组件用于存放角色和用户的添加、修改和删除按钮 |
BevelOuter |
bvNone |
|||
Panel4 |
TPanel |
Align |
alTop |
该组件用于存放角色和用户添加信息所使用的文本框 |
BevelOuter |
bvNone |
|||
TreeView1 |
TTreeView |
BorderStyle |
bsNone |
以节点来显示已添加的角色和用户名 |
Images |
ImageList1 |
|||
ReadOnly |
True |
|||
Panel1 |
TPanel |
Align |
alClient |
用于在窗体的右边存放设置角色权限的相关信息 |
BevelOuter |
bvNone |
|||
TreeView2、TreeView3 |
TTreeView |
BorderStyle |
bsNone |
TreeView3用来显示模块权限表(tb_Part_Tem);TreeView2用来显示在TreeView3中所获取的节点信息 |
Images |
ImageList1 |
|||
ReadOnly |
True |
|||
ListView1 |
TListView |
Ctl3D |
False |
用来显示各窗体中的按钮权限 |
GridLines |
True |
|||
ViewStyle |
vsReport |
|||
Columns |
添加“选项”和“功能”按钮 |
对窗体的设置有一定了解后,下面对各组件的相关操作进行一下详细说明。
l 窗体的显示及最大化、最小化部分
在窗体显示时,首先将模块权限表(tb_Part_Tem)中的信息显示在TreeView3组件上(窗体右边的树形组件),以便在添加角色时,根据该组件中的节点来添加角色权限,将已添加的角色和用户名显示在TreeView1组件上(窗体左边的树形组件),同时用MaxModule和AModule数组来记录窗体最大化和还原时各组件的位置,并根据权限设置窗体中各组件的可用状态。代码如下:
procedure TFrm_User_Part.FormShow(Sender: TObject);
begin
self.Height := 605;
self.Width := 777;
IsMax := 0;
SDirIma := Extractfilepath(Application.ExeName)+'\Image\'; //记录各按钮图片的位置
UDirIma := SDirIma+'U';
//记录原始窗体组件的位置
AModule[1] := Label1.Left;
AModule[2] := Label2.Left;
AModule[3] := Label3.Left;
AModule[4] := Label4.Left;
AModule[5] := Label5.Left;
AModule[6] := Label6.Left;
AModule[7] := Label7.Left;
AModule[8] := Label8.Left;
AModule[9] := Label9.Left;
AModule[10] := Label10.Left;
AModule[11] := Edit1.Left;
AModule[12] := Edit2.Left;
AModule[13] := TreeView2.Height;
AModule[14] := ListView1.Top;
AModule[15] := BitBtn1.Top;
AModule[16] := BitBtn2.Top;
AModule[17] := BitBtn3.Top;
AModule[18] := Image10.Left;
AModule[19] := TreeView3.Height;
AModule[20] := TreeView1.Top;
AModule[21] := TreeView1.Height;
AModule[22] := Panel4.Height;
AModule[23] := TreeView1.Width;
AModule[24] := CheckBox2.Left;
//记录窗体最大化组件的位置
MaxModule[1] := AModule[1]+30;
MaxModule[2] := AModule[2]+30;
MaxModule[3] := AModule[3]+30;
MaxModule[4] := AModule[4]+30;
MaxModule[5] := AModule[5]+30;
MaxModule[6] := AModule[6]+30;
MaxModule[7] := AModule[7]+30;
MaxModule[8] := AModule[8]+30;
MaxModule[9] := AModule[9]+30;
MaxModule[10] := AModule[10]+30;
MaxModule[11] := AModule[11]+30;
MaxModule[12] := AModule[12]+30;
MaxModule[13] := AModule[13]+140;
MaxModule[14] := AModule[14]+140;
MaxModule[15] := AModule[15]+140;
MaxModule[16] := AModule[16]+140;
MaxModule[17] := AModule[17]+140;
MaxModule[18] := AModule[18]+30;
MaxModule[19] := AModule[19]+140;
MaxModule[20] := AModule[20];
MaxModule[21] := AModule[21]+140;
MaxModule[22] := AModule[22]+140;
MaxModule[23] := AModule[23]+250;
MaxModule[24] := AModule[24]+30;
Edit1.Clear;
Edit2.Clear;
TreeView3.Items.Clear;
//在TreeView3组件上添加模块权限表(tb_Part_Tem)中的信息
myshow(TreeView3,DataModule1.ADOQuery2,DataModule1.ADOQuery3,
'0','tb_Part_Tem','',Nil,0);
TreeViewShow; //在TreeView1组件中显示角色和用户
//根据角色权限表(tb_Part_Module)控制“操作员权限”窗体中各组件的可用状态
Frm_Main.ModuleIfTrue(DataModule1.ADOQuery2,Frm_User_Part);
Label11.Enabled := CheckBox1.Enabled;
//在窗体显示时,使各按钮显示原始状态
if Image4.Enabled=True then
Image4.Picture.LoadFromFile(SDirIma+'\1.jpg')
else
Image4.Picture.LoadFromFile(SDirIma+'\11.bmp');
if Image5.Enabled=True then
Image5.Picture.LoadFromFile(SDirIma+'\5.jpg')
else
Image5.Picture.LoadFromFile(SDirIma+'\12.bmp');
if Image6.Enabled=True then
Image6.Picture.LoadFromFile(SDirIma+'\3.jpg')
else
Image6.Picture.LoadFromFile(SDirIma+'\13.bmp');
if Image7.Enabled=True then
Image7.Picture.LoadFromFile(SDirIma+'\7.jpg')
else
Image7.Picture.LoadFromFile(SDirIma+'\14.bmp');
end;
自定义过程myshow(),用于在TreeView3组件上添加模块权限表(tb_Part_Tem)中根节点信息。代码如下:
procedure TFrm_User_Part.myshow(TreeV : TTreeView; ADOQuer,ADOQuer1: TADOQuery; S,
NTable,NField : String; N : TTreeNode; P : Integer);
var
i : integer;
TempNode:TTreeNode;
CRMRecord:PCRMRecord;
S1 : String;
begin
S1 := '';
if Length(NField)>0 then
S1 := ' and '+NField+'='+''''+PartName+'''' //查询条件,在表中查询指定的角色
else
S1 := '';
with ADOQuer do //查询权限信息
begin
close;
SQL.Clear;
SQL.Add('select * from '+NTable+' where FU_ID='+''''+S+''''+S1);
open;
end;
for i:=0 to ADOQuer.RecordCount-1 do
begin
TempNode := TreeV.Items.AddChild(N,ADOQuer.FieldByName('Tree_Name').AsString);
//用CRMRecord结构变量记录所有根节点的主ID号
New(CRMRecord);
CRMRecord.ID := ADOQuer.FieldByName('Zhu_ID').AsString;
TempNode.Data := CRMRecord;
//如果P为0时,查找当前节点是否有子节点,如果有,添加一个空的子节点
if p=0 then
begin
with ADOQuer1 do
begin
close;
SQL.Clear;
SQL.Add('select * from '+NTable+' where FU_ID='+''''+ADOQuer.FieldByName
('Zhu_ID').AsString+''''+S1);
Open;
end;
if ADOQuer1.RecordCount>0 then
TreeV.Items.AddChild(TempNode,'');
end;
ADOQuer.Next;
end;
end;
myshow()过程的参数说明如表3.17所示。
表3.17 myshow()函数的参数说明
参数 |
说明 |
TreeV |
TTreeView类型,表示要添加节点的TTreeView组件 |
ADOQuer,ADOQuer1 |
TADOQuery类型,数据源 |
S |
表示副ID值 |
NTable |
表名 |
NField |
字段名 |
N |
表示节点值 |
P |
表示是否查询了节点 |
自定义过程TreeViewShow是在TreeView1组件中显示已存在的角色名,当该角色下存在用户时,在当前角色节点下添加一个空的子节点。代码如下:
procedure TFrm_User_Part.TreeViewShow;
var
S1 : String;
TempNode : TTreeNode;
begin
S1 := '';
with DataModule1.ADOQuery2 do //获取tb_Part_Table表中的信息
begin
Close;
SQL.Clear;
SQL.Add('select * from tb_Part_Table');
Open;
if RecordCount>0 then
begin
First;
while Not Eof do
begin
S1 := FieldByName('Part_ID').AsString; //获取角色编号
//将角色名添加到TreeView1组件上
TempNode := TreeView1.Items.AddChild(nil,FieldByName('Part_Name').AsString);
with DataModule1.ADOQuery3 do //根据角色编号查找该角色下是否有用户
begin
Close;
SQL.Clear;
SQL.Add('select * from tb_Part_User where Part_ID='+''''+S1+'''');
Open;
if RecordCount>0 then //如果有,在该角色下添加一个空子节点
TreeView1.Items.AddChild(TempNode,'');
end;
Next;
end;
end;
end;
end;
在使用“操作员角色”窗体时,为了更大限度的观看权限的相关内容,可以对窗体进行最大化和还原的操作,该操作可自定义一个最大化和还原的消息,在该窗体的单元中定义一个WMSysCommand()消息。代码如下:
Procedure WMSysCommand(Var message : TMessage) ; Message WM_SYSCOMMAND;
WMSysCommand()消息的实现代码如下:
procedure TFrm_User_Part.WMSysCommand(var message: TMessage);
begin
if (Message.WParam = SC_MAXIMIZE) then //当窗体最大化时
begin
MaxFrm;
IsMax:=1;
end;
if (Message.WParam = SC_RESTORE) then //当窗体还原时
begin
DarbarismFrm;
IsMax:=0;
end;
if Panel4.Visible=True then //当添加和修改角色和用户的下拉面板显示时
begin
Panel4.Height := NPand; //公有变量Npand记录了Panel4组件的高度
TreeView1.Top := AModule[20]+NPand; //使TreeView1组件下移
if IsMax=1 then //当窗体最大化时,使缩短TreeView1组件的高度
TreeView1.Height := MaxModule[21]-NPand
else
TreeView1.Height := AModule[21]-NPand
end;
Inherited;
end;
说明:MaxModule[21]中记录了窗体最大化时TreeView1组件的高度,AModule[21]中记录了窗体还原时TreeView1组件的高度。
在以上代码中使用了自定义函数MaxFrm和DarbarismFrm,下面对其进行说明。
自定义函数MaxFrm是在窗体最大化时,改变窗体中各组件的位置。代码如下:
procedure TFrm_User_Part.MaxFrm;
begin
Label1.Left := MaxModule[1];
Label2.Left := MaxModule[2];
Label3.Left := MaxModule[3];
Label4.Left := MaxModule[4];
Label5.Left := MaxModule[5];
Label6.Left := MaxModule[6];
Label7.Left := MaxModule[7];
Label8.Left := MaxModule[8];
Label9.Left := MaxModule[9];
Label10.Left := MaxModule[10];
Edit1.Left := MaxModule[11];
Edit2.Left := MaxModule[12];
TreeView2.Height := MaxModule[13];
ListView1.Top := MaxModule[14];
BitBtn1.Top := MaxModule[15];
BitBtn2.Top := MaxModule[16];
BitBtn3.Top := MaxModule[17];
Image10.Left := MaxModule[18];
TreeView3.Height := MaxModule[19];
TreeView1.Top := MaxModule[20];
TreeView1.Height := MaxModule[21];
Panel4.Height := MaxModule[22];
TreeView1.Width := MaxModule[23];
CheckBox2.Left := MaxModule[24];
end;
自定义函数DarbarismFrm是在窗体还原时,改变窗体中各组件的位置。代码如下:
procedure TFrm_User_Part.DarbarismFrm;
begin
Label1.Left := AModule[1];
Label2.Left := AModule[2];
Label3.Left := AModule[3];
Label4.Left := AModule[4];
Label5.Left := AModule[5];
Label6.Left := AModule[6];
Label7.Left := AModule[7];
Label8.Left := AModule[8];
Label9.Left := AModule[9];
Label10.Left := AModule[10];
Edit1.Left := AModule[11];
Edit2.Left := AModule[12];
TreeView2.Height := AModule[13];
ListView1.Top := AModule[14];
BitBtn1.Top := AModule[15];
BitBtn2.Top := AModule[16];
BitBtn3.Top := AModule[17];
Image10.Left := AModule[18];
TreeView3.Height := AModule[19];
TreeView1.Top := AModule[20];
TreeView1.Height := AModule[21];
Panel4.Height := AModule[22];
TreeView1.Width := AModule[23];
CheckBox2.Left := AModule[24];
end;
l 角色和用户名的操作部分
在“操作员角色”窗体的左上角,可以通过“添加”、“修改”和“删除”按钮对角色和用户进行操作。当对以上按钮进行单击时,为了使按钮具有动态将果,可以在鼠标按下时,改变按钮的颜色,当鼠标松开时,恢复原来的颜色,下面以用户角色的“添加”按钮为例,对其进行一下说明,代码如下:
//当鼠标按下时,改变字体的颜色
procedure TFrm_User_Part.Label2MouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
ChangeColor(Label2,clMaroon);
end;
//当鼠标松开时,恢复字体原来的颜
procedure TFrm_User_Part.Label2MouseUp(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
ChangeColor(Label2,clTeal);
end;
自定义过程ChangeColor(),是用来改变TLabel组件的字体颜色。代码如下:
procedure TFrm_User_Part.ChangeColor(Lab: TLabel; C: TColor);
begin
Lab.Font.Color := C;
end;
ChangeColor()过程的参数说明如表3.18所示。
表3.18 ChangeColor()过程的参数说明
参数 |
说明 |
Lab |
TLabel类型,用来对TLabel组件进行操作 |
C |
颜色值 |
在单击“添加”和“修改”按钮后,将在下面弹出一个下拉面板,该下拉面板将根据操作目的的不同,显示的组件也不同。下面以用户角色的“添加”按钮为例进行说明,代码如下:
procedure TFrm_User_Part.Label2Click(Sender: TObject);
begin
Uimage(1, Image10);
end;
自定义过程Uimage(),是用来显示和隐藏下拉面板,当依次按同一个按钮时,将实现显示和隐藏的效果,当按不同的按钮时,将根据按钮的实现目的,显示并改变下拉面板中的内容。代码如下:
procedure TFrm_User_Part.Uimage(N: Integer; Ima: TImage);
begin
NoteImg := N;
if Panel4.Visible=True then
begin
if (N=NWork) and (Panel4.Visible=True) then //当同一按钮连续单击两次后,将下拉面板隐藏
begin
Panel4.Visible := False;
if IsMax=0 then
DarbarismFrm //窗体还原时的隐藏效果
else
MaxFrm; //窗体最大化时的隐藏效果
Edit2.Visible := True;
Label10.Visible := True;
end;
if (N<>NWork) and (Panel4.Visible=True) then //当单击其他按钮时,改变面板中的内容
NotUser(N,Ima);
end
else
NotUser(N,Ima);
NPand := Panel4.Height;
NWork := N; //公有变量NWork,用于记录本次按钮的标记值
end;
Uimage()过程的参数说明如表3.19所示。
表3.19 Uimage()过程的参数说明
参数 |
说明 |
N |
标记,用来判断当前操作的是那个按钮 |
Ima |
TImage类型,通过N值改变TImage组件的图片样式 |
注意:以上参数是为自定义函数NotUser()做准备的,在这里没有实际意义。
交叉链接:自定义函数MaxFrm和DarbarismFrm以在前面进行了讲解,在这里不再进行介绍。
自定义过程NotUser()是根据第一个参数值来判断当前单击的是哪一个按钮,并根据按钮的操作目的来显示下拉面板中的内容。代码如下:
procedure TFrm_User_Part.NotUser(N: Integer; Ima: TImage);
var
VH : Integer;
begin
if IsMax=0 then //通过窗体的大小,记录下拉面板的高度
VH := AModule[21]
else
VH := MaxModule[21];
UImg := 1; //公有变量Uimg,记录面板中按钮图片的相应样式
case N of
1,2:begin //单击的是用户角色的“添加”和“修改”按钮
Label9.Caption := '角色名:';
Panel4.Height := AModule[22]-28;
TreeView1.Top := AModule[20]+Panel4.Height;
Edit2.Visible := False;
Label10.Visible := False;
CheckBox2.Visible := False;
Panel4.Visible := True;
TreeView1.Height := VH-Panel4.Height;
if N=2 then
UImg:=3;
Image10.Picture.LoadFromFile(UDirIma+IntToStr(UImg)+'.JPG');
end;
3,4:begin //单击的是用户的“添加”和“修改”按钮
Label9.Caption := '用户名:';
TreeView1.Top := AModule[20]+AModule[22];
Panel4.Visible := True;
Panel4.Height := AModule[22];
Edit2.Visible := True;
Label10.Visible := True;
if N=3 then
begin
CheckBox2.Visible := False;
UImg := 5;
end
else
begin
CheckBox2.Visible := True;
UImg := 7;
end;
Image10.Picture.LoadFromFile(UDirIma+IntToStr(UImg)+'.JPG');
end;
end;
end;
说明:Uimage()过程的参数与NotUser()过程的参数意义相同,在这里不作解释。
下面给出在操作用户角色和用户的“添加”和“修改”按钮时,下拉面板的显示情况。如图3.55、图3.56、图3.57、图3.58所示。
图3.55 用户角色的添加 图3.56 用户角色的修改
图3.57 用户的添加 图3.58 用户的修改
在对用户角色进行修改时,需要窗体左边的树形结构中选择相应的角色,当角色的名称显示在下拉面板的角色文本框中,便可以对当前角色的名称进行修改。可以在TreeView1组件的OnClick事件中完成该操作。代码如下:
procedure TFrm_User_Part.TreeView1Click(Sender: TObject);
begin
Fat1 := ''; //公有变量,用于保存当前所选的角色名称
Son1 := ''; //公有变量,用于保存当前所选的用户名称
PartName := '0'; //公有变量,用于保存当前所选的角色编号
TreeView2.Items.Clear;
if TreeView1.Items.Count>0 then //当TreeView1组件中有根节点时
begin
if TreeView1.Selected<>nil then //当在TreeView1组件中选中节点时
begin
Son1 := TreeView1.Selected.Text; //保存当前所选节点的名称
if TreeView1.Selected.Level=1 then //当所选节点为根节点的下一节点时
Fat1 := TreeView1.selected.Parent.Text //保存当前节点的父节点名称
else
begin
Fat1 := TreeView1.Selected.Text; //保存当前根节点
Son1 := '';
end;
case NoteImg of //公有变量NoteImg,记录当前单击按钮的标识
1..2: Edit1.Text := Fat1; //如果是角色用户中的按钮,则将角色名显示在文本框中
3..4: Edit1.Text := Son1; //如果是用户中的按钮,则将用户名显示在文本框中
end;
end;
with DataModule1.ADOQuery3 do //根据角色节点的名称,在TreeView2组件中显示相关权限
begin
Close;
SQL.Clear;
SQL.Add('select * from tb_Part_Table where Part_Name='+''''+Fat1+'''');
Open;
if RecordCount>0 then
begin
PartName := DataModule1.ADOQuery3.FieldByName('Part_ID').AsString;
myshow(TreeView2,DataModule1.ADOQuery2,DataModule1.ADOQuery3,
'0','tb_Part_Module','Part_ID',Nil,0);
end
else
PartName := '0'; //此值也可以设为空,并没有实际上的意义
end;
end;
CheckBox1.Checked := False;
IsUse(CheckBox1, Son1, 0);
//在窗体的上面显示当前所选的角色和用户名
Label12.Caption := '['+Fat1+']';
if Son1<>'' then
Label13.Caption := '['+Son1+']'
else
Label13.Caption := '';
end;
自定义函数IsUse(),不但可以在“禁止用户登录”复选框中显示当前用户是否被禁止,还可以改变当前用户的禁止状态。代码如下:
procedure TFrm_User_Part.IsUse(Check : TCheckBox; SUser: String; N: Integer);
begin
if SUser='' then //当没有选中用户名时
Exit; //退出该过程
with DataModule1.ADOQuery6 do //在用户表中查询当前用户的信息
begin
Close;
SQL.Clear;
SQL.Add('select * from tb_Part_User where User_Name='+''''+SUser+'''');
Open;
if RecordCount>0 then //如果有记录
case N of
0: Check.Checked := FieldByName('Title').AsBoolean; //显示当前用户的状态
1: begin //设置当前用户是否被禁止
Edit;
FieldByName('Title').AsBoolean := Check.Checked;
Post;
end;
end;
end;
end;
IsUse()过程的参数说明如表3.20所示。
表3.20 IsUse()过程的参数说明
参数 |
说明 |
Check |
TCheckBox类型,用于传递TCheckBox类型的组件信息 |
SUser |
用户名称 |
N |
标识,用于判断是显示用户的禁止状,还是修改用户的禁止状态 |
如果要对角色中的用户进行修改,必需在窗体左边的树形结构中选择要修改的用户名。本方案在窗体显示时,只在Treeview1组件中添加了角色名称,如果想要显示各角色的用户名,必需在Treeview1组件的OnExpanding事件中动态加载。代码如下:
procedure TFrm_User_Part.TreeView1Expanding(Sender: TObject;
Node: TTreeNode; var AllowExpansion: Boolean);
var
S1 : String;
begin
Node.DeleteChildren; //清空当前节点下的所有子节点
with DataModule1.ADOQuery4 do //根据节点(角色)名称,获取当前角色的编号
begin
close;
SQL.Clear;
SQL.Add('select * from tb_Part_Table where Part_Name='+''''+Node.Text+'''');
open;
if RecordCount>0 then
S1 := DataModule1.ADOQuery4.FieldByName('Part_ID').AsString;
end;
//根据角色的编号,在tb_Part_User表中查找用户名,并添加到子节点中
with DataModule1.ADOQuery4 do
begin
close;
SQL.Clear;
SQL.Add('select * from tb_Part_User where Part_ID='+''''+S1+'''');
Open;
if RecordCount>0 then
begin
First;
while Not Eof do
begin
TreeView1.Items.AddChild(Node,DataModule1.ADOQuery4.FieldByName
('User_Name').AsString);
Next;
end;
end;
end;
end;
下面以修改角色名为例,对角色和用户添加和修改的操作进行详细说明。
选中要修改的角色,在“下拉面板”中的“角色名”文本框中输入修改后的角色名,单击“修改用户角色”按钮即可,在该按钮的OnClick事件,也能实现角色和用户的添加、修改操作。代码如下:
procedure TFrm_User_Part.Image10Click(Sender: TObject);
var
SelNode:TTreeNode;
begin
if Edit1.Text = '' then //名称文本框不能为空
begin
showmessage('文本框不能为空。');
Exit;
end;
SelNode:=treeview1.Selected; //获取当前选中的节点值
case NoteImg of
1..2: //对用户角色的添加和修改进行操作
Begin
//在tb_Part_Table数据表中查找是否有相同的名称
if FindNodeDate('tb_Part_Table', 'Part_Name', Edit1.Text) then
begin
showmessage('当前已有该角色。');
Exit;
end;
case NoteImg of
1: Treeview1.Items.AddChild(nil,Edit1.Text); //在Treeview1组件中添加新角色
2: begin //修改Treeview1组件中选中的角色名称
if SelNode = nil then
begin
showmessage('没有选中要修改的角色。');
Exit;
end;
SelNode.Text := Edit1.Text;
end;
end;
SetPart(GetPartID(Fat1,NoteImg),Edit1.Text,NoteImg); //将修改保存到数据库中
end;
3..4: //在Treeview1组件的角色节点下添加或修改用户
begin
if SelNode = nil then //判断是否选择角色
begin
Showmessage('请选择用户添加的角色位置.');
Exit;
end;
if FindNodeDate('tb_Part_User', 'User_Name', Edit1.Text) then
begin //判断是否有重复的用户名
showmessage('当前已有该用户名.');
Exit;
end;
case NoteImg of
3: begin 添加用户信息
if (Edit1.Text='') and (Edit2.Text='') then
begin
showmessage('用户和密码不能为空.');
Exit;
end;
if SelNode.Level = 0 then //当前节点是否为根节点
Treeview1.Items.AddChild(SelNode,Edit1.Text)
else
Treeview1.Items.AddChild(SelNode.Parent,Edit1.Text);
end;
4: begin //修改用户信息
if CheckBox2.Checked then //当CheckBox2为可选状态时,密码可修改
begin
if Edit2.Text='' then
begin
showmessage('密码不能为空.');
Exit;
end;
end;
SelNode.Text := Edit1.Text;
end;
end;
SetUser(GetUserID(Son1,NoteImg-2),GetPartID(Fat1,2),Edit1.Text,
Edit2.Text,NoteImg-2,CheckBox2.Checked); //将用户的修改信息存入到数据库中
end;
end;
Edit1.Clear;
Edit2.Clear;
CheckBox2.Checked := False;
end;
自定义过程FindNodeDate(),用于在指定数据表中查找是否有相同的信息,如果有相同信息则返回False值。代码如下:
function TFrm_User_Part.FindNodeDate(Table, Field, Text: String): Boolean;
var
S : String;
begin
result := False;
S := 'Select '+Field+' from '+Table+' where '+Field+'='+''''+Text+'''';
with DataModule1.ADOQuery4 do
begin
Close;
SQL.Clear;
SQL.Add(S);
Open;
if RecordCount>0 then
result := True;
end
end;
FindNodeDate()过程的参数说明如表3.21所示。
表3.21 FindNodeDate()过程的参数说明
参数 |
说明 |
Table |
要查找的表名 |
Field |
要查找的相关字段 |
Text |
要查找的信息 |
返回值:如果有信息则返回False,否则返回True。
自定义过程GetPartID(),用于在添加和修改角色时,获取角色的编号。代码如下:
function TFrm_User_Part.GetPartID(Text:string;TypeID:integer): string;
var
ID:string;
begin
with DataModule1.ADOQuery2 do
begin
Close;
if TypeID = 1 then //如果标识为1,连接tb_Part_Table数据表
begin
SQL.Text := 'select Part_ID from tb_Part_Table';
end
else
begin //如果为2,在表中查询指定的角色名
SQL.Text := 'select Part_ID from tb_Part_Table where Part_Name='+''''+Text+'''';
end;
Open;
if Recordcount > 0 then
begin
if TypeID=1 then //如果是添加角色,自动生成角色编号
begin
Last;
ID := FieldByName('Part_ID').AsString;
ID := Format('%.4d',[StrToInt(ID)+1]);
result := ID;
end
else
begin
ID := FieldByName('Part_ID').AsString; //获取要修改角色的编号
result := ID;
end;
end
else
begin
if TypeID=1 then //如果在添加角色时,角色信息表为空,则将编号设为'0001'
result := '0001';
end;
end;
end;
GetPartID()过程的参数说明如表3.22所示。
表3.22 GetPartID()过程的参数说明
参数 |
说明 |
Text |
要查找的角色名称 |
TypeID |
标识,判断是对角色进行添加还是修改 |
自定义函数SetPart(),用于将添加或修后的角色信息保存在角色信息表(tb_Part_Table)中。代码如下:
procedure TFrm_User_Part.SetPart(ID, Text: String; TypeID: integer);
begin
with DataModule1.ADOQuery2 do
begin
Close;
if TypeID = 1 then //在tb_Part_Table表中添加新的角色
begin
SQL.Text := 'select * from tb_Part_Table';
Open;
Append;
end
else
begin //根据角色编号进行查找,并对角色名称进行修改
SQL.Text := 'select * from tb_Part_Table where Part_ID='+''''+ID+'''';
Open;
Edit;
end;
FieldByName('Part_ID').AsString := ID;
FieldByName('Part_Name').AsString := Text;
Post;
end;
end;
SetPart()过程的参数说明如表3.23所示。
表3.23 SetPart()过程的参数说明
参数 |
说明 |
ID |
角色编号 |
Text |
角色名称 |
TypeID |
标识,判断是添加还是修改 |
自定义过程SetUser(),用于将添加或修改的用户信息保存到用户登录表(tb_Part_User)中。代码如下:
procedure TFrm_User_Part.SetUser(ID, PID, UText, PText : String; TypeID: integer;
B : Boolean);
begin
with DataModule1.ADOQuery2 do
begin
Close;
if TypeID = 1 then
begin
SQL.Text := 'select * from tb_Part_User'; //在tb_Part_User表中添加用户
Open;
Append;
end
else
begin //根据用户编号,查找要修改的用户
SQL.Text := 'select * from tb_Part_User where User_ID='+''''+ID+'''';
Open;
Edit;
end;
if TypeID=1 then
begin //添加新的用户信息
FieldByName('User_ID').AsString := ID;
FieldByName('User_Name').AsString := UText;
FieldByName('User_Pass').AsString := PText;
FieldByName('Part_ID').AsString := PID;
FieldByName('Title').AsBoolean := False;
end
else
begin //修改用户名或密码
FieldByName('User_Name').AsString := UText;
if B=True then
FieldByName('User_Pass').AsString := PText; //修改用户密码
end;
Post;
end;
end;
SetUser()过程的参数说明如表3.24所示。
表3.24 SetUser()过程的参数说明
参数 |
说明 |
ID |
用户编号 |
PID |
角色编号 |
UText |
用户名称 |
PText |
角色名称 |
TypeID |
标识,判断当前用户是添加还是修改 |
B |
标识,判断用户密码是否也被修改 |
在对角色和用户进行添加后,可以通过相应的“删除”按钮对其进行删除。
“删除”按钮实际上就是将用户登录表(tb_Part_User)中的用户信息进行删除。相关操作在该按钮的OnClick事件中完成的。代码如下:
procedure TFrm_User_Part.Label8Click(Sender: TObject);
begin
if TreeView1.Selected<>Nil then
if TreeView1.Selected.Level=1 then
begin
with DataModule1.ADOQuery5 do
begin
Close;
SQL.Clear;
SQL.ADD('Delete tb_Part_User where User_Name='+''''+Son1+'''');
ExecSQL;
end;
TreeView1.Selected.Delete;
end;
end;
角色“删除”按钮是将角色信息表(tb_Part_Table)中的角色信息进行删除,同时根据角色的编号,将角色权限表(tb_Part_Module)中的相关信息进行删除。相关操作在该按钮的OnClick事件中完成的。代码如下:
procedure TFrm_User_Part.Label4Click(Sender: TObject);
begin
if TreeView1.Selected<>Nil then
if TreeView1.Selected.Level=0 then
begin
with DataModule1.ADOQuery5 do
begin
Close;
SQL.Clear;
SQL.ADD('Delete tb_Part_Table where Part_ID='+''''+PartName+'''');
ExecSQL;
end;
TreeView1.Selected.Delete;
end;
if TreeView1.Items.Count<1 then
TreeView2.Items.Clear;
end;
注意:本实例在删除角色信息表(tb_Part_Table)中的角色时,在数据库中定义了一个触发器,根据角色的编号,在用户信息表中删除相关信息。
l 改变节点的图片样式
在TTreeView组件的树形结构发生变化时,被操作节点的图片样式也将随之发生改变,这样,可以与用户建立友好的交互。方便用户在操作时,快速查找正在操作的节点。
主要是在TTreeView组件的OnGetImageIndex和OnGetSelectedIndex事件中完成的。下面对这两个事件的操作过程进行详细说明。
OnGetImageIndex事件主要是在节点扩展时,改变子节点的图片样式。代码如下:
procedure TFrm_User_Part.TreeView1GetImageIndex(Sender: TObject;
Node: TTreeNode);
begin
if node.Level = 0 then
begin
if Node.Expanded then
begin
node.ImageIndex := 1;
end
else
begin
node.ImageIndex := 0;
end;
end
else
if Node.Level = 1 then
begin
node.ImageIndex := 2;
end;
end;
OnGetSelectedIndex事件是在鼠标选中节点时,改变节点的图片样式。代码如下:
procedure TFrm_User_Part.TreeView1GetSelectedIndex(Sender: TObject;
Node: TTreeNode);
begin
if node.Level = 0 then
begin
if Node.Expanded then
begin
Node.SelectedIndex := 1;
end
else
begin
Node.SelectedIndex := 0;
end;
end
else
if Node.Level = 1 then
begin
Node.SelectedIndex :=3;
end;
end;
注意:以上操作,只适合三层节点。
l 授权部分
在对角色进行添加后,就是对各角色的权限进行设置,本方案在“操作员权限”窗体右边的TTreeView组件中以节点的方式显示模块权限表(tb_Part_Tem)中的权限信息,并通过“单个授权”和“批量授权”按钮,将权限添加到窗体中间的TreeView2组件中,并保存到角色权限表上。
下面对角色的授权部分进行详细的讲解。
在对角色进行授权前,必须在窗体左边的TreeView1组件中选择角色或用户名,然后在窗体右边的TreeView3组件中选择要添加的权限。为了方便角色的添加,在单击TreeView3组件中的角色时,用公有变量保存当前选中节点的名称,以上操作可以在TreeView1组件的OnClick事件中完成。代码如下:
procedure TFrm_User_Part.TreeView3Click(Sender: TObject);
begin
Fat := ''; //公有变量
Son := ''; ; //公有变量
if TreeView3.Selected.Selected then
begin
Son := TreeView3.Selected.Text; //获取当前节点的名称
if TreeView3.Selected.Level=1 then //如果当前节点不是根节点
Fat := TreeView3.selected.Parent.Text //保存该节点的根节点
else
begin
Fat := TreeView3.Selected.Text; //当前节点为根节点
Son := '';
end;
end;
end;
接下来就可以用“单个授权”按钮或“批量授权”按钮将所选节点添加到TreeView2组件中。
“单个授权”按钮是将TreeView3组件中的节点以单个节点的形式进行添加,如果没有添加根节点,那么,不可以直接添加根节点下的子节点信息。该按钮的实现可以在Image4组件的OnClick事件中完成。代码如下:
procedure TFrm_User_Part.Image4Click(Sender: TObject);
var
i : Integer;
TempNode : TTreeNode;
S : String;
begin
if (TreeView1.Items.Count<1) or (Label12.Caption='') then //是否选中要添加权限的角色
Exit;
if TreeView3.Selected<>Nil then //是否选中要添加的权限
begin
if TreeView3.Selected.Level=1 then //如果当前选中的节点不是根节点
begin
//在角色权限表中查找是否已有当前节点的根节点
if FintNodeCount(TreeView3.Selected.Parent.Text,PartName,2)<>0 then
begin //在角色权限表中查找是否已有当前节点
if FintNodeCount(TreeView3.Selected.Text,PartName,2)<>0 then
begin
Exit;
end;
end
else
Exit;
end else if FintNodeCount(TreeView3.Selected.Text,PartName,2)<>0 then
Exit;
if Son='' then
begin
TreeView2.Items.AddChild(nil,Fat); //添加根节点
S := Fat;
end
else if ((Fat<>'') and (Son<>'')) then //如果在角色权限表发现有根节点
begin
for i:=0 to TreeView2.Items.Count-1 do //遍历查找根节点
begin
if TreeView2.Items.Item[i].Text=Fat then //当查找到根节点后
begin
TempNode := TreeView2.Items.Item[i];
TreeView2.Items.AddChild(TempNode,Son); //在根节点下添加子节点
S := Son;
SetModel(S, PartName, TreeView3.Selected.Level); //添加信息保存到数据库中
//锁定更新当前节点下的信息
TreeView2.Items.BeginUpdate;
TempNode.Expanded := False;
TempNode.Expanded := True;
TreeView2.Items.EndUpdate;
Exit;
end;
end;
end;
SetModel(S, PartName, TreeView3.Selected.Level); //将添加的根节点信息保存在数据库中
end;
end;
自定义过程SetModel(),将TreeView2组件中所添加的权限信息保存到数据库中。也就是将模块权限表(tb_Part_Tem)中所指定的权限信息添加到角色权限表(tb_Part_Module)中。代码如下:
procedure TFrm_User_Part.SetModel(Text, PID: String; Le : Integer);
var
FStr : String;
i : Integer;
begin
with DataModule1.ADOQuery3 do //打开角色权限表
begin
Close;
SQL.Clear;
SQL.Add('select * from tb_Part_Module');
Open;
end;
with DataModule1.ADOQuery2 do //在模块权限表中查找添加的权限信息
begin
Close;
SQL.Clear;
SQL.Add('select * from tb_Part_Tem where Tree_Name='+''''+Text+''''+' and Title=1');
Open;
if RecordCount>0 then //如果有记录
begin //将权限信息和角色编号添加到角色权限表中
DataModule1.ADOQuery3.Insert;
DataModule1.ADOQuery3.FieldByName('Part_ID').AsString := PID;
DataModule1.ADOQuery3.FieldByName('Zhu_ID').AsString := FieldByName
('Zhu_ID').AsString;
FStr := FieldByName('Zhu_ID').AsString;
DataModule1.ADOQuery3.FieldByName('Fu_ID').AsString := FieldByName
('Fu_ID').AsString;
DataModule1.ADOQuery3.FieldByName('Tree_Name').AsString := FieldByName
('Tree_Name').AsString;
DataModule1.ADOQuery3.FieldByName('Title').AsString := FieldByName
('Title').AsString;
DataModule1.ADOQuery3.FieldByName('User_Pop').AsString := FieldByName
('User_Pop').AsString;
DataModule1.ADOQuery3.Post;
end;
end;
if Le=1 then //如果添加的是窗体权限
begin
with DataModule1.ADOQuery2 do //查找窗体权限下的所有按钮权限
begin
Close;
SQL.Clear;
SQL.Add('select * from tb_Part_Tem where Fu_ID='+''''+FStr+''''+' and Title=0');
Open;
if RecordCount>0 then
begin
for i:=0 to RecordCount-1 do //将窗体下的所有按钮权限和角色编号添加到角色权限表
begin
DataModule1.ADOQuery3.Insert;
DataModule1.ADOQuery3.FieldByName('Part_ID').AsString := PID;
DataModule1.ADOQuery3.FieldByName('Zhu_ID').AsString := FieldByName
('Zhu_ID').AsString;
DataModule1.ADOQuery3.FieldByName('Fu_ID').AsString := FieldByName
('Fu_ID').AsString;
DataModule1.ADOQuery3.FieldByName('Tree_Name').AsString := FieldByName
('Tree_Name').AsString;
DataModule1.ADOQuery3.FieldByName('Title').AsString := FieldByName
('Title').AsString;
DataModule1.ADOQuery3.FieldByName('User_Pop').AsString := FieldByName
('User_Pop').AsString;
DataModule1.ADOQuery3.Post;
Next;
end;
end;
end;
end;
end;
SetModel()过程的参数说明如表3.25所示。
表3.25 SetModel()过程的参数说明
参数 |
说明 |
Text |
要添加的权限名称 |
PID |
角色编号 |
Le |
标识,用于判断是否添加窗体权限下的所有按钮权限信息 |
用“单个授权”按钮添加完权限后,可以用“单个取消”按钮在TreeView2组件删除已有的权限。相关操作在Image5组件的OnClick事件中完成。代码如下:
procedure TFrm_User_Part.Image6Click(Sender: TObject);
begin
if TreeView2.Selected=Nil then //当没有选中节点时不操作
Exit;
if TreeView2.Selected.Level=0 then //如果是根节点
if FintNodeCount(GetZhuID(TreeView2.Selected.Text),PartName,1)<>0 then
Exit; //如果在角色权限表中找不到该节点,不操作
DeleteOdd(GetZhuID(TreeView2.Selected.Text), PartName); //删除角色权限表中信息
TreeView2.Selected.Delete; //删除当前选中的节点
ListView1.Clear;
{在删除子节点时,焦点会移到下一个子节点上,调用单击事件是为了将当前节点的按钮权限显示在
ListView1组件上}
TreeView2.OnClick(Sender);
end;
自定义过程DeleteOdd(),在角色权限表中删除当前所选的权限信息。代码如下:
procedure TFrm_User_Part.DeleteOdd(ID, Part_ID : String);
begin
with DataModule1.ADOQuery3 do //在tb_Part_Module表中查找要删除的记录
begin
Close;
SQL.Clear;
SQL.Add('select * from tb_Part_Module where Part_ID='+''''+Part_ID+''''+
' and Zhu_ID='+''''+ID+'''');
Open;
if RecordCount>0 then
while Not Eof do
begin
with DataModule1.ADOQuery4 do //删除当前权限下的所有权限信息
begin
Close;
SQL.Clear;
SQL.Add('Delete tb_Part_Module where Part_ID='+''''+Part_ID+''''+
' and Fu_ID='+''''+ID+'''');
ExecSQL;
end;
Delete; //删除当前记录
end;
end;
end;
DeleteOdd()过程的参数说明如表3.26所示。
表3.26 DeleteOdd()过程的参数说明
参数 |
说明 |
ID |
要删除权限的主ID号 |
Part_ID |
角色编号 |
在TreeView3组件中选择根节点或是子节点后,单击“批量授权”按钮,可以将该节点所在的根节点下的所有子节点添加到TreeView2组件中,当TreeView2组件中有当前根节点的部分节点时,只添加根节点中没有的子节点。以上操作将在Image6组件的OnClick事件中完成。代码如下:
procedure TFrm_User_Part.Image5Click(Sender: TObject);
var
ItemNode,LNode : TTreeNode;
i : Integer;
begin
if (TreeView1.Items.Count<1) or (Label12.Caption='') then //没有选中角色,不进行操作
Exit;
if TreeView3.Selected<>Nil then //选中要添加的权限
begin
ItemNode := TreeView3.Selected;
if ItemNode.Level=1 then //如果前节点是子节点
ItemNode := TreeView3.Selected.Parent; //获取当前节点的父节点
if FindNode(TreeView2,ItemNode.Text) then //在TreeView2组件中查找是否有相同的根节点
begin //如果有相同的根节点,在TreeView2组件中进行遍历
for i:=0 to TreeView2.Items.Count-1 do
if TreeView2.Items[i].Text=ItemNode.Text then //如果找到相同的根节点
begin
//当角色权限表中指定角色的根节点个数与模块权限表中的根节点个数不同
if FintNodeCount(GetZhuID(ItemNode.Text),'',0)<>FintNodeCount
(GetZhuID(ItemNode.Text),PartName,1) then
Begin
//将剩余的子节点添加到角色权限表中
BatchADDNode(GetZhuID(ItemNode.Text), PartName,1);
TreeView2.Items.AddChild(TreeView2.Items[i],'');
TreeView2.Items.BeginUpdate; //对当前节点进行锁定更新
TreeView2.Items[i].Expanded := false;
TreeView2.Items[i].Expanded := True;
TreeView2.Items.EndUpdate;
end;
Exit;
end;
end
else
begin
//如果当前根节点下有子节点
if FintNodeCount(GetZhuID(ItemNode.Text),'',0)>0 then
begin
LNode := TreeView2.Items.AddChild(nil,ItemNode.Text);
TreeView2.Items.AddChild(LNode,''); //在根节点下添加一个子节点
end
else
TreeView2.Items.AddChild(nil,ItemNode.Text); //只添加根节点
BatchADDNode(GetZhuID(ItemNode.Text), PartName,0);
end;
end;
end;
自定义过程BatchADDNode(),将模块权限表(tb_Part_Tem)中指定的Zhu_ID字段中的信息,批量添加到角色权限表(tb_Part_Module)中,如果角色权限表中已存在,则只添加没有的部分。代码如下:
procedure TFrm_User_Part.BatchADDNode(ID, Part_ID: string; N: Integer); //0表示全部,1表示部分
var
i : Integer;
begin
i := 0;
with DataModule1.ADOQuery4 do //在模块权限表中查找与根节点相关的子节点信息
begin
Close;
SQl.Clear;
SQL.Add('select * from tb_Part_Tem where Zhu_ID='+''''+ID+''''+' or Fu_ID
='+''''+ID+'''');
Open;
end;
with DataModule1.ADOQuery5 do //在角色权限表中查找指定角色的窗体权限
begin
Close;
SQl.Clear;
SQL.Add('select * from tb_Part_Module where Part_ID='+''''+Part_ID+''''+
' and Fu_ID='+''''+ID+'''');
Open;
end;
case N of
0: begin //将根节点下的所有权限进行添加
with DataModule1.ADOQuery4 do
begin
if RecordCount>0 then
while Not DataModule1.ADOQuery4.Eof do
begin
if FieldByName('Zhu_ID').AsString = ID then
SetModel(FieldByName('Tree_Name').AsString, Part_ID, 0)
else
SetModel(FieldByName('Tree_Name').AsString, Part_ID, 1);
Next;
end;
end;
end;
1: begin //添加根节点下没有的权限
with DataModule1.ADOQuery4 do
begin
if RecordCount>0 then
while Not DataModule1.ADOQuery4.Eof do
begin
if FieldByName('Zhu_ID').AsString = ID then
Next
else
begin
DataModule1.ADOQuery5.First;
while Not DataModule1.ADOQuery5.Eof do
begin
if DataModule1.ADOQuery5.FieldByName('Tree_Name').AsString =
FieldByName('Tree_Name').AsString then
begin
i := 1;
Break;
end;
DataModule1.ADOQuery5.Next;
end;
if i=0 then
SetModel(FieldByName('Tree_Name').AsString, Part_ID, 1);
i := 0;
Next;
end;
end;
end;
end;
end
end;
BatchADDNode()过程的参数说明如表3.27所示。
表3.27 BatchADDNode()过程的参数说明
参数 |
说明 |
ID |
获取当前窗体所有主菜单项的主ID号 |
Part_ID |
角色编号 |
N |
标识,如果为0,添加所有子节点,如果为1,添加没有的部分节点 |
用“批量授权”按钮添加完权限后,可以用“批量取消”按钮在TreeView2组件中批量删除已有的权限。相关操作在Image7组件的OnClick事件中完成。代码如下:
procedure TFrm_User_Part.Image7Click(Sender: TObject);
var
ItemNode : TTreeNode;
begin
if TreeView2.Selected=Nil then
Exit;
if TreeView2.Selected.Level=1 then
ItemNode := TreeView2.Selected.Parent
else
ItemNode := TreeView2.Selected;
DeleteBatch(GetZhuID(ItemNode.Text), PartName);
ItemNode.Delete;
end;
自定义过程DeleteBatch(),在角色权限表中删除指定角色的根节点及其所有子节点的权限。代码如下:
procedure TFrm_User_Part.DeleteBatch(ID, Part_ID: String);
begin
with DataModule1.ADOQuery5 do //查找指定角色的主菜单项权限
begin
Close;
SQL.Clear;
SQL.Add('select * from tb_Part_Module where Part_ID='+QuotedStr(Part_ID)
+' and Zhu_ID='+QuotedStr(ID));
Open;
end;
if DataModule1.ADOQuery5.Recordcount>0 then //删除相应的所有权限
begin
with DataModule1.ADOQuery2 do
begin
Close;
SQL.Clear;
SQL.Add('select * from tb_Part_Module where Part_ID='+QuotedStr(Part_ID)
+' and Fu_ID='+QuotedStr(ID));
Open;
if Recordcount>0 then
while Not Eof do
begin
DeleteOdd(FieldByName('Zhu_ID').AsString, Part_ID);
Next;
end;
end;
DataModule1.ADOQuery5.Delete;
end;
end;
DeleteBatch()过程的参数说明如表3.28所示。
表3.28 DeleteBatch()过程的参数说明
参数 |
说明 |
ID |
获取当前窗体所有主菜单项的主ID号 |
Part_ID |
角色编号 |
在对各角色的权限添加完成后,就要对各窗体下的按钮权限的应用进行设置,可以在TreeView2组件中单击子节点(窗体),如果窗体中有按钮权限,则在ListView1组件上进行显示。可以在TreeView2组件的OnClick事件中完成以上操作。代码如下:
procedure TFrm_User_Part.TreeView2Click(Sender: TObject);
var
LItem : TListItem;
begin
if TreeView2.Selected<>Nil then
begin
if TreeView2.Selected.Level=1 then
begin
ListView1.Clear;
with DataModule1.ADOQuery3 do //查找当前节点下是否有按钮权限
begin
Close;
SQL.Clear;
SQL.Add('select * from tb_Part_Module where Part_ID='+''''+PartName+''''
+' and Fu_ID='+''''+PCRMRecord(TreeView2.Selected.data).ID+''''+' and Title=0');
Open;
if RecordCount>0 then 如果有,循环添加到ListView1组件上
while Not Eof do
begin
LItem := ListView1.Items.Add;
Litem.Caption := '';
Litem.SubItems.Add(FieldByName('Tree_Name').AsString);
Litem.Checked := FieldByName('User_Pop').AsBoolean;
Next;
end;
end;
end;
end;
end;
在ListView1组件上可对当前已显示的按钮权限进行设置,只需要对该组件中的复选框进行设置,然后单击“保存”按钮便可。“保存”按钮的相关代码如下:
procedure TFrm_User_Part.BitBtn3Click(Sender: TObject);
var
i : Integer;
begin
if ListView1.Items.Count<1 then
Exit;
if TreeView2.Selected.Level=1 then
begin
with DataModule1.ADOQuery3 do
begin
Close;
SQL.Clear;
SQL.Add('select * from tb_Part_Module where Part_ID='+''''+PartName+''''+
' and Fu_ID='+''''+PCRMRecord(TreeView2.Selected.data).ID+''''+' and Title=0');
Open;
if RecordCount>0 then
for i:=0 to RecordCount-1 do
begin
Edit;
FieldByName('User_Pop').AsBoolean := ListView1.Items[i].Checked;
Post;
Next;
end;
end;
end;
end;
也可以通过ListView1组件下的“全选”或“反选”按钮对该组件中的复选框进行控制。代码如下:
//对ListView1组件上的复选框进行全选操作
procedure TFrm_User_Part.BitBtn1Click(Sender: TObject);
var
i : Integer;
begin
if ListView1.Items.Count>0 then
for i:=0 to ListView1.Items.Count-1 do
begin
ListView1.Items[i].Checked := True;
end;
end;
//对ListView1组件上的复选框进行反选操作
procedure TFrm_User_Part.BitBtn2Click(Sender: TObject);
var
i : Integer;
begin
if ListView1.Items.Count>0 then
for i:=0 to ListView1.Items.Count-1 do
begin
if ListView1.Items[i].Checked = True then
ListView1.Items[i].Checked := False
else
ListView1.Items[i].Checked := True
end;
end;
在窗体的上面有一个禁止用户登录复选框,通过对该复选框的设置,可以控制当前所选用户是否可以进行登录,主要是改变用户登录表(tb_Part_User)中的Title字段值。该操作将在CheckBox1组件的OnMouseUp事件中完成。代码如下:
procedure TFrm_User_Part.CheckBox1MouseUp(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if Label13.Caption='' then //判断是否选择用户
Exit;
if CheckBox1.Checked=True then
begin
if Application.MessageBox('确定要禁止当前用户吗?','消息',MB_YESNO+
MB_ICONINFORMATION)=IDYes then
begin
CheckBox1.Checked := True;
IsUse(CheckBox1, Son1, 1); //该过程已在前面进行说明,在这里不作讲解
end
else
CheckBox1.Checked := False;
end
else
begin
if Application.MessageBox('确定要开启当前用户吗?','消息',MB_YESNO+
MB_ICONINFORMATION)=IDYes then
begin
CheckBox1.Checked := False;
IsUse(CheckBox1, Son1, 1);
end
else
CheckBox1.Checked := True;
end;
end;
3.补充说明
本方案中的用户权限,实际上就是各角色的用户权限,当角色的用户权限改变时,该角色下的所有用户的权限也随之改变。为了可以更好的控制各用户对应用程序的控制,可以在数据库中创建一个用户权限表(tb_Part_Pop),在该表中记录了用户编号,以及用户所对应角色的权限信息。用户权限表的关系图如图3.59所示。
图3.59 tb_Part_Pop数据表的关系图
在对各用户的权限进行编辑时,实际上是修改用户权限表中的信息,也就是说,角色权限表只是记录了各角色所对应的权限名称,并不能对各权限的应用进行编辑,当在角色中添加用户时,将角色权限表中的相关角色信息添加到用户权限表中,在“操作员角色”窗体中设置的用户权限实际上是对用户权限表的操作。在这里要注意的是,不可以在用户权限表中进行添加和删除的操作,只可以在角色权限表中进行添加、删除的同时对用户权限表进行相应的操作。