Merge pull request #4940 from devansh-webkul/default-variant-enhancement

Default Variant Enhancement Completed #4911
This commit is contained in:
Glenn Hermans 2021-06-23 14:54:22 +02:00 committed by GitHub
commit 20cd22fd1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 376 additions and 203 deletions

View File

@ -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="&quot;{{ __('admin::app.catalog.products.sku') }}&quot;"
v-slugify/>
:name="[variantInputName + '[sku]']" class="control"
data-vv-as="&quot;{{ __('admin::app.catalog.products.sku') }}&quot;"
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="&quot;{{ __('admin::app.catalog.products.name') }}&quot;"/>
:name="[variantInputName + '[name]']" class="control"
data-vv-as="&quot;{{ __('admin::app.catalog.products.name') }}&quot;"/>
<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="'&quot;' + inventorySource.name + '&quot;'"/>
:name="[variantInputName + '[inventories][' + inventorySource.id + ']']"
v-model="inventories[inventorySource.id]" class="control"
v-on:keyup="updateTotalQty()"
:data-vv-as="'&quot;' + inventorySource.name + '&quot;'"/>
<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="&quot;{{ __('admin::app.catalog.products.price') }}&quot;"
step="any"/>
:name="[variantInputName + '[price]']" class="control"
data-vv-as="&quot;{{ __('admin::app.catalog.products.price') }}&quot;"
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="&quot;{{ __('admin::app.catalog.products.weight') }}&quot;"
step="any"/>
:name="[variantInputName + '[weight]']" class="control"
data-vv-as="&quot;{{ __('admin::app.catalog.products.weight') }}&quot;"
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);

View File

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

View File

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

View File

@ -67,7 +67,7 @@ abstract class AbstractType
/**
* Product instance
*
* @var \Webkul\Product\Contracts\Product
* @var \Webkul\Product\Models\Product
*/
protected $product;

View File

@ -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 = "")

View File

@ -28,7 +28,11 @@
:id="['attribute_' + attribute.id]"
:data-vv-as="'&quot;' + attribute.label + '&quot;'">
<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="'&quot;' + attribute.label + '&quot;'"
@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

View File

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

View File

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

View File

@ -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="'&quot;' + attribute.label + '&quot;'">
<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="'&quot;' + attribute.label + '&quot;'"
@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