如何在Laravel-Excel中实现单列合并并嵌套显示多列内容?
Laravel-Excel 实现主列合并并对应子列行数的解决方案
核心思路
要实现主列单元格纵向合并(行数等于对应子列数量),并在下方显示子列内容,需分两步完成:
- 构建符合表格结构的数据数组,主列仅在第一行显示,子列按行填充,空缺列留空;
- 通过Laravel-Excel的
AfterSheet事件设置单元格合并规则,同时可添加样式优化显示效果。
步骤1:重构数据数组
从你的productGroup和all_product数据源中整理主列与子列的对应关系,生成规范的表格数据:
// 整理主列与子列的关联数据 $groupedProducts = []; foreach ($productGroup as $group) { // 筛选当前分组下的有效子产品名称 $subNames = []; foreach ($all_product as $product) { if ($group['id'] == $product->product_group_id && !empty($product->product_sur_name)) { $subNames[] = $product->product_sur_name; } } // 仅保留有子列的主列 if (!empty($subNames)) { $groupedProducts[] = [ 'main_title' => $group['gorupName'], // 注意:此处字段名是你的代码拼写,若为笔误请修正为groupName 'sub_items' => $subNames ]; } } if ($customer_type == 2) { // 计算表格最大行数(取子列数量最多的分组的子列数) $maxRowCount = !empty($groupedProducts) ? max(array_map(fn($item) => count($item['sub_items']), $groupedProducts)) : 0; // 构建表格头部行 $headerRow = array_merge( ['SL', 'Order No', 'Order Date', 'Challan No', 'Challan Date', 'Bill No', 'Party Name', 'Area'], array_column($groupedProducts, 'main_title'), ['Total'] ); // 构建子列数据行 $dataRows = []; for ($i = 0; $i < $maxRowCount; $i++) { // 前8列固定留空 $row = array_fill(0, 8, ''); // 填充对应主列的子项,无则留空 foreach ($groupedProducts as $group) { $row[] = $group['sub_items'][$i] ?? ''; } // Total列留空 $row[] = ''; $dataRows[] = $row; } // 合并头部与数据行,得到最终表格数据 $total_final_array = array_merge([$headerRow], $dataRows); }
步骤2:创建Excel导出类并设置合并规则
使用Maatwebsite Excel的WithEvents接口,在表格生成后通过AfterSheet事件实现单元格合并:
namespace App\Exports; use Maatwebsite\Excel\Concerns\FromCollection; use Maatwebsite\Excel\Concerns\WithEvents; use Maatwebsite\Excel\Events\AfterSheet; use PhpOffice\PhpSpreadsheet\Style\Alignment; use PhpOffice\PhpSpreadsheet\Style\Fill; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; class ProductListExport implements FromCollection, WithEvents { protected $tableData; protected $groupedData; public function __construct(array $tableData, array $groupedData) { $this->tableData = $tableData; $this->groupedData = $groupedData; } // 返回表格数据 public function collection() { return collect($this->tableData); } // 注册表格事件 public function registerEvents(): array { return [ AfterSheet::class => function(AfterSheet $event) { $sheet = $event->sheet->getDelegate(); $currentColumn = 9; // 前8列是固定列,主列从第9列(Excel的I列)开始 foreach ($this->groupedData as $group) { $subCount = count($group['sub_items']); // 计算合并的单元格范围:第1行到第1+子列数行,当前列 $startCell = Coordinate::stringFromColumnIndex($currentColumn) . '1'; $endCell = Coordinate::stringFromColumnIndex($currentColumn) . (1 + $subCount); // 执行单元格合并 $sheet->mergeCells("$startCell:$endCell"); // 设置主列单元格样式(垂直居中、灰色背景) $sheet->getStyle($startCell)->applyFromArray([ 'alignment' => [ 'vertical' => Alignment::VERTICAL_CENTER, 'horizontal' => Alignment::HORIZONTAL_CENTER, ], 'fill' => [ 'fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => 'E0E0E0'], ], ]); $currentColumn++; } // 可选:给所有单元格添加边框 $lastColumn = Coordinate::stringFromColumnIndex($currentColumn); $lastRow = count($this->tableData); $sheet->getStyle("A1:$lastColumn$lastRow")->applyFromArray([ 'borders' => [ 'allBorders' => ['borderStyle' => \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN], ], ]); }, ]; } }
步骤3:控制器中调用导出
在你的控制器方法中,实例化导出类并触发下载:
use App\Exports\ProductListExport; use Maatwebsite\Excel\Facades\Excel; public function exportExcel() { // ... 此处省略获取productGroup、all_product、customer_type的逻辑 ... // 生成$total_final_array和$groupedProducts(参考步骤1) return Excel::download( new ProductListExport($total_final_array, $groupedProducts), 'product_list.xlsx' ); }
关键说明
- 单元格合并必须通过
AfterSheet事件实现,因为Laravel-Excel的数据填充阶段无法直接处理合并逻辑; - 合并行数等于对应子列的数量,确保主列完全覆盖子列的行数;
- 数据行按最大子列数生成,避免子列内容被截断;
- 代码中
gorupName为你的原代码拼写,若为数据库字段笔误请修正为groupName。
内容的提问来源于stack exchange,提问作者Anika Tabassum
相关产品推荐
相关产品推荐

