Excel.php 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. <?php
  2. namespace app\common\service\excel;
  3. use PhpOffice\PhpSpreadsheet\Spreadsheet;
  4. use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
  5. use PhpOffice\PhpSpreadsheet\Cell\DataType;
  6. use PhpOffice\PhpSpreadsheet\Style\Alignment;
  7. use PhpOffice\PhpSpreadsheet\IOFactory;
  8. /**
  9. * @notes excel相关操作
  10. * composer require phpoffice/phpspreadsheet 或者 php composer.phar require phpoffice/phpspreadsheet
  11. * author lbzy
  12. * @datetime 2020-10-11 00:25:03
  13. * @class Excel
  14. * @package app\common\server\excel
  15. */
  16. class Excel
  17. {
  18. /**
  19. * 导出数据到excel
  20. * @param $data mixed 数据集|cursor对象 格式为select出来的格式 如: model('user')->select()
  21. * @param array $options 输出配置 array(
  22. * [ 'title'=>'名字', 'field'=>'name' ],
  23. * [ 'title'=>'邮箱', 'field'=>'email', 'callback'=>'aaaaa' ],
  24. * );
  25. * @param string $sheet_title 工作表标题
  26. * @param string $merge_title 第一行合并单元格里面的标题 留空则无标题
  27. * @param string $filename 导出的文件的名称,留空则默认为时间
  28. */
  29. static function out($data, array $options, string $sheet_title = '', string $merge_title = '', string $filename = '', string $save_path = null)
  30. {
  31. set_time_limit(0);
  32. $filename = $filename ? : (date('Ymd.H.i.s'));
  33. $options = array_values($options);
  34. // Create new Spreadsheet object
  35. $spreadsheet = new Spreadsheet();
  36. $sheet = $spreadsheet->getActiveSheet();
  37. $sheet->setTitle($sheet_title ? : '未命名sheet');
  38. // 列
  39. $columns = self::get2Cols();
  40. $options_length = count($options) - 1;
  41. $row = 1;
  42. // 合并第一行并设置标题和居中
  43. if ($merge_title) {
  44. $sheet->mergeCells('A1:' . $columns[$options_length] . '1')->setCellValue('A' . $row, $merge_title);
  45. $sheet->getStyle('A1')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
  46. $row++;
  47. }
  48. foreach($options as $ko => $option) {
  49. $sheet->getColumnDimension($columns[$ko])->setAutoSize(true);
  50. $column = $columns[$ko];
  51. $cell_area = $column . $row;
  52. // 设置第二行为备注名
  53. $cell_value = $option['title'] ?? '';
  54. $sheet->setCellValue($cell_area, $cell_value);
  55. $sheet->getColumnDimension($column)->setAutoSize(true);
  56. }
  57. $row++;
  58. // 循环插入数据
  59. $column_merges = [];
  60. foreach ($data as $key => $dataInfo) {
  61. foreach ($options as $ko => $option) {
  62. $column = $columns[$ko];
  63. $cell_area = "{$column}{$row}";
  64. // 单元格置值
  65. $cell_value = self::out_cell_value($dataInfo, $option);
  66. // 设单元格置值
  67. if (isset($option['excel_data_type'])) {
  68. $sheet->setCellValueExplicit($cell_area, $cell_value, $option['excel_data_type']);
  69. // $sheet->setCellValueExplicit($cell_area, $cell_value, DataType::TYPE_STRING);
  70. } else {
  71. $sheet->setCellValue($cell_area, $cell_value);
  72. }
  73. // 设置居中
  74. // $sheet->getStyle($cell_area)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
  75. // 是否列合并
  76. if (($option['column_merge'] ?? false)) {
  77. //
  78. if (isset($column_merges[$ko])) {
  79. $column_merge_length = count($column_merges[$ko]);
  80. if ($cell_value != $column_merges[$ko][$column_merge_length - 1]) {
  81. // 长度大于1则 合并并垂直居中
  82. if ($column_merge_length > 1) {
  83. $cell_merge_area = $column . ($row - $column_merge_length) . ':' . $column . ($row - 1);
  84. $sheet->mergeCells($cell_merge_area)->getStyle($cell_merge_area)->getAlignment()->setVertical(Alignment::VERTICAL_CENTER);;
  85. }
  86. // 清除已合并的数据
  87. unset($column_merges[$ko]);
  88. }
  89. }
  90. // 添加数据存储
  91. $column_merges[$ko][] = $cell_value;
  92. }
  93. }
  94. $row++;
  95. }
  96. $writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
  97. if (is_null($save_path)) {
  98. // Redirect output to a client’s web browser (Xlsx)
  99. header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
  100. header('Content-Disposition: attachment;filename="' . $filename . '.xlsx"');
  101. header('Cache-Control: max-age=0');
  102. // If you're serving to IE 9, then the following may be needed
  103. // header('Cache-Control: max-age=1');
  104. // If you're serving to IE over SSL, then the following may be needed
  105. // header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past
  106. // header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); // always modified
  107. // header('Cache-Control: cache, must-revalidate'); // HTTP/1.1
  108. // header('Pragma: public'); // HTTP/1.0
  109. $writer->save('php://output');
  110. } else {
  111. $writer->save($save_path . $filename . '.xlsx');
  112. }
  113. }
  114. static function get2Cols()
  115. {
  116. $a = $b = $res = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'];
  117. foreach ($a as $key => $col1) {
  118. foreach ($b as $ko => $col2) {
  119. $res[] = $col1 . $col2;
  120. }
  121. }
  122. return $res;
  123. }
  124. static function out_cell_value($dataInfo, $option)
  125. {
  126. $cell_value = '';
  127. if (isset($option['field'])) {
  128. $cell_value = $dataInfo[$option['field']] ?? '';
  129. }
  130. // callback 自定义单元格置值 返回什么都按字符串处理
  131. $callback = $option['callback'] ?? null;
  132. is_callable($callback) && $cell_value = call_user_func_array($callback, [ $dataInfo, $option ]);
  133. return $cell_value;
  134. }
  135. static function load_file($filename)
  136. {
  137. $spreadsheet = IOFactory::load($filename);
  138. // $reader->setReadDataOnly(true);
  139. return $spreadsheet->getActiveSheet()->toArray(null, true, true, true);
  140. }
  141. }