商品导入
parent
b00238f320
commit
d38169f74b
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace App\Admin\Actions\Form;
|
||||
|
||||
use Dcat\Admin\Widgets\Form;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Admin\Imports\Product;
|
||||
|
||||
class ProductImportForm extends Form
|
||||
{
|
||||
public function handle(array $input)
|
||||
{
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
$file = $input['file'];
|
||||
(new Product())->load($file);
|
||||
DB::commit();
|
||||
return $this->response()->success('数据导入成功')->refresh();
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
dd($e);
|
||||
return $this->response()->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function form()
|
||||
{
|
||||
$this->text('file', '上传数据(Excel)')
|
||||
->attribute('type', 'file')
|
||||
->attribute('accept', 'application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
|
||||
->rules('required', ['required' => '文件不能为空']);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace App\Admin\Actions\Modal;
|
||||
|
||||
use Dcat\Admin\Admin;
|
||||
use Dcat\Admin\Grid\Tools\AbstractTool;
|
||||
use App\Admin\Actions\Form\ProductImportForm;
|
||||
use Dcat\Admin\Widgets\Modal;
|
||||
use Dcat\Admin\Widgets\Alert;
|
||||
|
||||
class ProductImport extends AbstractTool
|
||||
{
|
||||
protected $title = '导入商品';
|
||||
public function render()
|
||||
{
|
||||
$form = ProductImportForm::make();
|
||||
$items = [
|
||||
'<a href="'.admin_url('product-categories').'" target="_blank" style="color:#000;font-weight: bold;">商品分类<a>是否录入?',
|
||||
'<a href="javascript:void(0)" style="color:#000;font-weight: bold;">商品图片<a>是否上传?',
|
||||
'<a href="'.admin_url('product-groups').'" target="_blank" style="color:#000;font-weight: bold;">商品规格属性<a>是否录入?',
|
||||
'<a href="'.admin_url('shipping-templates').'" target="_blank" style="color:#000;font-weight: bold;">运费模板<a>是否录入?',
|
||||
];
|
||||
$html = '<div class="list-group list-group-flush">';
|
||||
foreach($items as $item) {
|
||||
$html .= '<li class="list-group-item list-group-item-warning">'.$item.'</li>';
|
||||
}
|
||||
$html .= '</div>';
|
||||
$alert = Alert::make($html, '请先检查数据是否完整');
|
||||
$alert->warning();
|
||||
return Modal::make()
|
||||
->lg()
|
||||
->title($this->title)
|
||||
->body($alert->render() . $form->render())
|
||||
->button($this->html());
|
||||
}
|
||||
}
|
||||
|
|
@ -74,6 +74,11 @@ class ProductSpuController extends AdminController
|
|||
}
|
||||
});
|
||||
|
||||
$grid->tools(function (Grid\Tools $tools) {
|
||||
// Excel导入
|
||||
$tools->append(new \App\Admin\Actions\Modal\ProductImport());
|
||||
});
|
||||
|
||||
/** 查询 **/
|
||||
$grid->filter(function (Grid\Filter $filter) {
|
||||
$filter->panel();
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use Dcat\Admin\Models\Administrator;
|
|||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Dcat\Admin\Traits\HasFormResponse;
|
||||
use App\Models\ProductSku;
|
||||
|
||||
class ProductController extends Controller
|
||||
{
|
||||
|
|
@ -29,11 +30,7 @@ class ProductController extends Controller
|
|||
{
|
||||
$store = Store::findOrFail($store_id);
|
||||
$form = Form::make();
|
||||
$form->selectTable('product', '商品')
|
||||
->from(\App\Admin\Renderable\ProductSkuSimpleTable::make())
|
||||
->model(\App\Models\ProductSku::class, 'id', 'name')
|
||||
->required();
|
||||
// $form->number('amount', '库存')->min(0);
|
||||
$form->select('product', '商品')->options(ProductSku::class)->ajax('api/product-skus');
|
||||
$form->switch('status', '上架')->default(1);
|
||||
return (new Content())->title('新增')->body($form);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,173 @@
|
|||
<?php
|
||||
|
||||
namespace App\Admin\Imports;
|
||||
|
||||
use App\Exceptions\ImportException;
|
||||
use Box\Spout\Reader\Common\Creator\ReaderEntityFactory;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use App\Models\{ProductSpu, ProductCategory, ProductGroup};
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class Product
|
||||
{
|
||||
public function load($file)
|
||||
{
|
||||
$reader = ReaderEntityFactory::createXLSXReader();
|
||||
$reader->open($file);
|
||||
|
||||
foreach ($reader->getSheetIterator() as $sheet) {
|
||||
foreach ($sheet->getRowIterator() as $num => $row) {
|
||||
if ($num === 1) {
|
||||
continue;
|
||||
}
|
||||
$cells = $row->toArray();
|
||||
|
||||
$goods = [
|
||||
'name' => $cells[0],
|
||||
'subtitle' => $cells[1],
|
||||
'shipping_template_id' => 1,
|
||||
];
|
||||
$category = ProductCategory::where('name', $cells[2])->firstOrFail();
|
||||
$goods['category_id'] = $category->id;
|
||||
|
||||
if ($path = $cells[3]) {
|
||||
$images = $this->getImageUrlFromPath($path);
|
||||
if (count($images) > 0) {
|
||||
$goods['cover'] = $images[0];
|
||||
$goods['images'] = $images;
|
||||
}
|
||||
}
|
||||
|
||||
// 运费模板
|
||||
if ($cell_11 = data_get($cells, 11)) {
|
||||
$goods['shipping_template_id'] = $cell_11;
|
||||
}
|
||||
// 重量
|
||||
if ($cell_12 = data_get($cells, 12)) {
|
||||
$goods['weight'] = $cell_12;
|
||||
}
|
||||
// 详细描述
|
||||
if ($cell_13 = data_get($cells, 13)) {
|
||||
$description = '';
|
||||
foreach(explode(',', $cell_13) as $item) {
|
||||
$description .= '<p><img src="'.$item.'"/></p>';
|
||||
}
|
||||
$goods['description'] = $description;
|
||||
}
|
||||
|
||||
$goods = array_merge($goods, [
|
||||
'sell_price' => $cells[4] * 100,
|
||||
'market_price' => $cells[5] * 100,
|
||||
'cost_price' => $cells[6] * 100,
|
||||
'vip_price' => $cells[7] * 100,
|
||||
'sales_value' => $cells[8] * 100,
|
||||
]);
|
||||
|
||||
$product = ProductSpu::updateOrCreate(['name' => $goods['name']],$goods);
|
||||
|
||||
// 属性规格
|
||||
$coll_9 = data_get($cells, 9);
|
||||
$cell_10 = data_get($cells, 10);
|
||||
if ($coll_9) {
|
||||
$format_specs = $this->formatAttr($coll_9);
|
||||
$format_attrs = $this->formatAttr($cell_10);
|
||||
$spec_group = $this->getSpec($cells[0], $coll_9, $cell_10);
|
||||
|
||||
$attrs = [];
|
||||
foreach($spec_group->attrs as $index => $item) {
|
||||
$attr = ['name' => $item['title'], 'attrs' => []];
|
||||
foreach(explode(PHP_EOL, $item['value']) as $value) {
|
||||
array_push($attr['attrs'], ['name' => $value, 'value' => data_get($format_attrs, "$index.values.$value")]);
|
||||
}
|
||||
array_push($attrs, $attr);
|
||||
}
|
||||
$product->update(['attrs' => $attrs]);
|
||||
|
||||
$specs = [];
|
||||
$product->specs()->delete();
|
||||
foreach($spec_group->specs as $index => $item) {
|
||||
$spec = ['name' => $item['title'], 'items' => []];
|
||||
foreach(explode(PHP_EOL, $item['value']) as $value) {
|
||||
array_push($spec['items'], ['name' => $value, 'value' => data_get($format_specs, "$index.values.$value")]);
|
||||
}
|
||||
array_push($specs, $spec);
|
||||
}
|
||||
$product->specs()->createMany($specs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$reader->close();
|
||||
}
|
||||
|
||||
protected function getImageUrlFromPath($dir)
|
||||
{
|
||||
$disk = Storage::disk('public');
|
||||
$files = $disk->files('goods/' . $dir);
|
||||
$images = [];
|
||||
|
||||
foreach($files as $filename) {
|
||||
array_push($images, $disk->url($filename));
|
||||
}
|
||||
|
||||
return $images;
|
||||
}
|
||||
|
||||
protected function getSpec($name, $specs, $attrs)
|
||||
{
|
||||
if (Str::contains($specs, ':')) {
|
||||
// 创建新的规格
|
||||
$spec = ProductGroup::updateOrCreate([
|
||||
'name' => $name
|
||||
], [
|
||||
'attrs' => $this->formatGroupAttr($this->formatAttr($attrs)),
|
||||
'specs' => $this->formatGroupAttr($this->formatAttr($specs))
|
||||
]);
|
||||
} else {
|
||||
$spec = ProductGroup::where('name', $specs)->firstOrFail();
|
||||
}
|
||||
|
||||
return $spec;
|
||||
}
|
||||
|
||||
/**
|
||||
* 颜色[灰色:0,银色:0]
|
||||
* 尺寸[13寸:0,14寸:500,16寸:1000]
|
||||
* 处理器[M1:0,M1 Pro:1000,M1 Max:1500]
|
||||
* 内存[8G:0,16G:1000,32G:2000]
|
||||
* 硬盘[256G:0,512G:1000,1TB:2000]
|
||||
*
|
||||
* [
|
||||
* ['name' => '颜色', 'values' => ['灰色' => 0, '银色' => 0]],
|
||||
* ['name' => '尺寸', 'values' => ['13寸' => 0, '14寸' => 500, '16寸' => '1000']]
|
||||
* ...
|
||||
* ]
|
||||
*/
|
||||
protected function formatAttr($attrs)
|
||||
{
|
||||
$attr_data = [];
|
||||
if (!Str::contains($attrs, ':')) {
|
||||
return $attr_data;
|
||||
}
|
||||
foreach(explode("\n", $attrs) as $item) {
|
||||
$name = explode('[', $item)[0];
|
||||
$attr_values = [];
|
||||
foreach(explode(',', Str::between($item, '[', ']')) as $item1) {
|
||||
$item1_explode = explode(':', $item1);
|
||||
$attr_values[$item1_explode[0]] = $item1_explode[1];
|
||||
}
|
||||
array_push($attr_data, ['name' => $name, 'values' => $attr_values]);
|
||||
}
|
||||
|
||||
return $attr_data;
|
||||
}
|
||||
|
||||
protected function formatGroupAttr($data)
|
||||
{
|
||||
$list = [];
|
||||
foreach($data as $item) {
|
||||
array_push($list, ['title' => $item['name'], 'value' => implode(PHP_EOL, array_keys($item['values']))]);
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,8 @@ class ProductGroup extends Model
|
|||
{
|
||||
use HasDateTimeFormatter;
|
||||
|
||||
protected $fillable = ['id', 'name', 'attrs', 'specs', 'created_at', 'updated_at'];
|
||||
|
||||
protected $casts = [
|
||||
'attrs' => JsonArray::class,
|
||||
'specs' => JsonArray::class,
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ class ProductSpu extends Model
|
|||
'sales',
|
||||
'release_at',
|
||||
'sales_value',
|
||||
'cover',
|
||||
'description'
|
||||
];
|
||||
|
||||
public function skus()
|
||||
|
|
|
|||
|
|
@ -12092,5 +12092,5 @@
|
|||
"php": "^8.0"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.1.0"
|
||||
"plugin-api-version": "2.3.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ class DatabaseSeeder extends Seeder
|
|||
ProductPartSeeder::class,
|
||||
ProductCategorySeeder::class,
|
||||
AdAddressSeeder::class,
|
||||
ShippingSeeder::class
|
||||
ShippingSeeder::class,
|
||||
ProductAttrSeeder::class
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use App\Models\ProductGroup;
|
||||
|
||||
class ProductAttrSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
ProductGroup::truncate();
|
||||
ProductGroup::create([
|
||||
'name' => '华为智慧屏',
|
||||
'attrs' => [
|
||||
['title' => '属性组', 'value' => implode(PHP_EOL, ['分辨率', '摄像头', '能效等级'])]
|
||||
],
|
||||
'specs' => [
|
||||
['title' => '尺寸', 'value' => implode(PHP_EOL, ['55英寸', '65英寸', '75英寸'])]
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue