PHPExcel 打印报表

    因工作需求,需要将前端页面显示的 table 报表打印成 Excel 文件,因为报表类型多样,好多的 td 标签存在跨行又跨列的情况,PHPExcel 作为一款 php 处理 Excel 的插件,虽然方便,但在处理这些报表起来也非常的复杂。

    如果我们能够在 js 中确定整个 table 的行数和列数,并确定每个 td 在 Excel 中的坐标以及跨行跨列后最终点的坐标,然后在后台 php 将接收到的数据打印至 Excel,这样不仅能够减缓服务端压力,在前端的调用中也将为我们带来便利。

    前台用到了 jQuery,获取 table 数据及 td 坐标的数据操作可以封装成一个插件并指定后台接口。jQ 代码如下:

(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"}]}

而在后台 php 接口中,核心代码如下

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;
	}

效果如下:

猜你喜欢

转载自my.oschina.net/u/3460260/blog/1568057