最近处理完一个asp.net项目,其中有大量的报表需要操作。客户是一个开发区的财政局和企业。因此,这些报表都是财务报表。(做过报表的朋友们肯定知道报表中最复杂的就是财务报表,且不说是典型的中国式报表,更是分组报表、交叉报表、三维汇总报表等等。)如果采用常用的报表工具,比如:水晶报表、如意报表等等比较流行的WEB报表,几十份报表如果还搞不晕你的话,绝对会将宝贵的项目时间浪费在报表界面的绘制上面。
我们的客户群体有个特点,计算机操作水平一般般,但是一般对Excel的操作比较熟练,对报表的输出形式或者表现形式要求不大,但是报表的格式一定要符合他们日常的工作方式。因为他们的工作如果说有一个工具软件必不可少的话,那就是MS Office(非国产的那些)。他们对Excel中的各种排版、样式几乎是了如指掌。如果你拿水晶报表或者如意报表之类的辛辛苦苦绘制的报表到他们跟前,他们肯定会拿你的辛苦杰作和他的电脑中的Excel文件甚至是Word文件进行对比,很有可能就会将你的报表指的头破血淋,因为他们的参考标准就是他们自己的Office文档。我相信,现在还没有一种报表工具做出来的报表可以和Excel或Word的灵活性进行比较(也许有些做报表的公司不服气了,别争了,呵呵!),因为Word或者Excel根本就不是报表工具而是办公软件!
经过对以上情况的分析,我决定在项目中采取Excel报表(严格的说Excel并不是报表工具,正如上所述)。同时采用样表的方式。这也是我在本文中最想提到的一点。为什么要采用样表呢?我们对Excel的掌握程度没有我们的客户研究的那么仔细,他们是专家(奉承的话,别告密哦,^_^)。他们也许对报表的表现要求五彩斑斓,但是没有关系,交给我一份样表吧!(给我一份样表,我就可以撑起整个需求,仿自伽利略,^_^)告诉我那个单元格填写什么,记住别再变的太多(要不我也没有办法,如果内容也经常变化的,不使用这种方法阿,事前申明!),其他的格式,线条,字体,颜色,合并,你愿意怎么变就怎么变!表单的内容照常进行数据库操作,添加、修改和删除均填写正常的表单,当然表单的格式没有必要和Excel的表单完全一样(要是一样也够累的),存放到你制定的数据库表中(可以修改,如果没有这个必要可以跳过这里以及后面会涉及到的地方)。在需要生成报表的地方,从数据库中提取相应的内容,打开样表,一个萝卜一个坑,填空吧,哈哈!竟是如此地简单。居然可以做出要多么漂亮有多么漂亮的表格,呵呵!
最后的报表要显示给用户看或者是要预览怎么办,直接让他去下载Excel文件吧,哈哈!(这样,用户看到的是他自己设计或者想要的Excel文件!八路的!好的!)
光说不做那行!以下是项目的实际例子!
1.既然是Excel报表,要引用Excel组件,位置如下:E:\Program Files\Microsoft Office\Office\EXCEL9.OLB
2.ReportExcelData.cs文件是专么用来处理Excel报表的自定义类,30多个方法,4000多行代码。
using System;
using System.Data;
using System.Data.SqlClient;
using System.IO;
namespace wjk.ZHKCGA
{
/// <summary>
/// 专门处理Excel报表文件的类
/// by wjk
/// </summary>
public class ReportExcelData
{
private DBHelper myDBHelper=new DBHelper();
private Function myFunc=new Function();
private ApproveData myApprove=new ApproveData();
DBHelper.strctSqlParam[] myParam1 = new DBHelper.strctSqlParam[1];
string strSQL;
int ret,iCount;
Excel.ApplicationClass myApp;
Excel.Workbook myBook;
Excel.Worksheet mySheet;
public ReportExcelData()
{
//
// TODO: 在此处添加构造函数逻辑
//
}
/// <summary>
/// 财政基建资金拨款申请表
/// </summary>
/// <param name="id">材料编号</param>
/// <param name="strPath">临时文件路径</param>
/// <param name="strUser">创建临时文件的用户</param>
/// <returns>-1样表不存在;1成功;0失败</returns>
public int WriteExcel_JJ_BoKuan(int id,string strPath,string strUniteCode,string strReportName,out string strErr)
{
strErr="";
#region 样表文件
string strSampleName="[样表]基建资金拨款申请表";
string strSampleFile=strPath+"File\\样表\\"+strSampleName+".xls";
string strReportFile=strPath+"File\\报表\\"+strUniteCode+"\\"+strReportName+".xls";
string strReportFileDB="\\File\\报表\\"+strUniteCode+"\\"+strReportName+".xls";
if(!File.Exists(strSampleFile))
{
strErr="样表不存在";
return -1;
}
Directory.CreateDirectory(strPath+"File\\报表\\"+strUniteCode);
if(File.Exists(strReportFile))
File.Delete(strReportFile);
File.Copy(strSampleFile,strReportFile);
#endregion
#region 写文件
myApp=null;
myBook=null;
mySheet=null;
object oMissiong=System.Reflection.Missing.Value;
myApp=new Excel.ApplicationClass();
myApp.Visible=false;
myApp.Workbooks.Open(strReportFile,oMissiong,oMissiong,oMissiong,oMissiong,oMissiong,oMissiong,oMissiong,oMissiong,oMissiong,oMissiong,oMissiong,oMissiong,oMissiong,oMissiong);
myBook=myApp.Workbooks[1];
mySheet=(Excel.Worksheet)myBook.ActiveSheet;
#region 一个萝卜一个坑
DataSet mySet;
strSQL="select 项目名称,项目计划总投资,年度计划投资,合同造价,财政已拨资金,项目单位财务支出,"+
"项目单位资金结余,计划投资财政未拨数,项目单位本次申请拨款,财政部门拟定拨款,财政部门核定拨款,"+
"备注,请款单位说明,请款单位签名,请款单位日期,请款单位负责人,财务负责人,项目负责人,联系电话 "+
"from 基建_基建资金拨款申请表 where 材料编号="+id+" order by 序号";
ret=myDBHelper.RunSQLReturnDataSet(strSQL,out mySet,out iCount,out strErr);
if(ret==0) return 0;
//if(iCount<1) return -1;
int iRow=7; //初始行号
for(int i=0;i<iCount;i++)
{
#region 写单元格
mySheet.Cells[iRow+i,1]=mySet.Tables[0].Rows[i][0].ToString();
mySheet.Cells[iRow+i,2]=mySet.Tables[0].Rows[i][1].ToString();
mySheet.Cells[iRow+i,3]=mySet.Tables[0].Rows[i][2].ToString();
mySheet.Cells[iRow+i,4]=mySet.Tables[0].Rows[i][3].ToString();
mySheet.Cells[iRow+i,5]=mySet.Tables[0].Rows[i][4].ToString();
mySheet.Cells[iRow+i,6]=mySet.Tables[0].Rows[i][5].ToString();
mySheet.Cells[iRow+i,7]=mySet.Tables[0].Rows[i][6].ToString();
mySheet.Cells[iRow+i,8]=mySet.Tables[0].Rows[i][7].ToString();
mySheet.Cells[iRow+i,9]=mySet.Tables[0].Rows[i][8].ToString();
mySheet.Cells[iRow+i,10]=mySet.Tables[0].Rows[i][9].ToString();
mySheet.Cells[iRow+i,11]=mySet.Tables[0].Rows[i][10].ToString();
mySheet.Cells[iRow+i,12]=mySet.Tables[0].Rows[i][11].ToString();
#endregion
}
if(iCount>0)
{
mySheet.Cells[16,9]=mySet.Tables[0].Rows[0][12].ToString();
mySheet.Cells[17,10]=mySet.Tables[0].Rows[0][13].ToString();
mySheet.Cells[17,12]=mySet.Tables[0].Rows[0][14].ToString();
mySheet.Cells[19,2]=mySet.Tables[0].Rows[0][15].ToString();
mySheet.Cells[19,5]=mySet.Tables[0].Rows[0][16].ToString();
mySheet.Cells[19,8]=mySet.Tables[0].Rows[0][17].ToString();
mySheet.Cells[19,11]=mySet.Tables[0].Rows[0][18].ToString();
}
mySet.Dispose();
#region 合计内容
strSQL="select sum(项目计划总投资),sum(年度计划投资),sum(合同造价),"+
"sum(财政已拨资金),sum(项目单位财务支出),sum(项目单位资金结余),"+
"sum(计划投资财政未拨数),sum(项目单位本次申请拨款),sum(财政部门拟定拨款),"+
"sum(财政部门核定拨款) "+
" from 基建_基建资金拨款申请表 where 材料编号="+id;
ret=myDBHelper.RunSQLReturnDataSet(strSQL,out mySet,out iCount,out strErr);
if(ret==0) return 0;
iRow=13; //初始行号
for(int i=0;i<iCount;i++)
{
#region 具体参数
mySheet.Cells[iRow+i,2]=mySet.Tables[0].Rows[i][0].ToString();
mySheet.Cells[iRow+i,3]=mySet.Tables[0].Rows[i][1].ToString();
mySheet.Cells[iRow+i,4]=mySet.Tables[0].Rows[i][2].ToString();
mySheet.Cells[iRow+i,5]=mySet.Tables[0].Rows[i][3].ToString();
mySheet.Cells[iRow+i,6]=mySet.Tables[0].Rows[i][4].ToString();
mySheet.Cells[iRow+i,7]=mySet.Tables[0].Rows[i][5].ToString();
mySheet.Cells[iRow+i,8]=mySet.Tables[0].Rows[i][6].ToString();
mySheet.Cells[iRow+i,9]=mySet.Tables[0].Rows[i][7].ToString();
mySheet.Cells[iRow+i,10]=mySet.Tables[0].Rows[i][8].ToString();
mySheet.Cells[iRow+i,11]=mySet.Tables[0].Rows[i][9].ToString();
#endregion
}
#endregion
#endregion
myBook.Save();;
myBook.Close( true,strReportFile,true);
//myApp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(mySheet);
System.Runtime.InteropServices.Marshal.ReleaseComObject(myBook);
System.Runtime.InteropServices.Marshal.ReleaseComObject(myApp);
GC.Collect();
#endregion
#region 写数据库
string strDate=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
strSQL="update 附件表 set 路径='"+strReportFileDB+"',类型='.xls',日期='"+strDate+"',操作者='"+strUniteCode+"' where 序号="+id;
return myDBHelper.RunSQL(strSQL,out strErr);
#endregion
}
/*以下省去30个类似的操作例子*/
}
3.Excel2000和Excel2002对Excel的引用对象是不一样的,一个是EXCEL9.OLB;另一个是EXCEL10.OLB;分别到相应的目录下去引用即可!同时,Excel2000和Excel2002下面的参数是不一样的。Excel2000下面的参数少一个,将oMissiong干掉一个就OK(这么简单阿!)。
4.操作Excel的时候,可能会发生Excel进程被锁定,无法退出,解决方法是在保存完并关闭myBook(工作簿)后,别关闭Excel进程(//myApp.Quit();)。这样的结果是服务器上始终有一个Excel的进程。但也无所谓,比起开始n个Excel进程好多了!(试过好多网友提供的方法,几乎没有好用的,呵呵)
5.可能会出现asp_net用户操作Excel的权限不够,配置Dcom。运行Dcomcnfg.exe,找到Excel应用程序,配置其属性,身份验证级别选“无”,身份标识选“交互式用户”,安全性页面,启动和访问均给everyone。注意:查看当前进程中是否有Winword进程存在,如果有且不能被结束,那么重启动计算机。再次运行你的代码即OK。这样以后就不会出现权限不够的情况了。
文档到此暂告结束。本人认为可以此法可以解决头疼的web报表,欢迎互相探讨