Merge pull request #4940 from devansh-webkul/default-variant-enhancement
Default Variant Enhancement Completed #4911
This commit is contained in:
commit
20cd22fd1b
|
|
@ -1,5 +1,6 @@
|
|||
@section('css')
|
||||
@parent
|
||||
|
||||
<style>
|
||||
.table th.price, .table th.weight {
|
||||
width: 100px;
|
||||
|
|
@ -68,7 +69,7 @@
|
|||
<div class="form-container">
|
||||
|
||||
<div v-for='(attribute, index) in super_attributes' class="control-group"
|
||||
:class="[errors.has('add-variant-form.' + attribute.code) ? 'has-error' : '']"
|
||||
:class="[errors.has('add-variant-form.' + attribute.code) ? 'has-error' : '']"
|
||||
>
|
||||
<label :for="attribute.code" class="required">@{{ attribute.admin_name
|
||||
}}</label>
|
||||
|
|
@ -85,7 +86,7 @@
|
|||
</option>
|
||||
</select>
|
||||
<span class="control-error"
|
||||
v-if="errors.has('add-variant-form.' + attribute.code)">@{{ errors.first('add-variant-form.' + attribute.code) }}</span>
|
||||
v-if="errors.has('add-variant-form.' + attribute.code)">@{{ errors.first('add-variant-form.' + attribute.code) }}</span>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-lg btn-primary">
|
||||
|
|
@ -101,34 +102,31 @@
|
|||
<script type="text/x-template" id="variant-list-template">
|
||||
<div class="table" style="margin-top: 20px; overflow-x: auto;">
|
||||
<table>
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sku">{{ __('admin::app.catalog.products.sku') }}</th>
|
||||
<th>{{ __('admin::app.catalog.products.name') }}</th>
|
||||
<th>{{ __('admin::app.catalog.products.images') }}</th>
|
||||
<tr>
|
||||
<th class="is-default">{{ __('admin::app.catalog.products.is-default') }}</th>
|
||||
<th class="sku">{{ __('admin::app.catalog.products.sku') }}</th>
|
||||
<th>{{ __('admin::app.catalog.products.name') }}</th>
|
||||
<th>{{ __('admin::app.catalog.products.images') }}</th>
|
||||
|
||||
@foreach ($product->super_attributes as $attribute)
|
||||
<th class="{{ $attribute->code }}"
|
||||
style="width: 150px">{{ $attribute->admin_name }}</th>
|
||||
@endforeach
|
||||
@foreach ($product->super_attributes as $attribute)
|
||||
<th class="{{ $attribute->code }}"
|
||||
style="width: 150px">{{ $attribute->admin_name }}</th>
|
||||
@endforeach
|
||||
|
||||
<th class="qty">{{ __('admin::app.catalog.products.qty') }}</th>
|
||||
<th class="price">{{ __('admin::app.catalog.products.price') }}</th>
|
||||
<th class="weight">{{ __('admin::app.catalog.products.weight') }}</th>
|
||||
<th class="status">{{ __('admin::app.catalog.products.status') }}</th>
|
||||
<th class="actions"></th>
|
||||
</tr>
|
||||
<th class="qty">{{ __('admin::app.catalog.products.qty') }}</th>
|
||||
<th class="price">{{ __('admin::app.catalog.products.price') }}</th>
|
||||
<th class="weight">{{ __('admin::app.catalog.products.weight') }}</th>
|
||||
<th class="status">{{ __('admin::app.catalog.products.status') }}</th>
|
||||
<th class="actions"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
|
||||
<variant-item v-for='(variant, index) in variants' :variant="variant" :key="index"
|
||||
:index="index"
|
||||
@onRemoveVariant="removeVariant($event)"></variant-item>
|
||||
|
||||
<variant-item v-for='(variant, index) in variants' :key="index" :index="index"
|
||||
:variant="variant" @onRemoveVariant="removeVariant($event)">
|
||||
</variant-item>
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
</script>
|
||||
|
|
@ -136,22 +134,32 @@
|
|||
<script type="text/x-template" id="variant-item-template">
|
||||
<tr>
|
||||
<td>
|
||||
<div class="control-group"
|
||||
:class="[errors.has(variantInputName + '[sku]') ? 'has-error' : '']">
|
||||
<div class="control-group">
|
||||
<span class="radio">
|
||||
<input type="radio" id="default_variant_id"
|
||||
name="default_variant_id" :value="variant.id"
|
||||
v-on:change="checkDefaultVariant(variant.id)" :checked="variant.id == default_variant_id">
|
||||
<label class="radio-view" :for="[variantInputName + '[default_variant_id]']"></label>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<div class="control-group" :class="[errors.has(variantInputName + '[sku]') ? 'has-error' : '']">
|
||||
<input type="text" v-validate="'required'" v-model="variant.sku"
|
||||
:name="[variantInputName + '[sku]']" class="control"
|
||||
data-vv-as=""{{ __('admin::app.catalog.products.sku') }}""
|
||||
v-slugify/>
|
||||
:name="[variantInputName + '[sku]']" class="control"
|
||||
data-vv-as=""{{ __('admin::app.catalog.products.sku') }}""
|
||||
v-slugify/>
|
||||
<span class="control-error" v-if="errors.has(variantInputName + '[sku]')">@{{ errors.first(variantInputName + '[sku]') }}</span>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<div class="control-group"
|
||||
:class="[errors.has(variantInputName + '[name]') ? 'has-error' : '']">
|
||||
:class="[errors.has(variantInputName + '[name]') ? 'has-error' : '']">
|
||||
<input type="text" v-validate="'required'" v-model="variant.name"
|
||||
:name="[variantInputName + '[name]']" class="control"
|
||||
data-vv-as=""{{ __('admin::app.catalog.products.name') }}""/>
|
||||
:name="[variantInputName + '[name]']" class="control"
|
||||
data-vv-as=""{{ __('admin::app.catalog.products.name') }}""/>
|
||||
<span class="control-error" v-if="errors.has(variantInputName + '[name]')">@{{ errors.first(variantInputName + '[name]') }}</span>
|
||||
</div>
|
||||
</td>
|
||||
|
|
@ -179,9 +187,9 @@
|
|||
<td v-for='(attribute, index) in superAttributes'>
|
||||
<div class="control-group">
|
||||
<input type="hidden" :name="[variantInputName + '[' + attribute.code + ']']"
|
||||
:value="variant[attribute.code]"/>
|
||||
:value="variant[attribute.code]"/>
|
||||
<input type="text" class="control" :value="optionName(variant[attribute.code])"
|
||||
readonly/>
|
||||
readonly/>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
|
|
@ -196,15 +204,15 @@
|
|||
<ul>
|
||||
<li v-for='(inventorySource, index) in inventorySources'>
|
||||
<div class="control-group"
|
||||
:class="[errors.has(variantInputName + '[inventories][' + inventorySource.id + ']') ? 'has-error' : '']">
|
||||
:class="[errors.has(variantInputName + '[inventories][' + inventorySource.id + ']') ? 'has-error' : '']">
|
||||
<label>@{{ inventorySource.name }}</label>
|
||||
<input type="text" v-validate="'numeric|min:0'"
|
||||
:name="[variantInputName + '[inventories][' + inventorySource.id + ']']"
|
||||
v-model="inventories[inventorySource.id]" class="control"
|
||||
v-on:keyup="updateTotalQty()"
|
||||
:data-vv-as="'"' + inventorySource.name + '"'"/>
|
||||
:name="[variantInputName + '[inventories][' + inventorySource.id + ']']"
|
||||
v-model="inventories[inventorySource.id]" class="control"
|
||||
v-on:keyup="updateTotalQty()"
|
||||
:data-vv-as="'"' + inventorySource.name + '"'"/>
|
||||
<span class="control-error"
|
||||
v-if="errors.has(variantInputName + '[inventories][' + inventorySource.id + ']')">@{{ errors.first(variantInputName + '[inventories][' + inventorySource.id + ']') }}</span>
|
||||
v-if="errors.has(variantInputName + '[inventories][' + inventorySource.id + ']')">@{{ errors.first(variantInputName + '[inventories][' + inventorySource.id + ']') }}</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
@ -214,22 +222,22 @@
|
|||
|
||||
<td>
|
||||
<div class="control-group"
|
||||
:class="[errors.has(variantInputName + '[price]') ? 'has-error' : '']">
|
||||
:class="[errors.has(variantInputName + '[price]') ? 'has-error' : '']">
|
||||
<input type="number" v-validate="'required'" v-model="variant.price"
|
||||
:name="[variantInputName + '[price]']" class="control"
|
||||
data-vv-as=""{{ __('admin::app.catalog.products.price') }}""
|
||||
step="any"/>
|
||||
:name="[variantInputName + '[price]']" class="control"
|
||||
data-vv-as=""{{ __('admin::app.catalog.products.price') }}""
|
||||
step="any"/>
|
||||
<span class="control-error" v-if="errors.has(variantInputName + '[price]')">@{{ errors.first(variantInputName + '[price]') }}</span>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<div class="control-group"
|
||||
:class="[errors.has(variantInputName + '[weight]') ? 'has-error' : '']">
|
||||
:class="[errors.has(variantInputName + '[weight]') ? 'has-error' : '']">
|
||||
<input type="number" v-validate="'required'" v-model="variant.weight"
|
||||
:name="[variantInputName + '[weight]']" class="control"
|
||||
data-vv-as=""{{ __('admin::app.catalog.products.weight') }}""
|
||||
step="any"/>
|
||||
:name="[variantInputName + '[weight]']" class="control"
|
||||
data-vv-as=""{{ __('admin::app.catalog.products.weight') }}""
|
||||
step="any"/>
|
||||
<span class="control-error" v-if="errors.has(variantInputName + '[weight]')">@{{ errors.first(variantInputName + '[weight]') }}</span>
|
||||
</div>
|
||||
</td>
|
||||
|
|
@ -239,9 +247,9 @@
|
|||
<select type="text" v-model="variant.status"
|
||||
:name="[variantInputName + '[status]']" class="control">
|
||||
<option value="1"
|
||||
:selected="variant.status">{{ __('admin::app.catalog.products.enabled') }}</option>
|
||||
:selected="variant.status">{{ __('admin::app.catalog.products.enabled') }}</option>
|
||||
<option value="0"
|
||||
:selected="!variant.status">{{ __('admin::app.catalog.products.disabled') }}</option>
|
||||
:selected="!variant.status">{{ __('admin::app.catalog.products.disabled') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</td>
|
||||
|
|
@ -263,11 +271,10 @@
|
|||
];
|
||||
});
|
||||
|
||||
var super_attributes = @json(app('\Webkul\Product\Repositories\ProductRepository')->getSuperAttributes($product));
|
||||
var variants = @json($product->variants);
|
||||
let super_attributes = @json(app('\Webkul\Product\Repositories\ProductRepository')->getSuperAttributes($product));
|
||||
let variants = @json($product->variants);
|
||||
|
||||
Vue.component('variant-form', {
|
||||
|
||||
data: function () {
|
||||
return {
|
||||
variant: {},
|
||||
|
|
@ -285,18 +292,18 @@
|
|||
addVariant: function (formScope) {
|
||||
this.$validator.validateAll(formScope).then((result) => {
|
||||
if (result) {
|
||||
var this_this = this;
|
||||
let self = this;
|
||||
|
||||
var filteredVariants = variants.filter(function (variant) {
|
||||
var matchCount = 0;
|
||||
let filteredVariants = variants.filter(function (variant) {
|
||||
let matchCount = 0;
|
||||
|
||||
for (var key in this_this.variant) {
|
||||
if (variant[key] == this_this.variant[key]) {
|
||||
for (let key in self.variant) {
|
||||
if (variant[key] == self.variant[key]) {
|
||||
matchCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return matchCount == this_this.super_attributes.length;
|
||||
return matchCount == self.super_attributes.length;
|
||||
})
|
||||
|
||||
if (filteredVariants.length) {
|
||||
|
|
@ -309,9 +316,9 @@
|
|||
|
||||
this.$root.addFlashMessages()
|
||||
} else {
|
||||
var optionIds = [];
|
||||
for (var key in this_this.variant) {
|
||||
optionIds.push(this_this.variant[key]);
|
||||
let optionIds = [];
|
||||
for (let key in self.variant) {
|
||||
optionIds.push(self.variant[key]);
|
||||
}
|
||||
|
||||
variants.push(Object.assign({
|
||||
|
|
@ -331,17 +338,16 @@
|
|||
},
|
||||
|
||||
resetModel: function () {
|
||||
var this_this = this;
|
||||
let self = this;
|
||||
|
||||
this.super_attributes.forEach(function (attribute) {
|
||||
this_this.variant[attribute.code] = '';
|
||||
self.variant[attribute.code] = '';
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Vue.component('variant-list', {
|
||||
|
||||
template: '#variant-list-template',
|
||||
|
||||
inject: ['$validator'],
|
||||
|
|
@ -357,15 +363,15 @@
|
|||
},
|
||||
|
||||
created: function () {
|
||||
var index = 0;
|
||||
let index = 0;
|
||||
|
||||
for (var key in this.old_variants) {
|
||||
var variant = this.old_variants[key];
|
||||
for (let key in this.old_variants) {
|
||||
let variant = this.old_variants[key];
|
||||
|
||||
if (key.indexOf('variant_') !== -1) {
|
||||
var inventories = [];
|
||||
let inventories = [];
|
||||
|
||||
for (var inventorySourceId in variant['inventories']) {
|
||||
for (let inventorySourceId in variant['inventories']) {
|
||||
inventories.push({
|
||||
'qty': variant['inventories'][inventorySourceId],
|
||||
'inventory_source_id': inventorySourceId
|
||||
|
|
@ -376,13 +382,13 @@
|
|||
|
||||
variants.push(variant);
|
||||
} else {
|
||||
for (var code in variant) {
|
||||
for (let code in variant) {
|
||||
if (code != 'inventories') {
|
||||
variants[index][code] = variant[code];
|
||||
} else {
|
||||
variants[index][code] = [];
|
||||
|
||||
for (var inventorySourceId in variant[code]) {
|
||||
for (let inventorySourceId in variant[code]) {
|
||||
variants[index][code].push({
|
||||
'qty': variant[code][inventorySourceId],
|
||||
'inventory_source_id': inventorySourceId
|
||||
|
|
@ -403,11 +409,9 @@
|
|||
this.variants.splice(index, 1)
|
||||
},
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Vue.component('variant-item', {
|
||||
|
||||
template: '#variant-item-template',
|
||||
|
||||
props: ['index', 'variant'],
|
||||
|
|
@ -416,6 +420,7 @@
|
|||
|
||||
data: function () {
|
||||
return {
|
||||
default_variant_id: parseInt('{{ $product->additional['default_variant_id'] ?? null }}'),
|
||||
inventorySources: @json($inventorySources),
|
||||
inventories: {},
|
||||
totalQty: 0,
|
||||
|
|
@ -429,25 +434,25 @@
|
|||
},
|
||||
|
||||
created: function () {
|
||||
var this_this = this;
|
||||
let self = this;
|
||||
|
||||
this.inventorySources.forEach(function (inventorySource) {
|
||||
this_this.inventories[inventorySource.id] = this_this.sourceInventoryQty(inventorySource.id)
|
||||
this_this.totalQty += parseInt(this_this.inventories[inventorySource.id]);
|
||||
self.inventories[inventorySource.id] = self.sourceInventoryQty(inventorySource.id)
|
||||
self.totalQty += parseInt(self.inventories[inventorySource.id]);
|
||||
})
|
||||
},
|
||||
|
||||
mounted () {
|
||||
var this_this = this;
|
||||
let self = this;
|
||||
|
||||
this_this.variant.images.forEach(function(image) {
|
||||
this_this.items.push(image)
|
||||
this_this.imageCount++;
|
||||
self.variant.images.forEach(function(image) {
|
||||
self.items.push(image)
|
||||
self.imageCount++;
|
||||
|
||||
if (image.id && image.url) {
|
||||
this_this.imageData.push(image.url);
|
||||
self.imageData.push(image.url);
|
||||
} else if (image.id && image.file) {
|
||||
this_this.readFile(image.file);
|
||||
self.readFile(image.file);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
@ -463,11 +468,15 @@
|
|||
|
||||
methods: {
|
||||
removeVariant: function () {
|
||||
this.$emit('onRemoveVariant', this.variant)
|
||||
this.$emit('onRemoveVariant', this.variant);
|
||||
},
|
||||
|
||||
checkDefaultVariant: function (variantId) {
|
||||
this.default_variant_id = variantId;
|
||||
},
|
||||
|
||||
optionName: function (optionId) {
|
||||
var optionName = '';
|
||||
let optionName = '';
|
||||
|
||||
this.superAttributes.forEach(function (attribute) {
|
||||
attribute.options.forEach(function (option) {
|
||||
|
|
@ -484,7 +493,7 @@
|
|||
if (!Array.isArray(this.variant.inventories))
|
||||
return 0;
|
||||
|
||||
var inventories = this.variant.inventories.filter(function (inventory) {
|
||||
let inventories = this.variant.inventories.filter(function (inventory) {
|
||||
return inventorySourceId === parseInt(inventory.inventory_source_id);
|
||||
})
|
||||
|
||||
|
|
@ -497,13 +506,13 @@
|
|||
updateTotalQty: function () {
|
||||
this.totalQty = 0;
|
||||
|
||||
for (var key in this.inventories) {
|
||||
for (let key in this.inventories) {
|
||||
this.totalQty += parseInt(this.inventories[key]);
|
||||
}
|
||||
},
|
||||
|
||||
createFileType: function() {
|
||||
var this_this = this;
|
||||
let self = this;
|
||||
|
||||
this.imageCount++;
|
||||
|
||||
|
|
@ -521,8 +530,8 @@
|
|||
},
|
||||
|
||||
addImageView: function($event, index) {
|
||||
var ref = "imageInput" + index;
|
||||
var imageInput = this.$refs[ref][0];
|
||||
let ref = "imageInput" + index;
|
||||
let imageInput = this.$refs[ref][0];
|
||||
|
||||
if (imageInput.files && imageInput.files[0]) {
|
||||
if (imageInput.files[0].type.includes('image/')) {
|
||||
|
|
@ -537,7 +546,7 @@
|
|||
},
|
||||
|
||||
readFile: function(image, index) {
|
||||
var reader = new FileReader();
|
||||
let reader = new FileReader();
|
||||
|
||||
reader.onload = (e) => {
|
||||
this.imageData.splice(index, 1, e.target.result);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AlterProductsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('products', function (Blueprint $table) {
|
||||
$table->json('additional')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('products', function (Blueprint $table) {
|
||||
$table->dropColumn('additional');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -12,10 +12,14 @@ use Webkul\Attribute\Models\AttributeFamilyProxy;
|
|||
use Webkul\Inventory\Models\InventorySourceProxy;
|
||||
use Webkul\Attribute\Repositories\AttributeRepository;
|
||||
use Webkul\Product\Contracts\Product as ProductContract;
|
||||
use Webkul\CatalogRule\Models\CatalogRuleProductPriceProxy;
|
||||
|
||||
class Product extends Model implements ProductContract
|
||||
{
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var $fillable
|
||||
*/
|
||||
protected $fillable = [
|
||||
'type',
|
||||
'attribute_family_id',
|
||||
|
|
@ -23,6 +27,20 @@ class Product extends Model implements ProductContract
|
|||
'parent_id',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var $casts
|
||||
*/
|
||||
protected $casts = [
|
||||
'additional' => 'array'
|
||||
];
|
||||
|
||||
/**
|
||||
* The type of product.
|
||||
*
|
||||
* @var $typeInstance
|
||||
*/
|
||||
protected $typeInstance;
|
||||
|
||||
/**
|
||||
|
|
@ -412,11 +430,10 @@ class Product extends Model implements ProductContract
|
|||
}
|
||||
|
||||
/**
|
||||
* Overrides the default Eloquent query builder
|
||||
* Overrides the default Eloquent query builder.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
* @param \Illuminate\Database\Query\Builder $query
|
||||
* @return \Webkul\Product\Database\Eloquent\Builder
|
||||
*/
|
||||
public function newEloquentBuilder($query)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ abstract class AbstractType
|
|||
/**
|
||||
* Product instance
|
||||
*
|
||||
* @var \Webkul\Product\Contracts\Product
|
||||
* @var \Webkul\Product\Models\Product
|
||||
*/
|
||||
protected $product;
|
||||
|
||||
|
|
|
|||
|
|
@ -66,6 +66,55 @@ class Configurable extends AbstractType
|
|||
*/
|
||||
protected $productOptions = [];
|
||||
|
||||
/**
|
||||
* Get default variant.
|
||||
*
|
||||
* @return \Webkul\Product\Models\Product
|
||||
*/
|
||||
public function getDefaultVariant()
|
||||
{
|
||||
return $this->product->variants()->find($this->getDefaultVariantId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default variant id.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getDefaultVariantId()
|
||||
{
|
||||
return $this->product->additional['default_variant_id'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default variant id.
|
||||
*
|
||||
* @param int $defaultVariantId
|
||||
* @return void
|
||||
*/
|
||||
public function setDefaultVariantId($defaultVariantId)
|
||||
{
|
||||
$this->product->additional = array_merge($this->product->additional ?? [], [
|
||||
'default_variant_id' => $defaultVariantId
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update default variant id if present in request.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function updateDefaultVariantId()
|
||||
{
|
||||
$defaultVariantId = request()->get('default_variant_id');
|
||||
|
||||
if ($defaultVariantId) {
|
||||
$this->setDefaultVariantId($defaultVariantId);
|
||||
|
||||
$this->product->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create configurable product.
|
||||
*
|
||||
|
|
@ -106,6 +155,9 @@ class Configurable extends AbstractType
|
|||
public function update(array $data, $id, $attribute = "id")
|
||||
{
|
||||
$product = parent::update($data, $id, $attribute);
|
||||
|
||||
$this->updateDefaultVariantId();
|
||||
|
||||
$route = request()->route() ? request()->route()->getName() : '';
|
||||
|
||||
if ($route != 'admin.catalog.products.massupdate') {
|
||||
|
|
@ -533,12 +585,16 @@ class Configurable extends AbstractType
|
|||
* Add product. Returns error message if can't prepare product.
|
||||
*
|
||||
* @param array $data
|
||||
* @return array
|
||||
* @return array|string
|
||||
*/
|
||||
public function prepareForCart($data)
|
||||
{
|
||||
if (! isset($data['selected_configurable_option']) || ! $data['selected_configurable_option']) {
|
||||
return trans('shop::app.checkout.cart.integrity.missing_options');
|
||||
if ($this->getDefaultVariantId()) {
|
||||
$data['selected_configurable_option'] = $this->getDefaultVariantId();
|
||||
} else {
|
||||
return trans('shop::app.checkout.cart.integrity.missing_options');
|
||||
}
|
||||
}
|
||||
|
||||
$data = $this->getQtyRequest($data);
|
||||
|
|
@ -670,8 +726,7 @@ class Configurable extends AbstractType
|
|||
/**
|
||||
* Validate cart item product price.
|
||||
*
|
||||
* @param \Webkul\Product\Type\CartItem $item
|
||||
*
|
||||
* @param \Webkul\Product\Type\CartItem $item
|
||||
* @return \Webkul\Product\Datatypes\CartItemValidationResult
|
||||
*/
|
||||
public function validateCartItem(CartItemModel $item): CartItemValidationResult
|
||||
|
|
@ -704,8 +759,7 @@ class Configurable extends AbstractType
|
|||
/**
|
||||
* Get product options.
|
||||
*
|
||||
* @param string $product
|
||||
*
|
||||
* @param string $product
|
||||
* @return array
|
||||
*/
|
||||
public function getProductOptions($product = "")
|
||||
|
|
|
|||
|
|
@ -28,7 +28,11 @@
|
|||
:id="['attribute_' + attribute.id]"
|
||||
:data-vv-as="'"' + attribute.label + '"'">
|
||||
|
||||
<option v-for='(option, index) in attribute.options' :value="option.id">@{{ option.label }}</option>
|
||||
<option
|
||||
v-for='(option, index) in attribute.options' :value="option.id"
|
||||
:selected="index == attribute.selectedIndex">
|
||||
@{{ option.label }}
|
||||
</option>
|
||||
|
||||
</select>
|
||||
</span>
|
||||
|
|
@ -46,7 +50,8 @@
|
|||
:id="['attribute_' + attribute.id + '_option_' + option.id]"
|
||||
:value="option.id"
|
||||
:data-vv-as="'"' + attribute.label + '"'"
|
||||
@change="configure(attribute, $event.target.value)"/>
|
||||
@change="configure(attribute, $event.target.value)"
|
||||
:checked="index == attribute.selectedIndex"/>
|
||||
|
||||
<span v-if="attribute.swatch_type == 'color'" :style="{ background: option.swatch_value }"></span>
|
||||
|
||||
|
|
@ -69,18 +74,22 @@
|
|||
</div>
|
||||
</script>
|
||||
|
||||
<?php $config = $configurableOptionHelper->getConfigurationConfig($product) ?>
|
||||
@php
|
||||
$defaultVariant = $product->getTypeInstance()->getDefaultVariant();
|
||||
$config = $configurableOptionHelper->getConfigurationConfig($product);
|
||||
@endphp
|
||||
|
||||
<script>
|
||||
|
||||
Vue.component('product-options', {
|
||||
|
||||
template: '#product-options-template',
|
||||
|
||||
inject: ['$validator'],
|
||||
|
||||
data: function() {
|
||||
return {
|
||||
defaultVariant: @json($defaultVariant),
|
||||
|
||||
config: @json($config),
|
||||
|
||||
childAttributes: [],
|
||||
|
|
@ -89,40 +98,56 @@
|
|||
|
||||
simpleProduct: null,
|
||||
|
||||
galleryImages: []
|
||||
galleryImages: [],
|
||||
}
|
||||
},
|
||||
|
||||
created: function() {
|
||||
var config = @json($config);
|
||||
mounted: function() {
|
||||
this.init();
|
||||
|
||||
var childAttributes = this.childAttributes,
|
||||
attributes = config.attributes.slice(),
|
||||
index = attributes.length,
|
||||
attribute;
|
||||
|
||||
while (index--) {
|
||||
attribute = attributes[index];
|
||||
|
||||
attribute.options = [];
|
||||
|
||||
if (index) {
|
||||
attribute.disabled = true;
|
||||
} else {
|
||||
this.fillSelect(attribute);
|
||||
}
|
||||
|
||||
attribute = Object.assign(attribute, {
|
||||
childAttributes: childAttributes.slice(),
|
||||
prevAttribute: attributes[index - 1],
|
||||
nextAttribute: attributes[index + 1]
|
||||
});
|
||||
|
||||
childAttributes.unshift(attribute);
|
||||
}
|
||||
this.initDefaultSelection();
|
||||
},
|
||||
|
||||
methods: {
|
||||
init: function () {
|
||||
let config = @json($config);
|
||||
|
||||
let childAttributes = this.childAttributes,
|
||||
attributes = config.attributes.slice(),
|
||||
index = attributes.length,
|
||||
attribute;
|
||||
|
||||
while (index--) {
|
||||
attribute = attributes[index];
|
||||
|
||||
attribute.options = [];
|
||||
|
||||
if (index) {
|
||||
attribute.disabled = true;
|
||||
} else {
|
||||
this.fillSelect(attribute);
|
||||
}
|
||||
|
||||
attribute = Object.assign(attribute, {
|
||||
childAttributes: childAttributes.slice(),
|
||||
prevAttribute: attributes[index - 1],
|
||||
nextAttribute: attributes[index + 1]
|
||||
});
|
||||
|
||||
childAttributes.unshift(attribute);
|
||||
}
|
||||
},
|
||||
|
||||
initDefaultSelection: function() {
|
||||
if (this.defaultVariant) {
|
||||
this.childAttributes.forEach((attribute) => {
|
||||
let attributeValue = this.defaultVariant[attribute.code];
|
||||
|
||||
this.configure(attribute, attributeValue);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
configure: function(attribute, value) {
|
||||
this.simpleProduct = this.getSelectedProductId(attribute, value);
|
||||
|
||||
|
|
@ -152,7 +177,7 @@
|
|||
},
|
||||
|
||||
getSelectedIndex: function(attribute, value) {
|
||||
var selectedIndex = 0;
|
||||
let selectedIndex = 0;
|
||||
|
||||
attribute.options.forEach(function(option, index) {
|
||||
if (option.id == value) {
|
||||
|
|
@ -164,7 +189,7 @@
|
|||
},
|
||||
|
||||
getSelectedProductId: function(attribute, value) {
|
||||
var options = attribute.options,
|
||||
let options = attribute.options,
|
||||
matchedOptions;
|
||||
|
||||
matchedOptions = options.filter(function (option) {
|
||||
|
|
@ -179,7 +204,7 @@
|
|||
},
|
||||
|
||||
fillSelect: function(attribute) {
|
||||
var options = this.getAttributeOptions(attribute.id),
|
||||
let options = this.getAttributeOptions(attribute.id),
|
||||
prevOption,
|
||||
index = 1,
|
||||
allowedProducts,
|
||||
|
|
@ -233,15 +258,15 @@
|
|||
return;
|
||||
|
||||
if (! attribute.swatch_type || attribute.swatch_type == '' || attribute.swatch_type == 'dropdown') {
|
||||
var element = document.getElementById("attribute_" + attribute.id);
|
||||
let element = document.getElementById("attribute_" + attribute.id);
|
||||
|
||||
if (element) {
|
||||
element.selectedIndex = "0";
|
||||
}
|
||||
} else {
|
||||
var elements = document.getElementsByName('super_attribute[' + attribute.id + ']');
|
||||
let elements = document.getElementsByName('super_attribute[' + attribute.id + ']');
|
||||
|
||||
var this_this = this;
|
||||
let self = this;
|
||||
|
||||
elements.forEach(function(element) {
|
||||
element.checked = false;
|
||||
|
|
@ -250,7 +275,7 @@
|
|||
},
|
||||
|
||||
getAttributeOptions: function (attributeId) {
|
||||
var this_this = this,
|
||||
let self = this,
|
||||
options;
|
||||
|
||||
this.config.attributes.forEach(function(attribute, index) {
|
||||
|
|
@ -263,7 +288,7 @@
|
|||
},
|
||||
|
||||
reloadPrice: function () {
|
||||
var selectedOptionCount = 0;
|
||||
let selectedOptionCount = 0;
|
||||
|
||||
this.childAttributes.forEach(function(attribute) {
|
||||
if (attribute.selectedIndex) {
|
||||
|
|
@ -271,9 +296,9 @@
|
|||
}
|
||||
});
|
||||
|
||||
var priceLabelElement = document.querySelector('.price-label');
|
||||
var priceElement = document.querySelector('.final-price');
|
||||
var regularPriceElement = document.querySelector('.regular-price');
|
||||
let priceLabelElement = document.querySelector('.price-label');
|
||||
let priceElement = document.querySelector('.final-price');
|
||||
let regularPriceElement = document.querySelector('.regular-price');
|
||||
|
||||
if (this.childAttributes.length == selectedOptionCount) {
|
||||
priceLabelElement.style.display = 'none';
|
||||
|
|
@ -313,7 +338,7 @@
|
|||
},
|
||||
|
||||
changeStock: function (productId) {
|
||||
var inStockElement = document.querySelector('.stock-status');
|
||||
let inStockElement = document.querySelector('.stock-status');
|
||||
|
||||
if (productId) {
|
||||
inStockElement.style.display= "block";
|
||||
|
|
@ -322,7 +347,6 @@
|
|||
}
|
||||
},
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"/js/ui.js": "/js/ui.js?id=1a2a11fc54d0a5962a66",
|
||||
"/css/ui.css": "/css/ui.css?id=6d93a4a052e38d6aa795"
|
||||
"/css/ui.css": "/css/ui.css?id=87c15a3e7af4ab272377"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -559,10 +559,10 @@ h5 {
|
|||
margin: 10px 5px 5px 0px;
|
||||
|
||||
input {
|
||||
left: 0;
|
||||
left: -4px;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
top: -1px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
|
|
@ -587,6 +587,15 @@ h5 {
|
|||
}
|
||||
}
|
||||
|
||||
.rtl {
|
||||
.radio {
|
||||
input {
|
||||
left: unset;
|
||||
right: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.control-group {
|
||||
display: block;
|
||||
margin-bottom: 25px;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
@inject ('configurableOptionHelper', 'Webkul\Product\Helpers\ConfigurableOption')
|
||||
|
||||
@php
|
||||
$defaultVariant = $product->getTypeInstance()->getDefaultVariant();
|
||||
$config = $configurableOptionHelper->getConfigurationConfig($product);
|
||||
$galleryImages = productimage()->getGalleryImages($product);
|
||||
@endphp
|
||||
|
|
@ -20,7 +21,7 @@
|
|||
type="hidden"
|
||||
:value="selectedProductId"
|
||||
id="selected_configurable_option"
|
||||
name="selected_configurable_option" />
|
||||
name="selected_configurable_option"/>
|
||||
|
||||
<div
|
||||
:key="index"
|
||||
|
|
@ -43,16 +44,18 @@
|
|||
:disabled="attribute.disabled"
|
||||
:id="['attribute_' + attribute.id]"
|
||||
:name="['super_attribute[' + attribute.id + ']']"
|
||||
@change="configure(attribute, $event.target.value, $event)"
|
||||
@change="configure(attribute, $event.target.value)"
|
||||
:data-vv-as="'"' + attribute.label + '"'">
|
||||
|
||||
<option
|
||||
:value="option.id"
|
||||
v-for='(option, index) in attribute.options'>
|
||||
v-for='(option, index) in attribute.options'
|
||||
:selected="index == attribute.selectedIndex">
|
||||
@{{ option.label }}
|
||||
</option>
|
||||
|
||||
</select>
|
||||
|
||||
<div class="select-icon-container">
|
||||
<span class="select-icon rango-arrow-down"></span>
|
||||
</div>
|
||||
|
|
@ -72,7 +75,8 @@
|
|||
:name="['super_attribute[' + attribute.id + ']']"
|
||||
:id="['attribute_' + attribute.id + '_option_' + option.id]"
|
||||
:data-vv-as="'"' + attribute.label + '"'"
|
||||
@change="configure(attribute, $event.target.value)"/>
|
||||
@change="configure(attribute, $event.target.value)"
|
||||
:checked="index == attribute.selectedIndex">
|
||||
|
||||
<span v-if="attribute.swatch_type == 'color'" :style="{ background: option.swatch_value }"></span>
|
||||
|
||||
|
|
@ -97,52 +101,76 @@
|
|||
|
||||
<script type="text/javascript">
|
||||
(() => {
|
||||
var galleryImages = @json($galleryImages);
|
||||
let galleryImages = @json($galleryImages);
|
||||
|
||||
Vue.component('product-options', {
|
||||
template: '#product-options-template',
|
||||
|
||||
inject: ['$validator'],
|
||||
|
||||
data: function() {
|
||||
return {
|
||||
galleryImages: [],
|
||||
simpleProduct: null,
|
||||
childAttributes: [],
|
||||
selectedProductId: '',
|
||||
defaultVariant: @json($defaultVariant),
|
||||
|
||||
config: @json($config),
|
||||
|
||||
galleryImages: [],
|
||||
|
||||
simpleProduct: null,
|
||||
|
||||
childAttributes: [],
|
||||
|
||||
selectedProductId: '',
|
||||
}
|
||||
},
|
||||
|
||||
created: function() {
|
||||
var config = @json($config);
|
||||
mounted: function () {
|
||||
this.init();
|
||||
|
||||
var childAttributes = this.childAttributes,
|
||||
attributes = config.attributes.slice(),
|
||||
index = attributes.length,
|
||||
attribute;
|
||||
|
||||
while (index--) {
|
||||
attribute = attributes[index];
|
||||
|
||||
attribute.options = [];
|
||||
|
||||
if (index) {
|
||||
attribute.disabled = true;
|
||||
} else {
|
||||
this.fillSelect(attribute);
|
||||
}
|
||||
|
||||
attribute = Object.assign(attribute, {
|
||||
childAttributes: childAttributes.slice(),
|
||||
prevAttribute: attributes[index - 1],
|
||||
nextAttribute: attributes[index + 1]
|
||||
});
|
||||
|
||||
childAttributes.unshift(attribute);
|
||||
}
|
||||
this.initDefaultSelection();
|
||||
},
|
||||
|
||||
methods: {
|
||||
configure: function(attribute, value, event) {
|
||||
init: function () {
|
||||
let config = @json($config);
|
||||
|
||||
let childAttributes = this.childAttributes,
|
||||
attributes = config.attributes.slice(),
|
||||
index = attributes.length,
|
||||
attribute;
|
||||
|
||||
while (index--) {
|
||||
attribute = attributes[index];
|
||||
|
||||
attribute.options = [];
|
||||
|
||||
if (index) {
|
||||
attribute.disabled = true;
|
||||
} else {
|
||||
this.fillSelect(attribute);
|
||||
}
|
||||
|
||||
attribute = Object.assign(attribute, {
|
||||
childAttributes: childAttributes.slice(),
|
||||
prevAttribute: attributes[index - 1],
|
||||
nextAttribute: attributes[index + 1]
|
||||
});
|
||||
|
||||
childAttributes.unshift(attribute);
|
||||
}
|
||||
},
|
||||
|
||||
initDefaultSelection: function() {
|
||||
if (this.defaultVariant) {
|
||||
this.childAttributes.forEach((attribute) => {
|
||||
let attributeValue = this.defaultVariant[attribute.code];
|
||||
|
||||
this.configure(attribute, attributeValue);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
configure: function(attribute, value) {
|
||||
this.simpleProduct = this.getSelectedProductId(attribute, value);
|
||||
|
||||
if (value) {
|
||||
|
|
@ -168,7 +196,7 @@
|
|||
},
|
||||
|
||||
getSelectedIndex: function(attribute, value) {
|
||||
var selectedIndex = 0;
|
||||
let selectedIndex = 0;
|
||||
|
||||
attribute.options.forEach(function(option, index) {
|
||||
if (option.id == value) {
|
||||
|
|
@ -180,7 +208,7 @@
|
|||
},
|
||||
|
||||
getSelectedProductId: function(attribute, value) {
|
||||
var options = attribute.options,
|
||||
let options = attribute.options,
|
||||
matchedOptions;
|
||||
|
||||
matchedOptions = options.filter(function (option) {
|
||||
|
|
@ -195,12 +223,12 @@
|
|||
},
|
||||
|
||||
fillSelect: function(attribute) {
|
||||
var options = this.getAttributeOptions(attribute.id);
|
||||
var prevOption;
|
||||
var index = 1;
|
||||
var allowedProducts;
|
||||
var i;
|
||||
var j;
|
||||
let options = this.getAttributeOptions(attribute.id);
|
||||
let prevOption;
|
||||
let index = 1;
|
||||
let allowedProducts;
|
||||
let i;
|
||||
let j;
|
||||
|
||||
this.clearSelect(attribute)
|
||||
|
||||
|
|
@ -252,13 +280,13 @@
|
|||
|| attribute.swatch_type == ''
|
||||
|| attribute.swatch_type == 'dropdown'
|
||||
) {
|
||||
var element = document.getElementById(`attribute_${attribute.id}`);
|
||||
let element = document.getElementById(`attribute_${attribute.id}`);
|
||||
|
||||
if (element) {
|
||||
element.selectedIndex = "0";
|
||||
}
|
||||
} else {
|
||||
var elements = document.getElementsByName(`super_attribute[${attribute.id}]`);
|
||||
let elements = document.getElementsByName(`super_attribute[${attribute.id}]`);
|
||||
|
||||
elements.forEach(function(element) {
|
||||
element.checked = false;
|
||||
|
|
@ -267,7 +295,7 @@
|
|||
},
|
||||
|
||||
getAttributeOptions: function (attributeId) {
|
||||
var options;
|
||||
let options;
|
||||
|
||||
this.config.attributes.forEach(function(attribute, index) {
|
||||
if (attribute.id == attributeId) {
|
||||
|
|
@ -279,7 +307,7 @@
|
|||
},
|
||||
|
||||
reloadPrice: function () {
|
||||
var selectedOptionCount = 0;
|
||||
let selectedOptionCount = 0;
|
||||
|
||||
this.childAttributes.forEach(function(attribute) {
|
||||
if (attribute.selectedIndex) {
|
||||
|
|
@ -287,9 +315,9 @@
|
|||
}
|
||||
});
|
||||
|
||||
var priceLabelElement = document.querySelector('.price-label');
|
||||
var priceElement = document.querySelector('.final-price');
|
||||
var regularPriceElement = document.querySelector('.regular-price');
|
||||
let priceLabelElement = document.querySelector('.price-label');
|
||||
let priceElement = document.querySelector('.final-price');
|
||||
let regularPriceElement = document.querySelector('.regular-price');
|
||||
|
||||
if (this.childAttributes.length == selectedOptionCount) {
|
||||
priceLabelElement.style.display = 'none';
|
||||
|
|
@ -337,7 +365,7 @@
|
|||
},
|
||||
|
||||
changeStock: function (productId) {
|
||||
var inStockElement = document.querySelector('.disable-box-shadow');
|
||||
let inStockElement = document.querySelector('.disable-box-shadow');
|
||||
|
||||
if (productId) {
|
||||
inStockElement.style.display= "block";
|
||||
|
|
@ -347,7 +375,7 @@
|
|||
},
|
||||
}
|
||||
});
|
||||
})()
|
||||
})();
|
||||
</script>
|
||||
@endpush
|
||||
@endif
|
||||
Loading…
Reference in New Issue