是否录入?',
+ ];
+ $html = '';
+ foreach($items as $item) {
+ $html .= '
'.$item.'';
+ }
+ $html .= '';
+ $alert = Alert::make($html, '请先检查数据是否完整');
+ $alert->warning();
+ return Modal::make()
+ ->lg()
+ ->title($this->title)
+ ->body($alert->render() . $form->render())
+ ->button($this->html());
+ }
+}
diff --git a/app/Admin/Controllers/ProductSpuController.php b/app/Admin/Controllers/ProductSpuController.php
index 655a0060..2e15e579 100644
--- a/app/Admin/Controllers/ProductSpuController.php
+++ b/app/Admin/Controllers/ProductSpuController.php
@@ -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();
diff --git a/app/Admin/Controllers/Store/ProductController.php b/app/Admin/Controllers/Store/ProductController.php
index a2dc96d3..0b66ebe2 100644
--- a/app/Admin/Controllers/Store/ProductController.php
+++ b/app/Admin/Controllers/Store/ProductController.php
@@ -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);
}
diff --git a/app/Admin/Imports/Product.php b/app/Admin/Imports/Product.php
new file mode 100644
index 00000000..db20599a
--- /dev/null
+++ b/app/Admin/Imports/Product.php
@@ -0,0 +1,173 @@
+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 .= '
';
+ }
+ $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;
+ }
+}
diff --git a/app/Models/ProductGroup.php b/app/Models/ProductGroup.php
index 993263ac..0282c4e5 100644
--- a/app/Models/ProductGroup.php
+++ b/app/Models/ProductGroup.php
@@ -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,
diff --git a/app/Models/ProductSpu.php b/app/Models/ProductSpu.php
index 3c40be52..2772ae73 100644
--- a/app/Models/ProductSpu.php
+++ b/app/Models/ProductSpu.php
@@ -53,6 +53,8 @@ class ProductSpu extends Model
'sales',
'release_at',
'sales_value',
+ 'cover',
+ 'description'
];
public function skus()
diff --git a/composer.lock b/composer.lock
index ef988496..72554070 100644
--- a/composer.lock
+++ b/composer.lock
@@ -12092,5 +12092,5 @@
"php": "^8.0"
},
"platform-dev": [],
- "plugin-api-version": "2.1.0"
+ "plugin-api-version": "2.3.0"
}
diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php
index 4313682f..cd89ae51 100644
--- a/database/seeders/DatabaseSeeder.php
+++ b/database/seeders/DatabaseSeeder.php
@@ -27,7 +27,8 @@ class DatabaseSeeder extends Seeder
ProductPartSeeder::class,
ProductCategorySeeder::class,
AdAddressSeeder::class,
- ShippingSeeder::class
+ ShippingSeeder::class,
+ ProductAttrSeeder::class
]);
}
}
diff --git a/database/seeders/ProductAttrSeeder.php b/database/seeders/ProductAttrSeeder.php
new file mode 100644
index 00000000..39cbcec1
--- /dev/null
+++ b/database/seeders/ProductAttrSeeder.php
@@ -0,0 +1,28 @@
+ '华为智慧屏',
+ 'attrs' => [
+ ['title' => '属性组', 'value' => implode(PHP_EOL, ['分辨率', '摄像头', '能效等级'])]
+ ],
+ 'specs' => [
+ ['title' => '尺寸', 'value' => implode(PHP_EOL, ['55英寸', '65英寸', '75英寸'])]
+ ]
+ ]);
+ }
+}