SpreadsheetExcelService.php 12 KB

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