PHP使用PhpSpreadsheet实现导出Excel时带下拉框列表(可支持联动)

因项目需要导出Excel表单还支持三级联动功能,在网上一顿搜索找到这位大佬的代码
(https://blog.csdn.net/qq_15957557/article/details/123926163),
亲测有效,不过遇到些小问题,记录下以后方便修改

效果:

在这里插入图片描述

代码:

<?php
// 需要的扩展
use PhpOffice\PhpSpreadsheet\Cell\DataValidation;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\NamedRange;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
?>

class Excel extends Controller
{

	/**
     * @name: 导出下拉列表Excel
     * @author: Turbo
     * @Date: 2022-03-30 02:05:24
     */    
    public function exportselectexcel()
    {
        // 定义表头
        $header = [
            'A1' => '门店',
            'B1' => '公寓', 
            'C1' => '门牌号', 
            'D1' => '记账类型', 
            'E1' => '记账项目', 
            'F1' => '支出类型', 
            'G1' => '费用分类', 
            'H1' => '金额', 
            'I1' => '记账日期', 
            'J1' => '收款人', 
            'K1' => '收款人帐号', 
            'L1' => '开户支行',
            'M1' => '备注'
        ];

        // 下拉数据[这里模拟出来数据格式,实际情况从数据库获取数据并整理成下列数据格式]
        $oneData = [
            [
                'id' => 1,
                'title' => '我是A',
                'children' =>
                    [
                        [
                            'id' => 2,
                            'title' => '我是A的下级A1',
                            'children' =>
                                [
                                    [
                                        'id' => 3,
                                        'title' => '我是A1的下级A11'
                                    ],
                                    [
                                        'id' => 4,
                                        'title' => '我是A1的下级A12'
                                    ]
                                ]
                        ],
                        [
                            'id' => 5,
                            'title' => '我是A的下级A2',
                            'children' =>
                                [
                                    [
                                        'id' => 6,
                                        'title' => '我是A2的下级A21'
                                    ],
                                    [
                                        'id' => 7,
                                        'title' => '我是A2的下级A22'
                                    ]
                                ]
                        ]
                    ]
            ],
            [
                'id' => 1,
                'title' => '我是B',
                'children' =>
                    [
                        [
                            'id' => 2,
                            'title' => '我是B的下级B1',
                            'children' =>
                                [
                                    [
                                        'id' => 3,
                                        'title' => '我是B1的下级B11'
                                    ],
                                    [
                                        'id' => 4,
                                        'title' => '我是B1的下级B12'
                                    ]
                                ]
                        ],
                        [
                            'id' => 5,
                            'title' => '我是B的下级B2',
                            'children' =>
                                [
                                    [
                                        'id' => 6,
                                        'title' => '我是B2的下级B21'
                                    ],
                                    [
                                        'id' => 7,
                                        'title' => '我是B2的下级B22'
                                    ]
                                ]
                        ]
                    ]
            ],
        ];

        $keepType = '支出'; // 记账类别(默认只有支出)这里如果超出255字符的数据可采用"Sheet"赋值的方法(和下面这种联动的一样)

        // 实例化Spreadsheet对象
        $spreadsheet = new Spreadsheet();
        $sheetMain = $spreadsheet->getsheet(0); // 设置主sheet
        $sheetMain->setTitle('Excel导出下拉框-示例'); // 设置sheet的名称
        $sheetMain->getPageSetup()->setHorizontalCentered(true);
        $sheetMain->getPageSetup()->setVerticalCentered(false);
        // 插入表头
        foreach ($header as $key => $value) {
            $sheetMain->setCellValue($key, $value);
        }
        $sheetOne = $spreadsheet->createSheet(1); // 创建第一级sheet
        $sheetTwo = $spreadsheet->createSheet(2); // 创建第二级sheet
        $sheetTwoCol = 0; // 属性sheet的列
        foreach ($oneData as $key => $value) {
            $row = 1; // 定义赋值开始的行数
            /* 
                这里采用 Coordinate::stringFromColumnIndex 获取对应的列数名
                Ps:顺序从“1”开始,所以获取列名时需要在原有数组索引上加“1”
            */
            $sheetOne->setCellValue(Coordinate::stringFromColumnIndex(bcadd($key, 1, 0)) . $row, $value['title']); // 所有数据
            if (!empty($value['children'])) { // 判断是否有子级
                foreach ($value['children'] as $value2) { // 第二级
                    $row2 = 1; // 属性sheet的第n行
                    $sheetOne->setCellValue(Coordinate::stringFromColumnIndex(bcadd($key, 1, 0)) . ++$row, $value2['title']);
                    $sheetTwo->setCellValue(Coordinate::stringFromColumnIndex(bcadd($sheetTwoCol, 1, 0)) . $row2, $value2['title']);
                    if (!empty($value2['children'])) { // 判断是否有子级
                        foreach ($value2['children'] as $value3) { // 第三级
                            $sheetTwo->setCellValue(Coordinate::stringFromColumnIndex(bcadd($sheetTwoCol, 1, 0)) . ++$row2, $value3['title']);
                        }
                        $spreadsheet->addNamedRange(new NamedRange($value2['title'], $sheetTwo, Coordinate::stringFromColumnIndex(bcadd($sheetTwoCol, 1, 0)) . '2:' . Coordinate::stringFromColumnIndex(bcadd($sheetTwoCol, 1, 0)) . $row2)); // 设置第二级和第三季联动
                    }
                    $sheetTwoCol++; // 属性sheet列+1
                }
                // 定义数据命名范围
                $spreadsheet->addNamedRange(new NamedRange($value['title'], $sheetOne, Coordinate::stringFromColumnIndex(bcadd($key, 1, 0)) . '2:' . Coordinate::stringFromColumnIndex(bcadd($key, 1, 0)) . $row));
            }
        }
        // 定义数据命名范围
        $spreadsheet->addNamedRange(new NamedRange('oneData', $sheetOne, 'A1:' . Coordinate::stringFromColumnIndex(bcadd(count($oneData), 1, 0)) . '1'));

        // 隐藏不显示的辅助工作表
        $sheetOne->setSheetState(Worksheet::SHEETSTATE_HIDDEN);
        $sheetTwo->setSheetState(Worksheet::SHEETSTATE_HIDDEN);
  

        $maxRows = 5; // 设置最大填充行数[不包含第一行]
        foreach (range(2, bcadd($maxRows, 1, 0)) as $row) {
            // 设置下拉
            $this->setValidation($sheetMain, "A${row}", '=oneData'); // 第一级下拉
            $this->setValidation($sheetMain, "B${row}", "=INDIRECT(A${row})"); // indirect间接的,以某个坐标为依托,联级 第二级下拉
            $this->setValidation($sheetMain, "C${row}", "=INDIRECT(B${row})"); // indirect间接的,以某个坐标为依托,联级 第三级下拉
            $this->setValidation($sheetMain, "D${row}", '"' . $keepType . '"'); // 记账类型
            $this->setInputRule($sheetMain, "H${row}", DataValidation::TYPE_DECIMAL); // 设置输入数字类型
            $this->setInputRule($sheetMain, "I${row}", DataValidation::TYPE_TIME); // 设置输入时间类型
        }
        
        // 调整sheet样式
        $styleArray = ['font' => ['bold' => true], 'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER]];
        $sheetMain->getStyle('A1:M1')->applyFromArray($styleArray);
        // 设置表格宽度(设置'auto'为自动宽度)
        $sheetMain->getColumnDimension('A')->setWidth(25);
        $sheetMain->getColumnDimension('B')->setWidth(25);
        $sheetMain->getColumnDimension('C')->setWidth(25);
        $sheetMain->getColumnDimension('D')->setWidth(20);
        $sheetMain->getColumnDimension('E')->setWidth(25);
        $sheetMain->getColumnDimension('F')->setWidth(25);
        $sheetMain->getColumnDimension('G')->setWidth(25);
        $sheetMain->getColumnDimension('H')->setWidth(20);
        $sheetMain->getColumnDimension('I')->setWidth(20);
        $sheetMain->getColumnDimension('J')->setWidth(18);
        $sheetMain->getColumnDimension('K')->setWidth(22);
        $sheetMain->getColumnDimension('L')->setWidth(25);
        $sheetMain->getColumnDimension('M')->setWidth(30);

        //定义文件名称,需要带有定义的后缀名
        $filename = date('YmdHis') . 'Excel下拉框-示例.xlsx';
        ob_end_clean(); //清除缓冲区,避免乱码
        //将输出重定向到客户端的web浏览器
        header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
        header('Content-Disposition: attachment;filename="' . $filename . '"');
        header('Cache-Control: max-age=0');
        //如果浏览器为IE9
        header('Cache-Control: max-age=1');
        //如果通过SSL向IE提供服务
        header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
        header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
        header('Cache-Control: cache, must-revalidate');//HTTP/1.1
        header('Pragma: public');//HTTP/1.0
        $writer = IOFactory ::createWriter($spreadsheet, 'Xlsx');
        $writer->save('php://output');
        exit;
    }
	/**
     * 设置某个单元格的下拉列表规则
     * @param Worksheet $sheet
     * @param [string] $cellPoint 单元格坐标. A1
     * @param [sting] $format 公式
     * @return void
     */
    private function setValidation(Worksheet $sheet, $cellPoint, $format)
    {
        $validation = $sheet->getCell($cellPoint)->getDataValidation();
        $validation->setType(DataValidation::TYPE_LIST);
        $validation->setErrorStyle(DataValidation::STYLE_INFORMATION);
        $validation->setAllowBlank(false);
        $validation->setShowInputMessage(true);
        $validation->setShowErrorMessage(true);
        $validation->setShowDropDown(true);
        $validation->setErrorTitle('输出错误');
        $validation->setError('值不在列表中');
        $validation->setPromptTitle('请选择');
        $validation->setPrompt('请从列表中选择一个值');
        $validation->setFormula1($format); // 使用某个公司 eg: '=format'
        // $validation->setFormula1('"A1,A2,A3"');//可以直接写列表,用逗号分隔,最多255字符
        // $objValidation->setFormula1('=INDIRECT(A8)');//indirect间接的,以某个坐标为依托,联级
    }

    /**
     * 设置某个单元格的输入规则
     * @param Worksheet $sheet
     * @param [string] $cellPoint 单元格坐标. A1
     * @param [sting] type 输入类型 DataValidation::TYPE_LIST
     * @return void
     */
    private function setInputRule(Worksheet $sheet, $cellPoint, $type)
    {
        $validation = $sheet->getCell($cellPoint)->getDataValidation();
        $validation->setType($type);
        $validation->setErrorStyle(DataValidation::STYLE_INFORMATION); //输错了的话显示的提示属于哪个级别
        $validation->setAllowBlank(false);
        $validation->setShowInputMessage(true);
        $validation->setShowErrorMessage(true);
        $validation->setErrorTitle('输入格式有误');
    }
}

注:联动数据中不能有空格,不然会影响下一级数据显示

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值