SpreadsheetExcelService.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. <?php
  2. namespace ln\services;
  3. use think\exception\ValidateException;
  4. class SpreadsheetExcelService
  5. {
  6. //
  7. private static $instance = null;
  8. //PHPSpreadsheet实例化对象
  9. private static $spreadsheet = null;
  10. //sheet实例化对象
  11. private static $sheet = null;
  12. private static $createsheet = 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. //总行数
  30. protected static $colum = 3;
  31. //设置style
  32. private static $styleArray = [
  33. // 'borders' => [
  34. // 'allBorders' => [
  35. // // PHPExcel_Style_Border里面有很多属性,想要其他的自己去看
  36. // // 'style' => \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THICK,//边框是粗的
  37. // // 'style' => \PHPExcel_Style_Border::BORDER_DOUBLE,//双重的
  38. // // 'style' => \PHPExcel_Style_Border::BORDER_HAIR,//虚线
  39. // // 'style' => \PHPExcel_Style_Border::BORDER_MEDIUM,//实粗线
  40. // // 'style' => \PHPExcel_Style_Border::BORDER_MEDIUMDASHDOT,//虚粗线
  41. // // 'style' => \PHPExcel_Style_Border::BORDER_MEDIUMDASHDOTDOT,//点虚粗线
  42. // 'style' => \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN,//细边框
  43. // // 'color' => ['argb' => 'FFFF0000'],
  44. // ],
  45. // ],
  46. 'font' => [
  47. 'bold' => true
  48. ],
  49. 'alignment' => [
  50. 'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
  51. 'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER
  52. ]
  53. ];
  54. private function __construct(){}
  55. private function __clone(){}
  56. public static function instance()
  57. {
  58. if (self::$instance === null) {
  59. self::$instance = new self();
  60. self::$spreadsheet = $spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
  61. }
  62. return self::$instance;
  63. }
  64. public function createOrActive($i = null)
  65. {
  66. if($i){
  67. self::$sheet = self::$spreadsheet->createSheet();
  68. }else{
  69. self::$sheet = self::$spreadsheet->getActiveSheet();
  70. }
  71. return $this;
  72. }
  73. /**
  74. *设置字体格式
  75. * @param $title string 必选
  76. * return string
  77. */
  78. public static function setUtf8($title)
  79. {
  80. return iconv('utf-8', 'gb2312', $title);
  81. }
  82. /**
  83. * 创建保存excel目录
  84. * return string
  85. */
  86. public static function savePath()
  87. {
  88. if(!is_dir(self::$path)){
  89. if (mkdir(self::$path, 0700) == false) {
  90. return false;
  91. }
  92. }
  93. //年月一级目录
  94. $mont_path = self::$path.date('Ym');
  95. if(!is_dir($mont_path)){
  96. if (mkdir($mont_path, 0700) == false) {
  97. return false;
  98. }
  99. }
  100. //日二级目录
  101. $day_path = $mont_path.'/'.date('d');
  102. if(!is_dir($day_path)){
  103. if (mkdir($day_path, 0700) == false) {
  104. return false;
  105. }
  106. }
  107. return $day_path;
  108. }
  109. /**
  110. * 设置标题
  111. * @param $title string || array ['title'=>'','name'=>'','info'=>[]]
  112. * @param $Name string
  113. * @param $info string || array;
  114. * @param $funName function($style,$A,$A2) 自定义设置头部样式
  115. * @return $this
  116. */
  117. public function setExcelTile(array $data)
  118. {
  119. //设置参数
  120. if (is_array($data)) {
  121. if (isset($data['title'])) $title = $data['title'];
  122. if (isset($data['sheets'])) $sheets = $data['sheets'];
  123. }
  124. empty($title) ? $title = self::$title : self::$title = $title;
  125. if (empty($sheets)) $sheets = time();
  126. //设置Excel属性
  127. self::$spreadsheet->getProperties()
  128. ->setCreator("Neo")
  129. ->setLastModifiedBy("Neo")
  130. ->setTitle(self::setUtf8($title))
  131. ->setSubject($sheets)
  132. ->setDescription("")
  133. ->setKeywords($sheets)
  134. ->setCategory("");
  135. self::$sheet->setTitle($sheets);
  136. self::$sheet->mergeCells('A1:' . self::$cells . '1'); //合并表头单元格
  137. self::$sheet->getRowDimension('A')->setRowHeight(40); //设置行高
  138. self::$sheet->setCellValue('A1', $title); //负值
  139. self::$sheet->getStyle('A1')->getFont()->setName('黑体');
  140. self::$sheet->getStyle('A1')->getFont()->setSize(20);
  141. self::$sheet->getStyle('A1')->getFont()->setBold(true);
  142. self::$sheet->getStyle('A1')->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER); //设置左对齐
  143. if(isset($data['mark']) && !empty($data['mark'])){
  144. foreach ($data['mark'] as $k => $v){
  145. $i = $k + 2;
  146. self::$sheet->mergeCells('A'.$i.':' . self::$cells . $i);
  147. self::$sheet->setCellValue('A'.$i, $v);
  148. self::$sheet->getStyle('A'.$i)->getFont()->setName('宋体');
  149. self::$sheet->getStyle('A'.$i)->getFont()->setSize(16);
  150. self::$sheet->getStyle('A'.$i)->getFont()->setBold(true);
  151. self::$sheet->getStyle('A'.$i)->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_LEFT);
  152. }
  153. }
  154. return $this;
  155. }
  156. /**
  157. * 设置第二行标题内容
  158. * @param $info array (['name'=>'','site'=>'','phone'=>123] || ['我是表名','我是地址','我是手机号码'] ) || string 自定义
  159. * @return string
  160. */
  161. private static function setCellInfo($info)
  162. {
  163. $content = ['操作者:', '导出日期:' . date('Y-m-d', time()), '地址:', '电话:'];
  164. if (is_array($info) && !empty($info)) {
  165. if (isset($info['name'])) {
  166. $content[0] .= $info['name'];
  167. } else {
  168. $content[0] .= isset($info[0]) ? $info[0] : '';
  169. }
  170. if (isset($info['site'])) {
  171. $content[2] .= $info['site'];
  172. } else {
  173. $content[2] .= isset($info[1]) ? $info[1] : '';
  174. }
  175. if (isset($info['phone'])) {
  176. $content[3] .= $info['phone'];
  177. } else {
  178. $content[3] .= isset($info[2]) ? $info[2] : '';
  179. }
  180. return implode(' ', $content);
  181. } else if (is_string($info)) {
  182. return empty($info) ? implode(' ', $content) : $info;
  183. }
  184. }
  185. /**
  186. * 设置头部信息
  187. * @param $data array
  188. * @return $this
  189. */
  190. public function setExcelHeader($data,$topNumber)
  191. {
  192. $span = 'A';
  193. self::$topNumber = $topNumber;
  194. foreach ($data as $key => $value) {
  195. self::$sheet->getColumnDimension($span)->setWidth(self::$width);
  196. self::$sheet->setCellValue($span.self::$topNumber, $value);
  197. self::$sheet->getStyle($span.self::$topNumber)->getFont()->setSize(16);
  198. $span++;
  199. }
  200. $span = chr(ord($span) -1);
  201. self::$sheet->getRowDimension(self::$topNumber)->setRowHeight(25);
  202. self::$sheet->getStyle('A1:' . $span.self::$topNumber)->getBorders()->getAllBorders()->setBorderStyle(\PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN);
  203. self::$cells = $span;
  204. return $this;
  205. }
  206. /**
  207. *
  208. * execl数据导出
  209. * @param $data 需要导出的数据 格式和以前的可是一样
  210. *
  211. * 特殊处理:合并单元格需要先对数据进行处理
  212. */
  213. public function setExcelContent($data = [])
  214. {
  215. if (!empty($data) && is_array($data)) {
  216. $column = self::$topNumber+1;
  217. // 行写入
  218. foreach ($data as $key => $rows) {
  219. $span = 'A';
  220. // 列写入
  221. foreach ($rows as $keyName => $value) {
  222. self::$sheet->setCellValue($span . $column, $value);
  223. $span++;
  224. }
  225. $column++;
  226. }
  227. $span = chr(ord($span) -1);
  228. self::$colum = $column;
  229. self::$sheet->getDefaultRowDimension()->setRowHeight(self::$height);
  230. //设置内容字体样式
  231. self::$sheet->getStyle('A'.self::$topNumber .':'. $span.$column)->applyFromArray(self::$styleArray);
  232. //设置边框
  233. self::$sheet->getStyle('A1:' . $span.$column )->getBorders()->getAllBorders()->setBorderStyle(\PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN);
  234. //设置自动换行
  235. self::$sheet->getStyle('A4:' . $span.$column)->getAlignment()->setWrapText(true);
  236. }
  237. return $this;
  238. }
  239. public function setExcelEnd(array $data)
  240. {
  241. if(!empty($data)){
  242. foreach ($data as $key => $value){
  243. $i = self::$colum + $key ;
  244. self::$sheet->mergeCells('A'.$i.':' . self::$cells.$i);
  245. self::$sheet->setCellValue('A'.$i, $value);
  246. self::$sheet->getStyle('A'.$i)->getFont()->setName('宋体');
  247. self::$sheet->getStyle('A'.$i)->getFont()->setSize(16);
  248. self::$sheet->getStyle('A'.$i)->getFont()->setBold(true);
  249. self::$sheet->getStyle('A'.$i)->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_LEFT);
  250. }
  251. self::$sheet->getStyle('A1:' .self::$cells.$i)->getBorders()->getAllBorders()->setBorderStyle(\PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN);
  252. }
  253. return $this;
  254. }
  255. /**
  256. * 保存表格数据
  257. * @param $filename 文件名称
  258. * @param $suffix 文件后缀名
  259. * @param $path 是否保存文件文件
  260. * @return 保存文件:return string
  261. */
  262. public function excelSave($fileName = '',$suffix = 'xlsx',$path)
  263. {
  264. if(empty($fileName)) $fileName = date('YmdHis').time();
  265. if(empty($suffix)) $suffix = 'xlsx';
  266. // 重命名表(UTF8编码不需要这一步)
  267. if (mb_detect_encoding($fileName) != "UTF-8") $fileName = iconv("utf-8", "gbk//IGNORE", $fileName);
  268. $save_path = self::$path.$path;
  269. $root_path = app()->getRootPath().'public/'.$save_path;
  270. if(!is_dir($root_path)) mkdir($root_path, 0700,true);
  271. $spreadsheet = self::$spreadsheet;
  272. $writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
  273. $writer->save($root_path.'/'.$fileName.'.'.$suffix);
  274. return $save_path.'/'.$fileName.'.'.$suffix;
  275. }
  276. /**
  277. * TODO
  278. * @param $filePath 文件路径
  279. * @param array $sql 需要入库的字段 => excel表的列 例 [order_sn => 'B']
  280. * @param array $where 每条入库的条件 同上
  281. * @param int $startRow 有效数据从第几行开始
  282. * @return array
  283. * @author Qinii
  284. * @day 3/15/21
  285. */
  286. public function _import($filePath,array $sql,$where = [],$startRow = 1)
  287. {
  288. if(!file_exists($filePath)) return ;
  289. $ext = ucfirst(pathinfo($filePath, PATHINFO_EXTENSION));
  290. $ret = [];
  291. if(in_array($ext,['Xlsx','Xls'])){
  292. $reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader($ext);
  293. $spreadsheet = $reader->load($filePath);
  294. $sheet = $spreadsheet->getActiveSheet();
  295. $row_count = $sheet->getHighestDataRow();//取得总行数
  296. for ($row = $startRow; $row <= $row_count; $row++){
  297. $con = [];
  298. $item = [];
  299. $one = [];
  300. if(!empty($where)){
  301. foreach ($where as $k => $v){
  302. $con_value = $sheet->getCell($v.$row)->getValue();
  303. if($con_value){
  304. $con[$k] = $con_value;
  305. }
  306. }
  307. if(!empty($con))$one['where'] = $con;
  308. }
  309. if(!empty($sql)){
  310. foreach ($sql as $key => $value){
  311. $key_value = $sheet->getCell($value.$row)->getValue();
  312. if($key_value) {
  313. $item[$key] = $key_value;
  314. }
  315. }
  316. if(!empty($item))$one['value'] = $item;
  317. }
  318. if($one)$ret[] = $one;
  319. }
  320. }
  321. return $ret;
  322. }
  323. /**
  324. * TODO 检测导入格式
  325. * @param $filePath
  326. * @param array $check
  327. * @return bool
  328. * @author Qinii
  329. * @day 5/7/21
  330. */
  331. public function checkImport($filePath,$check = [])
  332. {
  333. $ext = ucfirst(pathinfo($filePath, PATHINFO_EXTENSION));
  334. $reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader($ext);
  335. $spreadsheet = $reader->load($filePath);
  336. $sheet = $spreadsheet->getActiveSheet();
  337. if(!empty($check)){
  338. foreach ($check as $s => $c){
  339. $_c = $sheet->getCell($s)->getValue();
  340. if($_c !== $c) throw new ValidateException('表格"'.$s.'"不是"'.$c.'"不可导入');
  341. }
  342. }
  343. return true;
  344. }
  345. }