很久没写文章了,前几天有几个朋友问了我很多关于业务系统(ERP,DRP,CRM等)设计里很多概念性东西,花了很长时间给他解释,最后他门还是知其然不知其所以然,这对于刚跨出学校大门的朋友来说,理解起确实有点枯燥。这里我以以前参与的用友ERP,DRP产品项目里的部分设计为参考,通过几篇文章来和大家分享业务系统里的部门平台通用部分的设计应用。
本系列文章目录:
本文主要介绍业务系统设计里的菜单设计,关注我博客的朋友或许以前见过一篇我写的关于菜单的文章,文章连接:使用RadControls的RadMenu控件开发系统菜单 ,这篇文章主要是介绍了RadControls的RadMenu控件的基本使用,没有详细介绍到菜单如何设计,为什么要那么设计。本文就以这片文章为基础,同样通过此文章的示例程序和示例数据库,结合业务系统设计的相关术语介绍业务系统的菜单设计。
首先看看上面的数据库设计结构,其中ID为自动编号的主键字段,Code为菜单编码(也称业务编码),Name为菜单名称, Url为单击菜单所导航的路径,ImageUrl为菜单上显示的图片地址,ShortCut为快捷键,Order为菜单项显示顺序,Grade为菜单级限,EndGrade为是否为末级菜单(既最后一菜单项),SupMenuCode为当前菜单项的父级菜单编码(对应于Code字段),完整的还应该包括是否受权限控制等字段,这里不一一列出。
上面的数据库设计其实很简单,需要主要的字段有Code菜单编码(也称业务编码),在业务系统里通常设计为每一菜单项都可以认为是一个业务操作,这里的Code非常重要,业务系统的后续工作都是从这里开始,通常与权限设计结合使用得最多。
系统菜单应该是多层次结构的,比如常见的Windows菜单,业务系统里的菜单设计同样如此,通过SupMenuCode字段来标记该菜单项属于那一项菜单的子菜单,如果没有父级菜单(系统顶层菜单)则标记为0。
既然菜单有层次结构,那就一定有层次标识,通过Grade来标记;EndGrade则表示当前菜单项在该层次级别上的菜单项上是否为末级。
分析清楚了需求和菜单设计方案,下面我们着手代码开发,下图为项目解决方案截图:
如上图,Menu.cs里封装了数据库查询功能,App_Data下存放的Access数据库,RadControls为RadMenu控件所需要的相关文件,Default.aspx则为菜单UI界面。
首先需要配置数据库连串,如下:
2 + HttpContext.Current.Server.MapPath( @" App_Data\MenuDB.mdb " );
接着我们需要一个执行SQL的方法,如下代码块:
2 {
3 using (OleDbConnection conn = new OleDbConnection(connectionString))
4 {
5 using (OleDbDataAdapter oda = new OleDbDataAdapter(cmdText, conn))
6 {
7 DataSet ds = new DataSet();
8 oda.Fill(ds);
9 return ds.Tables[ 0 ];
10 }
11 }
12 }
最后我们还需要写两个方法,一个是查询顶层菜单的方法,一个则是实现查询菜单下的子菜单方法(做法有很多种,我个人建议把所有的数据查询出然后在操作内存数据,这里为了方便理解我故采用多次查询数据库的方式来实现)。
2 {
3 string sql = " select * from menu where grade=0 " ;
4 return ExecuteQuery(sql);
5 }
6
7 public DataTable GetMenuBySupCode( string supMenuCode)
8 {
9 string sql = " select * from menu where supmenucode=' " + supMenuCode + " ' " ;
10 return ExecuteQuery(sql);
11 }
OK,准备工作做好后,现在就是需要到UI层上去做菜单的展现工作了。菜单的展现我通过RadContrls的RadMenu控件来实现,此控件相对ASP.NET的标准Menu控件功能强大。要使用RadMenu则需要注册控件的引用,如果你是通过RadContrls控件库安装包安装的则会自动注册到VS工具箱,和标准控件使用方法一样。
2 < rad:RadMenu ID = " SystemMenu " runat = " server " Skin = " Vista " >
3 </ rad:RadMenu >
4 <%-- 菜单结束 --%>
上面准备好了顶层菜单的查询方法,直接通过该方法查询出顶层菜单项对菜单进行初始化:
2 {
3 if ( ! IsPostBack)
4 {
5 InitMenu();
6 }
7 }
8
9 Menu menu = new Menu();
10 private void InitMenu()
11 {
12 // 查询出菜单配置信息
13 DataTable dtMenu = menu.GetMenu();
14
15 for ( int i = 0 ; i < dtMenu.Rows.Count; i ++ )
16 {
17 // Rad菜单项
18 RadMenuItem item = new RadMenuItem();
19
20 item.ID = dtMenu.Rows[i][ " Code " ].ToString();
21 item.Text = dtMenu.Rows[i][ " Name " ].ToString();
22 item.Value = dtMenu.Rows[i][ " Url " ].ToString();
23 item.AccessKey = dtMenu.Rows[i][ " ShortCut " ].ToString();
24
25 this .SystemMenu.Items.Add(item);
26
27 // 菜建子菜单
28 InitSubMenu(item, dtMenu.Rows[i][ " Code " ].ToString());
29 }
30 }
我们设计为多层次菜单结构,那么在初始化顶层菜单的时候就应该判断该菜单项是否有子菜单,如果有则也初始化子菜单,如果是多级层次的结构,通过递归算法来完成多层次的初始化。如下代码块:
2 private void InitSubMenu(RadMenuItem item, string supMenuCode)
3 {
4 // 根据父菜单编码查询出子菜单配置项
5 DataTable dtMenu = menu.GetMenuBySupCode(supMenuCode);
6
7 if (dtMenu != null )
8 {
9 foreach (DataRow dataRow in dtMenu.Rows)
10 {
11 RadMenuItem subItem = new RadMenuItem();
12
13 subItem.ID = dataRow[ " ID " ].ToString();
14 subItem.Text = dataRow[ " Name " ].ToString();
15 subItem.Value = dataRow[ " Url " ].ToString();
16 subItem.AccessKey = dataRow[ " ShortCut " ].ToString();
17
18 item.Items.Add(subItem);
19
20 if (Convert.ToInt32(dataRow[ " EndGrade " ]) != 1 )
21 {
22 InitSubMenu(subItem, Convert.ToString(dataRow[ " Code " ]));
23 }
24 }
25 }
26 }
如上就完成了系统的菜单设计,实际项目开发中的设计与这样的设计几乎都大同小异。主要是了解设计思想,或许看完了本文你还是没有明白为什么要这样设计,如果是这样那请你关注下一篇文章,下一篇文章中就用到了菜单中的业务编码,应该可以给一个满意的答案。
相关文章连接:使用RadControls的RadMenu控件开发系统菜单
版权说明
本文属原创文章,欢迎转载,其版权归作者和博客园共有。
作 者:Beniao
文章出处:http://beniao.cnblogs.com/ 或 http://www.cnblogs.com/