Files
xin-procurement/app/Exports/PurchaseSupplierSheet.php
T
2026-08-27 14:20:09 +08:00

164 lines
5.8 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Exports;
use App\Models\PurchaseOrderModel;
use App\Models\SupplierModel;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithStrictNullComparison;
use Maatwebsite\Excel\Concerns\WithStyles;
use Maatwebsite\Excel\Concerns\WithTitle;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
/**
* 供应商采购明细导出 · 单市场工作表
* 模板列序:品名、汇总、市场、各门店明细(门店列=该供应商有明细的门店);
* 有数据的单元格(品名/汇总/门店数量)填充突出颜色并加粗
*/
class PurchaseSupplierSheet implements FromCollection, WithStrictNullComparison, WithStyles, WithTitle
{
/** 有数据单元格填充色(浅黄,突出显示) */
private const string DATA_FILL = 'FFFFFF99';
private ?Collection $rows = null;
/** @var array<int, string> 特殊行索引(1 起)=> 类型(title/header/summary */
private array $specialRows = [];
/** 列头所在行索引 */
private int $headerRow = 1;
/** @var array<int, array<int, int>> 有数据的单元格:行索引(1 起)=> 列索引(1 起)列表 */
private array $dataCells = [];
/**
* @param PurchaseOrderModel $purchase 采购单
* @param SupplierModel $supplier 供应商
* @param string $marketLabel 市场名(空市场已转为「未设置」)
* @param array<int, array<string, mixed>> $productRows 商品行(品名/汇总数量/各门店数量)
* @param array<int, string> $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;
}
}