因工做需求,須要將前端頁面顯示的 table 報表打印成 Excel 文件,由於報表類型多樣,好多的 td 標籤存在跨行又跨列的狀況,PHPExcel 做爲一款 php 處理 Excel 的插件,雖然方便,但在處理這些報表起來也很是的複雜。javascript
若是咱們可以在 js 中肯定整個 table 的行數和列數,並肯定每一個 td 在 Excel 中的座標以及跨行跨列後最終點的座標,而後在後臺 php 將接收到的數據打印至 Excel,這樣不只可以減緩服務端壓力,在前端的調用中也將爲咱們帶來便利。php
前臺用到了 jQuery,獲取 table 數據及 td 座標的數據操做能夠封裝成一個插件並指定後臺接口。jQ 代碼以下:html
(function($){ $.fn.extend({ forExcel:function(){ var list = [];// 傳入後臺的數據 var span_all = []; // 記錄既跨行又跨列的坐標 var max_td = []; // 記錄最大列數 var height_tr = []; // 記錄行高 // 行 tr 循環 $(this).children().find("tr").each(function(i, dom){ // 獲取每一行的高度 height_tr.push($(dom).height()*0.75); // 像素轉換 // 記錄最大列數 max_td.push($(dom).children("td").length); // 列 td 循環 $(dom).children("td").each(function(isub, domsub){ var tdata = {}; // 獲取跨行,跨列 var col = $(domsub).attr("colspan"); col = col ? parseInt(col) : 0; var row = $(domsub).attr("rowspan"); row = row ? parseInt(row) : 0; // 圖片判斷 ------ PHPExcel只容許本地圖片地址 ----------------- if($(domsub).find("img").length != 0){ tdata.img = $(domsub).find("img").length; tdata.img_src = $(domsub).find("img").attr('src'); // 圖片地址需加前綴 tdata.img_width = $(domsub).find("img").width(); tdata.img_height = $(domsub).find("img").height(); }else{ tdata.img = "none"; } // 記錄跨行坐標 if(row>0){ span_all.push({row:i, col:isub, rowspan:row, colspan:col}); } // 獲取 td 在 Excel 中的位置 var data = getIdx(i, isub, span_all, this, row, col); tdata.idx = data.idx; tdata.span = data.span; tdata.text = $(domsub).text(); list.push(tdata); }); }); var tdata = {}; tdata.tdlength = Math.max.apply(null, max_td); tdata.height_tr = height_tr; var countr = $(this).children().find("tr").length; var trdom = $(this).children().find("td").last(); var rows = $(trdom).attr("rowspan"); rows = rows ? parseInt(rows) : 1; tdata.trlength = countr+rows-1; tdata.tdData = list; var tableData = JSON.stringify(tdata); // 後臺接口 $(this).after("<form id='forexcelform' action='後臺接口' method='post'></form>"); $("#forexcelform").html("<input type='hidden' name=tbdata value='"+ tableData +"' />"); $("#forexcelform").submit(); $("#forexcelform").remove(); } }); // 返回列名 function getColIdx(idx){ var col = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z', 'AA','AB','AC','AD','AE','AF','AG','AH','AI','AJ','AK','AL','AM','AN','AO','AP','AQ','AR','AS','AT','AU','AV','AW','AX','AY','AZ', 'BA','BB','BC','BD','BE','BF','BG','BH','BI','BJ','BK','BL','BM','BN','BO','BP','BQ','BR','BS','BT','BU','BV','BW','BX','BY','BZ', 'CA','CB','CC','CD','CE','CF','CG','CH','CI','CJ','CK','CL','CM','CN','CO','CP','CQ','CR','CS','CT','CU','CV','CW','CX','CY','CZ', 'DA','DB','DC','DD','DE','DF','DG','DH','DI','DJ','DK','DL','DM','DN','DO','DP','DQ','DR','DS','DT','DU','DV','DW','DX','DY','DZ']; return col[idx]; } // 返回坐標與跨行或跨列後的坐標 function getIdx(row, col, span_all, dom, span_row, span_col){ var pre_colspan = 0; // // 是否位於跨行跨列的行數中 $.each(span_all, function(i, val){ var rows = span_all[i].row + span_all[i].rowspan; if(row>span_all[i].row && row<rows && col>=span_all[i].col){ pre_colspan += parseInt(span_all[i].colspan==0?1:span_all[i].colspan); } }); var tr_colspan = 0; // 計算同行 td 前的 colspan $(dom).prevAll().each(function(i, domsub){ var colspan = $(domsub).attr("colspan"); colspan = colspan ? colspan : 1; tr_colspan += parseInt(colspan)-1; }); pre_colspan += tr_colspan; // 記錄開始坐標 var idx = getColIdx(col+pre_colspan) +""+ (row+1); // 記錄跨後終點坐標 var span = "none"; // 跨行或跨列後的坐標 if(span_row>0 && span_col==0){ span = getColIdx(col+pre_colspan) +""+ (span_row+row); }else if(span_row==0 && span_col>0){ span = getColIdx(span_col+col+pre_colspan-1) +""+ (1+row); }else if(span_row>0 && span_col>0){ span = getColIdx(span_col+col+pre_colspan-1) +""+ (span_row+row); } return {idx: idx, span: span}; } })(jQuery);
該插件獲取以下圖的請求參數爲前端
{"tdlength":3,"height_tr":[16.5,16.5,16.5],"width_td":15.76,"trlength":3,"tdData":[{"idx":"A1","span":"A2","text":"eq(跨2行)","img":"none"},{"idx":"B1","span":"none","text":" eq","img":"none"},{"idx":"C1","span":"none","text":"eq","img":"none"},{"idx":"B2","span":"none","text":"eq","img":"none"},{"idx":"C2","span":"none","text":"eq","img":"none"},{"idx":"A3","span":"B3","text":"eq(跨2列)","img":"none"},{"idx":"C3","span":"none","text":"eq","img":"none"}]}java
而在後臺 php 接口中,核心代碼以下json
public function x_xPrintExcel(){ vendor("PHPExcel.PHPExcel"); // thinkPHP 加載插件 $objPHPExcel = new PHPExcel(); // 設置文件的一些屬性,在xls文件——>屬性——>詳細信息裏能夠看到這些值,xml表格裏是沒有這些值的 $objPHPExcel ->getProperties() //得到文件屬性對象,給下文提供設置資源 ->setCreator( "") //設置文件的建立者 ->setLastModifiedBy( "") //設置最後修改者 ->setTitle( "Office 2007 XLSX Test Document" ) //設置標題 ->setSubject( "Office 2007 XLSX Test Document" ) //設置主題 ->setDescription( "Test document for Office 2007 XLSX, generated using PHP classes.") //設置備註 ->setKeywords( "office 2007 openxml php") //設置標記 ->setCategory( "Test result file"); //設置類別 $tbdata = $_POST['tbdata']; $a = str_replace('\\','',$tbdata); // 前臺傳入 json 字符串 $_tdata = json_decode($a); $tbdata = $_tdata->tdData; //dump($tbdata);die(); for($i=0; $i<count($tbdata); $i++){ $idx = $tbdata[$i]->idx; $text = $tbdata[$i]->text; $span = $tbdata[$i]->span; $img = $tbdata[$i]->img; $img_src = $tbdata[$i]->img_src; $img_width = $tbdata[$i]->img_width; $img_height = $tbdata[$i]->img_height; if("none" != $img){ /*實例化插入圖片類*/ $objDrawing = new PHPExcel_Worksheet_Drawing(); $objDrawing->setResizeProportional(false); $objDrawing->setPath($img_src); $objDrawing->setWidth($img_width); $objDrawing->setHeight($img_height); $objDrawing->setCoordinates($idx);//單元格 $objDrawing->setWorksheet($objPHPExcel->setActiveSheetIndex(0));//$sheet爲當前工做表 }else{ $objPHPExcel->setActiveSheetIndex(0) //設置第一個內置表(一個xls文件裏能夠有多個表)爲活動的 ->setCellValue( $idx, $text );//setWrapText } // 垂直居中 $objPHPExcel->getActiveSheet()->getStyle($idx)->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_CENTER); //自動換行 $objPHPExcel->getActiveSheet()->getStyle($idx)->getAlignment()->setWrapText(true); if("none" != $span){ $objPHPExcel->getActiveSheet()->mergeCells( $idx.':'.$span); } } // 確定行列 $tdlength = $_tdata->tdlength; $trlength = $_tdata->trlength; $cellName = array('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z', 'AA','AB','AC','AD','AE','AF','AG','AH','AI','AJ','AK','AL','AM','AN','AO','AP','AQ','AR','AS','AT','AU','AV','AW','AX','AY','AZ', 'BA','BB','BC','BD','BE','BF','BG','BH','BI','BJ','BK','BL','BM','BN','BO','BP','BQ','BR','BS','BT','BU','BV','BW','BX','BY','BZ', 'CA','CB','CC','CD','CE','CF','CG','CH','CI','CJ','CK','CL','CM','CN','CO','CP','CQ','CR','CS','CT','CU','CV','CW','CX','CY','CZ', 'DA','DB','DC','DD','DE','DF','DG','DH','DI','DJ','DK','DL','DM','DN','DO','DP','DQ','DR','DS','DT','DU','DV','DW','DX','DY','DZ'); $height_tr = $_tdata->height_tr; // 行高 // 設置邊框 for($i=0; $i<$trlength; $i++){ // 行高 $objPHPExcel->getActiveSheet()->getRowDimension(''.($i+1).'')->setRowHeight($height_tr[$i]); for($j=0; $j<$tdlength; $j++){ $idx = $cellName[$j].''.($i+1) ; $objPHPExcel->getActiveSheet()->getStyle($idx)->getBorders()->getTop()->setBorderStyle(PHPExcel_Style_Border::BORDER_THIN); $objPHPExcel->getActiveSheet()->getStyle($idx)->getBorders()->getBottom()->setBorderStyle(PHPExcel_Style_Border::BORDER_THIN); $objPHPExcel->getActiveSheet()->getStyle($idx)->getBorders()->getLeft()->setBorderStyle(PHPExcel_Style_Border::BORDER_THIN); $objPHPExcel->getActiveSheet()->getStyle($idx)->getBorders()->getRight()->setBorderStyle(PHPExcel_Style_Border::BORDER_THIN); } } // for($j=0; $j<$tdlength; $j++){ $objPHPExcel->getActiveSheet()->getColumnDimension($cellName[$j])->setWidth(15); } header('Content-Type: application/vnd.ms-excel'); header('Content-Disposition: attachment;filename="simple.xls"'); header('Cache-Control: max-age=0'); $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5'); $objWriter->save('php://output'); exit; }
效果以下:app