6.5 自定义报表方案

来源:互联网 发布:c语言自学知乎 编辑:程序博客网 时间:2024/05/21 19:47
6.5、自定义报表方案
现在越来越多的应用系统中都要求提供自定义报表功能,即用户可以自行设计、修改报表。除了要求打印操作之外,还要求可以导入多种文件格式,例如:Excel、Word、PDF等文件格式。然而自定义报表不仅仅功能强大,同时设计起来也比较复杂。
1.方案分析
自定义报表实现的功能是比较强大的,自定义报表主旨在于根据用户的选择动态生成报表,这里笔者为用户提供了表格字段选择,报表标题动态化,报表标题字体、报表正文字体、报表正文颜色、报表正文字体大小的选择,除此之外,根据用户选择的字段动态生成查询条件下拉列表,用户可以根据选择的字段填写查询条件进行查询,可以动态定义打印分页页码,预置了“打印预览”、“打印”、“打印设置”按钮,可以将报表分别导出到Excel、Word、PDF格式进行显示。为了更好的理解本方案,请读者看流程图,如图6.90所示。
图6.90  自定义报表打印方案流程图
2.实施过程
在开发图书馆管理系统时,要求系统对图书借阅情况进行报表打印,而为了得到更为详尽的信息,用户需要自定义图书借阅情况报表,并且能打印,可以根据不同情况将报表导入到Excel、Word、PDF等文件格式中。在这种情况下,就可以应用到自定义报表打印,如图6.91、6.92所示。
图6.91  自定义报表
图6.92  自定义报表打印
*  实例位置光盘/mr/6/6.5/6.5.1/20
为了更好的演示本方案,首先建立一个图书借阅数据表,数据结构如表6.19所示。
表6.19                  表格 tb_zdbb
字段名称
数据类型
描述
是否为空
id
int(4)
序列号
not null
tsmc
varchar(50)
图书名称
null
jysj
varchar(20)
借阅时间
null
yhsj
varchar(20)
应还时间
null
cbs
varchar(20)
出版社
null
sj
varchar(20)
书架
null
dj
money
单价
null
下面详细介绍各个部分实现原理。
为了实现表字段动态选择,可以使用JavaScript来实现,在一个下拉中菜单选择一些字段名称后,在另一个下拉菜单中即时显示这些字段,可以在这个下拉菜单中删除不需要显示的字段名称,选好字段名称后,使用JavaScript做表单提交,为了取得下拉菜单中的值,在表单中使用一个Hidden类型的文本框,把JavaScript中提交上来的字段值赋给这个文本框,部分程序代码如下:
例程6-103  代码位置:光盘/mr/6/6.5/6.5.1/20/zdbbnew.jsp
if (count == 0) {
   strValues = document.choiceForm.choiceBox.options[i].value;
}
else {
   strValues = strValues + "," + document.choiceForm.choiceBox.options[i].value;
}
count++;
if (strValues.length == 0) {
   alert("您没有选择字段值");
}
else  {
   choiceForm.hidd.value=strValues;
   choiceForm.submit();
alert("您已经选择的字段为:/r/n" + strValues);
   }
取出文本框中的值后,由于这个字符串的值以逗号分割,所以这里笔者使用String类中的split()方法,使这个数值以逗号分割返回到数组中,部分程序代码如下:
例程6-104  代码位置:光盘/mr/6/6.5/6.5.1/20/table.jsp
if(request.getParameter("hidd")!=""&&request.getParameter("hidd")!=null){
String available=request.getParameter("hidd");
String sub[]=available.split(",");
然后根据数组然后通过用户选择的表格字体、颜色、字体大小等使用CSS动态设置表格样式,部分程序代码如下:
例程6-105  代码位置:光盘/mr/6/6.5/6.5.1/20/table.jsp
<table width="737" style="font:<%=s.tranC(btzt)%>; font-size:14px" >
  <tr>
    <td><div align="center" >
   <%=s.tranC(biaoti) %>
    </div></td>
  </tr>
</table>
<table width="737"  border="1" bordercolor="#CCCCCC" style="font:<%=s.tranC(zwzt)%>; color:<%=ys%>; font-size: <%=zwztdx%> " >
         当用户没有根据字段进行查询的情形下,页面显示表格中的所有数据,为了美化页面,本方案中使用了分页技术,使用分页需要一个计算页码的类DownTable.java,DownTable.java的主要功能是计算总页码,设置每页显示的行数,取当前页数,取表格的总行数。为了实现DownTable.java中的方法,需要一个连接数据库的类ConnDB.java,此类与上述章节中的连接数据库类相同,所以在此笔者不再赘述。
在DownTable.java类中,计算表格总行数的方法无参数,返回值类型为int型,为表格总行数,此方法的具体实现实际上就是一条SQL语句,使用语句"select count(*) from tb_zdbb",结果集中返回第一个字段的值即为数据表的总行数,程序代码如下:
例程6-106  代码位置:光盘/mr/6/6.5/6.5.1/20/src/com/wsy/DownTable.java
public int getRows()
   {
       try{
              ConnDB conn=new ConnDB();
              rs=conn.executeQuery("select count(*) from tb_zdbb");
              if(rs.next())
              {
                 totalRows=rs.getInt(1);
              }
              s.close();
              conn.close();
       }
       catch(Exception e)
       {
       }
       return totalRows;
   }
得到数据表总行数后,由于DownTable.java中有一个setXXX()、getXXX()方法,用于用户控制每页显示的行数传值使用,所以可以使用getPageSize()取出用户希望每页显示的行数,在这里使用总行数除以每页显示的行数便可以得到总页数,程序代码如下:
例程6-107  代码位置:光盘/mr/6/6.5/6.5.1/20/src/com/wsy/DownTable.java
public int getTotalPage(){
       if(getRows()%getPageSize()==0)
              return getRows()/getPageSize();
       else
              return getRows()/getPageSize()+1;
   } 
分页除了一个计算页码的DownTable.java类之外,还需要一个“首页”、“上一页”、“下一页”、“尾页”连接,在连接中笔者设置一个累计变量current,用于取得当前用户浏览的页数,“首页”的连接中current值赋为1,“上一页”的连接current值赋为current+1,“下一页”的连接current值赋为current-1,“尾页”的连接中current值赋为DownTable.java类中的getTotalPage()方法中返回的值。程序代码如下:
例程6-108  代码位置:光盘/mr/6/6.5/6.5.1/20/table2.jsp
    <div align="center">
     <table width="65%">
       <tr class="style4">
         <td><a href=table2.jsp?current=1>首页</a></td>
         <td><a href=table2.jsp?current=<%=current-1 %>>上一页</a></td>
         <td><a href=table2.jsp?current=<%=current+1 %>>下一页</a></td>
         <td><a href=table2.jsp?current=<%=down.getTotalPage() %>>尾页</a></td>
       </tr>
     </table></div>
当用户没有输入查询条件时,通过取出current的值,便可以使用SQL语句进行分页处理,分页SQL语句为: "select top "+down.getPageSize()+" * from tb_zdbb where(id not in(select top "+down.getPageSize()*(current-1)+" id from tb_zdbb order by id)) order by id",然后根据用户选择的字段值循环表格列,根据列名,也就是字段名循环显示数据表中的数据,在此有一个问题,下拉菜单中的显示字段名称为中文,但下拉菜单中的value值却是英文,但在表格中显示字段时需要中文显示,根据字段取数据数据表中数据的时候则需要英文显示,此时,为了解决这个问题,笔者引用Map数据结构,把中文字段和英文字段对应起来,程序代码如下:
例程6-109  代码位置:光盘/mr/6/6.5/6.5.1/20/table.jsp
Map a=new HashMap();
a.put("id","id");
a.put("tsmc","图书名称");
a.put("jysj","借阅时间");
a.put("yhsj","应还时间");
a.put("cbs","出版社");
a.put("sj","书架");
a.put("dj","定价");
当遇到下拉菜单时,可以使用如下代码,数组sub为用户选择的字段值,a.get(sub[i])为在Map中取key对应中文值。
例程6-110  代码位置:光盘/mr/6/6.5/6.5.1/20/table2.jsp
<select name="cxtj" id="aa" >
             <%
              for(int i=0;i<sub.length;i++){
              %>
             <option value=<%=sub[i] %>><%=a.get(sub[i])%></option>
<%} %>
</select>
在页面显示表格时,字段名称需要中文显示,程序代码如下:
例程6-111  代码位置:光盘/mr/6/6.5/6.5.1/20/table2.jsp
   for(int i=0;i<sub.length;i++){
              %>
         <td><%=a.get(sub[i]) %></td>
         <%} %>
当显示数据时,根据结果集循环表格的行,根据用户选择的字段个数循环列,此时涉及到分页打印,根据用户输入的行数使用CSS控制打印分页,使用变量i累计变量,当i是用户输入行数的倍数时,使用CSS进行表格打印分页,在<tr>中使用"style='page-break-after:always'"样式,可以达到分页打印功能,程序代码如下:
例程6-112  代码位置:光盘/mr/6/6.5/6.5.1/20/table2.jsp
<%
       int i=1;
                 while(rs.next()){
 %>
       <tr <%if(i%fyRow==0){ %> style="page-break-after:always;"<%}%>>
         <%
               for(int j=0;j<sub.length;j++){
                  %>
         <td><%=rs.getString(sub[j]) %></td>
         <%
                } %>
       </tr>
       <%
       i++;
       } %>
当用户选择查询条件提交时,String类型的SQL变量被赋值为"select * from tb_zdbb where  "+cxtj+" like '%"+s.tranC(input)+"%' order by id",使用like进行模糊查询,tranC()是StringTrans.java类方法,是一个转码方法,把字符转为UTF-8编码,这个类曾经在以上章节中使用过,所以在此不再过多赘述。
结果显示到Web页面后,需要进行打印操作,本方案采用WebBrowser方式实现打印操作,需要在HTML中建立Object标签,调用WebBrowser控件,程序代码如下:
例程6-113  代码位置:光盘/mr/6/6.5/6.5.1/20/table2.jsp
<OBJECT   classid=CLSID:8856F961-340A-11D0-A96B-00C04FD705A2   height=0   id=WB   width=0>  
</OBJECT>
使用JavaScript设置打印设置、打印预览函数,代码如下:
例程6-114  代码位置:光盘/mr/6/6.5/6.5.1/20/table2.jsp
function   previewPrint(){  
  WB.ExecWB(7,1)  
  }  
function   setPrint(){  
  WB.ExecWB(8,1);  
  }
建立相关的打印超连接,并调用WebBrowser控件的相应参数实现打印预览、打印等功能。代码如下:
例程6-115  代码位置:光盘/mr/6/6.5/6.5.1/20/table2.jsp
<a href="#" class="style4" onclick="previewPrint();">打印预览 </a><a href="#" class="style4" onclick="window.print();"> 打印 </a> <a href="#" class="style4" onclick="setPrint();"> 打印设置</a>
当用户单击打印预览连接时,由于打印分页值默认为二,所以打印以两行进行分页,如图6.93所示。
图6.93  自定义报表打印预览
除了打印操作外,系统向用户提供了将报表导入到Excel、Word、PDF中,导出连接如下:
例程6-116  代码位置:光盘/mr/6/6.5/6.5.1/20/table2.jsp
<a href="#" class="style4" onClick="outExcel();"> 导出到Excel</a><a href="#" class="style4" onClick="outDoc();"> 导出到Word</a><a href="pdfOutput.jsp" class="style4"> 导出到PDF</a></div>
首先来看将报表导出到Excel文件格式,使用JavaScript的ActiveXObject()构造函数创建一个Excel.Application对象的实例来实现此功能,程序代码如下:
例程6-117  代码位置:光盘/mr/6/6.5/6.5.1/20/table2.jsp
<script language="javascript">
function outExcel(){
   var table=document.all.book;
   row=table.rows.length;
   column=table.rows(1).cells.length;
   var excelapp=new ActiveXObject("Excel.Application");
   excelapp.visible=true;
   objBook=excelapp.Workbooks.Add(); //添加新的工作簿
   var objSheet = objBook.ActiveSheet;
   title=objSheet.Range("D1").MergeArea;  //合并单元格
   title.Cells(1,1).Value ="书籍借还列表";
   title.Cells(1,1).Font.Size =16;
   for(i=1;i<row+1;i++){
       for(j=0;j<column;j++){
         objSheet.Cells(i+1,j+1).value=table.rows(i-1).cells(j).innerHTML.replace("&nbsp;","");
       }
   }
    objBook.SaveAs("C:/bookList.xls");
   excelapp.UserControl = true;
}
</script>
用户单击导出到Excel连接,结果如图6.94所示。
图6.94  自定义报表导出到Excel
将报表导出到Word格式文件中,同样使用JavaScript的ActiveXObject(),只是此时ActiveXObject()构造函数创建一个Word.Application对象的实例来实现导出到Word功能,程序代码如下:
例程6-118  代码位置:光盘/mr/6/6.5/6.5.1/20/table2.jsp
<script language="javascript">
function outDoc(){
  var table=document.all.book;
  row=table.rows.length;
  column=table.rows(1).cells.length;
  var wdapp=new ActiveXObject("Word.Application");
  wdapp.visible=true;
  wddoc=wdapp.Documents.Add();  //添加新的文档
  thearray=new Array();
//将页面中表格的内容存放在数组中
for(i=0;i<row;i++){
   thearray[i]=new Array();
   for(j=0;j<column;j++){
          thearray[i][j]=table.rows(i).cells(j).innerHTML;
   }
}
var range = wddoc.Range(0,0);
range.Text="书籍借还自定义列表"+"/n";
wdapp.Application.Activedocument.Paragraphs.Add(range);
wdapp.Application.Activedocument.Paragraphs.Add();
rngcurrent=wdapp.Application.Activedocument.Paragraphs(3).Range;
var objTable=wddoc.Tables.Add(rngcurrent,row,column)     //插入表格
for(i=0;i<row;i++){
   for(j=0;j<column;j++){
   objTable.Cell(i+1,j+1).Range.Text = thearray[i][j].replace("&nbsp;","");
   }
}
wdapp.Application.ActiveDocument.SaveAs("bookList.doc",0,false,"",true,"",false,false,false,false,false);
wdapp.Application.Printout();
wdapp=null;
}
</script>
当用户单击导出到Word连接时,结果如图6.95所示。
图6.95  自定义报表导出到Word
将报表导入到PDF中过程较以上两种导入操作要繁琐一些,本方案使用iText组件实现导入PDF,使用iText组件中com.lowagie.text.pdf.pdfPTable类来实现,iText组件详细介绍请读者参看6.1.1。
首先来看表题部分,创建列数为用户选择的字段个数的表格,创建单元格,把用户输入的标题添加到单元格中,此时使用PDFParagraph.java控制中文输出,单元格向右扩展,扩展单元格的个数为用户选择的字段的个数,程序代码如下:
例程6-119  代码位置:光盘/mr/6/6.5/6.5.1/20/pdfOutput.jsp
PdfPTable table=new PdfPTable(sub.length);
PdfPCell cellh=new PdfPCell(new PDFParagraph(s.tranC(biaoti)));
cellh.setColspan(sub.length);
table.addCell(cellh);
然后是表体部分,首先是添加字段名称,根据用户选择的字段个数,创建单元格,在单元格中添加用户选择的字段名称,注意利用亚洲包类库使字段名称转化为中文显示,程序代码如下:
例程6-120  代码位置:光盘/mr/6/6.5/6.5.1/20/ pdfOutput.jsp
PdfPCell cell=null;
for(int i=0;i<sub.length;i++){
cell=new PdfPCell();
cell.addElement(new PDFParagraph((String)a.get(sub[i])));
table.addCell(cell);
}
表体部分除了字段名称之外还有表格数据,首先连接数据库,根据字段名称取出数据表中的值,添加到单元格中,然后将单元格添加到表格中。程序代码如下:
例程6-121  代码位置:光盘/mr/6/6.5/6.5.1/20/ pdfOutput.jsp
ConnDB conn=new ConnDB();
   ResultSet rs=conn.executeQuery(sql);
       while(rs.next()){
                 for(int j=0;j<sub.length;j++){
                     table.addCell(new PDFParagraph(rs.getString(sub[j])));
                   }
         }
最后以PDF流的形式输出。
当用户单击导出到PDF连接时,结果如图6.96所示。
图6.96  自定义报表导出到PDF
3.补充说明
本方案中笔者采用WebBrowser方式进行打印操作,WebBrowser是IE内置的浏览器控件,该控件的具体参数为:
document.all.WebBrowser.Execwb(7,1):表示打印预览。
document.all.WebBrowser.Execwb(6,1):表示打印。
document.all.WebBrowser.Execwb(6,6):表示直接打印。
document.all.WebBrowser.Execwb(8,1):表示页面设置。
document.all.WebBrowser.Execwb(4,1):表示另存为。
document.all.WebBrowser.Execwb(1,1):表示打开。
document.all.WebBrowser.Execwb(17,1):表示全选。
document.all.WebBrowser.Execwb(10,1):表示属性。
document.all.WebBrowser.Execwb(8,1):表示页面设置。
document.all.WebBrowser.Execwb(22,1):表示刷新页面。
document.all.WebBrowser.Execwb(45,1):表示关闭窗体时无提示。