選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

SpreadsheetExcelService.php 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. <?php
  2. namespace crmeb\services;
  3. use crmeb\utils\Arr;
  4. use PhpOffice\PhpSpreadsheet\IOFactory;
  5. class SpreadsheetExcelService
  6. {
  7. //
  8. private static $instance = null;
  9. //PHPSpreadsheet实例化对象
  10. private static $spreadsheet = null;
  11. //sheet实例化对象
  12. private static $sheet = null;
  13. //表头计数
  14. protected static $count;
  15. //表头占行数
  16. protected static $topNumber = 3;
  17. //表能占据表行的字母对应self::$cellkey
  18. protected static $cells;
  19. //表头数据
  20. protected static $data = [];
  21. //文件名
  22. protected static $title = '订单导出';
  23. //行宽
  24. protected static $width = 20;
  25. //行高
  26. protected static $height = 50;
  27. //保存文件目录
  28. protected static $path = '/phpExcel/';
  29. //设置style
  30. private static $styleArray = [
  31. // 'borders' => [
  32. // 'allBorders' => [
  33. // // PHPExcel_Style_Border里面有很多属性,想要其他的自己去看
  34. // // 'style' => \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THICK,//边框是粗的
  35. // // 'style' => \PHPExcel_Style_Border::BORDER_DOUBLE,//双重的
  36. // // 'style' => \PHPExcel_Style_Border::BORDER_HAIR,//虚线
  37. // // 'style' => \PHPExcel_Style_Border::BORDER_MEDIUM,//实粗线
  38. // // 'style' => \PHPExcel_Style_Border::BORDER_MEDIUMDASHDOT,//虚粗线
  39. // // 'style' => \PHPExcel_Style_Border::BORDER_MEDIUMDASHDOTDOT,//点虚粗线
  40. // 'style' => \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN,//细边框
  41. // // 'color' => ['argb' => 'FFFF0000'],
  42. // ],
  43. // ],
  44. 'font' => [
  45. 'bold' => true
  46. ],
  47. 'alignment' => [
  48. 'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
  49. 'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER
  50. ]
  51. ];
  52. private function __construct()
  53. {
  54. }
  55. private function __clone()
  56. {
  57. }
  58. public static function instance()
  59. {
  60. if (self::$instance === null) {
  61. self::$instance = new self();
  62. self::$spreadsheet = $spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
  63. self::$sheet = $spreadsheet->getActiveSheet();
  64. }
  65. return self::$instance;
  66. }
  67. /**
  68. * 设置字符串字符集
  69. * @param string $str 需要设置字符集的字符串
  70. * @return string
  71. */
  72. public function utf8String($str)
  73. {
  74. $str = is_object($str) ? $str->__toString() : $str;
  75. $encode = mb_detect_encoding($str, ["ASCII", 'UTF-8', "GB2312", "GBK", 'BIG5', 'CP936']);
  76. if (strtoupper($encode) != 'UTF-8') $str = mb_convert_encoding($str, 'utf-8', $encode);
  77. return $str;
  78. }
  79. /**
  80. * 创建保存excel目录
  81. * return string
  82. */
  83. public static function savePath()
  84. {
  85. if (!is_dir(self::$path)) {
  86. if (mkdir(self::$path, 0700) == false) {
  87. return false;
  88. }
  89. }
  90. //年月一级目录
  91. $mont_path = self::$path . date('Ym');
  92. if (!is_dir($mont_path)) {
  93. if (mkdir($mont_path, 0700) == false) {
  94. return false;
  95. }
  96. }
  97. //日二级目录
  98. $day_path = $mont_path . '/' . date('d');
  99. if (!is_dir($day_path)) {
  100. if (mkdir($day_path, 0700) == false) {
  101. return false;
  102. }
  103. }
  104. return $day_path;
  105. }
  106. /**
  107. * 设置标题
  108. * @param $title string || array ['title'=>'','name'=>'','info'=>[]]
  109. * @param $Name string
  110. * @param $info string || array;
  111. * @param $funName function($style,$A,$A2) 自定义设置头部样式
  112. * @return $this
  113. */
  114. public function setExcelTile($title = '', $Name = '', $info = [], $funName = null)
  115. {
  116. //设置参数
  117. if (is_array($title)) {
  118. if (isset($title['title'])) $title = $title['title'];
  119. if (isset($title['name'])) $Name = $title['name'];
  120. if (isset($title['info'])) $info = $title['info'];
  121. }
  122. if (empty($title))
  123. $title = self::$title;
  124. else
  125. self::$title = $title;
  126. if (empty($Name)) $Name = time();
  127. //设置Excel属性
  128. self::$spreadsheet->getProperties()
  129. ->setCreator("Neo")
  130. ->setLastModifiedBy("Neo")
  131. ->setTitle($this->utf8String($title))
  132. ->setSubject($Name)
  133. ->setDescription("")
  134. ->setKeywords($Name)
  135. ->setCategory("");
  136. self::$sheet->setTitle($Name);
  137. self::$sheet->setCellValue('A1', $title);
  138. self::$sheet->setCellValue('A2', self::setCellInfo($info));
  139. //文字居中
  140. self::$sheet->getStyle('A1')->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
  141. self::$sheet->getStyle('A2')->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
  142. //合并表头单元格
  143. self::$sheet->mergeCells('A1:' . self::$cells . '1');
  144. self::$sheet->mergeCells('A2:' . self::$cells . '2');
  145. self::$sheet->getRowDimension(1)->setRowHeight(40);
  146. self::$sheet->getRowDimension(2)->setRowHeight(20);
  147. //设置表头字体
  148. self::$sheet->getStyle('A1')->getFont()->setName('黑体');
  149. self::$sheet->getStyle('A1')->getFont()->setSize(20);
  150. self::$sheet->getStyle('A1')->getFont()->setBold(true);
  151. self::$sheet->getStyle('A2')->getFont()->setName('宋体');
  152. self::$sheet->getStyle('A2')->getFont()->setSize(14);
  153. self::$sheet->getStyle('A2')->getFont()->setBold(true);
  154. self::$sheet->getStyle('A3:' . self::$cells . '3')->getFont()->setBold(true);
  155. return $this;
  156. }
  157. /**
  158. * 设置第二行标题内容
  159. * @param $info array (['name'=>'','site'=>'','phone'=>123] || ['我是表名','我是地址','我是手机号码'] ) || string 自定义
  160. * @return string
  161. */
  162. private static function setCellInfo($info)
  163. {
  164. $content = ['操作者:', '导出日期:' . date('Y-m-d', time()), '地址:', '电话:'];
  165. if (is_array($info) && !empty($info)) {
  166. if (isset($info['name'])) {
  167. $content[0] .= $info['name'];
  168. } else {
  169. $content[0] .= isset($info[0]) ? $info[0] : '';
  170. }
  171. if (isset($info['site'])) {
  172. $content[2] .= $info['site'];
  173. } else {
  174. $content[2] .= isset($info[1]) ? $info[1] : '';
  175. }
  176. if (isset($info['phone'])) {
  177. $content[3] .= $info['phone'];
  178. } else {
  179. $content[3] .= isset($info[2]) ? $info[2] : '';
  180. }
  181. return implode(' ', $content);
  182. } else if (is_string($info)) {
  183. return empty($info) ? implode(' ', $content) : $info;
  184. }
  185. }
  186. /**
  187. * 设置头部信息
  188. * @param $data array
  189. * @return $this
  190. */
  191. public function setExcelHeader($data)
  192. {
  193. $span = 'A';
  194. foreach ($data as $key => $value) {
  195. self::$sheet->getColumnDimension($span)->setWidth(self::$width);
  196. self::$sheet->setCellValue($span . self::$topNumber, $value);
  197. $span++;
  198. }
  199. self::$sheet->getRowDimension(3)->setRowHeight(self::$height);
  200. self::$cells = $span;
  201. return $this;
  202. }
  203. /**
  204. * 读取表格内的文件数据
  205. * @param string $filePath
  206. * @param array $cellsData
  207. * @param callable $closure
  208. * @param int $startLine
  209. * @return array
  210. * @throws \PhpOffice\PhpSpreadsheet\Exception
  211. * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
  212. */
  213. public static function getExcelData(string $filePath, array $cellsData = [], callable $closure = null, int $startLine = 2)
  214. {
  215. if (!file_exists($filePath)) {
  216. throw new \RuntimeException('文件不存在');
  217. }
  218. $extension = ucwords(pathinfo($filePath, PATHINFO_EXTENSION));
  219. $io = IOFactory::createreader($extension);
  220. $spreadsheet = $io->load($filePath);
  221. $worksheet = $spreadsheet->getActiveSheet();
  222. $highestRow = $worksheet->getHighestRow();
  223. $data = [];
  224. if ($closure) {
  225. $closure($worksheet);
  226. }
  227. for ($j = $startLine; $j <= (int)$highestRow; $j++) {
  228. $value = [];
  229. foreach ($cellsData as $key => $val) {
  230. if ($v = $worksheet->getCell($val . $j)->getValue()) {
  231. $value[$key] = $v;
  232. }
  233. }
  234. if ($value) {
  235. $data[] = $value;
  236. }
  237. }
  238. return Arr::filterValue($data);
  239. }
  240. /**
  241. *
  242. * execl数据导出
  243. * @param $data 需要导出的数据 格式和以前的可是一样
  244. *
  245. * 特殊处理:合并单元格需要先对数据进行处理
  246. */
  247. public function setExcelContent($data = [])
  248. {
  249. if (!empty($data) && is_array($data)) {
  250. $column = self::$topNumber + 1;
  251. // 行写入
  252. foreach ($data as $key => $rows) {
  253. $span = 'A';
  254. // 列写入
  255. foreach ($rows as $keyName => $value) {
  256. self::$sheet->setCellValue($span . $column, $this->utf8String($value));
  257. $span++;
  258. }
  259. $column++;
  260. }
  261. self::$sheet->getDefaultRowDimension()->setRowHeight(self::$height);
  262. //设置内容字体样式
  263. self::$sheet->getStyle('A1:' . $span . $column)->applyFromArray(self::$styleArray);
  264. //设置边框
  265. self::$sheet->getStyle('A1:' . $span . ($column - 1))->getBorders()->getAllBorders()->setBorderStyle(\PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN);
  266. //设置自动换行
  267. self::$sheet->getStyle('A4:' . $span . $column)->getAlignment()->setWrapText(true);
  268. }
  269. return $this;
  270. }
  271. /**
  272. * 保存表格数据,直接下载
  273. * @param $filename 文件名称
  274. * @param $suffix 文件后缀名
  275. * @param $is_save 是否保存文件
  276. * @return 保存文件:return string
  277. */
  278. public function excelSave($fileName = '', $suffix = 'xlsx', $is_save = false)
  279. {
  280. if (empty($fileName)) {
  281. $fileName = date('YmdHis') . time();
  282. }
  283. if (empty($suffix)) {
  284. $suffix = 'xlsx';
  285. }
  286. // 重命名表(UTF8编码不需要这一步)
  287. if (mb_detect_encoding($fileName) != "UTF-8") {
  288. $fileName = iconv("utf-8", "gbk//IGNORE", $fileName);
  289. }
  290. $spreadsheet = self::$spreadsheet;
  291. $writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
  292. if (!$is_save) {//直接下载
  293. header('Content-Disposition: attachment;filename="' . $fileName . '.' . $suffix . '"');
  294. header('Cache-Control: max-age=0');
  295. $writer->save('php://output');
  296. // 删除清空 释放内存
  297. // $spreadsheet->disconnectWorksheets();
  298. unset($spreadsheet);
  299. } else {//保存文件
  300. $root_path = app()->getRootPath() . 'public/phpExcel';
  301. if (!is_dir($root_path)) mkdir($root_path, 0700, true);
  302. $path = $root_path . '/' . $fileName . '.' . $suffix;
  303. //$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Xlsx');
  304. //$writer->save($path);
  305. $writer->save($path);
  306. // 删除清空 释放内存
  307. // $spreadsheet->disconnectWorksheets();
  308. unset($spreadsheet);
  309. return '/phpExcel/' . $fileName . '.' . $suffix;
  310. }
  311. }
  312. }