ItemSalaryFTSheetExport.php 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. <?php
  2. namespace App\Exports;
  3. use Maatwebsite\Excel\Concerns\FromCollection;
  4. use Maatwebsite\Excel\Concerns\WithEvents;
  5. use Maatwebsite\Excel\Concerns\WithTitle;
  6. use Maatwebsite\Excel\Concerns\WithCustomStartCell;
  7. use Maatwebsite\Excel\Events\AfterSheet;
  8. use PhpOffice\PhpSpreadsheet\Style\Alignment;
  9. use PhpOffice\PhpSpreadsheet\Style\Border;
  10. use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
  11. class ItemSalaryFTSheetExport implements FromCollection, WithEvents, WithCustomStartCell, WithTitle
  12. {
  13. protected $month;
  14. protected $data;
  15. protected $projects;
  16. protected $company;
  17. public function __construct(string $month, array $data, array $projects, $company = '')
  18. {
  19. $this->month = $month;
  20. $this->data = $data;
  21. $this->projects = $projects;
  22. $this->company = $company;
  23. }
  24. public function title(): string { return $this->month; }
  25. public function startCell(): string { return 'A5'; }
  26. public function collection() { return collect($this->data); }
  27. public function registerEvents(): array
  28. {
  29. return [
  30. AfterSheet::class => function(AfterSheet $event) {
  31. $sheet = $event->sheet->getDelegate();
  32. $projectCount = count($this->projects);
  33. // 总列数计算:4(基础) + 项目数 + 1(合计工时) + 项目数 + 1(总计工资)
  34. $totalColNum = 4 + ($projectCount + 1) + ($projectCount + 1);
  35. $highestColumn = Coordinate::stringFromColumnIndex($totalColNum);
  36. $highestRow = $sheet->getHighestRow();
  37. // 保证即便数据少,也画出至少 10 行的格子,显得美观
  38. $renderToRow = max($highestRow, 10);
  39. // --- 1. 第一行:主标题 ---
  40. $sheet->mergeCells("A1:{$highestColumn}1");
  41. $sheet->setCellValue('A1', "{$this->month}研发工资分摊表");
  42. $sheet->getStyle('A1')->getFont()->setSize(16)->setBold(true);
  43. // --- 2. 第二行:单位名称 ---
  44. $sheet->mergeCells("A2:{$highestColumn}2");
  45. $sheet->setCellValue('A2', "单位:{$this->company}");
  46. $sheet->getStyle('A2')->getFont()->setSize(12);
  47. // --- 3. 第三、四行:复合表头 ---
  48. // 垂直合并基础列
  49. foreach (['A', 'B', 'C', 'D'] as $col) { $sheet->mergeCells("{$col}3:{$col}4"); }
  50. $sheet->setCellValue('A3', '序号');
  51. $sheet->setCellValue('B3', '技术人员');
  52. $sheet->setCellValue('C3', '工资');
  53. $sheet->setCellValue('D3', '月总工时');
  54. // A. 月工时区域 (E列开始)
  55. $workHourStartCol = 5;
  56. $workHourEndCol = $workHourStartCol + $projectCount;
  57. $workHourStartLetter = Coordinate::stringFromColumnIndex($workHourStartCol);
  58. $workHourEndLetter = Coordinate::stringFromColumnIndex($workHourEndCol);
  59. $sheet->mergeCells("{$workHourStartLetter}3:{$workHourEndLetter}3");
  60. $sheet->setCellValue("{$workHourStartLetter}3", '月 工 时');
  61. foreach ($this->projects as $idx => $code) {
  62. $sheet->setCellValue(Coordinate::stringFromColumnIndex($workHourStartCol + $idx) . '4', $code);
  63. }
  64. $sheet->setCellValue($workHourEndLetter . '4', '合计工时');
  65. // B. 金额区域
  66. $moneyStartCol = $workHourEndCol + 1;
  67. $moneyEndCol = $moneyStartCol + $projectCount;
  68. $moneyStartLetter = Coordinate::stringFromColumnIndex($moneyStartCol);
  69. $moneyEndLetter = Coordinate::stringFromColumnIndex($moneyEndCol);
  70. $sheet->mergeCells("{$moneyStartLetter}3:{$moneyEndLetter}3");
  71. $sheet->setCellValue("{$moneyStartLetter}3", '项目应计工资金额');
  72. foreach ($this->projects as $idx => $code) {
  73. $sheet->setCellValue(Coordinate::stringFromColumnIndex($moneyStartCol + $idx) . '4', $code);
  74. }
  75. $sheet->setCellValue($moneyEndLetter . '4', '总计工资');
  76. // --- 4. 样式美化 ---
  77. // 仅对表格主体(3行开始)加边框
  78. $tableRange = "A3:{$highestColumn}{$renderToRow}";
  79. $sheet->getStyle($tableRange)->applyFromArray([
  80. 'borders' => ['allBorders' => ['borderStyle' => Border::BORDER_THIN]],
  81. 'alignment' => [
  82. 'vertical' => Alignment::VERTICAL_CENTER,
  83. 'horizontal' => Alignment::HORIZONTAL_CENTER,
  84. ],
  85. 'font' => ['name' => '宋体', 'size' => 10],
  86. ]);
  87. // 标题和单位行样式
  88. $sheet->getStyle("A1:A2")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT);
  89. $sheet->getStyle("A1")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
  90. $sheet->getStyle("A3:{$highestColumn}4")->getFont()->setBold(true);
  91. // 格式化金额(解决 0.02 问题)
  92. $sheet->getStyle("C5:C{$renderToRow}")->getNumberFormat()->setFormatCode('#,##0.00');
  93. $sheet->getStyle("{$moneyStartLetter}5:{$highestColumn}{$renderToRow}")->getNumberFormat()->setFormatCode('#,##0.00');
  94. // 设置列宽
  95. $sheet->getColumnDimension('A')->setWidth(6);
  96. $sheet->getColumnDimension('B')->setWidth(12);
  97. for ($i = 3; $i <= $totalColNum; $i++) {
  98. $sheet->getColumnDimension(Coordinate::stringFromColumnIndex($i))->setWidth(13);
  99. }
  100. },
  101. ];
  102. }
  103. }