属性配置前端
This commit is contained in:
parent
de7df33788
commit
c05b21efc7
|
|
@ -0,0 +1,161 @@
|
|||
@extends('admin::layouts.master')
|
||||
|
||||
@section('title', __('admin/attribute_groups.index'))
|
||||
|
||||
@section('content')
|
||||
<div id="customer-app" class="card" v-cloak>
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between mb-4">
|
||||
<button type="button" class="btn btn-primary" @click="checkedCustomersCreate('add', null)">{{ __('admin/attribute_groups.create_at_groups') }}</button>
|
||||
</div>
|
||||
<div class="table-push">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>{{ __('common.name') }}</th>
|
||||
<th>{{ __('common.created_at') }}</th>
|
||||
<th width="130px">{{ __('common.action') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody v-if="attribute_groups.length">
|
||||
<tr v-for="group, index in attribute_groups" :key="index">
|
||||
<td>@{{ group.id }}</td>
|
||||
<td>@{{ group.description?.name || '' }}</td>
|
||||
<td>@{{ group.created_at }}</td>
|
||||
<td>
|
||||
<button class="btn btn-outline-secondary btn-sm" @click="checkedCustomersCreate('edit', index)">{{ __('common.edit') }}</button>
|
||||
<button class="btn btn-outline-danger btn-sm ml-1" type="button" @click="deleteCustomer(group.id, index)">{{ __('common.delete') }}</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody v-else><tr><td colspan="9" class="border-0"><x-admin-no-data /></td></tr></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-dialog title="{{ __('admin/attribute_groups.index') }}" :visible.sync="dialog.show" width="670px"
|
||||
@close="closeCustomersDialog('form')" :close-on-click-modal="false">
|
||||
|
||||
<el-form ref="form" :rules="rules" :model="dialog.form" label-width="155px">
|
||||
<el-form-item label="{{ __('common.name') }}" required class="language-inputs">
|
||||
<el-form-item :prop="'name.' + lang.code" :inline-message="true" v-for="lang, lang_i in source.languages" :key="lang_i"
|
||||
:rules="[
|
||||
{ required: true, message: '{{ __('common.error_required', ['name' => __('common.name')]) }}', trigger: 'blur' },
|
||||
]"
|
||||
>
|
||||
<el-input size="mini" v-model="dialog.form.name[lang.code]" placeholder="{{ __('common.name') }}"><template slot="prepend">@{{lang.name}}</template></el-input>
|
||||
</el-form-item>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="{{ __('common.sort_order') }}" prop="sort_order">
|
||||
<el-input class="mb-0" v-model="dialog.form.sort_order" type="number" placeholder="{{ __('common.sort_order') }}"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<div class="d-flex d-lg-block">
|
||||
<el-button type="primary" @click="addCustomersFormSubmit('form')">{{ __('common.save') }}</el-button>
|
||||
<el-button @click="closeCustomersDialog('form')">{{ __('common.cancel') }}</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('footer')
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#customer-app',
|
||||
|
||||
data: {
|
||||
attribute_groups: @json($attribute_groups ?? []),
|
||||
|
||||
source: {
|
||||
languages: @json(locales()),
|
||||
},
|
||||
|
||||
dialog: {
|
||||
show: false,
|
||||
index: null,
|
||||
type: 'add',
|
||||
form: {
|
||||
id: null,
|
||||
name: {},
|
||||
sort_order: 0,
|
||||
},
|
||||
},
|
||||
|
||||
rules: {
|
||||
sort_order: [{required: true,message: '{{ __('common.error_required', ['name' => __('common.sort_order')])}}',trigger: 'blur'}, ],
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
checkedCustomersCreate(type, index) {
|
||||
this.dialog.show = true
|
||||
this.dialog.type = type
|
||||
this.dialog.index = index
|
||||
|
||||
if (type == 'edit') {
|
||||
let group = this.attribute_groups[index];
|
||||
|
||||
this.dialog.form = {
|
||||
id: group.id,
|
||||
name: {},
|
||||
sort_order: group.sort_order,
|
||||
}
|
||||
|
||||
group.descriptions.forEach((e, index) => {
|
||||
this.$set(this.dialog.form.name, e.locale, e.name)
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
addCustomersFormSubmit(form) {
|
||||
const self = this;
|
||||
const type = this.dialog.type == 'add' ? 'post' : 'put';
|
||||
const url = this.dialog.type == 'add' ? 'attribute_groups' : 'attribute_groups/' + this.dialog.form.id;
|
||||
|
||||
this.$refs[form].validate((valid) => {
|
||||
if (!valid) {
|
||||
this.$message.error('{{ __('common.error_form') }}');
|
||||
return;
|
||||
}
|
||||
|
||||
$http[type](url, this.dialog.form).then((res) => {
|
||||
this.$message.success(res.message);
|
||||
if (this.dialog.type == 'add') {
|
||||
this.attribute_groups.unshift(res.data)
|
||||
} else {
|
||||
this.attribute_groups[this.dialog.index] = res.data
|
||||
}
|
||||
this.dialog.show = false
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
deleteCustomer(id, index) {
|
||||
const self = this;
|
||||
this.$confirm('{{ __('common.confirm_delete') }}', '{{ __('common.text_hint') }}', {
|
||||
confirmButtonText: '{{ __('common.confirm') }}',
|
||||
cancelButtonText: '{{ __('common.cancel') }}',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
$http.delete('attribute_groups/' + id).then((res) => {
|
||||
this.$message.success(res.message);
|
||||
self.attribute_groups.splice(index, 1)
|
||||
})
|
||||
}).catch(()=>{})
|
||||
},
|
||||
|
||||
closeCustomersDialog(form) {
|
||||
this.$refs[form].resetFields();
|
||||
this.dialog.form.name = {};
|
||||
this.dialog.form.description = {};
|
||||
this.dialog.show = false
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@endpush
|
||||
|
|
@ -0,0 +1,223 @@
|
|||
@extends('admin::layouts.master')
|
||||
|
||||
@section('title', __('admin/attribute.index'))
|
||||
|
||||
@section('content')
|
||||
<div id="customer-app-form" class="card" v-cloak>
|
||||
<div class="card-body h-min-600">
|
||||
<el-form :model="form" :rules="rules" ref="form" label-width="140px">
|
||||
<el-tabs v-model="customerTab">
|
||||
<el-tab-pane label="{{ __('admin/attribute.attribute_info') }}" name="customer">
|
||||
<div class="form-max-w">
|
||||
<el-form-item label="{{ __('common.name') }}" required class="language-inputs">
|
||||
<el-form-item :prop="'name.' + lang.code" :inline-message="true" v-for="lang, lang_i in source.languages" :key="lang_i"
|
||||
:rules="[
|
||||
{ required: true, message: '{{ __('common.error_required', ['name' => __('common.name')]) }}', trigger: 'blur' },
|
||||
]"
|
||||
>
|
||||
<el-input size="mini" v-model="form.name[lang.code]" placeholder="{{ __('common.name') }}"><template slot="prepend">@{{lang.name}}</template></el-input>
|
||||
</el-form-item>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="{{ __('admin/attribute_groups.index') }}" required prop="attribute_group_id">
|
||||
<el-select v-model="form.attribute_group_id" placeholder="{{ __('common.please_choose') }}">
|
||||
<el-option
|
||||
v-for="item in source.attributeGroup"
|
||||
:key="item.id"
|
||||
:label="item.description?.name || ''"
|
||||
:value="item.id">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="{{ __('common.sort_order') }}" prop="sort_order">
|
||||
<el-input v-model="form.sort_order" style="width: 189px;" placeholder="{{ __('common.sort_order') }}"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" class="mt-5" @click="submitForm('form')">{{ __('common.submit') }}</el-button>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="{{ __('admin/attribute.attribute_value') }}" name="address" v-if="form.id">
|
||||
<button class="btn btn-primary mb-3" type="button" @click="editAddress">{{ __('common.add') }}</button>
|
||||
<div class="table-push">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>{{ __('admin/attribute.attribute_value') }}</th>
|
||||
{{-- <th>排序</th> --}}
|
||||
<th width="160px">{{ __('common.action') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody v-if="source.attributeValues.length">
|
||||
<tr v-for="item, index in source.attributeValues" :key="index">
|
||||
<td>@{{ item.id }}</td>
|
||||
<td>@{{ item.description?.name || '' }}</td>
|
||||
<td>
|
||||
<button class="btn btn-outline-secondary btn-sm" type="button"
|
||||
@click="editAddress(index)">{{ __('common.edit') }}</button>
|
||||
<button class="btn btn-outline-danger btn-sm ml-1" type="button"
|
||||
@click="deleteAddress(item.id, index)">{{ __('common.delete') }}</button>
|
||||
</td>
|
||||
</tbody>
|
||||
<tbody v-else><tr><td colspan="9" class="border-0"><x-admin-no-data /></td></tr></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<el-dialog title="{{ __('admin/attribute.attribute_value') }}" :visible.sync="dialog.show" width="670px"
|
||||
@close="closeDialog('form')" :close-on-click-modal="false">
|
||||
|
||||
<el-form ref="form" :rules="attributeRules" :model="dialog.form" label-width="155px">
|
||||
<el-form-item label="{{ __('common.name') }}" required class="language-inputs">
|
||||
<el-form-item :prop="'name.' + lang.code" :inline-message="true" v-for="lang, lang_i in source.languages" :key="lang_i"
|
||||
:rules="[
|
||||
{ required: true, message: '{{ __('common.error_required', ['name' => __('common.name')]) }}', trigger: 'blur' },
|
||||
]"
|
||||
>
|
||||
<el-input size="mini" v-model="dialog.form.name[lang.code]" placeholder="{{ __('common.name') }}"><template slot="prepend">@{{lang.name}}</template></el-input>
|
||||
</el-form-item>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<div class="d-flex d-lg-block mt-4">
|
||||
<el-button type="primary" @click="formSubmit('form')">{{ __('common.save') }}</el-button>
|
||||
<el-button @click="closeDialog('form')">{{ __('common.cancel') }}</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('footer')
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#customer-app-form',
|
||||
data: {
|
||||
customerTab: 'customer',
|
||||
form: {
|
||||
id: @json($attribute['id']),
|
||||
name: {},
|
||||
attribute_group_id: @json($attribute['attribute_group_id']),
|
||||
sort_order: @json($attribute['sort_order']),
|
||||
},
|
||||
|
||||
addresses: @json($customer['addresses'] ?? []),
|
||||
|
||||
source: {
|
||||
languages: @json(locales()),
|
||||
locale: @json(locale()),
|
||||
descriptions: @json($attribute['descriptions']),
|
||||
attributeValues: @json($attribute['values']),
|
||||
attributeGroup: @json($attribute_group ?? []),
|
||||
},
|
||||
|
||||
dialog: {
|
||||
show: false,
|
||||
index: null,
|
||||
form: {
|
||||
id: '',
|
||||
name: {},
|
||||
// sort_order: 0,
|
||||
}
|
||||
},
|
||||
|
||||
rules: {
|
||||
name: [{required: true, message: "{{ __('common.error_required', ['name' => __('admin/customer.user_name')] ) }}", trigger: 'blur'}, ],
|
||||
sort_order: [{required: true,message: '{{ __('common.error_required', ['name' => __('common.sort_order')])}}',trigger: 'blur'}, ],
|
||||
},
|
||||
|
||||
attributeRules: {
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.source.languages.forEach((item) => {
|
||||
this.$set(this.form.name, item.code, this.source.descriptions.find(e => e.locale == item.code)?.name || '')
|
||||
})
|
||||
},
|
||||
|
||||
methods: {
|
||||
submitForm(form) {
|
||||
const self = this;
|
||||
|
||||
this.$refs[form].validate((valid) => {
|
||||
if (!valid) {
|
||||
this.$message.error('{{__('common.error_form')}}');
|
||||
return;
|
||||
}
|
||||
|
||||
$http.put(`attributes/{{ $attribute['id'] }}`, self.form).then((res) => {
|
||||
layer.msg(res.message);
|
||||
location = '{{ admin_route("attributes.index") }}'
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
editAddress(index) {
|
||||
if (typeof index == 'number') {
|
||||
this.dialog.index = index;
|
||||
let descriptions = this.source.attributeValues[index].descriptions;
|
||||
this.source.languages.forEach((item) => {
|
||||
this.$set(this.dialog.form.name, item.code, descriptions.find(e => e.locale == item.code)?.name || '')
|
||||
})
|
||||
this.dialog.form.id = this.source.attributeValues[index].id
|
||||
}
|
||||
|
||||
this.dialog.show = true
|
||||
},
|
||||
|
||||
deleteAddress(id, index) {
|
||||
this.$confirm('{{ __('admin/customer.confirm_delete_address') }}', '{{__('common.text_hint')}}', {
|
||||
confirmButtonText: '{{__('common.confirm')}}',
|
||||
cancelButtonText: '{{__('common.cancel')}}',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
$http.delete(`customers/{{ $attribute['id'] }}/addresses/${id}`).then((res) => {
|
||||
this.$message.success(res.message);
|
||||
this.source.attributeValues.splice(index, 1)
|
||||
})
|
||||
}).catch(()=>{})
|
||||
},
|
||||
|
||||
formSubmit(form) {
|
||||
const self = this;
|
||||
|
||||
this.$refs[form].validate((valid) => {
|
||||
if (!valid) {
|
||||
this.$message.error('{{ __('common.error_form') }}');
|
||||
return;
|
||||
}
|
||||
|
||||
const type = this.dialog.form.id ? 'put' : 'post';
|
||||
const url = type == 'post' ? `attributes/${this.form.id}/values` : `attributes/${this.form.id}/values/${this.dialog.form.id}`;
|
||||
|
||||
$http[type](url, this.dialog.form).then((res) => {
|
||||
this.$message.success(res.message);
|
||||
if (this.dialog.form.id) {
|
||||
this.source.attributeValues[this.dialog.index] = res.data
|
||||
} else {
|
||||
this.source.attributeValues.push(res.data)
|
||||
}
|
||||
|
||||
this.dialog.show = false
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
closeDialog(form) {
|
||||
this.$refs[form].resetFields();
|
||||
this.dialog.form.id = 0
|
||||
this.dialog.form.name = {}
|
||||
this.dialog.show = false
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
@extends('admin::layouts.master')
|
||||
|
||||
@section('title', __('admin/attribute.index'))
|
||||
|
||||
@section('content')
|
||||
<div id="app" class="card" v-cloak>
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between mb-4">
|
||||
<button type="button" class="btn btn-primary" @click="checkedCreate">{{ __('admin/attribute.create_at') }}</button>
|
||||
</div>
|
||||
<div class="table-push h-min-500">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ __('common.id') }}</th>
|
||||
<th>{{ __('common.name') }}</th>
|
||||
<th>{{ __('admin/attribute_groups.index') }}</th>
|
||||
<th>{{ __('common.created_at') }}</th>
|
||||
<th width="150px">{{ __('common.action') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody v-if="attributes.data.length">
|
||||
<tr v-for="item, index in attributes.data" :key="index">
|
||||
<td>@{{ item.id }}</td>
|
||||
<td>@{{ item.name }}</td>
|
||||
<td>@{{ item.attribute_group_name }}</td>
|
||||
<td>@{{ item.created_at }}</td>
|
||||
<td>
|
||||
<a class="btn btn-outline-secondary btn-sm" :href="linkEdit(item.id)">{{ __('common.edit') }}</a>
|
||||
<button class="btn btn-outline-danger btn-sm ml-1" type="button" @click="deleteItem(item.id)">{{ __('common.delete') }}</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody v-else><tr><td colspan="9" class="border-0"><x-admin-no-data /></td></tr></tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<el-pagination v-if="attributes.data.length" layout="prev, pager, next" background :page-size="attributes.per_page" :current-page.sync="page"
|
||||
:total="attributes.total"></el-pagination>
|
||||
</div>
|
||||
|
||||
<el-dialog title="{{ __('admin/attribute.index') }}" :visible.sync="dialog.show" width="670px"
|
||||
@close="closeDialog('form')" :close-on-click-modal="false">
|
||||
|
||||
<el-form ref="form" :rules="rules" :model="dialog.form" label-width="155px">
|
||||
<el-form-item label="{{ __('common.name') }}" required class="language-inputs">
|
||||
<el-form-item :prop="'name.' + lang.code" :inline-message="true" v-for="lang, lang_i in source.languages" :key="lang_i"
|
||||
:rules="[
|
||||
{ required: true, message: '{{ __('common.error_required', ['name' => __('common.name')]) }}', trigger: 'blur' },
|
||||
]"
|
||||
>
|
||||
<el-input size="mini" v-model="dialog.form.name[lang.code]" placeholder="{{ __('common.name') }}"><template slot="prepend">@{{lang.name}}</template></el-input>
|
||||
</el-form-item>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="{{ __('admin/attribute_groups.index') }}" required prop="attribute_group_id">
|
||||
<el-select v-model="dialog.form.attribute_group_id" placeholder="{{ __('common.please_choose') }}">
|
||||
<el-option
|
||||
v-for="item in source.attribute_group"
|
||||
:key="item.id"
|
||||
:label="item.description?.name || ''"
|
||||
:value="item.id">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="{{ __('common.sort_order') }}" prop="sort_order">
|
||||
<el-input class="mb-0 wp-100" v-model="dialog.form.sort_order" type="number" placeholder="{{ __('common.sort_order') }}"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<div class="d-flex d-lg-block mt-4">
|
||||
<el-button type="primary" @click="formSubmit('form')">{{ __('common.save') }}</el-button>
|
||||
<el-button @click="closeDialog('form')">{{ __('common.cancel') }}</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('footer')
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
|
||||
data: {
|
||||
page: 1,
|
||||
attributes: @json($attribute_list ?? []),
|
||||
|
||||
source: {
|
||||
attribute_group: @json($attribute_group ?? []),
|
||||
languages: @json(locales()),
|
||||
locale: @json(locale()),
|
||||
},
|
||||
|
||||
dialog: {
|
||||
show: false,
|
||||
index: null,
|
||||
type: 'add',
|
||||
form: {
|
||||
id: null,
|
||||
name: {},
|
||||
sort_order: 0,
|
||||
attribute_group_id: '',
|
||||
},
|
||||
},
|
||||
|
||||
rules: {
|
||||
attribute_group_id: [
|
||||
{required: true, message: '{{ __('common.error_required', ['name' => __('admin/attribute_groups.index')] ) }}', trigger: 'blur'},
|
||||
],
|
||||
sort_order: [{required: true,message: '{{ __('common.error_required', ['name' => __('common.sort_order')])}}',trigger: 'blur'}, ],
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
page: function() {
|
||||
this.loadData();
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
loadData() {
|
||||
$http.get(`attributes?page=${this.page}`).then((res) => {
|
||||
this.attributes = res.data.attribute_list;
|
||||
})
|
||||
},
|
||||
|
||||
linkEdit(id) {
|
||||
return '{{ admin_route('attributes.index') }}' + `/${id}`
|
||||
},
|
||||
|
||||
checkedCreate() {
|
||||
this.dialog.show = true
|
||||
},
|
||||
|
||||
formSubmit(form) {
|
||||
const self = this;
|
||||
|
||||
this.$refs[form].validate((valid) => {
|
||||
if (!valid) {
|
||||
this.$message.error('{{ __('common.error_form') }}');
|
||||
return;
|
||||
}
|
||||
|
||||
$http.post('attributes', this.dialog.form).then((res) => {
|
||||
this.$message.success(res.message);
|
||||
this.loadData();// this.customers.data.push(res.data);
|
||||
this.dialog.show = false
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
deleteItem(id) {
|
||||
const self = this;
|
||||
this.$confirm('{{ __('common.confirm_delete') }}', '{{ __('common.text_hint') }}', {
|
||||
confirmButtonText: '{{ __('common.confirm') }}',
|
||||
cancelButtonText: '{{ __('common.cancel') }}',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
$http.delete(`attributes/${id}`).then((res) => {
|
||||
self.$message.success(res.message);
|
||||
window.location.reload();
|
||||
// self.customers.splice(index, 1)
|
||||
})
|
||||
}).catch(()=>{})
|
||||
},
|
||||
|
||||
closeDialog(form) {
|
||||
this.$refs[form].resetFields();
|
||||
this.dialog.show = false
|
||||
},
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@endpush
|
||||
|
|
@ -31,6 +31,9 @@
|
|||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#tab-descriptions" type="button">{{ __('admin/product.product_details') }}</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#tab-attribute" type="button">{{ __('admin/attribute.index') }}</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="card">
|
||||
|
|
@ -266,6 +269,51 @@
|
|||
</div>
|
||||
</x-admin::form.row>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="tab-attribute">
|
||||
<x-admin::form.row title="{{ __('admin/attribute.set_attribute') }}">
|
||||
<div class="pdf-table">
|
||||
<table class="table table-bordered w-max-600">
|
||||
<thead><th>{{ __('admin/attribute.index') }}</th><th>{{ __('admin/attribute.attribute_value') }}</th><th width="50px"></th></thead>
|
||||
<tbody>
|
||||
<tr v-for="item, index in form.attributes" :key="index">
|
||||
<td>
|
||||
<el-autocomplete
|
||||
v-model="item.attribute.name"
|
||||
:fetch-suggestions="attributeQuerySearch"
|
||||
placeholder="{{ __('admin/builder.modules_keywords_search') }}"
|
||||
value-key="name"
|
||||
class="w-100"
|
||||
size="small"
|
||||
@select="(e) => {attributeHandleSelect(e, index, 'attribute')}"
|
||||
></el-autocomplete>
|
||||
<input type="text" :name="'attributes['+ index +'][attribute_id]'" v-model="item.attribute.id" class="form-control d-none">
|
||||
</td>
|
||||
<td>
|
||||
<el-autocomplete
|
||||
v-model="item.attribute_value.name"
|
||||
:fetch-suggestions="((query, cb) => {attributeValueQuerySearch(query, cb, index)})"
|
||||
size="small"
|
||||
:disabled="item.attribute.id == ''"
|
||||
value-key="name"
|
||||
class="w-100"
|
||||
:placeholder="item.attribute.id == '' ? '{{ __('admin/attribute.before_attribute') }}' : '{{ __('admin/builder.modules_keywords_search') }}'"
|
||||
@select="(e) => {attributeHandleSelect(e, index, 'attribute_value')}"
|
||||
></el-autocomplete>
|
||||
<input type="text" :name="'attributes['+ index +'][attribute_value_id]'" v-model="item.attribute_value.id" class="form-control d-none">
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<i @click="form.attributes.splice(index, 1)" class="bi bi-x-circle fs-4 text-danger cursor-pointer"></i>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"></td>
|
||||
<td class="text-end"><i class="bi bi-plus-circle cursor-pointer fs-4" @click="addAttribute"></i></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</x-admin::form.row>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
|
@ -287,7 +335,7 @@
|
|||
{ required: true, message: '{{ __('common.error_input_required') }}', trigger: 'blur' },
|
||||
]"
|
||||
>
|
||||
<el-input size="mini" v-model="dialogVariables.form.name[lang.code]" placeholder="请填写名称"><template slot="prepend">@{{lang.name}}</template></el-input>
|
||||
<el-input size="mini" v-model="dialogVariables.form.name[lang.code]" placeholder="{{ __('common.name') }}"><template slot="prepend">@{{lang.name}}</template></el-input>
|
||||
</el-form-item>
|
||||
</el-form-item>
|
||||
|
||||
|
|
@ -297,6 +345,29 @@
|
|||
</el-form-item>
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="{{ __('admin/attribute.attribute_value') }}" :visible.sync="attributeDialog.show" width="670px"
|
||||
@close="attributeCloseDialog('attribute_form')" :close-on-click-modal="false">
|
||||
|
||||
<el-form ref="attribute_form" :model="attributeDialog.form" label-width="155px">
|
||||
<el-form-item label="{{ __('common.name') }}" required class="language-inputs">
|
||||
<el-form-item :prop="'name.' + lang.code" :inline-message="true" v-for="lang, lang_i in source.languages" :key="lang_i"
|
||||
:rules="[
|
||||
{ required: true, message: '{{ __('common.error_required', ['name' => __('common.name')]) }}', trigger: 'blur' },
|
||||
]"
|
||||
>
|
||||
<el-input size="mini" v-model="attributeDialog.form.name[lang.code]" placeholder="{{ __('common.name') }}"><template slot="prepend">@{{lang.name}}</template></el-input>
|
||||
</el-form-item>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<div class="d-flex d-lg-block mt-4">
|
||||
<el-button type="primary" @click="attributeSubmit('attribute_form')">{{ __('common.save') }}</el-button>
|
||||
<el-button @click="attributeCloseDialog('attribute_form')">{{ __('common.cancel') }}</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -310,6 +381,7 @@
|
|||
current_language_code: '{{ locale() }}',
|
||||
isMove: false,
|
||||
form: {
|
||||
attributes: @json(old('pickups', $product_attributes) ?? []),
|
||||
images: @json($product->images ?? []),
|
||||
model: @json($product->skus[0]['model'] ?? ''),
|
||||
price: @json($product->skus[0]['price'] ?? ''),
|
||||
|
|
@ -319,6 +391,7 @@
|
|||
variables: @json($product->variables ?? []),
|
||||
skus: @json($product->skus ?? []),
|
||||
},
|
||||
|
||||
source: {
|
||||
variables: @json($product->variables ?? []),
|
||||
languages: @json($languages ?? []),
|
||||
|
|
@ -337,6 +410,14 @@
|
|||
},
|
||||
},
|
||||
|
||||
attributeDialog: {
|
||||
show: false,
|
||||
index: null,
|
||||
form: {
|
||||
name: {},
|
||||
}
|
||||
},
|
||||
|
||||
rules: {}
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -417,7 +498,7 @@
|
|||
|
||||
this.$refs[form].validate((valid) => {
|
||||
if (!valid) {
|
||||
this.$message.error('请检查表单是否填写正确');
|
||||
this.$message.error('{{ __('common.error_form') }}');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -502,6 +583,65 @@
|
|||
this.dialogVariables.variantIndex = variantIndex;
|
||||
},
|
||||
|
||||
addAttribute() {
|
||||
this.form.attributes.push({attribute:{id:'',name:''}, attribute_value: {id:'',name:''}})
|
||||
},
|
||||
|
||||
attributeQuerySearch(keyword, cb) {
|
||||
$http.get('attributes/autocomplete?name=' + encodeURIComponent(keyword), null, {hload:true}).then((res) => {
|
||||
cb(res.data);
|
||||
})
|
||||
},
|
||||
|
||||
attributeValueQuerySearch(keyword, cb, index) {
|
||||
$http.get(`attributes/${this.form.attributes[index].attribute.id}/values/autocomplete?name=${encodeURIComponent(keyword)}`, null, {hload:true}).then((res) => {
|
||||
res.data.push({id: 'add', name: '{{ __('admin/attribute.add_attribute') }}'})
|
||||
cb(res.data);
|
||||
})
|
||||
},
|
||||
|
||||
attributeHandleSelect(item, index, type) {
|
||||
if (type == 'attribute' && item.id != this.form.attributes[index].attribute.id) {
|
||||
this.form.attributes[index].attribute_value.name = ''
|
||||
this.form.attributes[index].attribute_value.id = ''
|
||||
}
|
||||
|
||||
if (item.id == 'add') {
|
||||
this.attributeDialog.show = true;
|
||||
this.attributeDialog.index = index;
|
||||
this.form.attributes[index].attribute_value.name = ''
|
||||
return;
|
||||
}
|
||||
|
||||
this.form.attributes[index][type].name = item.name
|
||||
this.form.attributes[index][type].id = item.id
|
||||
},
|
||||
|
||||
attributeSubmit(form) {
|
||||
const self = this;
|
||||
|
||||
this.$refs[form].validate((valid) => {
|
||||
if (!valid) {
|
||||
this.$message.error('{{ __('common.error_form') }}');
|
||||
return;
|
||||
}
|
||||
|
||||
const id = this.form.attributes[this.attributeDialog.index].attribute.id;
|
||||
|
||||
$http.post(`attributes/${id}/values`, this.attributeDialog.form).then((res) => {
|
||||
this.form.attributes[this.attributeDialog.index].attribute_value.id = res.data.id
|
||||
this.form.attributes[this.attributeDialog.index].attribute_value.name = res.data.description.name
|
||||
this.attributeDialog.show = false
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
attributeCloseDialog(form) {
|
||||
this.$refs[form].resetFields();
|
||||
this.attributeDialog.form.name = {}
|
||||
this.attributeDialog.show = false
|
||||
},
|
||||
|
||||
remakeSkus() {
|
||||
const combos = makeVariableIndexes();
|
||||
|
||||
|
|
|
|||
|
|
@ -230,4 +230,24 @@ body.page-product {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.attribute-table {
|
||||
tr {
|
||||
td:first-of-type {
|
||||
@media (min-width: 768px) {
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
width: 40%;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(2n+1) {
|
||||
td {
|
||||
background-color: rgba(249, 172, 17, .05);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
/**
|
||||
* order.php
|
||||
*
|
||||
* @copyright 2022 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2022-08-02 14:22:41
|
||||
* @modified 2022-08-02 14:22:41
|
||||
*/
|
||||
|
||||
return [
|
||||
'index' => 'attribut',
|
||||
'attribute_info' => 'Attributinformationen',
|
||||
'create_at' => 'Attribut erstellen',
|
||||
'attribute_value' => 'Attributwert',
|
||||
'set_attribute' => 'Konfigurationsattribut',
|
||||
'add_attribute' => 'Attributwert hinzufügen',
|
||||
'before_attribute' => 'Bitte zuerst das linke Attribut auswählen',
|
||||
];
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
/**
|
||||
* order.php
|
||||
*
|
||||
* @copyright 2022 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2022-08-02 14:22:41
|
||||
* @modified 2022-08-02 14:22:41
|
||||
*/
|
||||
|
||||
return [
|
||||
'index' => 'Attributgruppe',
|
||||
'create_at_groups' => 'Attributgruppe erstellen',
|
||||
];
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
/**
|
||||
* order.php
|
||||
*
|
||||
* @copyright 2022 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2022-08-02 14:22:41
|
||||
* @modified 2022-08-02 14:22:41
|
||||
*/
|
||||
|
||||
return [
|
||||
'index' => 'Attribute',
|
||||
'attribute_info' => 'Attribute information',
|
||||
'create_at' => 'Create attribute',
|
||||
'attribute_value' => 'Cttribute value',
|
||||
'set_attribute' => 'Configuration attribute',
|
||||
'add_attribute' => 'Add attribute value',
|
||||
'before_attribute' => 'Please select the left attribute first',
|
||||
];
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
/**
|
||||
* order.php
|
||||
*
|
||||
* @copyright 2022 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2022-08-02 14:22:41
|
||||
* @modified 2022-08-02 14:22:41
|
||||
*/
|
||||
|
||||
return [
|
||||
'index' => 'Attribute group',
|
||||
'create_at_groups' => 'Create attribute group',
|
||||
];
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
/**
|
||||
* order.php
|
||||
*
|
||||
* @copyright 2022 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2022-08-02 14:22:41
|
||||
* @modified 2022-08-02 14:22:41
|
||||
*/
|
||||
|
||||
return [
|
||||
'index' => 'atributo',
|
||||
'attribute_info' => 'información del atributo',
|
||||
'create_at' => 'crear atributo',
|
||||
'attribute_value' => 'valor de atributo',
|
||||
'set_attribute' => 'atributo de configuración',
|
||||
'add_attribute' => 'agregar valor de atributo',
|
||||
'before_attribute' => 'Seleccione primero el atributo de la izquierda',
|
||||
];
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
/**
|
||||
* order.php
|
||||
*
|
||||
* @copyright 2022 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2022-08-02 14:22:41
|
||||
* @modified 2022-08-02 14:22:41
|
||||
*/
|
||||
|
||||
return [
|
||||
'index' => 'grupo de atributos',
|
||||
'create_at_groups' => 'crear grupo de atributos',
|
||||
];
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
/**
|
||||
* order.php
|
||||
*
|
||||
* @copyright 2022 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2022-08-02 14:22:41
|
||||
* @modified 2022-08-02 14:22:41
|
||||
*/
|
||||
|
||||
return [
|
||||
'index' => 'attribut',
|
||||
'attribute_info' => 'informations d\'attribut',
|
||||
'create_at' => 'créer un attribut',
|
||||
'attribute_value' => 'valeur d\'attribut',
|
||||
'set_attribute' => 'attribut de configuration',
|
||||
'add_attribute' => 'ajouter une valeur d\'attribut',
|
||||
'before_attribute' => 'Veuillez d\'abord sélectionner l\'attribut de gauche',
|
||||
];
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
/**
|
||||
* order.php
|
||||
*
|
||||
* @copyright 2022 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2022-08-02 14:22:41
|
||||
* @modified 2022-08-02 14:22:41
|
||||
*/
|
||||
|
||||
return [
|
||||
'index' => 'groupe d\'attributs',
|
||||
'create_at_groups' => 'créer un groupe d\'attributs',
|
||||
];
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
/**
|
||||
* order.php
|
||||
*
|
||||
* @copyright 2022 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2022-08-02 14:22:41
|
||||
* @modified 2022-08-02 14:22:41
|
||||
*/
|
||||
|
||||
return [
|
||||
'index' => 'attributo',
|
||||
'attribute_info' => 'informazioni sugli attributi',
|
||||
'create_at' => 'crea attributo',
|
||||
'attribute_value' => 'valore attributo',
|
||||
'set_attribute' => 'attributo di configurazione',
|
||||
'add_attribute' => 'aggiungi valore attributo',
|
||||
'before_attribute' => 'Si prega di selezionare prima l\'attributo di sinistra',
|
||||
];
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
/**
|
||||
* order.php
|
||||
*
|
||||
* @copyright 2022 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2022-08-02 14:22:41
|
||||
* @modified 2022-08-02 14:22:41
|
||||
*/
|
||||
|
||||
return [
|
||||
'index' => 'gruppo di attributi',
|
||||
'create_at_groups' => 'crea gruppo di attributi',
|
||||
];
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
/**
|
||||
* order.php
|
||||
*
|
||||
* @copyright 2022 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2022-08-02 14:22:41
|
||||
* @modified 2022-08-02 14:22:41
|
||||
*/
|
||||
|
||||
return [
|
||||
'index' => '属性',
|
||||
'attribute_info' => '属性情報',
|
||||
'create_at' => '属性を作成',
|
||||
'attribute_value' => '属性値',
|
||||
'set_attribute' => '構成属性',
|
||||
'add_attribute' => '属性値を追加',
|
||||
'before_attribute' => '最初に左の属性を選択してください',
|
||||
];
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
/**
|
||||
* order.php
|
||||
*
|
||||
* @copyright 2022 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2022-08-02 14:22:41
|
||||
* @modified 2022-08-02 14:22:41
|
||||
*/
|
||||
|
||||
return [
|
||||
'index' => '属性グループ',
|
||||
'create_at_groups' => '属性グループの作成',
|
||||
];
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
/**
|
||||
* order.php
|
||||
*
|
||||
* @copyright 2022 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2022-08-02 14:22:41
|
||||
* @modified 2022-08-02 14:22:41
|
||||
*/
|
||||
|
||||
return [
|
||||
'index' => 'атрибут',
|
||||
'attribute_info' => 'информация об атрибутах',
|
||||
'create_at' => 'создать атрибут',
|
||||
'attribute_value' => 'значение атрибута',
|
||||
'set_attribute' => 'атрибут конфигурации',
|
||||
'add_attribute' => 'добавить значение атрибута',
|
||||
'before_attribute' => 'Сначала выберите левый атрибут',
|
||||
];
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
/**
|
||||
* order.php
|
||||
*
|
||||
* @copyright 2022 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2022-08-02 14:22:41
|
||||
* @modified 2022-08-02 14:22:41
|
||||
*/
|
||||
|
||||
return [
|
||||
'index' => 'группа атрибутов',
|
||||
'create_at_groups' => 'создать группу атрибутов',
|
||||
];
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
/**
|
||||
* order.php
|
||||
*
|
||||
* @copyright 2022 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2022-08-02 14:22:41
|
||||
* @modified 2022-08-02 14:22:41
|
||||
*/
|
||||
|
||||
return [
|
||||
'index' => '属性',
|
||||
'attribute_info' => '属性信息',
|
||||
'create_at' => '创建属性',
|
||||
'attribute_value' => '属性值',
|
||||
'set_attribute' => '配置属性',
|
||||
'add_attribute' => '添加属性值',
|
||||
'before_attribute' => '请先选择左边属性',
|
||||
];
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
/**
|
||||
* order.php
|
||||
*
|
||||
* @copyright 2022 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2022-08-02 14:22:41
|
||||
* @modified 2022-08-02 14:22:41
|
||||
*/
|
||||
|
||||
return [
|
||||
'index' => '属性组',
|
||||
'create_at_groups' => '创建属性组',
|
||||
];
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
/**
|
||||
* order.php
|
||||
*
|
||||
* @copyright 2022 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2022-08-02 14:22:41
|
||||
* @modified 2022-08-02 14:22:41
|
||||
*/
|
||||
|
||||
return [
|
||||
'index' => '屬性',
|
||||
'attribute_info' => '屬性信息',
|
||||
'create_at' => '創建屬性',
|
||||
'attribute_value' => '屬性值',
|
||||
'set_attribute' => '配置屬性',
|
||||
'add_attribute' => '添加屬性值',
|
||||
'before_attribute' => '請先選擇左邊屬性',
|
||||
];
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
/**
|
||||
* order.php
|
||||
*
|
||||
* @copyright 2022 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2022-08-02 14:22:41
|
||||
* @modified 2022-08-02 14:22:41
|
||||
*/
|
||||
|
||||
return [
|
||||
'index' => '屬性組',
|
||||
'create_at_groups' => '創建屬性組',
|
||||
];
|
||||
|
|
@ -136,14 +136,31 @@
|
|||
|
||||
<div class="product-description">
|
||||
<div class="nav nav-tabs nav-overflow justify-content-start justify-content-md-center border-bottom mb-3">
|
||||
<h5 class="mb-0"><a class="nav-link fw-bold active" data-bs-toggle="tab" href="#product-description">
|
||||
<a class="nav-link fw-bold active fs-5" data-bs-toggle="tab" href="#product-description">
|
||||
{{ __('shop/products.product_details') }}
|
||||
</a></h5>
|
||||
</a>
|
||||
@if ($product['attributes'])
|
||||
<a class="nav-link fw-bold fs-5" data-bs-toggle="tab" href="#product-attributes">
|
||||
{{ __('admin/attribute.index') }}
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane fade show active" id="product-description" role="tabpanel" aria-labelledby="pills-home-tab">
|
||||
<div class="tab-pane fade show active" id="product-description" role="tabpanel">
|
||||
{!! $product['description'] !!}
|
||||
</div>
|
||||
<div class="tab-pane fade" id="product-attributes" role="tabpanel">
|
||||
<table class="table table-bordered attribute-table">
|
||||
<tbody>
|
||||
@foreach ($product['attributes'] as $item)
|
||||
<tr>
|
||||
<td>{{ $item['attribute'] }}</td>
|
||||
<td>{{ $item['attribute_value'] }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue