Merge branch 'master' of https://gitee.com/paddy_technology/dcat-admin
commit
6a3b9d2613
51
README.md
51
README.md
|
|
@ -4,17 +4,52 @@ Fork From [jqhph/dcat-admin](https://github.com/jqhph/dcat-admin)
|
|||
|
||||
## 改动
|
||||
|
||||
### Show
|
||||
- 文件: `dcat-admin/src/Form/Footer.php`, 将 `reset` 按钮默认设置为 `false`
|
||||
|
||||
### 详细页-列表按钮点击跳转方式
|
||||
```php
|
||||
protected $buttons = ['reset' => false, 'submit' => true, 'back' => true];
|
||||
|
||||
- `packages/dcat-admin/src/Show/Tools.php` 254行
|
||||
|
||||
```html
|
||||
<a href="{$this->getListPath()}" class="btn btn-sm btn-primary ">
|
||||
public function disableBack(bool $disable = true)
|
||||
{
|
||||
$this->buttons['back'] = !$disable;
|
||||
}
|
||||
```
|
||||
|
||||
```html
|
||||
<a href="javascript:window.history.back()" class="btn btn-sm btn-primary ">
|
||||
- 添加 Form 表单的 `back` 按钮, 文件: `dcat-admin/resources/views/form/footer.blade.php`
|
||||
|
||||
```php
|
||||
@if(! empty($buttons['back']))
|
||||
<div class="btn-group pull-left">
|
||||
<a href="javascript:window.history.back()" class="btn btn-white"><i class="feather icon-arrow-left"></i> {{ trans('admin.back') }}</a>
|
||||
</div>
|
||||
@endif
|
||||
```
|
||||
|
||||
- 文件: `dcat-admin/src/Show/Tools.php`, 添加 `back` 按钮, `list` 默认设置为 `false`, 修改按钮的渲染方式(去掉外层的 `btn-group` 标签, 添加类名 `mr-1`)
|
||||
|
||||
```php
|
||||
protected $tools = ['back', 'list', 'edit', 'delete'];
|
||||
|
||||
protected $showBack = true;
|
||||
|
||||
public function disableBack(bool $disable = true)
|
||||
{
|
||||
$this->showBack = !$disable;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function renderBack()
|
||||
{
|
||||
if (! $this->showBack) {
|
||||
return;
|
||||
}
|
||||
|
||||
$back = trans('admin.back');
|
||||
|
||||
return <<<HTML
|
||||
<a href="javascript:window.history.back()" class="btn btn-sm btn-primary mr-1">
|
||||
<i class="feather icon-arrow-left"></i><span class="d-none d-sm-inline"> {$back}</span>
|
||||
</a>
|
||||
HTML;
|
||||
}
|
||||
```
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ return [
|
|||
'GoodsBrand' => '品牌管理',
|
||||
'goods' => '商品管理',
|
||||
'brand' => '品牌',
|
||||
'create' => '创建',
|
||||
'edit' => '修改',
|
||||
],
|
||||
'fields' => [
|
||||
'name' => '名称',
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ return [
|
|||
'labels' => [
|
||||
'GoodsSku' => '货品管理',
|
||||
'goods' => '商品管理',
|
||||
'sku' => '商品管理',
|
||||
'sku' => '货品管理',
|
||||
'create' => '添加',
|
||||
],
|
||||
'fields' => [
|
||||
'sn' => '货号',
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ return [
|
|||
'goods' => '商品信息',
|
||||
'create' => '创建',
|
||||
'edit' => '修改',
|
||||
'attr' => '属性',
|
||||
'spec' => '规格',
|
||||
],
|
||||
'fields' => [
|
||||
'category_id' => '分类',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,86 @@
|
|||
|
||||
<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"> </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> {{ 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> {{ 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>
|
||||
|
|
@ -0,0 +1,240 @@
|
|||
<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>
|
||||
@if($type)
|
||||
<button type="button" class="btn btn-sm btn-outline-primary generate-button">
|
||||
<i class="fa fa-plus-square"></i>
|
||||
</button>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($formatValue as $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'] }}">
|
||||
@foreach($keys as $key)
|
||||
<td class="editable" data-{{$key}}="{{ data_get($subItem, $key) }}">{{ data_get($subItem, $key) }}</td>
|
||||
@endforeach
|
||||
<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']}}">
|
||||
@foreach($keys as $index => $key)
|
||||
<td>
|
||||
<input type="text" class="form-control add-item-input" placeholder="填写 {{ $headers[$index + 1] }}">
|
||||
</td>
|
||||
@endforeach
|
||||
<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, item) => {
|
||||
var subValue = {}
|
||||
for (let k = 0; k < keys.length; k++) {
|
||||
subValue[keys[k]] = $(item).find('td').eq(k).html()
|
||||
}
|
||||
subValues.push(subValue)
|
||||
})
|
||||
values.push({name: id, values: subValues})
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
function updateInputValue() {
|
||||
element.find('input[name="{{$name}}"]').val(JSON.stringify(formatValue()))
|
||||
}
|
||||
</script>
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
@foreach($value as $item)
|
||||
<div class="mt-1">
|
||||
<span class="label bg-info">{{ $item['name'] }}</span>
|
||||
<span>{{ $item['value'] }} {{ isset($item['price']) ? '(+'.$item['price'].')' : '' }}</span>
|
||||
<span>{{ $item['name'] }}({{ $item['value'] }})</span>
|
||||
</div>
|
||||
@endforeach
|
||||
@endif
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
<!-- $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
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<!-- $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
|
||||
|
|
@ -5,11 +5,7 @@
|
|||
@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
|
||||
<span>{{ $item['value'] }}({{ $item['price'] }})</span>
|
||||
</div>
|
||||
@endforeach
|
||||
@endif
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
<!-- $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
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Peidikeji\Goods\Actions;
|
||||
|
||||
use Dcat\Admin\Grid\RowAction;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class RowGoodsSkuList extends RowAction
|
||||
{
|
||||
protected $title = '货品信息';
|
||||
|
||||
public function handle(Request $request)
|
||||
{
|
||||
return $this->response()->redirect(admin_route('goods-sku.index', ['goods' => $this->getKey()]));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace Peidikeji\Goods\Form;
|
||||
|
||||
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
|
||||
{
|
||||
use LazyWidget;
|
||||
|
||||
public function handle(array $input)
|
||||
{
|
||||
$goods = Goods::findOrFail($this->payload['goods_id']);
|
||||
$goods->update(['attr' => json_decode($input['attr'])]);
|
||||
|
||||
return $this->response()->success('保存成功');
|
||||
}
|
||||
|
||||
public function form()
|
||||
{
|
||||
$attr = $this->model()->type?->attr;
|
||||
$this->spec('attr')->header(['分组', '名称', '属性值'])->keys(['name', 'value'])->type($attr);
|
||||
}
|
||||
|
||||
protected function renderResetButton()
|
||||
{
|
||||
return "<a href=\"javascript:window.history.back()\" class=\"btn btn-white pull-left\"><i class=\"feather icon-arrow-left\"></i> 返回</a>";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace Peidikeji\Goods\Form;
|
||||
|
||||
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
|
||||
{
|
||||
use LazyWidget;
|
||||
|
||||
public function handle(array $input)
|
||||
{
|
||||
$goods = Goods::findOrFail($this->payload['goods_id']);
|
||||
$spec = json_decode($input['spec'], true);
|
||||
foreach($spec as &$item) {
|
||||
foreach($item['values'] as &$subItem) {}
|
||||
$subItem['price'] = floatval($subItem['price']);
|
||||
}
|
||||
$goods->update(['spec' => $spec]);
|
||||
return $this->response()->success('保存成功');
|
||||
}
|
||||
|
||||
public function form()
|
||||
{
|
||||
$spec = $this->model()->type?->spec;
|
||||
|
||||
$this->spec('spec')->header(['名称', '可选值', '价格'])->keys(['value', 'price'])->type($spec);
|
||||
}
|
||||
|
||||
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 '保存';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
namespace Peidikeji\Goods\Form;
|
||||
|
||||
use Dcat\Admin\Form\Field;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class Spec extends Field
|
||||
{
|
||||
protected $view = 'peidikeji.dcat-admin-extension-goods::form.spec';
|
||||
|
||||
protected $variables = [
|
||||
'headers' => [],
|
||||
'keys' => [],
|
||||
'type' => null,
|
||||
'formatValue' => [],
|
||||
];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
$value = $this->value;
|
||||
$type = $this->variables['type'];
|
||||
$newValue = [];
|
||||
if ($type && !$value) {
|
||||
foreach($type as $item) {
|
||||
$itemValue = $value ? Arr::first($value, fn($i) => $i['name'] === $item['name']) : [];
|
||||
$values = data_get($itemValue, 'values', []);
|
||||
foreach($values as &$items) {
|
||||
$subItem = Arr::first($item['values'], fn($i) => $i['name'] === $items['name']);
|
||||
$items['options'] = data_get($subItem, 'values');
|
||||
}
|
||||
array_push($newValue, [
|
||||
'name' => $item['name'],
|
||||
'values' => $values
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
$newValue = $value ?: [];
|
||||
}
|
||||
$this->addVariables([
|
||||
'formatValue' => $newValue
|
||||
]);
|
||||
return parent::render();
|
||||
}
|
||||
}
|
||||
|
|
@ -22,14 +22,22 @@ class GoodsService
|
|||
GoodsSku::where('goods_id', $goods->id)->delete();
|
||||
}
|
||||
|
||||
public function generateSku(Goods $goods)
|
||||
/**
|
||||
* 根据规格生成SKU
|
||||
*
|
||||
* @param Goods $goods 商品
|
||||
* @param array $options {spec: 指定规格, price: 基础价格, name: 基础名称, stock: 默认库存, name_add: 是否在名称上面追加属性值, price_add: 是否在价格上面追加属性的加价, force: 是否覆盖原有的货品}
|
||||
*/
|
||||
public function generateSku(Goods $goods, $options = [])
|
||||
{
|
||||
$this->clearSku($goods);
|
||||
if ($goods->spec) {
|
||||
$spec = $goods->spec;
|
||||
$price = $goods->price;
|
||||
$name = $goods->name;
|
||||
|
||||
$spec = data_get($options, 'spec', $goods->spec);
|
||||
$price = data_get($options, 'price', $goods->price);
|
||||
$name = data_get($options, 'name', $goods->name);
|
||||
$stock = data_get($options, 'stock', $goods->stock);
|
||||
$nameAdd = !!data_get($options, 'name_add', false);
|
||||
$priceAdd = !!data_get($options, 'price_add', false);
|
||||
$force = !!data_get($options, 'force', false);
|
||||
if ($spec) {
|
||||
$specList = [];
|
||||
foreach ($spec as $item) {
|
||||
$items = [];
|
||||
|
|
@ -37,21 +45,30 @@ class GoodsService
|
|||
array_push($items, [
|
||||
'name' => $item['name'],
|
||||
'value' => $value['value'],
|
||||
'price' => $value['price'],
|
||||
'price' => floatval($value['price']),
|
||||
]);
|
||||
}
|
||||
array_push($specList, $items);
|
||||
}
|
||||
$cartesianList = $this->cartesianProduct($specList);
|
||||
foreach($cartesianList as $items) {
|
||||
$specPrice = array_sum(array_column($items, 'price'));
|
||||
$goods->skus()->create([
|
||||
'sn' => $this->generateSn(),
|
||||
'name' => $name,
|
||||
'price' => $price + $specPrice,
|
||||
'stock' => $goods->stock,
|
||||
$specPrice = $priceAdd ? $price + array_sum(array_column($items, 'price')) : $price;
|
||||
$specName = $nameAdd ? $name . ' ' . implode(' ', array_column($items, 'value')) : $name;
|
||||
$exists = $goods->skus()->jsonArray($items)->exists();
|
||||
$attributes = [
|
||||
'name' => $specName,
|
||||
'price' => $specPrice,
|
||||
'stock' => $stock,
|
||||
'spec' => $items,
|
||||
]);
|
||||
];
|
||||
if ($exists) {
|
||||
if ($force) {
|
||||
$goods->skus()->jsonArray($items)->update($attributes);
|
||||
}
|
||||
} else {
|
||||
$attributes['sn'] = $this->generateSn();
|
||||
$goods->skus()->create($attributes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ namespace Peidikeji\Goods;
|
|||
|
||||
use Dcat\Admin\Extend\ServiceProvider;
|
||||
use Dcat\Admin\Admin;
|
||||
use Dcat\Admin\Form;
|
||||
use Peidikeji\Goods\Form\Spec;
|
||||
|
||||
class GoodsServiceProvider extends ServiceProvider
|
||||
{
|
||||
|
|
@ -14,4 +16,10 @@ class GoodsServiceProvider extends ServiceProvider
|
|||
['title' => '商品类别', 'uri' => 'goods/type', 'icon' => '', 'parent' => '商品管理'],
|
||||
['title' => '商品信息', 'uri' => 'goods', 'icon' => '', 'parent' => '商品管理'],
|
||||
];
|
||||
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
Form::extend('spec', Spec::class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,26 +2,55 @@
|
|||
|
||||
namespace Peidikeji\Goods\Http\Controllers\Admin;
|
||||
|
||||
use Dcat\Admin\Admin;
|
||||
use Dcat\Admin\Form;
|
||||
use Dcat\Admin\Form\BlockForm;
|
||||
use Dcat\Admin\Form\NestedForm;
|
||||
use Dcat\Admin\Form\Row;
|
||||
use Dcat\Admin\Grid;
|
||||
use Dcat\Admin\Grid\Displayers\Actions;
|
||||
use Dcat\Admin\Grid\Tools\Selector;
|
||||
use Dcat\Admin\Http\Controllers\AdminController;
|
||||
use Dcat\Admin\Layout\Content;
|
||||
use Dcat\Admin\Show;
|
||||
use Peidikeji\Goods\Actions\RowGoodsSkuList;
|
||||
use Dcat\Admin\Widgets\Card;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Peidikeji\Goods\Form\GoodsAttrForm;
|
||||
use Peidikeji\Goods\Form\GoodsSpecForm;
|
||||
use Peidikeji\Goods\Models\Goods;
|
||||
use Peidikeji\Goods\Models\GoodsBrand;
|
||||
use Peidikeji\Goods\Models\GoodsCategory;
|
||||
use Peidikeji\Goods\Models\GoodsSku;
|
||||
use Peidikeji\Goods\Models\GoodsType;
|
||||
|
||||
class GoodsController extends AdminController
|
||||
{
|
||||
protected $translation = 'peidikeji.dcat-admin-extension-goods::goods';
|
||||
|
||||
public function attr($goods, Content $content)
|
||||
{
|
||||
$goods = Goods::with(['type'])->findOrFail($goods);
|
||||
$form = GoodsAttrForm::make([
|
||||
'type' => $goods->type,
|
||||
'attr' => $goods->attr
|
||||
])->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);
|
||||
}
|
||||
|
||||
public function spec($goods, Content $content)
|
||||
{
|
||||
$goods = Goods::findOrFail($goods);
|
||||
$form = GoodsSpecForm::make([
|
||||
'type' => $goods->type,
|
||||
'spec' => $goods->spec
|
||||
])->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()
|
||||
{
|
||||
return Grid::make(Goods::with(['category', 'brand', 'type']), function (Grid $grid) {
|
||||
|
|
@ -56,15 +85,18 @@ class GoodsController extends AdminController
|
|||
$grid->column('name')->display(function () {
|
||||
return ($this->cover_image ? '<img src="'.$this->cover_image.'" width="60" class="img-thumbnail"/> ' : '') . '<a href="'.admin_url('goods/' . $this->id).'">'.$this->name.'</a>';
|
||||
});
|
||||
$grid->column('price');
|
||||
$grid->column('spec')->view('peidikeji.dcat-admin-extension-goods::grid.spec');
|
||||
$grid->column('price')->editable();
|
||||
$grid->column('spec')->view('peidikeji.dcat-admin-extension-goods::grid.specs');
|
||||
$grid->column('on_sale')->switch();
|
||||
$grid->column('sold_count');
|
||||
|
||||
$grid->disableRowSelector();
|
||||
|
||||
$grid->actions(function (Actions $actions) {
|
||||
$actions->append(new RowGoodsSkuList());
|
||||
$row = $actions->row;
|
||||
$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>');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -82,13 +114,14 @@ 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.spec');
|
||||
$show->field('attr')->view('peidikeji.dcat-admin-extension-goods::grid.attr');
|
||||
$show->field('part')->view('peidikeji.dcat-admin-extension-goods::grid.spec');
|
||||
$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('on_sale')->bool();
|
||||
$show->field('sold_count');
|
||||
$show->field('created_at')->as(fn($v) => $this->created_at->format('Y-m-d H:i:s'));
|
||||
$show->field('updated_at')->as(fn($v) => $this->updated_at->format('Y-m-d H:i:s'));
|
||||
|
||||
return $show;
|
||||
}
|
||||
|
||||
|
|
@ -97,109 +130,48 @@ class GoodsController extends AdminController
|
|||
return Form::make(new Goods(), function (Form $form) {
|
||||
$model = $form->model();
|
||||
$isCreating = $form->isCreating();
|
||||
$type = null;
|
||||
$unique = Rule::unique('goods', 'goods_sn');
|
||||
|
||||
if (request('type_id') && $isCreating) {
|
||||
$typeId = request('type_id');
|
||||
$type = GoodsType::find($typeId);
|
||||
if ($type) {
|
||||
$attrbutes = [
|
||||
'type_id' => $type->id,
|
||||
'attr' => $type->attr,
|
||||
'spec' => $type->spec,
|
||||
'part' => $type->part,
|
||||
];
|
||||
$form->model($attrbutes);
|
||||
}
|
||||
}
|
||||
$model = $form->model();
|
||||
if (!$type) {
|
||||
$type = $model && $model->type_id ? GoodsType::find($form->model()->type_id) : null;
|
||||
if ($isCreating) {
|
||||
$form->select('type_id')->options(GoodsType::pluck('name', 'id'));
|
||||
} else {
|
||||
$type = $model->type_id ? GoodsType::find($model->type_id) : null;
|
||||
$form->display('type_id')->with(fn() => $model->type_id ? $type->name : '');
|
||||
$unique->ignore($model->id);
|
||||
}
|
||||
$form->select('category_id')->options(GoodsCategory::selectOptions(null, false))->required();
|
||||
$form->select('brand_id')->options(GoodsBrand::pluck('name', 'id'));
|
||||
$form->text('name')->required();
|
||||
$form->text('goods_sn')->required()->rules([$unique], [
|
||||
'unique' => '商品编号已经存在'
|
||||
]);
|
||||
$form->image('cover_image')
|
||||
->autoUpload()
|
||||
->saveFullUrl()
|
||||
->move('goods/cover-image')
|
||||
->required();
|
||||
$form->multipleImage('images')
|
||||
->autoUpload()
|
||||
->saveFullUrl()
|
||||
->move('goods/images');
|
||||
$form->multipleImage('content')
|
||||
->autoUpload()
|
||||
->saveFullUrl()
|
||||
->move('goods/content');
|
||||
|
||||
$form->disableHeader();
|
||||
$form->tab('基本设置', function (Form $form) use ($isCreating, $type) {
|
||||
if ($isCreating) {
|
||||
$form->select('type_id')->options(GoodsType::pluck('name', 'id'));
|
||||
} else {
|
||||
$form->display('type_id')->with(fn() => $type->name);
|
||||
}
|
||||
$form->select('category_id')->options(GoodsCategory::selectOptions(null, false))->required();
|
||||
$form->select('brand_id')->options(GoodsBrand::pluck('name', 'id'));
|
||||
$form->text('name')->required();
|
||||
$form->text('goods_sn');
|
||||
$form->image('cover_image')
|
||||
->autoUpload()
|
||||
->saveFullUrl()
|
||||
->move('goods/goods')
|
||||
->required();
|
||||
$form->multipleImage('images')
|
||||
->autoUpload()
|
||||
->saveFullUrl()
|
||||
->move('goods/goods');
|
||||
$form->multipleImage('content')
|
||||
->autoUpload()
|
||||
->saveFullUrl()
|
||||
->move('goods/goods');
|
||||
|
||||
$form->number('price')->min(0)->attribute('step', 0.01);
|
||||
$form->switch('on_sale');
|
||||
});
|
||||
|
||||
$form->tab('属性', function (Form $form) use ($type) {
|
||||
$form->array('attr', null, function (NestedForm $table) use ($type) {
|
||||
$values = data_get($type, 'attr.' . $table->getKey() . '.values') ?: [];
|
||||
$table->text('group', '分组');
|
||||
$table->text('name', '属性名')->required();
|
||||
$table->autocomplete('value', '属性值')->options($values)->configs(['minChars' => 0]);
|
||||
});
|
||||
});
|
||||
$form->tab('规格', function (Form $form) use ($type) {
|
||||
$form->array('spec', null, function (NestedForm $table) use ($type) {
|
||||
$table->text('name', '名称')->required();
|
||||
$values = data_get($type, 'spec.' . $table->getKey() . '.values') ?: [];
|
||||
$table->array('values', null, function (NestedForm $table) use ($values) {
|
||||
$index = $table->getKey();
|
||||
$table->autocomplete('value', '可选值')
|
||||
->default($index === null ? '' : data_get($values, $index, ''))
|
||||
->options($values)
|
||||
->configs(['minChars' => 0]);
|
||||
$table->number('price', '加价')->min(0)->default();
|
||||
});
|
||||
});
|
||||
});
|
||||
$form->tab('配件', function (Form $form) use ($type) {
|
||||
$form->array('part', null, function (NestedForm $table) use ($type) {
|
||||
$table->text('name', '名称')->required();
|
||||
$values = data_get($type, 'part.' . $table->getKey() . '.values') ?: [];
|
||||
$table->array('values', null, function (NestedForm $table) use ($values) {
|
||||
$index = $table->getKey();
|
||||
$table->autocomplete('value', '可选值')
|
||||
->default($index === null ? '' : data_get($values, $index, ''))
|
||||
->options($values)
|
||||
->configs(['minChars' => 0]);
|
||||
$table->number('price', '加价')->min(0)->default();
|
||||
});
|
||||
});
|
||||
});
|
||||
$form->number('price')->min(0)->attribute('step', 0.01);
|
||||
$form->switch('on_sale');
|
||||
|
||||
$form->disableResetButton();
|
||||
$form->disableCreatingCheck();
|
||||
$form->disableViewCheck();
|
||||
$form->disableEditingCheck();
|
||||
|
||||
$admin_url = request()->url();
|
||||
Admin::script(
|
||||
<<<JS
|
||||
var url = "{$admin_url}"
|
||||
var isCreating = "${isCreating}"
|
||||
$('[name="type_id"]').change(function (e) {
|
||||
if (isCreating) {
|
||||
Dcat.reload(url + '?type_id=' + e.target.value);
|
||||
}
|
||||
})
|
||||
JS
|
||||
);
|
||||
$form->deleting(function (Form $form) {
|
||||
$data = $form->model()->toArray();
|
||||
// 删除 SKU
|
||||
GoodsSku::whereIn('goods_id', array_column($data, 'id'))->delete();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use Dcat\Admin\Show;
|
|||
use Illuminate\Routing\Controller;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Peidikeji\Goods\GoodsService;
|
||||
use Peidikeji\Goods\Models\Goods;
|
||||
use Peidikeji\Goods\Models\GoodsSku;
|
||||
|
||||
|
|
@ -26,14 +27,16 @@ class GoodsSkuController extends Controller
|
|||
|
||||
$grid->selector(function (Selector $selector) use ($goods) {
|
||||
$specs = $goods->spec;
|
||||
foreach($specs as $key => $item) {
|
||||
$values = array_column($item['values'], 'value');
|
||||
$selector->selectOne('spec_' . $key, $item['name'], array_column($item['values'], 'value'), function ($q, $value) use ($values, $item) {
|
||||
$selected = array_values(Arr::only($values, $value));
|
||||
if (count($selected) > 0) {
|
||||
$q->jsonArray([['name' => $item['name'], 'value' => $selected[0]]]);
|
||||
}
|
||||
});
|
||||
if ($specs) {
|
||||
foreach($specs as $key => $item) {
|
||||
$values = array_column($item['values'], 'value');
|
||||
$selector->selectOne('spec_' . $key, $item['name'], array_column($item['values'], 'value'), function ($q, $value) use ($values, $item) {
|
||||
$selected = array_values(Arr::only($values, $value));
|
||||
if (count($selected) > 0) {
|
||||
$q->jsonArray([['name' => $item['name'], 'value' => $selected[0]]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -43,13 +46,15 @@ class GoodsSkuController extends Controller
|
|||
$grid->column('origin_price')->display(fn() => $goods->price);
|
||||
$grid->column('price');
|
||||
$grid->column('stock');
|
||||
foreach($goods->spec as $key => $item) {
|
||||
$grid->column('spec_' . $key, $item['name'])->display(function () use ($item) {
|
||||
$filtered = current(array_filter($this->spec, fn($subItem) => $subItem['name'] === $item['name']));
|
||||
$value = data_get($filtered, 'value');
|
||||
$price = data_get($filtered, 'price');
|
||||
return '<span class="label bg-info">'.$value.'</span>';
|
||||
});
|
||||
if ($goods->spec) {
|
||||
foreach($goods->spec as $key => $item) {
|
||||
$grid->column('spec_' . $key, $item['name'])->display(function () use ($item) {
|
||||
$filtered = current(array_filter($this->spec, fn($subItem) => $subItem['name'] === $item['name']));
|
||||
$value = data_get($filtered, 'value');
|
||||
$price = data_get($filtered, 'price');
|
||||
return '<span class="label bg-info">'.$value.'</span>';
|
||||
});
|
||||
}
|
||||
}
|
||||
// $grid->column('spec')->view('peidikeji.dcat-admin-extension-goods::grid.attr');
|
||||
});
|
||||
|
|
@ -69,7 +74,7 @@ class GoodsSkuController extends Controller
|
|||
$show->field('name');
|
||||
$show->field('price');
|
||||
$show->field('stock');
|
||||
$show->field('spec')->view('peidikeji.dcat-admin-extension-goods::grid.attr');
|
||||
$show->field('spec')->view('peidikeji.dcat-admin-extension-goods::grid.spec');
|
||||
// $goods = $show->model()->goods;
|
||||
// foreach($goods->spec as $key => $item) {
|
||||
// $show->field('spec_' . $key, $item['name'])->as(function () use ($item) {
|
||||
|
|
@ -90,11 +95,8 @@ class GoodsSkuController extends Controller
|
|||
protected function form($goods)
|
||||
{
|
||||
return Form::make(new GoodsSku(), function (Form $form) use ($goods) {
|
||||
$unqiue = Rule::unique('goods_sku', 'sn');
|
||||
if ($form->isEditing()) {
|
||||
$unqiue->ignore($form->model()->id);
|
||||
}
|
||||
$form->text('sn')->rules([$unqiue])->required();
|
||||
$unqiue = Rule::unique('goods_sku', 'sn')->ignore($form->model()->id);
|
||||
$form->text('sn')->rules([$unqiue], ['unique' => '货号已经存在'])->required();
|
||||
$form->text('name')->default($goods->name);
|
||||
$form->number('price')->min(0)->default($goods->price);
|
||||
$form->number('stock')->min(0)->default($goods->stock);
|
||||
|
|
@ -102,22 +104,26 @@ class GoodsSkuController extends Controller
|
|||
$form->hidden('goods_id')->default($goods->id);
|
||||
|
||||
$spec = $form->model()->spec;
|
||||
foreach($goods->spec as $key => $item) {
|
||||
$values = array_column($item['values'], 'value', 'value');
|
||||
$value = null;
|
||||
if ($spec) {
|
||||
$filtered = current(array_filter($spec, fn($subItem) => $subItem['name'] === $item['name']));
|
||||
$value = array_search($filtered['value'], $values);
|
||||
if ($goods->spec) {
|
||||
foreach($goods->spec as $item) {
|
||||
$values = array_column($item['values'], 'value', 'value');
|
||||
$value = null;
|
||||
if ($spec) {
|
||||
$filtered = current(array_filter($spec, fn($subItem) => $subItem['name'] === $item['name']));
|
||||
$value = array_search($filtered['value'], $values);
|
||||
}
|
||||
$form->radio($item['name'], $item['name'])->options($values)->value($value);
|
||||
}
|
||||
$form->radio($item['name'], $item['name'])->options($values)->value($value);
|
||||
}
|
||||
|
||||
$form->saving(function (Form $form) use ($goods) {
|
||||
$info = $form->model();
|
||||
$spec = [];
|
||||
foreach($goods->spec as $item) {
|
||||
array_push($spec, ['name' => $item['name'], 'value' => $form->input($item['name'])]);
|
||||
$form->deleteInput($item['name']);
|
||||
if ($goods->spec) {
|
||||
foreach($goods->spec as $item) {
|
||||
array_push($spec, ['name' => $item['name'], 'value' => $form->input($item['name'])]);
|
||||
$form->deleteInput($item['name']);
|
||||
}
|
||||
}
|
||||
$form->input('spec', $spec);
|
||||
$query = GoodsSku::where('goods_id', $goods->id)->jsonArray($spec);
|
||||
|
|
@ -148,11 +154,67 @@ class GoodsSkuController extends Controller
|
|||
|
||||
public function create($goods, Content $content)
|
||||
{
|
||||
$goods = Goods::findOrFail($goods);
|
||||
return $content
|
||||
->translation($this->translation)
|
||||
->title(admin_trans_label())
|
||||
->description(trans('admin.create'))
|
||||
->body($this->form(Goods::findOrFail($goods)));
|
||||
->title(__('peidikeji.dcat-admin-extension-goods::goods-sku.labels.sku'))
|
||||
->description(__('peidikeji.dcat-admin-extension-goods::goods-sku.labels.create'))
|
||||
->body($this->createForm($goods));
|
||||
}
|
||||
|
||||
protected function createForm($goods)
|
||||
{
|
||||
return Form::make(new GoodsSku(), function (Form $form) use ($goods) {
|
||||
$form->text('name')->default($goods->name);
|
||||
if ($goods->spec) {
|
||||
$form->checkbox('name_append', '')->options([1 => '是否在名称上面追加属性值']);
|
||||
}
|
||||
$form->number('price')->min(0)->default($goods->price);
|
||||
if ($goods->spec) {
|
||||
$form->checkbox('price_append', '')->options([1 => '是否在价格上面追加属性的加价'])->default([1]);
|
||||
}
|
||||
|
||||
if ($goods->spec) {
|
||||
foreach($goods->spec as $item) {
|
||||
$values = array_column($item['values'], 'value', 'value');
|
||||
$form->checkbox($item['name'], $item['name'])->options($values);
|
||||
}
|
||||
|
||||
$form->checkbox('force', '')->options([1 => '覆盖已经存在的货品'])->default(1);
|
||||
}
|
||||
|
||||
|
||||
$form->disableCreatingCheck();
|
||||
$form->disableEditingCheck();
|
||||
$form->disableViewCheck();
|
||||
$form->disableResetButton();
|
||||
|
||||
$form->saving(function (Form $form) use ($goods) {
|
||||
$price = $form->price;
|
||||
$name = $form->name;
|
||||
$spec = [];
|
||||
if ($goods->spec) {
|
||||
foreach($goods->spec as $index => $item) {
|
||||
$values = $form->input($item['name']);
|
||||
$values = array_filter($values, fn($v) => $v);
|
||||
if (count($values) === 0) {
|
||||
return $form->response()->error('请勾选 ' . $item['name']);
|
||||
}
|
||||
array_push($spec, ['name' => $item['name'], 'values' => array_filter($item['values'], fn($v) => in_array($v['value'], $values))]);
|
||||
$form->deleteInput($item['name']);
|
||||
}
|
||||
}
|
||||
GoodsService::make()->generateSku($goods, [
|
||||
'spec' => $spec,
|
||||
'price' => $price,
|
||||
'name' => $name,
|
||||
'name_add' => !!data_get($form->name_append, 0),
|
||||
'price_add' => !!data_get($form->price_append, 0),
|
||||
'force' => !!$form->force
|
||||
]);
|
||||
return $form->response()->success('添加成功')->redirect(admin_route('goods_sku.index', ['goods' => $goods->id]));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public function update($goods, $id)
|
||||
|
|
@ -162,7 +224,7 @@ class GoodsSkuController extends Controller
|
|||
|
||||
public function store($goods)
|
||||
{
|
||||
return $this->form(Goods::findOrFail($goods))->store();
|
||||
return $this->createForm(Goods::findOrFail($goods))->store();
|
||||
}
|
||||
|
||||
public function destroy($goods, $id)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ Route::resource('goods/category', GoodsCategoryController::class);
|
|||
Route::resource('goods/brand', GoodsBrandController::class);
|
||||
Route::resource('goods/type', GoodsTypeController::class);
|
||||
|
||||
Route::resource('goods/{goods}/sku', GoodsSkuController::class)->names('goods-sku');
|
||||
Route::resource('goods/{goods}/sku', GoodsSkuController::class)->names('goods_sku');
|
||||
|
||||
Route::get('goods/{goods}/attr', [GoodsController::class, 'attr'])->name('goods.attr');
|
||||
Route::get('goods/{goods}/spec', [GoodsController::class, 'spec'])->name('goods.spec');
|
||||
Route::resource('goods', GoodsController::class);
|
||||
|
|
|
|||
|
|
@ -59,9 +59,9 @@ class CreateGoodsTable extends Migration
|
|||
$table->unsignedInteger('stock')->default(0)->comment('库存');
|
||||
$table->unsignedInteger('sold_count')->default(0)->comment('销量');
|
||||
$table->decimal('price', 12, 2)->comment('售价');
|
||||
$table->json('attr')->nullable()->comment('属性[{name, value}]');
|
||||
$table->json('spec')->nullable()->comment('规格[{name, values: [{value, price}]}]');
|
||||
$table->json('part')->nullable()->comment('配件[{name, values: [{value, price}]}]');
|
||||
$table->json('attr')->nullable()->comment('属性[{name, values: [{name, value}]}]');
|
||||
$table->json('spec')->nullable()->comment('规格[{name, values: [{name, value}]}]');
|
||||
$table->json('part')->nullable()->comment('配件[{name, values: [{name, value}]}]');
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
|
||||
|
|
|
|||
|
|
@ -36,9 +36,11 @@ class GoodsTableSeeder extends Seeder
|
|||
[
|
||||
'name' => '手机',
|
||||
'attr' => [
|
||||
['group' => '主体', 'name' => '入网型号', 'values' => ['5G', '4G']],
|
||||
['group' => '主体', 'name' => '上市年份', 'values' => null],
|
||||
['group' => '主体', 'name' => '品牌', 'values' => null]
|
||||
['name' => '主体', 'values' => [
|
||||
['name' => '入网型号', 'values' => ['5G', '4G']],
|
||||
['name' => '上市年份', 'values' => null],
|
||||
['name' => '品牌', 'values' => null]
|
||||
]]
|
||||
],
|
||||
'spec' => [
|
||||
['name' => '颜色', 'values' => ['白色', '红色', '黑色']],
|
||||
|
|
@ -51,8 +53,10 @@ class GoodsTableSeeder extends Seeder
|
|||
[
|
||||
'name' => '笔记本电脑',
|
||||
'attr' => [
|
||||
['group' => '显示器', 'name' => '屏幕类型', 'values' => ['LED 背光显示屏']],
|
||||
['group' => '显示器','name' => '物理分辨率', 'values' => ['3072 x 1920 (226 ppi)']],
|
||||
['name' => '显示器', 'values' => [
|
||||
['name' => '屏幕类型', 'values' => ['LED 背光显示屏']],
|
||||
['name' => '物理分辨率', 'values' => ['3072 x 1920 (226 ppi)']]
|
||||
]],
|
||||
],
|
||||
'spec' => [
|
||||
['name' => '颜色', 'values' => ['白色', '灰色']],
|
||||
|
|
@ -92,9 +96,11 @@ class GoodsTableSeeder extends Seeder
|
|||
'stock' => 100,
|
||||
'price' => 6499.00,
|
||||
'attr' => [
|
||||
['group' => '主体', 'name' => '入网型号', 'value' => '5G'],
|
||||
['group' => '主体', 'name' => '品牌', 'value' => '三星Galaxy'],
|
||||
['group' => '主体', 'name' => '上市年份', 'value' => '2020'],
|
||||
['name' => '主体', 'values' => [
|
||||
['name' => '入网型号', 'value' => '5G'],
|
||||
['name' => '品牌', 'value' => '三星Galaxy'],
|
||||
['name' => '上市年份', 'value' => '2020'],
|
||||
]],
|
||||
],
|
||||
'spec' => [
|
||||
['name' => '颜色', 'values' => [
|
||||
|
|
@ -133,8 +139,10 @@ class GoodsTableSeeder extends Seeder
|
|||
'stock' => 150,
|
||||
'price' => 17999.00,
|
||||
'attr' => [
|
||||
['group' => '显示器','name' => '屏幕类型', 'value' => 'LED 背光显示屏'],
|
||||
['group' => '显示器','name' => '物理分辨率', 'value' => '3072 x 1920 (226 ppi)'],
|
||||
['name' => '显示器', 'values' => [
|
||||
['name' => '屏幕类型', 'value' => 'LED 背光显示屏'],
|
||||
['name' => '物理分辨率', 'value' => '3072 x 1920 (226 ppi)']
|
||||
]],
|
||||
],
|
||||
'spec' => [
|
||||
['name' => '颜色', 'values' => [
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div class="{{$viewClass['form-group']}}" >
|
||||
|
||||
<label class="{{$viewClass['label']}} control-label pt-0">{!! $label !!}</label>
|
||||
<label class="{{$viewClass['label']}} control-label">{!! $label !!}</label>
|
||||
|
||||
<div class="{{$viewClass['field']}}">
|
||||
|
||||
|
|
@ -55,4 +55,4 @@
|
|||
});
|
||||
$(selector+':checked').trigger('change')
|
||||
</script>
|
||||
@endif
|
||||
@endif
|
||||
|
|
|
|||
|
|
@ -20,5 +20,11 @@
|
|||
<button type="reset" class="btn btn-white"><i class="feather icon-rotate-ccw"></i> {{ trans('admin.reset') }}</button>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(! empty($buttons['back']))
|
||||
<div class="btn-group pull-left">
|
||||
<a href="javascript:window.history.back()" class="btn btn-white"><i class="feather icon-arrow-left"></i> {{ trans('admin.back') }}</a>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class Footer implements Renderable
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $buttons = ['reset' => true, 'submit' => true];
|
||||
protected $buttons = ['reset' => false, 'submit' => true, 'back' => true];
|
||||
|
||||
/**
|
||||
* Available checkboxes.
|
||||
|
|
@ -59,6 +59,11 @@ class Footer implements Renderable
|
|||
$this->builder = $builder;
|
||||
}
|
||||
|
||||
public function disableBack(bool $disable = true)
|
||||
{
|
||||
$this->buttons['back'] = !$disable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable reset button.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class Tools implements Renderable
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $tools = ['delete', 'edit', 'list'];
|
||||
protected $tools = ['back', 'list', 'edit', 'delete'];
|
||||
|
||||
/**
|
||||
* Tools should be appends to default tools.
|
||||
|
|
@ -47,7 +47,7 @@ class Tools implements Renderable
|
|||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $showList = true;
|
||||
protected $showList = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
|
|
@ -59,6 +59,8 @@ class Tools implements Renderable
|
|||
*/
|
||||
protected $showEdit = true;
|
||||
|
||||
protected $showBack = true;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
|
|
@ -173,6 +175,12 @@ class Tools implements Renderable
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function disableBack(bool $disable = true)
|
||||
{
|
||||
$this->showBack = !$disable;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $disable
|
||||
* @return $this
|
||||
|
|
@ -250,11 +258,24 @@ class Tools implements Renderable
|
|||
$list = trans('admin.list');
|
||||
|
||||
return <<<HTML
|
||||
<div class="btn-group pull-right btn-mini" style="margin-right: 5px">
|
||||
<a href="javascript:window.history.back()" class="btn btn-sm btn-primary ">
|
||||
<a href="{$this->getListPath()}" class="btn btn-sm btn-primary mr-1">
|
||||
<i class="feather icon-list"></i><span class="d-none d-sm-inline"> {$list}</span>
|
||||
</a>
|
||||
</div>
|
||||
HTML;
|
||||
}
|
||||
|
||||
protected function renderBack()
|
||||
{
|
||||
if (! $this->showBack) {
|
||||
return;
|
||||
}
|
||||
|
||||
$back = trans('admin.back');
|
||||
|
||||
return <<<HTML
|
||||
<a href="javascript:window.history.back()" class="btn btn-sm btn-primary mr-1">
|
||||
<i class="feather icon-arrow-left"></i><span class="d-none d-sm-inline"> {$back}</span>
|
||||
</a>
|
||||
HTML;
|
||||
}
|
||||
|
||||
|
|
@ -276,7 +297,7 @@ HTML;
|
|||
|
||||
if ($this->showEdit) {
|
||||
$btn = <<<EOF
|
||||
<a href="{$url}" class="btn btn-sm btn-primary">
|
||||
<a href="{$url}" class="btn btn-sm btn-primary mr-1">
|
||||
<i class="feather icon-edit-1"></i><span class="d-none d-sm-inline"> {$edit}</span>
|
||||
</a>
|
||||
EOF;
|
||||
|
|
@ -293,11 +314,11 @@ EOF;
|
|||
|
||||
$text = $this->showEdit ? '' : "<span class='d-none d-sm-inline'> $edit</span>";
|
||||
|
||||
$quickBtn = "<button data-url='$url' class='btn btn-sm btn-primary {$id}'><i class=' fa fa-clone'></i>$text</button>";
|
||||
$quickBtn = "<button data-url='$url' class='btn btn-sm btn-primary mr-1 {$id}'><i class=' fa fa-clone'></i>$text</button>";
|
||||
}
|
||||
|
||||
return <<<HTML
|
||||
<div class="btn-group pull-right btn-mini" style="margin-right: 5px">{$btn}{$quickBtn}</div>
|
||||
{$btn}{$quickBtn}
|
||||
HTML;
|
||||
}
|
||||
|
||||
|
|
@ -315,11 +336,9 @@ HTML;
|
|||
$delete = trans('admin.delete');
|
||||
|
||||
return <<<HTML
|
||||
<div class="btn-group pull-right btn-mini" style="margin-right: 5px">
|
||||
<button class="btn btn-sm btn-white " data-action="delete" data-url="{$this->getDeletePath()}" data-redirect="{$this->getListPath()}">
|
||||
<button class="btn btn-sm btn-white mr-1" data-action="delete" data-url="{$this->getDeletePath()}" data-redirect="{$this->getListPath()}">
|
||||
<i class="feather icon-trash"></i><span class="d-none d-sm-inline"> {$delete}</span>
|
||||
</button>
|
||||
</div>
|
||||
HTML;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue