SingleSheetExport.php 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. <?php
  2. namespace App\Exports;
  3. use Illuminate\Support\Collection;
  4. use Maatwebsite\Excel\Concerns\FromCollection;
  5. use Maatwebsite\Excel\Concerns\WithTitle;
  6. use Maatwebsite\Excel\Concerns\WithCustomValueBinder;
  7. use Maatwebsite\Excel\Concerns\WithStyles;
  8. use Maatwebsite\Excel\Concerns\WithEvents;
  9. use Maatwebsite\Excel\Concerns\WithCustomStartCell;
  10. use Maatwebsite\Excel\Events\AfterSheet;
  11. use PhpOffice\PhpSpreadsheet\Cell\Cell;
  12. use PhpOffice\PhpSpreadsheet\Cell\DataType;
  13. use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder;
  14. use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
  15. use PhpOffice\PhpSpreadsheet\Style\Alignment;
  16. use PhpOffice\PhpSpreadsheet\Style\Border;
  17. class SingleSheetExport extends DefaultValueBinder implements
  18. FromCollection,
  19. WithTitle,
  20. WithCustomValueBinder,
  21. WithStyles,
  22. WithEvents,
  23. WithCustomStartCell
  24. {
  25. protected $data;
  26. protected $sheetName;
  27. protected $type; // 1: 研发人员, 2: 研发设备
  28. protected $columns;
  29. protected $timeRow;
  30. public function __construct(array $data, string $sheetName, int $type = 1, array $columns, array $timeRow)
  31. {
  32. $this->data = $data;
  33. $this->sheetName = $sheetName;
  34. $this->type = $type;
  35. $this->columns = $columns;
  36. $this->timeRow = $timeRow;
  37. }
  38. /**
  39. * 设置数据开始写入的起始单元格
  40. * 因为 A1-A4 被标题占用,数据从 A5 开始
  41. */
  42. public function startCell(): string
  43. {
  44. return 'A5';
  45. }
  46. public function collection()
  47. {
  48. return new Collection($this->data);
  49. }
  50. public function title(): string
  51. {
  52. return $this->sheetName;
  53. }
  54. public function bindValue(Cell $cell, $value)
  55. {
  56. if (is_numeric($value) && strlen($value) > 10) {
  57. $cell->setValueExplicit($value, DataType::TYPE_STRING);
  58. return true;
  59. }
  60. return parent::bindValue($cell, $value);
  61. }
  62. /**
  63. * 处理合并单元格和固定文字
  64. */
  65. public function registerEvents(): array
  66. {
  67. return [
  68. AfterSheet::class => function(AfterSheet $event) {
  69. $sheet = $event->sheet->getDelegate();
  70. $sheet->freezePane('A6');
  71. $highestColumn = $sheet->getHighestColumn();
  72. $highestRow = $sheet->getHighestRow();
  73. // 1. 设置下拉筛选 (Auto Filter)
  74. // 范围从 A5 开始到最后一列的最后一行
  75. // $sheet->setAutoFilter('A5:' . $highestColumn . $highestRow);
  76. // --- 原有的配置定义 ---
  77. $config = [
  78. 1 => [
  79. 'title' => '研发人员工时统计单',
  80. 'dept' => '部门:研发部',
  81. 'desc' => '现就本月研发人员的工时统计数据如下:',
  82. ],
  83. 2 => [
  84. 'title' => '仪器设备运行工时统计单',
  85. 'dept' => '部门:研发部、生产部',
  86. 'desc' => '现就本月研发设备的工时统计数据如下:',
  87. ]
  88. ];
  89. $c = $config[$this->type] ?? $config[1];
  90. // --- 第一行:主标题 ---
  91. $sheet->mergeCells('A1:W1');
  92. $sheet->setCellValue('A1', $c['title']);
  93. $sheet->getStyle('A1')->getFont()->setSize(12)->setBold(false)->setName('宋体');
  94. $sheet->getStyle('A1')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
  95. // --- 第二行:部门 ---
  96. $sheet->setCellValue('A2', $c['dept']);
  97. $sheet->getStyle('A2')->getFont()->setName('宋体');
  98. // --- 第三行:描述语 + Sheet名 ---
  99. if($this->type == 1){
  100. $sheet->mergeCells('A3:J3');
  101. }else{
  102. $sheet->mergeCells('A3:N3');
  103. }
  104. $sheet->setCellValue('A3', $c['desc']);
  105. $sheet->getStyle('A3')->getFont()->setName('宋体');
  106. // $sheet->mergeCells('W3:Y3');
  107. // $sheet_name = str_replace('合计','',$this->sheetName);
  108. // $sheet->setCellValue('W3', $sheet_name);
  109. // $sheet->getStyle('W3')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_RIGHT);
  110. // $sheet->getStyle('W3')->getFont()->setName('宋体');
  111. $sheet_name = str_replace('合计', '', $this->sheetName);
  112. $highestColumn = $sheet->getHighestColumn(); // 获取当前数据最大的列标,例如 'Z'
  113. // 直接在第 3 行的最右侧单元格写入
  114. $sheet->setCellValue($highestColumn . '3', $sheet_name);
  115. // 设置样式:右对齐,确保文字靠边
  116. $sheet->getStyle($highestColumn . '3')->applyFromArray([
  117. 'font' => ['name' => '宋体'],
  118. 'alignment' => [
  119. 'horizontal' => Alignment::HORIZONTAL_RIGHT,
  120. ],
  121. ]);
  122. foreach ($this->columns as $index => $colKey) {
  123. if (isset($this->timeRow[$colKey]) && $this->timeRow[$colKey] !== '') {
  124. // 根据索引获取列字母 (0->A, 1->B...)
  125. $colLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($index + 1);
  126. // 写入 A4, B4...
  127. $sheet->setCellValue($colLetter . '4', $this->timeRow[$colKey]);
  128. // 样式:小字、居中、无边框(或根据你喜好)
  129. $sheet->getStyle($colLetter . '4')->applyFromArray([
  130. 'font' => ['size' => 9, 'name' => 'Times New Roman'],
  131. 'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER],
  132. ]);
  133. }
  134. }
  135. // 也可以给第 4 行设置一个较窄的行高,让它看起来更像表头附件
  136. $sheet->getRowDimension(4)->setRowHeight(15);
  137. },
  138. ];
  139. }
  140. public function styles(Worksheet $sheet)
  141. {
  142. $sheet->getSheetView()->setZoomScale(63);
  143. $highestRow = $sheet->getHighestRow();
  144. $highestColumn = $sheet->getHighestColumn();
  145. // 样式应用范围从 A5(表头)开始到最后
  146. $dataRange = 'A5:' . $highestColumn . $highestRow;
  147. // 全表基础样式(针对数据区)
  148. $sheet->getStyle($dataRange)->applyFromArray([
  149. 'font' => ['name' => '宋体', 'size' => 12],
  150. 'alignment' => [
  151. 'vertical' => Alignment::VERTICAL_CENTER,
  152. 'horizontal' => Alignment::HORIZONTAL_CENTER,
  153. ],
  154. 'borders' => [
  155. 'allBorders' => [
  156. 'borderStyle' => Border::BORDER_THIN,
  157. ],
  158. ],
  159. ]);
  160. // 表头(第五行)特殊处理
  161. $sheet->getStyle('A5:' . $highestColumn . '5')->applyFromArray([
  162. 'borders' => [
  163. 'bottom' => ['borderStyle' => Border::BORDER_MEDIUM],
  164. ],
  165. ]);
  166. // 3. 根据 type 类型定制列宽
  167. if ($this->type === 1) {
  168. // --- 研发人员格式设置 ---
  169. $this->setEmployeeWidths($sheet);
  170. } else {
  171. // --- 研发设备格式设置 ---
  172. $this->setDeviceWidths($sheet);
  173. }
  174. return [];
  175. }
  176. /**
  177. * 研发人员列宽定制
  178. */
  179. private function setEmployeeWidths(Worksheet $sheet)
  180. {
  181. foreach ($sheet->getColumnIterator() as $column) {
  182. $col = $column->getColumnIndex();
  183. $sheet->getColumnDimension($col)->setAutoSize(false);
  184. switch ($col) {
  185. case 'A': $sheet->getColumnDimension($col)->setWidth(6); break; // 序号
  186. case 'B': $sheet->getColumnDimension($col)->setWidth(12); break; // 姓名
  187. case 'E': $sheet->getColumnDimension($col)->setWidth(25); break; // RD编号/项目名(通常较长)
  188. case 'F':
  189. case 'G':
  190. case 'H': $sheet->getColumnDimension($col)->setWidth(10); break; // 各种比例 %
  191. default: $sheet->getColumnDimension($col)->setWidth(12); break; // 动态RD列
  192. }
  193. }
  194. }
  195. /**
  196. * 研发设备列宽定制
  197. */
  198. private function setDeviceWidths(Worksheet $sheet)
  199. {
  200. foreach ($sheet->getColumnIterator() as $column) {
  201. $col = $column->getColumnIndex();
  202. $sheet->getColumnDimension($col)->setAutoSize(false);
  203. switch ($col) {
  204. case 'A': $sheet->getColumnDimension($col)->setWidth(6); break; // 序号
  205. case 'B': $sheet->getColumnDimension($col)->setWidth(20); break; // 设备名称(通常比人名长)
  206. case 'C': $sheet->getColumnDimension($col)->setWidth(15); break; // 型号/编号
  207. default: $sheet->getColumnDimension($col)->setWidth(12); break;
  208. }
  209. }
  210. }
  211. }