6
0
Fork 0

完成商品添加SKU

release
vine_liutk 2021-12-01 14:28:26 +08:00
parent 853ee66d1f
commit acbebdcc6b
18 changed files with 734 additions and 11 deletions

View File

@ -0,0 +1,69 @@
<?php
namespace App\Admin\Actions\Grid;
use Dcat\Admin\Actions\Response;
use Dcat\Admin\Grid\RowAction;
use Dcat\Admin\Traits\HasPermissions;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
class SkuList extends RowAction
{
/**
* @return string
*/
protected $title = 'Title';
public function title()
{
if ($this->title) {
return $this->title;
}
return '<i class="feather icon-list grid-action-icon"></i> '.__('admin.list').' &nbsp;&nbsp;';
}
/**
* Handle the action request.
*
* @param Request $request
*
* @return Response
*/
public function handle(Request $request)
{
// dump($this->getKey());
return $this->response()
// ->success('Processed successfully: '.$this->getKey())
->redirect(admin_route('product_spus.list', ['spu' =>$this->getKey()]));
}
/**
* @return string|array|void
*/
public function confirm()
{
// return ['Confirm?', 'contents'];
}
/**
* @param Model|Authenticatable|HasPermissions|null $user
*
* @return bool
*/
protected function authorize($user): bool
{
return true;
}
/**
* @return array
*/
protected function parameters()
{
return [];
}
}

View File

@ -110,9 +110,10 @@ class ProductGroupController extends AdminController
$data['attrs'][] = [
'name' => $attr['title'],
'attrs'=> array_map(function ($value) {
$_value = explode(':', $value);
return [
'name'=>$value,
'value'=>'',
'name'=>$_value[0]??'',
'value'=>$_value[1]??'',
];
}, explode(',', str_replace("\r\n", ',', trim($attr['value'])))),
];
@ -123,7 +124,7 @@ class ProductGroupController extends AdminController
'specs'=> array_map(function ($value) {
return [
'name'=>$value,
'value'=>'',
'value'=>0,
];
}, explode(',', str_replace("\r\n", ',', trim($spec['value'])))),
];

View File

@ -0,0 +1,115 @@
<?php
namespace App\Admin\Controllers;
use App\Admin\Repositories\ProductSku;
use Dcat\Admin\Form;
use Dcat\Admin\Grid;
use Dcat\Admin\Http\Controllers\AdminController;
use Dcat\Admin\Show;
class ProductSkuController extends AdminController
{
/**
* Make a grid builder.
*
* @return Grid
*/
protected function grid()
{
return Grid::make(new ProductSku(), function (Grid $grid) {
$grid->column('id')->sortable();
$grid->column('spu_id');
$grid->column('name');
$grid->column('subtitle');
$grid->column('category_id');
$grid->column('cover');
$grid->column('images');
$grid->column('description');
$grid->column('sell_price');
$grid->column('market_price');
$grid->column('cost_price');
$grid->column('vip_price');
$grid->column('media');
$grid->column('weight');
$grid->column('attrs');
$grid->column('specs');
$grid->column('stock');
$grid->column('sales');
$grid->column('release_at');
$grid->column('created_at');
$grid->column('updated_at')->sortable();
$grid->filter(function (Grid\Filter $filter) {
$filter->equal('id');
});
});
}
/**
* Make a show builder.
*
* @param mixed $id
*
* @return Show
*/
protected function detail($id)
{
return Show::make($id, new ProductSku(), function (Show $show) {
$show->field('id');
$show->field('spu_id');
$show->field('name');
$show->field('subtitle');
$show->field('category_id');
$show->field('cover');
$show->field('images');
$show->field('description');
$show->field('sell_price');
$show->field('market_price');
$show->field('cost_price');
$show->field('vip_price');
$show->field('media');
$show->field('weight');
$show->field('attrs');
$show->field('specs');
$show->field('stock');
$show->field('sales');
$show->field('release_at');
$show->field('created_at');
$show->field('updated_at');
});
}
/**
* Make a form builder.
*
* @return Form
*/
protected function form()
{
return Form::make(new ProductSku(), function (Form $form) {
$form->display('id');
$form->text('spu_id');
$form->text('name');
$form->text('subtitle');
$form->text('category_id');
$form->text('cover');
$form->text('images');
$form->text('description');
$form->text('sell_price');
$form->text('market_price');
$form->text('cost_price');
$form->text('vip_price');
$form->text('media');
$form->text('weight');
$form->text('attrs');
$form->text('specs');
$form->text('stock');
$form->text('sales');
$form->text('release_at');
$form->display('created_at');
$form->display('updated_at');
});
}
}

View File

@ -2,15 +2,22 @@
namespace App\Admin\Controllers;
use App\Admin\Actions\Grid\SkuList;
use App\Admin\Extensions\Grid\Tools\Product\AddSku;
use App\Admin\Extensions\Grid\Tools\Product\InitSkuBySpecs;
use App\Admin\Extensions\Grid\Tools\Product\SettingSpecs;
use App\Admin\Repositories\ProductSpu;
use App\Models\ProductBuynote;
use App\Models\ProductFeature;
use App\Models\ProductGroup;
use App\Models\ProductSku;
use App\Models\ProductSpu as ProductSpuModel;
use Carbon\Carbon;
use Dcat\Admin\Admin;
use Dcat\Admin\Form;
use Dcat\Admin\Grid;
use Dcat\Admin\Http\Controllers\AdminController;
use Dcat\Admin\Layout\Content;
use Dcat\Admin\Show;
class ProductSpuController extends AdminController
@ -48,6 +55,9 @@ class ProductSpuController extends AdminController
$grid->actions(function (Grid\Displayers\Actions $actions) {
$actions->disableEdit(Admin::user()->cannot('dcat.admin.product_spus.edit'));
$actions->disableDelete(Admin::user()->cannot('dcat.admin.product_spus.destroy'));
if (Admin::user()->can('dcat.admin.product_spus.list')) {
$actions->prepend(new SkuList(__('admin.list')));
}
});
/** 查询 **/
@ -104,6 +114,7 @@ class ProductSpuController extends AdminController
}
$form->text('name')->required();
$form->text('subtitle');
$form->divider();
$form->image('cover')
->move('product-spus/cover/'.Carbon::now()->toDateString())
->saveFullUrl()
@ -130,11 +141,12 @@ class ProductSpuController extends AdminController
$form->editor('description');
$form->select('buynote_id')->options(ProductBuynote::all()->pluck('name', 'id'));
$form->number('weight');
$form->divider();
$form->currency('sell_price')->symbol('¥')->default(0);
$form->currency('market_price')->symbol('¥')->default(0);
$form->currency('cost_price')->symbol('¥')->default(0);
$form->currency('vip_price')->symbol('¥');
$form->divider();
$form->select('attr_group')->options(ProductGroup::all()->pluck('name', 'id'));
$form->selectAttr('attrs')->listen('attr_group');
if ($form->isCreating()) {
@ -146,4 +158,37 @@ class ProductSpuController extends AdminController
$form->display('updated_at');
});
}
/**
* Undocumented function
*
* @return void
*/
public function list(Content $content, ProductSpuModel $spu)
{
return $content->header(__('product-spu.labels.ProductSpu'))
->description($spu->name)
->body(Grid::make(ProductSku::where('spu_id', $spu->id), function ($grid) use ($spu) {
$grid->id();
$grid->disableRowSelector(false);
$grid->tools(function (Grid\Tools $tools) use ($spu) {
//设置规格
$tools->append(new SettingSpecs($spu->id));
if ($spu->hasSku()) { //下面有sku手动创建sku
$tools->append(new AddSku($spu->id));
} else {//下面无sku根据规格自动生成sku
$tools->append(new InitSkuBySpecs($spu->id));
}
});
$grid->column('name');
$grid->column('subtitle');
$grid->column('specs')->label();
$grid->column('sell_price')->prepend('¥');
$grid->column('market_price')->prepend('¥');
$grid->column('cost_price')->prepend('¥');
$grid->column('vip_price')->prepend('¥');
$grid->column('weight');
}));
}
}

View File

@ -37,10 +37,10 @@ class SelectSpec extends Field
{
$specs = $value;
if ($specs) {
$specs =json_decode($specs, true);
$specs = json_decode($specs, true);
foreach ($specs as $key=> &$item) {
$item['items'] = array_filter(array_map(function ($value) {
if (!empty($value['value'])) {
if ($value['value'] !=='' && $value['name'] !== 'NONE') {
return $value;
}
}, $item['specs']));

View File

@ -0,0 +1,54 @@
<?php
namespace App\Admin\Extensions\Grid\Tools\Product;
use App\Admin\Forms\AddSku as AddSkuForm;
use Dcat\Admin\Grid\Tools\AbstractTool;
use Dcat\Admin\Widgets\Modal;
class AddSku extends AbstractTool
{
public function __construct($spu_id = 0, $title=null)
{
parent::__construct($title);
$this->setKey($spu_id);
}
/**
* 按钮样式定义,默认 btn btn-white waves-effect
*
* @var string
*/
protected $style = 'btn btn btn-primary';
/**
* 按钮文本
*
* @return string|void
*/
public function title()
{
return __('admin_message.extensions.grid.tools.add_sku');
}
public function render()
{
$form = AddSkuForm::make()->payload(['spu_id'=>$this->getKey()]);
return Modal::make()
->lg()
->title($this->title())
->body($form)
->button($this->html());
}
/**
* 设置请求参数
*
* @return array|void
*/
public function parameters()
{
return [
];
}
}

View File

@ -0,0 +1,102 @@
<?php
namespace App\Admin\Extensions\Grid\Tools\Product;
use App\Models\ProductSku;
use App\Models\ProductSpu;
use Dcat\Admin\Grid\Tools\AbstractTool;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
class InitSkuBySpecs extends AbstractTool
{
public function __construct($spu_id = 0, $title=null)
{
parent::__construct($title);
$this->setKey($spu_id);
}
/**
* 按钮样式定义,默认 btn btn-white waves-effect
*
* @var string
*/
protected $style = 'btn btn-primary';
/**
* 按钮文本
*
* @return string|void
*/
public function title()
{
return __('admin_message.extensions.grid.tools.init_sku_by_specs');
}
public function confirm()
{
// 显示标题和内容
return [__('admin_message.extensions.grid.init_sku_by_specs.confirm.title'), __('admin_message.extensions.grid.init_sku_by_specs.confirm.message')];
}
/**
* 处理请求
* 如果你的类中包含了此方法则点击按钮后会自动向后端发起ajax请求并且会通过此方法处理请求逻辑
*
* @param Request $request
*/
public function handle(Request $request)
{
$nowTime = now();
//获取SPU
$spu = ProductSpu::with('specs')->findOrFail($this->getKey());
$skuSpecs = [];
foreach ($spu->specs as $spec) {
$_items = [];
foreach ($spec->items as $item) {
$_items[] = [
'id' => $spec->id,
'name'=>$item['name'],
'price'=>$item['value'],
];
}
$skuSpecs[] = $_items;
}
//生成规格笛卡尔集
$skuSpecs = Arr::crossJoin(...$skuSpecs);
$insertData = [];
//根据规格笛卡尔集生成预插入数据
foreach ($skuSpecs as $skuSpec) {
$_skuSpecs = [];
$_price = 0;
foreach ($skuSpec as $value) {
$_skuSpecs[$value['id']] = $value['name'];
$_price += $value['price'];
}
$insertData[] = [
'spu_id' => $spu->id,
'name' => $spu->name,
'subtitle' => $spu->subtitle,
'category_id' => $spu->category_id,
'cover' => $spu->cover,
'images' => json_encode($spu->images),
'description' => $spu->description,
'sell_price' => bcadd($spu->sell_price, bcmul($_price, 100)),
'market_price' => $spu->market_price,
'cost_price' => $spu->cost_price,
'vip_price' => !is_null($spu->vip_price) ? bcadd($spu->vip_price, bcmul($_price, 100)) : null,
'media' => $spu->media,
'weight' => $spu->weight,
'attrs' => json_encode($spu->attrs),
'specs' => json_encode($_skuSpecs),
'stock' => $spu->stock,
'sales' => $spu->sales,
'buynote_id' => $spu->buynote_id,
'created_at' => $nowTime,
'updated_at' => $nowTime,
];
}
count($insertData)>0 && ProductSku::insert($insertData);
return $this->response()->success(__('admin.update_succeeded'))->refresh();
}
}

View File

@ -0,0 +1,54 @@
<?php
namespace App\Admin\Extensions\Grid\Tools\Product;
use App\Admin\Forms\SettingSpuSpecs;
use Dcat\Admin\Grid\Tools\AbstractTool;
use Dcat\Admin\Widgets\Modal;
class SettingSpecs extends AbstractTool
{
public function __construct($spu_id = 0, $title=null)
{
parent::__construct($title);
$this->setKey($spu_id);
}
/**
* 按钮样式定义,默认 btn btn-white waves-effect
*
* @var string
*/
protected $style = 'btn btn btn-primary';
/**
* 按钮文本
*
* @return string|void
*/
public function title()
{
return __('admin_message.extensions.grid.tools.setting_specs');
}
public function render()
{
$form = SettingSpuSpecs::make()->payload(['spu_id'=>$this->getKey()]);
return Modal::make()
->lg()
->title($this->title())
->body($form)
->button($this->html());
}
/**
* 设置请求参数
*
* @return array|void
*/
public function parameters()
{
return [
];
}
}

View File

@ -0,0 +1,102 @@
<?php
namespace App\Admin\Forms;
use App\Models\ProductSku;
use App\Models\ProductSpu;
use App\Models\ProductSpuSpec;
use Dcat\Admin\Contracts\LazyRenderable;
use Dcat\Admin\Traits\LazyWidget;
use Dcat\Admin\Widgets\Form;
class AddSku extends Form implements LazyRenderable
{
use LazyWidget;
/**
* Handle the form request.
*
* @param array $input
*
* @return mixed
*/
public function handle(array $input)
{
if ($input) {
$spuId = $this->payload['spu_id'] ?? 0;
$_skuSpecs = [];
$_price = 0;
foreach ($input as $id => $value) {
$spuSpec = ProductSpuSpec::where('id', $id)->first();
foreach ($spuSpec->items as $item) {
if ($item['name'] == $value) {
$_skuSpecs[$id] = $item['name'];
$_price += $item['value'];
}
}
}
//如果存在该规格组合
$skuQuery = ProductSku::where('spu_id', $spuId);
foreach ($_skuSpecs as $id=> $spec) {
$skuQuery->where('specs->'.$id, $spec);
}
if ($skuQuery->first()) {
return $this->response()->error(__('admin_message.forms.add_sku.errors.has_specs'));
} else {
$spu = ProductSpu::findOrFail($spuId);
ProductSku::create([
'spu_id' => $spu->id,
'name' => $spu->name,
'subtitle' => $spu->subtitle,
'category_id' => $spu->category_id,
'cover' => $spu->cover,
'images' => $spu->images,
'description' => $spu->description,
'sell_price' => bcadd($spu->sell_price, $_price),
'market_price' => $spu->market_price,
'cost_price' => $spu->cost_price,
'vip_price' => !is_null($spu->vip_price) ? bcadd($spu->vip_price, $_price) : null,
'media' => $spu->media,
'weight' => $spu->weight,
'attrs' => $spu->attrs,
'specs' => $_skuSpecs,
'stock' => $spu->stock,
'sales' => $spu->sales,
'buynote_id' => $spu->buynote_id,
]);
}
}
return $this->response()
->success(__('admin.update_succeeded'))
->refresh();
}
/**
* Build a form here.
*/
public function form()
{
$spuId = $this->payload['spu_id'] ?? 0;
$spu = ProductSpu::findOrFail($spuId);
foreach ($spu->specs as $spec) {
$this->select($spec->id, $spec->name)->options(function () use ($spec) {
return collect($spec->items)->pluck('name', 'name');
})->required();
}
$this->disableResetButton();
}
/**
* The data of the form.
*
* @return array
*/
public function default()
{
return [
// 'name' => 'John Doe',
// 'email' => 'John.Doe@gmail.com',
];
}
}

View File

@ -0,0 +1,89 @@
<?php
namespace App\Admin\Forms;
use App\Models\ProductSku;
use App\Models\ProductSpu;
use App\Models\ProductSpuSpec;
use Dcat\Admin\Contracts\LazyRenderable;
use Dcat\Admin\Traits\LazyWidget;
use Dcat\Admin\Widgets\Form;
class SettingSpuSpecs extends Form implements LazyRenderable
{
use LazyWidget;
/**
* Handle the form request.
*
* @param array $input
*
* @return mixed
*/
public function handle(array $input)
{
if ($input) {
$spuId = $this->payload['spu_id'] ?? 0;
foreach ($input as $id => $items) {
$spuSpec = ProductSpuSpec::findOrFail($id);
$spuSpecsItems = array_column($spuSpec->items, 'name');
//除去空值
foreach ($items as $key => $item) {
if ($item['name'] === '' || $item['value'] === '') {
unset($items[$item]);
}
}
//如果删除某个属性需要判断该属性下是否有sku
$nowSpecsItems = array_column($items, 'name');
$diffSpecsItems = array_diff($spuSpecsItems, $nowSpecsItems);
foreach ($diffSpecsItems as $name) {
if (ProductSku::where('spu_id', $spuId)->where('specs->'.$id, $name)->exists()) {
return $this->response()->error(__('admin_message.forms.setting_spu_specs.errors.has_specs', ['name'=>$name]));
}
}
ProductSpuSpec::where('id', $id)->update([
'items' => $items,
]);
}
}
// return $this->response()->error('Your error message.');
return $this
->response()
->success(__('admin.update_succeeded'))
->refresh();
}
/**
* Build a form here.
*/
public function form()
{
$spuId = $this->payload['spu_id'] ?? null;
$spu = ProductSpu::findOrFail($spuId);
foreach ($spu->specs as $spec) {
$this->table($spec->id, $spec->name, function ($table) {
$table->text('name', __('admin_message.forms.setting_spu_specs.table.name'));
$table->currency('value', __('admin_message.forms.setting_spu_specs.table.value'))->symbol('¥')->default(0);
})->customFormat(function () use ($spec) {
return $spec->items;
});
}
$this->disableResetButton();
}
/**
* The data of the form.
*
* @return array
*/
public function default()
{
return [
// 'name' => 'John Doe',
// 'email' => 'John.Doe@gmail.com',
];
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace App\Admin\Repositories;
use App\Models\ProductSku as Model;
use Dcat\Admin\Repositories\EloquentRepository;
class ProductSku extends EloquentRepository
{
/**
* Model.
*
* @var string
*/
protected $eloquentClass = Model::class;
}

View File

@ -48,7 +48,11 @@ Route::group([
])->names('product_buynotes');
$router->resource('product-spus', 'ProductSpuController')->names('product_spus');
$router->get('product-spus/{spu}/list', 'ProductSpuController@list')->name('product_spus.list');
$router->resource('product-skus', 'ProductSkuController')->only([
'index',
])->names('product_skus');

View File

@ -51,4 +51,10 @@ class ProductSpu extends Model
{
return $this->belongsToMany(ProductFeature::class, 'product_spu_features', 'feature_id', 'spu_id');
}
public function hasSku()
{
// dd($this->hasMany(ProductSku::class, 'spu_id')->exists());
return $this->hasMany(ProductSku::class, 'spu_id')->exists();
}
}

View File

@ -15,7 +15,7 @@ class CreateProductSpuSpecsTable extends Migration
{
Schema::create('product_spu_specs', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('product_spu_id')->comment('商品主ID');
$table->unsignedBigInteger('spu_id')->comment('商品主ID');
$table->string('name')->column('规格名称');
/**
* [
@ -25,7 +25,7 @@ class CreateProductSpuSpecsTable extends Migration
$table->json('items')->nullable()->comment('规格值');
$table->timestamps();
$table->index('product_spu_id');
$table->index('spu_id');
});
}

View File

@ -83,6 +83,7 @@ namespace Dcat\Admin {
* @property Grid\Column|Collection product_spu_id
* @property Grid\Column|Collection items
* @property Grid\Column|Collection is_sell
* @property Grid\Column|Collection buynote_id
* @property Grid\Column|Collection phone
* @property Grid\Column|Collection code
* @property Grid\Column|Collection is_use
@ -172,6 +173,7 @@ namespace Dcat\Admin {
* @method Grid\Column|Collection product_spu_id(string $label = null)
* @method Grid\Column|Collection items(string $label = null)
* @method Grid\Column|Collection is_sell(string $label = null)
* @method Grid\Column|Collection buynote_id(string $label = null)
* @method Grid\Column|Collection phone(string $label = null)
* @method Grid\Column|Collection code(string $label = null)
* @method Grid\Column|Collection is_use(string $label = null)
@ -266,6 +268,7 @@ namespace Dcat\Admin {
* @property Show\Field|Collection product_spu_id
* @property Show\Field|Collection items
* @property Show\Field|Collection is_sell
* @property Show\Field|Collection buynote_id
* @property Show\Field|Collection phone
* @property Show\Field|Collection code
* @property Show\Field|Collection is_use
@ -355,6 +358,7 @@ namespace Dcat\Admin {
* @method Show\Field|Collection product_spu_id(string $label = null)
* @method Show\Field|Collection items(string $label = null)
* @method Show\Field|Collection is_sell(string $label = null)
* @method Show\Field|Collection buynote_id(string $label = null)
* @method Show\Field|Collection phone(string $label = null)
* @method Show\Field|Collection code(string $label = null)
* @method Show\Field|Collection is_use(string $label = null)

View File

@ -24,4 +24,35 @@ return [
],
],
],
'extensions'=>[
'grid'=>[
'tools'=>[
'setting_specs'=>'设置规格',
'init_sku_by_specs'=>'初始化SKU',
'add_sku'=>'添加SKU',
],
'init_sku_by_specs'=>[
'confirm'=>[
'title'=>'初始化SKU',
'message'=>'是否确认按当前规格初始化所有SKU',
],
],
],
],
'forms'=>[
'setting_spu_specs'=>[
'table'=>[
'name'=>'属性名称',
'value'=>'属性价格',
],
'errors'=>[
'has_specs' => ':name 已存在SKU商品无法删除',
],
],
'add_sku'=>[
'errors'=>[
'has_specs'=> '已存在该规格组合商品',
],
],
],
];

View File

@ -0,0 +1,29 @@
<?php
return [
'labels' => [
'ProductSku' => 'ProductSku',
'product-sku' => 'ProductSku',
],
'fields' => [
'spu_id' => '主商品ID',
'name' => '商品名称',
'subtitle' => '商品副标题',
'category_id' => '商品分类',
'cover' => '封面图',
'images' => '商品图片',
'description' => '商品详情',
'sell_price' => '销售价格:分',
'market_price' => '市场价格:分',
'cost_price' => '成本价格:分',
'vip_price' => '会员价格:分',
'media' => '媒体地址',
'weight' => '重量:g',
'attrs' => '属性文本',
'specs' => '规格属性',
'stock' => '库存',
'sales' => '销量',
'release_at' => '上架时间',
],
'options' => [
],
];

View File

@ -29,7 +29,10 @@
<td>
<div class="form-group " style="margin-bottom: 0 !important;">
<div class="col-sm-12">
<input name="" class="form-control" v-model="spec.value">
<div class="input-group">
<span class="input-group-prepend"><span class="input-group-text bg-white"></span></span>
<input name="" class="form-control" placeholder="输入 属性价格" v-model="spec.value">
</div>
</div>
</div>
</td>
@ -80,12 +83,11 @@ var vm = new Vue({
this.spec_group[specIndex].specs.push(
{
name:"NONE",
value:""
value:"0"
}
);
},
delSpecs(specIndex, itemIndex){
console.log("123456")
if(this.spec_group[specIndex].specs[itemIndex]){
if(itemIndex >= 0){
let arr = this.spec_group[specIndex].specs;