商品ID => 顶级分类名 */ private array $categoryNames = []; /** @var array 供应商ID => 名称 */ private array $supplierNames = []; private ?Collection $items = null; /** * @param PurchaseOrderModel $purchase 采购单 * @param string $type all 全品类 / category 仅蔬果分类 */ public function __construct( private readonly PurchaseOrderModel $purchase, private readonly string $type = 'all', ) { } /** * 导出行(按 sort 排序;蔬果分类时按顶级分类名过滤) */ public function collection(): Collection { if ($this->items !== null) { return $this->items; } $items = $this->purchase->items()->orderBy('sort')->get(); $this->loadLookups($items); if ($this->type === 'category') { $items = $items->filter(function ($item): bool { $rootName = $this->categoryNames[(int) $item->product_id] ?? ''; return str_contains($rootName, '蔬菜') || str_contains($rootName, '水果'); })->values(); } return $this->items = $items; } public function headings(): array { return ['序号', '分类', '品名', '规格/包规', '单价', '数量', '实际称重', '金额', '供应商', '备注']; } public function map($item): array { return [ $item->sort, $this->categoryNames[(int) $item->product_id] ?? '', $item->product_name, $item->product_spec, (float) $item->price, (float) $item->quantity, (float) $item->weight, (float) $item->amount, $this->supplierNames[(int) $item->supplier_id] ?? '', $item->remark, ]; } /** * 表头加粗 + 冻结首行 */ public function styles(Worksheet $sheet): array { $sheet->freezePane('A2'); return [ 1 => ['font' => ['bold' => true]], ]; } /** * PDF 模板视图数据 * * @return array{purchase: PurchaseOrderModel, items: Collection, categoryNames: array, supplierNames: array} */ public function viewData(): array { return [ 'purchase' => $this->purchase, 'items' => $this->collection(), 'categoryNames' => $this->categoryNames, 'supplierNames' => $this->supplierNames, ]; } /** * 预加载供应商名与商品顶级分类名(商品/供应商均含软删除,保证历史单据可导出) */ private function loadLookups(Collection $items): void { $this->supplierNames = SupplierModel::withTrashed() ->whereIn('id', $items->pluck('supplier_id')->unique()) ->pluck('name', 'id') ->toArray(); $productCategoryIds = ProductModel::withTrashed() ->whereIn('id', $items->pluck('product_id')->unique()) ->pluck('category_id', 'id'); $categories = ProductCategoryModel::all()->keyBy('id'); $this->categoryNames = []; foreach ($productCategoryIds as $productId => $categoryId) { $rootName = ''; $cursor = (int) $categoryId; $guard = 0; while ($cursor > 0 && $guard++ < 20) { $category = $categories->get($cursor); if ($category === null) { break; } $rootName = $category->name; $cursor = (int) $category->parent_id; } $this->categoryNames[(int) $productId] = $rootName; } } }