需求描述:

在做一些类似 word 报告格式报表时,为了有更好的阅读效果,经常需要在首页增加一个目录导航页,这样就能够快速的知道关心的数据在第几页,但是报表不同于 word 文档,word 文档中格式都是固定的,所以能够方便快速的生成目录页,在报表中数据都是动态变化的,并且通常会涉及到动态扩展,这样很难固定目录项,并且由于数据动态扩展,对应的页码也很难精确获得,下面通过一个实例看下,如果在报表中动态增加目录。

解决方案:

报表数据是动态的,所以设计时就不能用固定的目录,需要报表计算后才能知道数据在第几页,润乾报表提供了一个报表计算侦听类,在 java 程序中可以动态获取报表计算后的结果,并能够在程序中动态更改单元格的值。

首先,看下报表模板的设计界面:

这个报表要对订单数据按照地区进行汇总分析,并展示详细数据,要求目录中以地区为导航进行设置,报表前 5 行是目录页,第三行中,概要分析一般是固定的,这里就写了固定一行,页码设置 2 就行。

A4:=ds1.group(货主地区; 货主地区:1),表达式按照地区进行分组,也就是目录这块按照地区进行展示,对应 E4 单元格后续要设置成目录页,此处暂时为空。

A5:目录通常在第一页,所以此处设置一个行后分页

A7:就是一个固定的汇总描述,里边可以用字符串拼接方式将固定文本和动态数据拼接在一起展示。

A8:=ds1.group(货主地区; 货主地区:1),按照地区进行分组

A9:对该地区数据做一个汇总说明

A10:=ds1.select(订单 ID),取数订单数据,B10 往后依次类推。

将 A9,A10,A11,A12 单元格的左主格设置成 A8,此片数据根据 A8 进行纵向扩展,这样报表展示结果为:

这里可以看到,目录项处列出了对应地区,接下来看下,如果给地区增加对应页码。

从报表结果中看到,第一页中的目录名称和报表中的地区名称相同,这样就可以根据这两个名称做匹配,判断如果名称相同获取数据区域的名称所在的页码,放到对应目录行就行,如数据区域的“东北”在第 2 页,那么目录中东北的页码应该为 2,接下来看下,如何给目录设置动态的页码。

这里就用到了之前说到的报表侦听类的使用,源码如下:

import com.raqsoft.common.Area;
import com.raqsoft.report.usermodel.Context;
import com.raqsoft.report.usermodel.IPagerListener;
import com.raqsoft.report.usermodel.IReport;
import com.raqsoft.report.usermodel.IReportListener;
import com.raqsoft.report.usermodel.PageBuilder;
import com.raqsoft.report.util.ReportUtils;

public class createmulu implements IReportListener {
	 public void afterCalc(Context arg0, IReport arg2) {
		 PageBuilder arg1 = null;
		try {
			arg1 = new PageBuilder(arg2);
		} catch (Throwable e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
			System.out.println("报表总页数"+arg1.getPageCount());
			IReport p1;
			try {
				 p1=arg1.getPage(1);//目录通常在第一页,所以获取第一页为目录页
				 int page=2;//设置数据初始循环页,通常为第二页
				 for(int i=3;i<=p1.getRowCount();i++){//目录行从第三行开始
					String muluName=(String)p1.getCell(i, 2).getValue() ;//报表中设置第二页为目录名称
					for(int j=page;j<=arg1.getPageCount();j++){//按照页数进行循环,分别取分页后每页报表对象
						for(int k=1;k<=arg1.getPage(j).getRowCount();k++){//每页中按照每行进行循环
						String mName=arg1.getPage(j).getCell(k,1).getValue() != null ? arg1.getPage(j).getCell(k,1).getValue().toString() : "";//本例中目录项在报表中的第一列,也就是A8单元格
						if(mName!="" && mName==muluName){//判断每页中的目录项和第一页中的目录名称是否相同
							arg2.getCell(i, 5).setValue(j);//如果相同,则设置目录页第5列对应的值为对应页码,意,此处是arg2对象
							arg2.getCell(i, 5).setHyperlink("javaScript:toPage('report1',"+j+")");//设置超链接
							page=j;//为提高计算效率,下次在循环时,不用从第2页开始,从上次终端的页码开始就行
							break;//找到页码,跳出此处循环,提高效率
						}
						}
					}
				 }
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	   }
	   public void beforeCalc(Context arg0, IReport arg1) {
	   }

}

import com.raqsoft.common.Area; import com.raqsoft.report.usermodel.Context; import com.raqsoft.report.usermodel.IPagerListener; import com.raqsoft.report.usermodel.IReport; import com.raqsoft.report.usermodel.IReportListener; import com.raqsoft.report.usermodel.PageBuilder; import com.raqsoft.report.util.ReportUtils; public class createmulu implements IReportListener { public void afterCalc(Context arg0, IReport arg2) { PageBuilder arg1 = null; try { arg1 = new PageBuilder(arg2); } catch (Throwable e1) { // TODO Auto-generated catch block e1.printStackTrace(); } System.out.println("报表总页数"+arg1.getPageCount()); IReport p1; try { p1=arg1.getPage(1);//目录通常在第一页,所以获取第一页为目录页 int page=2;//设置数据初始循环页,通常为第二页 for(int i=3;i<=p1.getRowCount();i++){//目录行从第三行开始 String muluName=(String)p1.getCell(i, 2).getValue() ;//报表中设置第二页为目录名称 for(int j=page;j<=arg1.getPageCount();j++){//按照页数进行循环,分别取分页后每页报表对象 for(int k=1;k<=arg1.getPage(j).getRowCount();k++){//每页中按照每行进行循环 String mName=arg1.getPage(j).getCell(k,1).getValue() != null ? arg1.getPage(j).getCell(k,1).getValue().toString() : "";//本例中目录项在报表中的第一列,也就是A8单元格 if(mName!="" && mName==muluName){//判断每页中的目录项和第一页中的目录名称是否相同 arg2.getCell(i, 5).setValue(j);//如果相同,则设置目录页第5列对应的值为对应页码,意,此处是arg2对象 arg2.getCell(i, 5).setHyperlink("javaScript:toPage('report1',"+j+")");//设置超链接 page=j;//为提高计算效率,下次在循环时,不用从第2页开始,从上次终端的页码开始就行 break;//找到页码,跳出此处循环,提高效率 } } } } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void beforeCalc(Context arg0, IReport arg1) { } }

核心思路就是在类中根据名称进行匹配,获取页码,放到对应单元格中,将编译好的类放到报表类路径中,如:应用的 WEB-INF\classes 下,意如果有包路径,此处要要带相应的路径,放置过去后重启应用,在页面端访问报表,结果如下:

可以看到,目录后边会生成对应的页码,word 中目录有个功能点是,点击页码,能够快速跳转到对应的页数,报表中同样可以增加对应的功能,报表提供了一个跳转页数的 js 函数,toPage,在 java 类中,在对应的页码单元格设置了一个超链接,调用这个 js 就行:

arg2.getCell(i, 5).setHyperlink("javaScript:toPage('report1',"+j+")");

arg2.getCell(i, 5).setHyperlink("javaScript:toPage('report1',"+j+")");

这样,在页面端点击页码,就能够快速跳转到对应页数,当然,此功能要求报表在页面端分页后才有效,否则无法跳转,并且如果导出到 word 的话,只能显示页数,无法跳转。

09-26 13:32