goods
parent
6a3b9d2613
commit
6f64e33a75
|
|
@ -2,6 +2,11 @@
|
|||
|
||||
Fork From [jqhph/dcat-admin](https://github.com/jqhph/dcat-admin)
|
||||
|
||||
## 安装
|
||||
|
||||
- `composer config repositories.peidikeji/dcat-admin git git@gitee.com:paddy_technology/dcat-admin.git`
|
||||
- `composer require peidikeji/dcat-admin`
|
||||
|
||||
## 改动
|
||||
|
||||
- 文件: `dcat-admin/src/Form/Footer.php`, 将 `reset` 按钮默认设置为 `false`
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
[
|
||||
{
|
||||
"table": "goods-type.spec",
|
||||
"name": "颜色",
|
||||
"values": [
|
||||
"白色",
|
||||
"红色",
|
||||
"蓝色"
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "goods-type.attr",
|
||||
"name": "主体",
|
||||
"values": [
|
||||
"入网型号",
|
||||
"上市年份"
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "goods-type.part",
|
||||
"name": "套餐",
|
||||
"values": [
|
||||
"套餐1",
|
||||
"套餐2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "goods.attr",
|
||||
"name": "主体",
|
||||
"values": [
|
||||
{ "name": "入网型号", "value": "5G" },
|
||||
{ "name": "上市年份", "value": "2020" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "goods.spec",
|
||||
"name": "颜色",
|
||||
"values": [
|
||||
{ "name": "白色", "price": 0 },
|
||||
{ "name": "红色", "price": 0 },
|
||||
{ "name": "蓝色", "price": 1 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "goods.part",
|
||||
"name": "套餐",
|
||||
"values": [
|
||||
{ "name": "套餐1", "price": 150 },
|
||||
{ "name": "套餐2", "price": 100 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "goods-sku.attr",
|
||||
"name": "主体",
|
||||
"values": [
|
||||
{ "name": "入网型号", "value": "5G" },
|
||||
{ "name": "上市年份", "value": "2020" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "goods-sku.spec",
|
||||
"name": "颜色",
|
||||
"value": "白色",
|
||||
"price": 0
|
||||
},
|
||||
{
|
||||
"table": "goods-sku.part",
|
||||
"name": "颜色",
|
||||
"values": [
|
||||
{ "name": "套餐1", "price": 150 },
|
||||
{ "name": "套餐2", "price": 100 }
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
.grid-attr .group:not(:first-child) {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.grid-attr .group-item {
|
||||
display: inline-block;vertical-align: top;
|
||||
}
|
||||
.grid-attr .group-title {
|
||||
min-width: 80px
|
||||
}
|
||||
.grid-attr .group-value:not(:first-child) {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
|
@ -0,0 +1,227 @@
|
|||
<div class="table-responsive p-1" id="spec-{{$name}}">
|
||||
<input type="hidden" name="{{$name}}" value="{{ json_encode($value, JSON_UNESCAPED_UNICODE) }}">
|
||||
<table class="table table-bordered table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
@foreach($headers as $item)
|
||||
<td>{{ $item }}</td>
|
||||
@endforeach
|
||||
<td></td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($value as $index => $item)
|
||||
<tr data-id="{{ $item['name'] }}">
|
||||
<td rowspan="{{count($item['values']) + 2}}" class="editable">{{ $item['name'] }}</td>
|
||||
</tr>
|
||||
@foreach($item['values'] as $subItem)
|
||||
<tr data-pid="{{ $item['name'] }}">
|
||||
<td class="editable">{{ $subItem }}</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger delete-item-button">
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
<tr data-pid="{{$item['name']}}">
|
||||
<td>
|
||||
<input type="text" class="form-control add-item-input" placeholder="填写 {{ $headers[$index] }}">
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary add-item-button">
|
||||
<i class="fa fa-plus"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
<tr>
|
||||
<td colspan="{{ count($headers) }}">
|
||||
<input type="text" class="form-control add-attr-input" placeholder="添加 {{ $headers[0] }}">
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-sm btn-outline-warning add-attr-button">
|
||||
<i class="fa fa-plus"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<style>
|
||||
.table {
|
||||
text-align: center;
|
||||
}
|
||||
.table td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.editable {
|
||||
color: #586cb1;
|
||||
cursor: pointer;
|
||||
}
|
||||
</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}}')
|
||||
|
||||
// 添加父级属性
|
||||
element.on('click', '.add-attr-button', function () {
|
||||
var value = $('.add-attr-input').val()
|
||||
if (!value) {
|
||||
return Dcat.swal.warning(`请填写 ${headers.name}`)
|
||||
}
|
||||
|
||||
addGroup(value)
|
||||
|
||||
$('.add-attr-input').val('')
|
||||
updateInputValue()
|
||||
})
|
||||
// 添加子属性
|
||||
.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()
|
||||
}
|
||||
addItem(pid, values)
|
||||
// 清空输入框
|
||||
inputs.each((key, item) => {
|
||||
item.value = ''
|
||||
})
|
||||
|
||||
updateInputValue()
|
||||
})
|
||||
// 删除子属性
|
||||
.on('click', '.delete-item-button', function () {
|
||||
var tr = $(this).parents('tr')
|
||||
var pid = tr.data('pid')
|
||||
var parent = $('tr[data-id="'+pid+'"]')
|
||||
var parentTd = parent.find('td').first()
|
||||
parentTd.attr('rowspan', parseInt(parentTd.attr('rowspan')) - 1)
|
||||
tr.remove()
|
||||
|
||||
// 子属性全部删除, 删除父级属性
|
||||
if (element.find(`tr[data-pid="${pid}"]`).length <= 1) {
|
||||
element.find(`tr[data-pid="${pid}"]`).remove()
|
||||
element.find(`tr[data-id="${pid}"]`).remove()
|
||||
}
|
||||
|
||||
updateInputValue()
|
||||
})
|
||||
// 修改
|
||||
.on('click', '.editable', function () {
|
||||
var td = $(this)
|
||||
var tr = td.parents('tr')
|
||||
var value = td.html()
|
||||
Dcat.swal.fire({
|
||||
input: 'text',
|
||||
inputValue: value,
|
||||
showCancelButton: true,
|
||||
cancelButtonText: '取消',
|
||||
confirmButtonText: '确定',
|
||||
}).then(result => {
|
||||
if (result.value !== undefined && result.value) {
|
||||
// 修改父级属性
|
||||
if (tr.attr('data-id')) {
|
||||
var pid = tr.attr('data-id')
|
||||
$('tr[data-pid="'+pid+'"]').attr('data-pid', result.value)
|
||||
tr.attr('data-id', result.value)
|
||||
}
|
||||
td.html(result.value)
|
||||
|
||||
updateInputValue()
|
||||
}
|
||||
})
|
||||
})
|
||||
// 重新生成
|
||||
.on('click', '.generate-button', function () {
|
||||
var trs = $(this).parents('table').find('tr[data-id]')
|
||||
for (let i = 0; i < type.length; i++) {
|
||||
let item = type[i]
|
||||
|
||||
// 生成父级属性
|
||||
if (element.find(`tr[data-id="${item.name}"]`).length === 0) {
|
||||
addGroup(item.name)
|
||||
}
|
||||
// 生成子级属性
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
updateInputValue()
|
||||
})
|
||||
|
||||
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 += '</tr>'
|
||||
html += `<tr data-pid="${id}">`
|
||||
for (let i = 1; i < headers.length; i++) {
|
||||
html += `<td><input type="text" class="form-control add-item-input" placeholder="填写 ${headers[i]}"></td>`
|
||||
}
|
||||
html += '<td><button type="button" class="btn btn-sm btn-outline-primary add-item-button"><i class="fa fa-plus"></i></button></td>'
|
||||
html += '</tr>'
|
||||
tr.before(html)
|
||||
}
|
||||
|
||||
function addItem(id, values) {
|
||||
var tr = element.find(`tr[data-id="${id}"]`)
|
||||
|
||||
// 构造 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>`
|
||||
})
|
||||
for(let i = 0; i < values.length; i++) {
|
||||
html += `<td class="editable">${values[i]}</td>`
|
||||
}
|
||||
html += '<td><button type="button" class="btn btn-sm btn-outline-danger delete-item-button"><i class="fa fa-trash"></i></button></td></tr>'
|
||||
|
||||
// 修改 rowspan
|
||||
var parentTd = tr.find('td').first()
|
||||
parentTd.attr('rowspan', parseInt(parentTd.attr('rowspan')) + 1)
|
||||
|
||||
// 追加 html
|
||||
$(`tr[data-pid="${id}"]:last`).before(html)
|
||||
}
|
||||
|
||||
// 整合表格里面的值
|
||||
function formatValue() {
|
||||
var values = []
|
||||
var tr = element.find('tr[data-id]')
|
||||
for (let i = 0; i < tr.length; i++) {
|
||||
var item = tr.eq(i)
|
||||
var id = item.data('id')
|
||||
var subTr = element.find(`[data-pid="${id}"]:not(:last)`)
|
||||
var subValues = []
|
||||
subTr.each((key, ele) => {
|
||||
subValues.push($(ele).find('td:first-child').html())
|
||||
})
|
||||
values.push({name: id, values: subValues})
|
||||
}
|
||||
console.log(values)
|
||||
return values
|
||||
}
|
||||
|
||||
function updateInputValue() {
|
||||
element.find('input[name="{{$name}}"]').val(JSON.stringify(formatValue()))
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<!-- $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 }}</span>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace Peidikeji\Goods\Form;
|
||||
|
||||
use Dcat\Admin\Form\Field;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class Attr extends Field
|
||||
{
|
||||
protected $view = 'peidikeji.dcat-admin-extension-goods::form.attr';
|
||||
|
||||
protected $variables = [
|
||||
'headers' => [],
|
||||
'keys' => [],
|
||||
'type' => null
|
||||
];
|
||||
|
||||
public function header(array $headers)
|
||||
{
|
||||
$this->addVariables([
|
||||
'headers' => $headers,
|
||||
]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function keys($keys)
|
||||
{
|
||||
$this->addVariables([
|
||||
'keys' => $keys,
|
||||
]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function type($type)
|
||||
{
|
||||
$this->addVariables(['type' => $type]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -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 AttrForm extends Form implements LazyRenderable
|
||||
{
|
||||
use LazyWidget;
|
||||
|
||||
public function handle(array $input)
|
||||
{
|
||||
$info = GoodsType::findOrFail($this->payload['type_id']);
|
||||
$info->update(['attr' => json_decode($input['attr'])]);
|
||||
|
||||
return $this->response()->success('保存成功');
|
||||
}
|
||||
|
||||
public function form()
|
||||
{
|
||||
$this->fill(['attr' => $this->payload['attr']]);
|
||||
|
||||
$this->attr('attr')->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>" : '';
|
||||
}
|
||||
}
|
||||
|
|
@ -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 SpecForm extends Form implements LazyRenderable
|
||||
{
|
||||
use LazyWidget;
|
||||
|
||||
public function handle(array $input)
|
||||
{
|
||||
$info = GoodsType::findOrFail($this->payload['type_id']);
|
||||
$info->update(['spec' => json_decode($input['spec'])]);
|
||||
|
||||
return $this->response()->success('保存成功');
|
||||
}
|
||||
|
||||
public function form()
|
||||
{
|
||||
$this->fill(['spec' => $this->payload['spec']]);
|
||||
|
||||
$this->attr('spec')->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>" : '';
|
||||
}
|
||||
}
|
||||
|
|
@ -5,10 +5,14 @@ namespace Peidikeji\Goods;
|
|||
use Dcat\Admin\Extend\ServiceProvider;
|
||||
use Dcat\Admin\Admin;
|
||||
use Dcat\Admin\Form;
|
||||
use Peidikeji\Goods\Form\Attr;
|
||||
use Peidikeji\Goods\Form\Spec;
|
||||
|
||||
class GoodsServiceProvider extends ServiceProvider
|
||||
{
|
||||
protected $css = [
|
||||
'goods.css'
|
||||
];
|
||||
protected $menu = [
|
||||
['title' => '商品管理', 'uri' => '', 'icon' => ''],
|
||||
['title' => '商品分类', 'uri' => 'goods/category', 'icon' => '', 'parent' => '商品管理'],
|
||||
|
|
@ -21,5 +25,8 @@ class GoodsServiceProvider extends ServiceProvider
|
|||
{
|
||||
parent::init();
|
||||
Form::extend('spec', Spec::class);
|
||||
Form::extend('attr', Attr::class);
|
||||
|
||||
Admin::requireAssets(['@peidikeji.dcat-admin-extension-goods']);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ class GoodsSkuController extends Controller
|
|||
$grid->column('id');
|
||||
$grid->column('sn');
|
||||
$grid->column('name');
|
||||
$grid->column('origin_price')->display(fn() => $goods->price);
|
||||
// $grid->column('reset')->display(fn() => $goods->price);
|
||||
$grid->column('price');
|
||||
$grid->column('stock');
|
||||
if ($goods->spec) {
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@
|
|||
namespace Peidikeji\Goods\Http\Controllers\Admin;
|
||||
|
||||
use Dcat\Admin\Form;
|
||||
use Dcat\Admin\Form\NestedForm;
|
||||
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\SpecForm;
|
||||
use Peidikeji\Goods\Models\Goods;
|
||||
use Peidikeji\Goods\Models\GoodsType;
|
||||
|
||||
|
|
@ -18,32 +20,30 @@ class GoodsTypeController extends AdminController
|
|||
return Grid::make(new GoodsType(), function (Grid $grid) {
|
||||
$grid->disableRowSelector();
|
||||
$grid->disableViewButton();
|
||||
$grid->disableEditButton();
|
||||
|
||||
$grid->column('name');
|
||||
$grid->column('attr')->view('peidikeji.dcat-admin-extension-goods::grid.part');
|
||||
$grid->column('spec')->view('peidikeji.dcat-admin-extension-goods::grid.part');
|
||||
$grid->column('part')->view('peidikeji.dcat-admin-extension-goods::grid.part');
|
||||
$grid->column('name')->editable();
|
||||
$grid->column('attr')->display(fn() => view('peidikeji.dcat-admin-extension-goods::goods-type.grid-attr', ['value' => $this->attr]))->modal(__('peidikeji.dcat-admin-extension-goods::goods-type.fields.attr'), function (Modal $modal) {
|
||||
$modal->icon('');
|
||||
return AttrForm::make()->payload(['type_id' => $this->id, 'reset' => false, 'attr' => $this->attr]);
|
||||
});
|
||||
$grid->column('spec')->display(fn() => view('peidikeji.dcat-admin-extension-goods::goods-type.grid-attr', ['value' => $this->spec]))->modal(__('peidikeji.dcat-admin-extension-goods::goods-type.fields.spec'), function (Modal $modal) {
|
||||
$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');
|
||||
});
|
||||
}
|
||||
|
||||
protected function form()
|
||||
{
|
||||
return Form::make(new GoodsType(), function (Form $form) {
|
||||
|
||||
$form->text('name');
|
||||
|
||||
$form->array('attr', function (NestedForm $table) {
|
||||
$table->text('name')->required();
|
||||
$table->text('group');
|
||||
$table->list('values');
|
||||
});
|
||||
$form->array('spec', function (NestedForm $table) {
|
||||
$table->text('name')->required();
|
||||
$table->list('values');
|
||||
});
|
||||
$form->array('part', function (NestedForm $table) {
|
||||
$table->text('name')->required();
|
||||
$table->list('values');
|
||||
});
|
||||
// $form->textarea('attr');
|
||||
// $form->textarea('spec');
|
||||
// $form->textarea('part');
|
||||
|
||||
$form->disableResetButton();
|
||||
$form->disableCreatingCheck();
|
||||
|
|
|
|||
|
|
@ -36,11 +36,8 @@ class GoodsTableSeeder extends Seeder
|
|||
[
|
||||
'name' => '手机',
|
||||
'attr' => [
|
||||
['name' => '主体', 'values' => [
|
||||
['name' => '入网型号', 'values' => ['5G', '4G']],
|
||||
['name' => '上市年份', 'values' => null],
|
||||
['name' => '品牌', 'values' => null]
|
||||
]]
|
||||
['name' => '主体', 'values' => ['入网型号', '上市年份', '品牌']],
|
||||
['name' => '显示器', 'values' => ['屏幕类型', '物理分辨率']],
|
||||
],
|
||||
'spec' => [
|
||||
['name' => '颜色', 'values' => ['白色', '红色', '黑色']],
|
||||
|
|
@ -53,10 +50,7 @@ class GoodsTableSeeder extends Seeder
|
|||
[
|
||||
'name' => '笔记本电脑',
|
||||
'attr' => [
|
||||
['name' => '显示器', 'values' => [
|
||||
['name' => '屏幕类型', 'values' => ['LED 背光显示屏']],
|
||||
['name' => '物理分辨率', 'values' => ['3072 x 1920 (226 ppi)']]
|
||||
]],
|
||||
['name' => '显示器', 'values' => ['屏幕类型', '物理分辨率']],
|
||||
],
|
||||
'spec' => [
|
||||
['name' => '颜色', 'values' => ['白色', '灰色']],
|
||||
|
|
|
|||
Loading…
Reference in New Issue