init git
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<div class="buy">
|
||||
<p>
|
||||
<span>{{ $t('priceForYou') }}</span>
|
||||
<small v-if="boxSeller">
|
||||
{{ $t('everyBox') }}
|
||||
</small>
|
||||
</p>
|
||||
<div>
|
||||
<span v-if="specialPrice">
|
||||
{{ numberFormat(specialPrice) }}
|
||||
<small>{{ $t('priceUnit') }}</small>
|
||||
</span>
|
||||
<span>
|
||||
{{ numberFormat(price) }}
|
||||
<small v-if="!specialPrice">
|
||||
{{ $t('priceUnit') }}
|
||||
</small>
|
||||
</span>
|
||||
</div>
|
||||
<CartQuantity v-if="cart.find(id)" v-model="quantity" :range="orderRange" />
|
||||
<Button v-else :label="$t('addToCart')" :loading="cart.loading" @click="quantity = orderRange.min" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
const { id, orderRange, boxSeller, specialPrice, price } = inject('data')
|
||||
|
||||
const cart = useCartStore()
|
||||
const quantity = computed({
|
||||
get: () => cart.find(id)?.quantity || 0,
|
||||
set: (v) => cart.update(id, v)
|
||||
})
|
||||
|
||||
const numberFormat = (number) =>
|
||||
new Intl.NumberFormat('fa-IR', { maximumSignificantDigits: 3 }).format(number)
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.product .buy {
|
||||
@apply flex flex-wrap justify-between items-center px-6 pt-1 pb-2.5 lg:gap-6 shadow-[0px_-3px_5px_#00000005];
|
||||
@apply fixed inset-x-0 bottom-0 h-[6.2rem] bg-white shadow border-t border-[#F1F2F4] lg:h-auto;
|
||||
@apply z-10 lg:z-0 lg:static lg:bg-transparent lg:shadow-none lg:border-t-0 lg:p-0 lg:gap-y-6 lg:mt-auto;
|
||||
|
||||
p {
|
||||
@apply w-full lg:w-max text-[#7F7F7F] font-medium font-vazir pr-1.5 lg:p-0;
|
||||
|
||||
span {
|
||||
@apply text-xs lg:text-xl leading-[2.1rem];
|
||||
}
|
||||
|
||||
small {
|
||||
@apply text-[0.7em] lg:text-base leading-[1.9rem];
|
||||
}
|
||||
}
|
||||
|
||||
&>div:nth-of-type(1) {
|
||||
@apply flex flex-col justify-end gap-2 max-lg:order-last;
|
||||
|
||||
|
||||
span {
|
||||
@apply text-left text-[#333] text-sm font-bold lg:text-[1.6em] font-vazir;
|
||||
}
|
||||
|
||||
small {
|
||||
@apply text-xs font-medium lg:hidden;
|
||||
}
|
||||
|
||||
&:has(span:nth-child(2)) {
|
||||
span:first-child {
|
||||
@apply max-lg:-mt-8;
|
||||
}
|
||||
|
||||
span:last-child {
|
||||
@apply line-through text-xs lg:text-base text-gray-600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&>button {
|
||||
@apply w-[10.8rem] h-[2.6rem] lg:w-[25.1rem] lg:h-14 #{!important};
|
||||
|
||||
.p-button-label {
|
||||
@apply text-xs lg:text-xl font-bold font-iran-sans;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,169 @@
|
||||
<template>
|
||||
<router-link :to="`/product/${uid}`">
|
||||
<article :class="['product-card', `product-card-${type}`]">
|
||||
<img :src="coverPath" :alt="title" />
|
||||
<div>
|
||||
<h2 v-if="type != 'minimal'">
|
||||
{{ category[category.length - 1]?.name }}
|
||||
</h2>
|
||||
<h1>{{ title }}</h1>
|
||||
<ul v-if="type != 'minimal' && stars">
|
||||
<li v-for="i in 5" :key="i">
|
||||
<img :src="`/icons/star.svg`" :class="starsClass(i)" />
|
||||
</li>
|
||||
</ul>
|
||||
<template v-if="type != 'minimal'">
|
||||
<Button :icon="`isax isax-heart${like ? '5' : ''}`" severity="secondary" text
|
||||
@click.prevent="favorite.update(id)" />
|
||||
</template>
|
||||
<div v-if="type != 'minimal'">
|
||||
<div>
|
||||
<span>
|
||||
<b>{{ numberFormat(price) }}</b>
|
||||
<small>{{ $t('priceUnit') }}</small>
|
||||
</span>
|
||||
<s v-if="type == 'action' && specialPrice">
|
||||
{{ numberFormat(specialPrice) }}
|
||||
</s>
|
||||
</div>
|
||||
<ProductQuantity v-if="type == 'action'" v-model="quantity" :range="orderRange" />
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
data: { type: Object, default: {} },
|
||||
type: { type: String, default: 'main' }
|
||||
})
|
||||
const { _id: id, uid, coverPath, category = [], title, price, specialPrice, stars, orderRange } = props.data
|
||||
|
||||
const favorite = useFavoritesStore()
|
||||
const like = computed(() => favorite.find(id))
|
||||
|
||||
const cart = useCartStore()
|
||||
const quantity = computed({
|
||||
get: () => cart.find(id)?.quantity || 0,
|
||||
set: (v) => cart.update(id, v)
|
||||
})
|
||||
|
||||
const starsClass = (i) => (i > stars ? 'opacity-25' : '')
|
||||
const numberFormat = (number) =>
|
||||
new Intl.NumberFormat('fa-IR', { maximumSignificantDigits: 3 }).format(number)
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.product-card {
|
||||
box-shadow: 0px 2px 10px 0px #87C29A;
|
||||
@apply bg-white rounded-[0.6rem] border flex flex-col relative;
|
||||
|
||||
&>img {
|
||||
@apply w-[3.8rem] h-[3.8rem] lg:w-[10.3rem] lg:h-[10.3rem] self-center object-contain object-bottom;
|
||||
}
|
||||
|
||||
&>div {
|
||||
@apply flex flex-col grow;
|
||||
|
||||
h2 {
|
||||
@apply text-[#7F7F7F] text-[0.6em] leading-5 lg:text-sm font-medium font-vazir;
|
||||
}
|
||||
|
||||
h1 {
|
||||
@apply text-[#333] text-[0.7em] leading-5 lg:text-base font-bold font-vazir mt-1 line-clamp-1 lg:line-clamp-2;
|
||||
}
|
||||
|
||||
ul {
|
||||
@apply hidden lg:flex gap-1 mt-3 ;
|
||||
|
||||
img {
|
||||
@apply w-4 h-4;
|
||||
}
|
||||
}
|
||||
|
||||
&>div {
|
||||
@apply flex justify-between items-center gap-4 mt-auto;
|
||||
|
||||
&>div:nth-of-type(1) {
|
||||
@apply flex flex-col items-end font-vazir;
|
||||
|
||||
span {
|
||||
@apply text-[#333];
|
||||
|
||||
small {
|
||||
@apply text-[0.6em] lg:text-[0.8em] font-medium mr-1;
|
||||
}
|
||||
|
||||
b {
|
||||
@apply text-sm lg:text-lg;
|
||||
}
|
||||
}
|
||||
|
||||
s {
|
||||
@apply mt-1 text-[#7F7F7F] text-[0.7em] lg:text-[0.9em] font-medium;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&>button {
|
||||
@apply absolute rounded-full w-12 h-12 #{!important};
|
||||
|
||||
.p-button-icon {
|
||||
@apply text-base lg:text-2xl text-[#333];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.product-card-main {
|
||||
@apply w-[9.3rem] h-[11.4rem] p-3 lg:w-[18.25rem] lg:h-[25.4rem] lg:p-6 lg:pb-8;
|
||||
|
||||
&>div {
|
||||
h2 {
|
||||
@apply mt-6;
|
||||
}
|
||||
|
||||
&>button {
|
||||
@apply absolute -bottom-1 -left-1 lg:bottom-[1.4rem] lg:left-3.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.product-card-action {
|
||||
@apply w-[12.6rem] h-[16.5rem] lg:w-[18.3rem] lg:h-[29rem] p-3 lg:p-5;
|
||||
|
||||
&>img {
|
||||
@apply max-lg:mt-1.5 max-lg:w-[5.7rem] max-lg:h-[5.7rem];
|
||||
}
|
||||
|
||||
&>div {
|
||||
h2 {
|
||||
@apply mt-9;
|
||||
}
|
||||
|
||||
&>div {
|
||||
@apply flex-row-reverse;
|
||||
}
|
||||
|
||||
&>button {
|
||||
@apply absolute top-1 left-1 lg:top-2 lg:left-2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.product-card-minimal {
|
||||
@apply w-[10.1rem] h-[11.4rem] px-2.5 gap-[1.9rem] items-center;
|
||||
@apply lg:w-[16.5rem] lg:h-[16.5rem] py-6 lg:p-4 lg:gap-6;
|
||||
|
||||
&>div {
|
||||
h1 {
|
||||
@apply text-center text-[0.7em] lg:text-base leading-4;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
@apply w-[4.7rem] h-20 lg:w-[8.4rem] lg:h-[8.9rem];
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<section class="description">
|
||||
<h2>{{ $t('description') }}</h2>
|
||||
<div>
|
||||
<div v-html="description" />
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const { description } = inject('data')
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.product .description {
|
||||
@apply w-full flex items-start gap-[13.8rem] px-6;
|
||||
|
||||
& > h2 {
|
||||
@apply hidden lg:flex gap-3 items-center text-primary-600 text-xl font-bold font-vazir;
|
||||
|
||||
&::before {
|
||||
@apply content-['_'] h-6 w-[0.3rem] rounded-full bg-primary-600;
|
||||
}
|
||||
}
|
||||
|
||||
& > div {
|
||||
@apply w-full max-h-[17.1rem] lg:w-[57.8rem] lg:max-h-[36.1rem] overflow-y-auto lg:pt-6;
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
@apply bg-transparent;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
@apply w-1;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
@apply bg-[#E5E5E5];
|
||||
}
|
||||
|
||||
div * {
|
||||
@apply font-vazir;
|
||||
all: initial;
|
||||
|
||||
// @apply text-[#595959] text-xs lg:text-xl font-normal font-vazir lg:leading-9 pl-4 lg:pl-8;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<div class="gallery">
|
||||
<div>
|
||||
<ul>
|
||||
<li v-for="(act, key) in actions" :key="key">
|
||||
<Button v-bind="act.props" @click="act.click()" />
|
||||
</li>
|
||||
</ul>
|
||||
<img :src="coverImage" />
|
||||
</div>
|
||||
<ul>
|
||||
<li v-for="(image, i) in images" :key="i">
|
||||
<i class="isax isax-gallery-slash" />
|
||||
<Image :src="image" preview />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const { actions } = inject('init')
|
||||
const { coverImage, images } = inject('data')
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.product .gallery {
|
||||
@apply w-full h-full max-h-[38.4rem] flex flex-col gap-6 ;
|
||||
|
||||
&>div {
|
||||
@apply flex p-6 w-full grow max-h-[29.9rem] border border-[#E5E5E5] overflow-hidden;
|
||||
|
||||
&>ul {
|
||||
@apply flex flex-col gap-0 -m-3 relative;
|
||||
|
||||
li {
|
||||
@apply ltr;
|
||||
|
||||
i {
|
||||
@apply absolute inset-0;
|
||||
}
|
||||
|
||||
button {
|
||||
@apply w-12 h-12 #{!important};
|
||||
|
||||
span {
|
||||
@apply text-2xl text-[#333333];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&>img {
|
||||
@apply flex w-full h-full max-w-[24.1rem] max-h-[24.1rem] object-contain;
|
||||
@apply shrink mt-5 mx-auto 2xl:translate-x-3 px-8 object-top;
|
||||
}
|
||||
}
|
||||
|
||||
&>ul {
|
||||
@apply flex gap-3 flex-wrap h-28 overflow-hidden;
|
||||
|
||||
li {
|
||||
@apply w-28 h-28 flex items-center justify-center relative bg-[#F2F2F2] cursor-pointer;
|
||||
|
||||
span {
|
||||
@apply w-full h-full absolute;
|
||||
|
||||
img {
|
||||
@apply w-full h-full object-cover;
|
||||
}
|
||||
}
|
||||
|
||||
i {
|
||||
@apply text-2xl absolute;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<div class="product-card-quantity">
|
||||
<p v-if="count < 0">{{ $t('unavailable') }}</p>
|
||||
<template v-else-if="count == 0">
|
||||
<Button :label="$t('buy')" icon="isax isax-bag-2" iconPos="right" text @click.prevent="count++" />
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<Button text icon="isax isax-add" @click.prevent="count++" :disabled="count >= range.max" />
|
||||
<p>{{ count }}</p>
|
||||
<Button text icon="isax isax-minus" @click.prevent="count--" :disabled="count <= range.min" />
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps({
|
||||
range: { type: Object, default: { min: 1, max: 1000 } }
|
||||
})
|
||||
const count = defineModel()
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.product-card-quantity {
|
||||
@apply flex items-center justify-between w-[4.8rem] h-[1.9rem] lg:w-[5.8rem] lg:h-10 rounded-[1.8rem] border border-primary-600 overflow-hidden;
|
||||
|
||||
p {
|
||||
@apply flex w-max items-center text-primary-600 text-[0.7em] lg:text-[1.1em] font-medium font-vazir mx-auto;
|
||||
}
|
||||
|
||||
&:not(:has(button)) {
|
||||
@apply border-[#7F7F7F];
|
||||
|
||||
p {
|
||||
@apply text-[#7F7F7F];
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
@apply rounded-full font-normal font-iran-sans text-[0.7em] lg:text-[1.1em] #{!important};
|
||||
|
||||
.isax {
|
||||
@apply text-xl;
|
||||
}
|
||||
}
|
||||
|
||||
button:not(:has(.isax-bag-2)) {
|
||||
@apply w-7 h-7 first:mr-1 last:ml-1 #{!important};
|
||||
}
|
||||
|
||||
button:has(.isax-bag-2) {
|
||||
@apply w-full h-full #{!important};
|
||||
}
|
||||
|
||||
button[disabled]{
|
||||
@apply opacity-20;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,204 @@
|
||||
<template>
|
||||
<div class="seller">
|
||||
<div>
|
||||
<h2>{{ $t('seller') }}</h2>
|
||||
<template v-if="device == 'mobile'">
|
||||
<Button :label="$t('otherSellersCount', { count: 3 })" text @click="handleClick" />
|
||||
</template>
|
||||
</div>
|
||||
<ul>
|
||||
<li v-for="(item, i) in items.slice(0, 3)" :key="i">
|
||||
<i :class="['isax', `isax-${item.icon}`]" />
|
||||
<span>{{ item.title }}</span>
|
||||
</li>
|
||||
<li v-show="shopID?.rate >= 0">
|
||||
<small>{{ shopID.rate * 10 }}%</small>
|
||||
<span>{{ $t('satisfaction') }}</span>
|
||||
<span>{{ $t('performance') }} {{ shopID?.performance }}</span>
|
||||
</li>
|
||||
<li v-show="boxSeller">
|
||||
<i class="isax isax-d-square"></i>
|
||||
<span>{{ $t('boxShopping') }}</span>
|
||||
<span>{{ $t('minBuyBox', { minBuy }) }}</span>
|
||||
</li>
|
||||
<li v-show="boxSeller">
|
||||
<div><i /></div>
|
||||
<span>{{ $t('itemInBox', { itemInBox }) }}</span>
|
||||
</li>
|
||||
<li>
|
||||
<div>
|
||||
<template v-for="({ primary }, i) in items.slice(3)" :key="i">
|
||||
<i :class="primary ? 'bg-primary-600' : 'bg-neutral-200'" />
|
||||
</template>
|
||||
</div>
|
||||
<ul>
|
||||
<li v-for="(item, i) in items.slice(3)" :key="i">
|
||||
<i :class="`isax isax-${item.icon}`" />
|
||||
<span>{{ item.title }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<ProductBuy />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const { dialog, device, t } = inject('service')
|
||||
const { shopID, warranty, boxSeller, localSend, minBuy, itemInBox } = inject('data')
|
||||
|
||||
const items = reactive([
|
||||
{ title: shopID.shopName, icon: 'shop' },
|
||||
{ title: warranty, icon: 'shield-tick' },
|
||||
{ title: t('availableSellerWarehouse'), icon: 'box-tick' },
|
||||
{ title: t('sendFromLocal'), icon: 'box', primary: localSend },
|
||||
{ title: t('sendFromSeller'), icon: 'profile-circle', primary: !localSend }
|
||||
])
|
||||
|
||||
const handleClick = () => {
|
||||
dialog.show(resolveComponent('ProductOtherSellers'))
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.product .seller {
|
||||
@apply w-full lg:w-[28.1rem] lg:h-[42rem] bg-[#FAFAFA] lg:rounded-[0.6rem] lg:border border-neutral-200;
|
||||
@apply flex flex-col gap-6 lg:gap-7 px-6 py-5 lg:py-6 max-lg:order-4;
|
||||
|
||||
&>div:not([class]) {
|
||||
@apply flex justify-between items-center;
|
||||
|
||||
h2 {
|
||||
@apply text-[#191919] text-base lg:text-2xl font-bold font-vazir;
|
||||
}
|
||||
|
||||
button {
|
||||
@apply rtl p-0 h-max #{!important};
|
||||
|
||||
.p-button-label {
|
||||
@apply text-[0.7em] text-primary-600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&>ul {
|
||||
@apply flex flex-col gap-[1.1rem] lg:gap-4;
|
||||
|
||||
&>li {
|
||||
@apply flex items-center;
|
||||
|
||||
&:nth-of-type(-n + 3) {
|
||||
@apply gap-3 lg:gap-4;
|
||||
|
||||
i {
|
||||
@apply text-[1.4em] lg:text-2xl;
|
||||
}
|
||||
|
||||
span {
|
||||
@apply text-[#333] text-sm lg:text-xl font-medium font-vazir;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-of-type(2) {
|
||||
@apply order-5 py-[1.2rem] lg:py-4 border-y border-[#E5E5E5];
|
||||
}
|
||||
|
||||
&:nth-of-type(3) {
|
||||
@apply order-6;
|
||||
}
|
||||
|
||||
&:nth-of-type(4) {
|
||||
@apply gap-1 lg:pr-10 font-vazir order-2 pb-[1.1rem] lg:pb-4 border-b border-[#E5E5E5];
|
||||
|
||||
small {
|
||||
@apply text-[#019907] text-xs lg:text-base;
|
||||
}
|
||||
|
||||
span {
|
||||
@apply text-[#999999] text-xs lg:text-base;
|
||||
|
||||
&:first-of-type {
|
||||
@apply pl-2.5 relative;
|
||||
|
||||
&::after {
|
||||
@apply content-['_'] w-px bg-[#D9D9D9] h-4 absolute left-0.5 top-1/2 -translate-y-1/2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-of-type(5) {
|
||||
@apply gap-4 font-vazir order-3;
|
||||
|
||||
i {
|
||||
@apply text-[#019907] text-[1.4em] lg:text-2xl;
|
||||
}
|
||||
|
||||
span {
|
||||
@apply text-[#019907] text-xs lg:text-lg font-medium;
|
||||
|
||||
&:first-of-type {
|
||||
@apply pl-2.5 -ml-2 relative;
|
||||
|
||||
&::after {
|
||||
@apply content-['_'] w-px bg-[#D9D9D9] h-4 absolute left-0 top-1/2 -translate-y-1/2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-of-type(6) {
|
||||
@apply gap-4 font-vazir order-4;
|
||||
|
||||
div {
|
||||
@apply w-2 lg:w-2.5 h-5 lg:h-6 mx-[0.4rem] -mt-2.5 lg:-mt-3.5 relative flex justify-center;
|
||||
|
||||
i {
|
||||
@apply w-2 h-2 lg:w-2.5 lg:h-2.5 rounded-full mt-auto bg-[#019907] z-0;
|
||||
}
|
||||
|
||||
&::before {
|
||||
@apply content-['_'] absolute w-0.5 inset-y-0 bg-neutral-200;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
@apply text-[#019907] text-xs lg:text-lg font-medium;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-of-type(7) {
|
||||
@apply gap-6 lg:gap-4 order-7 pb-4 lg:border-b border-[#E5E5E5];
|
||||
|
||||
div {
|
||||
@apply w-2.5 h-[4.3rem] lg:mx-[0.4rem] pt-4 lg:pt-3.5 -mt-2 lg:-mt-3.5 flex flex-col items-center justify-between relative;
|
||||
|
||||
i {
|
||||
@apply w-2.5 h-2.5 rounded-full z-0;
|
||||
}
|
||||
|
||||
&::before {
|
||||
@apply content-['_'] absolute w-0.5 inset-y-0 bg-neutral-200;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
@apply flex flex-col mt-2 gap-6 lg:gap-5 font-vazir;
|
||||
|
||||
li {
|
||||
@apply flex gap-2 items-center h-4 lg:h-[1.6rem];
|
||||
|
||||
i {
|
||||
@apply text-xl text-primary-600;
|
||||
}
|
||||
|
||||
span {
|
||||
@apply text-[#999] text-[0.7em] lg:text-base font-normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<section v-if="similars.length > 0" class="similars">
|
||||
<h2>{{ $t('productSimilars') }}</h2>
|
||||
<div>
|
||||
<template v-if="device == 'desktop'">
|
||||
<Button v-for="(btn, i) in arrows" :key="i" v-bind="btn" />
|
||||
</template>
|
||||
<Swiper v-bind="swiper.similars">
|
||||
<SwiperSlide v-for="(item, i) in similars" :key="i">
|
||||
<ProductCard :data="item" type="action" />
|
||||
</SwiperSlide>
|
||||
</Swiper>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const { swiper, arrows } = inject('init')
|
||||
const { device } = inject('service')
|
||||
const { similars } = inject('data')
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.product .similars {
|
||||
@apply w-full flex flex-col items-center overflow-hidden;
|
||||
|
||||
&>h2 {
|
||||
@apply text-[#0B1F11] text-lg lg:text-3xl font-bold font-vazir;
|
||||
}
|
||||
|
||||
&>div {
|
||||
@apply max-w-full w-full flex gap-10 h-max mt-6 lg:my-16 relative;
|
||||
|
||||
.swiper {
|
||||
@apply w-full container h-max py-4 px-6;
|
||||
|
||||
.swiper-wrapper {
|
||||
@apply w-max h-max;
|
||||
|
||||
.swiper-slide {
|
||||
@apply w-max ml-[0.9rem] last:ml-0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&>button {
|
||||
@apply w-10 h-10 self-center absolute text-xl p-0 bg-[#B2B2B2] border-none #{!important};
|
||||
@apply first-of-type:-right-16 last-of-type:-left-16 max-lg:hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<section class="specification">
|
||||
<h2>{{ $t('specifications') }}</h2>
|
||||
<ul>
|
||||
<li v-for="(item, key) in specs" :key="key">
|
||||
<label>{{ key }}</label>
|
||||
<span>{{ item }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const { specs } = inject('data')
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.product .specification {
|
||||
@apply w-full flex items-start gap-[13.6rem] px-6;
|
||||
|
||||
h2 {
|
||||
@apply hidden lg:flex gap-3 items-center text-primary-600 text-xl font-bold font-vazir;
|
||||
|
||||
&::before {
|
||||
@apply content-['_'] h-6 w-[0.3rem] rounded-full bg-primary-600;
|
||||
}
|
||||
}
|
||||
|
||||
& > ul {
|
||||
@apply w-full lg:w-max flex flex-col;
|
||||
|
||||
li {
|
||||
@apply flex justify-between lg:justify-start gap-12 border-b-2 py-[1.1rem] lg:py-6;
|
||||
|
||||
label {
|
||||
@apply lg:w-[20.2rem] flex items-center gap-3 text-[#7F7F7F] text-xs lg:text-lg font-medium font-vazir;
|
||||
|
||||
&::before {
|
||||
@apply content-['_'] w-2 h-2 bg-[#7F7F7F] rounded-full;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
@apply text-[#333333] text-xs lg:text-lg font-vazir;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<div class="summary">
|
||||
<h1>{{ title }}</h1>
|
||||
<h2>
|
||||
<span>{{ titleEN }}</span>
|
||||
<hr />
|
||||
</h2>
|
||||
<div>
|
||||
<i class="isax isax-star-15" />
|
||||
<span>{{ stars }}</span>
|
||||
<a href="#comments">
|
||||
{{ comments.length }} {{ $t('viewpoint') }}
|
||||
</a>
|
||||
</div>
|
||||
<h3>{{ $t('keyFeatures') }}</h3>
|
||||
<ul>
|
||||
<li v-for="(value, key) in shortDesc" :key="key">
|
||||
<label>{{ key }}:</label>
|
||||
<span>{{ value }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const { title, titleEN, stars, comments, shortDesc } = inject('data')
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.product>.wrapper>.summary {
|
||||
@apply w-full lg:w-auto lg:min-w-[17.5rem] grow px-6 lg:px-0 lg:max-w-[47.9rem] lg:h-[29.9rem] flex flex-col mt-9 lg:mt-0 lg:mr-6 max-lg:order-2;
|
||||
|
||||
h1 {
|
||||
@apply text-[#191919] font-bold font-vazir text-base lg:text-2xl;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@apply flex items-center gap-3 mt-3 lg:mt-4 text-sm lg:text-base;
|
||||
|
||||
span {
|
||||
@apply text-[#B2B2B2] font-vazir;
|
||||
}
|
||||
|
||||
hr {
|
||||
@apply hidden lg:block h-px lg:h-0.5 grow bg-[#E5E5E5];
|
||||
}
|
||||
}
|
||||
|
||||
&>div {
|
||||
@apply flex items-center gap-1 font-vazir mt-1.5 lg:mt-2.5;
|
||||
|
||||
i {
|
||||
@apply text-xl text-[#FFB628];
|
||||
}
|
||||
|
||||
span {
|
||||
@apply flex items-center gap-4 ml-4 text-[#191919] text-base lg:text-xl;
|
||||
|
||||
&::after {
|
||||
@apply content-['_'] w-1.5 h-1.5 bg-neutral-200 rounded-full;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
@apply text-primary-500 text-base lg:text-xl;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
@apply text-zinc-900 font-bold font-vazir mt-12 text-base lg:text-2xl;
|
||||
}
|
||||
|
||||
ul {
|
||||
@apply flex flex-col gap-[1.1rem] lg:gap-7 mt-6 lg:mt-8;
|
||||
|
||||
li {
|
||||
@apply flex items-center gap-3 font-vazir;
|
||||
|
||||
label {
|
||||
@apply flex items-center gap-3 text-[#7F7F7F] text-xs lg:text-lg font-medium;
|
||||
|
||||
&::before {
|
||||
@apply content-['_'] w-2 h-2 bg-[#7F7F7F] rounded-full;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
@apply text-[#333] text-xs lg:text-lg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<section class="wrapper">
|
||||
<Swiper v-if="device == 'mobile'" v-bind="swiper.gallery">
|
||||
<SwiperSlide v-for="(image, i) in product.images" :key="i">
|
||||
<img :src="image" />
|
||||
</SwiperSlide>
|
||||
</Swiper>
|
||||
<div>
|
||||
<Button v-bind="btns.report.props" @click="handleClick()" />
|
||||
<ProductGallery v-if="device == 'desktop'" />
|
||||
</div>
|
||||
<ProductSummary />
|
||||
<ProductSeller />
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const { swiper, btns } = inject('init')
|
||||
const { device, dialog } = inject('service')
|
||||
const product = inject('data')
|
||||
|
||||
const handleClick = () => {
|
||||
dialog.show(resolveComponent('ProductDialogReport'), product)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.product>.wrapper {
|
||||
@apply w-full flex max-lg:flex-col gap-3 lg:gap-[1.6rem];
|
||||
|
||||
.swiper {
|
||||
@apply w-screen h-max pb-4 max-lg:order-1;
|
||||
|
||||
.swiper-wrapper {
|
||||
@apply w-max z-0;
|
||||
|
||||
.swiper-slide {
|
||||
@apply h-max w-screen;
|
||||
|
||||
img {
|
||||
@apply w-[11.8rem] h-[12.5rem] lg:w-[15.6rem] lg:h-[15.6rem] mx-auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.swiper-pagination {
|
||||
@apply pl-5 left-3 translate-x-0 lg:hidden;
|
||||
|
||||
.swiper-pagination-bullet {
|
||||
@apply bg-[#D9D9D9] opacity-100;
|
||||
}
|
||||
|
||||
* {
|
||||
@apply mx-0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&>div:not([class]) {
|
||||
@apply flex max-lg:flex-col max-lg:order-3 gap-y-3 max-w-[38rem] grow;
|
||||
|
||||
&>button {
|
||||
@apply w-max h-10 lg:h-14 shrink-0 max-lg:flex-row-reverse self-end #{!important};
|
||||
@apply lg:-ml-[14.2rem] lg:-mb-2;
|
||||
|
||||
.p-button-icon {
|
||||
@apply text-[1.1em] lg:text-2xl max-lg:ml-0 max-lg:mr-2 #{!important};
|
||||
}
|
||||
|
||||
.p-button-label {
|
||||
@apply text-[#7F7F7F] font-vazir font-medium text-[0.7em] lg:text-base;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div class="product-comment-item">
|
||||
<p>{{ content }}</p>
|
||||
<div>
|
||||
<span>
|
||||
{{
|
||||
new Date(createdAt).toLocaleDateString('fa', {
|
||||
month: 'long', year: 'numeric', day: 'numeric'
|
||||
})
|
||||
}}
|
||||
</span>
|
||||
<span>{{ userID?.firstName }} {{ userID?.lastName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps(['data'])
|
||||
const { createdAt, userID, content } = props.data
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.product-comment-card {
|
||||
@apply w-[16.9rem] h-[13.1rem] rounded-xl border p-[1.1rem] flex flex-col justify-between;
|
||||
|
||||
p {
|
||||
@apply w-full text-[#7F7F7F] font-vazir text-xs leading-5;
|
||||
}
|
||||
|
||||
&>div {
|
||||
@apply flex gap-1.5 items-center;
|
||||
|
||||
span:nth-child(1) {
|
||||
@apply text-[#B2B2B2] font-vazir text-[0.7em];
|
||||
}
|
||||
|
||||
span:nth-child(2) {
|
||||
@apply relative flex items-center gap-1.5;
|
||||
|
||||
&::before {
|
||||
@apply content-['_'] w-1.5 h-1.5 bg-neutral-200 rounded-full;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<div class="product-comment-item">
|
||||
<div>
|
||||
<span>{{ point }}</span>
|
||||
<span>{{ role || $t('buyer') }}</span>
|
||||
<span>
|
||||
{{
|
||||
new Date(createdAt).toLocaleDateString('fa', {
|
||||
month: 'long', year: 'numeric', day: 'numeric'
|
||||
})
|
||||
}}
|
||||
</span>
|
||||
<span>{{ userID?.firstName }} {{ userID?.lastName }}</span>
|
||||
</div>
|
||||
<p>{{ content }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps(['data'])
|
||||
const { point, role, createdAt, userID, content } = props.data
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.product-comment-item {
|
||||
@apply w-full max-w-[47.9rem] xl:h-[8.3rem] flex flex-col border-b border-[#E5E5E5];
|
||||
@apply pb-4 lg:pb-8;
|
||||
@apply gap-4 lg:gap-6;
|
||||
|
||||
div {
|
||||
@apply flex gap-4 items-center;
|
||||
|
||||
span:nth-child(-n + 2) {
|
||||
@apply flex items-center justify-center pt-0.5 rounded font-iran-sans;
|
||||
@apply text-sm md:text-base xl:text-[1.1em];
|
||||
@apply h-7 lg:h-[2.2rem];
|
||||
@apply px-3 lg:px-4;
|
||||
}
|
||||
|
||||
span:nth-child(1) {
|
||||
@apply bg-[#019907] text-white;
|
||||
}
|
||||
|
||||
span:nth-child(2) {
|
||||
@apply bg-[#F2F2F2] text-[#999999] -mr-2;
|
||||
}
|
||||
|
||||
span:nth-child(n + 3) {
|
||||
@apply text-[#B2B2B2] font-vazir;
|
||||
@apply text-[0.7em] lg:text-lg;
|
||||
}
|
||||
|
||||
span:nth-child(4) {
|
||||
@apply relative flex items-center gap-1.5 lg:gap-4;
|
||||
|
||||
&::before {
|
||||
@apply content-['_'] w-1.5 h-1.5 bg-neutral-200 rounded-full;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
@apply w-full text-[#7F7F7F] font-vazir text-lg leading-[2.3rem];
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<div class="sort">
|
||||
<h3>
|
||||
<i class="isax isax-document-filter"></i>
|
||||
<span>{{ $t('sortBy') }}:</span>
|
||||
</h3>
|
||||
<ul>
|
||||
<li v-for="(btn, key) in btns.sort" :key="key">
|
||||
<Button v-bind="btn.props" @click="sort(key)" :active="sorted == key" />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const { btns } = inject('init')
|
||||
let { comments } = inject('data')
|
||||
const sorted = ref('createdAt')
|
||||
const sort = (key) => {
|
||||
sorted.value = key
|
||||
comments = comments.sort((a, b) => {
|
||||
return key == 'createdAt' ?
|
||||
new Date(b.createdAt) - new Date(a.createdAt) :
|
||||
b.point - a.point
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.product .comments .sort {
|
||||
@apply flex gap-4;
|
||||
|
||||
h3 {
|
||||
@apply flex items-center gap-2 text-[#333333] ml-9;
|
||||
|
||||
i {
|
||||
@apply text-2xl;
|
||||
}
|
||||
|
||||
span {
|
||||
@apply text-lg font-medium font-iran-sans;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
@apply flex;
|
||||
|
||||
button {
|
||||
@apply h-10 py-1.5 font-iran-sans font-medium text-lg text-[#B2B2B2] #{!important};
|
||||
|
||||
&[active=true] {
|
||||
@apply text-primary-600 #{!important};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<div class="summary">
|
||||
<h2>{{ $t('buyersComments') }}</h2>
|
||||
<div>
|
||||
<i class="isax isax-star-15"></i>
|
||||
<bdi>5 / {{ product.stars }}</bdi>
|
||||
</div>
|
||||
<p>{{ $t('buyersCommentsDesc') }}</p>
|
||||
<Button v-bind="btns.submit.props" @click="handleClick" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const { btns } = inject('init')
|
||||
const product = inject('data')
|
||||
const { dialog } = inject('service')
|
||||
|
||||
const handleClick = () => {
|
||||
dialog.show(resolveComponent('ProductCommentDialogForm'), product)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.product .comments>.summary {
|
||||
@apply flex w-[18.3rem] flex-col gap-4;
|
||||
|
||||
h2 {
|
||||
@apply flex gap-3 items-center text-primary-600 text-xl font-bold font-vazir;
|
||||
|
||||
&::before {
|
||||
@apply content-['_'] h-6 w-[0.3rem] rounded-full bg-primary-600;
|
||||
}
|
||||
}
|
||||
|
||||
div {
|
||||
@apply flex gap-2 items-center mt-6;
|
||||
|
||||
i {
|
||||
@apply text-xl text-[#FFB628];
|
||||
}
|
||||
|
||||
bdi {
|
||||
@apply text-[#191919] text-xl font-normal font-vazir;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
@apply text-[#999999] font-vazir;
|
||||
}
|
||||
|
||||
button {
|
||||
@apply h-[3.4rem] text-xl font-medium font-iran-sans #{!important};
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<section class="tab-comments">
|
||||
<Button :label="$t('commentsCount', { count: 4 })" text @click="handleClick" />
|
||||
<Swiper>
|
||||
<SwiperSlide v-for="(item, i) in items" :key="i">
|
||||
<ProductCommentCard :data="item" />
|
||||
</SwiperSlide>
|
||||
</Swiper>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.product .tab-comments {
|
||||
@apply flex flex-col gap-1 items-start;
|
||||
|
||||
&>button {
|
||||
@apply mr-3 #{!important};
|
||||
|
||||
.p-button-label {
|
||||
@apply text-[#47B556];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.swiper {
|
||||
@apply w-full h-[13.1rem] px-6;
|
||||
|
||||
.swiper-wrapper {
|
||||
@apply w-max z-0;
|
||||
|
||||
.swiper-slide {
|
||||
@apply h-max w-max ml-2.5 last:ml-0;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<section class="comments">
|
||||
<ProductCommentSummary />
|
||||
<div>
|
||||
<ProductCommentSort />
|
||||
<div>
|
||||
<ul>
|
||||
<li v-for="(comment, i) in comments" :key="i">
|
||||
<ProductCommentItem :data="comment" />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const { comments } = inject('data')
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.product .comments {
|
||||
@apply w-full flex gap-[11.4rem] lg:px-6;
|
||||
|
||||
&>div:not([class]) {
|
||||
@apply flex flex-col gap-3 lg:gap-8 overflow-hidden grow;
|
||||
|
||||
&>button {
|
||||
@apply lg:hidden w-10 h-5 p-0 mr-6;
|
||||
|
||||
.p-button-label {
|
||||
@apply text-[0.7em] rtl text-primary-600;
|
||||
}
|
||||
}
|
||||
|
||||
&>div:nth-of-type(2) {
|
||||
&>ul {
|
||||
@apply flex lg:flex-col gap-9;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,234 @@
|
||||
<template>
|
||||
<div class="product-comment-form">
|
||||
<div>
|
||||
<div>
|
||||
<h2>{{ $t('yourComment') }}</h2>
|
||||
<Button v-bind="closeBtn.props" @click="dialog.close()" />
|
||||
</div>
|
||||
<p>{{ $t('aboutTheProdcut', { title }) }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<ul>
|
||||
<li v-for="(item, key) in fields" :key="key">
|
||||
<component :is="item.is" ref="chips" :id="key" v-bind="item.props" v-model="form[key]" />
|
||||
<label :for="key">{{ item.label }}</label>
|
||||
<template v-if="['positive', 'negative'].includes(key)">
|
||||
<Button icon="isax isax-add" severity="secondary" text @click="addChip(i)" />
|
||||
</template>
|
||||
<small v-if="errors[key]">{{ errors[key] }}</small>
|
||||
</li>
|
||||
<li>
|
||||
<label> {{ $t('yourScore') }} : </label>
|
||||
<ul>
|
||||
<li v-for="i in 5" :key="i" @click="form.point = i">
|
||||
<img src="/icons/star.svg" :class="starsClass(i)" />
|
||||
</li>
|
||||
</ul>
|
||||
<small v-if="errors.point">{{ errors.point }}</small>
|
||||
</li>
|
||||
<li>
|
||||
<label>{{ $t('description') }} :</label>
|
||||
<Textarea v-model="form.content" :placeholder="$t('writeDesc')" />
|
||||
<small v-if="errors.content">{{ errors.content }}</small>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<Button :label="$t('submitComment')" :loading="loading" @click="submit()" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import init from '../../../../initialize/productCommentForm.js'
|
||||
const { closeBtn, fields } = init()
|
||||
const { toast, t } = inject('service')
|
||||
const dialog = inject('dialogRef')
|
||||
const { title, id } = dialog.value.data
|
||||
const chips = ref()
|
||||
|
||||
const form = reactive({})
|
||||
const errors = ref({});
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
const submit = async () => {
|
||||
if (Object.keys(errors.value).length < 1) {
|
||||
loading.value = true
|
||||
|
||||
const { status, error } = await useFetch(`/api/comments/${id}`, {
|
||||
headers: { Authorization: useCookie('token') }, method: 'post', body: form
|
||||
})
|
||||
|
||||
if (status.value == 'success') {
|
||||
toast.add({
|
||||
life: 3000, severity: 'success', summary: t('success'), detail: t('yourCommentSummited')
|
||||
})
|
||||
dialog.value.close()
|
||||
} else {
|
||||
if (error.value.statusCode == 422)
|
||||
{errors.value = error.value.data}
|
||||
else
|
||||
toast.add({
|
||||
life: 3000, severity: 'error', summary: t('error'), detail: error.value.message
|
||||
})
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const addChip = (i) => {
|
||||
chips.value[i].addItem(new Event('keydown'), chips.value[i].$data.inputValue, true)
|
||||
}
|
||||
|
||||
const starsClass = (i) => (i > (form?.point || 0) ? 'opacity-25' : '')
|
||||
|
||||
|
||||
watch(computed(() => form), (value, old) => {
|
||||
Object.keys(errors.value).forEach(key => {
|
||||
if (value[key] != old[key]) delete errors.value[key]
|
||||
});
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.product-comment-form {
|
||||
@apply w-full lg:w-[40.1rem] lg:h-[48.6rem] bg-white rounded-t-[0.6rem] lg:rounded-[0.6rem] flex flex-col relative;
|
||||
|
||||
&::before {
|
||||
@apply content-['_'] absolute inset-x-0 bottom-[4.5rem] h-3 shadow-[0px_-2px_5px_#0000000a] lg:shadow-none z-0;
|
||||
}
|
||||
|
||||
&>div:nth-of-type(1) {
|
||||
@apply flex flex-col gap-1.5 lg:gap-2 p-6 lg:px-10 lg:py-8 lg:border-b-2 border-[#E5E5E5];
|
||||
|
||||
&>div {
|
||||
@apply flex justify-between items-center;
|
||||
|
||||
h2 {
|
||||
@apply text-zinc-800 text-sm lg:text-xl font-bold font-vazir;
|
||||
}
|
||||
|
||||
button {
|
||||
@apply -mx-4 -my-3;
|
||||
|
||||
.p-button-icon {
|
||||
@apply text-[1.4em] lg:text-3xl text-[#191919];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
@apply text-[#7F7F7F] text-[0.7em] lg:text-lg font-vazir;
|
||||
}
|
||||
}
|
||||
|
||||
&>div:nth-of-type(2) {
|
||||
@apply lg:h-[29.9rem] grow p-6 lg:py-8 lg:px-10 lg:overflow-y-auto;
|
||||
|
||||
&>ul {
|
||||
@apply w-full flex flex-col items-center gap-[1.9rem] lg:gap-6 lg:py-1.5 lg:pr-14 lg:pl-16;
|
||||
|
||||
&>li:nth-of-type(-n + 3) {
|
||||
@apply w-[21.4rem] lg:w-[27.1rem] lg:min-h-[3.5rem] relative mt-1.5 lg:mt-3 flex items-center [&_button]:first:hidden;
|
||||
|
||||
button {
|
||||
@apply text-2xl absolute left-0 top-0 text-[#7F7F7F];
|
||||
}
|
||||
|
||||
label {
|
||||
@apply w-max absolute right-3 top-4 lg:top-3.5 px-1 bg-white transition-['top'];
|
||||
@apply text-neutral-400 text-sm lg:text-base font-medium font-vazir cursor-text;
|
||||
@apply pointer-events-none;
|
||||
}
|
||||
|
||||
&>input {
|
||||
@apply h-[3.4rem] pl-10 rtl w-full rounded-[0.6rem] border shadow-none;
|
||||
@apply text-zinc-800 font-vazir;
|
||||
}
|
||||
|
||||
.p-chips {
|
||||
@apply w-full max-w-full rounded-[0.6rem];
|
||||
|
||||
ul {
|
||||
@apply pl-8 w-full h-max grow flex-row-reverse;
|
||||
|
||||
input {
|
||||
@apply w-full rtl text-zinc-800 font-vazir;
|
||||
}
|
||||
|
||||
.p-chips-token {
|
||||
@apply ltr my-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:has(input:focus),
|
||||
&:has(.p-chips-token-label),
|
||||
&:has(input:not(:placeholder-shown)) {
|
||||
label {
|
||||
@apply -top-2 h-4;
|
||||
@apply text-zinc-800 text-xs font-normal font-vazir;
|
||||
}
|
||||
}
|
||||
|
||||
small{
|
||||
@apply absolute -bottom-6 left-0 ;
|
||||
}
|
||||
}
|
||||
|
||||
&>li:nth-of-type(4) {
|
||||
@apply w-[21.4rem] lg:w-[27.1rem] flex items-center gap-4 lg:mt-2;
|
||||
|
||||
label {
|
||||
@apply text-[#4C4C4C] text-sm lg:text-xl font-medium font-vazir;
|
||||
}
|
||||
|
||||
ul {
|
||||
@apply flex gap-1.5;
|
||||
|
||||
img {
|
||||
@apply w-[1.1rem] h-[1.1rem] lg:w-6 lg:h-6 cursor-pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&>li:nth-of-type(5) {
|
||||
@apply flex flex-col gap-4 relative;
|
||||
|
||||
label {
|
||||
@apply text-[#4C4C4C] text-sm lg:text-xl font-medium font-vazir;
|
||||
}
|
||||
|
||||
textarea {
|
||||
@apply w-[21.4rem] h-[12.1rem] lg:w-[27.1rem] lg:h-[12.1rem] p-4 rounded-[0.6rem] border border-[#E5E5E5];
|
||||
|
||||
&::placeholder {
|
||||
@apply text-[#999999] text-xs lg:text-lg font-normal font-vazir;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&>li::after {
|
||||
content: attr(error);
|
||||
@apply text-red-500 text-sm absolute -bottom-6 left-0;
|
||||
}
|
||||
|
||||
small {
|
||||
@apply text-red-500 font-vazir mr-auto;
|
||||
}
|
||||
|
||||
li:has(small) * {
|
||||
@apply border-red-500 #{!important};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&>button {
|
||||
@apply w-[21.4rem] lg:w-[35.1rem] h-[2.9rem] my-6 self-center lg:h-14 lg:m-10 #{!important};
|
||||
|
||||
.p-button-label {
|
||||
@apply font-bold font-vazir leading-tight text-sm lg:text-base;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div class="product-comments">
|
||||
<div>
|
||||
<Button icon="isax isax-arrow-right-1" link @click="$emit('close')" />
|
||||
<h2>{{ $t("commentsCount", { count: 5 }) }}</h2>
|
||||
<Button icon="isax isax-document-filter" link @click="handleClick" />
|
||||
</div>
|
||||
<div>
|
||||
<ul>
|
||||
<li v-for="(item, i) in items" :key="i">
|
||||
<ProductCommentItem :data="item" />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<Button :label="$t('submitComment')" icon="isax isax-message" rounded @click="handleClick" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const items = reactive([{}, {}, {}, {}, {}, {}, {}, {}, {}]);
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.product-comments {
|
||||
@apply w-full bg-white rounded-t-[0.6rem] flex flex-col gap-10 relative px-6 pt-6 overflow-hidden;
|
||||
|
||||
&>div:nth-of-type(1) {
|
||||
@apply w-full flex items-center gap-3;
|
||||
|
||||
&>h2 {
|
||||
@apply text-[#333333] text-sm font-medium font-vazir ml-auto;
|
||||
}
|
||||
|
||||
&>button {
|
||||
@apply -mx-4 -my-3;
|
||||
|
||||
.p-button-icon {
|
||||
@apply text-[#333333] text-[1.4em];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&>div:nth-of-type(2) {
|
||||
@apply h-full overflow-y-auto;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
&>ul {
|
||||
@apply h-max flex flex-col gap-6;
|
||||
}
|
||||
}
|
||||
|
||||
&>button {
|
||||
@apply absolute left-6 bottom-5 w-[8.3rem] h-12 px-[1.4rem] #{!important};
|
||||
|
||||
.p-button-label {
|
||||
@apply text-xs font-vazir;
|
||||
}
|
||||
|
||||
.p-button-icon {
|
||||
@apply text-xl;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div class="product-comments-sort">
|
||||
<div>
|
||||
<Button icon="pi pi-times" link @click="$emit('close')" />
|
||||
<h2>{{ $t("sortBy") }}</h2>
|
||||
</div>
|
||||
<ul>
|
||||
<li v-for="(item, key) in items" :key="key">
|
||||
<h3 @click="events.select(key)" v-ripple>
|
||||
{{ item.title }}
|
||||
</h3>
|
||||
<i v-if="item.selected" class="pi pi-check"></i>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const { t } = useI18n();
|
||||
const items = reactive({
|
||||
newest: { title: t("newest"), selected: true },
|
||||
mostUseful: { title: t("mostUseful") },
|
||||
});
|
||||
|
||||
const events = reactive({
|
||||
select: (key) => {
|
||||
items.newest.selected = false;
|
||||
items.mostUseful.selected = false;
|
||||
items[key].selected = true;
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.product-comments-sort {
|
||||
@apply w-full flex flex-col gap-9 p-6 bg-white rounded-t-[0.6rem];
|
||||
|
||||
&>div {
|
||||
@apply flex items-center gap-3;
|
||||
|
||||
&>button {
|
||||
@apply -mx-4 -my-3 text-[#333333] #{!important};
|
||||
}
|
||||
|
||||
&>h2 {
|
||||
@apply text-[#333333] text-sm font-medium font-vazir;
|
||||
}
|
||||
}
|
||||
|
||||
&>ul {
|
||||
@apply w-full flex flex-col gap-4;
|
||||
|
||||
&>li {
|
||||
@apply flex items-center justify-between pb-4 first:border-b text-[#333333];
|
||||
|
||||
h3 {
|
||||
@apply text-xs font-medium font-vazir;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<div class="product-chart">
|
||||
<div>
|
||||
<h2>{{ $t('productPriceChangesChart') }}</h2>
|
||||
<Button v-bind="btns.close.props" @click="dialog.close()" />
|
||||
</div>
|
||||
<div>
|
||||
<p>{{ $t('priceFilterFrom') }}</p>
|
||||
<AppCalendar v-model="date.from" class="isax isax-note5" />
|
||||
<p>{{ $t('until') }}</p>
|
||||
<AppCalendar v-model="date.to" class="isax isax-note5" />
|
||||
</div>
|
||||
<Chart :data="chartData" :options="chartOptions" type="line" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Chart from 'primevue/chart';
|
||||
|
||||
const { meta } = useRoute()
|
||||
const { btns, chart } = meta.init
|
||||
|
||||
const dialog = inject('dialogRef')
|
||||
const { pastPrices } = dialog.value.data
|
||||
|
||||
const date = reactive({})
|
||||
const chartData = ref()
|
||||
const chartOptions = ref()
|
||||
|
||||
onBeforeMount(() => {
|
||||
try {
|
||||
date.from = new Date(pastPrices[0].date).toLocaleDateString('fa')
|
||||
date.to = new Date(pastPrices[pastPrices.length - 1].date).toLocaleDateString('fa')
|
||||
} catch (error) { }
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
chartData.value = chart.setChartData(pastPrices, date)
|
||||
chartOptions.value = chart.setChartOptions()
|
||||
})
|
||||
|
||||
watch(
|
||||
date,
|
||||
() => chartData.value = chart.setChartData(pastPrices, date),
|
||||
{ deep: true }
|
||||
)
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.product-chart {
|
||||
@apply w-full lg:w-[64rem] lg:h-[43.1rem] bg-white rounded-t-[0.6rem] lg:rounded-[0.6rem] flex flex-col lg:pb-[4.9rem];
|
||||
|
||||
&>div:nth-of-type(1) {
|
||||
@apply flex justify-between items-center gap-3 p-6 lg:p-8 pb-6 lg:border-b-2 border-[#E5E5E5];
|
||||
|
||||
h2 {
|
||||
@apply text-zinc-800 text-sm lg:text-xl font-bold font-vazir;
|
||||
}
|
||||
|
||||
button {
|
||||
@apply -mx-4 -my-3;
|
||||
|
||||
.p-button-icon {
|
||||
@apply text-[1.4em] lg:text-3xl text-[#191919];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&>div:nth-of-type(2) {
|
||||
@apply flex flex-wrap items-center gap-[1.1rem] p-6 lg:gap-6 lg:px-8 lg:py-10;
|
||||
|
||||
&>p {
|
||||
@apply text-[#333333] first:w-full lg:first:w-max text-sm lg:text-lg font-medium font-vazir;
|
||||
}
|
||||
|
||||
.p-calendar {
|
||||
@apply flex items-center gap-3 p-3 font-['iconsax'] #{!important};
|
||||
@apply w-32 h-11 lg:w-[10.4rem] lg:h-12 rounded-lg border-2 border-[#E5E5E5];
|
||||
|
||||
&::before {
|
||||
@apply text-xl lg:text-2xl order-1;
|
||||
}
|
||||
|
||||
&::after {
|
||||
@apply content-['_'] w-[0.1rem] h-4 bg-[#E5E5E5] order-2;
|
||||
}
|
||||
|
||||
input {
|
||||
@apply border-none text-left bg-transparent shadow-none w-[6rem] order-3 p-0;
|
||||
@apply text-zinc-800 text-xs lg:text-lg font-normal font-vazir;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.p-chart {
|
||||
@apply w-[22.9rem] h-[21.2rem] lg:w-[59.3rem] lg:h-[24.3rem];
|
||||
}
|
||||
}
|
||||
|
||||
body:has(.product-chart) {
|
||||
.p-datepicker {
|
||||
@apply lg:-translate-x-28;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<div class="product-menu">
|
||||
<ul>
|
||||
<li v-for="({ icon, label, click }, i) in meta.init.menu" :key="i" >
|
||||
<div @click="click" v-ripple>
|
||||
<i :class="icon" />
|
||||
<span>{{ label }}</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const { meta } = useRoute()
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.product-menu {
|
||||
@apply w-full flex flex-col px-6 py-[1.1rem] rounded-t-[0.6rem] bg-white rtl;
|
||||
|
||||
ul {
|
||||
@apply w-full flex flex-col gap-4;
|
||||
|
||||
li {
|
||||
@apply border-[#E5E5E5] border-b last:border-none;
|
||||
|
||||
div {
|
||||
@apply flex items-center gap-4 pb-4 text-[#333333] relative overflow-hidden;
|
||||
|
||||
i {
|
||||
@apply text-xl;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@apply text-xs font-medium font-vazir;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<div class="product-reminder">
|
||||
<div>
|
||||
<div>
|
||||
<h2>{{ $t('notices') }}</h2>
|
||||
<Button v-bind="btns.close.props" @click="dialog.close()" />
|
||||
</div>
|
||||
<p>{{ $t('howToInformAboutAmazingPrice') }}</p>
|
||||
</div>
|
||||
<ul>
|
||||
<li v-for="key in checkboxes.reminder" :key="key">
|
||||
<Checkbox v-model="form[key]" binary :input-id="key" />
|
||||
<label :for="key"> {{ $t(key + 'Notice') }} </label>
|
||||
</li>
|
||||
</ul>
|
||||
<Button :label="$t('submit')" :loading="loading" @click="submit()" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const { meta } = useRoute()
|
||||
const { btns, checkboxes } = meta.init
|
||||
const { toast, t } = inject('service')
|
||||
const dialog = inject('dialogRef')
|
||||
const { reminder, _id: id } = dialog.value.data
|
||||
|
||||
const form = reactive(reminder[0] || {})
|
||||
const store = useProductsStore()
|
||||
const loading = ref(false)
|
||||
|
||||
const submit = async () => {
|
||||
loading.value = true
|
||||
const { status, data } = await store.addReminder(id, form)
|
||||
loading.value = false
|
||||
|
||||
if (status.value == 'success') {
|
||||
dialog.value.data.reminder = data.value.reminder
|
||||
toast.add({
|
||||
life: 3000, severity: 'success', summary: t('success'), detail: t('submitedSuccessfully')
|
||||
})
|
||||
dialog.value.close()
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.product-reminder {
|
||||
@apply w-full lg:w-[40.3rem] lg:h-[25.2rem] bg-white rounded-t-[0.6rem] lg:rounded-[0.6rem] flex flex-col gap-8 lg:gap-10 p-6 lg:p-10 lg:pt-8 relative;
|
||||
|
||||
&::after {
|
||||
@apply max-lg:content-['_'] absolute inset-x-0 bottom-[4.5rem] h-3 shadow-[0px_-2px_5px_#0000000a];
|
||||
}
|
||||
|
||||
&>div:nth-of-type(1) {
|
||||
@apply flex flex-col gap-1.5 lg:gap-2;
|
||||
|
||||
&>div {
|
||||
@apply flex justify-between items-center;
|
||||
|
||||
h2 {
|
||||
@apply text-zinc-800 text-sm lg:text-xl font-bold font-vazir;
|
||||
}
|
||||
|
||||
button {
|
||||
@apply -mx-4 -my-3;
|
||||
|
||||
.p-button-icon {
|
||||
@apply text-[1.4em] lg:text-3xl text-[#191919];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
@apply text-[#7F7F7F] text-[0.7em] lg:text-lg font-normal font-vazir;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
@apply flex flex-col gap-[1.1rem] lg:gap-6 max-lg:mt-1;
|
||||
|
||||
li {
|
||||
@apply flex items-center gap-4 cursor-pointer w-max select-none;
|
||||
|
||||
.p-checkbox,
|
||||
.p-checkbox-box {
|
||||
@apply w-4 lg:w-6 h-4 lg:h-6;
|
||||
}
|
||||
|
||||
label {
|
||||
@apply text-[#4C4C4C] text-xs lg:text-xl font-medium font-vazir cursor-pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&>button {
|
||||
@apply h-[2.9rem] lg:h-14 #{!important};
|
||||
|
||||
.p-button-label {
|
||||
@apply font-bold font-iran-sans leading-tight text-sm lg:text-base;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<div class="product-report">
|
||||
<div>
|
||||
<div>
|
||||
<h2>{{ $t("reportIncorrectSpecs") }}</h2>
|
||||
<Button v-bind="btns.close.props" @click="dialog.close()" />
|
||||
</div>
|
||||
<p>{{ title }}</p>
|
||||
</div>
|
||||
<ul>
|
||||
<li v-for="key in checkboxes.report" :key="key">
|
||||
<Checkbox v-model="form.items[key]" binary :input-id="key" />
|
||||
<label :for="key">{{ $t(key + "Product") }}</label>
|
||||
</li>
|
||||
<li>
|
||||
<label>{{ $t("description") }}</label>
|
||||
<Textarea v-model="form.content" :placeholder="$t('writeDesc')" />
|
||||
</li>
|
||||
</ul>
|
||||
<Button :label="$t('send')" :loading="loading" @click="send()" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const { meta } = useRoute()
|
||||
const { btns, checkboxes } = meta.init
|
||||
const { toast, t } = inject('service')
|
||||
const dialog = inject('dialogRef')
|
||||
const { title, id, report } = dialog.value.data
|
||||
|
||||
const form = reactive(report ?? { items: {}, content: '' });
|
||||
const store = useProductsStore();
|
||||
const loading = ref(false)
|
||||
|
||||
const send = async () => {
|
||||
if (Object.keys(form.items).length > 0 && form.content.length > 0) {
|
||||
loading.value = true
|
||||
const { status, data } = await store.addReport(id, form);
|
||||
loading.value = false
|
||||
|
||||
if (status.value == 'success') {
|
||||
toast.add({
|
||||
life: 3000, severity: 'success', summary: t('success'), detail: t('submitedSuccessfully')
|
||||
})
|
||||
dialog.value.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.product-report {
|
||||
@apply w-full lg:w-[40.1rem] lg:h-[48.6rem] bg-white rounded-t-[0.6rem] lg:rounded-[0.6rem] flex flex-col relative;
|
||||
|
||||
&::after {
|
||||
@apply max-lg:content-['_'] absolute inset-x-0 bottom-[4.5rem] h-3 shadow-[0px_-2px_5px_#0000000a];
|
||||
}
|
||||
|
||||
&>div:nth-of-type(1) {
|
||||
@apply flex flex-col gap-1.5 lg:gap-2 border-b lg:border-b-2 border-[#E5E5E5] p-6 lg:px-10 lg:pt-8 pb-6;
|
||||
|
||||
&>div {
|
||||
@apply flex justify-between items-center;
|
||||
|
||||
h2 {
|
||||
@apply text-zinc-800 text-sm lg:text-xl font-bold font-vazir;
|
||||
}
|
||||
|
||||
button {
|
||||
@apply -mx-4 -my-3;
|
||||
|
||||
.p-button-icon {
|
||||
@apply text-[1.4em] lg:text-3xl text-[#191919];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
@apply text-[#7F7F7F] text-[0.7em] lg:text-lg font-normal font-vazir;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
@apply flex flex-col gap-[1.1rem] px-6 pt-9 lg:gap-5 lg:pt-6 lg:px-10;
|
||||
|
||||
li {
|
||||
@apply flex items-center gap-4 cursor-pointer w-max select-none;
|
||||
|
||||
.p-checkbox,
|
||||
.p-checkbox-box {
|
||||
@apply w-4 h-4 lg:w-6 lg:h-6;
|
||||
}
|
||||
|
||||
label {
|
||||
@apply text-[#4C4C4C] text-xs lg:text-xl font-medium font-vazir cursor-pointer;
|
||||
}
|
||||
}
|
||||
|
||||
li:last-child {
|
||||
@apply w-full lg:w-[35.1rem] flex flex-col items-start gap-4 mt-3 lg:mt-4;
|
||||
|
||||
label {
|
||||
@apply text-[#7F7F7F] text-xs lg:text-xl font-medium font-vazir max-lg:mr-2.5;
|
||||
}
|
||||
|
||||
textarea {
|
||||
@apply w-full h-[9.6rem] lg:w-[35.1rem] lg:h-[9.6rem] p-4 rounded-[0.6rem] border border-[#E5E5E5];
|
||||
|
||||
&::placeholder {
|
||||
@apply text-[#999999] text-[0.7em] lg:text-lg font-normal font-vazir;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&>button {
|
||||
@apply h-[2.9rem] m-6 mt-8 lg:h-14 lg:m-10 lg:mt-9 #{!important};
|
||||
|
||||
.p-button-label {
|
||||
@apply font-bold font-iran-sans text-sm lg:text-base;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<div class="product-share">
|
||||
<div>
|
||||
<div>
|
||||
<h2>{{ $t('share') }}</h2>
|
||||
<Button v-bind="btns.close.props" @click="dialog.close()" />
|
||||
</div>
|
||||
<p>{{ $t('shareProductWhitFriends') }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<template v-for="(btn, key) in btns.shares" :key="key">
|
||||
<Button v-bind="btn.props" @click="events[key]()" />
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const { meta } = useRoute()
|
||||
const { btns } = meta.init
|
||||
const { toast, t } = inject('service')
|
||||
const dialog = inject('dialogRef')
|
||||
|
||||
const events = reactive({
|
||||
copyLink: () => {
|
||||
navigator.clipboard.writeText(location.href).then(function () {
|
||||
toast.add({ life: 3000, summary: t('success'), detail: t('copied'), severity: 'success' })
|
||||
})
|
||||
dialog.value.close()
|
||||
},
|
||||
telegram: () => {
|
||||
window.open('tg://msg_url?url=' + location.href, '_blank')
|
||||
dialog.value.close()
|
||||
},
|
||||
whatsapp: () => {
|
||||
window.open('whatsapp://send?text=' + location.href, '_blank')
|
||||
dialog.value.close()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.product-share {
|
||||
@apply w-full lg:w-[40.1rem] lg:h-[19.9rem] bg-white rounded-t-[0.6rem] lg:rounded-[0.6rem] flex flex-col justify-between;
|
||||
|
||||
&>div:nth-of-type(1) {
|
||||
@apply flex flex-col gap-1.5 lg:gap-2 p-6 lg:px-10 lg:pb-2 lg:pt-8;
|
||||
|
||||
&>div {
|
||||
@apply flex justify-between items-center;
|
||||
|
||||
h2 {
|
||||
@apply text-zinc-800 text-sm lg:text-xl font-bold font-vazir;
|
||||
}
|
||||
|
||||
button {
|
||||
@apply -mx-4 -my-3;
|
||||
|
||||
.p-button-icon {
|
||||
@apply text-[1.4em] lg:text-3xl text-[#B2B2B2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
@apply text-[#7F7F7F] text-[0.7em] lg:text-lg font-normal font-vazir;
|
||||
}
|
||||
}
|
||||
|
||||
&>div:nth-of-type(2) {
|
||||
@apply flex flex-wrap gap-x-3 gap-y-[1.1rem] lg:gap-y-6 px-6 pb-5 pt-3 lg:p-10;
|
||||
|
||||
button {
|
||||
@apply grow justify-center h-12 lg:h-14;
|
||||
|
||||
.p-button-icon {
|
||||
@apply text-xl lg:text-[1.4em] ml-3;
|
||||
}
|
||||
|
||||
.p-button-label {
|
||||
@apply text-xs lg:text-xl font-medium font-iran-sans grow-0;
|
||||
}
|
||||
}
|
||||
|
||||
button:nth-of-type(1) {
|
||||
@apply w-full border-2 border-[#B2B2B2] text-[#333333];
|
||||
|
||||
.p-button-icon {
|
||||
@apply text-xl lg:text-2xl;
|
||||
}
|
||||
}
|
||||
|
||||
button:nth-of-type(2) {
|
||||
@apply bg-[#43E97B] hover:bg-[#467ca7] border-[#43E97B];
|
||||
}
|
||||
|
||||
button:nth-of-type(3) {
|
||||
@apply bg-[#34AD39] hover:bg-[#30a034] border-[#34AD39];
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,133 @@
|
||||
<template>
|
||||
<div class="other-seller-item">
|
||||
<div>
|
||||
<div>
|
||||
<i class="isax isax-shop"></i>
|
||||
<h3>{{ title }}</h3>
|
||||
</div>
|
||||
<p>
|
||||
<small>{{ percent }}%</small>
|
||||
<span>{{ $t("satisfaction") }}</span>
|
||||
<span>{{ $t("performance") }} {{ $t(performance) }}</span>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p v-for="(item, i) in items" :key="i">
|
||||
<i :class="item.icon"></i>
|
||||
<span>{{ item.label }}</span>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<Button :label="$t('show')" size="small" />
|
||||
<span>
|
||||
<b>{{ price }}</b>
|
||||
<small>{{ $t("priceUnit") }}</small>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
title: { type: String, default: "فروشگاه اسپیس" },
|
||||
percent: { type: Number, default: 50 },
|
||||
performance: { type: String, default: "veryGood" },
|
||||
shippingDesc: {
|
||||
type: String,
|
||||
default: "ارسال از فروشنده پس از تایید نهایی 1-2 روز دیگر",
|
||||
},
|
||||
warranty: { type: String, default: "گارانتی 12 ماهه اسپیس" },
|
||||
price: { type: String, default: "580,000" },
|
||||
});
|
||||
|
||||
const items = reactive([
|
||||
{ icon: "isax isax-box", label: props.shippingDesc },
|
||||
{ icon: "isax isax-shield-tick", label: props.warranty },
|
||||
]);
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.other-seller-item {
|
||||
@apply w-full flex flex-col gap-4 justify-center relative;
|
||||
@apply lg:flex-row xl:gap-8 lg:justify-between lg:items-center lg:bg-neutral-50 lg:rounded-[10px];
|
||||
@apply lg:h-28 lg:px-4 xl:px-6;
|
||||
|
||||
|
||||
& > div:nth-of-type(1) {
|
||||
@apply flex flex-col gap-3 pb-4 border-b lg:border-b-0 xl:pr-4 lg:pb-0;
|
||||
|
||||
& > div {
|
||||
@apply flex gap-3 lg:gap-4 items-center;
|
||||
|
||||
i {
|
||||
@apply text-xl lg:text-2xl;
|
||||
}
|
||||
|
||||
h3 {
|
||||
@apply text-zinc-800 text-xs lg:text-xl font-medium font-vazir;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
@apply flex flex-wrap whitespace-nowrap items-center gap-1 xl:mr-10 font-vazir;
|
||||
|
||||
small {
|
||||
@apply text-[#019907] text-[11.20px] lg:text-base;
|
||||
}
|
||||
|
||||
span {
|
||||
@apply text-right text-[#999999] text-[11.20px] lg:text-base;
|
||||
|
||||
&:first-of-type {
|
||||
@apply pl-3 lg:pl-4 relative;
|
||||
|
||||
&::after {
|
||||
@apply content-['_'] w-px bg-[#D9D9D9] h-4 absolute left-0.5 lg:left-1 top-1/2 -translate-y-1/2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& > div:nth-of-type(2) {
|
||||
@apply flex flex-col 2xl:flex-row gap-4 2xl:gap-40;
|
||||
|
||||
& > p {
|
||||
@apply flex items-center gap-2 pb-4 border-b lg:border-b-0 lg:pb-0;
|
||||
|
||||
i {
|
||||
@apply text-xl;
|
||||
}
|
||||
|
||||
span {
|
||||
@apply text-[#999] font-medium font-vazir text-[11.20px] lg:text-base;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& > div:nth-of-type(3) {
|
||||
@apply h-max flex justify-between items-center pb-[30px] border-b lg:border-b-0;
|
||||
@apply lg:flex-row-reverse lg:gap-4 xl:gap-6 2xl:gap-8 lg:pb-0;
|
||||
|
||||
& > span {
|
||||
@apply font-vazir text-[#4C4C4C];
|
||||
|
||||
b {
|
||||
@apply text-xs lg:text-[25px] font-medium;
|
||||
}
|
||||
|
||||
small {
|
||||
@apply text-xs lg:text-xl mr-1;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
@apply w-[147px] h-[41px] lg:w-[153px] lg:h-11 #{!important};
|
||||
|
||||
span {
|
||||
@apply font-iran-sans text-sm lg:text-lg font-medium;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<section class="product-other-sellers">
|
||||
<h2>{{ $t("otherSellers") }}</h2>
|
||||
<ul>
|
||||
<li v-for="(item, i) in items" :key="i">
|
||||
<OtherSellerItem />
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps({
|
||||
items: { type: Array, default: [{}, {}, {}] },
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.product-other-sellers {
|
||||
@apply w-full flex flex-col gap-16 px-6;
|
||||
|
||||
& > h2 {
|
||||
@apply text-[#0B1F11] text-3xl font-bold font-vazir;
|
||||
}
|
||||
|
||||
& > ul {
|
||||
@apply w-full flex flex-col gap-6;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user