4
0
Fork 0
master
panliang 2022-08-09 15:43:28 +08:00
parent 6f64e33a75
commit 7f2c6a9869
21 changed files with 177 additions and 250 deletions

View File

@ -36,17 +36,17 @@
"table": "goods.spec",
"name": "颜色",
"values": [
{ "name": "白色", "price": 0 },
{ "name": "红色", "price": 0 },
{ "name": "蓝色", "price": 1 }
{ "name": "白色", "value": 0 },
{ "name": "红色", "value": 0 },
{ "name": "蓝色", "value": 1 }
]
},
{
"table": "goods.part",
"name": "套餐",
"values": [
{ "name": "套餐1", "price": 150 },
{ "name": "套餐2", "price": 100 }
{ "name": "套餐1", "value": 150 },
{ "name": "套餐2", "value": 100 }
]
},
{

View File

@ -8,6 +8,7 @@ return [
'edit' => '修改',
'attr' => '属性',
'spec' => '规格',
'part' => '配件',
],
'fields' => [
'category_id' => '分类',

View File

@ -1,86 +0,0 @@
<div class="row" style="margin-top: 10px;">
<div class="{{$viewClass['label']}}"><h4 class="pull-right">{!! $label !!}</h4></div>
<div class="{{$viewClass['field']}}"></div>
</div>
<hr class="mt-0">
<div class="has-many-{{$columnClass}}">
<div class="has-many-{{$columnClass}}-forms row">
@foreach($forms as $pk => $form)
<div class="has-many-{{$columnClass}}-form fields-group col-md-4">
{!! $form->render() !!}
@if($options['allowDelete'])
<div class="form-group row">
<label class="{{$viewClass['label']}} control-label"></label>
<div class="{{$viewClass['field']}}">
<div class="{{$columnClass}}-remove btn btn-white btn-sm pull-right"><i class="feather icon-trash">&nbsp;</i>{{ trans('admin.remove') }}</div>
</div>
</div>
@endif
<hr>
</div>
@endforeach
</div>
<template class="{{$columnClass}}-tpl">
<div class="has-many-{{$columnClass}}-form fields-group col-md-4">
{!! $template !!}
<div class="form-group row">
<label class="{{$viewClass['label']}} control-label"></label>
<div class="{{$viewClass['field']}}">
<div class="{{$columnClass}}-remove btn btn-white btn-sm pull-right"><i class="feather icon-trash"></i>&nbsp;{{ trans('admin.remove') }}</div>
</div>
</div>
<hr>
</div>
</template>
@if($options['allowCreate'])
<div class="form-group row">
<label class="{{$viewClass['label']}} control-label"></label>
<div class="{{$viewClass['field']}}">
<div class="{{$columnClass}}-add btn btn-primary btn-outline btn-sm"><i class="feather icon-plus"></i>&nbsp;{{ trans('admin.new') }}</div>
</div>
</div>
@endif
</div>
<script>
var nestedIndex = {!! $count !!},
container = '.has-many-{{ $columnClass }}',
forms = '.has-many-{{ $columnClass }}-forms';
function replaceNestedFormIndex(value) {
return String(value)
.replace(/{{ Dcat\Admin\Form\NestedForm::DEFAULT_KEY_NAME }}/g, nestedIndex)
.replace(/{{ Dcat\Admin\Form\NestedForm::DEFAULT_PARENT_KEY_NAME }}/g, nestedIndex);
}
$(container).on('click', '.{{$columnClass}}-add', function () {
var tpl = $('template.{{ $columnClass }}-tpl');
nestedIndex++;
$(forms).append(replaceNestedFormIndex(tpl.html()));
});
$(container).on('click', '.{{$columnClass}}-remove', function () {
var $form = $(this).closest('.has-many-{{ $columnClass }}-form');
$form.hide();
$form.find('.{{ Dcat\Admin\Form\NestedForm::REMOVE_FLAG_CLASS }}').val(1);
$form.find('[required]').prop('required', false);
});
</script>

View File

@ -22,9 +22,8 @@
</tr>
@foreach($item['values'] as $subItem)
<tr data-pid="{{ $item['name'] }}">
@foreach($keys as $key)
<td class="editable" data-{{$key}}="{{ data_get($subItem, $key) }}">{{ data_get($subItem, $key) }}</td>
@endforeach
<td class="editable" data-name="{{ $subItem['name'] }}">{{ $subItem['name'] }}</td>
<td class="editable" data-value="{{ $subItem['value'] }}">{{ $subItem['value'] }}</td>
<td>
<button type="button" class="btn btn-sm btn-outline-danger delete-item-button">
<i class="fa fa-trash"></i>
@ -33,11 +32,12 @@
</tr>
@endforeach
<tr data-pid="{{$item['name']}}">
@foreach($keys as $index => $key)
<td>
<input type="text" class="form-control add-item-input" placeholder="填写 {{ $headers[$index + 1] }}">
<input type="text" class="form-control add-item-input" placeholder="填写 ">
</td>
<td>
<input type="text" class="form-control add-item-input" placeholder="填写 ">
</td>
@endforeach
<td>
<button type="button" class="btn btn-sm btn-outline-primary add-item-button">
<i class="fa fa-plus"></i>
@ -72,7 +72,6 @@
</style>
<script>
var type = JSON.parse('{!! json_encode($type) !!}')
var keys = JSON.parse('{!! json_encode($keys) !!}')
var headers = JSON.parse('{!! json_encode($headers) !!}')
var element = $('#spec-{{$name}}')
@ -92,12 +91,10 @@
.on('click', '.add-item-button', function () {
var tr = $(this).parents('tr')
var pid = tr.data('pid')
var values = {}
var inputs = tr.find('input.add-item-input')
for(let i = 0; i < keys.length; i++) {
let item = inputs.eq(i)[0]
let key = keys[i]
values[key] = inputs.eq(i).val()
var inputs = tr.find('.add-item-input')
var values = {
name: inputs.eq(0).val(),
value: inputs.eq(1).val(),
}
addItem(pid, values)
// 清空输入框
@ -162,14 +159,8 @@
// 生成子级属性
for (let k = 0; k < item.values.length; k++) {
let subItem = item.values[k]
let baseKey = keys[0]
if (element.find(`tr[data-pid="${item.name}"]`).find(`td[data-name="${subItem[baseKey]}"]`).length == 0) {
let values = {}
for(let j = 0; j < keys.length; j++) {
let key = keys[j]
values[key] = subItem[key] ?? ''
}
addItem(item.name, values)
if (element.find(`tr[data-pid="${item.name}"]`).find(`td[data-name="${subItem}"]`).length == 0) {
addItem(item.name, {name: item.name, value: subItem})
}
}
@ -180,7 +171,7 @@
function addGroup(id) {
var tr = element.find('.add-attr-button').parents('tr')
var html = `<tr data-id="${id}">`
html += `<td rowspan="${keys.length}" class="editable">${id}</td>`
html += `<td rowspan="2" class="editable">${id}</td>`
html += '</tr>'
html += `<tr data-pid="${id}">`
for (let i = 1; i < headers.length; i++) {
@ -196,10 +187,8 @@
// 构造 html
var html = `<tr data-pid="${id}">`;
Object.keys(values).forEach(key => {
let value = values[key]
html += `<td class="editable" data-${key}="${value}">${value}</td>`
})
html += `<td class="editable" data-name="${values.name}">${values.name}</td>`
html += `<td class="editable" data-value="${values.value}">${values.value}</td>`
for(let i = 0; i < values.length; i++) {
html += `<td class="editable">${values[i]}</td>`
}
@ -223,11 +212,7 @@
var subTr = element.find(`[data-pid="${id}"]:not(:last)`)
var subValues = []
subTr.each((key, item) => {
var subValue = {}
for (let k = 0; k < keys.length; k++) {
subValue[keys[k]] = $(item).find('td').eq(k).html()
}
subValues.push(subValue)
subValues.push({name: $(item).find('td').eq(0).html(), value: $(item).find('td').eq(1).html()})
})
values.push({name: id, values: subValues})
}

View File

@ -0,0 +1,22 @@
<!-- $model 当前行数据 -->
<!-- $name 字段名称 -->
<!-- $value 为当前列的值 -->
@if($value)
<div class="grid-attr">
@foreach($value as $item)
<div class="group">
<div class="group-item group-title">
<span class="label bg-danger">{{ $item['name'] }}</span>
</div>
<div class="group-item">
@foreach($item['values'] as $subItem)
<div class="group-value">
<span class="label bg-info">{{ $subItem['name'] }}</span>
<span class="text-danger">{{ $subItem['value'] }}</span>
</div>
@endforeach
</div>
</div>
@endforeach
</div>
@endif

View File

@ -1,11 +0,0 @@
<!-- $model 当前行数据 -->
<!-- $name 字段名称 -->
<!-- $value 为当前列的值 -->
@if($value)
@foreach($value as $item)
<div class="mt-1">
<span class="label bg-info">{{ $item['name'] }}</span>
<span>{{ $item['name'] }}({{ $item['value'] }})</span>
</div>
@endforeach
@endif

View File

@ -1,15 +0,0 @@
<!-- $model 当前行数据 -->
<!-- $name 字段名称 -->
<!-- $value 为当前列的值 -->
@if($value)
@foreach($value as $item)
<div class="mt-1">
<span class="label bg-info">{{ $item['name'] }}</span>
@if($item['values'])
@foreach($item['values'] as $subItem)
<span>{{ $subItem['name'] }}({{ $subItem['value'] }})</span>
@endforeach
@endif
</div>
@endforeach
@endif

View File

@ -1,15 +0,0 @@
<!-- $model 当前行数据 -->
<!-- $name 字段名称 -->
<!-- $value 为当前列的值 -->
@if($value)
@foreach($value as $item)
<div class="mt-1">
<span class="label bg-info">{{ $item['name'] }}</span>
@if($item['values'])
@foreach($item['values'] as $subItem)
<span>{{ $subItem }}</span>
@endforeach
@endif
</div>
@endforeach
@endif

View File

@ -1,15 +0,0 @@
<!-- $model 当前行数据 -->
<!-- $name 字段名称 -->
<!-- $value 为当前列的值 -->
@if($value)
@foreach($value as $item)
<div class="mt-1">
<span class="label bg-info">{{ $item['name'] }}</span>
@if($item['values'])
@foreach($item['values'] as $subItem)
<span>{{ $subItem['value'] }}({{ $subItem['price'] }})</span>
@endforeach
@endif
</div>
@endforeach
@endif

View File

@ -1,11 +0,0 @@
<!-- $model 当前行数据 -->
<!-- $name 字段名称 -->
<!-- $value 为当前列的值 -->
@if($value)
@foreach($value as $item)
<div class="mt-1">
<span class="label bg-info">{{ $item['name'] }}</span>
<span>{{ $item['value'] }}({{ $item['price'] }})</span>
</div>
@endforeach
@endif

View File

@ -1,15 +0,0 @@
<!-- $model 当前行数据 -->
<!-- $name 字段名称 -->
<!-- $value 为当前列的值 -->
@if($value)
@foreach($value as $item)
<div class="mt-1">
<span class="label bg-info">{{ $item['name'] }}</span>
@if($item['values'])
@foreach($item['values'] as $subItem)
<span>{{ $subItem['value'] }}({{ $subItem['price'] }})</span>
@endforeach
@endif
</div>
@endforeach
@endif

View File

@ -1,13 +1,13 @@
<?php
namespace Peidikeji\Goods\Form;
namespace Peidikeji\Goods\Form\Goods;
use Dcat\Admin\Contracts\LazyRenderable;
use Dcat\Admin\Traits\LazyWidget;
use Dcat\Admin\Widgets\Form;
use Peidikeji\Goods\Models\Goods;
class GoodsAttrForm extends Form implements LazyRenderable
class AttrForm extends Form implements LazyRenderable
{
use LazyWidget;
@ -22,7 +22,7 @@ class GoodsAttrForm extends Form implements LazyRenderable
public function form()
{
$attr = $this->model()->type?->attr;
$this->spec('attr')->header(['分组', '名称', '属性值'])->keys(['name', 'value'])->type($attr);
$this->spec('attr')->header(['分组', '名称', '属性值'])->type($attr);
}
protected function renderResetButton()

View File

@ -0,0 +1,42 @@
<?php
namespace Peidikeji\Goods\Form\Goods;
use Dcat\Admin\Contracts\LazyRenderable;
use Dcat\Admin\Traits\LazyWidget;
use Dcat\Admin\Widgets\Form;
use Peidikeji\Goods\Models\Goods;
class PartForm extends Form implements LazyRenderable
{
use LazyWidget;
public function handle(array $input)
{
$goods = Goods::findOrFail($this->payload['goods_id']);
$part = json_decode($input['part'], true);
foreach($part as &$item) {
foreach($item['values'] as &$subItem) {}
$subItem['value'] = floatval($subItem['value']);
}
$goods->update(['part' => $part]);
return $this->response()->success('保存成功');
}
public function form()
{
$part = $this->model()->type?->part;
$this->spec('part')->header(['名称', '可选值', '价格'])->type($part);
}
protected function renderResetButton()
{
return "<a href=\"javascript:window.history.back()\" class=\"btn btn-white pull-left\"><i class=\"feather icon-arrow-left\"></i> 返回</a>";
}
protected function getSubmitButtonLabel()
{
return '保存';
}
}

View File

@ -1,13 +1,13 @@
<?php
namespace Peidikeji\Goods\Form;
namespace Peidikeji\Goods\Form\Goods;
use Dcat\Admin\Contracts\LazyRenderable;
use Dcat\Admin\Traits\LazyWidget;
use Dcat\Admin\Widgets\Form;
use Peidikeji\Goods\Models\Goods;
class GoodsSpecForm extends Form implements LazyRenderable
class SpecForm extends Form implements LazyRenderable
{
use LazyWidget;
@ -17,7 +17,7 @@ class GoodsSpecForm extends Form implements LazyRenderable
$spec = json_decode($input['spec'], true);
foreach($spec as &$item) {
foreach($item['values'] as &$subItem) {}
$subItem['price'] = floatval($subItem['price']);
$subItem['value'] = floatval($subItem['value']);
}
$goods->update(['spec' => $spec]);
return $this->response()->success('保存成功');
@ -27,7 +27,7 @@ class GoodsSpecForm extends Form implements LazyRenderable
{
$spec = $this->model()->type?->spec;
$this->spec('spec')->header(['名称', '可选值', '价格'])->keys(['value', 'price'])->type($spec);
$this->spec('spec')->header(['名称', '可选值', '价格'])->type($spec);
}
protected function renderResetButton()

View File

@ -0,0 +1,36 @@
<?php
namespace Peidikeji\Goods\Form\GoodsType;
use Dcat\Admin\Contracts\LazyRenderable;
use Dcat\Admin\Traits\LazyWidget;
use Dcat\Admin\Widgets\Form;
use Peidikeji\Goods\Models\GoodsType;
class PartForm extends Form implements LazyRenderable
{
use LazyWidget;
public function handle(array $input)
{
$info = GoodsType::findOrFail($this->payload['type_id']);
$info->update(['part' => json_decode($input['part'])]);
return $this->response()->success('保存成功');
}
public function form()
{
$this->fill(['part' => $this->payload['part']]);
$this->attr('part')->header(['名称', '可选值'])->keys(['name']);
$reset = data_get($this->payload, 'reset', true);
$this->resetButton(!!$reset);
}
protected function renderResetButton()
{
return (! empty($this->buttons['reset'])) ? "<a href=\"javascript:window.history.back()\" class=\"btn btn-white pull-left\"><i class=\"feather icon-arrow-left\"></i> 返回</a>" : '';
}
}

View File

@ -11,7 +11,6 @@ class Spec extends Field
protected $variables = [
'headers' => [],
'keys' => [],
'type' => null,
'formatValue' => [],
];
@ -25,15 +24,6 @@ class Spec extends Field
return $this;
}
public function keys($keys)
{
$this->addVariables([
'keys' => $keys,
]);
return $this;
}
public function type($type)
{
$this->addVariables(['type' => $type]);

View File

@ -44,8 +44,8 @@ class GoodsService
foreach($item['values'] as $value) {
array_push($items, [
'name' => $item['name'],
'value' => $value['value'],
'price' => floatval($value['price']),
'value' => $value['name'],
'price' => floatval($value['value']),
]);
}
array_push($specList, $items);

View File

@ -9,10 +9,10 @@ use Dcat\Admin\Grid\Tools\Selector;
use Dcat\Admin\Http\Controllers\AdminController;
use Dcat\Admin\Layout\Content;
use Dcat\Admin\Show;
use Dcat\Admin\Widgets\Card;
use Illuminate\Validation\Rule;
use Peidikeji\Goods\Form\GoodsAttrForm;
use Peidikeji\Goods\Form\GoodsSpecForm;
use Peidikeji\Goods\Form\Goods\AttrForm;
use Peidikeji\Goods\Form\Goods\PartForm;
use Peidikeji\Goods\Form\Goods\SpecForm;
use Peidikeji\Goods\Models\Goods;
use Peidikeji\Goods\Models\GoodsBrand;
use Peidikeji\Goods\Models\GoodsCategory;
@ -26,7 +26,7 @@ class GoodsController extends AdminController
public function attr($goods, Content $content)
{
$goods = Goods::with(['type'])->findOrFail($goods);
$form = GoodsAttrForm::make([
$form = AttrForm::make([
'type' => $goods->type,
'attr' => $goods->attr
])->payload(['type_id' => $goods->type?->id, 'goods_id' => $goods->id])->appendHtmlAttribute('class', 'bg-white');
@ -40,7 +40,7 @@ class GoodsController extends AdminController
public function spec($goods, Content $content)
{
$goods = Goods::findOrFail($goods);
$form = GoodsSpecForm::make([
$form = SpecForm::make([
'type' => $goods->type,
'spec' => $goods->spec
])->payload(['type_id' => $goods->type_id, 'goods_id' => $goods->id])->appendHtmlAttribute('class', 'bg-white');
@ -50,6 +50,19 @@ class GoodsController extends AdminController
->description($goods->type?->name)
->body($form);
}
public function part($goods, Content $content)
{
$goods = Goods::findOrFail($goods);
$form = PartForm::make([
'type' => $goods->type,
'part' => $goods->part
])->payload(['type_id' => $goods->type_id, 'goods_id' => $goods->id])->appendHtmlAttribute('class', 'bg-white');
return $content
->translation($this->translation())
->title($goods->name)
->description($goods->type?->name)
->body($form);
}
protected function grid()
{
@ -86,7 +99,7 @@ class GoodsController extends AdminController
return ($this->cover_image ? '<img src="'.$this->cover_image.'" width="60" class="img-thumbnail"/>&nbsp;' : '') . '<a href="'.admin_url('goods/' . $this->id).'">'.$this->name.'</a>';
});
$grid->column('price')->editable();
$grid->column('spec')->view('peidikeji.dcat-admin-extension-goods::grid.specs');
$grid->column('spec')->view('peidikeji.dcat-admin-extension-goods::goods.grid-attr');
$grid->column('on_sale')->switch();
$grid->column('sold_count');
@ -97,6 +110,7 @@ class GoodsController extends AdminController
$actions->append('<a href="'.admin_route('goods_sku.index', ['goods' => $row->id]).'" class="">货品信息</a>');
$actions->append('<a href="'.admin_route('goods.attr', ['goods' => $row->id]).'" class="">商品属性</a>');
$actions->append('<a href="'.admin_route('goods.spec', ['goods' => $row->id]).'" class="">商品规格</a>');
$actions->append('<a href="'.admin_route('goods.part', ['goods' => $row->id]).'" class="">商品配件</a>');
});
});
}
@ -114,9 +128,9 @@ class GoodsController extends AdminController
$show->field('cover_image')->image('', 100);
$show->field('images')->image('', 100);
$show->field('content')->image('');
$show->field('spec')->view('peidikeji.dcat-admin-extension-goods::grid.specs');
$show->field('attr')->view('peidikeji.dcat-admin-extension-goods::grid.attrs');
$show->field('part')->view('peidikeji.dcat-admin-extension-goods::grid.parts');
$show->field('spec')->view('peidikeji.dcat-admin-extension-goods::goods.grid-attr');
$show->field('attr')->view('peidikeji.dcat-admin-extension-goods::goods.grid-attr');
$show->field('part')->view('peidikeji.dcat-admin-extension-goods::goods.grid-attr');
$show->field('on_sale')->bool();
$show->field('sold_count');
$show->field('created_at')->as(fn($v) => $this->created_at->format('Y-m-d H:i:s'));

View File

@ -7,6 +7,7 @@ use Dcat\Admin\Grid;
use Dcat\Admin\Grid\Displayers\Modal;
use Dcat\Admin\Http\Controllers\AdminController;
use Peidikeji\Goods\Form\GoodsType\AttrForm;
use Peidikeji\Goods\Form\GoodsType\PartForm;
use Peidikeji\Goods\Form\GoodsType\SpecForm;
use Peidikeji\Goods\Models\Goods;
use Peidikeji\Goods\Models\GoodsType;
@ -31,7 +32,10 @@ class GoodsTypeController extends AdminController
$modal->icon('');
return SpecForm::make()->payload(['type_id' => $this->id, 'reset' => false, 'spec' => $this->spec]);
});
$grid->column('part')->view('peidikeji.dcat-admin-extension-goods::goods-type.grid-attr');
$grid->column('part')->display(fn() => view('peidikeji.dcat-admin-extension-goods::goods-type.grid-attr', ['value' => $this->part]))->modal(__('peidikeji.dcat-admin-extension-goods::goods-type.fields.part'), function (Modal $modal) {
$modal->icon('');
return PartForm::make()->payload(['type_id' => $this->id, 'reset' => false, 'part' => $this->part]);
});
});
}

View File

@ -12,4 +12,5 @@ Route::resource('goods/{goods}/sku', GoodsSkuController::class)->names('goods_sk
Route::get('goods/{goods}/attr', [GoodsController::class, 'attr'])->name('goods.attr');
Route::get('goods/{goods}/spec', [GoodsController::class, 'spec'])->name('goods.spec');
Route::get('goods/{goods}/part', [GoodsController::class, 'part'])->name('goods.part');
Route::resource('goods', GoodsController::class);

View File

@ -98,21 +98,21 @@ class GoodsTableSeeder extends Seeder
],
'spec' => [
['name' => '颜色', 'values' => [
['value' => '白色', 'price' => 0],
['value' => '红色', 'price' => 800],
['value' => '黑色', 'price' => 0],
['name' => '白色', 'value' => 0],
['name' => '红色', 'value' => 800],
['name' => '黑色', 'value' => 0],
]],
['name' => '内存', 'values' => [
['value' => '32G', 'price' => 0],
['value' => '64G', 'price' => 1000],
['value' => '128G', 'price' => 2000],
['name' => '32G', 'value' => 0],
['name' => '64G', 'value' => 1000],
['name' => '128G', 'value' => 2000],
]]
],
'part' => [
['name' => '套餐', 'values' => [
['value' => '套餐1', 'price' => 850],
['value' => '套餐2', 'price' => 1200],
['value' => '套餐3', 'price' => 1800],
['name' => '套餐1', 'value' => 850],
['name' => '套餐2', 'value' => 1200],
['name' => '套餐3', 'value' => 1800],
]]
],
],
@ -140,20 +140,20 @@ class GoodsTableSeeder extends Seeder
],
'spec' => [
['name' => '颜色', 'values' => [
['value' => '白色', 'price' => 0],
['value' => '灰色', 'price' => 0]
['name' => '白色', 'value' => 0],
['name' => '灰色', 'value' => 0]
]],
['name' => '内存', 'values' => [
['value' => '16G', 'price' => 0],
['value' => '32G', 'price' => 3000],
['value' => '64G', 'price' => 6000],
['name' => '16G', 'value' => 0],
['name' => '32G', 'value' => 3000],
['name' => '64G', 'value' => 6000],
]]
],
'part' => [
['name' => '套餐', 'values' => [
['value' => '优惠套装1', 'price' => 850],
['value' => '优惠套装2', 'price' => 650],
['value' => '优惠套装3', 'price' => 1000],
['name' => '优惠套装1', 'value' => 850],
['name' => '优惠套装2', 'value' => 650],
['name' => '优惠套装3', 'value' => 1000],
]]
],
]