6.4 高级报表打印方案
6.4.1 生成分组报表
分组报表是根据表中的某个条件在另一张表中查询相关此内容的一系列信息,并将此类信息打印的报表。分组报表在实际中应用广泛。
1.方案分析
本章节将以WebBrowse打印技术演示分组报表,通过药品相关信息表中的药品编号与药品销售表中的药品号相关联查询数据,以达到分组的目的,为了使读者更好的了解此方案,下面给出流程图,如图6.48所示。

图6.48 分组报表打印流程图
2.实施过程
在开发药品后台管理系统时,通常要求以药品种类分组显示药品销售情况,在这种情况下,就可以应用到分组报表打印,如图6.49所示。

图6.49 药品销售管理系统分组报表打印
实例位置:光盘\mr\6\6.4\6.4.1\11
首先创建两个表,tb_yptable和tb_xsbb,分别为药品相关信息表和药品销售表,数据结构如表6.9、表6.10所示。
表6.9 表格 tb_yptable药品相关信息表
|
字段名称 |
数据类型 |
描述 |
是否为空 |
|
id |
int(4) |
序列号 |
not null |
|
ypid |
char(10) |
药品编号 |
null |
|
ypname |
varchar(20) |
药品名称 |
null |
|
ypregion |
varchar(20) |
药品产地 |
null |
|
ypgg |
varchar(20) |
药品规格 |
null |
|
ypbz |
varchar(10) |
药品包装 |
null |
表6.10 表格 tb_xsbb药品销售表
|
字段名称 |
数据类型 |
描述 |
是否为空 |
|
id |
int(4) |
序列号 |
not null |
|
ypph |
varchar(20) |
药品票号 |
null |
|
number |
int(4) |
数量 |
null |
|
dj |
money(8) |
单价 |
null |
|
je |
money(8) |
金额 |
null |
|
khqc |
varchar(50) |
客户全称 |
null |
|
ypbh |
varchar(20) |
药品编号 |
null |
本实例以药品票号关联两张表,同样也以药品编号进行分组。首先在页面中建立表格,背景设置为表格图片,程序代码如下:
例程6-58 代码位置:光盘\mr\6\6.4\6.4.1\11\tj.jsp
<table width="455" height="54" border="0" align="center" background="Images/tj2_05_01.gif" >
为了取得数据表中的值,创建JavaBean,名称为Tdbb.java,Tdbb.java除了基本的setXXX()和getXXX()方法之外,还有一个根据条件取数据的getReport()方法,此方法的参数是用户输入的产地,getReport()方法返回值类型为集合Collection,方便JSP取值时使用,程序代码如下:
例程6-59 代码位置:光盘\mr\6\6.4\6.4.1\11\src\com\wsy\Tdbb.java
public Collection getRecord(){
Collection ret=new ArrayList();
String sql="select * from tb_yptable";
ConnDB conn=new ConnDB();
ResultSet rs=conn.executeQuery(sql);
try{
while(rs.next()){
Tdbb t=new Tdbb();
t.setId(rs.getString(1));
t.setYpid(rs.getString(2));
t.setYpname(rs.getString(3));
t.setYpregion(rs.getString(4));
t.setYpgg(rs.getString(5));
t.setYpbz(rs.getString(6));
ret.add(t);
}
conn.close();
}catch(Exception e){
e.printStackTrace();
}
return ret;
}
取出药品相关信息表中内容放入Collection中,以便于显示,程序代码如下:
例程6-60 代码位置:光盘\mr\6\6.4\6.4.1\11\tdbb.jsp
Collection ret=(Collection)t.getRecord();
根据药品编号条件将两个表联系在一起,在 SQL语句中用左外连接把两个表联系起来,根据报表编号取得结果集,此方法与getRecord()方法采用了重载技术,具体程序代码如下:
例程6-61 代码位置:光盘\mr\6\6.4\6.4.1\11\src\com\wsy\Tdbb.java
public Collection getRecord(String ypbh){
Collection ret=new ArrayList();
String sql="SELECT t1.ypph, t1.number, t1.dj, t1.je, t1.khqc ,t1.ypbh FROM tb_xsbb t1 LEFT OUTER JOIN tb_yptable t2 ON t1.ypbh = t2.ypid where ypbh='"+ypbh+"'";
ConnDB conn=new ConnDB();
ResultSet rs=conn.executeQuery(sql);
try{
while(rs.next()){
Tdbb t=new Tdbb();
t.setJe(rs.getString("je"));
t.setKhqc(rs.getString("khqc"));
t.setNumber(rs.getString("number"));
t.setDj(rs.getString("dj"));
t.setYpph(rs.getString("ypph"));
t.setYpbh(rs.getString("ypbh"));
ret.add(t);
}
conn.close();
}catch(Exception e){
e.printStackTrace();
}
return ret;
}
在JSP中设置JavaScript打印按钮,程序代码如下:
例程6-62 代码位置:光盘\mr\6\6.4\6.4.1\11\ tj.jsp
<table align="center" id="pay">
<tr class="print">
<td ><INPUT type="button" value="打印设置" id=button1 name=button1 onclick="setPrint();">
<INPUT type="button" value="打印预览" id=button2 name=button2 onclick="previewPrint();">
<INPUT type="button" value="打印" id=button3 name=button3 onclick="window.print();"></td>
</tr>
</table>
注意<head></head>之间要放入<JavaScript>代码,程序代码如下:
例程6-63 代码位置:光盘\mr\6\6.4\6.4.1\11\tj.jsp
<script language="Javascript">
function previewPrint(){
WB.ExecWB(7,1)
}
function setPrint(){
WB.ExecWB(8,1);
}
</script>
注意需在<body>之间放入以下代码,上述JavaScript才可以使用:
例程6-64 代码位置:光盘\mr\6\6.4\6.4.1\11\tdbb.jsp
<OBJECT classid=CLSID:8856F961-340A-11D0-A96B-00C04FD705A2 height=0 id=WB width=0>
</OBJECT>
3.补充说明
在JSP中使用迭代函数列出数据较其他方案取值有一些特别,首先取出药品相关信息表的相关数据,并且打印在JSP界面上,然后根据药品票号取出药品销售信息表的相关数据,也就是使用了嵌套循环的方式实现统计报表功能。
6.4.2 生成主从报表
主从报表又叫做复合报表,即报表本身又包含了一张报表,两张报表的关系可以是从属关系,也可以是并列关系。
1.方案分析
首先要说明的是,子报表也是一张真正的报表,也会编译成相应的*.jasper文件,如果创建一个复合报表,需要创建两张报表,父报表和子报表,父报表是最终用来显示主从报表。在这个方案中,笔者依然使用iReport+JasperReport组件来实现,首先设计父报表,再根据主从报表的关系使用SQL语句查询子报表的数据,然后设计子报表,之后在主报表中放入子报表,具体流程如图6.50所示。

图6.50 打印主从报表流程图
2.实施过程
在开发杰明客户管理系统时,要求输出客户订单详情,包括客户信息,客户订单信息,为了提高系统的运行效率,可以将客户订单信息放入子报表中,以客户编号作为参数从父报表传递到子报表中,在这种情况下,可以应用到主从报表,如图6.51所示。

图6.51 客户订单表
实例位置:光盘\mr\6\6.4\6.4.2\12
为了演示复合报表的过程,首先创建两个表,分别为客户详细信息表和客户订单表,如表6.11、6.12所示。
表6.11 表格 tb_customer表
|
字段名称 |
数据类型 |
描述 |
是否为空 |
|
orderid |
int(4) |
订单号 |
not null |
|
custname |
varchar(100) |
客户名称 |
null |
|
customerid |
varchar(10) |
客户id |
null |
|
customadd |
varchar(100) |
客户住址 |
null |
表6.12 表格 tb_orderinfo表
|
字段名称 |
数据类型 |
描述 |
是否为空 |
|
orderid |
int(4) |
订单号 |
not null |
|
custname |
varchar(100) |
客户名称 |
null |
|
address |
varchar(200) |
地址 |
null |
|
e_mail |
varchar(100) |
电子邮件 |
null |
|
booklist |
varchar(300) |
订单信息 |
null |
|
price |
float(8) |
价格 |
null |
|
time |
varchar(20) |
订单时间 |
null |
|
num |
int(4) |
个数 |
null |
创建报表名字为fuheReport,在“Data”→“报表查询”输入SQL语句为“select * from tb_customer”,将字段拖入detail区域,为了能让字段名称循环打印,在detail区域输入字段名称,可以根据需要设计风格各异的报表。
设置完成后在iReport组件菜单栏中单击
看是否能正常运行。
新建子报表,命名为subReport,创建一个主从报表传值时使用的参数,右击“Parameters”→“add”选择Parameter,新增参数名称custname,“Default Value Expression”设置为" ",如图6.52所示。

图6.52 设置父报表参数
在“Data”→“报表查询”中输入SQL语句,“select * from tb_orderinfo where custname=$P{custname}”,如图6.53所示。

图6.53 Report query界面
由于子报表最终要在父报表中显示,所以将不必要的栏位高度设置为0,右键单击栏位即可设置,设置完毕编译生成subreport.jasper文件。
子报表设计完成就需要把子报表引入到父报表中。单击
“子报表”图标,在父报表detail栏位适当的位置拖入子报表,此时会弹出对话框设置子报表,如图6.54所示。

图6.54 iReport添加子报表
由于已经制作好了子报表,所以在图6.54中选择"Use an existing report",浏览到已经设置好的子报表的路径,单击next按钮,注意在subreport expession这个页面选择第一个选项,以第一种形式表示子报表的路径。如图6.55所示:

图6.55 iReport添加子报表
单击“Finish”按钮完成设置,如图6.56所示。

图6.56 iReport设计复合报表
接下来开始设置子报表属性,在主报表中双击子报表图表,在弹出的属性窗口中选择”"Subreport"标签页面,在Connection/Data Source expression下拉列表框中选择Use connection expression,如图6.57所示。

图6.57 iReport设置子报表属性
然后选择“Subreport(Other)”页面,设置如图6.58所示。

图6.58 iReport设置子报表属性
还需要增加一个子报表参数,默认值为custname字段,创建名称为custname的参数设置,如图6.59所示。

图6.59 添加custname参数
这样做的目的是把父报表的customer表中的custname字段的值作为参数传入子报表,子报表根据SQL语句检索tb_orderinfo表中对应的数据。
完毕后回到父报表,单击
按钮执行,这时会要求输入子报表所在的位置,单击"Use
default",使用SUBREPORT_DIR这个参数的默认值,如图6.60所示。

图6.60 Parameter prompt界面
做完上述的工作后,需要在JSP中读出PDF报表,此时使用JasperReport组件,首先需要将iReport制作好的fuhe.jrxml和subReport.jrxml放入Tomcat安装路径下\Webapps\projectname\reports中,注意同时将光盘中的clients_05.gif、clients_02.gif两张图片放入Tomcat安装路径下\Webapps\projectname\reports路径中,然后使用JasperReport类库中的compileReportToFile()进行编译。程序代码如下:
例程6-65 代码位置:光盘\mr\6\6.4\6.4.2\12\compile.jsp
JasperCompileManager.compileReportToFile(application.getRealPath("/reports/fuheReport.jrxml"),application.getRealPath("/reports/fuheReport.jasper"));
JasperCompileManager.compileReportToFile(application.getRealPath("/reports/subReport.jrxml"),application.getRealPath("/reports/subReport.jasper"));
//分别把主报表和子报表的*.jrxml格式编译成*.jasper格式。
%>
注意:如果使用上述程序,单击
按钮执行,这时会要求输入子报表所在的位置,如图6.60所示。请选择“Use default”,使用子报表参数的默认值,默认值为"",虽然是空值,不过会在下面的JSP中将赋予SUBREPORT_DIR这个参数值,也就是子报表所在的位置。
在父报表中有一个名字为SUBREPORT_DIR的Parameters,在JSP中赋予相应的值,也就是子报表的路径。程序代码如下:
例程6-66 代码位置:光盘\mr\6\6.4\6.4.2\12\zc.jsp
parameters.put("SUBREPORT_DIR",request.getRealPath("/reports/")+"/");
编译成PDF流,编译的时候会要求子报表的路径,由于之前设置了SUBREPORT_DIR为"Use default",所以系统会自动找到子表的真实位置。
例程6-67 代码位置:光盘\mr\6\6.4\6.4.2\12\zc.jsp
byte[] bytes = run.runReportToPdf(fuhereportFile.getPath(), parameters, conn);
执行结果如图6.61所示。

图6.61 JasperReport控制PDF格式输出界面
单击图6.61中的execute连接,主从报表以PDF格式输出。
3.补充说明
做主从报表JasperReport调用唯一的难点就是子报表的路径问题,在主报表中设置一个子报表路径参数,在程序中可以赋予参数实际的值以达到系统找到子报表的目的,但值得注意的是,在动态编译主从报表时,启用“use default”。
本方案中依然在报表中添加图片,以参数的方式赋予图片的路径,由于在统计报表中已经详细介绍过这个方法,所以在这里不再赘述。
6.4.3 生成分栏报表
分栏报表是报表中比较常见的报表形式,本章节着重说明分栏报表的制作,很多软件中都包括分栏操作,如Word、Excel等,可见分栏在现实系统中应用多么广泛。分栏报表打印的好处就在于可以节约纸张。
1.方案分析
本章示例依然延用iReport+JasperReport开发模式。
在iReport组件中制作分栏报表首先应该想到的是用什么条件进行分栏,一般情况都是采用分组条件进行分栏设定,然后考虑需要传入什么参数,如果在JSP中调用报表,参数就显得尤为重要,在本方案中,笔者设计传入显示行数的参数以及报表标题参数,此时显示的行数与报表标题都为动态的。在iReport组件中制作报表,设定栏位,创建参数,然后将*.jrxml放入项目中,使用JasperReport类库进行编译,在JSP中给报表参数赋值,最后以PDF形式显示报表。
下面就来看分栏报表实现流程图,如图6.62所示。

图6.62 分栏报表流程图
2.实施过程
在开发多尼工资系统中,会有一个工资表输出打印的操作,由于工资表内容繁多,在一个界面上正常显示分页较多,这时可以选择分栏显示,方便用户浏览,在这种情况下,就可以应用分栏报表,如图6.63所示即为根据员工职位进行分栏显示。

图6.63 iReport设计分栏报表
实例位置:光盘\mr\6\6.4\6.4.3\13
首先建立一个表,这是一个工资信息表tb_fenlan,数据结构如表6.13所示。
表6.13 表格 tb_fenlan表
|
字段名称 |
数据类型 |
描述 |
是否为空 |
|
id |
int(4) |
序列号 |
not null |
|
name |
varchar(20) |
姓名 |
null |
|
jbgz |
money(8) |
基本工资 |
null |
|
jj |
money(8) |
奖金 |
null |
|
ylbx |
money(8) |
医疗保险 |
null |
|
yanglao |
money(8) |
养老保险 |
null |
|
sgbx |
money(8) |
事故保险 |
null |
|
zfgjj |
money(8) |
住房公积金 |
null |
|
bb |
money(8) |
补助 |
null |
|
sfgz |
money(8) |
实发工资 |
null |
|
fftime |
varchar(20) |
发放时间 |
null |
|
zw |
varchar(10) |
职位 |
null |
打开iReport,创建新报表fenlan,新建一个参数ReportTitle,选择Use as a Prompt复选框,如图6.64所示。

图6.64 创建ReportTitle参数
将参数ReportTitle拖入title区域。
可以在报表title区域输出表的总记录数,输出总收益数,由于总记录数字是系统自带变量,可以直接引用。打开Variables区域,将Report_count变量拖入title区域,需要将Report_count转化成String形式。
单击
按钮,在title区域画入一个Text Field区域,在属性区域的Epression输入如下内容:
"在报表中有 " + String.valueOf($V{REPORT_COUNT}) + "条记录"
建立名为MaxOrderID参数,表示要用户输入所要显示的行数,将MaxOrderID拖入title区域,如图6.65所示。

图6.65 创建MaxOrderID参数
Title区域设计如图6.66所示。

图6.66 Title区域设计
这时设计分栏效果,选择报表属性,选择Report columns 添入栏位数为3,如图6.67所示。

图6.67 iReport设置栏位
设计分组变量,变量名为zw,以职位分组显示,如图6.68所示。

图6.68 iReport设置组
选择“Start on a New Column”复选框,表示每一次从新列开始。其中放入需要显示的字段,detail区域放入查询出来的结果集。在zwheader区域放入职位名称,将$F{zw}拖入zwheader区域,在zwFooter区域放入分组后的个数,由于这个变量是系统自带,不需要创建,而是手动拖入zwFooter区域即可。此部分设计如图6.69所示。

图6.69 栏位设计
动态执行报表,此时会要求输入表头信息,MaxOrderid 的值,也就是所要显示的个数,如图6.70所示。

图6.70 最大行数添加界面
同时也会要求输出报表标题参数,如图6.71所示。

图6.71 表头添加界面
来看用JSP如何调用报表,还是和以往的形式一样,首先把制作好的报表以及在报表中使用的两张图片放入Tomcat\Webapps\jasperWebappproject\reports路径中。
执行compile.jsp,方可执行打印报表,编译制作好的报表,使报表生成*.jasper形式。程序代码如下:
例程6-68 代码位置:光盘\mr\6\6.4\6.4.3\13\compile.jsp
JasperCompileManager.compileReportToFile(application.getRealPath("/reports/fenlan2.jrxml"),application.getRealPath("/reports/fenlan2.jasper"));
设置表头名称,参数为ReportTitle,需要显示行数,参数为MaxOrderID,图片路径BaseDir,最后以PDF流的形式打印出来。程序代码如下:
例程6-69 代码位置:光盘\mr\6\6.4\6.4.3\13\fenlan.jsp
<%
File reportFile = new File(application.getRealPath("/reports/fenlan.jasper"));
Map parameters = new HashMap();
parameters.put("ReportTitle", "工资表");
parameters.put("MaxOrderID",7);
parameters.put("BaseDir", reportFile.getParentFile());
Connection conn=null;
try { Class.forName("com.microsoft.jdbc.SQL Server 2000.SQLServerDriver");
conn=DriverManager.getConnection("jdbc:microsoft:SQL Server 2000://localhost:1433;DatabaseName=db_FABD06","sa","");
}catch(Exception e) {
e.printStackTrace();
}
JasperRunManager run=new JasperRunManager();
byte[] bytes = run.runReportToPdf(reportFile.getPath(), parameters, conn);
response.setContentType("application/pdf");
response.setContentLength(bytes.length);
ServletOutputStream ouputStream = response.getOutputStream();
ouputStream.write(bytes, 0, bytes.length);
ouputStream.flush();
out.clear();
out=pageContext.pushBody();
ouputStream.close();
%>
3.补充说明
细心的读者可能发现,在iReport中的图片可以自动找到路径,但在JSP调用的时候却提示图片不存在,为了解决这个问题,创建一个名为BaseDir的参数,类型设置为Java.io.File形式,具体如图6.72所示。

图6.72 BaseDir参数设置界面
参数值设置为reportFile.getParentFile(),读者可以打印这个值,正是Tomcat\Webapps\jasperWebappproject\reports。
修改图片的路径为:new File($P{BaseDir}, "JasperReports.gif"),类型为Java.io.File,如图6.73所示。

图6.73 Image属性
6.4.4 生成交叉报表
交叉报表在企业应用广泛,非常适合企业进行员工业务考评等。
1.方案分析
本方案与其他方案相同,首先连接数据库,取出数据表中的数据,为了方便传值,使用JavaBean技术,本方案使用员工业务考评演示交叉报表,为了将员工业绩信息录入数据库中,在JavaBean中设计一个插入数据的方法,该方法的返回值为整型,返回值可以确定是否将数据插入到数据库中,除此之外还有一个查询的JavaBean方法,此方法的返回值类型为Collection集合,在JSP中引用JavaBean,将数据输出到界面上,员工考评表中拥有使用前面两个字段的比较结果动态变化字段,此时就应用到了交叉表的概念,使用交叉表的SQL语句即可完成上述功能,最后使用JavaScript技术实现报表打印,为了更好理解JavaScript实现交叉报表的全过程,首先看一下流程图,如图6.74所示。

图6.74 交叉报表流程图
2.实施过程
在企业内部管理系统中,有时会根据前面员工考评的业绩动态生成结果,例如根据计划销售量与员工实际销售量之间的差额动态生成考评结果,这样方便了用户使用,能高效地完成员工考评工作,在这种情况下,就可以应用交叉报表,如图6.75、图6.76所示。

图6.75 员工考评表录入

图6.76 员工考评报表打印
为了更直观的演示本方案,首先在数据库中创建员工考评表和测评结构表,数据字典如表6.14、表6.15所示。
表6.14 表格 tb_ygkp员工考评表
|
字段名称 |
数据类型 |
描述 |
是否为空 |
|
id |
int(4) |
序列号 |
not null |
|
name |
varchar(20) |
姓名 |
null |
|
zjld |
varchar(20) |
直接领导 |
null |
|
zw |
varchar(20) |
职务 |
null |
|
jhyj |
float(8) |
计划业绩 |
null |
|
sjyj |
float(8) |
实际业绩 |
null |
|
dep |
varchar(20) |
部门 |
null |
|
df |
varchar(20) |
得分 |
null |
表6.15 表格 tb_pingC 测评结构表
|
字段名称 |
数据类型 |
描述 |
是否为空 |
|
id |
int(4) |
序列号 |
not null |
|
pcxm |
varchar(50) |
评测项目 |
null |
|
qz |
float(8) |
权重 |
null |
|
df |
varchar(20) |
得分 |
null |
实例位置:光盘\mr\6\6.4\6.4.4\14
与上面几个章节处理连接数据库方法相同,将数据库连接代码封装在一个类中,名为Conn.java。为了方便取值,创建一个名为Ygkp.java的JavaBean,此类除了对应数据表中字段的setXXX()、getXXX()方法之外,还有一个向数据表中插入数据的insert()方法,此方法的参数为用户录入的员工考评的详细信息数据,返回值类型为int型,在insert()方法中,引用了PreparedStatement类中setString()方法,插入的SQL语句可以写为SQL="insert into tb_ygkp(name,zjld,zw,jhyj,sjyj,dep,df)values(?,?,?,?,?,?,?)",最后的返回值为PreparedStatement类中的executeUpdate()返回的值,1代表插入成功,0代表没有插入成功,具体程序代码如下:
例程6-70 代码位置:光盘\mr\6\6.4\6.4.4\14\src\com\wsy\Ygkp.java
try{
String sql="insert into tb_ygkp(name,zjld,zw,jhyj,sjyj,dep,df)values(?,?,?,?,?,?,?)";
PreparedStatement pstmt=conn.executePUpdate(sql);
pstmt.setString(1, name);
pstmt.setString(2, zjld);
pstmt.setString(3, zw);
pstmt.setString(4, jhyj);
pstmt.setString(5, sjyj);
pstmt.setString(6, dep);
pstmt.setString(7, df6);
i=pstmt.executeUpdate();
}
除此之外还有一个从数据表中取值的getRecords()方法,返回值类型为集合Collection,其中SQL语句为sql="SELECT *, CASE WHEN (jhyj - sjyj) >= 0 THEN CASE WHEN (jhyj - sjyj) > 0 THEN '↓' ELSE '㊣' END ELSE '↑' END AS qk FROM tb_ygkp";代表如果计划业绩量大于实际业绩量,qk字段的数据为"↓",如果计划业绩量小于实际业绩量,qk字段的数据为"↑",同理,如果计划业绩量等于实际业绩量,qk字段的数据为"㊣",最后从数据库中取出的值通过JavaBean传值,保存到Collection中。具体程序代码如下:
例程6-71 代码位置:光盘\mr\6\6.4\6.4.4\14\src\com\wsy\Ygkp.java
public Collection getRecords(){
Collection ret=new ArrayList();
String sql="SELECT *, CASE WHEN (jhyj - sjyj) >= 0 THEN CASE WHEN (jhyj - sjyj) > 0 THEN '↓' ELSE '㊣' END ELSE '↑' END AS qk FROM tb_ygkp";
Conn conn=new Conn();
ResultSet rs=conn.executeQuery(sql);
try{
while(rs.next()){
Ygkp y=new Ygkp();
y.setId(rs.getString(1));
y.setName(rs.getString(2));
y.setZjld(rs.getString(3));
y.setZw(rs.getString(4));
y.setJhyj(rs.getString(5));
y.setSjyj(rs.getString(6));
y.setDep(rs.getString(7));
y.setDf6(rs.getString(8));
y.setQk(rs.getString(9));
ret.add(y);
}
}catch(Exception e){
e.printStackTrace();
}
return ret;
}
在录入员工考评界面中,设计用户选择分数的功能,此时使用了JavaScript,为单选按钮添加事件,当用户选择分数后的单选按钮时,激发了指定的文本框的值,为选择的分数乘以此类评测在总分中的权重,例如,户选择了“团队协调性”中10分,那么此类评测的得分为10*0.1,此时“得分”后面的文本框中的值为1,最后计算出总分数为各个评测所得分数的和。如图6.77所示:

图6.77 员工评测选择分数
当用户录入数据后,需要将数据提交到指定页面,并将结果显示在界面上,在这里笔者使用了JavaScript提交方式,并采用了弹出页面的方式将员工考评信息显示出来。具体代码如下:
例程6-72 代码位置:光盘\mr\6\6.4\6.4.4\14\jcbb.jsp
document.all.form1.submit();
window.open('jcbb2.jsp','advertise','width=700,height=300,top=10,left=20,scrollbars=yes');
在以往的方案中将数据提交上来后使用request.getParameter()取值,这样的方式很繁琐,其实JSP为程序员提供了更为简便的方式,即使用<jsp:setProperty>方式,当在JavaBean中定义的属性值与表单中的文本框的名字相同时,在JSP中使用<property="*">并提交,JSP自动将用户输入的值对应放入JavaBean中,当需要时,使用getXXX()方法即可使用,定义scope为session时,与此JSP有连接关系的界面都可以接收此JavaBean的值,具体代码如下:
<jsp:useBean id="y" class="com.wsy.Ygkp" scope="session"/>
<jsp:setProperty name="y" property="*"/>
在显示数据界面中,使用迭代函数取JavaBean中的值,程序代码如下:
例程6-73 代码位置:光盘\mr\6\6.4\6.4.4\14\jcbb2.jsp
<%
Collection ret=y.getRecords();
Iterator it=ret.iterator();
while(it.hasNext()){
Ygkp ys=(Ygkp)it.next();
%>
<tr bgcolor="#FFFFFF">
<td><%=ys.getName()%></td>
<td><%=ys.getDep() %></td>
<td><%=ys.getZjld() %></td>
<td><%=ys.getZw() %></td>
<td><%=ys.getJhyj() %></td>
<td><%=ys.getSjyj() %></td>
<td>
<%=ys.getQk() %>
</td>
<td><%=ys.getDf6()%></td>
<td><select name="select2">
<option value="1">↓</option>
<option value="2" selected>㊣</option>
<option value="3">↑</option>
</select></td>
<td><select name="select3">
<option value="1">↓</option>
<option value="2" selected>㊣</option>
<option value="3">↑</option>
</select>
显示数据之后,需要在JSP界面上设置打印按钮、打印预览按钮、打印设置按钮,程序代码如下:
例程6-74 代码位置:光盘\mr\6\6.4\6.4.4\14\jcbb2.jsp
<p>
<INPUT type="button" value="打印设置" id=button1 name=button1 onclick="setPrint();">
<INPUT type="button" value="打印预览" id=button2 name=button2 onclick="previewPrint();">
<INPUT type="button" value="打印" id=button3 name=button3 onclick="window.print();">
</p>
为了使用打印设置和打印预览按钮,需要在<head></head>之间设置JavaScript代码,如下:
例程6-75 代码位置:光盘\mr\6\6.4\6.4.4\14\jcbb2.jsp
<script language="Javascript">
function previewPrint(){
WB.ExecWB(7,1)
}
function setPrint(){
WB.ExecWB(8,1);
}
</script>
在打印预览时候,打印按钮会显示在打印界面上,使用CSS来控制打印按钮不显示。程序代码如下:
例程6-76 代码位置:光盘\mr\6\6.4\6.4.4\14\jcbb2.jsp
<style>
@media print{
div{display:none}
}
</style>
当单击打印预览时,结果如图6.78所示。

图6.78 员工考评表打印预览
3.补充说明
交叉报表在实际应用中非常广泛,意在使用现有字段的值,统计或则作一些运算取值作为新的字段显示到JSP页面中去。
本例子使用静态交叉报表,作为静态交叉有着本身的弊端,列数在语句中需要一一指定,不能根据数据动态调整列数。
在本方案中为单选按钮添加事件使用了DreamWeaver完成的,首先在设计页面中点中所要添加事件的单选按钮,选择DreamWeaver右面的“标签检查器” 下拉菜单,单击“+”,选择“设置文本”→“设置文本域文字”,选择所要设置的文本,添入所要显示的值,如图6.79所示。

图6.79 为控件设置事件
6.4.5 生成套打报表
“套打”是现在应用较为广范的一种打印方式。“套打”是指在使用事先印制有凭证、账簿、报表格式、证书等纸张上进行打印。这种打印方式在打印时,无须打印表格的表格线及其他固定的格式内容,有利于加快打印速度,节约打印耗材,延长打印机的使用寿命。
1.方案分析
首先扫描或用PhotoShop等制图软件制作出证书背景,然后从数据库中的读取数据,在Dreamweaver中按证书规格要求放置表格,然后在表格中放置数据,最后运用JavaScript方法打印报表,具体流程如图6.80所示。

图6.80 套打报表流程图
2.实施过程
在开发某学校管理系统时,用户要求按规格打印教师资格证书,为了提高打印效率,可以把证书背景放入图片中,每次只打印教师详细信息,这样不仅提高了工作效率,而且节省资源消耗,在这种情况下,就可以应用到套打报表,如图6.81所示。

图6.81 证书套打
为了更好的演示套打报表,首先创建一个表格,数据结构如表6.16所示。
表6.16 表格 tb_tdbb
|
字段名称 |
数据类型 |
描述 |
是否为空 |
|
id |
int(4) |
序列号 |
not null |
|
zgmc |
varchar(20) |
资格名称 |
null |
|
zsbh |
varchar(20) |
证书编号 |
null |
|
rzdw |
varchar(30) |
人证单位 |
null |
|
zzr |
varchar(10) |
持证人 |
null |
|
dw |
varchar(10) |
单位 |
null |
|
zw |
varchar(10) |
职位 |
null |
|
zc |
varchar(10) |
职称 |
null |
|
xk |
varchar(10) |
学科 |
null |
|
fzdw |
varchar(20) |
发证单位 |
null |
|
rzsj |
varchar(10) |
认证时间 |
null |
