目前,企业级应用软件逐渐由C/S结构转向B/S结构,B/S结构以其用户界面简洁、部署维护方便而备受青睐。但是,由于受到HTML元素的限制,在浏览器上无法实现强大的UI设计,例如数据输入检查、报表打印与打印预览、批量录入数据、数据模型与视图绑定等。用过PowerBuilder的朋友都知道,PB的DataWindow对象能很容易处理以上问题,大大地提高了开发效率,但是在目前常用的B/S开发工具中,尚未发现有如此强大的组件。
北京用友华表公司开发的Cell 产品是一套功能强大的表格类组件,它以单元格为基本设计元素,能够设计出格式复杂和界面美观的用户接口。开发者可以在一个可视环境下灵活地设置单元格字体、颜色、背景、对齐方式、边框、合并区域、以及内部计算公式,并且支持超强的打印和打印预览功能,可与Excel媲美,堪称国人第三方组件之精品。Cell 产品分Cell 组件和Cell 插件两个姐妹产品,前一个用于桌面程序,后一个用于Web程序。有了Cell,在B/S程序设计中实现类似DataWindow的功能,就成为现实。
利用Cell设计Web表格应用系统开发套件
Cell毕竟还是一个表格组件,只能用单元格位置表示数据,没有数据模型和数据绑定概念,离真正的应用开发还有段距离,我们可以利用Cell设计一套Web表格应用系统开发套件,以下简称“Reports套件”。该套件分三个相对独立的部分:Reports模板设计工具、Reports浏览器端API、Reports服务器端API。
Reports模板设计工具——是一个独立运行的桌面程序,用于设计表格格式和定义数据模型,完成表格单元格与数据项绑定,设计结果存储在一个独立的Reports模板文件,该文件可以被Reports浏览器端API与Reports服务器端API利用来显示表格格式和处理数据模型。根据实际开发需要,Reports模板分三种类型:单记录表、多记录表和主/细表。单记录表用于一次显示和编制一条记录数据;多记录表用于一次显示和编辑多条记录;主/细表用于一次显示和编辑一条主记录和多条明细记录。在模板设计时,可以充分利用Cell强大的格式定义功能,实现HTML元素无法实现的功能。
Reports浏览器端API——它是一套VBScript脚本库,为开发人员在浏览器上实现表格数据处理提供了一套通用接口函数,它以Cellweb为容器,以Reports模板文件为资源,能够实现数据装入、数据采集、数据显示、合法性检查、表格打印和打印预览等功能。开发者只需要调用几个函数就实现如上功能,不需要考虑单元格数据装入、提取、锁定、格式控制等复杂操作,甚至不需要了解Cell提供的上百个函数。
Reports服务器端API——它是一套面向Java的类库(也可以提供针对C#的类库),它由几个Reports模型类和工具类组成,用于解析和处理与Reports模板文件相对应的Reports数据。浏览器端以XML串方式作为一个整体提交表格数据,服务器端程序利用Reports模型类接收表格数据,调用者可以灵活地读取Reports模型类中的数据项值。数据接收后,往往要存储到数据库中,利用Reports工具类可以实现自动存储,不需要写任何SQL语句,同样道理,Reports数据与数据库之间的存储、提取、查询、删除等操作,都可以利用工具类完成,不需要开发者编写数据库访问程序。而且,在大多数情况下,开发者没有必要再写实体模型类,利用Reports数据模型类就可以了,这样可大大提高编程效率。
三个独立组件分别运行在不同环境下,实现不同的功能,它们之间的联系纽带是Reports模板文件和Reports数据模型,模型文件用xml封装,具备非常好的扩展性。
Reports套件应用实例说明
有了Reports套件,开发一个普通的Web表格应用管理系统将变得非常容易。我们就以用Java开发一个“客户购物登记表管理”为例,简述利用Reports套件的开发过程。本模块主要功能是帮助用户管理公司客户基本信息及其购物记录。客户基本信息有“客户编号”、“客户名称”、“登记日期”、“代理商编号”、“客户地址”、“联系电话”、“客户照片”等,购物记录的基本属性有“产品编号”、“产品名称”、“购买数量”、“金额”等。用户要求能同时编辑客户基本信息和客户购物记录。以下为开发步骤:
第一步:设计Reports模板文件
1)利用“Reports模板设计工具”新建表格文件,类型选用主/细表,定义表格编号和名称,工具首先生成一个空白表格,设计者可以设计表格样式,操作方式类似Excel。
2) 定义数据模型,因为该表是主/细表结构,所以需要定义主数据项和明细数据项,数据项类型支持文本、数值、整数、日期、图片、超连接类型,然后把数据项绑定到对应的单元格中,有数据项绑定的单元格是页面程序运行时用于输入和显示数据的单元格。
3)生成建立数据库表使用的SQL语句,在数据库中建立对应数据库表。
4)存储表格模型文件。
第二步:设计前端JSP页面
我们需要设计两个JSP文件,一个用于新增客户(AddCustomer.jsp),另一个用于编辑已存在客户(EditCustomer.jsp),因为大多数界面元素都已经在Reports模板中定义,JSP文件中的代码非常简单,大多数代码都是标准代码。下面把JSP文件中的几个关键代码片断进行说明。
1) 用XML指定Reports模板文件和数据文件,这些数据都可以由服务器端动态生成。
<xml id=”reportformatdoc” src=”/platform/sheet/files/rp<%=reportcode%>.xml”></xml>
<xml id=”reportdatadoc” src=”/reporttestservlet?request=NewReportData”></xml>
2) 引入Reports浏览器端API脚本库
<SCRIPT LANGUAGE=VBSCRIPT SRC=”/platform/common/report-common.vbs”></SCRIPT>
< SCRIPT LANGUAGE=VBSCRIPT SRC=”/platform/common/reportfile.vbs”></SCRIPT>
< SCRIPT LANGUAGE=VBSCRIPT SRC=”/platform/common/reportdata.vbs”></SCRIPT>
< SCRIPT LANGUAGE=VBSCRIPT SRC=”/platform/common/reportviewcontrol.vbs”></SCRIPT>
3) 在窗口初始化函数中,装入表格模板和数据;在装入数据后,可以根据需要进行一些设置,例如,当编辑记录时,需要设置编码数据项为只读。
Sub window_onload
initialize ‘初始化表格
‘装入报表格式
openreportFile "<%=homePath%>/platform/sheet/files/rp<%=reportcode%>.rpt", reportformatdoc
'设置编码数据项为只读
openreportFile "<%=homePath%>/platform/sheet/files/rp<%=reportcode%>.rpt", reportformatdoc
'设置编码数据项为只读
setMainDataItemReadOnly "code"
'注册代理商编号数据项更改函数
registerDataItemValueChangePostListener "dlcode","updateDLName"
'装入数据
loadReportData reportdatadoc
end sub
4) 在页面中引用Cell插件
<object classid="clsid:3F166327-8030-4881-8BD2-EA25350E574A" id="cell"
CODEBASE="<%=homePath%>/Platform/common/cellweb.cab#Version=1,0,0,0"
width="100%" height="100%">
<param name="_Version" value="65536">
<param name="_ExtentX" value="2646">
<param name="_ExtentY" value="1323">
<param name="_StockProps" value="0">
</object>
第三步:设计后台处理程序
这里用一个Java Servlet说明实现过程,在Servlet中处理来自前端的请求。
1)接收并存储新增的客户登记数据
private void addCustomer(HttpServletRequest request,
HttpServletResponse Response) throws Exception{
//前端表格编辑的数据以一个整体传到服务器,数据用XML格式表示
String reportDataxml = request.getParameter("ReportData");//报表数据XML格式数据
//生成XML文档模型
DocumenBuilder Parser = DocumentBuilderFactory.netInstance().netDocumentBuilder();
//把汉字转换成字节流
InputStream it = new ByteArrayInputStream(reportDataxml.getBytes("GBK"));
//生成报表文档
Document reportDataDocument = parser.parse(it);
//生成表格数据结构定义文档对象,该文档用于描述表格数据结构
ReportFile reportFile = new ReportFile();
//初始化报表结构定义模型
//通过表格数据结构定义文件对应的XML文档模型初始化,该文件由表格设计工具定义
reportFile.initializeReportFile(ApplicationContext.parseDocument(reportcode));
//根据表格数据结构定义文件对象,初始化报表数据对象,生成一个没有数据的ReporData对象
ReportData reportData = new ReportData(reportFile);
//从存储了报表数据的XML文档模型中,装入数据到ReportData对象
reportData.loadReportData(reportDataDocument);
//取得一个可用数据库连接对象
Connection conn = ConnectionPool.getConnection();
Reportutility.appendReportData(reportFile,reportData,conn);
... ...
}
2)接收并存储修改过的客户登记数据]
private void editCustomer(HttpServletRequest request,
HttpServletResponse response) throws Exception{
... ...
//接收数据代码与新增时向他那个
//通过主键值标识一条表记录,
Properties Keyvalues = new Properties();
keyvalues.setProperty("code",code);
//取得可用连接
Connection conn = ConnectionPool.getConnection();
ReportUtility.saveReportData(reportFile, keyvalues, reportData,conn);
... ...
}
3) 删除客户记录
private void delCustomer(HttpServletRequest request,
HttpServletResponse response) throws Exception{
//生成表格数据结构定义文档对象,该文档用于描述表格数据结构
ReportFile reportFile = new ReportFile();
//初始化报表结构定义模型
//通过表格数据结构定义文件对应的XML文档模型初始化,该文件由表格设计工具定义
reportFile.initializeReportFile(ApplicationContext.parseDocument(reportcode));
//通过主键值标识一条表记录,
Properties keyvalues = new Properties();
keyvalues.setProperty("code",code);
//取得可用连接
Connection conn = ConnectionPool.getConnection();
ReportUtility.deleteReportData(reportFile, keyvalues,conn);
... ...
}
4) 装入客户信息数据并发送给浏览器
private void loadReportData(HttpServletRequest request,
HttpServletResponse response) throws Exception{
... ...
//通过主键值标识一条表记录
properties keyvalues = new Properties();
keyvalues.setProperty("code",code);
//取得可用连接
Connection conn = ConnectionPool.getConnection();
//装入指定记录数据
ReportData reportData = ReportUtility.loadReportData(reportFile, keyvalues, conn);
//返回数据
response.setContentType(CONTENT_TYPE);//设置返回内容类型
ServletOutputStream out = response.getOutputStream();
XMLWriter xmlwt = new XMLWriter();
xmlwt.print(reportData.getReportDocument(), out, reportData.getReportDocumentDTD());
out.close();
... ...
}
这里仅对Reports套件的最基本功能进行了示例说明,还有一些强大、实用的功能在此不再详述,Reports套件已经过三套企业级应用系统的实践,在改进中不断走向成熟。