rabin 1 day ago
parent
commit
3025209e20
3 changed files with 325 additions and 0 deletions
  1. 19 0
      app/Lib/Core.php
  2. 178 0
      app/Lib/Export.php
  3. 128 0
      app/Lib/Import.php

+ 19 - 0
app/Lib/Core.php

@@ -0,0 +1,19 @@
+<?php namespace Excel\Lib;
+use Dever;
+set_time_limit(0);
+class Core
+{
+    protected $cell = array('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', 'AA', 'AB', 'AC', 'AD', 'AE', 'AF', 'AG', 'AH', 'AI', 'AJ', 'AK', 'AL', 'AM', 'AN', 'AO', 'AP', 'AQ', 'AR', 'AS', 'AT', 'AU', 'AV', 'AW', 'AX', 'AY', 'AZ', 'BA', 'BB', 'BC', 'BD', 'BE', 'BF', 'BG', 'BH', 'BI', 'BJ', 'BK', 'BL', 'BM', 'BN', 'BO', 'BP', 'BQ', 'BR', 'BS', 'BT', 'BU', 'BV', 'BW', 'BX', 'BY', 'BZ', 'CA', 'CB', 'CC', 'CD', 'CE', 'CF', 'CG', 'CH', 'CI', 'CJ', 'CK', 'CL', 'CM', 'CN', 'CO', 'CP', 'CQ', 'CR', 'CS', 'CT', 'CU', 'CV', 'CW', 'CX', 'CY', 'CZ', 'DA', 'DB', 'DC', 'DD', 'DE', 'DF', 'DG', 'DH', 'DI', 'DJ', 'DK', 'DL', 'DM', 'DN', 'DO', 'DP', 'DQ', 'DR', 'DS', 'DT', 'DU', 'DV', 'DW', 'DX', 'DY', 'DZ', 'EA', 'EB', 'EC', 'ED', 'EE', 'EF', 'EG', 'EH', 'EI', 'EJ', 'EK', 'EL', 'EM', 'EN', 'EO', 'EP', 'EQ', 'ER', 'ES', 'ET', 'EU', 'EV', 'EW', 'EX', 'EY', 'EZ');
+
+    protected function decimal($abc)
+    {
+        $ten = 0;
+        $len = strlen($abc);
+        for($i=1; $i<=$len; $i++){
+            $char = substr($abc,0-$i,1);//反向获取单个字符
+            $int = ord($char);
+            $ten += ($int-65)*pow(26,$i-1);
+        }
+        return $ten;
+    }
+}

+ 178 - 0
app/Lib/Export.php

@@ -0,0 +1,178 @@
+<?php namespace Excel\Lib;
+use Dever;
+Dever::apply('excel/vendor/autoload');
+use PhpOffice\PhpSpreadsheet\Cell\DataValidation;
+
+class Export extends Core
+{
+    public function act($data = array(), $header = array(), $fileName = '', $sheet = 0, $sheetName = '', $return = false, $xls = false, $password = '', $save = '')
+    {
+        if (!$xls) {
+            $xls = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
+        }
+        if ($sheet > 0) {
+            $xls->createSheet();
+        }
+        $act = $xls->setActiveSheetIndex($sheet);
+        if ($sheetName) {
+            $act->setTitle($sheetName);
+        }
+
+        $dropdownOptions = []; // 存下拉列配置
+
+        // ====== 多行表头处理 ======
+        $row = 1;
+        if ($header) {
+            $headerRowCount = count($header);
+            $colCount = max(array_map('count', $header));
+
+            for ($r = 0; $r < $headerRowCount; $r++) {
+                $rowHeader = $header[$r];
+                for ($c = 0; $c < $colCount; $c++) {
+                    $col = $this->cell[$c];
+                    $value = $rowHeader[$c] ?? null;
+                    if (is_array($value)) {
+                        $act->setCellValue($col . $row, $value[0]);
+                        $dropdownOptions[$col] = $value[1] ?? [];
+                    } else {
+                        $act->setCellValue($col . $row, $value);
+                    }
+                    $act->getColumnDimension($col)->setWidth(30);
+                }
+                $row++;
+            }
+
+            // 自动合并空单元格
+            for ($r = 0; $r < $headerRowCount; $r++) {
+                $rowHeader = $header[$r];
+                $startCol = null;
+                $mergeCount = 0;
+                for ($c = 0; $c < $colCount; $c++) {
+                    $col = $this->cell[$c];
+                    $cellVal = $act->getCell($col . ($r+1))->getValue();
+                    if ($cellVal !== null && $cellVal !== '') {
+                        if ($mergeCount > 1 && $startCol !== null) {
+                            $act->mergeCells($this->cell[$startCol] . ($r+1) . ':' . $this->cell[$c-1] . ($r+1));
+                        }
+                        $startCol = $c;
+                        $mergeCount = 1;
+                    } else {
+                        $mergeCount++;
+                    }
+                }
+                if ($mergeCount > 1 && $startCol !== null) {
+                    $act->mergeCells($this->cell[$startCol] . ($r+1) . ':' . $this->cell[$colCount-1] . ($r+1));
+                }
+            }
+        }
+
+        // ====== 数据处理 ======
+        if($data) {
+            $i = 0;
+            $height = $max = 80;
+            foreach($data as $v) {
+                $j = 0;
+                foreach($v as $cell) {
+                    $col = $this->cell[$j];
+                    $addr = $col . ($i+$row);
+
+                    $html = \Dever\Helper\Str::ishtml($cell);
+                    if ($html) {
+                        $wizard = new \PhpOffice\PhpSpreadsheet\Helper\Html;
+                        $cell = $wizard->toRichTextObject('<?xml encoding="UTF-8">' . $cell);
+                    }
+
+                    if (!$html && (strstr($cell, '.jpg') || strstr($cell, '.gif') || strstr($cell, '.png'))) {
+                        // === 图片处理逻辑保持原样 ===
+                        $key = ($i+$row);
+                        $value = false;
+
+                        if (strpos($cell, '||')) {
+                            $t = explode('||', $cell);
+                            $cell = $t[1];
+                            $value = $t[0];
+                        }
+                        $temp = explode(',', $cell);
+                        foreach ($temp as $ck => $cv) {
+                            $objDrawing[$ck] = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
+                            if (!is_file($cv)) continue;
+                            $objDrawing[$ck]->setPath($cv);
+                            $objDrawing[$ck]->setHeight($height);
+                            $objDrawing[$ck]->setCoordinates($addr);
+                            $objDrawing[$ck]->setOffsetX(12);
+                            $offsetY = ($ck == 0) ? 5 : ($offsetY + $height + 5);
+                            $objDrawing[$ck]->setOffsetY($offsetY);
+                            $objDrawing[$ck]->setWorksheet($act);
+                        }
+                        if ($value) {
+                            $act->setCellValue($addr, $value);
+                        }
+                        $th = $height * count($temp);
+                        if ($th > $max) $max = $th;
+                        $act->getRowDimension($i+$row)->setRowHeight($max);
+                    } else {
+                        if (is_numeric($cell) && mb_strlen($cell) >= 10) $cell .= "\t";
+                        $act->setCellValue($addr, $cell);
+                        $act->getStyle($addr)->getAlignment()->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
+                    }
+
+                    // ==== 下拉逻辑保持不变 ====
+                    if (!empty($dropdownOptions[$col])) {
+                        $validation = $act->getCell($addr)->getDataValidation();
+                        $validation->setType(DataValidation::TYPE_LIST);
+                        $validation->setErrorStyle(DataValidation::STYLE_INFORMATION);
+                        $validation->setAllowBlank(false);
+                        $validation->setShowInputMessage(true);
+                        $validation->setShowErrorMessage(true);
+                        $validation->setShowDropDown(true);
+                        $validation->setFormula1('"' . implode(',', $dropdownOptions[$col]) . '"');
+                    }
+
+                    $act->getColumnDimension($col)->setAutoSize(true);
+                    $act->getColumnDimension($col)->setWidth(30);
+                    $j++;
+                }
+                $i++;
+            }
+        }
+
+        // ====== 保存逻辑保持原样 ======
+        if ($header && $return) {
+            return $xls;
+        }
+        if (!$fileName) {
+            $fileName = uniqid(time(),true);
+        }
+        $fileName .= '-' . date('Ymd-His') . '-' . rand(1000,9999);
+        $write = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($xls, "Xlsx");
+        if ($save) {
+            $save = Dever::file('excel/' . $save . '.xlsx');
+            $write->save($save);
+            if ($password) {
+                Dever::apply('excel/secure/autoload');
+                $encrypt = new \Nick\SecureSpreadsheet\Encrypt();
+                $encrypt->input($save)->password($password)->output($save);
+            }
+            return $save;
+        } else {
+            if ($password) {
+                $save = Dever::file('excel/' . $save . '.xlsx');
+                $write->save($save);
+                Dever::apply('excel/secure/autoload');
+                $encrypt = new \Nick\SecureSpreadsheet\Encrypt();
+                $encrypt->input($save)->password($password)->output($save);
+                header('Content-Type: application/octet-stream');
+                header('Content-Transfer-Encoding: Binary');
+                header("Content-disposition: attachment; filename=\"" . basename($save) . "\"");
+                readfile($save);
+                @unlink($save);
+            } else {
+                ob_end_clean();
+                header('Content-Type: application/vnd.ms-excel');
+                header('pragma:public');
+                header("Content-Disposition:attachment;filename=$fileName.xlsx");
+                $write->save('php://output');
+            }
+        }
+    }
+}

+ 128 - 0
app/Lib/Import.php

@@ -0,0 +1,128 @@
+<?php
+namespace Excel\Lib;
+use Dever;
+Dever::apply('excel/vendor/autoload');
+use PhpOffice\PhpSpreadsheet\IOFactory;
+use PhpOffice\PhpSpreadsheet\RichText\RichText;
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+
+class Import extends Core
+{
+    /**
+     * 流式读取 Excel,每次返回一行
+     * @param string $file Excel 文件路径
+     * @param int $sheetIndex sheet 下标(默认 0)
+     * @param int $offset 跳过前几行
+     * @return \Generator
+     */
+    public function act($file = '', $sheetIndex = 0, $offset = 0, $head = [])
+    {
+        $file = mb_convert_encoding($file, "UTF-8", "gbk");
+        if (empty($file) || !file_exists($file)) {
+            Dever::error('file not exists!');
+        }
+
+        try {
+            $type = IOFactory::identify($file);
+            $reader = IOFactory::createReader($type);
+            //$reader->setReadDataOnly(true);
+            $spreadsheet = $reader->load($file);
+        } catch (\Exception $e) {
+            Dever::error('加载文件发生错误:"'.$file.'": ' . $e->getMessage());
+        }
+
+        $sheet = $spreadsheet->getSheet($sheetIndex);
+        $highestRow = $sheet->getHighestRow();
+        $highestCol = $sheet->getHighestColumn();
+        $columnCnt = array_search($highestCol, $this->cell) ?: 0;
+
+        $draws = $sheet->getDrawingCollection();
+        $pictures = [];
+        foreach ($draws as $img) {
+            list($col, $row) = Coordinate::coordinateFromString($img->getCoordinates());
+            $pictures[$row][$col][] = Dever::load(\Upload\Lib\Save::class)->init(1)->act($img->getPath())['url'];
+        }
+        for ($row = $offset + 1; $row <= $highestRow; $row++) {
+            $rowData = [];
+            for ($col = 0; $col <= $columnCnt; $col++) {
+                $key = $this->cell[$col];
+                if (isset($pictures[$row][$key])) {
+                    $cellValue = $pictures[$row][$key];
+                } else {
+                    $cellId = $key . $row;
+                    $cellValue = $sheet->getCell($cellId)->getCalculatedValue();
+                    if ($cellValue instanceof RichText) {
+                        $cellValue = $cellValue->__toString();
+                    }
+                }
+                if ($head && $key = Dever::issets($head, $col)) {
+                    if (is_array($key)) {
+                        $rowData[$key[0]][$key[1]] = $cellValue;
+                    } else {
+                        $rowData[$key] = $cellValue;
+                    }
+                } else {
+                    $rowData[$key] = $cellValue;
+                }
+            }
+            yield $rowData;
+        }
+    }
+}
+
+/*
+<?php namespace Excel\Lib;
+use Dever;
+Dever::apply('autoload', 'excel', 'vendor');
+class Import extends Core
+{
+    public function act($file = '', $sheet = 0, $offset = 0)
+    {
+        $file = mb_convert_encoding($file, "UTF-8", "gbk");
+        if(empty($file) OR !file_exists($file)) {
+            Dever::error('file not exists!');
+        }
+        try {
+            $type = \PhpOffice\PhpSpreadsheet\IOFactory::identify($file);
+            $read = \PhpOffice\PhpSpreadsheet\IOFactory::createReader($type);
+            //$read->setReadDataOnly(true);
+            $excel = $read->load($file);
+        } catch (\Exception $e) {
+            Dever::error('加载文件发生错误:"'.$file.'": '. $e->getMessage());
+        }
+        $sheet = $excel->getSheet($sheet);
+        $columnH = $sheet->getHighestColumn();
+        $columnCnt = array_search($columnH, $this->cell);
+        if (!$columnCnt) {
+            $columnCnt = $offset - 1;
+        }
+        if ($columnCnt < 0) {
+            $columnCnt = 0;
+        }
+        $rowCnt = $sheet->getHighestRow();
+      
+        $data = array();
+        for ($_row = 1; $_row <= $rowCnt; $_row++) {
+            for ($_column = 0; $_column <= $columnCnt; $_column++) {
+                $cellId = $this->cell[$_column].$_row;
+                //$cellValue = $sheet->getCell($cellId)->getValue();
+                $cellValue = $sheet->getCell($cellId)->getCalculatedValue();
+                if ($cellValue instanceof \PhpOffice\PhpSpreadsheet\RichText\RichText) {
+                    $cellValue = $cellValue->__toString();
+                }
+                $data[$_row][$this->cell[$_column]] = $cellValue;
+            }
+        }
+        $draws = $sheet->getDrawingCollection();
+        //处理图片
+        foreach ($draws as $img) {
+            list($startColumn, $startRow) = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::coordinateFromString($img->getCoordinates());//获取图片所在行和列
+            $pic = Dever::load('save', 'upload')->act(1, $img->getPath());
+            //插入代码
+            //$startColumn = $this->decimal($startColumn);//由于图片所在位置的列号为字母,转化为数字
+            $data[$startRow][$startColumn] = $pic['url'];
+        }
+        return $data;  
+    }
+}
+*/