作者:赵利城 昵称:Eazy
关键字:网管,资源管理,通用,Ajax
引言
资源管理已成为各个网管系统的一个必不可少的功能模块,面对各类资源的增删改查,每次的重复开发总是令人厌倦的,这样通用化的思想就变得迫切。为了用户使用的方便性,我们的实现方式采用基于BS的Web浏览方式,以前我们的开发都是针对性的,每类资源都是开发相应的页面,从功能实现上来说,很多页面都是类似的,只是修改一下数据库中相应的表、Sql语句等,这样做的结果就是高维护成本、牵一发而动全身,后期的维护占到了整个项目成本的大部分。
这就提出了功能模块通用化的需求,通用化一直是软件开发的一个目标,也是产品化的一个起点,面对各个电信厂商相似项目的需要,各个网管软件开发商也在走一条产品化的路线,各个网管软件开发商都建立了自己的一条产品线,如天元公司开发的传送网网络管理系统、电话交换网网络管理系统、CDMA网络管理系统、接入网网管系统、IP网网络管理系统等等,这些都是根据各个专业网系的不同有针对性的研发的产品。另一方面就是要介绍的一些通用的功能的开发,这些通用功能要达到的目标就是跨网系的产品,这样就使产品化集成度更高,不但提高了用户操作的方便性,也提高了维护的高效性。
下面我将概括的介绍通用资源的设计和框架,并针对用到的相应技术进行详细介绍,以期本篇文章给相关开发人员一些借鉴,达到抛砖引玉的效果。
一、 模块功能概述
通用资源管理模块实现在统一的平台上对各专业网(光纤网、数据网、电话网等)资源的综合管理,包括对资源的录入维护、查询统计和输出等功能。通用资源模块可根据需要录入各个专网的大部分资源,解决了根据各种资源分别开发的问题,向产品化迈出了一步。
通用资源管理基于BS模式,可通过浏览器链接进行访问,也可以通过各专业网的CS网管界面直接调用进入。主要采用VS.NET Framework1.1平台下的Ajax技术,实现用户无刷新的良好体验。该功能模块也是“肥客户端”(相对以前的“瘦客户端”)的一个代表,客户端完成了很强的功能,从代码量上来说,客户端脚本占了很大比例。
通用资源管理通过数据库配置,实现对不同资源的通用增删改查操作,所要实现的功能包括:
(1) 显示当前资源列表。
(2) 增加资源。
(3) 修改资源。
(4) 删除资源。
(5) 根据条件查询资源。
(6) 配置资源列表中显示列。
(7) 对部分资源提供节点资源树,便于快速定位。
该模块提供了一个通用增删改的框架,通过对数据库的配置,可以实现一些简单资源的增删改查的操作,在具体应用时,可根据需要针对不同的资源进行扩展。
二、 界面功能概述
通用资源增删改模块主要有以下几个界面组成:
(1) 菜单功能区。
(2) 当前资源列表。
(3) 增加资源输入区。
(4) 修改资源输入区。
(5) 查询资源输入区。
(6) 配置资源列表显示列。
(7) 节点资源树。
如下图所示,为了用户操作的方便,将所有功能统一在了一个界面上。其中查询功能界面是可隐藏的。
图1 通用资源操作界面
为了实现用户无刷新的良好体验,使用了ComponentArt公司的CallBack控件(CallBack控件是一个容器,后面章节将会详细介绍其用法)布局,布局结构如下:
图2 通用资源调用布局图
整个功能模块的布局如上,下面简单介绍一下该功能的使用方法和CallBack之间的调用结构。用户可以点击左边的资源树(CallBack1中内容)选择操作的资源类型,这时会调用CallBack2、CallBack3、CallBack4、CallBack5并更新相应数据。CallBack3中是该资源可查询的显示列,通过点击显示列可以添加条件,这时候调用CallBack4,并可动态生成查询条件选择条目;通过勾选复选框可以控制右侧的显示列,最后点击查询调用CallBack5,更新数据。
整个过程用户是感觉不到刷新的,因为数据的更新都是异步的,而且还会有友好的等待界面。
三、 数据库设计概述
与本功能相关的表有两个:CFG_RESOURCE_FIELDS、CFG_RESOURCE_INFO。
CFG_RESOURCE_INFO提供资源列表信息,CFG_RESOURCE_FIELDS提供所有资源列表对应的表字段和视图字段信息。数据由组件使用者录入,组件根据数据生成交互界面。
表1 CFG_RESOURCE_INFO表相关字段和含义
资源类型标识
网系
查询所用视图(表)
资源基本表名称
资源类型
……
RESID
NETWORKID
QUERYFROMVIEW
MODIFYINFOTABLE
VIEWINFO
……
表2 CFG_RESOURCE_FIELDS表相关字段和含义
资源表名称
字段名
字段显示名称
是否关键字
数据类型
是否可空
……
TABLENAME
ENFIELD
CHFIELD
ISKEY
DATATYPE
ISNULL
……
将各类资源根据其规则录入到上面两个表,就可实现通用的查询、录入、修改、删除,录入除了直接录入文本外,还可以根据各字段具体的要求通过选择列表录入,如单位信息,可以在一定权限范围内选择相关单位录入。附件类的二进制文件也可经配置后自动生成录入界面,并在资源列表中呈现。
四、 相关技术实现
为了给用户以好的用户体验,就要避免大范围的页面刷新,以前我们得解决方案是通过使用iframe来解决,这样做虽然也能在一定程度上解决刷新问题,但由于使用iframe必然是多个页面、多个链接集成到一个页面上,这样对服务器来说增加了很多不必要的负担;再者,iframe还是会有页面的闪动的。为了解决这个问题,引入了VS.NET Framework1.1平台下Ajax(Asynchronous JavaScript And XML)技术。这也是通用资源能够在一个页面上实现并能达到良好的用户体验的法宝,
下面我将根据开发前的总体设计以及实现过程中遇到的问题和解决方案来介绍通用资源的实现,这里用到的所有相关技术都是当时设计的需要,也是问题解决得需要,技术技巧上不仅是Ajax,还有一些相关的技巧,一些前后台的配合使用等等。不管您是否已经用过这些技术,我希望您都能从中吸取一些经验和教训。
1. CallBack控件的使用
在开发前的设计阶段,就确定了使用ComponentArt公司的CallBack控件搭建总体框架,CallBack控件是一个容器,使用它可以很方便的实现不刷新网页就可以更新数据的效果,同时加快了开发速度。为了讲解方便,现将CallBack布局图呈现如下:
图 3 通用资源内部CallBack控件布局图
使用上,首先在安装了ComponentArt公司的ComponentArt_WebUI_2006_1.exe后,就可以从工具箱将CallBack控件托到相应的位置上了,然后就可以以CallBack控件为容器向其中放入需要的服务器端控件或客户端控件了。如下所示:
<componentart:Callback id="Callback1" runat="server" Width="350" Height="250">
<content>
</content>
</componentart:Callback>
这个可以参考ComponentArt提供的参考手册和相应的例子,在这里我想要提醒的是,在<content>标记内我们通常放置一个PlaceHolder控件,在其内放置我们的数据和布局标记,而PlaceHolder控件内就不能再有服务端控件了,需要的话,将datagrid等类似服务端控件放在<content>标记内与其他服务端控件并列的地方。
前台javascript函数编写。由于是无刷新的,所以更新CallBack内的数据需要通过触发某个客户端的事件来完成,而该事件是前台javascrpt写的一个函数,函数内调用CallBack控件的CallBack事件。例如图1中我们点击“资源类型树”中的“标石”资源,该事件的函数如下:
function selectResType(Node){
var clbkFrom="clbkResType";
Callback5.Callback(clbkFrom,Node.ID,Node.Text);
}
其中selectResType是Callback1中树控件(TreeView)的节点的客户端事件,即点中Callback1中某类资源后出发selectResType函数,如上代码所示,将调用Callback5的Callback方法。
后台我将以VB.NET语言为例讲解实现过程。上面客户端调用了Callback5的Callback方法,这将触发后台的相应事件。
Private Sub Callback5_Callback(ByVal sender As Object, ByVal e As
_ComponentArt.Web.UI.CallBackEventArgs) Handles Callback5.Callback
Dim clbkFromID As String
clbkFromID = e.Parameters(0) '对应客户端的clbkFrom参数
'……省略中间代码
PlaceHolder6.RenderControl(e.Output)
End Sub
上面的代码需要指出的是,客户端Callback传递的参数通过后台代码的参数e获取,这是前后台交互的接口;更新后数据通过PlaceHolder6.RenderControl(e.Output)发送给客户端浏览器。PlaceHolder6都是定义的服务器端控件,这里可根据开发人员的需要自己定义,但各个服务器端控件都要通过RenderControl(e.Output)方法完成更新数据。
另外,所有用的参数、变量都是通过放在各Callback控件内的hidden发给客户端的,这些是为了便于客户端和服务端共享参数所作的处理,这也是通用的技巧。
2. 树型控件的客户端复选
之前在使用Componentart公司的TreeView控件以及Infragistics公司的UltraWebTree控件等树型控件时,节点复选的实现总是提交回服务器通过递归去处理选中或取消选中的节点信息,这在性能和用户体验上总是不能令人满意。下面介绍如何实现树型控件的客户端复选。希望大家根据自己的需要在安全性和性能及用户体验上作一个权衡,在尽量保证安全的前提下尽量用客户端脚本实现,以满足用户良好体验和缓解服务器压力。
在图1的查询条件以及图2的CallBack3处,我们能看到是一个树型控件,这里我使用的是Componentart公司的TreeView控件,这个复选在功能上是实现显示查询出来的资源的显示列。这个复选功能是完全的客户端实现的。
经过研究文档和客户端调试,发现了客户端复选的技巧,下面我将通过客户端脚本讲解TreeView对象相关客户端属性:
客户端函数为:
function columncheckchanged(Node){
var parTreeView = Node.ParentTreeView.Data; // 得到客户端TreeView对象的节点数据
var i=0,j=0;
var flag=true;
var colShow=""; //记录所有选中的节点的ID,以逗号为分割符
while(flag){//得到TreeView对象一级子节点的数量,用i记录
if(parTreeView[i]==null){
flag=false;
}
else{
i++;
}
}
for(j=1;j<i;j++) //j从1开始。因为parTreeView[0]数组中是根结点的信息
{ // parTreeView[j]表示第j个子节点
if(parTreeView[j][3][6]!= null) {//判断parTreeView是否有复选框
if(parTreeView[j][3][6][1]==true){//判断parTreeView第j个子节点ID
colShow= colShow + parTreeView[j][3][0][1] + ",";
}
}
}
//其他操作(字符串组装、CallBack操作等)……
}
上面代码实现了客户端复选,从代码中我们可以看到我们只须将选中的节点的ID存入变量就可以知道选中了那些节点。
下面列出了Node.ParentTreeView.Data数组的含义以供参考。
序号
对象
注释
1
Node.ParentTreeView.Data[i]
表示TreeView对象的第i个子节点,i从1开始。0表示根结点。
2
Node.ParentTreeView.Data[i][3]
3表示第i个子节点的数据域
3
Node.ParentTreeView.Data[i][3][j]
j从0到6分别表示:ID、Text、Expanded、ShowCheckBox、ToolTip、Checked。如j=6表示Checked属性
4
Node.ParentTreeView.Data[i][3][j][1]
1表示j的属性值
多层节点的TreeView类似,可参照上面实现。
3. 动态加载控件
由于资源录入界面需要根据不同的字段名、字段类型等自动生成,所以该界面所有控件须动态生成,包括下拉列表、选择框以及附件上传控件等等。网上也有相关资料提到动态生成Table等,下面将以通用资源为例介绍该界面用到的相关技术。
下图显示了通用资源的录入界面:
图 4 资源录入界面
首先,在页面中拖入一个Table服务端控件,如下设置:
<asp:table id="tblResource" runat="server" BorderWidth="0" BackColor="#7c7c7c" Width="100%" Height="100%">
</asp:table>
然后在后台函数中通过向tblResource中添加TableRow、TableCell以及需要的控件来动态布局,进行数据绑定等。
我是创建了一个动态绑定的类,来处理不同类型数据的绑定,下面给出一个简单的绑定函数以示说明:
建表函数:
Public Function createTable(ByVal control1 As Control) As Table
Dim table1 As New Table
Dim tr1 As New TableRow
Dim tc1 As New TableCell
tc1.Controls.Add(control1)
tr1.Cells.Add(tc1)
table1.Rows.Add(tr1)
Return table1
End Function
4. 资源列的动态绑定
数据的绑定才是实现的核心,由于我这里用的不一定是最好的,而且限于我的表达能力,我仅结合我的设计概括介绍一下。
首先获取到该资源的所有的自动编码字段、自动编码字段的类型(可通过SQL语句查询cfg_resource_fields表获得)和所有的非自动(即需要用户录入的)编码字段、字段中文名、字段类型、字段是否可空。
然后将非自动编码字段与控件(Lable、Text等)ID关联,以便于客户端从ID解析出字段,将字段中文名作为显示给用户的录入列名,根据字段类型显示不同的录入项(如图3中的单位、附件等选择项),根据字段是否可空判断那些字段要求用户必填(如图3中带红色*号标记的项)。
由于所有控件都是后台动态生成的,所以后台很难再访问到相应的控件,故所有数据的提取全部由客户端实现。最后再由客户端通过Ajax技术完成数据保存。
通用资源中,资源的录入、修改都是使用这个思想实现的。
5. Ajax实现资源录入和修改
资源的录入和修改没有再使用Componentart的CallBack控件,这是由于实际的需要,通过客户端获取到该资源的表名、录入列(修改列)、录入数据(修改后的数据)、修改列的条件等信息,组装出处理页面的URL后通过send_request发送回服务器。所以这里的关键还是数据的对应,如上面提到的,由于各控件ID与数据列名有一定关系,而且是唯一的,故问题的解决就清晰了。
五、 总结
通用资源在实际网管领域项目的应用过程中,缩短了开发周期,在维护上也节约了成本。现在已经在各专网得到了应用,并得到了用户的认可。实现通用的功能必然是要牺牲部分性能的,大量的数据表联查、后台动态界面的生成等等,但使用“肥客户端”大大的缓解了服务器的压力以及后台代码的复杂性,改善了用户体验。希望本篇文章能给大家以借鉴,以期达到本篇文章的初衷,能够抛砖引玉。