属性配置前端

This commit is contained in:
pushuo 2023-01-06 14:46:34 +08:00
parent de7df33788
commit c05b21efc7
24 changed files with 1058 additions and 5 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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);
}
}
}
}
}

View File

@ -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',
];

View File

@ -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',
];

View File

@ -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',
];

View File

@ -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',
];

View File

@ -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',
];

View File

@ -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',
];

View File

@ -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',
];

View File

@ -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',
];

View File

@ -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',
];

View File

@ -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',
];

View File

@ -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' => '最初に左の属性を選択してください',
];

View File

@ -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' => '属性グループの作成',
];

View File

@ -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' => 'Сначала выберите левый атрибут',
];

View File

@ -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' => 'создать группу атрибутов',
];

View File

@ -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' => '请先选择左边属性',
];

View File

@ -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' => '创建属性组',
];

View File

@ -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' => '請先選擇左邊屬性',
];

View File

@ -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' => '創建屬性組',
];

View File

@ -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>