Browse Source

excel导入导出完成

master
李可松 4 years ago
parent
commit
1879f14b31
  1. 4
      app/AdminSupplier/Controllers/ProductExportLogController.php
  2. 18
      app/AdminSupplier/Extensions/ProductToExcelExporter.php
  3. 38
      app/Exports/ProductExport.php
  4. 57
      app/Exports/ProductSpecExport.php
  5. 6
      app/Imports/ProductImport.php
  6. 7
      app/Imports/ProductSpecImport.php
  7. 56
      app/Jobs/ExportProductToExcel.php
  8. 5
      app/Jobs/ImportProductFromExcel.php

4
app/AdminSupplier/Controllers/ProductExportLogController.php

@ -26,12 +26,12 @@ class ProductExportLogController extends AdminController
$grid->model()->where('supplier_id', \Admin::user()->id)->orderByDesc('id'); $grid->model()->where('supplier_id', \Admin::user()->id)->orderByDesc('id');
$grid->header(Alert::make('提示:只要导出全部才会在此列表显示。导出全部产品可能需要几分钟,请稍后再刷新查看!')->info());
$grid->header(Alert::make('提示:导出产品及规格可能需要几分钟,请稍后再刷新查看!')->info());
\Admin::style('.alert.alert-info{margin-top:1rem;}'); \Admin::style('.alert.alert-info{margin-top:1rem;}');
$grid->column('id')->sortable(); $grid->column('id')->sortable();
$grid->column('filename')->downloadable(); $grid->column('filename')->downloadable();
$grid->column('created_at');
$grid->column('created_at', '导出时间');
$grid->filter(function (Grid\Filter $filter) { $grid->filter(function (Grid\Filter $filter) {
$filter->equal('id')->width(2); $filter->equal('id')->width(2);

18
app/AdminSupplier/Extensions/ProductToExcelExporter.php

@ -1,11 +1,8 @@
<?php <?php
namespace App\AdminSupplier\Extensions; namespace App\AdminSupplier\Extensions;
use App\Exports\ProductExport;
use App\Jobs\ExportProductToExcel; use App\Jobs\ExportProductToExcel;
use Dcat\Admin\Admin;
use Dcat\Admin\Grid\Exporters\AbstractExporter; use Dcat\Admin\Grid\Exporters\AbstractExporter;
use Maatwebsite\Excel\Facades\Excel;
/** /**
* Excel导出功能 * Excel导出功能
@ -15,14 +12,17 @@ class ProductToExcelExporter extends AbstractExporter
public function export() public function export()
{ {
$export_type = request('_export_'); $export_type = request('_export_');
if ($export_type == 'all') { if ($export_type == 'all') {
ExportProductToExcel::dispatch(\Admin::user()->id);
return redirect(admin_url('product/export'))->send();
$_export_ = 'all';
} else if (str_starts_with($export_type, 'selected:')) { } else if (str_starts_with($export_type, 'selected:')) {
$ids = explode(',', substr($export_type, strlen('selected:')));
$ids = array_filter($ids, fn($v) => preg_match('/^\d+$/', $v));
return Excel::download(new ProductExport(Admin::user()->id, $ids), '导出选择产品-' . date('Y-m-d H:i:s') . '.xlsx')->send();
$_export_ = explode(',', substr($export_type, strlen('selected:')));
$_export_ = array_filter($_export_, fn($v) => preg_match('/^\d+$/', $v));
} else {
return false;
} }
ExportProductToExcel::dispatch(\Admin::user()->id, $_export_);
return redirect(admin_url('product/export'))->send();
} }
} }

38
app/Exports/ProductExport.php

@ -4,6 +4,7 @@ namespace App\Exports;
use App\Common\ProductStatus; use App\Common\ProductStatus;
use App\Models\Product; use App\Models\Product;
use Illuminate\Support\Facades\Storage;
use Maatwebsite\Excel\Concerns\FromQuery; use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\WithColumnFormatting; use Maatwebsite\Excel\Concerns\WithColumnFormatting;
use Maatwebsite\Excel\Concerns\WithHeadings; use Maatwebsite\Excel\Concerns\WithHeadings;
@ -12,12 +13,14 @@ use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
class ProductExport implements FromQuery, WithHeadings, WithColumnFormatting class ProductExport implements FromQuery, WithHeadings, WithColumnFormatting
{ {
private int $supplier_id; private int $supplier_id;
private string|array|null $_export_ = null;
private string|array|null $_export_;
private string $export_dir;
public function __construct(int $supplier_id, string|array $_export_ = null)
public function __construct(int $supplier_id, string|array $_export_, string $export_dir)
{ {
$this->supplier_id = $supplier_id; $this->supplier_id = $supplier_id;
$this->_export_ = $_export_; $this->_export_ = $_export_;
$this->export_dir = $export_dir;
} }
public function query() public function query()
@ -27,13 +30,24 @@ class ProductExport implements FromQuery, WithHeadings, WithColumnFormatting
} else if (!empty($this->_export_) && is_array($this->_export_)) { } else if (!empty($this->_export_) && is_array($this->_export_)) {
return Product::with('category:id,name')->where('supplier_id', $this->supplier_id)->whereIn('id', $this->_export_); return Product::with('category:id,name')->where('supplier_id', $this->supplier_id)->whereIn('id', $this->_export_);
} else { } else {
return Product::with('category:id,name')->where('supplier_id', $this->supplier_id)->limit(15);
return Product::with('category:id,name')->where('supplier_id', $this->supplier_id)->limit(20);
} }
} }
public function prepareRows($rows) public function prepareRows($rows)
{ {
return $rows->transform(function ($row) { return $rows->transform(function ($row) {
# 复制图片到导出目录
if (is_array($row['pictures']) && !empty($row['pictures'])) {
foreach ($row['pictures'] as $key => $picture) {
try {
Storage::disk('public')->copy($picture, $this->export_dir . '产品主图/' . $row->id . '/' . ($key + 1) . '.' . pathinfo($picture)['extension']);
} catch (\Exception $exception) {
continue;
}
}
}
return [ return [
'id' => $row->id, 'id' => $row->id,
'status' => ProductStatus::array()[$row->status] ?? '', 'status' => ProductStatus::array()[$row->status] ?? '',
@ -76,25 +90,13 @@ class ProductExport implements FromQuery, WithHeadings, WithColumnFormatting
}); });
} }
public function map($row): array
{
return [
$row->id,
$row->category->name,
];
}
public function headings(): array public function headings(): array
{ {
return [ return [
'ID', '状态', '分类', '产品标题', '销售价', '市场价', '库存', '旅客须知', '产品详情', '核销手机号', '信息收集表单ID',
'产品ID', '状态', '分类', '产品标题', '销售价', '市场价', '库存', '旅客须知', '产品详情', '核销手机号', '信息收集表单ID',
'出发地', '出发地经度', '出发地纬度', '目的地', '目的地经度', '目的地纬度', '行程起始时间', '行程结束时间', '出发地', '出发地经度', '出发地纬度', '目的地', '目的地经度', '目的地纬度', '行程起始时间', '行程结束时间',
'酒店名', '酒店地址', '酒店经度', '酒店纬度',
'景区名', '景区地址', '景区经度', '景区纬度',
'餐厅名', '餐厅地址', '餐厅经度', '餐厅纬度',
'交通地址', '交通经度', '交通纬度',
'购物地址', '购物经度', '购物纬度',
'酒店名', '酒店地址', '酒店经度', '酒店纬度', '景区名', '景区地址', '景区经度', '景区纬度',
'餐厅名', '餐厅地址', '餐厅经度', '餐厅纬度', '交通地址', '交通经度', '交通纬度', '购物地址', '购物经度', '购物纬度',
]; ];
} }

57
app/Exports/ProductSpecExport.php

@ -0,0 +1,57 @@
<?php
namespace App\Exports;
use App\Models\ProductSpec;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\WithHeadings;
class ProductSpecExport implements FromQuery, WithHeadings
{
private int $product_id;
public function __construct(int $product_id)
{
$this->product_id = $product_id;
}
/**
* @return Collection
*/
public function query()
{
return ProductSpec::where([
['product_id', '=', $this->product_id],
['date', '>=', date('Y-m-d')],
])->orderBy('name')->orderBy('date');
}
public function prepareRows($rows)
{
return $rows->transform(function ($row) {
return [
'规格名称' => $row['name'],
'日期' => $row['date'],
'库存' => $row['stock'],
'市场价' => $row['original_price'],
'销售价' => $row['price'],
'成本价' => $row['cost_price'],
];
});
}
public function headings(): array
{
return [
'规格名称', '日期', '库存', '市场价', '销售价', '成本价',
];
}
public function columnFormats(): array
{
return [
'B' => 'yyyy/m/d',
];
}
}

6
app/Imports/ProductImport.php

@ -48,6 +48,8 @@ class ProductImport implements ToCollection
$category = Category::where(['agent_id' => 0, 'name' => trim($row[$keys['分类']])])->first(); $category = Category::where(['agent_id' => 0, 'name' => trim($row[$keys['分类']])])->first();
if (!$category) continue; if (!$category) continue;
$index = !empty($row[$keys['产品ID']]) ? $row[$keys['产品ID']] : $product_index + 1;
$insert_data = [ $insert_data = [
'supplier_id' => $this->supplier_id, 'supplier_id' => $this->supplier_id,
'category_id' => $category->id, 'category_id' => $category->id,
@ -60,7 +62,7 @@ class ProductImport implements ToCollection
'content' => $row[$keys['产品详情']] ?? '', 'content' => $row[$keys['产品详情']] ?? '',
'verify_mobile' => $row[$keys['核销手机号']] ?? '', 'verify_mobile' => $row[$keys['核销手机号']] ?? '',
'diy_form_id' => $row[$keys['信息收集表单ID']] ?? '', 'diy_form_id' => $row[$keys['信息收集表单ID']] ?? '',
'pictures' => $this->get_pictures($product_index),
'pictures' => $this->get_pictures($index),
]; ];
# 扩展字段 # 扩展字段
@ -78,7 +80,7 @@ class ProductImport implements ToCollection
$product = Product::create($insert_data); $product = Product::create($insert_data);
if ($product->id) { if ($product->id) {
$this->insert_spec($product_index, $product->id);
$this->insert_spec($index, $product->id);
} }
} }
} }

7
app/Imports/ProductSpecImport.php

@ -42,7 +42,12 @@ class ProductSpecImport implements ToCollection
DB::beginTransaction(); DB::beginTransaction();
foreach ($rows as $row) { foreach ($rows as $row) {
# Excel日期是从1900-01-01起的天数,PHP日期是1970-01-01起,所以要减去25569天数得到正确日期 # Excel日期是从1900-01-01起的天数,PHP日期是1970-01-01起,所以要减去25569天数得到正确日期
$row[$keys['日期']] = date('Y-m-d', strtotime('+ ' . ($row[$keys['日期']] - 25569) . 'day', 0));
$time = strtotime($row[$keys['日期']]);
if ($time === false) {
$row[$keys['日期']] = date('Y-m-d', strtotime('+ ' . ($row[$keys['日期']] - 25569) . 'day', 0));
} else {
$row[$keys['日期']] = date('Y-m-d', $time);
}
ProductSpec::updateOrCreate([ ProductSpec::updateOrCreate([
'product_id' => $this->product_id, 'product_id' => $this->product_id,

56
app/Jobs/ExportProductToExcel.php

@ -3,14 +3,21 @@
namespace App\Jobs; namespace App\Jobs;
use App\Exports\ProductExport; use App\Exports\ProductExport;
use App\Exports\ProductSpecExport;
use App\Models\Product;
use App\Models\ProductExportLog; use App\Models\ProductExportLog;
use App\Models\ProductSpec;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique; use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Storage;
use Maatwebsite\Excel\Facades\Excel; use Maatwebsite\Excel\Facades\Excel;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use ZipArchive;
/** /**
* 导出全部产品 * 导出全部产品
@ -20,15 +27,17 @@ class ExportProductToExcel implements ShouldQueue
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
private int $supplier_id; private int $supplier_id;
private string|array|null $_export_;
/** /**
* Create a new job instance. * Create a new job instance.
* *
* @return void * @return void
*/ */
public function __construct(int $supplier_id)
public function __construct(int $supplier_id, string|array $_export_ = null)
{ {
$this->supplier_id = $supplier_id; $this->supplier_id = $supplier_id;
$this->_export_ = $_export_;
} }
/** /**
@ -38,11 +47,50 @@ class ExportProductToExcel implements ShouldQueue
*/ */
public function handle() public function handle()
{ {
$filePath = "supplier/export/{$this->supplier_id}/" . date('Y-m') . "/导出全部产品-" . date('Y-m-d_H-i-s') . '.xlsx';
Excel::store(new ProductExport($this->supplier_id, 'all'), $filePath, 'public');
$export_dir = "supplier/export/{$this->supplier_id}/" . date('Y-m') . '/';
$product_file = $export_dir . '产品.xlsx';
Storage::disk('public')->deleteDirectory($export_dir);
if (!Excel::store(new ProductExport($this->supplier_id, $this->_export_, $export_dir), $product_file, 'public')) {
return;
}
# 导出规格
$ids = $this->_export_ == 'all' ?
Product::where('supplier_id', $this->supplier_id)->get('id')->pluck('id') :
Product::whereIn('id', $this->_export_)->where('supplier_id', $this->supplier_id)->get('id')->pluck('id');
foreach ($ids as $id) {
if (ProductSpec::where([['product_id', '=', $id], ['date', '>=', date('Y-m-d')]])->exists()) {
Excel::store(new ProductSpecExport($id), $export_dir . "产品规格{$id}.xlsx", 'public');
}
}
# 生成zip文件
$zip = new ZipArchive();
$zip_file = substr(rtrim($export_dir, '/'), 0, strrpos(rtrim($export_dir, '/'), '/')) . '/export_' . date('Y-m-d_H-i-s') . '.zip';
if ($zip->open(Storage::disk('public')->path($zip_file),ZipArchive::CREATE | ZipArchive::OVERWRITE) === true) {
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator(Storage::disk('public')->path($export_dir)),
RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($files as $file) {
if (!$file->isDir()) {
$filePath = $file->getRealPath();
$relativePath = substr($filePath, strlen(Storage::disk('public')->path($export_dir)));
$zip->addFile($filePath, $relativePath);
}
}
$zip->close();
}
Storage::disk('public')->deleteDirectory($export_dir);
# 生成导出记录
ProductExportLog::create([ ProductExportLog::create([
'supplier_id' => $this->supplier_id, 'supplier_id' => $this->supplier_id,
'filename' => $filePath,
'filename' => $zip_file,
]); ]);
} }
} }

5
app/Jobs/ImportProductFromExcel.php

@ -46,7 +46,10 @@ class ImportProductFromExcel implements ShouldQueue
throw new Exception('未指定要导入的zip文件'); throw new Exception('未指定要导入的zip文件');
} }
$extract_path = Storage::path('excel/extract/' . basename($this->zip_file, '.zip'));
$extract_dir = "excel/extract/{$this->supplier_id}/";
Storage::deleteDirectory($extract_dir);
Storage::makeDirectory($extract_dir);
$extract_path = Storage::path($extract_dir . basename($this->zip_file, '.zip'));
$zip = new ZipArchive; $zip = new ZipArchive;
if ($zip->open($this->zip_file) === TRUE && $zip->extractTo($extract_path)) { if ($zip->open($this->zip_file) === TRUE && $zip->extractTo($extract_path)) {

Loading…
Cancel
Save