复杂xls导出
可以使用xlswriter源码类:
https://github.com/mk-j/PHP_XLSXWriter/blob/master/xlsxwriter.class.php
实现帮助类:
<?php
require_once 'web/libraries/XLSXwriter.php';
class PHPExeclCore
{
private $headerData = array();
private $headerWidthSize = 0;
private $headerHeightSize = 0;
private $colDefalutFormat = 'string';// 列默认数据格式
private $colDefalutWidth = '10';// 列默认宽度
private $header = array();
private $headerWidth = array();
// 数据字段
private $dataFields = array();
// 标题行默认样式
private $headerDefalutStyle = array(
'halign' => 'center',//水平居中
'valign' => 'center',//竖直居中
'font-style' => 'bold',
'font-size' => 12,
'border' => 'top,bottom,left,right'
//'fill'=>'#fff'// 背景色
// 边框有点问题不能用
// 高度也不行 用font-size撑开);
);
// 标题行处理数组
/**
* $headerRow = array(
* array('content' =>['Merge Cells Example','','','',''],'style'=>[['halign'=>'center'],array(),array(),array(),array()]),
* array('content' =>['姓名', '吃饭', '', '考勤', ''],'style'=>[['halign'=>'center','valign'=>'center'],['halign'=>'center'],[],['halign'=>'center'],[]]),
* array('content' =>['', '上午', '下午', '上午', '下午'],'style'=>[[],['halign'=>'center'],[],[],[]]),
* );
*/
private $headerRow = array(0 => array('content' => array(), 'style' => array()));
private $headerMargin = array();
// 数据行
private $dataRow = array();
// 现在够用了,先不做了
private $dataStyle = array();
public function __construct()
{
}
public function __get($name)
{
if (isset($this->$name)) {
return $this->$name;
} else {
return null;
}
// TODO: Implement __get() method.
}
/**
* 设置表头
* @param array $head
*/
public function setHeader($header = array())
{
$this->headerData = $header;
// 设置表头尺寸
$this->setHeaderSize($this->headerData);
// 生成rows 和 margin 设置样式
$this->dealHeader($this->headerData);
}
public function setData($data = array())
{
foreach ($data as $d) {
$row = array();
foreach ($this->dataFields as $field) {
$value = isset($d[$field]) ? $d[$field] : '';
$row[] = $value;
}
$this->dataRow[] = $row;
}
}
/**
* 导出
* @param string $filename
* @param string $sheet
*/
public function writeToStdOut($filename = 'test', $sheet = 'Sheet1')
{
//$header = ['字段','标题','宽度','类型']
$filename .= '.xlsx';
$writer = new \XLSXWriter();
$writer->writeSheetHeader($sheet, $this->header, $col_options = array('suppress_row' => true, 'widths' => $this->headerWidth));
foreach ($this->headerRow as $row)
$writer->writeSheetRow($sheet, $row['content'], $row['style']);
foreach ($this->headerMargin as $margin) {
$writer->markMergedCell($sheet, $margin['startRow'], $margin['startCol'], $margin['endRow'], $margin['endCol']);
}
foreach ($this->dataRow as $data)
$writer->writeSheetRow($sheet, $data);
header('Content-disposition: attachment; filename="' . \XLSXWriter::sanitize_filename($filename));
header("Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
header('Content-Transfer-Encoding: binary');
header('Cache-Control: must-revalidate');
header('Pragma: public');
$writer->writeToStdOut();
}
/**
* 生成rows 和 margin 设置样式
* @param $headerData
*/
private function dealHeader($headerData)
{
$this->recursionSetHeaderRowsAndMargin($headerData, 0, 0, $this->headerDefalutStyle);
}
/**
* 递归设置表头行数据,和合并表格数据源
* @param $headerData
* @param $row
* @param $startCol
* // 换个思路,填坑赋值
*/
private function recursionSetHeaderRowsAndMargin($headerData, $row, $col, $defStyle = array())
{
$heightSize = $this->headerHeightSize;
$startRow = $row;
$startCol = $col;
$content = array();//标题
$style = array();//样式
foreach ($headerData as $single) {
$marginCol = isset($single['marginCol']) ? $single['marginCol'] : 1;
// 合并
// 是否有子节点
$hasChildren = (isset($single['children']) && !empty($single['children'])) ? true : false;
// 有子节点合并一行,没有子节点直接把剩下行都算上
if ($hasChildren) {
$marginRow = 1;
} else {
// 一共3行 当前第1行需要合并1,2两行
$marginRow = $heightSize - $startRow;
}
$style = isset($single['style']) ? array_merge($defStyle, $single['style']) : $defStyle;
// 如果不是合并一行一列就加入合并单元格的数组,同时每个合并的单元格都加样式(主要是方便边框)
if ($marginCol != 1 || $marginRow != 1) {
//0+2-1=1 合并了0,1两个单元格 end是1
$endCol = $startCol + $marginCol - 1;
// 当前第1行需要合并2两行,目标单元格行号1+2-1
$endRow = $startRow + $marginRow - 1;
$this->headerMargin[] = array(
'startRow' => $startRow,
'startCol' => $startCol,
'endRow' => $endRow,
'endCol' => $endCol);
if (isset($style['border'])) {
$s = array('border' => $style['border']);
for ($r = 0; $r < $marginRow; $r++) {
$row = $startRow + $r;
for ($c = 0; $c < $marginCol; $c++) {
$col = $startCol + $c;
$this->setHeaderRowCell($row, $col, 'style', $s);
}
}
}
}
$this->setHeaderRowCell($startRow, $startCol, 'style', $style);
$content = isset($single['title']) ? $single['title'] : '';
$this->setHeaderRowCell($startRow, $startCol, 'content', $content);
//处理子节点
if ($hasChildren) {
$this->recursionSetHeaderRowsAndMargin($single['children'], $startRow + 1, $startCol, $style);
}
$startCol += $marginCol;
}
}
//
//
/**
* 设置表头单元格尺寸
* 同事设置列的数据类型和宽度
* @param $header
*/
private function setHeaderSize(&$headerData)
{
list($w, $h) = $this->recursionCalSize($headerData);
$this->headerWidthSize = $w;
$this->headerHeightSize = $h;
$this->setHeaderRow($w, $h);
}
/**
* 初始化表头行,占坑
* @param $w
* @param $h
*/
private function setHeaderRow($w, $h)
{
$data = array();
$content = array();
$style = array();
while (true) {
$w--;
$content[] = '';
$style[] = array();
if ($w <= 0) break;
}
while (true) {
$h--;
$data[] = array('content' => $content, 'style' => $style);
if ($h <= 0)
break;
}
$this->headerRow = $data;
unset($content);
unset($style);
unset($data);
}
/**
* 修改表头行的数据
* @param $col
* @param $row
* @param $filed
* @param $value
*/
private function setHeaderRowCell($row, $col, $key, $value)
{
if (isset($this->headerRow[$row][$key][$col])) {
$this->headerRow[$row][$key][$col] = $value;
} else {
return false;
}
}
/**
*
* @param $headerData
* @return array
*/
private function recursionCalSize(&$headerData)
{
$w = 0; // 根节点++
$h = 0; // 是子类高度的最大值
$childHeightArr = array();
foreach ($headerData as &$single) {
$singleH = 1;
// 没有子节点设置为空数组
if ((isset($single['children']) && !empty($single['children']))) {
list($cw, $ch) = $this->recursionCalSize($single['children'], $h);
$w += $cw;
$singleH += $ch;
$single['marginCol'] = $cw;
} else {
//设置表头需要要参数
//字段类型
$type = isset($single['format']) ? $single['format'] : $this->colDefalutFormat;
// 字段宽度
$width = isset($single['width']) ? $single['width'] : $this->colDefalutWidth;
$this->header[] = $type;
$this->headerWidth[] = $width;
// data的键
$field = isset($single['field']) ? $single['field'] : '';
$this->dataFields[] = $field;
$w++;
$singleH = 1;
}
$childHeightArr[] = $singleH;
}
$h = max($childHeightArr);
return array($w, $h);
}
}
调用:
$headerConfig = [
[
'title' => 'test',
'children' => [
['title' => '日期', 'field' => '日期'],
['title' => '总内购', 'field' => '总内购'],
]
],
];
$exportData = [
['日期' => '2021-08-08', '总内购' => '923'],
['日期' => '2021-08-08', '总内购' => '924'],
];
$tool = new PHPExeclCore();
$tool->setHeader($headerConfig);
$tool->setData($exportData);
$filename = date('_YmdHis');
$tool->writeToStdOut($filename);
exit();
参考:
https://www.jianshu.com/p/aba213453525
其他:https://blog.csdn.net/qq_41049126/article/details/89532403