jh-admin/extend/excel/Export.php

400 lines
16 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace extend\excel;
use think\Exception;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Style\Color;
/**
* 导出
* Class Export
* @package extend\excel
*/
class Export
{
/**
* 配置项
* @var array
*/
private $config = [
// 默认行高
'row_height' => 24,
// 默认列宽
'column_width' => 18,
// 字体
'font_family' => '微软雅黑',
// 字体大小
'font_size' => 11,
// 字体颜色
'color' => '000000',
// 垂直对齐方式
'visibility_align' => Alignment::VERTICAL_CENTER,
// 水平对齐方式
'text_align' => Alignment::HORIZONTAL_CENTER,
// 边框样式
'border_style' => Border::BORDER_THIN,
// 边框颜色
'border_color' => '000000',
// 分割线填充样式
'cut_fill_style' => Fill::FILL_SOLID,
// 分割线填充颜色
'cut_fill_color' => 'CCCCCC',
// 是否自动换行
'is_auto_wrap' => true,
// 行开始位置
'row_start_num' => 0,
// 表头是否冻结
'header_freeze' => false,
// 是否筛选
'auto_filter' => true
];
/**
* 单元格
* @var array
*/
private $cellKeys = [
'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'
];
/**
* Excel实例对象
* @var null
*/
private $excelObj = null;
/**
* 文件后缀
* @var string
*/
private $fileExt = '.xlsx';
/**
* Excel 写入类型
* @var string
*/
private $writerType = 'Xlsx';
/**
* 构造函数
* Export constructor.
* @param array $config
*/
public function __construct(array $config = [])
{
$this->excelObj = new Spreadsheet();
$this->init($config);
}
/**
* 初始化
* @param array $config
*/
private function init(array $config = [])
{
$this->config = array_merge($this->config, $config);
}
/**
* 整理数据
* @param array $data
* @throws \PHPExcel_Exception
*/
private function neatenData(array $data)
{
if (!empty($data)) {
// 行开始位置
$row_num = $this->config['row_start_num'];
$data = json_decode(json_encode($data), true);
if (isset($data[0])) {
if (isset($data[0][0])) {
// 多个单子是一家, 分别有多家合并到一个表格
$temp_data = $data;
} else {
// 多个单子合并到一个表格
$temp_data = array(
$data
);
}
} else {
// 只一个单子
$temp_data = array(
array(
$data
)
);
}
// 统一设置默认行高度
$this->excelObj->getActiveSheet()->getDefaultRowDimension()->setRowHeight($this->config['row_height']);
// 统一设置字体
$this->excelObj->getDefaultStyle()->getFont()->setName($this->config['font_family']);
// 统一设置字体大小
$this->excelObj->getDefaultStyle()->getFont()->setSize($this->config['font_size']);
// 统一设置字体颜色
$this->excelObj->getDefaultStyle()->getFont()->getColor()->setRGB($this->config['color']);
// 统一设置垂直居中
$this->excelObj->getDefaultStyle()->getAlignment()->setVertical($this->config['visibility_align']);
// 是否自动换行
$this->excelObj->getDefaultStyle()->getAlignment()->setWrapText($this->config['is_auto_wrap']);
foreach ($temp_data as $temp_k => $temp_val) {
// 最后一个下标
$last_key = -1;
foreach ($temp_val as $key => $val) {
$last_key = $key;
// 处理表头数据, 以表头规定规则
$cell_header_data = (isset($val['cell_header']) && is_array($val['cell_header'])) ? $val['cell_header'] : [];
// 处理横向合并后的表头数据
$cell_header = [];
foreach ($cell_header_data as $k => $v) {
$cell_header[] = $v;
// 处理合并, 填充
if (isset($v['colspan']) && $v['colspan'] > 1) {
for ($i = 1; $i < $v['colspan']; $i++) $cell_header[] = [];
}
}
// 处理表头标题
if (isset($val['head_title']) && is_array($val['head_title']) && !empty($val['head_title'])) {
$head_title = array_merge([
// 标题内容
'value' => '默认标题',
// 是否加粗
'is_bold' => true,
// 字体大小
'font_size' => 20,
// 字体颜色
'color' => $this->config['color'],
// 水平对齐方式
'text_align' => $this->config['text_align']
], $val['head_title']);
$head_title = array_merge($head_title, [
// 行高
'row_height' => $head_title['font_size'] + 20,
]);
$row_num++;
$cell = $this->cellKeys[0] . $row_num;
// 设置内容
$this->excelObj->getActiveSheet()->setCellValue($cell, $head_title['value']);
// 设置行高
$this->excelObj->getActiveSheet()->getRowDimension($row_num)->setRowHeight($head_title['row_height']);
// 合并单元格
$cell = $cell . ':' . $this->cellKeys[count($cell_header) - 1] . $row_num;
$this->excelObj->getActiveSheet()->mergeCells($cell);
// 设置边框
$this->excelObj->getActiveSheet()->getStyle($cell)->applyFromArray([
'borders' => array(
'outline' => array(
'style' => $this->config['border_style'],
'color' => array('argb' => $this->config['border_color'])
),
),
]);
// 是否加粗
$this->excelObj->getActiveSheet()->getStyle($cell)->getFont()->setBold($head_title['is_bold']);
// 字体大小
$this->excelObj->getActiveSheet()->getStyle($cell)->getFont()->setSize($head_title['font_size']);
// 字体颜色
$this->excelObj->getActiveSheet()->getStyle($cell)->getFont()->getColor()->setRGB($head_title['color']);
// 设置文字位置
$this->excelObj->getActiveSheet()->getStyle($cell)->getAlignment()->setHorizontal($head_title['text_align']);
}
// 处理表头
$row_num++;
foreach ($cell_header as $cell_k => $cell_v) {
if (empty($cell_v)) continue;
$cell_v = array_merge([
// 字段
'field' => '',
// 名称
'name' => '',
// 横向合并
'colspan' => 1,
// 列宽
'width' => $this->config['column_width'],
// 水平对齐方式
'text_align' => $this->config['text_align'],
], $cell_v);
$cell_header[$cell_k] = $cell_v;
$cell = $this->cellKeys[$cell_k] . $row_num;
// 设置内容
$this->excelObj->getActiveSheet()->setCellValue($cell, $this->specialCharsDeal($cell_v['name']));
// 设置行高
$this->excelObj->getActiveSheet()->getRowDimension($row_num)->setRowHeight($this->config['row_height']);
// 设置列宽
$this->excelObj->getActiveSheet()->getColumnDimension($this->cellKeys[$cell_k])->setWidth($cell_v['width']);
// 处理合并
if ($cell_v['colspan'] > 1) {
$cell = $cell . ':' . $this->cellKeys[$cell_k + $cell_v['colspan'] - 1] . $row_num;
$this->excelObj->getActiveSheet()->mergeCells($cell);
}
// 设置文字位置
$this->excelObj->getActiveSheet()->getStyle($cell)->getAlignment()->setHorizontal($cell_v['text_align']);
// 边框
$this->excelObj->getActiveSheet()->getStyle($cell)->applyFromArray([
'borders' => array(
'outline' => array(
'style' => $this->config['border_style'],
'color' => array('argb' => $this->config['border_color'])
),
),
]);
}
// 表头每列筛选 TODO
if ($this->config['auto_filter']) {
//$this->excelObj->getActiveSheet()->setAutoFilter($this->cellKeys[0] . $row_num . ':' . $this->cellKeys[count($cell_header) - 1] . $row_num);
}
// 冻结表头 TODO
$temp_val[$key]['cell_header'] = $cell_header;
// 处理数据
$table_data = [];
$val['data'] = (isset($val['data']) && !empty($val['data'])) ? $val['data'] : [];
foreach ($val['data'] as $vv) $table_data[] = $vv;
foreach ($table_data as $k => $v) {
$row_num++;
foreach ($cell_header as $cell_k => $cell_v) {
if (empty($cell_v)) continue;
$cell = $this->cellKeys[$cell_k] . $row_num;
// 设置内容
$this->excelObj->getActiveSheet()->setCellValue($cell, $this->specialCharsDeal((isset($v[$cell_v['field']]) ? $v[$cell_v['field']] : '')));
// 设置行高
$this->excelObj->getActiveSheet()->getRowDimension($row_num)->setRowHeight($this->config['row_height']);
// 处理合并
if ($cell_v['colspan'] > 1) {
$cell = $cell . ':' . $this->cellKeys[$cell_k + $cell_v['colspan'] - 1] . $row_num;
$this->excelObj->getActiveSheet()->mergeCells($cell);
}
// 设置文字位置
$this->excelObj->getActiveSheet()->getStyle($cell)->getAlignment()->setHorizontal($cell_v['text_align']);
// 设置边框
$this->excelObj->getActiveSheet()->getStyle($cell)->applyFromArray([
'borders' => array(
'outline' => array(
'style' => $this->config['border_style'],
'color' => array('argb' => $this->config['border_color'])
),
),
]);
}
}
}
$temp_data[$temp_k] = $temp_val;
// 分割线
if ($temp_k < (count($temp_data) - 1)) {
$row_num++;
// 设置行高
$this->excelObj->getActiveSheet()->getRowDimension($row_num)->setRowHeight($this->config['row_height']);
// 合并单元格
$cell = $this->cellKeys[0] . $row_num . ':' . $this->cellKeys[count($temp_data[$temp_k][$last_key]['cell_header']) - 1] . $row_num;
$this->excelObj->getActiveSheet()->mergeCells($cell);
// 填充背景色
$this->excelObj->getActiveSheet()->getStyle($cell)->getFill()->applyFromArray(array(
'type' => $this->config['cut_fill_style'],
'startcolor' => array(
'rgb' => $this->config['cut_fill_color'],
)
));
}
}
}
}
/**
* string 特殊字符处理
* @param $value
* @return string
*/
private function specialCharsDeal($value)
{
if (is_string($value)) {
if (strpos($value, '=') !== false) $value = "'" . $value;
}
return $value;
}
/**
* 文件名
* @param string $file_name
* @return string
*/
private function fileName(string $file_name)
{
return $file_name . date('_YmdHis') . $this->fileExt;
}
/**
* 导出文件
* @param string $file_name
* @param array $data
* @throws \PHPExcel_Exception
* @throws \PHPExcel_Reader_Exception
* @throws \PHPExcel_Writer_Exception
*/
public function exportFile(string $file_name, array $data)
{
$file_name = $this->fileName($file_name);
$this->neatenData($data);
header('pragma:public');
// 将输出重定向到客户端的网络浏览器(Xlsx)
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment;filename="' . $file_name . '"');//文件名
header('Cache-Control: max-age=0');
// 如果你服务于IE 9那么以下可能是需要的
header('Cache-Control: max-age=1');
// 如果您通过SSL为工业工程服务那么可能需要以下内容
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); // always modified
header('Cache-Control: cache, must-revalidate'); // HTTP/1.1
header('Pragma: public'); // HTTP/1.0
$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($this->excelObj, ucwords($this->writerType));
$writer->save('php://output');
}
/**
* 保存文件
* @param string $file_path
* @param array $data
* @return mixed|string
* @throws Exception
* @throws \PHPExcel_Exception
* @throws \PHPExcel_Reader_Exception
* @throws \PHPExcel_Writer_Exception
*/
public function saveFile(string $file_path, array $data,$fileExt='xlsx')
{
$file_path = $this->fileName($file_path);
$dir_path = pathinfo($file_path, PATHINFO_DIRNAME);
$this->neatenData($data);
if (dir_mkdir($dir_path)) {
// 执行保存
ob_end_clean();
$writer = IOFactory::createWriter($this->excelObj, ucwords($this->writerType));
$writer->save($file_path, true);
return $file_path;
}
throw new Exception('创建文件夹失败');
}
/**
* 析构函数
*/
public function __destruct()
{
$this->excelObj = null;
}
}