aogg
9/19/2019 - 7:07 AM

maatwebsite/excel 相关

maatwebsite/excel 相关

这是laravel专用的excel

<?php

namespace App\Library\Tools;

use Illuminate\Support\Arr;
use Maatwebsite\Excel\Facades\Excel;

/**
 * Class ExcelTool
 * @package App\Library\Tools
 */
class ExcelTool
{
    /**
     * 导出Excel
     * @param $file_name
     * @param $header
     * @param $rows
     * @param int $row_width
     * @param ...$funcs
     */
    public static function export($file_name, $header, $rows, $row_width = 20, ...$funcs)
    {
        $excel_funcs = (isset($funcs[0]) && $funcs[0]) ? $funcs[0] : [];
        $sheet_funcs = (isset($funcs[1]) && $funcs[1]) ? $funcs[1] : [];

        Excel::create($file_name, function ($excel) use ($header, $rows, $row_width, $excel_funcs, $sheet_funcs) {
            foreach ($excel_funcs as $excel_func) {
                $excel = $excel_func($excel);
            }

            $excel->sheet('数据', function ($sheet) use ($header, $rows, $row_width, $excel_funcs, $sheet_funcs) {
                foreach($header as $key=>$title){
                    $header_title[] = $title;
                    $header_key[] = $key;
                }
                $header = $header_title;
                $new_rows = array();
                foreach($rows as $row){
                    $new_row = array();
                    foreach($header_key as $key){
                        $new_row[$key] = isset($row[$key])?$row[$key]:'';
                    }
                    $new_rows[] = $new_row;
                }
                $rows = $new_rows;
                $letters = '';
                $header_count = count($header);
                for ($i = 0; $i < $header_count; $i++) {
                    $multiple = floor(($i/26));
                    if ($multiple) {
                        $first_letter = chr($multiple + 64);
                        $letters = $first_letter . chr(($i - $multiple * 26) + 65);
                    } else {
                        $letters = chr($i + 65);
                    }
                    $width[$letters] = $row_width;
                }
                $sheet->setWidth($width);
                $sheet->row(1, $header);
                $sheet->rows($rows);
                $sheet->freezeFirstRow();
                $max = $letters . (count($rows) + 1);
                $sheet->setBorder('A1:' . $max, 'thin');
                foreach ($sheet_funcs as $sheet_func) {
                    $sheet = $sheet_func($sheet);
                }
            });
        })->export('xlsx');
    }

    /**
     * 导入excel文件,以数组形式返回内容
     * @param $file
     * @return mixed
     */
    public static function import($file)
    {
        return Excel::selectSheets('Sheet1')
            ->load($file, function ($reader) {})
            ->toArray();
    }

    /**
     * 带合并单元格功能的excel
     * 
     * 注意:
     * 如果写position为AC:A3这种错误写法就会报内存溢出
     *
    ExcelTool::exportMergeWithTitle('站场预付费车辆统计表' .date('YmdHis'), [
    ['name' => '站场预付费车辆统计表', 'position' => 'A1:R1'],
    ['config' => ['setFreeze' => 'B5'],], // 冻结单元格,要看单元格的左上角

    ['name' => '复核:', 'position' => ['row' => 'D', 'col' => 'last']],
    ['name' => '时间:', 'position' => ['row' => 'A', 'col' => 'last+1']],

    ['name' => '序号', 'position' => 'A2:A3', 'key' => 'i'],
    ['name' => '站场', 'position' => 'B2:B3', 'key' => 'station_name'],
    ['name' => '路段(财务别名)', 'position' => 'C2:C3', 'key' => 'finance_name'],
    ['name' => '路段', 'position' => 'A2', 'key' => 'road_name', 'colspan' => true],
    ['name' => '预付费', 'position' => ExcelTool::incPosition('D2:F2', 1)],

    ], $list);
     *
     * @author aogg
     * @param $filename
     * @param $headers
     * @param $list
     * @param bool $widthBool
     */
    public static function exportMergeWithTitle($filename, $headers, $list, $widthBool = true)
    {

        Excel::create($filename, function ($excel) use ($headers, $list, $filename, $widthBool) {

            $excel->sheet('数据', function ($sheet) use ($headers, $list, $filename, $widthBool) {
                /** @var \Maatwebsite\Excel\Classes\LaravelExcelWorksheet $sheet */

                // 处理位置,处理单元格合并
                $handleStringPositionAtCell = function ($data)use(&$sheet){
                    if (strpos($data['position'], ':') !== false){
                        $sheet->mergeCells($data['position']);
                        $sheet->cell(current(explode(':', $data['position'])), function($cell) use($data){
                            /** @var \Maatwebsite\Excel\Writers\CellWriter $cell */
                            // manipulate the cell
                            $cell->setValue($data['name']);
                            $cell->setAlignment('center');
                            $cell->setValignment('center');

                            $cell->setFontWeight('bold');
                            $cell->setFontSize(14);
                            $cell->setFontColor('#3e446b');
                        });
                    }else{
                        $sheet->cell($data['position'], function($cell) use($data){
                            /** @var \Maatwebsite\Excel\Writers\CellWriter $cell */
                            // manipulate the cell
                            $cell->setValue($data['name']);

                            // 设置标题样式
                            $cell->setFontWeight('bold');
                            $cell->setFontSize(16);
                            $cell->setFontColor('#3e446b');
                        });
                    }
                };

                $getStartRow = function ()use(&$sheet){ // @see \Maatwebsite\Excel\Classes\LaravelExcelWorksheet::getStartRow
                    if ($sheet->getHighestRow() == 1)
                        return 1;

                    return $sheet->getHighestRow() + 1;
                };

                $rowStartNum = 1;
                $headerKeyArr = $colspanArr = $lastArr = $configArr = [];
                foreach($headers as $key=>$data){
                    if (!empty($data['config'])) { // 自定义配置
                        $configArr = array_merge($configArr, $data['config']);
                        continue;
                    }

                    if (empty($data['position'])) {
                        continue;
                    }

                    $matches = [];
                    // 那数据的开始行
                    if (is_array($data['position'])) { // 暂不支持冒号
                        if (!isset($data['position']['row']) || !isset($data['position']['col'])) {
                            continue;
                        }
                        if (strpos($data['position']['col'], 'last') === 0) { // 放最后
                            $lastArr[$key] = $data;
                            continue;
                        }else{
                            continue; // 暂无其他
                        }

                    }else{
                        if (preg_match('/\d+/', $data['position'], $matches)) {
                            $rowStartNum = max($rowStartNum, $matches[0]);
                        }
                    }

                    if (isset($data['key'])) {
                        $headerKeyArr[$data['key']] = $data['key'];

                        if (isset($data['colspan'])) { // 定义竖列
                            $colspanArr[$data['key']] = ['colspan' => $data['colspan'], 'position' => $data['position']];
                        }
                    }

                    $handleStringPositionAtCell($data);
                }

                if (!empty($configArr)) {
                    foreach ($configArr as $funcName => $args) {
                        if (!method_exists($sheet, $funcName)) {
                            continue;
                        }

                        call_user_func_array([$sheet, $funcName], (array)$args);
                    }
                }

                // 处理宽度
                if ($widthBool) {
                    $header_count = count($headers);
                    for ($i = 0; $i < $header_count; $i++) {
                        $multiple = floor(($i/26));
                        if ($multiple) {
                            $first_letter = chr($multiple + 64);
                            $letters = $first_letter . chr(($i - $multiple * 26) + 65);
                        } else {
                            $letters = chr($i + 65);
                        }
                        $width[$letters] = 20;
                    }
                    $sheet->setWidth($width);
                }


                // 过滤数据数组
                $new_rows = $colspanRowNumArr = $colspanRowTempArr = array();

                $i = $rowStartNum + 1;
                foreach($list as $row){
                    $new_row = array();

                    foreach($headerKeyArr as $key){
                        $value = Arr::get($row, $key, '');

                        if (!empty($colspanArr[$key])) { // 竖列
                            if (isset($colspanRowTempArr[$key]) && $colspanRowTempArr[$key]['value'] === $value){ // 和上一个相等
                                $tempKey = $key . '_' . $colspanRowTempArr[$key]['numKey']; // 找到第一个

                                // A2:A3
                                $colspanRowNumArr[$tempKey] = isset($colspanRowNumArr[$tempKey])?
                                    static::incPosition($colspanRowNumArr[$tempKey], 1, 2, 2): // 已有数据,递增后面
                                    // 初始化数据
                                    static::incPosition(
                                        static::replacePosition( // 替换右边
                                            static::replacePosition( // 替换左边
                                                (
                                                    // 处理为必须带冒号
                                                    strpos($colspanArr[$key]['position'], ':')!==false?
                                                    $colspanArr[$key]['position']:
                                                    $colspanArr[$key]['position'] . ':' . $colspanArr[$key]['position']
                                                ),
                                                $colspanRowTempArr[$key]['i'], 2, 1
                                            ),
                                            $i - 1, 2, 2
                                        ),
                                        1, 2, 2);
                            }else{ // 没和上一个相等
                                $colspanRowTempArr[$key] = [
                                    'value' => $value,
                                    'i' => $i,
                                    'numKey' => isset($colspanRowTempArr[$key]) && !empty($colspanRowTempArr[$key]['numKey']) ?
                                        ++$colspanRowTempArr[$key]['numKey'] : 1
                                ];
                            }

                        }

                        $new_row[] = $value;
                    }
                    $new_rows[] = $new_row;
                    ++$i;
                }
                $sheet->rows($new_rows);

                // 合并竖列
                if (!empty($colspanRowNumArr)) {
                    foreach ($colspanRowNumArr as $itemPosition){
                        $sheet->mergeCells($itemPosition, true);
                        $sheet->cells($itemPosition, function ($cell)
                        {
                            /** @var \Maatwebsite\Excel\Writers\CellWriter $cell */
                            $cell->setValignment('center'); // 垂直居中
                        });
                    }
                }

                // 最后
                if (!empty($lastArr)) {
                    $rowEndRow = $getStartRow();
                    foreach ($lastArr as $item) {
                        $matches = [];
                        if (preg_match('/([a-z]+)([+-]\d+)?$/i', $item['position']['col'], $matches)) { // 放最后
                            $item['position'] = $item['position']['row'] . ($rowEndRow + ($matches[2]??0));
                        }else{
                            continue; // 暂无其他
                        }

                        $handleStringPositionAtCell($item);
                    }
                }

            });
        })->export('xlsx');
    }

    /**
     * 横向或竖向递增
     *
     * @param $position
     * @param $inc
     * @param int $incType 1为横向,2为竖向
     * @param bool $bothType 冒号分割,同时递增,1为只递增前面,2为只递增后面
     * @return string
     */
    public static function incPosition($position, $inc, $incType = 1, $bothType = true)
    {
        $arr = explode(':', $position);
        $matches = [];
        $incWordFunc = function (&$word, &$num){
            if (empty($num)){
                return;
            }

            ++$word;
            --$num;
        };

        foreach ($arr as &$item) {
            if (!preg_match('/([A-Z]+)(\d*)/', $item, $matches)) {
                continue;
            }

            if ($bothType === 2){ // 只递增后面
                $bothType = true;
                continue;
            }

            $wordIncNum = $incType === 1 ? $inc : 0;
            $incWordFunc($matches[1], $wordIncNum);

            $item = $matches[1] .
                ($matches[2] ? ($incType === 2 ? $matches[2] + $inc : $matches[2]) : '');

            if ($bothType === 1){ // 只递增前面
                break;
            }
        }

        return join(':', $arr);
    }

    /**
     * 横向或竖向递增
     *
     * @param $position
     * @param $inc
     * @param int $incType 1为横向,2为竖向
     * @param bool $bothType 冒号分割,同时递增,1为只递增前面,2为只递增后面
     * @return string
     */
    public static function replacePosition($position, $inc, $incType = 1, $bothType = true)
    {
        $arr = explode(':', $position);
        $matches = [];

        foreach ($arr as &$item) {
            if (!preg_match('/([A-Z]+)(\d*)/', $item, $matches)) {
                continue;
            }

            if ($bothType === 2){ // 只处理冒号后面
                $bothType = true;
                continue;
            }

            $item = ($incType === 1?$inc:$matches[1]) . ($incType === 2?$inc:$matches[2]);

            if ($bothType === 1){ // 只处理冒号前面
                break;
            }
        }

        return join(':', $arr);
    }
}

使用示例

合并单元格的使用示例

<?php
        if(!empty($requestData['export'])){
            ExcelTool::exportMergeWithTitle($fileNameArr[$requestData['type']]??''.date('YmdHis'), array_merge([
                ['name' => $fileNameArr[$requestData['type']??'']??'', 'position' => 'A1:P1'],
                ['name' => '站场预付费车辆统计表', 'position' => 'A1:R1'],
                ['name' => '复核:', 'position' => ['row' => 'D', 'col' => 'last']],
                ['name' => '时间:', 'position' => ['row' => 'A', 'col' => 'last+1']],
            
                ['name' => '序号', 'position' => 'A2:A3', 'key' => 'i'],
                ['name' => '站场', 'position' => 'B2:B3', 'key' => 'station_name'],
                ['name' => '路段(财务别名)', 'position' => 'C2:C3', 'key' => 'finance_name'],
                ['name' => '路段', 'position' => 'A2', 'key' => 'road_name', 'colspan' => true],
                ['name' => '预付费', 'position' => ExcelTool::incPosition('D2:F2', 1)],
                
            ],($requestData['type'] == 2?[
                ['name' => '岗位', 'position' => 'D2:D3', 'key' => 'post_name'],
            ]:[]),[
                ['name' => '预付费', 'position' => ExcelTool::incRow('D2:F2', 1)],
                ['name' => '停车次数', 'position' => ExcelTool::incRow('D3', 1), 'key' => 'vip_num_sum'],
                ['name' => '停车时长', 'position' => ExcelTool::incRow('E3', 1), 'key' => 'vip_parking_time_sum'],
                ['name' => '日分摊金额', 'position' => ExcelTool::incRow('F3', 1), 'key' => 'vip_real_total_price_sum'],

                ['name' => '临停', 'position' => ExcelTool::incRow('G2:I2', 1)],
                ['name' => '停车次数', 'position' => ExcelTool::incRow('G3', 1), 'key' => 'temp_num_sum'],
                ['name' => '停车时长', 'position' => ExcelTool::incRow('H3', 1), 'key' => 'temp_parking_time_sum'],
                ['name' => '金额', 'position' => ExcelTool::incRow('I3', 1), 'key' => 'temp_real_total_price_sum'],

                ['name' => '自刷卡', 'position' => ExcelTool::incRow('J2:L2', 1)],
                ['name' => '停车次数', 'position' => ExcelTool::incRow('J3', 1), 'key' => 'card_num_sum'],
                ['name' => '停车时长', 'position' => ExcelTool::incRow('K3', 1), 'key' => 'card_parking_time_sum'],
                ['name' => '金额', 'position' => ExcelTool::incRow('L3', 1), 'key' => 'card_real_total_price_sum'],

                ['name' => '支付类型(次数)', 'position' => ExcelTool::incRow('M2:N2', 1)],
                ['name' => '微信', 'position' => ExcelTool::incRow('M3', 1), 'key' => 'wechat_pay_way_sum'],
                ['name' => '电子钱包', 'position' => ExcelTool::incRow('N3', 1), 'key' => 'wallet_pay_way_sum'],

                ['name' => '支付类型(金额)', 'position' => ExcelTool::incRow('O2:P2', 1)],
                ['name' => '微信', 'position' => ExcelTool::incRow('O3', 1), 'key' => 'wechat_real_total_price_sum'],
                ['name' => '电子钱包', 'position' => ExcelTool::incRow('P3', 1), 'key' => 'wallet_real_total_price_sum'],


            ]), $list);
        }