特殊行索引(1 起)=> 类型(title/header/summary) */ private array $specialRows = []; /** 列头所在行索引 */ private int $headerRow = 1; /** @var array> 有数据的单元格:行索引(1 起)=> 列索引(1 起)列表 */ private array $dataCells = []; /** * @param PurchaseOrderModel $purchase 采购单 * @param SupplierModel $supplier 供应商 * @param string $marketLabel 市场名(空市场已转为「未设置」) * @param array> $productRows 商品行(品名/汇总数量/各门店数量) * @param array $stores 门店列(门店ID => 名称) * @param string $sheetTitle 工作表名 */ public function __construct( private readonly PurchaseOrderModel $purchase, private readonly SupplierModel $supplier, private readonly string $marketLabel, private readonly array $productRows, private readonly array $stores, private readonly string $sheetTitle, ) { } /** * 导出行:标题/空行/列头(品名、汇总、市场、各门店)/明细/合计 */ public function collection(): Collection { if ($this->rows !== null) { return $this->rows; } $storeIds = array_map('intval', array_keys($this->stores)); $rows = []; $rowIndex = 0; // 标题行 $rows[] = [$this->supplier->name . ' · ' . $this->marketLabel . ' · 采购单 ' . $this->purchase->purchase_no]; $this->specialRows[++$rowIndex] = 'title'; // 空行 $rows[] = ['']; $rowIndex++; // 列头:品名、汇总、市场、各门店明细 $rows[] = array_merge(['品名', '汇总', '市场'], array_values($this->stores)); $this->specialRows[++$rowIndex] = 'header'; $this->headerRow = $rowIndex; // 明细行(有数据的单元格记录到 dataCells:品名/汇总/门店数量) $totalQuantity = 0; $storeTotals = array_fill_keys($storeIds, 0); foreach ($this->productRows as $productRow) { $line = [ $productRow['product_name'], (int) $productRow['quantity'], $this->marketLabel, ]; $filled = [1, 2, 3]; foreach ($storeIds as $position => $storeId) { $quantity = (int) ($productRow['store_quantities'][$storeId] ?? 0); $line[] = $quantity > 0 ? $quantity : ''; if ($quantity > 0) { $filled[] = 4 + $position; $storeTotals[$storeId] += $quantity; } } $rows[] = $line; $this->dataCells[++$rowIndex] = $filled; $totalQuantity += (int) $productRow['quantity']; } // 合计行 $rows[] = array_merge( ['合计', $totalQuantity, ''], array_map(static fn (int $storeId): int => $storeTotals[$storeId], $storeIds), ); $this->specialRows[++$rowIndex] = 'summary'; return $this->rows = collect($rows); } public function title(): string { return $this->sheetTitle; } /** * 标题/列头/合计加粗,有数据的单元格填充突出颜色,冻结列头 */ public function styles(Worksheet $sheet): array { $this->collection(); $sheet->freezePane('A' . ($this->headerRow + 1)); $widths = [24, 10, 12]; foreach ($widths as $index => $width) { $sheet->getColumnDimension(Coordinate::stringFromColumnIndex($index + 1))->setWidth($width); } $storeCount = count($this->stores); for ($i = 0; $i < $storeCount; $i++) { $sheet->getColumnDimension(Coordinate::stringFromColumnIndex(4 + $i))->setWidth(12); } // 有数据的单元格:填充突出颜色并加粗(含品名/汇总/门店数量) foreach ($this->dataCells as $rowIndex => $columns) { foreach ($columns as $columnIndex) { $style = $sheet->getStyle(Coordinate::stringFromColumnIndex($columnIndex) . $rowIndex); $style->getFill() ->setFillType(Fill::FILL_SOLID) ->getStartColor()->setARGB(self::DATA_FILL); $style->getFont()->setBold(true); } } $styles = []; foreach ($this->specialRows as $row => $type) { $style = match ($type) { 'title' => ['font' => ['bold' => true, 'size' => 14]], 'header', 'summary' => ['font' => ['bold' => true]], default => [], }; if ($style !== []) { $styles[$row] = $style; } } return $styles; } }