您现在的位置是:首页 >技术杂谈 >PHP基于xlswriter支持无限表头层级Excel导出网站首页技术杂谈

PHP基于xlswriter支持无限表头层级Excel导出

dreamboycs 2024-06-26 18:01:02
简介PHP基于xlswriter支持无限表头层级Excel导出

本章介绍基于PHP扩展xlswriter的VtifulKernelExcel类可以支持无限层级的复杂表头导出!
废了九牛二虎之力,终于把这个功能类写完了…后续会持续更新优化

准备xlswriter扩展
windows系统:
到PECL网站下载符合自己本地PHP环境的ddl文件下载地址,并复制到PHP的扩展目录ext文件夹下,修改php.ini文件,加上这行

 

extension=xlswriter

打开phpinfo()验证扩展是否安装成功

Linux系统:
 

pecl install xlswriter

 php配置文件添加
 

extension = xlswriter.so

重启php nginx

composer下载phpoffice/phpexcel

因为有用到单元格相关函数,所以需要执行下列命令

composer require phpoffice/phpexcel 1.8

封装导出类文件(重点来了)

  • 支持多层表头
  • 支持多Sheet
  • 支持过滤选项
  • 支持单元格格式
  • 支持单元格公式
  • 支持表头加粗
  • 支持表头斜体
  • 支持冻结表头
  • 支持插入图片
  • 支持表头居中
  • 支持锁定保护
  • 支持数据合并
  • 支持数据背景颜色
<?php
use PHPExcel_Cell;

class MultiFloorXlsWriterService
{
    // 默认宽度
    private $defaultWidth = 16;
    // 默认高度
    private $defaultHeight = 15;
    // 默认导出格式
    private $exportType = '.xlsx';
    // 表头最大层级
    private $maxHeight  = 1;
    // 文件名
    private $fileName = null;
    // 默认公式行距离数据间隔
    private $defaultFormulaTop = 2;
    // 数据占用截至行
    private $maxDataLine = 2;
    // 默认的单元格格式,常规
    private $defaultCellFormat = 'general';
    // 支持的单元格格式,可扩充
    private $allowCellFormat = [
        'general' => PHPExcel_Style_NumberFormat::FORMAT_GENERAL,
        'text' => PHPExcel_Style_NumberFormat::FORMAT_TEXT,
    ];
    // 支持的单元列操作-数据合并
    const CELL_ACT_MERGE = 'merge';
    // 支持的单元列操作-背景颜色
    const CELL_ACT_BACKGROUND = 'background';
    // 数据合并开始标识
    const ACT_MERGE_START = 'start';
    // 数据合并结束标识
    const ACT_MERGE_END = 'end';
    private $allowCellActs = [
        self::CELL_ACT_MERGE,
        self::CELL_ACT_BACKGROUND,
    ];
    // 单元格操作集合
    private $cellActs = [];

    private $xlsObj;
    private $fileObject;
    private $format;
    private $boldIStyle;
    private $colManage;
    private $lastColumnCode;

    public function __construct()
    {
        // 文件默认输出地址
        $path = base_path().'/storage/logs';
        $config = [
            'path' => $path
        ];

        $this->xlsObj = (new VtifulKernelExcel($config));
    }

    /**
     * 设置文件名
     * @param string $fileName 文件名
     * @param string $sheetName 第一个sheet名
     */
    public function setFileName(string $fileName = '', string $sheetName = 'Sheet1')
    {
        $fileName = empty($fileName) ? (string)time() : $fileName;
        $fileName .= $this->exportType;

        $this->fileName = $fileName;

        $this->fileObject = $this->xlsObj->fileName($fileName, $sheetName);
        $this->format     = (new VtifulKernelFormat($this->fileObject->getHandle()));
    }

    /**
     * 设置表头
     * @param array $header
     * @throws Exception
     */
    public function setHeader(array $header)
    {
        if (empty($header)) {
            throw new Exception('表头数据不能为空');
        }

        if (is_null($this->fileName)) {
            self::setFileName(time());
        }

        // 获取单元格合并需要的信息
        $colManage = self::setHeaderNeedManage($header);

        // 完善单元格合并信息
        $this->colManage = self::completeColMerge($colManage);

        // 设置最后单元格标识
        $this->lastColumnCode = self::getColumn(end($this->colManage)['cursorEnd']) . $this->maxHeight;

        // 合并单元格
        self::queryMergeColumn();
    }

    /**
     * 填充文件数据
     * @param array $data
     * @throws Exception
     */
    public function setData(array $data)
    {
        // 起始行
        $indexRow = $this->maxHeight + 1;
        // 起始列
        $indexCol = 0;
        foreach ($data as $row => $datum) {
            foreach ($datum as $column => $value) {
                // 列值为数组,说明有额外操作
                if (is_array($value)) {
                    $val = $value[0];
                    $act = $value[1];
                    $pos = self::getColumn($indexCol) . $indexRow;
                    // 有效行为
                    $availableActs = array_intersect($this->allowCellActs, array_keys($act));
                    foreach ($availableActs as $availableAct) {
                        switch ($availableAct) {
                            case self::CELL_ACT_MERGE:
                                // 数据合并
                                $this->cellActs[$indexCol][self::CELL_ACT_MERGE][$act[$availableAct]] = $pos;
                                $this->cellActs[$indexCol][self::CELL_ACT_MERGE]['val'] = $val;
                                break;
                            case self::CELL_ACT_BACKGROUND:
                                // 背景颜色
                                $this->cellActs[$indexCol][self::CELL_ACT_BACKGROUND][] = [
                                    'row'    => $row,
                                    'column' => $column,
                                    'color'  => $act[$availableAct],
                                    'val'    => $val
                                ];
                                break;
                            default:
                                throw new Exception('不支持的单元格操作['. $availableAct .']');
                        }
                    }
                } else {
                    $this->fileObject->insertText($row + $this->maxHeight, $column, $value);
                }
                $indexCol++;
            }
            $indexRow++;
            $indexCol = 0;
        }
        // 执行单元格操作
        self::queryCellActs();
        $this->maxDataLine = $this->maxHeight + count($data);
    }

    /**
     * 添加Sheet
     * @param string $sheetName
     */
    public function addSheet(string $sheetName)
    {
        $this->fileObject->addSheet($sheetName);
    }

    /**
     * 设置公式
     * {start}:数据开始行 {end}:数据结束行
     * col_title:公式标题所在列标识,从0开始
     * title:公式标题
     * col_formula:公式结果所在列标识
     * formula:公式内容
     * @param array $formulas
     * @throws Exception
     */
    public function setFormula(array $formulas)
    {
        if (empty($formulas)) {
            throw new Exception('公式格式错误');
        }

        $line = $this->maxDataLine + $this->defaultFormulaTop;

        foreach ($formulas as $formula) {
            if (isset($formula['col_title']) && isset($formula['title'])) {
                $this->fileObject->insertText($line, $formula['col_title'], $formula['title']);
            }

            if (!isset($formula['col_formula']) || !isset($formula['formula']) || empty($formula['formula'])) {
                throw new Exception('公式格式错误');
            }

            $formula['formula'] = str_ireplace('{start}', $this->maxHeight + 1, $formula['formula']);
            $formula['formula'] = str_ireplace('{end}', $this->maxDataLine, $formula['formula']);

            $this->fileObject->insertFormula($line, $formula['col_formula'], $formula['formula']);
        }
    }

    /**
     * 设置公式行距离数据间隔
     * @param $top
     */
    public function reBuildFormulaTop(int $top)
    {
        $this->defaultFormulaTop = $top;
    }

    /**
     * 插入本地图片
     * @param int $row
     * @param int $column
     * @param string $localImagePath
     * @param float|int $widthScale
     * @param float|int $heightScale
     * @throws Exception
     */
    public function setImage(int $row, int $column, string $localImagePath, float $widthScale = 1, float $heightScale = 1)
    {
        if (!file_exists($localImagePath)) {
            throw new Exception("未检测到图片{$localImagePath}");
        }
        $this->fileObject->insertImage($row, $column, $localImagePath, $widthScale, $heightScale);
    }

    /**
     * 冻结表头(需放到setHeader后调用)
     */
    public function setFreezeHeader()
    {
        $this->fileObject->freezePanes($this->maxHeight, 0);
    }

    /**
     * 开启过滤选项(需放到setHeader后调用)
     */
    public function setFilter()
    {
        $this->fileObject->autoFilter("A1:{$this->lastColumnCode}");
    }

    /**
     * 设置表头加粗(需放到setHeader后调用)
     */
    public function setBoldHeader()
    {
        $this->boldIStyle = $this->format->bold()->toResource();
        $this->fileObject->setRow("A1:{$this->lastColumnCode}", $this->defaultHeight, $this->boldIStyle);
    }

    /**
     * 设置表头斜体(需放到setHeader后调用)
     */
    public function setItalicHeader()
    {
        $this->boldIStyle = $this->format->italic()->toResource();
        $this->fileObject->setRow("A1:{$this->lastColumnCode}", $this->defaultHeight, $this->boldIStyle);
    }

	/**
     * 设置表头水平居中对齐(需放到setHeader后调用)
     */
    public function setAlignCenterHeader()
    {
        $this->boldIStyle = $this->format->align(VtifulKernelFormat::FORMAT_ALIGN_CENTER, VtifulKernelFormat::FORMAT_ALIGN_VERTICAL_CENTER)->toResource();
    }

	/**
     * 文件密码保护
     * @param $password
     */
    public function setFileProtection($password = null)
    {
        $this->fileObject->protection($password);
    }

    /**
     * 保存文件至服务器
     */
    public function output()
    {
        return $this->fileObject->output();
    }

    /**
     * 输出到浏览器
     * @param $filePath
     * @throws Exception
     */
    public function excelDownload($filePath)
    {
        $fileName = $this->fileName;
        $userBrowser = $_SERVER['HTTP_USER_AGENT'];
        if( preg_match('/MSIE/i', $userBrowser)) {
            $fileName = urlencode($fileName);
        } else {
            $fileName = iconv('UTF-8', 'GBK//IGNORE', $fileName);
        }

        header("Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        header('Content-Disposition: attachment;filename="' . $fileName . '"');
        header('Content-Length: ' . filesize($filePath));
        header('Content-Transfer-Encoding: binary');
        header('Cache-Control: must-revalidate');
        header('Cache-Control: max-age=0');
        header('Pragma: public');

        if (ob_get_contents()) {
            ob_clean();
        }

        flush();

        if (copy($filePath, 'php://output') === false) {
            throw new Exception($filePath. '地址出问题了');
        }

        // 删除本地文件
        @unlink($filePath);

        exit();
    }

    /**
     * 组装单元格合并需要的信息
     * @param $header
     * @param int $cursor
     * @param int $col
     * @param array $colManage
     * @param $parentList
     * @param $parent
     * @throws Exception
     * @return array
     */
    private function setHeaderNeedManage($header, $col = 1, &$cursor = 0, &$colManage = [], $parent = null, $parentList = [])
    {
        foreach ($header as $head) {
            if (empty($head['title'])) {
                throw new Exception('表头数据格式有误');
            }

            if (is_null($parent)) {
                // 循环初始化
                $parentList = [];
                $col = 1;
            } else {
                // 递归进入,高度和父级集合通过相同父级条件从已有数组中获取,避免递归增加与实际数据不符
                foreach ($colManage as $value) {
                    if ($value['parent'] == $parent) {
                        $parentList = $value['parentList'];
                        $col = $value['height'];
                        break;
                    }
                }
            }

            // 单元格标识
            $column = $this->getColumn($cursor) . $col;

            // 单元格格式
            $format = $this->allowCellFormat[$this->defaultCellFormat];
            if (!empty($head['format'])) {
                if (!isset($this->allowCellFormat[$head['format']])) {
                    throw new Exception("不支持的单元格格式{$head['format']}");
                }
                $format = $this->allowCellFormat[$head['format']];
            }

            // 组装单元格需要的各种信息
            $colManage[$column] = [
                'title'      => $head['title'],      // 标题
                'cursor'     => $cursor,             // 游标
                'cursorEnd'  => $cursor,             // 结束游标
                'height'     => $col,                // 高度
                'width'      => $this->defaultWidth, // 宽度
                'format'     => $format,             // 单元格格式
                'mergeStart' => $column,             // 合并开始标识
                'hMergeEnd'  => $column,             // 横向合并结束标识
                'zMergeEnd'  => $column,             // 纵向合并结束标识
                'parent'     => $parent,             // 父级标识
                'parentList' => $parentList,         // 父级集合
            ];

            if (!empty($head['children']) && is_array($head['children'])) {
                // 有下级,高度加一
                $col += 1;
                // 当前标识加入父级集合
                $parentList[] = $column;

                $this->setHeaderNeedManage($head['children'], $col, $cursor,$colManage, $column, $parentList);
            } else {
                // 没有下级,游标加一
                $cursor += 1;
            }
        }

        return $colManage;
    }

    /**
     * 完善单元格合并信息
     * @param $colManage
     * @return array
     */
    private function completeColMerge($colManage)
    {
        $this->maxHeight = max(array_column($colManage, 'height'));
        $parentManage    = array_column($colManage, 'parent');

        foreach ($colManage as $index => $value) {
            // 设置横向合并结束范围:存在父级集合,把所有父级的横向合并结束范围设置为当前单元格
            if (!is_null($value['parent']) && !empty($value['parentList'])) {
                foreach ($value['parentList'] as $parent) {
                    $colManage[$parent]['hMergeEnd'] = self::getColumn($value['cursor']) . $colManage[$parent]['height'];
                    $colManage[$parent]['cursorEnd'] = $value['cursor'];
                }
            }

            // 设置纵向合并结束范围:当前高度小于最大高度 且 不存在以当前单元格标识作为父级的项
            $checkChildren = array_search($index, $parentManage);
            if ($value['height'] < $this->maxHeight && !$checkChildren) {
                $colManage[$index]['zMergeEnd'] = self::getColumn($value['cursor']) . $this->maxHeight;
            }
        }

        return $colManage;
    }

    /**
     * 合并单元格
     */
    private function queryMergeColumn()
    {
        foreach ($this->colManage as $value) {
            $this->fileObject->mergeCells("{$value['mergeStart']}:{$value['zMergeEnd']}", $value['title']);
            $this->fileObject->mergeCells("{$value['mergeStart']}:{$value['hMergeEnd']}", $value['title']);

            // 设置单元格需要的宽度
            if ($value['cursor'] != $value['cursorEnd']) {
                $value['width'] = ($value['cursorEnd'] - $value['cursor'] + 1) * $this->defaultWidth;
            }

            // 设置单元格格式
            $formatCell =  (new VtifulKernelFormat($this->fileObject->getHandle()));
            $boldStyle  = $formatCell->number($value['format'])->toResource();

            // 设置列单元格样式
            $toColumnStart = self::getColumn($value['cursor']);
            $toColumnEnd   = self::getColumn($value['cursorEnd']);
            $this->fileObject->setColumn("{$toColumnStart}:{$toColumnEnd}", $value['width'], $boldStyle);
        }
    }

    /**
     * 执行单元格操作
     */
    private function queryCellActs()
    {
        if (!empty($this->cellActs)) {
            foreach ($this->cellActs as $actNote) {
                $tmpActStyle = (new VtifulKernelFormat($this->fileObject->getHandle()));
                // 背景颜色
                if (isset($actNote[self::CELL_ACT_BACKGROUND])) {
                    foreach ($actNote[self::CELL_ACT_BACKGROUND] as $item) {
                        // 支持颜色常量
                        $tmpActStyle->background($this->backgroundConst($item['color']));
                        $this->fileObject->insertText($item['row'] + $this->maxHeight, $item['column'], $item['val'], '', $tmpActStyle->toResource());
                    }
                }
                // 数据合并
                if (isset($actNote[self::CELL_ACT_MERGE])) {
                    if (!empty($actNote[self::CELL_ACT_MERGE][self::ACT_MERGE_START]) && !empty($actNote[self::CELL_ACT_MERGE][self::ACT_MERGE_END])) {
                        // 合并样式:水平左对齐,垂直居中对齐
                        $tmpActStyle->align(VtifulKernelFormat::FORMAT_ALIGN_LEFT, VtifulKernelFormat::FORMAT_ALIGN_VERTICAL_CENTER);
                        $this->fileObject->mergeCells(
                            "{$actNote[self::CELL_ACT_MERGE][self::ACT_MERGE_START]}:{$actNote[self::CELL_ACT_MERGE][self::ACT_MERGE_END]}",
                            $actNote[self::CELL_ACT_MERGE]['val'],
                            $tmpActStyle->toResource()
                        );
                    }
                }
            }
            $this->cellActs = [];
        }
    }

   /**
     * 颜色常量转换
     * @param $color
     * @return mixed
     */
    private function backgroundConst($color)
    {
        $const = [
            'black'   => VtifulKernelFormat::COLOR_BLACK,   // 黑色
            'blue'    => VtifulKernelFormat::COLOR_BLUE,    // 蓝色
            'brown'   => VtifulKernelFormat::COLOR_BROWN,   // 棕色
            'cyan'    => VtifulKernelFormat::COLOR_CYAN,    // 青色
            'gray'    => VtifulKernelFormat::COLOR_GRAY,    // 灰色
            'green'   => VtifulKernelFormat::COLOR_GREEN,   // 绿色
            'lime'    => VtifulKernelFormat::COLOR_LIME,    // 石灰
            'magenta' => VtifulKernelFormat::COLOR_MAGENTA, // 洋红
            'navy'    => VtifulKernelFormat::COLOR_NAVY,    // 深蓝
            'orange'  => VtifulKernelFormat::COLOR_ORANGE,  // 橙色
            'pink'    => VtifulKernelFormat::COLOR_PINK,    // 粉红
            'purple'  => VtifulKernelFormat::COLOR_PURPLE,  // 紫色
            'red'     => VtifulKernelFormat::COLOR_RED,     // 红色
            'silver'  => VtifulKernelFormat::COLOR_SILVER,  // 银色
            'white'   => VtifulKernelFormat::COLOR_WHITE,   // 白色
            'yellow'  => VtifulKernelFormat::COLOR_YELLOW,  // 黄色
        ];

        return $const[$color] ?? $color;
    }

    /**
     * 获取单元格列标识
     * @param $num
     * @return string
     */
    private function getColumn($num)
    {
        return PHPExcel_Cell::stringFromColumnIndex($num);
    }

}

使用示例

header头规则 title表示列标题,children表示子列,没有子列children可不写或为空

$header = [
            [
                'title' => '一级表头1',
                'children' => [
                    [
                        'title' => '二级表头1',
                    ],
                    [
                        'title' => '二级表头2',
                    ],
                    [
                        'title' => '二级表头3',
                    ],
                ]
            ],
            [
                'title' => '公式测试',
            ],
            [
                'title' => '一级表头3',
                'children' => [
                    [
                        'title' => '二级表头1',
                        'children' => [
                            [
                                'title' => '三级表头1',
                            ],
                            [
                                'title' => '三级表头2',
                            ],
                        ]
                    ],
                    [
                        'title' => '公式测试',
                    ],
                    [
                        'title' => '二级表头3',
                        'children' => [
                            [
                                'title' => '三级表头1',
                                'children' => [
                                    [
                                        'title' => '四级表头1',
                                        'children' => [
                                            [
                                                'title' => '五级表头1',
                                            ],
                                            [
                                                'title' => '五级表头2',
                                            ]
                                        ]
                                    ],
                                    [
                                        'title' => '四级表头2'
                                    ]
                                ]
                            ],
                            [
                                'title' => '三级表头2',
                            ],
                        ]
                    ]
                ]
            ],
            [
                'title' => '一级表头4',
                'format' => 'text',
            ],
            [
                'title' => '一级表头5',
                'format' => 'text',
            ],
        ];

        $data = [];
        for ($i = 0; $i < 35; $i++) {
            // 数据合并
            $merge = '这是第'. $i .'行测试';
            if ($i == 10) {
                $merge = [
                    '数据合并测试',
                    ['merge' => 'start']
                ];
            }
            if ($i == 30) {
                $merge = [
                    '数据合并测试',
                    ['merge' => 'end']
                ];
            }

            // 数据合并+背景颜色
            $megBack = '这是第'. $i .'行测试';
            if ($i == 0) {
                $megBack = [
                    '数据合并+背景颜色测试',
                    ['merge' => 'start']
                ];
            }
            if ($i == 20) {
                $megBack = [
                    '数据合并+背景颜色',
                    ['merge' => 'end', 'background' => 'brown']
                ];
            }

            // 单个单元格背景颜色
            $testBack = '这是第'. $i .'行测试';
            if ($i == 10) {
                $testBack = [
                    '单行单列背景颜色测试',
                    ['background' => 'orange']
                ];
            }

            // RGB十六进制颜色
            $rgbColor = '这是第'. $i .'行测试';
            if ($i == 15) {
                $rgbColor = [
                    'RGB十六进制颜色测试',
                    ['background' => 0xFDB8]
                ];
            }

            $data[] = [
                $merge,
                '这是第'. $i .'行测试',
                $megBack,
                rand(1, 99),
                $testBack,
                $rgbColor,
                rand(1, 10),
                '这是第'. $i .'行测试',
                '这是第'. $i .'行测试',
                '这是第'. $i .'行测试',
                '这是第'. $i .'行测试',
                '这是第'. $i .'行测试',
                '这是第'. $i .'行测试',
            ];
        }

        // 公式测试,计算列的总和、平均
        $formulas_test = [
            [
                'col_title' => 2,
                'title' => '总计',
                'col_formula' => 3,
                'formula' => '=SUM(D{start}:D{end})'
            ],
            [
                'col_title' => 5,
                'title' => '平均',
                'col_formula' => 6,
                'formula' => '=AVERAGE(G{start}:G{end})'
            ],
        ];
        // 公式测试,计算列相乘
        $formulas_test_two = [
            [
                'col_title' => 5,
                'title' => '总计',
                'col_formula' => 6,
                'formula' => '=SUM(G{start}:G{end})'
            ]
        ];

        $data_two = [];
        for ($i = 0; $i < 30; $i++) {
            // 数据合并
            $merge = '这是第'. $i .'行测试';
            if ($i == 5) {
                $merge = [
                    '数据合并测试',
                    ['merge' => 'start']
                ];
            }
            if ($i == 10) {
                $merge = [
                    '数据合并测试',
                    ['merge' => 'end']
                ];
            }

            // 数据合并+背景颜色
            $megBack = '这是第'. $i .'行测试';
            if ($i == 0) {
                $megBack = [
                    '数据合并+背景颜色测试',
                    ['merge' => 'start']
                ];
            }
            if ($i == 10) {
                $megBack = [
                    '数据合并+背景颜色',
                    ['merge' => 'end', 'background' => 'brown']
                ];
            }

            // RGB十六进制颜色
            $rgbColor = '这是第'. $i .'行测试';
            if ($i == 15) {
                $rgbColor = [
                    'RGB十六进制颜色测试2',
                    ['background' => 0xFDB8]
                ];
            }

            $data_two[] = [
                $merge,
                '这是第'. $i .'行测试',
                $megBack,
                'test',
                'test2',
                $rgbColor,
                'test',
                '这是第'. $i .'行测试',
                '这是第'. $i .'行测试',
                $merge,
                '这是第'. $i .'行测试',
                '这是第'. $i .'行测试',
                $megBack,
            ];
        }

        $data_three = [];
        for ($i = 0; $i < 100; $i++) {
            $data_three[] = [
                '这是第'. $i .'行测试',
                '这是第'. $i .'行测试',
                '这是第'. $i .'行测试',
                '这是第'. $i .'行测试',
                '这是第'. $i .'行测试',
                '这是第'. $i .'行测试',
                '这是第'. $i .'行测试',
                '这是第'. $i .'行测试',
                '这是第'. $i .'行测试',
                '这是第'. $i .'行测试',
                '这是第'. $i .'行测试',
                '这是第'. $i .'行测试',
                '这是第'. $i .'行测试',
            ];
        }

        try {
            $fileName = '很厉害的文件导出类';
            $xlsWriterServer = new MultiFloorXlsWriterService();
            $xlsWriterServer->setFileName($fileName, '这是Sheet1别名');
            $xlsWriterServer->setHeader($header);
            $xlsWriterServer->setBoldHeader(); // 设置表头加粗
            $xlsWriterServer->setItalicHeader(); // 设置表头斜体
            $xlsWriterServer->setFilter(); // 表头开启过滤选项
            $xlsWriterServer->setFreezeHeader(); // 冻结表头
            $xlsWriterServer->setAlignCenterHeader(); // 设置表头水平居中
            $xlsWriterServer->setFileProtection('testpwd'); // 设置文件解除锁定保护密码
            $xlsWriterServer->setData($data);

            $xlsWriterServer->setFormula($formulas_test); // 设置公式
            $xlsWriterServer->reBuildFormulaTop(3);  // 设置公式行距离数据行的间隔(默认2),这里使第二个公式数组在第一个公式下面
            $xlsWriterServer->setFormula($formulas_test_two);

            $xlsWriterServer->addSheet('这是Sheet2别名');
            $xlsWriterServer->setHeader($header); //这里可以使用新的header
            $xlsWriterServer->setFreezeHeader(); // 冻结表头
            $xlsWriterServer->setData($data_two); // 这里也可以根据新的header定义数据格式

            $xlsWriterServer->addSheet('这是Sheet3别名');
            $xlsWriterServer->setHeader($header); //这里可以使用新的header
            $xlsWriterServer->setFreezeHeader(); // 冻结表头
            $xlsWriterServer->setData($data_three); // 这里也可以根据新的header定义数据格式

            $filePath = $xlsWriterServer->output(); // 保存到服务器
            $xlsWriterServer->excelDownload($filePath); // 输出到浏览器
        } catch (Exception $e) {
            exit($e->getMessage());
        }

导出效果图:

原:PHP基于xlswriter支持无限表头层级Excel导出_php xlswriter_叶先生i的博客-CSDN博客

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。