<?php
<?php
namespace app\common\service;
use Generator;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Writer\Exception;
class ExcelService
{
const READ = 0;
const WRITE = 1;
private $spreadsheet = null;
private $sheet = null;
private $drawingCollection = [];
const TYPE_DRAWING = 1;
private $columns = [];
public function __construct($rw = self::WRITE, string $filename = '')
{
if ($rw === self::WRITE) {
$this->spreadsheet = new Spreadsheet();
} else {
if ($filename === '') throw new \think\Exception('缺少文件');
$this->spreadsheet = IOFactory::load($filename);
}
$this->sheet = $this->spreadsheet->getActiveSheet();
}
public function setSheet(Worksheet $sheet): ExcelService
{
$this->sheet = $sheet;
return $this;
}
public function setColumns(array $columns): ExcelService
{
$this->columns = $columns;
foreach ($this->columns as $column) {
$this->sheet->getColumnDimension($column['col'])->setWidth($column['width'] ?? 20);
$this->sheet->setCellValue($column['col'] . '1', $column['title']);
}
return $this;
}
public function setCellValues(array $values): ExcelService
{
if (empty($values)) return $this;
if (empty($this->columns)) return $this;
$index = 2;
foreach ($values as $value) {
foreach ($this->columns as $column) {
if (isset($column['type']) && $column['type'] === self::TYPE_DRAWING) {
$drawing = new Drawing();
$drawing->setWorksheet($this->sheet);
$path = $value[$column['field']] ?? '';
$drawing->setPath($path);
$drawing->setWidth(20);
$drawing->setHeight(20);
$drawing->setCoordinates($column['col'] . $index);
} else {
$this->sheet->setCellValue($column['col'] . $index, $value[$column['field']] ?? '');
}
}
++$index;
}
return $this;
}
public function exportXlsx(string $filename)
{
$filename = $filename . '-' . date('YmdHis') . '.xlsx';
header('Content-Type: application/vnd.ms-excel');
header('Content-Disposition: attachment;filename=' . urlencode($filename));
header('Cache-Control: max-age=0');
$writer = IOFactory::createWriter($this->spreadsheet, 'Xlsx');
$writer->save('php://output');
exit();
}
public function yieldData(int $rowIdx, array $columns): Generator
{
$highest = $this->sheet->getHighestRow();
while ($rowIdx <= $highest) {
$datum = [];
foreach ($columns as $col) {
$coordinate = $col['col'] . $rowIdx;
$val = $this->drawingCollection[$coordinate] ?? trim($this->sheet->getCell($coordinate)->getValue());
$datum[$col['field']] = $val;
}
++$rowIdx;
yield $datum;
}
}
public function dealImg(): ExcelService
{
$pub = public_path() . 'temp/excelimg/';
if (!is_dir($pub)) {
mkdir($pub, 0755, true);
}
foreach ($this->sheet->getDrawingCollection() as $drawing) {
$c = $drawing->getCoordinates();
list($startColumn, $startRow) = Coordinate::coordinateFromString($c);
$path = $pub . $c . mt_rand(1000, 9999) . $drawing->getFilename();
switch ($drawing->getExtension()) {
case 'jpg':
case 'jpeg':
$source = imagecreatefromjpeg($drawing->getPath());
imagejpeg($source, $path);
break;
case 'gif':
$source = imagecreatefromgif($drawing->getPath());
imagegif($source, $path);
break;
case 'png':
$source = imagecreatefrompng($drawing->getPath());
imagepng($source, $path);
break;
default:
throw new \Exception('不支持的图片格式');
}
$this->drawingCollection[] = [$startColumn . $startRow => $path];
}
return $this;
}
}
const fileDownload = (res, filename) => {
let blob = new Blob([res.data]);
if ("msSaveOrOpenBlob" in navigator) {
window.navigator.msSaveOrOpenBlob(blob, filename);
} else {
const elink = document.createElement("a");
elink.download = filename;
elink.style.display = "none";
elink.href = URL.createObjectURL(blob);
if (typeof elink.download === "undefined") {
elink.setAttribute("target", "_blank");
}
document.body.appendChild(elink);
elink.click();
URL.revokeObjectURL(elink.href);
document.body.removeChild(elink);
}
};
export function _download(url, formData, errorback) {
return new Promise((resolve, reject) => {
axios
.post(url, formData, { responseType: "blob" })
.then(res => {
const fileName = decodeURI(
res.headers["content-disposition"].match(/filename=(.*)/)[1]
);
if (res.status === 200) {
fileDownload(res, fileName);
}
})
.catch(error => {
reject(error);
});
});
}