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

news2024/11/30 6:44:00

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

准备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 \Vtiful\Kernel\Excel($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 \Vtiful\Kernel\Format($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(\Vtiful\Kernel\Format::FORMAT_ALIGN_CENTER, \Vtiful\Kernel\Format::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 \Vtiful\Kernel\Format($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 \Vtiful\Kernel\Format($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(\Vtiful\Kernel\Format::FORMAT_ALIGN_LEFT, \Vtiful\Kernel\Format::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'   => \Vtiful\Kernel\Format::COLOR_BLACK,   // 黑色
            'blue'    => \Vtiful\Kernel\Format::COLOR_BLUE,    // 蓝色
            'brown'   => \Vtiful\Kernel\Format::COLOR_BROWN,   // 棕色
            'cyan'    => \Vtiful\Kernel\Format::COLOR_CYAN,    // 青色
            'gray'    => \Vtiful\Kernel\Format::COLOR_GRAY,    // 灰色
            'green'   => \Vtiful\Kernel\Format::COLOR_GREEN,   // 绿色
            'lime'    => \Vtiful\Kernel\Format::COLOR_LIME,    // 石灰
            'magenta' => \Vtiful\Kernel\Format::COLOR_MAGENTA, // 洋红
            'navy'    => \Vtiful\Kernel\Format::COLOR_NAVY,    // 深蓝
            'orange'  => \Vtiful\Kernel\Format::COLOR_ORANGE,  // 橙色
            'pink'    => \Vtiful\Kernel\Format::COLOR_PINK,    // 粉红
            'purple'  => \Vtiful\Kernel\Format::COLOR_PURPLE,  // 紫色
            'red'     => \Vtiful\Kernel\Format::COLOR_RED,     // 红色
            'silver'  => \Vtiful\Kernel\Format::COLOR_SILVER,  // 银色
            'white'   => \Vtiful\Kernel\Format::COLOR_WHITE,   // 白色
            'yellow'  => \Vtiful\Kernel\Format::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博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/572107.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java内部类(成员内部类、静态嵌套类、方法内部类、匿名内部类)

文章目录 一、内部类的共性二、为什么需要内部类三、静态内部类&#xff08;静态嵌套类&#xff09;四、成员内部类五、局部内部类&#xff08;方法内部类&#xff09;六、匿名内部类 Java 类中不仅可以定义变量和方法&#xff0c;还可以定义类&#xff0c;这样定义在类内部的类…

WSL2+windows terminal

WSL2的安装与使用-Windows与Linux双系统的新选择 既要使用Windows系统满足日常生活&#xff0c;协作办公&#xff0c;又要使用Linux系统满足开发工作&#xff0c;双系统来回重启好麻烦&#xff0c;双主机成本高&#xff0c;远程服务器好多限制&#xff0c;WSL也许是更好的解决…

“智慧赋能 强链塑链”——打造电力特色智慧供应链体系

构建业务数智化、资源集约化、运营一体化、发展绿色化的智慧供应链体系&#xff0c;是电力企业实现智慧供应链建设的使命和目标。同时&#xff0c;在国内外双循环、一带一路、建立统一大市场的政策背景推动下&#xff0c;企业经营和居民生活对电力的需求仍然强劲并持续增长&…

Seata AT模式源码解析一(Seata Server端启动流程)

文章目录 启动类 ServerSessionHolder初始化DefaultCoordinator初始化初始化NettyRemotingServer 启动类 Server seata-server的入口类在Server类中&#xff0c;源码如下&#xff1a; public class Server {/*** The entry point of application.** param args the input arg…

Intel Realsense D405 在Ubuntu18.04下使用kalibr标定

目录 一. 在ubuntu下使用kalibr标定realsense_ros的安装总结下面是标定步骤 一. 在ubuntu下使用kalibr标定 在Realsense官网上librealsense现在D405只接受ROS2下的环境&#xff08;相机确实很新&#xff09; 在ROS1下我想到了改设备ID号的方式进行标定 这里需要注意libreals…

蓝桥杯并查集总结

本文先是给出三篇并查集原理解释文章链接&#xff0c;又提供了python代码模版&#xff1b;而后给出了一份蓝桥杯并查集的题单&#xff0c;并附有部分题目及其求解思路、代码。 目录部分 并查集原理 python代码 并查集题单 蓝桥幼儿园 题目描述 输入描述 输出描述 输入…

ERP有哪些系统?运用在哪些行业?

国内目前市面上ERP系统五花八门&#xff0c;但能真正快速匹配企业业务&#xff0c;且可以进行快速迭代二次开发的系统并不多见。 所以在选择ERP系统的时候可以参考下面这张表格中的内容&#xff1a; 目前常见的ERP软件大概可以分为三大类&#xff1a; ① 标准ERP应用&#xf…

白银实时价格应该在最适合的地方下注

小时候我们看战争片&#xff0c;总是发现主角们带兵打仗&#xff0c;战无不胜&#xff0c;偶尔有一场大的失利&#xff0c;但是总是能耐化险为夷&#xff0c;逢凶化吉&#xff0c;甚至最后成功反扑、反败为胜。后来小编一琢磨&#xff0c;发现&#xff0c;其实这些将才们打仗&a…

如何使用C++ 在Word文档中创建列表

列表分类是指在Word文档中使用不同格式排序的列表&#xff0c;来帮助我们一目了然地表达出一段文字的主要内容。比如&#xff0c;当我们描述了某个主题的若干点&#xff0c;就可以用列表把它们一一表达出来&#xff0c;而不是写成完整的段落形式。同时&#xff0c;列表也可以帮…

如何查看mysql里面的锁(详细)

通过查询表统计信息查看 information_schema库下相关事务表和锁相关信息表介绍innodb_trx存储了当前正在执行的事务信息trx_id&#xff1a;事务ID。trx_state&#xff1a;事务状态&#xff0c;有以下几种状态&#xff1a;RUNNING、LOCK WAIT、ROLLING BACK 和 COMMITTING。trx…

各位自学网络安全的同学,你们的学习路线真的对吗

最近在知乎上看到很多问题&#xff0c;都是小白想要转行网络安全行业咨询学习路线和学习资料的&#xff0c;作为一个培训机构&#xff0c;学习路线和免费学习资料肯定是很多的。机构里面的不是顶级的黑阔大佬就是正在学习的同学&#xff0c;也用不上这些内容&#xff0c;每天都…

Educational Codeforces Round 139 (Rated for Div. 2)

Educational Codeforces Round 139 (Rated for Div. 2) Problem - 1766E - Codeforces 显然我们可以把0序列的贡献单独算: i*(n-i1) 考虑只存在1,2,3的情况. 首先通过&#xff0c;观察到一个重要性质: 最多只有三种序列. 含有3或纯1或纯2型.纯1或纯2型纯2或纯1型 我们每次添加…

照片资源异地共享 ? tftgallery、xampp、快解析三种工具就能实现!

我的工作中&#xff0c;经常会收到处理各种图片的任务&#xff0c;在处理完图片之后&#xff0c;怎么发送给客户呢&#xff1f;传输的实现&#xff0c;需要一个安全而稳定的环境和即时方便的工具去进行操作与下载。一般情况下&#xff0c;我们大多会选择微信、QQ来作为传输下载…

Seata AT模式源码解析三(AT模式工作机制)

文章目录 代码示例流程源码解析开启全局事务注册分支事务一阶段提交全局事务提交分支事务二阶段提交全局事务回滚分支事务二阶段回滚 代码示例 从一个微服务示例开始&#xff0c;案例采用Seata官方提供的Demo。 用户购买商品的业务逻辑。整个业务逻辑由3个微服务提供支持&…

五大网络IO模型

网络IO模型 1. IO是什么&#xff1f; I/O&#xff08;英语&#xff1a;Input/Output&#xff09;&#xff0c;即输入&#xff0f;输出&#xff0c;通常指数据在存储器&#xff08;内部和外部&#xff09;或其他周边设备之间的输入和输出&#xff0c;是信息处理系统&#xff0…

Qt编程基础 | 第七章-MVD框架 | 7.1、MVD框架简介

一、MVD框架 1.1、MVC设计模式 MVC是一种与用户界面相关的设计模式&#xff0c;通过使用该设计模式&#xff0c;可以有效的分离数据与用户界面。MVC设计模式包含三个元素&#xff1a;表示数据的模型&#xff08;Model&#xff09;、表示用户界面的视图&#xff08;View&#x…

Android开发 外部存储空间

一、存储空间 对Andriod来说&#xff0c;存储空间分为内部存储空间和外部存储空间。 外部存储空间也就是常说的SD卡&#xff0c;可以分为私有存储空间和公共存储空间。 内部存储空间和外部存储空间的私有存储空间&#xff0c;都是每个APP独有的&#xff0c;不允许其他APP访问…

超详细的wireshark抓包使用教程

前言 Wireshark是非常流行的网络封包分析软件&#xff0c;可以截取各种网络数据包&#xff0c;并显示数据包详细信息。常用于开发测试过程各种问题定位。本文主要内容包括&#xff1a; 1、Wireshark软件下载和安装以及Wireshark主界面介绍。 2、WireShark简单抓包示例。通过该…

sql函数练习题

1. 计算用户8月每天的练题数量 题目&#xff1a;现在运营想要计算出2021年8月每天用户练习题目的数量&#xff0c;请取出相应数据。 示例&#xff1a;question_practice_detail iddevice_idquestion_idresultdate12138111wrong2021-05-0323214112wrong2021-05-0933214113wro…

2019上半年上午题

2019上半年上午题 a c c c d b b 应用代理网关防火墙 c a 使用数字证书对用户的身份进行认证 d 发送方的私钥签名&#xff0c;发送方公钥确认 d b 职务作品&#xff1a;归公司所有 a b b 从抽象到具体 d 等差数列求和&#xff1a; d 构建节点之间的关系图 然后…