[TOC]
### 1、composer 安裝phpoffice/phpexcel
~~~
composer require phpoffice/phpexcel
~~~
### 2、通用建議放在common模塊
~~~
php think make:controller common/Export --plain
--plain 表示僅僅生成控制器
~~~
### 3、該控制器設置了三種通用導出設置
~~~
1、common_excel_export 單頁excel導出--不計數量-支持多個sheet工作區
2、caculate_excel_export 多頁excel導出--計數量-將相同數據按每個工作區的量導入多個工作區即設置每個工作的上限
3、recursion_create_excel 導出excel--支持生成表頭-插入數據-設置數據等功能
~~~
### 4、開始代碼--入口統一
~~~
/**
* 導出excel入口
*
* @param integer $type 選擇導出的類型
* @param array $data 需要處理的數據
* @return void
*/
public static function export($type, array $data = [])
{
switch ($type) {
case 1:
//單頁excel導出--不計數量--可以導出多個sheet工作區
$fun = 'common_excel_export';
break;
case 2:
//多頁excel導出--計數量--將相同數據按每個工作區的量導入多個工作區即設置每個工作的上限
$fun = 'caculate_excel_export';
break;
case 3:
//導出excel 生成表頭 插入數據,設置數據等功能
$fun = 'recursion_create_excel';
break;
default:
break;
}
self::$fun($data);
}
解釋說明:通過php可變函數特性處理,data數據在具體方法中解析說明
~~~
### 5、common_excel_export 方法實現
~~~
代碼說明在代碼中解析說明
/**
* 單頁導出--不計數量--可以導出多個sheet工作區
*
* @param array $data 需要的相關數據
* $data = [
* //表示sheet0對應需要導出的數據 數據是否存在根據key中的keys個數判斷
* 'sheet0'=>['data' => $dataInfos],
* //表示sheet1對應需要導出的數據
* 'sheet1'=>['data' => $dataInfos2],
* //excel中對應的key
* 'key' => 'common_export',
* //導出的文件名
* 'fileInfo' => [
* //文件名
* 'fileName' => '多頁導出計數測試',
* //工作表名替換key中設置的,不設置將使用key中設置的
* 'sheetName0' => '測試替換',
* 'sheetName1' => '配置不設置'
* ]
* ];
*
* @return void
*/
protected static function common_excel_export(array $data)
{
@ini_set('memory_limit', '2048M');
set_time_limit(0);
error_reporting(E_ALL);
//在data中必須指定excel【config/excel.php】配置文件中的key
//key的具體信息將在后面給出
$key = $data['key'];
//fileInfo數組中配置一些簡單的excel文件信息
$fileInfo = $data['fileInfo'];
//fileInfo中的fileName將會覆蓋配置文件中的fileName
$fileName = $fileInfo['fileName'];
//獲取excel配置文件中的數據,具體有哪些稍后說明
$excelInfo = Config::get('excel.' . $key);
//判斷文件名
$fileName = empty($fileName) ?
($excelInfo['fileName'] ?? uniqid().time()) :
$fileName;
unset($data['key']);
unset($data['fileInfo']);
ksort($data);
$objPHPExcel = \PHPExcel_IOFactory::createReader('Excel5')->load($excelInfo['tempExcel']);
$index = 0;
foreach ($excelInfo['keys'] as $v) {
$objPHPExcel->getSheet($v['index'])
->setTitle($fileInfo['sheetName'.$v['index']] ?? $v['sheetName']);
$dataInfos = $data['sheet' . $index]['data'];
$num = $v['num'];
$keys = $v['key'];
foreach ($dataInfos as $info) {
$column = count($keys);
$temp = 0;
for ($n = 0; $n < $column; $n++) {
if ($temp == $column) {
break;
} else {
$pcoordinate = \PHPExcel_Cell::stringFromColumnIndex($n) . '' . $num;
$keys[$temp] == 'index_id' ? (
$objPHPExcel->setActiveSheetIndex($v['index'])
->setCellValue($pcoordinate, ($num-1))
) : (
$objPHPExcel->setActiveSheetIndex($v['index'])
->setCellValue($pcoordinate, $info[$keys[$temp]] . "\t")
);
$temp++;
}
}
$num++;
}
$index++;
}
ob_end_clean();
$fileName = iconv("utf-8", "gb2312", $fileName);
header ( 'Content-Type: application/vnd.ms-excel' );
header ( 'Content-Disposition: attachment;filename="' . $fileName . '.xls"');
header ( 'Cache-Control: max-age=0' );
$objWriter = \PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5');
$objWriter->save('php://output');
exit;
}
~~~
config目錄下的excel配置文件說明
~~~
//key值唯一
'common_export' => [
//需要加載的模板文件
'tempExcel' => './mdfile/excels/common_export.xls',
//文件名
'fileName' => '測試通用',
//相關數據配置
'keys' => [
[
//與數據對應的字段,字段順序對應模板中的顯示字段,其中id代表序號(不需要數據庫中需要)
'key'=>[ 'id','name','idcard','gender','age','birthday','phone','address' ],
//表示從第幾行開始寫入【excel行的下標計算從1開始】
'num' => 2,
//對應excel中的工作區,且需要與調用方法中的sheet0,1,2對應
'index' => 0,
//當前工作區的名稱
'sheetName' => '測試不替換',
],
[
//與數據對應的字段,字段順序對應模板中的顯示字段,其中id代表序號(不需要數據庫中需要)
'key'=>[ 'id','name','idcard','gender','age','birthday','phone','address' ],
//表示從第幾行開始寫入【excel行的下標計算從1開始】
'num' => 2,
//對應excel中的工作區,且需要與調用方法中的sheet0,1,2對應
'index' => 1,
],
],
],
~~~
### 6、caculate_excel_export
~~~
/**
* 多頁導出--計數量--將相同數據按每個工作區的量導入多個工作區即設置每個工作的上限---只支持無表頭
*
* @param array $data 需要的相關數據
*$data = [
* //表示sheet0對應需要導出的數據 數據是否存在根據key中的keys個數判斷
* 'sheet0'=>['data' => $dataInfos],
* //表示sheet1對應需要導出的數據
* 'sheet1'=>['data' => $dataInfos2],
* //excel中對應的key
* 'key' => 'caculate_export',
* //導出的文件名
* 'fileInfo' => [
* //文件名
* 'fileName' => '多頁導出計數測試',
* //工作表名替換key中設置的,不設置將使用key中設置的
* 'sheetName0' => '測試替換',
* 'sheetName1' => '配置不設置'
* ]
* ];
*
* @return void
*/
protected static function caculate_excel_export(array $data)
{
@ini_set('memory_limit', '2048M');
set_time_limit(0);
error_reporting(E_ALL);
$key = $data['key'];
$fileInfo = $data['fileInfo'];
$fileName = $fileInfo['fileName'];
$excelInfo = Config::get('excel.' . $key);
$fileName = empty($fileName) ?
($excelInfo['fileName'] ?? uniqid().time()) :
$fileName;
unset($data['key']);
unset($data['fileInfo']);
ksort($data);
$objPHPExcel = \PHPExcel_IOFactory::createReader('Excel5')->load($excelInfo['tempExcel']);
$sheetindex = 0;
$temp_sheet_index = 0;
$sheetSize = 40000;
foreach ($excelInfo['keys'] as $v) {
$dataIndex = $v['index'];
$dataInfos = $data['sheet' . $dataIndex]['data'];
$num = $v['num'];
$keys = $v['key'];
$row_size = (isset($v['sheetSize']) && is_numeric($v['sheetSize'])) ?
intval($v['sheetSize']) :
$sheetSize;
$temp_s = 0;
$data_len = count($dataInfos);
$sheet_counts = ceil(bcdiv($data_len, $row_size, 6));
$sheet_temp = 0;
$objPHPExcel->getSheet($sheetindex)
->setTitle(($fileInfo['sheetName'.$dataIndex] ?? $v['sheetName']) . $sheet_temp );
foreach ($dataInfos as $info) {
$column = count($keys);
$temp = 0;
for ($n = 0; $n < $column; $n++) {
if ($temp == $column) {
break;
} else {
$pcoordinate = \PHPExcel_Cell::stringFromColumnIndex($n) . '' . $num;
$keys[$temp] == 'index_id' ? (
$objPHPExcel->setActiveSheetIndex($sheetindex)
->setCellValue($pcoordinate, $num)
) : (
$objPHPExcel->setActiveSheetIndex($sheetindex)
->setCellValue($pcoordinate, $info[$keys[$temp]] . "\t")
);
$temp++;
}
}
$num++;
$temp_s ++;
if ($temp_s % $row_size == 0 && $temp_s <= $data_len) {
$objPHPExcel->createSheet();
$sheetindex++;
$sheet_temp++;
if ($sheet_temp < $sheet_counts) {
$sheetName = $fileInfo['sheetName' . $dataIndex] ?? $v['sheetName'];
$objPHPExcel->getSheet($sheetindex)
->setTitle($sheetName . $sheet_temp);
}
$num = $v['num'];
}
}
}
ob_end_clean();
$fileName = iconv("utf-8", "gb2312", $fileName);
header ( 'Content-Type: application/vnd.ms-excel' );
header ( 'Content-Disposition: attachment;filename="' . $fileName . '.xls"');
header ( 'Cache-Control: max-age=0' );
$objWriter = \PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5');
$objWriter->save ('php://output');
exit;
}
~~~
~~~
'caculate_export' => [
//需要加載的模板文件
'tempExcel' => './mdfile/excels/common_export.xls',
//文件名
'fileName' => '測試分頁',
//相關數據配置
'keys' => [
[
//與數據對應的字段,字段順序對應模板中的顯示字段,其中id代表序號(不需要數據庫中需要)
'key'=>[ 'index_id','name','idcard','gender','age','birthday','phone','address' ],
//表示從第幾行開始寫入【excel行的下標計算從1開始】
'num' => 1,
//對應數據序列
'index' => 0,
//sheetSize設置每個工作區的大小
'sheetSize' => 2000,
//當前工作區的名稱
'sheetName' => '測試不替換',
],
[
//與數據對應的字段,字段順序對應模板中的顯示字段,其中id代表序號(不需要數據庫中需要)
'key'=>[ 'index_id','name','idcard','gender','age','birthday','phone','address' ],
//表示從第幾行開始寫入【excel行的下標計算從1開始】
'num' => 1,
//sheetSize設置每個工作區的大小
'sheetSize' => 2000,
//對應數據序列
'index' => 1,
],
],
],
~~~
### 7、recursion_create_excel
~~~
/**
* 生成表頭導出數據
*
* @param array $data
* $data = [
* 'head' => [
* [
* 'value' => '表頭名',
* 'col' => 2, //占據多少列
* 'row' => 2, //占據多少行
* 'width' => 20, //單元格寬度
* //單元格下拉格式 list=下拉選擇,range=范圍選擇
* 'type' => 'list',
* //下拉數據,以英文逗號分隔
* 'allowarray' => 'aa,bb',
* 'content' => '備注信息'
* ],
* [
* 'value' => '表頭名',
* 'col' => 2,
* 'row' => 1,
* 'width' => 20,
* 'content' => '備注信息',
* //下一行數據
* 'children' => [
* [
* 'value' => '表頭名',
* 'col' => 1,
* 'width' => 20,
* //單元格范圍設置
* 'type' => 'range',
* //范圍數據,以英文逗號分隔,僅支持最大最小值設置
* 'allowarray' => '10,100'
* ],
* [
* 'value' => '',
* 'width' => 20
* ],
* ],
* ],
* [],
* []
* ],
* //需要插入的數據
* 'data' => [
* [],[],[],[]
* ],
* //文件名
* 'fileName' => '',
* //從第幾行開始插入數據
* 'row' => 2
* ];
*
* @return void
*/
protected static function recursion_create_excel(array $data)
{
$PHPExecl = new \PHPExcel();
$objWriter = \PHPExcel_IOFactory::createWriter($PHPExecl, 'Excel2007');
$PHPExecl ->getProperties()
->setCreator("4399om")
->setTitle("Office 2007 XLSX Test Document")
->setSubject("Office 2007 XLSX Test Document")
->setDescription("Generate document for Office 2007 XLSX, generated using PHP classes.")
->setKeywords("office 2007 openxml php")
->setCategory("Test result file");
$PHPExecl ->setActiveSheetIndex(0);
$PHPExecl ->getActiveSheet()
->getDefaultRowDimension()
->setRowHeight(30);
$sheet = $PHPExecl->getActiveSheet();
self::generate_excel_header($sheet, $data['head'], 1, 0, 0);
self::summer_insert_data_to_excel(
$sheet,
$data['head'],
$data['data'],
$data['row']
);
self::out_input_header($objWriter, $data['fileName']);
}
/**
* 生成表頭
*
* @param object $sheet
* @param array $head 表頭數據
* @param integer $beginRow 起始行
* @param integer $col 起始列
* @param integer $startCol 開始列
* @return void
*/
private static function generate_excel_header(
$sheet,
$head,
$beginRow,
$col,
$startCol
)
{
foreach ($head as $key => $cells) {
$row = $beginRow;
$beginCol = \PHPExcel_Cell::stringFromColumnIndex($col) . $row;
$sheet ->getCell($beginCol)
->setValue($cells['value']);
//設置表格樣式
$sheet ->getStyle($beginCol)
->applyFromArray(Config::get('excel.excel_type_03'));
//設置單元格的寬度
if(isset($cells['width'])) {
$Cell = $sheet->getColumnDimension(\PHPExcel_Cell::stringFromColumnIndex($col));
$Cell->setWidth($cells['width']);
}
//元素打上標記
if (isset($cells['content'])) {
self::set_comment($sheet, $beginCol, $cells['content']);
}
//合并單元格
$merge = false;
if (isset($cells['col'])) {
$col += $cells['col'] - 1;
$merge = true;
}
if (isset($cells['row'])) {
$row += $cells['row'] - 1;
$merge = true;
}
if ($merge) {
$endCol = \PHPExcel_Cell::stringFromColumnIndex($col) . $row;
$sheet->mergeCells($beginCol . ':' . $endCol);
}
$row ++;
$col ++;
if (isset($cells['children']) && is_array($cells['children'])) {
$cols = $startCol;
if (! self::is_exist_chilren($cells['children'])) {
$cols = $col - 2;
$startCol = $col;
}
self::generate_excel_header(
$sheet,
$cells['children'],
$row,
$cols,
$startCol
);
} else {
$startCol = $col;
}
}
}
/**
* 判斷自己的孩子節點中是否存在孫子節點
*
* @param array $data
* @return bool
*/
private static function is_exist_chilren($data)
{
foreach ($data as $key => $value) {
if (isset($value['children']) && is_array($value['children'])) {
return true;
}
}
return false;
}
/**
* 生成Execl單元格備注
*
* @param object $sheet 當前的工作簿對象
* @param integer $cell 需要設置屬性的單元格
* @param string $content 備注內容
* @return void
*/
private static function set_comment($sheet, $cell, $content)
{
$sheet->getComment($cell)->setAuthor('4399om');
$objCommentRichText = $sheet->getComment($cell)
->getText()
->createTextRun('4399om:');
$objCommentRichText ->getFont()
->setBold(true);
$sheet ->getComment($cell)
->getText()
->createTextRun("\r\n");
$sheet ->getComment($cell)
->getText()
->createTextRun($content);
$sheet ->getComment($cell)
->setWidth('100pt');
$sheet ->getComment($cell)
->setHeight('100pt');
$sheet ->getComment($cell)
->setMarginLeft('150pt');
$sheet ->getComment($cell)
->getFillColor()
->setRGB('EEEEEE');
}
/**
* 將數據寫入到數據表中
*
* @param object $sheet
* @param array $head
* @param array $data 要插入進Execl數據
* @param integer $n 表示從第幾行起的插入數據
* @param array $ruleData 表示數據格式的規則數組
* @return void
*/
public static function summer_insert_data_to_excel(
$sheet,
$head,
$data,
$n = 3,
array $ruleData = []
)
{
$simpleHead = self::get_head($head);
$row = $n;
foreach ($data as $key => $valueArr) {
$m = 0;
foreach ($valueArr as $k=>$v) {
$startCol = \PHPExcel_Cell::stringFromColumnIndex($m) . $row;
$sheet ->getCell($startCol)
->setValue($v);
$sheet ->getStyle($startCol)
->getAlignment()
->applyFromArray([
'horizontal'=> \PHPExcel_Style_Alignment::HORIZONTAL_CENTER,
'vertical' => \PHPExcel_Style_Alignment::VERTICAL_CENTER,
'rotation' => 0,
'wrap' => true,
]);
if (isset($simpleHead[$k]['col'])) {
$m = $m + $simpleHead[$k]['col'] - 1;
$endCol = \PHPExcel_Cell::stringFromColumnIndex($m) . $row;
$sheet->mergeCells($startCol . ':' . $endCol);
}
$m++;
$type = false;
if (isset($simpleHead[$k]['type'])) {
$type = $simpleHead[$k]['type'];
$allowArray = $simpleHead[$k]['allowarray'];
}
//設置單元格的數據驗證
if ($type) {
switch ($type) {
case 'list':
self::set_selection_range($sheet, $startCol, $allowArray);
break;
case 'range':
self::set_value_range($sheet, $startCol, $allowArray);
break;
default:
break;
}
}
}
$row ++;
}
}
/**
* 獲取底層數據
*
* @param array $head
* @param array $node
* @return array
*/
private static function get_head($head, &$node = [])
{
foreach ($head as $key => $value) {
if (isset($value['children']) && is_array($value['children'])) {
self::get_head($value['children'], $node);
} else {
$node[] = $value;
}
}
return $node;
}
/**
* 數據控制,設置單元格數據在一個可選方位類
*
* @param object $sheet
* @param integer $cell
* @param string $rangeStr
* @param string $title
* @return void
*/
private static function set_selection_range(
$sheet,
$cell,
$rangeStr,
$title = '數據類型'
)
{
$objValidation = $sheet ->getCell($cell)
->getDataValidation();
$objValidation ->setType(\PHPExcel_Cell_DataValidation::TYPE_LIST)
->setErrorStyle(\PHPExcel_Cell_DataValidation::STYLE_STOP)
->setAllowBlank(true)
->setShowInputMessage(true)
->setShowErrorMessage(true)
->setShowDropDown(true)
->setErrorTitle('輸入的值有誤')
->setError('您輸入的值不在下拉框列表內.')
->setPromptTitle('"'.$title.'"')
->setFormula1('"'.$rangeStr.'"');
}
/**
* 現在單元格的有效數據范圍,暫時僅限于數字
* @param object $sheet 當前的工作簿對象
* @param integer $cell 需要設置屬性的單元格
* @param string $valueRange 允許輸入數組的訪問
* @return void
*/
private static function set_value_range($sheet, $cell, $valueRange)
{
//設置單元格的的數據類型是數字,并且保留有效位數
$sheet ->getStyle($cell)
->getNumberFormat()
->setFormatCode(\PHPExcel_Style_NumberFormat::FORMAT_NUMBER_00);
$valueRange = explode(',', $valueRange);
//開始數值有效訪問設定
$objValidation = $sheet ->getCell($cell)
->getDataValidation();
$objValidation->setType(\PHPExcel_Cell_DataValidation::TYPE_WHOLE );
$objValidation->setErrorStyle(\PHPExcel_Cell_DataValidation::STYLE_STOP );
$objValidation->setAllowBlank(true);
$objValidation->setShowInputMessage(true);
$objValidation->setShowErrorMessage(true);
$objValidation->setErrorTitle('輸入錯誤');
$objValidation->setError('請輸入數據范圍在從' . $valueRange[0] . '到' . $valueRange[1] . '之間的所有值');
$objValidation->setPromptTitle('允許輸入');
$objValidation->setPrompt('請輸入數據范圍在從' . $valueRange[0] . '到' . $valueRange[1] . '之間的所有值');
$objValidation->setFormula1($valueRange['0']);
$objValidation->setFormula2($valueRange['1']);
}
/**
* 輸出
*
* @param object $objWriter
* @return void
*/
private static function out_input_header($objWriter, $fileName = '')
{
ob_end_clean();
$fileName = ($fileName == '' ? time() : $fileName) . '.xlsx';
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
header('Content-Disposition:inline;filename="'.$fileName.'"');
header("Content-Transfer-Encoding: binary");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Pragma: no-cache");
$objWriter->save('php://output');
exit;
}
~~~
### 8、作者有話說
該些封裝提供給phper,希望減輕一些不必要的彎路
該些方法沒有設置一些文檔屬性等功能
phper可以根據具體需求做調整
- 產品介紹
- 功能介紹
- 聯系合作
- 修訂記錄
- 環境安裝
- 依賴環境
- 依賴服務
- 前端環境
- 核心功能
- 流程引擎
- 流程編輯步驟
- 流程設計
- 業務表設計
- 表單設計
- 報表設計
- 節點配置
- 全局配置
- 規則引擎
- 模塊功能
- 系統管理
- 平臺架構
- 平臺(單位)管理
- 組織架構
- 用戶管理
- 角色管理
- 運維管理
- 菜單管理
- 流程管理
- 代碼管理
- 字典管理
- 車型庫管理
- 報表管理
- 產品管理
- 經銷商管理
- 數據字典
- 業務設置
- 業務報表設置
- 系統配置
- 微信小程序設置
- OCR識別
- 釘釘通知
- 釘釘登錄授權
- 云儲存設置
- 服務規劃
- 其他產品
- 教育點播
- 退役政務
- 三方報表
- 微信社群
- 場景名片
- 合同電子簽
- TP5實用封裝
- 通用封裝
- Export通用封裝
- Import通用封裝
- 配合Import通用封裝的ImportBaseVerify類
- Files通用封裝
- Directory通用封裝
- Pdf通用封裝
- Words通用封裝
- Nredis(redis封裝)
- ZipArchives壓縮zip文件封裝
- BarQrcode條形碼二維碼
- Publics公共方法封裝
- Curls(cUrl請求封裝)
- extend擴展開發
- 何時編寫擴展文件包
- 擴展文件包如何編寫
- 題外話
- 常用的compose安裝
- 一些建議及細節
- 實用工具
- 源碼分析
- 入口文件index.php
- think\Facade類詳解
- App對接Api設計
- 設計模式說明(為什么這么做)
- 代碼設計邏輯方案
- 數據庫備份
- 實用案例
- 如何“偷”JAVA代碼
- SAAS接口設計
- 1、創建saas基類文件
- 2、初始化控制器服務
- 3、身份校驗
- 4、創建登錄控制器
- 5、實現登錄服務層
- 6、服務層繼承的service類
- 7、該案例的完整模塊包
- 校驗型規則校驗擴展包
- 自定義規則解析(可供參考)
- 通用文件擴展包(File,Zip)
- PHP實現word轉PDF功能環境搭建
- 日志通用擴展包
- 定時任務
- PHP視頻處理器安裝環境搭建
- 日常開發問題記錄
- 1、mysql存儲中帶一些不可見字符
- 2、php開發規范校驗及修復