当前位置: 代码迷 >> Web前端 >> cell的使用
  详细解决方案

cell的使用

热度:367   发布时间:2012-10-08 19:54:56.0
cell的应用
近年来,随着客户需求不断上升,传统C/S结构程序的不易升级、维护、管理,且只能局限于局域网,不利于扩展等问题逐渐暴露出来,越来越不能满足客户的需求。而B/S结构程序具有:(1)无须开发客户端软件,维护和升级方便;(2)可跨平台操作,任何一台计算机只要装有WWW浏览器软件,均可作为客户机来访问系统;(3)具有良好的开放性和可扩充性;(4)可采用防火墙技术来保证系统的安全性,有效地适应了当前用户对管理信息系统的新需求等特点更适合客户的需求。网络技术日益成熟和硬件配置不断升级的今天,B/S结构存在网络通信数据量高和响应速度慢的问题逐渐被淡化。但是无论C/S结构还是B/S结构开发MIS系统,都会遇到一个令程序员十分痛苦的问题――报表数据的显示和打印!在技术十分成熟的C/S结构中,对于报表数据的显示和打印问题,相对B/S结构来说容易处理的多,而在B/S结构中要处理中国式复杂报表的显示和打印,技术上是不可想象的。


目前很多软件开发商在B/S结构中处理中国式报表打印时都会采用第三方的ActiveX控件来解决,用友华表的CELL插件也是一款基于ActiveX技术的解决中国式报表显示和打印的第三方控件,但是第三方ActiveX控件只能运行在客户端,直接处理客户端数据,而客户的数据都存放在服务器端,这势必要求客户端的ActiveX控件与服务器端进行数据交互。我们知道在B/S结构中对于少量数据的交互是相当方便的,但是在实际的MIS系统中,服务器端和客户端的数据都是大批量的,如何解决客户端ActiveX控件与服务端大批量数据交互已经成为B/S结构开发MIS系统成功的关键。XML技术正是为解决这种大批量数据交互问题孕育而生。


下面以用友华表CELL插件为例,介绍客户端的ActiveX控件与服务器端使用XML技术进行大批量数据交互。和其它第三方ActiveX控件一样,用友华表CELL插件只能运行在客户端,只能使用JAVASCRIPT、VBSCRIPT客户端语言操纵控件,而控件上的数据是从服务器端生成的并且控件上的数据必须提交至服务器,这样就产生上述所说的数据交互问题。实现客户端与服务器端数据交互必须按照两个步骤实现:1、将服务器端数据库中的数据显示在客户端的CELL插件中;2、将客户端CELL插件中的数据上传至服务器端的数据库。下面以简单示例说明如何利用CELL插件实现客户端与服务器端交换数据(示例采用ASP+VBSCRIPT语言):


1.? 环境配置



1.1?? 服务器配置



服务器运行服务器脚本,必须在服务器端安装WEB服务器。本示例程序使用ASP脚本语言,请安装IIS或PWS服务器,以及使用IE 5.5及以上的版本;


1.2?? 添加CELL插件至网页中



有两种方法可以CELL插件引入至网页中:


a. 在开发工具中将CELL插件引入至工程,然后将CELL插件拖至页面中即可。现以Microsoft Visual InterDev 6.0为例,先新建一个HTML页面,点击TOOL菜单中的Customize Toolbox 子菜单项,弹出引入ActiveX控件界面,如下图所示。




将CellWeb5 Control勾上,确定即可将CELL插件添加至工具箱中,如下图所示




双击后添加至页面中。


b. 直接将CELL插件的OBJECT标记添加至网页,


<OBJECT


id=Cell1 style="LEFT: 0px; WIDTH: 388px; TOP: 0px; HEIGHT: 265px"


classid=clsid:3F166327-8030-4881-8BD2-EA25350E574A


CodeBase="..\CellWeb5.cab#version=5,2,6,328"


VIEWASTEXT>


<PARAM NAME="_Version" VALUE="65536">


<PARAM NAME="_ExtentX" VALUE="10266">


<PARAM NAME="_ExtentY" VALUE="7011">


<PARAM NAME="_StockProps" VALUE="0">


</OBJECT>


注:如果用户升级CAB包,先将原先的CAB包替换,再将CodeBase属性中的Version设置为当前CAB包的版本号。用户再次访问时,IE会重新提示下载最新的CAB包。



?


2. 从服务器端读取数据

?

?





?




2.1打开报表模板



在这里之所以在请求XML数据前要先有打开报表模板,是因为如果将报表样式都存放在XML数据中,势必会增加XML数据量,一方面影响网页的运行速度;另一方面,一旦更改报表样式后,就得修改程序代码,从而增加程序开发和代码维护的工作量;而在模板中存放报表格式,查询报表时先打开模板,上传数据和下传数据时只需传送实际的报表数据,从而大大减少传送数据量和工作量;模板文件可以通过外部程序(可以使用CELL自带的报表设计器)设计,模板文件存放至服务器端的某个虚拟目录下,使用CELL的OpenFile方法打开,文件路径可以是相对路径。


在示例程序中除了对模板格式的设计外,还在模板中设置相应的单元格变量(单元格变量可以根据变量名找到它所对应的单元格),这样做有两个好处:1. 在显示和保存数据时快速地根据变量名找到相应的数据单元格(注:示例程序中变量名采用数据库中的字段名,XML数据中各节点名也用字段名,这样做是为了更好地将数据库字段与单元格对应);2. 即使用户调整模板样式(主要是指更改单元格位置)也可以正确找到相应的单元格;3.加快存取速度;

先介绍在CELL中定义单元格变量和遍历单元格变量(VBS代码)


'定义变量???????? 变量名,列号,行号,页号


Cell1.DefineCellVar "数据库中的字段名",1,1,0



用户也可以在华表自定的模板设计器中定义变量,具体操作步骤:




先在点击要在定义变量的单元格,然后在数据菜单中点击名称定义子菜单项如上图所示,在定义名称对话框中添加变量名称即可,如下图所示



?


'遍历变量


for i=0 to Cell1.GetVarCount-1


'根据ID得到变量名称


Cell1.EnumVar i,strName,strType


'根据变量名称 得到变量所指的区域


Cell1.GetCellVar strName,ncol,nrow,nsheet


Next



?


示例程序中打开远程文件的代码放至在OpenRemoteCllFile()方法中(OpenRemoteCllFile()在Index.asp文件中),关键代码如下:

'打开模板文件


iRet=Cell1.OpenFile("<%=sPath%>","")


if iRet<0 then


msgbox "<%=sPath%>模板,返回值="+iRet+",文件打开失败!"


exit sub


end if



?


说明:


a. sPath为模板的路径,

<%Path=getServerHost()+"/CELLTEMP/test.cll"%>


getServerHost函数在GeneraL.asp文件中:

function getServerHost()


?????? prot = Request.ServerVariables("SERVER_PROTOCOL")


?????? prot = left(lcase(prot),instr(prot,"/")-1)+"://"


?????? url=Request.ServerVariables("SERVER_NAME")


?????? url = url+Request.ServerVariables("PATH_INFO")


?????? url = left(url,instr(instr(url,"/")+1,url,"/"))


?????? getServerHost=prot+url


end function


b. OpenFile返回值如果不等于1,则表示打开不成功;此时还应注意如果是WINXP或WIN2003的操作系统还应在服务器上进行MIME配置,具体可参见CELL帮助中的“目录/FAQ/《为什么打不开远程序文件》” 一文。

2.2 服务器端生成XML数据并发送给客户端


客户端打开模板后,根据查询要求向服务器端发送请求,服务器端接收到请求后,组织XML数据,再发送给客户端;

a.?????? 客户端发送请求


关键代码如下(Index.asp):


'发送查询请求命令


OpenRemoteCllFile '打开模板


txtId=txtOrderId.value? ' txtOrderId为查询订单的编辑框


' IE5.5及以上用XMLDOM, 5.5以下用Msxml2.DOMDocument


set xmlDoc=CreateObject("Microsoft.XMLDOM")



?


'创建XMLHTTP发送对象


set HttpSend=CreateObject("Microsoft.XMLHTTP")


str="GetServerData.asp?OrderId="+txtId? '请求地址


HttpSend.open "GET",str,false?? '采用GET方式请求


HttpSend.send


此段代码在Index.asp文件中,xmlDoc对象只在接收到服务器端传回的XML数据进行解析的作用。发送请求数据的目标地址是可带一个或多个参数的,示例程序中添加OrderId参数作为查询条件,用户可根据实际情况添加多个参数作为查询条件向服务器提交。


?


b.?????? 服务器接收到请求,组织数据后,发送给客户端


主要代码如下(GetServer.asp):


strOrderId=Request("OrderId")? '接收单据ID


set rst=server.CreateObject("adodb.Recordset") '创建记录集对象


rst.ActiveConnection="Provider=Microsoft.Jet.OLEDB.4.0;Data Source="&Server.Mappath("Data\Data.mdb")&";Persist Security Info=False"


rst.CursorLocation = 1


rst.LockType = 3


rst.CursorType = 3


rst.Source = "select * from WipeOrder where nOrderId='" & strOrderId & "'"


rst.Open?? '打开记录集


set xmlDoc=Server.CreateObject("Microsoft.XMLDOM") '创建XML文档对象


’组织XML数据



?


'表头

For i=0 to rst.Fields.Count-1


set xmlChildNode=xmlDoc.CreateElement(rst.Fields(i).Name)? '以字段名为表头子节点名


xmlChildNode.setAttribute "DataType",rst.Fields(i).Type? '添加字段类型属性


xmlChildNode.text=CStr(strValue)? '添加节点值


xmlParentNode.AppendChild xmlChildNode? '添加到"表头"节点


Next


'组织表体数据


rst.Source="select * from WipeOrders where nOrderId='" +strOrderId + "'"


rst.Open? '打开记录集


While not rst.EOF


set xmlParentNode=xmlDoc.CreateElement("表体")? '创建"表体"结点,每一行记录生成一个表体节点

For i=0 to rst.Fields.Count-1


set xmlChildNode=xmlDoc.CreateElement(rst.Fields(i).Name)? '以字段名为表头子节点名


xmlChildNode.text=CStr(strValue) ?'添加节点值


xmlParentNode.AppendChild xmlChildNode ?'添加到"表体"节点中


Next


WEnd


xmlDoc.AppendChild xmlEle ?'将"实施报销单"节点添加至XML文档中



?


'发送XML数据


Response.Write? "<?xml version=""1.0"" encoding=""gb2312""?>" & xmlDoc.Xml Response.End


set xmlDoc=nothing



?


服务器端根据客户端提交的参数,组织查询语句,从数据库中取得记录集,生成XML数据,并通过Response对象将XML数据传给客户端。示例程序中XML文件格式:


?


'XML文件数据示例


<?xml version="1.0" encoding="gb2312"?>


<实施报销单>


<表头>


<ID DataType="3">29</ID>


<nOrderID DataType="202">1</nOrderID>


<sDepName DataType="202">开发部</sDepName>


<sWiper DataType="202">张三</sWiper>


<sWipeWays DataType="202">现金</sWipeWays>


<sSuperior DataType="202">李明</sSuperior>


<sManager DataType="202">李四</sManager>


<sfinancer DataType="202">五王</sfinancer>


<sAccountant DataType="202">五王</sAccountant>


<sCashier DataType="202">五王</sCashier>


<sPayee DataType="202">张三</sPayee>


<sOrderDate DataType="202">2005-03-29</sOrderDate>


<sPrintDate DataType="202">2005-03-29</sPrintDate>


</表头>


<表体>


<ID DataType="3">48</ID>


<nOrderId DataType="202">1</nOrderId>


<sCharge DataType="202">实施费</sCharge>


<sProject DataType="202">软件名称1</sProject>


<fSum DataType="4">11</fSum>


<sChargeMemo DataType="202"></sChargeMemo>


<sBudgetName DataType="202"></sBudgetName>


</表体>


<表体>


<ID DataType="3">49</ID>


<nOrderId DataType="202">1</nOrderId>


<sCharge DataType="202">补贴</sCharge>


<sProject DataType="202">软件名称</sProject>


<fSum DataType="4">2000</fSum>


<sChargeMemo DataType="202"></sChargeMemo><


sBudgetName DataType="202"></sBudgetName>


</表体>????????????????????


</实施报销单>



?


说明:总节点<实施报销单>以单据名称命名;主表数据以<表头>节点命名;子表数据以<表体>节点命名;子表每一条记录生成一个<表体>节点,表中数据存放至<表头>或<表体>各子节点,子节点名以主子表中字段名标识,并增加DataType属性以标识字段数据类型。



?


2.3 客户端接收XML数据,分析、填充至CELL中


客户端将服务端传回的XML数据将载到XML文档对象中,然后在XML对象中对数据进行分析,提取,填充。关键代码如下:



?


'创建XML文件头


HttpSend.responseXML.createProcessingInstruction "xml"," version=""1.0"" encoding = ""gb2312"""


set root=HttpSend.responseXML '以XML方式接收



?


xmlDoc.load root? ‘将XML文件加载至XML文档对象中



?


‘解析将填充CELL控件



?


‘表头


For i=0 to Cell1.GetVarCount-1


Cell1.EnumVar i,strName,strType


'根据'单元‘格中存放的字段名称取得XML节'点的值


set xmlTitle=xmlDoc.selectSingleNode("/实施报销单/表头[0]/"&strItem)


Next



?


‘表体


set xmlItems=xmlDoc.selectNodes("/实施报销单/表体")? '选择所有“表体”节点


Cell1.InsertRow 6, xmlItems.length-1,n


For? j=0? to? xmlItems.length-1


set strItem="/实施报销单/表体["+j+"]"? '选择第J个表体节点


For i=0 to Cell1.GetVarCount-1


Cell1.EnumVar i,strName,strType


'根据单元'格中存放的字段名称取得XML节点的值


set xmlTitle=xmlDoc.selectSingleNode(strItem+"/"+strItem)


Next


Next



?


在示例程序中采用遍历模板所有单元格变量(变量名称=表头/表体+字段名称,例如,主表的nOrderId字段则表示为[0]nOrderId),根据变量名称中记录的字段信息在XML文档中取得相关的数据,然后根据DataType属性,确定用单元格的数据类型,采用S或D方法。在一个XML文档只有一个表头节点,所以其子节点可以表示为“表头[0]/节点名”,直接用selectSingleNode 方法取得,而一个XML文档中有多个表体节点,所以先用selectNodes("/实施报销单/表体")方法取得所有表体的对象集,然后再循环遍历每个表体节点的各个子节点。如果客户在单元格中要存储的信息量比较多,单元格变量承载不下时,可以考虑使用单元格标注,通过SetCellNote和GetCellNote方法设置和取得标注内容。



?


3.? 保存数据到服务器端

?





3.1 客户端生成XML数据并传到服务器端


客户端根据CELL控件上的数据组织成XML数据,XML文档格式同上述(从服务器端传回的XML文档)大致相同,只是在各未级子节点没有DataType属性。示例程序中将生成XML程序部分,存至CreateXMLFile()方法中,关键代码如下:


?


'创建XML文件对象


set xmlDoc=CreateObject("Microsoft.XMLDOM")?? 'XML文件头



?


'创建第一个子节点


set xmlRoot=xmlDoc.createElement("实施报销单")



?


'创建表头内容


set xmlTitle=xmlDoc.createElement("表头")


'添加表头子节点


For i=0 to Cell1.GetVarCount()-1


Cell1.EnumVar i,strName,strType



?


set xmlChild=xmlDoc.createElement(strItem) '创建节点


If isnull(xmlChild)=false Then


Cell1.GetCellVar strName,ncol,nrow,nsheet


xmlChild.text=Cell1.GetCellString(ncol,nrow,nsheet)


xmlTitle.appendChild xmlChild '添加节点


End If


Next



?


For i=5 to nMaxBodyRow-1


set xmlBody=xmlDoc.createElement("表体")


For j=0 to Cell1.GetVarCount()-1


Cell1.EnumVar j,strName,strType



?


set xmlChild=xmlDoc.createElement(strItem) '创建节点


xmlBody.appendChild xmlChild '添加节点


Next



?


xmlRoot.appendChild xmlBody


Next



?


xmlDoc.appendChild(xmlRoot); ‘添加“实施报销单”节点至XML文件中


'创建HTTP发送对象


set HttpSend=CreateObject("Microsoft.XMLHTTP")


'打开发送通道


HttpSend.open "POST","SaveClientData.asp",false


HttpSend.send xmlDoc '发送数据


strReturn=HttpSend.ResponseText '接收返回结果


msgbox strReturn '弹出提示



?


示例中求表体的起始行和结束行都非常固定,用户可以增加表体标识精确判断。创建XMLHTTP对象,采用“POST”动作打开通道,发送后根据SaveClientData.asp页面处理返回的结果弹出提示,告知用户。


?


3.2 服务器端接收XML数据,分析、填充至数据库中


SaveClientData.asp页面根据客户端提交的XML数据,接收后,进行分析,并保存至数据库,关键代码如下(ASP):


?


'创建XMLDOM IE5.5以下用Msxml2.DOMDocument


set xmlDoc=server.CreateObject("Microsoft.XMLDOM")


xmlDoc.load Request? '加载远程提交XML文件



?


set xmlTitle=xmlDoc.SelectSingleNode("/实施报销单/表头[0]")? '选择表头,表头只有一个节点


set xmlBody=xmlDoc.SelectNodes("/实施报销单/表体")? '选择表体



?


Response.ContentType = "text/xml; charset=GB2312"



?


‘建立数据库连接


strcon="Provider=Microsoft.Jet.OLEDB.4.0;Data Source="&Server.Mappath("Data\Data.mdb")&";Persist Security Info=False"


set cmd=server.CreateObject("adodb.command")


cmd.ActiveConnection=strcon



?


‘先删除数据库中原表头表体中的数据


cmd.CommandText="delete from WipeOrder where nOrderID='" & Node.text & "'"


cmd.Execute


cmd.CommandText="delete from WipeOrders where nOrderID='" &Node.text & "'"


cmd.Execute


'表头


'组织表头SQL语句


For i=0 to xmlTitle.childnodes.length-1


strSql=strSql & xmlTitle.childnodes(i).nodeName&" ,"


strSubSql=strSubSql&"'"& xmlTitle.childnodes(i).Text&"' ,"


Next


'插入表头数据


strSql= mid(strSql,1,len(strSql)-1)& ")" & mid(strSubSql,1,len(strSubSql)-1)&")"


cmd.CommandText=strSql cmd.Execute ?'插入表头数据


?


'表体


'组织表体SQL语句


For i=0 to xmlBody.length-1


'组织表体的SQL语句

strSql="insert into wipeorders(nOrderID, " strSubSql=" values('" & Node.text& "',"


set xmlChildBody=xmlDoc.SelectSingleNode("/实施报销单/表体["&i&"]")? '得到第i个表体节点

for j=0 to xmlChildBody.childnodes.length-1 ?'遍历第i个表体节点的所有子节点

strSql=strSql & xmlChildBody.childnodes(j).nodeName&" ,"


strSubSql=strSubSql&"'"& xmlChildBody.childnodes(j).Text&"' ,"


Next


strSql= mid(strSql,1,len(strSql)-1)& ")" & mid(strSubSql,1,len(strSubSql)-1)&")"

cmd.CommandText=strSql cmd.Execute ?'插入表体数据


Next



?


set cmd=nothing? '清空对象


Response.Write "保存成功!" ?'返回成功信息



?


服务器端将客户端提交的XML数据,创建XML文档对象,并加载存放在request对象中的XML数据,再解析。示例程序中对XML数据的要求只限定在单据号,用户在实际项目中可以增加判断的条件。为保证数据库中数据的正确性,采用在保存新数据前,先将数据库中原数据记录全部删除 ,再插入新记录的方式。

  相关解决方案