[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开发规范校验及修复