month = $month; $this->data = $data; $this->projects = $projects; $this->company = $company; } public function title(): string { return $this->month; } public function startCell(): string { return 'A5'; } public function collection() { return collect($this->data); } public function registerEvents(): array { return [ AfterSheet::class => function(AfterSheet $event) { $sheet = $event->sheet->getDelegate(); $projectCount = count($this->projects); // 总列数计算:4(基础) + 项目数 + 1(合计工时) + 项目数 + 1(总计工资) $totalColNum = 4 + ($projectCount + 1) + ($projectCount + 1); $highestColumn = Coordinate::stringFromColumnIndex($totalColNum); $highestRow = $sheet->getHighestRow(); // 保证即便数据少,也画出至少 10 行的格子,显得美观 $renderToRow = max($highestRow, 10); // --- 1. 第一行:主标题 --- $sheet->mergeCells("A1:{$highestColumn}1"); $sheet->setCellValue('A1', "{$this->month}研发工资分摊表"); $sheet->getStyle('A1')->getFont()->setSize(16)->setBold(true); // --- 2. 第二行:单位名称 --- $sheet->mergeCells("A2:{$highestColumn}2"); $sheet->setCellValue('A2', "单位:{$this->company}"); $sheet->getStyle('A2')->getFont()->setSize(12); // --- 3. 第三、四行:复合表头 --- // 垂直合并基础列 foreach (['A', 'B', 'C', 'D'] as $col) { $sheet->mergeCells("{$col}3:{$col}4"); } $sheet->setCellValue('A3', '序号'); $sheet->setCellValue('B3', '技术人员'); $sheet->setCellValue('C3', '工资'); $sheet->setCellValue('D3', '月总工时'); // A. 月工时区域 (E列开始) $workHourStartCol = 5; $workHourEndCol = $workHourStartCol + $projectCount; $workHourStartLetter = Coordinate::stringFromColumnIndex($workHourStartCol); $workHourEndLetter = Coordinate::stringFromColumnIndex($workHourEndCol); $sheet->mergeCells("{$workHourStartLetter}3:{$workHourEndLetter}3"); $sheet->setCellValue("{$workHourStartLetter}3", '月 工 时'); foreach ($this->projects as $idx => $code) { $sheet->setCellValue(Coordinate::stringFromColumnIndex($workHourStartCol + $idx) . '4', $code); } $sheet->setCellValue($workHourEndLetter . '4', '合计工时'); // B. 金额区域 $moneyStartCol = $workHourEndCol + 1; $moneyEndCol = $moneyStartCol + $projectCount; $moneyStartLetter = Coordinate::stringFromColumnIndex($moneyStartCol); $moneyEndLetter = Coordinate::stringFromColumnIndex($moneyEndCol); $sheet->mergeCells("{$moneyStartLetter}3:{$moneyEndLetter}3"); $sheet->setCellValue("{$moneyStartLetter}3", '项目应计工资金额'); foreach ($this->projects as $idx => $code) { $sheet->setCellValue(Coordinate::stringFromColumnIndex($moneyStartCol + $idx) . '4', $code); } $sheet->setCellValue($moneyEndLetter . '4', '总计工资'); // --- 4. 样式美化 --- // 仅对表格主体(3行开始)加边框 $tableRange = "A3:{$highestColumn}{$renderToRow}"; $sheet->getStyle($tableRange)->applyFromArray([ 'borders' => ['allBorders' => ['borderStyle' => Border::BORDER_THIN]], 'alignment' => [ 'vertical' => Alignment::VERTICAL_CENTER, 'horizontal' => Alignment::HORIZONTAL_CENTER, ], 'font' => ['name' => '宋体', 'size' => 10], ]); // 标题和单位行样式 $sheet->getStyle("A1:A2")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT); $sheet->getStyle("A1")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER); $sheet->getStyle("A3:{$highestColumn}4")->getFont()->setBold(true); // 格式化金额(解决 0.02 问题) $sheet->getStyle("C5:C{$renderToRow}")->getNumberFormat()->setFormatCode('#,##0.00'); $sheet->getStyle("{$moneyStartLetter}5:{$highestColumn}{$renderToRow}")->getNumberFormat()->setFormatCode('#,##0.00'); // 设置列宽 $sheet->getColumnDimension('A')->setWidth(6); $sheet->getColumnDimension('B')->setWidth(12); for ($i = 3; $i <= $totalColNum; $i++) { $sheet->getColumnDimension(Coordinate::stringFromColumnIndex($i))->setWidth(13); } }, ]; } }