@ecomplus/storefront-components
Version:
Vue components for E-Com Plus Storefront
488 lines (458 loc) • 15.3 kB
HTML
<section
class="product"
:data-product-id="body._id"
:data-sku="body.sku"
:data-selected-variation="selectedVariationId"
>
<a-alert
:can-show="hasLoadError"
variant="danger"
>
{{ i19loadProductErrorMsg }}
<a
href="#"
class="alert-link"
@click.prevent="fetchProduct"
>
{{ i19retry }}
</a>
</a-alert>
<transition enter-active-class="animated fadeIn slower">
<div
v-if="body._id"
class="row"
>
<slot name="gallery-col">
<div :class="galleryColClassName">
<component
:is="isSSR ? 'portal' : 'div'"
selector="#product-gallery"
>
<slot name="stamps"/>
<product-gallery
:product="body"
:can-add-to-cart="canAddToCart && body.available && isInStock"
:current-slide.sync="currentGalleyImg"
:is-s-s-r="isSSR"
>
<slot name="first-picture"/>
</product-gallery>
<slot name="gallery-footer"/>
</component>
</div>
</slot>
<div
class="col"
ref="actions"
>
<slot
v-if="!isSSR"
name="heading"
>
<component
:is="headingTag"
class="product__name"
>
{{ name }}
</component>
<p class="product__sku">
COD: {{ body.sku }}
</p>
</slot>
<component
:is="isSSR ? 'portal' : 'div'"
selector="#product-actions"
>
<slot name="rating">
<div
v-once
class="product__rating"
:data-sku="body.sku"
></div>
</slot>
<div
v-if="!body.available"
class="product__unavailable"
>
<slot name="unavailable">
{{ i19unavailable }}
</slot>
</div>
<div
v-else-if="!isInStock"
class="product__out-of-stock"
>
<slot name="out-of-stock">
{{ i19outOfStock }}
</slot>
</div>
<div
v-else-if="isWithoutPrice"
class="product__without-price"
>
<slot name="without-price">
<a
v-if="quoteLink"
target="_blank"
rel="noopener"
:href="quoteLink"
>
{{ i19quoteProduct }}
</a>
</slot>
</div>
<template v-else>
<slot name="prices">
<p class="product__prices">
<a-prices
:product="ghostProductForPrices"
:is-literal="true"
:is-big="true"
@fix-price="price => fixedPrice = price"
/>
<slot
name="discount-tag"
v-bind="{ discount }"
>
<span
v-if="discount > 0"
class="product__discount"
>
{{ i19discountOf }}
<strong>{{ discount }}%</strong>
</span>
</slot>
</p>
</slot>
<slot
name="variations"
v-if="hasVariations"
>
<product-variations
:product="body"
:selected-id.sync="selectedVariationId"
:max-options-btns="maxVariationOptionsBtns"
@select-option="handleGridOption"
/>
<a-alert :can-show="hasClickedBuy && !selectedVariationId">
{{ i19selectVariationMsg }}
</a-alert>
<slot name="variations-info"/>
</slot>
<slot
name="customizations"
v-if="body.customizations"
>
<div
v-for="custom in body.customizations"
v-if="custom.custom_value"
:key="custom._id"
class="product__customization form-group"
:class="custom.grid_id ? `product__customization--${custom.grid_id}` : null"
>
<label :for="`c-${custom._id}`">
{{ custom.label }}
<span
v-if="custom.add_to_price"
class="badge badge-secondary"
>
{{ formatAdditionalPrice(custom.add_to_price) }}
</span>
</label>
<input
type="text"
class="form-control"
:id="`c-${custom._id}`"
@keyup="ev => setCustomizationOption(custom, ev.target.value)"
>
</div>
</slot>
<div
v-if="isKit"
class="product__kit"
>
<slot
name="kit"
v-bind="{ kitItems }"
>
<transition enter-active-class="animated fadeInUp">
<quantity-selector
v-if="kitItems.length && !isKitWithVariations"
:items="kitItems"
:min="body.min_quantity"
:max="body.quantity"
:slug="body.slug"
:kit-product-id="body._id"
:kit-name="name"
:kit-price="fixedPrice"
@buy="d => $emit('buy', d)"
>
<template #buy-button-content>
<slot name="buy-button-content"/>
</template>
</quantity-selector>
<kit-product-variations
v-if="kitItems.length && isKitWithVariations"
:items="kitItems"
:min="body.min_quantity"
:max="body.quantity"
:slug="body.slug"
:kit-product-id="body._id"
:kit-name="name"
:kit-price="fixedPrice"
:max-options-btns="maxVariationOptionsBtns"
>
<template #buy-button-content>
<slot name="buy-button-content"/>
</template>
</kit-product-variations>
</transition>
<span
v-if="!kitItems.length"
class="product__kit-loading spinner-border"
role="status"
>
<span class="sr-only">Loading...</span>
</span>
</slot>
</div>
<template v-else>
<div
v-if="!isVariationInStock"
class="product__out-of-stock"
>
<slot name="out-of-stock">
{{ i19outOfStock }}
</slot>
</div>
<div
v-else-if="hasBuyButton"
class="product__buy"
ref="buy"
>
<component
:is="hasQuantitySelector ? 'quantity-selector' : 'div'"
:items="hasQuantitySelector ? [{ _id: body._id, quantity: body.min_quantity || 1 }] : null"
:min="body.min_quantity"
:max="body.quantity"
:has-buy-button="false"
@set-quantity="({ quantity }) => qntToBuy = quantity"
>
<span @click="buy">
<slot
name="buy"
v-bind="{ hasClickedBuy, isOnCart }"
>
<button
type="button"
class="btn btn-lg btn-primary"
:disabled="hasClickedBuy && !isOnCart"
>
<slot name="buy-button-content">
<i class="i-shopping-bag mr-1"></i>
{{ strBuy }}
</slot>
</button>
</slot>
</span>
</component>
</div>
<p
class="product__short-stock"
v-if="isLowQuantity"
>
<i class="i-forward mr-1"></i>
{{ i19only }}
<strong>{{ productQuantity }}</strong>
{{ i19unitsInStock }}
</p>
</template>
<slot name="sale-timer">
<div
v-if="isOnSale"
class="product__sale-timer mb-3"
>
<div>
{{ i19offer }}
<br><small>{{ i19endsIn }}</small>
</div>
<div
class="h1 ml-3 mb-0"
ref="timer"
>
00:00:00
</div>
</div>
</slot>
<slot name="favorite">
<div>
<a
class="btn btn-sm product__favorite"
@click="toggleFavorite"
:href="isLogged ? null : accountUrl"
>
<i
class="i-heart mr-1"
:class="isFavorite ? 'active' : null"
>
</i>
<span>
{{ isFavorite ? i19removeFromFavorites : i19addToFavorites }}
</span>
</a>
</div>
</slot>
<slot name="share">
<a-share
v-if="body.slug"
class="mb-3"
:url="`/${body.slug}`"
:title="body.name"
:description="body.short_description"
/>
</slot>
<transition enter-active-class="animated fadeInUp">
<slot
name="payment-gateways"
v-bind="{ paymentOptions }"
v-if="!isQuickview && paymentOptions.length"
>
<article>
<div class="product__payment card mb-3">
<a
id="product-payment-header"
class="card-header"
role="button"
href="#product-payment"
data-toggle="collapse"
aria-expanded="false"
aria-controls="product-payment"
>
<span>{{ i19paymentOptions }}</span>
<i class="i-chevron-down"></i>
</a>
<div
id="product-payment"
class="collapse"
aria-labelledby="product-payment-header"
>
<div class="card-body pb-0">
<div
v-for="paymentOption in paymentOptions"
:key="paymentOption.app_id"
:id="`product-payment-${paymentOption.app_id}`"
class="mb-3"
>
<slot :name="`payment-${paymentOption.app_id}`">
<payment-option
v-for="(gateway, i) in paymentOption.payment_gateways"
:key="`${paymentOption.app_id}-${i}`"
:payment-gateway="gateway"
:installments-option="paymentOption.installments_option"
:price="fixedPrice"
/>
</slot>
</div>
</div>
</div>
</div>
</article>
</slot>
</transition>
<p
v-if="body.production_time && body.production_time.days"
class="product__production"
>
<i class="i-info-circle mr-1"></i>
{{ i19productionDeadline }}:
<strong>
{{ body.production_time.days }}
{{ body.production_time.working_days ? i19workingDays : i19days }}
<template v-if="body.production_time.cumulative">
{{ i19perUnit }}
</template>
</strong>
</p>
<slot
v-if="!isQuickview && (!isKit || kitItems.length)"
name="shipping"
>
<shipping-calculator
:shippedItems="isKit ? kitItems : [{
...body,
...selectedVariation,
product_id: body._id,
quantity: qntToBuy || body.min_quantity || 1
}]"
>
<template v-slot:free-from-value="{ amountSubtotal, freeFromValue }">
<div class="product__free-shipping-from">
{{ i19freeShippingFrom }}
<strong>
{{ Math.ceil(freeFromValue / amountSubtotal) }}
{{ i19units }}
</strong>
</div>
</template>
</shipping-calculator>
</slot>
<slot name="track-price">
<div
v-once
class="product__track-price"
:data-sku="body.sku"
></div>
</slot>
</template>
</component>
<slot
v-if="!isSSR && body.short_description"
name="short-description"
>
<p class="product__info lead">
{{ body.short_description }}
</p>
</slot>
</div>
</div>
</transition>
<template v-if="!isQuickview && hasStickyBuyButton && body.available && isInStock">
<transition
enter-active-class="animated fadeIn"
leave-active-class="animated fadeOut fast"
>
<div
v-show="isStickyBuyVisible"
ref="sticky"
class="product__sticky"
>
<div class="product__sticky-container container">
<div class="product__sticky-info">
<a-picture
:can-calc-height="false"
:src="thumbnail"
class="product__sticky-picture"
/>
<h5>{{ name }}</h5>
</div>
<div class="product__sticky-buy">
<a-prices
:product="ghostProductForPrices"
:is-literal="false"
:can-show-price-options="true"
/>
<a
class="btn btn-lg btn-primary"
href="#"
@click.prevent="buyOrScroll"
>
<i class="i-shopping-bag mr-1"></i>
{{ strBuy }}
</a>
</div>
</div>
</div>
</transition>
</template>
<slot v-if="!body._id"/>
</section>