公元19XX年前,關於EXCEL的操作就如滔滔江水,連綿不絕,真正操作EXCEL我也是從去年下半年開始的,有些比較復雜的年度報表之類的,做起來也有點費力,不過還是都能畫出來了,關於EXCEL的報表導出,考慮到導出耗時的問題我主要采用AJAX來做的,分別捕捉幾個起止狀態,給客戶端提示3個狀態:正在檢索數據。。。---》准備導出數據。。。(只是從數據庫成功取出,還沒有讀寫excel文件)--》正在讀寫文件--》導出數據成功,當然如果哪一過程出錯,都有對應的提示,只所以想到寫這篇文章,主要是因為今年有個系統的部分EXCEL的操作也讓我做,順便結合之前操作EXCEL的經驗作一下總結,可能也算不上什麼,對於絕大多數來說也沒什麼技術含量,網上一搜一大把,但我想還是有必要總結一下,至少能給園子裡的新手些許幫助,OK,Let's Go...
一. 程序操作EXCEL的應用主要還是在統計報表方面,您可能會考慮讀EXCEL模板,也可能會考慮沒必要讀模板,其實讀不讀模板都能達到一樣的效果,看實際情況而用了。
1. 讀模板的話,首先模板存放在某個路徑下,根據模板把從數據庫裡取出的數據寫回EXCEL然後生成一個新的EXCEL存放都另一個路徑以供下載,模板不變。
我這裡的EXCEL操作主要是在VS2005裡的,VS2003也可以的,不過沒怎麼研究03裡的操作(文章最後我會把05的示例下載地址貼上 那個demo裡之前打包忘了放了一個生成數據的文件,剛放進去了,不加也是可以運行的,還有模板文件的數據稍微過濾了下重新放了部分對照看下)vs05中操作EXCEL直接引用.NET自帶的COM組件
,添加後項目的bin目錄下會自動出現
Interop.Excel.dll這個DLL(需安裝office2003 excel,下面的說明及示例都是基於office2003的,版本不同調用可能會不一樣)
頁面的命名空間引用 using Excel;
下面是調用模板的一段代碼
復制代碼 代碼如下:
#region 使用模板導出Excel表
case "ReportByTemp":
{
DataView dv = Cache["ReportByTemp"] as DataView;
//建立一個Excel.Application的新進程
Excel.Application app = new Excel.Application();
if (app == null)
{
return;
}
app.Visible = false;
app.UserControl = true;
Workbooks workbooks = app.Workbooks;
_Workbook workbook = workbooks.Add(template_path + "\\EXCEL測試模板.xls");//這裡的Add方法裡的參數就是模板的路徑
Sheets sheets = workbook.Worksheets;
_Worksheet worksheet = (_Worksheet)sheets.get_Item(1);//模板只有一個sheet表
if (worksheet == null)
{
return;
}
int rowNum = 0;
for (int i = 0; i < dv.Count; i++)
{
rowNum = i + 1;
worksheet.Cells[3 + i, 1] = rowNum;
worksheet.Cells[3 + i, 2] = dv[i].Row[0].ToString();
worksheet.Cells[3 + i, 3] = dv[i].Row[1].ToString();
excelOperate.SetBold(worksheet, worksheet.Cells[3 + i, 1], worksheet.Cells[3 + i, 1]); //黑體
excelOperate.SetHAlignCenter(worksheet, worksheet.Cells[3 + i, 1], worksheet.Cells[3 + i, 3]);//居中
worksheet.get_Range(worksheet.Cells[3 + i, 1], worksheet.Cells[3 + i, 3]).Borders.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Black);
}
tick = DateTime.Now.Ticks.ToString();
save_path = temp_path + "\\" + tick + ".xls";
workbook.SaveAs(save_path, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Excel.XlSaveAsAccessMode.xlNoChange, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value);
excelOperate.Dispose(worksheet, workbook, app);//關閉Excel進程
}
break;
#endregion
效果如下:
2. 不讀模板的話,調用的時候其實會繼承一個空白模板,然後寫入數據,程序畫表頭,最終達到一樣的效果,程序如下:
復制代碼 代碼如下:
#region 不使用模板生成Excel表
case "ReportByNone":
{
DataView dv = Cache["ReportByNone"] as DataView;
//建立一個Excel.Application的新進程
Excel.Application app = new Excel.Application();
if (app == null)
{
return;
}
app.Visible = false;
app.UserControl = true;
Workbooks workbooks = app.Workbooks;
_Workbook workbook = workbooks.Add(XlWBATemplate.xlWBATWorksheet);//這裡的Add方法裡的參數就相當於繼承了一個空模板(暫這樣理解吧)
Sheets sheets = workbook.Worksheets;
_Worksheet worksheet = (_Worksheet)sheets.get_Item(1);
if (worksheet == null)
{
return;
}
worksheet.get_Range(worksheet.Cells[1, 1], worksheet.Cells[1, 3]).Merge(Missing.Value); //橫向合並
worksheet.get_Range(worksheet.Cells[1, 1], worksheet.Cells[1, 1]).Value2 = "導出EXCEL測試一";
excelOperate.SetBold(worksheet, worksheet.Cells[1, 1], worksheet.Cells[1, 1]); //黑體
excelOperate.SetHAlignCenter(worksheet, worksheet.Cells[1, 1], worksheet.Cells[1, 1]);//居中
excelOperate.SetBgColor(worksheet, worksheet.Cells[1, 1], worksheet.Cells[1, 1], System.Drawing.Color.Red);//背景色
excelOperate.SetFontSize(worksheet, worksheet.Cells[1, 1], worksheet.Cells[1, 1], 16);//字體大小
excelOperate.SetRowHeight(worksheet, worksheet.Cells[1, 1], worksheet.Cells[1, 1], 32.25);//行高
worksheet.get_Range(worksheet.Cells[1, 1], worksheet.Cells[1, 1]).Borders.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Black);//黑色連續邊框
worksheet.Cells[2, 1] = "序號";
worksheet.Cells[2, 2] = "公司";
worksheet.Cells[2, 3] = "部門";
excelOperate.SetBold(worksheet, worksheet.Cells[2, 1], worksheet.Cells[2, 3]); //黑體
worksheet.get_Range(worksheet.Cells[2, 1], worksheet.Cells[2, 3]).Borders.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Black);
excelOperate.SetHAlignRight(worksheet, worksheet.Cells[2, 1], worksheet.Cells[2, 3]);
excelOperate.SetBgColor(worksheet, worksheet.Cells[2, 1], worksheet.Cells[2, 3], System.Drawing.Color.Silver);//背景色
int rowNum = 0;
for (int i = 0; i < dv.Count; i++)
{
rowNum = i + 1;
worksheet.Cells[3 + i, 1] = rowNum;
worksheet.Cells[3 + i, 2] = dv[i].Row[0].ToString();
worksheet.Cells[3 + i, 3] = dv[i].Row[1].ToString();
excelOperate.SetBold(worksheet, worksheet.Cells[3 + i, 1], worksheet.Cells[3 + i, 1]); //黑體
excelOperate.SetHAlignCenter(worksheet, worksheet.Cells[3 + i, 1], worksheet.Cells[3 + i, 3]);//居中
worksheet.get_Range(worksheet.Cells[3 + i, 1], worksheet.Cells[3 + i, 3]).Borders.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Black);//設置邊框顏色,不然打印預覽,會非常不雅觀
}
excelOperate.SetColumnWidth(worksheet, "A", 10);
excelOperate.SetColumnWidth(worksheet, "B", 20);
excelOperate.SetColumnWidth(worksheet, "C", 20);
worksheet.Name = "導出EXCEL測試一";
tick = DateTime.Now.Ticks.ToString();
save_path = temp_path + "\\"+ tick + ".xls";
workbook.SaveAs(save_path, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Excel.XlSaveAsAccessMode.xlNoChange, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value);
excelOperate.Dispose(worksheet, workbook, app);//關閉Excel進程
}
break;
#endregion
效果如下:
以上我給了兩個最簡單的操作說明,下面詳細說一下對於一些稍微復雜的報表的生成處理
二. 對於復雜的EXCEL報表的生成處理,無非是縱向合並相同的數據行及嵌套縱向合並等一些操作,下面就幾個具有針對性的報表作下說明.
1.要生成相對復雜的EXCEL表,在從數據庫取數據時,要注意先按照合理的要求排好序,有時候可能order by後面要跟好幾個字段,而且這幾個字段誰先誰後也要注意,因為這些會直接影響報表呈現的效果,比如你的EXCEL表要按月份統計國內外的項目,顯示出來的時候要多個項目相同的人連續,那麼排序就可能要這樣order by 月份,項目類別,用戶ID,項目ID(這是寫好的視圖,基於視圖來檢索的),這個排序的字段順序就不能變了,變了的話就不太好生成想要的形式了,如下圖:
這個也是動態畫的,用了個簡單的模板,模板就一個表頭,沒多大意義,除非表頭很復雜而且在列表中不需要重畫,考慮模板就比較好,向上面那個一月份國際的和其它月份的都是需要重畫表頭的。至於合並,如果不是嵌套的合並,我們可以在向模板循環寫數據的時候直接控制,比如下面一個簡單的寫法:
復制代碼 代碼如下:
for (i = 0; i < table.Rows.Count; i++)
{
bidName = table.Rows[index]["BIDNAME"].ToString();
if (table.Rows[i]["BIDNAME"].ToString() == bidName)
{
projNum++;
worksheet.Cells[5 + i, 2] = table.Rows[i]["PROJNO"];
worksheet.Cells[5 + i, 3] = table.Rows[i]["PROJNAME"];
worksheet.Cells[5 + i, 4] = table.Rows[i]["STAT_DATE"];
worksheet.Cells[5 + i, 5] = table.Rows[i]["PROJTYPE"];
worksheet.Cells[5 + i, 6] = table.Rows[i]["CONTENT"];
worksheet.Cells[5 + i, 7] = table.Rows[i]["OPENDT"];
worksheet.Cells[5 + i, 8] = table.Rows[i]["OPENADDRESS"];
worksheet.Cells[5 + i, 9] = table.Rows[i]["REV_DATE"];
worksheet.Cells[5 + i, 10] = table.Rows[i]["BID_UNIT"];
worksheet.Cells[5 + i, 11] = table.Rows[i]["AGT_AMOUNT"];
worksheet.Cells[5 + i, 12] = table.Rows[i]["CURRENCY"] + ":" + table.Rows[i]["BIDSER_AMOUNT"];
worksheet.Cells[5 + i, 13] = table.Rows[i]["SENDDATE"];
worksheet.Cells[5 + i, 14] = table.Rows[i]["CURRENCY"] + ":" + table.Rows[i]["BIDPRICE"];
worksheet.Cells[5 + i, 15] = table.Rows[i]["BOOKAMOUNT"];
worksheet.Cells[5 + i, 16] = table.Rows[i]["CURRENCY"] + ":" + table.Rows[i]["BAIL_AMOUNT"];
worksheet.Cells[5 + i, 17] = table.Rows[i]["USERNAME"];
worksheet.Cells[5 + i, 18] = table.Rows[i]["SECOND_USER"];
worksheet.Cells[5 + i, 19] = "";
worksheet.get_Range(worksheet.Cells[5 + i, 1], worksheet.Cells[5 + i, 19]).Borders.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Black);
continue;
}
worksheet.get_Range(worksheet.Cells[5 + rowid, 1], worksheet.Cells[5 + i - 1, 1]).Merge(Missing.Value); //將第一列按投標單位合並
worksheet.get_Range(worksheet.Cells[5 + rowid, 1], worksheet.Cells[5 + rowid, 1]).Value2 = bidName + "(" + projNum.ToString() + "個項目)";//合並後的單元格內容
合並單元格的時候也要注意一個問題,就是合並的單元格必須是為空的,不然在執行合並時,會提示“合並後的單元格的值將丟失”,具體不這樣提示的,大致是這個意思,一般我們合並都單元格相同的內容,在合並前我們先保存那個值,再清空後合並,上面的代碼中把worksheet.Cell[5+rowid,1]這裡系列的單元格的值空出來了,沒寫數據,而且最後合並了再寫值,避免了去循環清空。
2.嵌套的合並向上面那樣做可能控制比較麻煩,而且思路可能很混亂,我們可以考慮先循環填充所有的數據,在循環出來要合並的列,比如像下面的這張表
先循環填充數據,如下:
復制代碼 代碼如下:
int index = 0, rownum = 0;
string ProjNo = "";
for (i = 0; i < table.Rows.Count; i++)
{
ProjNo = table.Rows[index]["PROJNO"].ToString();
if (table.Rows[i]["PROJNO"].ToString() == ProjNo)
{
wksheet.Cells[3 + i, 1] = rownum + 1;
wksheet.Cells[3 + i, 2] = "'" + table.Rows[i]["PROJNO"]; //加上單引號保證以0開頭的字符原樣輸出
wksheet.Cells[3 + i, 3] = "'" + table.Rows[i]["PROJNAME"];
wksheet.Cells[3 + i, 4] = "'" + table.Rows[i]["PA_NAME"];
wksheet.Cells[3 + i, 5] = "'" + table.Rows[i]["BIDER_NAME"];
wksheet.Cells[3 + i, 6] = table.Rows[i]["BAIL_AMOUNT"];
wksheet.Cells[3 + i, 7] = table.Rows[i]["NOT_BACK"];
wksheet.get_Range(wksheet.Cells[3 + i, 1], wksheet.Cells[3 + i, 7]).Borders.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Black);
continue;
}
index = i;
rownum++;
i--;
}
下面合並前三列相同內容的單元:
復制代碼 代碼如下:
//合並前三列操作
int m = 1, rowid = 3, k;
string projName = "";
for (k = 3; k <= i + 2; k++)
{
if (Convert.ToInt32(wksheet.get_Range(wksheet.Cells[k, 1], wksheet.Cells[k, 1]).Value2) == m)
{
ProjNo = wksheet.get_Range(wksheet.Cells[k, 2], wksheet.Cells[k, 2]).Value2.ToString();
projName = wksheet.get_Range(wksheet.Cells[k, 3], wksheet.Cells[k, 3]).Value2.ToString();
wksheet.get_Range(wksheet.Cells[k, 1], wksheet.Cells[k, 1]).Value2 = "";
wksheet.get_Range(wksheet.Cells[k, 2], wksheet.Cells[k, 2]).Value2 = "";
wksheet.get_Range(wksheet.Cells[k, 3], wksheet.Cells[k, 3]).Value2 = "";
continue;
}
wksheet.get_Range(wksheet.Cells[rowid, 1], wksheet.Cells[k - 1, 1]).Merge(Missing.Value);
wksheet.get_Range(wksheet.Cells[rowid, 1], wksheet.Cells[rowid, 1]).Value2 = m;
wksheet.get_Range(wksheet.Cells[rowid, 2], wksheet.Cells[k - 1, 2]).Merge(Missing.Value);
wksheet.get_Range(wksheet.Cells[rowid, 2], wksheet.Cells[rowid, 2]).Value2 = "'" + ProjNo;
wksheet.get_Range(wksheet.Cells[rowid, 3], wksheet.Cells[k - 1, 3]).Merge(Missing.Value);
wksheet.get_Range(wksheet.Cells[rowid, 3], wksheet.Cells[rowid, 3]).Value2 = "'" + projName;
m++;
rowid = k;
k--;
}
//跳出循環後合並最後一個招標項目
wksheet.get_Range(wksheet.Cells[rowid, 1], wksheet.Cells[k - 1, 1]).Merge(Missing.Value);
wksheet.get_Range(wksheet.Cells[rowid, 1], wksheet.Cells[rowid, 1]).Value2 = m;
wksheet.get_Range(wksheet.Cells[rowid, 2], wksheet.Cells[k - 1, 2]).Merge(Missing.Value);
wksheet.get_Range(wksheet.Cells[rowid, 2], wksheet.Cells[rowid, 2]).Value2 = "'" + ProjNo;
wksheet.get_Range(wksheet.Cells[rowid, 3], wksheet.Cells[k - 1, 3]).Merge(Missing.Value);
wksheet.get_Range(wksheet.Cells[rowid, 3], wksheet.Cells[rowid, 3]).Value2 = "'" + projName;
下面合並標段列
復制代碼 代碼如下:
//合並標段列
index = 0; rowid = 3; //重置變量
string pa_name = string.Empty; //標段名稱
for (k = 3; k <= i + 2; k++)
{
pa_name = table.Rows[index]["PA_NAME"].ToString();
if (wksheet.get_Range(wksheet.Cells[k, 4], wksheet.Cells[k, 4]).Value2.ToString() == pa_name)
{
wksheet.get_Range(wksheet.Cells[k, 4], wksheet.Cells[k, 4]).Value2 = "";
continue;
}
wksheet.get_Range(wksheet.Cells[rowid, 4], wksheet.Cells[k - 1, 4]).Merge(Missing.Value);
wksheet.get_Range(wksheet.Cells[rowid, 4], wksheet.Cells[rowid, 4]).Value2 = "'" + pa_name;
index = k - 3;
rowid = k;
k--;
}
//退出循環時合並最後一個項目的標段
wksheet.get_Range(wksheet.Cells[rowid, 4], wksheet.Cells[k - 1, 4]).Merge(Missing.Value);
wksheet.get_Range(wksheet.Cells[rowid, 4], wksheet.Cells[rowid, 4]).Value2 = "'" + pa_name;
tick = DateTime.Now.ToString("yyyyMMddhhmmss");
save_path = temp_path + "\\" + tick + "保證金收退情況表.xls";
Session["BailBackID"] = tick + "保證金收退情況表.xls";
Session["_BailBack"] = "true";
workbook.SaveAs(save_path, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Excel.XlSaveAsAccessMode.xlNoChange, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value);
excelOperate.Dispose(worksheet, workbook, app);//關閉Excel進程
//DownLoad(save_path);
//Page_Close();
當然,上面的操作中會進行好幾次循環,在性能方面不太可取,園子裡的兄弟也許會有更好的方法,小弟不吝賜教了
下面我們看下幾個效果圖:
(注意:這裡提示的導出數據是指從數據庫成功取出數據,還沒有操作EXCEL對象,剛開始已經說過了,當然這個提示文字換成其它的也可以)
整個過程采用AJAX提示的,一來不刷新,二來導出時間比較長的話,可以給客戶一個良好的體驗效果,否可,用戶一點導出按鈕,半天沒反應也沒提示,客戶就覺得怎麼這麼慢的,是不是你們程序有問題,指責一大堆,有了這麼些交互提示信息,讓客戶多等幾分鐘也能承受。
3.生成的表格包含多個sheet的操作,比如下面一種情況
繪制這張表的要求是根據選擇某年的幾月到幾月,生成這個幾個月的一個綜合情況的sheet,然後分別生成這幾個月的單獨的sheet表,生成上面表的模板,包含兩個sheet ,一個綜合月份的sheet和一個單獨月份的sheet,因為單獨月份的sheet表現形式都是一樣的,我們可以根據選擇的月份個數Copy幾個sheet就可以了
復制代碼 代碼如下:
Workbooks workbooks = app.Workbooks;
_Workbook workbook = workbooks.Add(template_path + "\\招標單位年度招標情況逐月統計表.xls");
Sheets sheets = workbook.Worksheets;
_Worksheet Yearsheet = (_Worksheet)sheets.get_Item(1);
_Worksheet worksheet = (_Worksheet)sheets.get_Item(2);
if (worksheet == null)
{
return;
}
for (int i = 1; i < monthCount; i++)
worksheet.Copy(Missing.Value, workbook.Worksheets[2]);//月統計工作薄
Yearsheet的操作就不說了,和前面幾個一樣操作,關鍵是月份的sheet的生成,其實就是循環操作get_Item(i),代碼如下
復制代碼 代碼如下:
//////////////////////////////////////每月詳細統計////////////////////////////////////
int item_id = 2;
rowNum = 0; book_Amount = 0; index = 0;
bid_Amount = ""; bidser_Amount = ""; agent_Amount = 0;//清空變量
_Worksheet ws = null;
for (int i = 0; i < tableMM.Rows.Count; i++)
{
rowNum++;
Month = tableMM.Rows[index]["DATE_MONTH"].ToString();
if (tableMM.Rows[i]["DATE_MONTH"].ToString() == Month)
{
ws = (_Worksheet)sheets.get_Item(item_id);
ws.Cells[3 + rowNum - 1, 1] = rowNum;
ws.Cells[3 + rowNum - 1, 2] = tableMM.Rows[i]["PROJNO"];
ws.Cells[3 + rowNum - 1, 3] = tableMM.Rows[i]["PROJNAME"];
ws.Cells[3 + rowNum - 1, 4] = tableMM.Rows[i]["BID_TYPE"];
ws.Cells[3 + rowNum - 1, 5] = tableMM.Rows[i]["BID_MODE"];
ws.Cells[3 + rowNum - 1, 6] = tableMM.Rows[i]["OPENDT"];
ws.Cells[3 + rowNum - 1, 7] = tableMM.Rows[i]["OPENADDRESS"];
ws.Cells[3 + rowNum - 1, 8] = tableMM.Rows[i]["BID_UNIT"];
ws.Cells[3 + rowNum - 1, 9] = tableMM.Rows[i]["NOTICE_NO"].ToString().Replace("神華國貿", "");
ws.Cells[3 + rowNum - 1, 10] = tableMM.Rows[i]["BOOKAMOUNT"];
ws.Cells[3 + rowNum - 1, 11] = tableMM.Rows[i]["BIDPRICE"] + "(萬" + tableMM.Rows[i]["CURRENCY"] + ")";
ws.Cells[3 + rowNum - 1, 12] = tableMM.Rows[i]["BIDSER_AMOUNT"] + "(萬" + tableMM.Rows[i]["CURRENCY"]+")";
ws.Cells[3 + rowNum - 1, 13] = tableMM.Rows[i]["AGT_AMOUNT"];
ws.Cells[3 + rowNum - 1, 14] = "";
ws.get_Range(ws.Cells[3 + rowNum - 1, 1], ws.Cells[3 + rowNum - 1, 14]).Borders.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Black);
continue;
}
ws.Cells[1, 1] = year + "年" + bidName + GetMonth(Month) + "月份招標項目情況一覽表";
//每月合計
sql = " SELECT CURRENCY, NVL(SUM(BIDPRICE),0) AS BIDPRICE,NVL(SUM(BOOKAMOUNT),0) AS BOOKAMOUNT,NVL(SUM(BIDSER_AMOUNT),0) AS BIDSER_AMOUNT,NVL(SUM(AGT_AMOUNT),0) AS AGT_AMOUNT FROM IBS_V_BID_MONTHLY_STAT" + SqlFilter +
" AND DATE_YEAR ='" + year + "' AND COMPANY_ID=" + biderID + " AND DATE_MONTH ='" + Month + "'" +
" GROUP BY CURRENCY";
System.Data.DataTable dt1 = OracleHelper.RetDataTable(sql);
for (int m = 0; m < dt1.Rows.Count; m++)
{
bid_Amount += dt1.Rows[m]["BIDPRICE"] + "(萬"+dt1.Rows[m]["CURRENCY"] + ")\r\t";
book_Amount += float.Parse(dt1.Rows[m]["BOOKAMOUNT"].ToString());
bidser_Amount += dt1.Rows[m]["BIDSER_AMOUNT"] + "(萬" + dt1.Rows[m]["CURRENCY"] + ")\r\t";
agent_Amount += float.Parse(dt1.Rows[m]["AGT_AMOUNT"].ToString());
}
ws.Cells[3 + rowNum - 1, 3] = "合 計";
ws.Cells[3 + rowNum - 1, 10] = book_Amount;
ws.Cells[3 + rowNum - 1, 11] = bid_Amount;
ws.Cells[3 + rowNum - 1, 12] = bidser_Amount;
ws.Cells[3 + rowNum - 1, 13] = agent_Amount;
ws.get_Range(ws.Cells[3 + rowNum - 1, 1], ws.Cells[3 + rowNum - 1, 14]).Borders.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Black);
ws.Name = GetMM(Month);
item_id++;
index = i; //匯總下一個月份的招標項目
i--;
rowNum = 0; book_Amount = 0;
bid_Amount = ""; bidser_Amount = ""; agent_Amount = 0;//清空變量
}
//跳出循環時進行最後一個月份的項目匯總
用的是oracle數據庫,所以上面那個sql語句。。。 呵呵
============================================================================================
上面大致說得就差不多了,因為是不斷循環的什麼的,可能對於大的數據量讀寫來說,比較好性能,如果大家有什麼更好的方法,可以指點下,為了彌補等待時間過長,所以才結合了AJAX來處理。
最後我把做的一個小demo的鏈接帖出來給大家,還有一些空模板和對應生成的數據表給大家對照看下,尤其相對復雜一些的表畫應該是能畫出來的,主要看大家采用什麼樣的方法,能少循環一次就盡量少循環,呵呵~~~
EXCEL模板讀寫說明
http://www.justlike.com.cn/upfiles/template_xls.rar
http://www.justlike.com.cn/upfiles/ExcelFiles.rar
http://www.justlike.com.cn/upfiles/ExcelReportDemo.rar
(說明:最後彈出下載文件的一個頁面一直想讓其自動關掉,但是不行,如果不關掉,再點導出,不會彈出下載框,實際的處理中我們可以在導出旁邊放個下載按鈕,就像上面的效果圖裡那樣,當然可以點導出的時候讓其在網頁中直接打開,點下載的時候再彈出下載框,但是直接打開的話,文件需要生成在虛擬目錄下,不太安全,呵呵~~,看實際情況處理了)
==========================================================================================
今天補充說明下,關於那個調用ajax回調的效果,有個地方用到了所謂的“ajax嵌套調用”,如下
復制代碼 代碼如下:
function ExcelReportCallback(resp)
{
if(resp.value == "OK")
{
$('tipMsg').innerHTML = "<img border=\"0\" src=\"images/s_progressbar.gif\"><font color=#FF0000 style=font-weight:bold>准備導出數據,請稍等</font>";
setTimeout("RedirectUrl()",1000);//延時體驗
}
else
if(resp.value == "NO")
{
$('tipMsg').innerHTML = "<font color=#FF0000 style=font-weight:bold>沒有找到符合該查詢條件的數據</font>";
$('btnExcel').disabled = false;
}
else
{
$('tipMsg').innerHTML = "<font color=#FF0000 style=font-weight:bold>警告:導出數據出錯</font>";
$('btnExcel').disabled = false;
}
}
復制代碼 代碼如下:
function RedirectUrl()
{
$('tipMsg').innerHTML = "<img border=\"0\" src=\"images/ajaxloading.gif\"><font color=#7fffd4 style=font-weight:bold>正在讀寫報表文件,請稍後</font>";
var ajax = new ajax_request("ExcelReport.aspx?flag=ReportByTemp&"+Math.random(), "", "", ReportCallback);
function ReportCallback(resp)
{
if(resp.value != "Error" && resp.value !="")
{
$('btnExcel').disabled = false;
$('tipMsg').innerHTML = "<font color=#FF0000 style=font-weight:bold>數據導出成功!</font>";
Open("XLS_DownLoad.aspx?path="+resp.value);//window.location.href = resp.value;//
}
else
{
$('btnExcel').disabled = false;
$('tipMsg').innerHTML = "<font color=#FF0000 style=font-weight:bold>文件讀寫出錯,請檢查文件模板是否存在或對文件是否有讀寫權限!</font>";
}
}
}
function Open(url)
{
window.open(url,'newwindow','height=1,width=1,top=1500,left=1500,toolbar=no,menubar=no,scrollbars=yes,location=no,status=no')
}
ExcelReportCallback(resp)原本是一個回調函數,但是裡面調用了一個RedirectUrl()方法,這個方法又包含了一個回調函數,這樣就形成了回調的嵌套,之所以這麼做,是因為,第一個回調是處理從數據庫取出數據成功與否,如果成功了跳轉到畫EXCEL的頁面,這樣的話會出現一個空白頁等生成好後出現下載框,後來覺得是否可以嵌套一個回調來繼續一次異步操作,這樣就不會出現長時間等待的空白頁面了,而是生成好EXCEL後返回地址,或者可以返回一個文件名到XLS_DownLoad.aspx頁面直接下載,但是XLS_DownLoad.aspx也是要出現的,我嘗試過讓下載後這個頁面自動關閉,無賴做不到,所以把Open()方法裡的數據值調得讓頁面不顯示,但是狀態欄還是有顯示的。
到這裡算是寫完了,決定奢侈下,放到首頁下:),總覺得首頁的文章只有高手才能放,而且放到首頁也是一種奢侈,希望對園子裡的某些人有一定的幫助吧~~
http://xiazai.jb51.net/201102/yuanma/ExcelReport.rar