Updated
This commit is contained in:
@@ -24,7 +24,7 @@ const { mutateAsync: createComment, isPending: isCreateCommentPending } =
|
|||||||
const submitComment = async () => {
|
const submitComment = async () => {
|
||||||
if (userComment.value.length > 3) {
|
if (userComment.value.length > 3) {
|
||||||
await createComment({
|
await createComment({
|
||||||
content: userComment.value,
|
content: userComment.value
|
||||||
});
|
});
|
||||||
|
|
||||||
userComment.value = "";
|
userComment.value = "";
|
||||||
@@ -36,11 +36,11 @@ const submitComment = async () => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section class="bg-slate-50">
|
<section class="bg-slate-50">
|
||||||
<div class="flex relative gap-8 my-42 container">
|
<div class="flex relative gap-8 my-42 container max-lg:flex-col">
|
||||||
<div
|
<div
|
||||||
class="sticky top-0 flex flex-col gap-6 min-w-[400px] max-h-min bg-white p-8 rounded-xl border-[0.5px] border-slate-200"
|
class="sticky top-0 flex flex-col gap-6 lg:min-w-[400px] h-fit bg-white p-8 rounded-xl border-[0.5px] border-slate-200"
|
||||||
>
|
>
|
||||||
<h3 class="typo-h-3">نظرات کاربران</h3>
|
<h3 class="typo-h-6 max-sm:text-xl md:typo-h-5 lg:typo-h-4">نظرات کاربران</h3>
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<Rating :rate="2" />
|
<Rating :rate="2" />
|
||||||
<span class="typo-p-sm">
|
<span class="typo-p-sm">
|
||||||
|
|||||||
@@ -9,21 +9,21 @@ const { selectedVariant } = inject("productVariant") as ProductVariantProvideTyp
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section class="w-full p-[5rem] flex flex-col gap-y-[1.5rem]">
|
<section class="w-full container py-20 flex flex-col gap-y-[1.5rem]">
|
||||||
<div class="w-full flex">
|
<div class="w-full flex">
|
||||||
<span class="text-black typo-h-3"> جزيات محصول </span>
|
<span class="text-black max-lg:hidden typo-h-4 mb-4"> جزيات محصول </span>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full flex items-start justify-between gap-[3rem]">
|
<div class="w-full flex items-start justify-between gap-[3rem] max-lg:flex-col-reverse">
|
||||||
<div class="w-8/12">
|
<div class="flex-1 w-full">
|
||||||
<Accordion />
|
<Accordion />
|
||||||
</div>
|
</div>
|
||||||
<div class="w-4/12">
|
<div class="w-full lg:w-[450px] xl:w-[600px]">
|
||||||
<div
|
<div
|
||||||
class="w-full bg-slate-50 rounded-xl flex-col-center p-[5rem] gap-[1.5rem]"
|
class="w-full bg-slate-50 rounded-xl flex-col-center px-5 py-16 sm:p-[5rem] gap-[1.5rem]"
|
||||||
>
|
>
|
||||||
<span>داخل جعبه چیه؟</span>
|
<span class="typo-h-6 mb-8">داخل جعبه چیه؟</span>
|
||||||
<div
|
<div
|
||||||
class="w-full grid grid-cols-2 gap-y-[1.5rem] gap-x-[3rem]"
|
class="w-full grid grid-cols-2 gap-y-[1.5rem] sm:gap-x-[3rem]"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-for="inPackItem in selectedVariant.in_pack_items"
|
v-for="inPackItem in selectedVariant.in_pack_items"
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
||||||
// import
|
// import
|
||||||
|
|
||||||
import useGetProduct from "~/composables/api/product/useGetProduct";
|
import useGetProduct from "~/composables/api/product/useGetProduct";
|
||||||
@@ -20,9 +21,7 @@ const selectedColor = ref(product.value!.colors[0]);
|
|||||||
|
|
||||||
// provide / inject
|
// provide / inject
|
||||||
|
|
||||||
const { selectedVariant, changeSelectedVariant } = inject(
|
const { selectedVariant, changeSelectedVariant } = inject("productVariant") as ProductVariantProvideType;
|
||||||
"productVariant"
|
|
||||||
) as ProductVariantProvideType;
|
|
||||||
|
|
||||||
// computed
|
// computed
|
||||||
|
|
||||||
@@ -32,79 +31,105 @@ const sanitizedProductDescription = computed(() => {
|
|||||||
|
|
||||||
// watch
|
// watch
|
||||||
|
|
||||||
watch(
|
watch(() => selectedVariantId.value, (newId) => {
|
||||||
() => selectedVariantId.value,
|
const newVariant = product.value!.variants.find(variant => variant.id === newId)!;
|
||||||
(newId) => {
|
changeSelectedVariant(newVariant);
|
||||||
const newVariant = product.value!.variants.find(
|
});
|
||||||
(variant) => variant.id === newId
|
|
||||||
)!;
|
|
||||||
changeSelectedVariant(newVariant);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
watch(
|
watch(() => selectedColor.value, (newValue) => {
|
||||||
() => selectedColor.value,
|
const filteredVariants = product.value!.variants.filter(v => v.color === newValue);
|
||||||
(newValue) => {
|
selectedVariantId.value = filteredVariants[0].id;
|
||||||
const filteredVariants = product.value!.variants.filter(
|
selectedVariant.value = filteredVariants[0];
|
||||||
(v) => v.color === newValue
|
}, {
|
||||||
);
|
immediate: true
|
||||||
selectedVariantId.value = filteredVariants[0].id;
|
});
|
||||||
selectedVariant.value = filteredVariants[0];
|
|
||||||
},
|
watch(() => selectedVariant.value, (newValue) => {
|
||||||
{
|
selectedQuantity.value = 1;
|
||||||
immediate: true,
|
selectedSlide.value = newValue.images[0].id;
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => selectedVariant.value,
|
|
||||||
(newValue) => {
|
|
||||||
selectedQuantity.value = 1;
|
|
||||||
selectedSlide.value = newValue.images[0].id;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex gap-16 container pt-[5rem] pb-28">
|
<div class="flex max-lg:flex-col gap-12 xl:gap-16 container pt-[5rem] pb-28">
|
||||||
<Slider
|
<div class="flex flex-col gap-3 lg:hidden">
|
||||||
class="flex-1"
|
<NuxtLink to="#" class="typo-label-sm"> {{ product!.category.name }}</NuxtLink>
|
||||||
v-model:selectedSlide="selectedSlide"
|
<h1 class="typo-h-6 xs:typo-h-5 sm:typo-h-4 lg:typo-h-2"> {{ product!.name }} </h1>
|
||||||
:slides="selectedVariant.images"
|
|
||||||
/>
|
|
||||||
<div class="flex-1 flex flex-col gap-3 mt-12">
|
|
||||||
<span class="typo-label-sm"> سامسونگ </span>
|
|
||||||
<h1 class="typo-h-2">{{ product!.name }}</h1>
|
|
||||||
<div class="flex w-full items-center justify-between h-[85px]">
|
<div class="flex w-full items-center justify-between h-[85px]">
|
||||||
<div class="flex items-end gap-4">
|
<div class="flex items-end gap-4">
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<span
|
<span
|
||||||
v-if="selectedVariant.discount > 0"
|
v-if="selectedVariant.discount > 0"
|
||||||
class="typo-p-lg relative flex-center w-fit line-through"
|
class="typo-p-lg relative flex-center w-fit"
|
||||||
|
:class="'after:w-full after:h-[2px] after:bg-black after:absolute'"
|
||||||
>
|
>
|
||||||
{{ selectedVariant.price }}
|
{{ selectedVariant.price }}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
class="typo-p-2xl relative flex-center w-fit font-medium"
|
class="typo-p-2xl relative flex-center w-fit font-medium"
|
||||||
>
|
>
|
||||||
{{
|
{{ selectedVariant.discount > 0 ? selectedVariant.price : selectedVariant.price }}
|
||||||
selectedVariant.discount > 0
|
|
||||||
? selectedVariant.price
|
|
||||||
: selectedVariant.price
|
|
||||||
}}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="selectedVariant.discount > 0"
|
v-if="selectedVariant.discount > 0"
|
||||||
class="text-white bg-blue-500 mb-1 px-4 py-2 text-xs rounded-full flex items-center gap-1"
|
class="text-white bg-blue-500 mb-1 px-4 py-2 text-xs rounded-full flex items-center gap-1"
|
||||||
>
|
>
|
||||||
<Icon name="bi:percent" class="size-4" />
|
<Icon name="material-symbols:percent" class="size-4" />
|
||||||
{{ selectedVariant.discount }}
|
{{ selectedVariant.discount }}
|
||||||
درصد تخفیف
|
درصد تخفیف
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Rating :rate="3" />
|
<Rating :rate="3" />
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<Slider
|
||||||
|
class="w-full lg:w-1/2 lg:max-w-[620px]"
|
||||||
|
v-model:selectedSlide="selectedSlide"
|
||||||
|
:slides="selectedVariant.images"
|
||||||
|
/>
|
||||||
|
<div class="lg:w-1/2 flex flex-col gap-3 lg:mt-12">
|
||||||
|
<NuxtLink
|
||||||
|
to="#"
|
||||||
|
class="typo-label-sm max-lg:hidden"
|
||||||
|
>
|
||||||
|
{{ product!.category.name }}
|
||||||
|
</NuxtLink>
|
||||||
|
<h1 class="typo-h-4 xl:typo-h-3 max-lg:hidden"> {{ product!.name }} </h1>
|
||||||
|
<div class="flex w-full items-center justify-between h-[85px] max-lg:hidden">
|
||||||
|
<div class="flex items-end gap-4">
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<span
|
||||||
|
v-if="selectedVariant.discount > 0"
|
||||||
|
class="typo-p-lg relative flex-center w-fit"
|
||||||
|
:class="'after:w-full after:h-[2px] after:bg-black after:absolute'"
|
||||||
|
>
|
||||||
|
{{ selectedVariant.price }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="typo-p-2xl relative flex-center w-fit font-medium"
|
||||||
|
>
|
||||||
|
{{ selectedVariant.discount > 0 ? selectedVariant.price : selectedVariant.price }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="selectedVariant.discount > 0"
|
||||||
|
class="text-white bg-blue-500 mb-1 px-4 py-2 text-xs rounded-full flex items-center gap-1"
|
||||||
|
>
|
||||||
|
<Icon name="material-symbols:percent" class="size-4" />
|
||||||
|
{{ selectedVariant.discount }}
|
||||||
|
<span class="max-sm:hidden">درصد</span>
|
||||||
|
تخفیف
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Rating :rate="3" class="sm:hidden" />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Rating :rate="3" class="max-sm:hidden" />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="py-8 typo-p-md text-slate-500 text-justify [&_a]:text-blue-400 [&_strong]:font-bold [&_u]:text-red-400"
|
class="py-8 typo-p-md text-slate-500 text-justify [&_a]:text-blue-400 [&_strong]:font-bold [&_u]:text-red-400"
|
||||||
@@ -112,15 +137,17 @@ watch(
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<span class="typo-p-lg"> تنوع رنگی : </span>
|
<span class="typo-p-lg">
|
||||||
|
تنوع رنگی :
|
||||||
|
</span>
|
||||||
<div class="flex items-center gap-4 py-4">
|
<div class="flex items-center gap-4 py-4">
|
||||||
<ColorCircle
|
<ColorCircle
|
||||||
v-for="color in product!.colors"
|
v-for="color in product!.colors"
|
||||||
:key="color"
|
:key="color"
|
||||||
@click="selectedColor = color"
|
@click="selectedColor = color"
|
||||||
selectable
|
selectable
|
||||||
:selected="selectedColor === color"
|
:selected="selectedColor === color "
|
||||||
:style="{ backgroundColor: color }"
|
:style="{backgroundColor: color}"
|
||||||
class="cursor-pointer"
|
class="cursor-pointer"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -128,11 +155,7 @@ watch(
|
|||||||
|
|
||||||
<div class="flex items-center gap-6 flex-wrap">
|
<div class="flex items-center gap-6 flex-wrap">
|
||||||
<ProductVariant
|
<ProductVariant
|
||||||
@click="
|
@click="variant.in_stock > 0 ? selectedVariantId = variant.id : undefined"
|
||||||
variant.in_stock > 0
|
|
||||||
? (selectedVariantId = variant.id)
|
|
||||||
: undefined
|
|
||||||
"
|
|
||||||
v-for="variant in product!.variants.filter(p => p.color === selectedColor)"
|
v-for="variant in product!.variants.filter(p => p.color === selectedColor)"
|
||||||
:key="variant.id"
|
:key="variant.id"
|
||||||
:variantDetail="variant"
|
:variantDetail="variant"
|
||||||
@@ -141,28 +164,26 @@ watch(
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-full flex flex-col gap-6 mt-10">
|
<div class="w-full flex flex-col gap-6 mt-10">
|
||||||
|
|
||||||
<RemainQuantity
|
<RemainQuantity
|
||||||
:maxQuantity="selectedVariant.in_stock"
|
:maxQuantity="selectedVariant.in_stock"
|
||||||
:quantity="selectedQuantity"
|
:quantity="selectedQuantity"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="w-full flex gap-3 flex-col">
|
<div class="w-full flex gap-3">
|
||||||
<div class="w-full flex gap-3">
|
<Button class="w-full rounded-full" end-icon="ci:plus">
|
||||||
<Button class="w-full rounded-full" end-icon="ci:plus">
|
افزودن به سبد خرید
|
||||||
افزودن به سبد خرید
|
|
||||||
</Button>
|
|
||||||
<QuantityCounter
|
|
||||||
v-model="selectedQuantity"
|
|
||||||
:max="selectedVariant.in_stock"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Button class="w-full rounded-full" variant="outlined">
|
|
||||||
همین الان بخر
|
|
||||||
</Button>
|
</Button>
|
||||||
|
<QuantityCounter
|
||||||
|
v-model="selectedQuantity"
|
||||||
|
:max="selectedVariant.in_stock"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<InfoCard />
|
<InfoCard />
|
||||||
|
|
||||||
<Share />
|
<Share />
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -26,12 +26,14 @@ const { selectedVariant } = inject("productVariant") as ProductVariantProvideTyp
|
|||||||
loop
|
loop
|
||||||
/>
|
/>
|
||||||
<div class="size-full absolute inset-0 bg-black/20" />
|
<div class="size-full absolute inset-0 bg-black/20" />
|
||||||
<StickyCard
|
<div class="absolute max-sm:flex items-center justify-center max-sm:px-5 sm:right-10 bottom-10 w-full">
|
||||||
:color="selectedVariant.color!"
|
<StickyCard
|
||||||
:price="selectedVariant.price"
|
:color="selectedVariant.color!"
|
||||||
:picture="selectedVariant.images[0].image"
|
:price="selectedVariant.price"
|
||||||
:title="product!.name"
|
:picture="selectedVariant.images[0].image"
|
||||||
class="absolute right-10 bottom-10"
|
:title="product!.name"
|
||||||
/>
|
class="max-sm:!w-full"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user