merge
This commit is contained in:
@@ -12,7 +12,7 @@ const {} = toRefs(props);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="relative w-full flex flex-col justify-center h-[350px]">
|
||||
<div class="relative w-full flex flex-col justify-center min-h-[700px] h-[80svh]">
|
||||
<div class="-rotate-z-2 z-20">
|
||||
<div
|
||||
class="bg-warning-500 flex pr-20 gap-20 py-2 w-max animate-marquee-reverse"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// types
|
||||
|
||||
type Props = {
|
||||
selected ?: boolean;
|
||||
selected?: boolean;
|
||||
}
|
||||
|
||||
// props
|
||||
@@ -14,7 +14,7 @@ defineProps<Props>();
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="size-[25px] rounded-full shadow-black/30 shadow-inner"
|
||||
:class="selected ? 'ring-black' : 'ring-transparent'"
|
||||
class="size-[25px] rounded-full transition-all ring-2 ring-offset-4 shadow-black/30 shadow-inner"
|
||||
:class="selected ? 'ring-blue-500' : 'ring-transparent'"
|
||||
/>
|
||||
</template>
|
||||
@@ -21,7 +21,7 @@ nuxtApp.hook("page:finish", () => {
|
||||
<Transition name="fade">
|
||||
<div
|
||||
v-if="isLoading"
|
||||
class="h-[20px] flex items-center justify-center bg-black w-full left-0 top-0 fixed z-100"
|
||||
class="h-[20px] flex items-center justify-center bg-black w-full left-0 top-0 fixed z-9999"
|
||||
>
|
||||
<div class="absolute progress-indicator w-1/3 bg-white h-1 rounded-full"></div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
<script lang="ts" setup>
|
||||
|
||||
// state
|
||||
|
||||
const { $gsap: gsap } = useNuxtApp();
|
||||
|
||||
// lifecycle
|
||||
|
||||
onMounted(() => {
|
||||
const timeline = gsap.timeline();
|
||||
|
||||
timeline
|
||||
.to("#loading-overlay", {
|
||||
scale: 1
|
||||
})
|
||||
.to("#loading-overlay", {
|
||||
scale: 0.8,
|
||||
opacity: 0,
|
||||
delay: 5
|
||||
})
|
||||
.to("#loading-overlay", {
|
||||
opacity: 0,
|
||||
y: "20%"
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="loading-overlay" class="fixed inset-0 size-full z-9999 flex-center bg-black">
|
||||
<img id="loading-overlay-image" src="/video/loading-2.gif" class="opacity-0 scale-70 absolute z-20" alt="" />
|
||||
<div
|
||||
id="loading-overlay-gradient"
|
||||
class="opacity-0 scale-x-0 w-[1000px] h-[70px] bg-linear-to-r from-blue-500 via-violet-500 to-purple-500 blur-[150px] rounded-[100px]"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
|
||||
#loading-overlay-image {
|
||||
animation-name: loading-overlay-image-animation;
|
||||
animation-duration: 1s;
|
||||
animation-delay: 0.75s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
#loading-overlay-gradient {
|
||||
animation: 1.5s normal 0.5s 1 forwards loading-overlay-gradient-animation,
|
||||
1s ease-in-out 2s infinite alternate-reverse loading-overlay-gradient-pules-animation;
|
||||
}
|
||||
|
||||
@keyframes loading-overlay-image-animation {
|
||||
from {
|
||||
opacity: 0;
|
||||
scale: 0.7;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
scale: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes loading-overlay-gradient-animation {
|
||||
from {
|
||||
opacity: 0;
|
||||
scale: 0 1 1;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 0.9;
|
||||
scale: 1 1 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes loading-overlay-gradient-pules-animation {
|
||||
from {
|
||||
opacity: 0.8;
|
||||
scale: 0.8 1 1;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 0.9;
|
||||
scale: 1 1 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -91,9 +91,9 @@ const onSwiper = (swiper: SwiperClass) => {
|
||||
:id="product.id"
|
||||
brand="برند محصول"
|
||||
:title="product.name"
|
||||
:picture="product.image1"
|
||||
:colors="['white', 'black']"
|
||||
:price="product.price"
|
||||
:picture="product.variants[0].images[0].image"
|
||||
:colors="product.variants.map(v => v.color)"
|
||||
:price="product.variants[0].price"
|
||||
:rate="product.rating"
|
||||
:dark-layer="true"
|
||||
/>
|
||||
|
||||
@@ -10,49 +10,58 @@ type Props = {
|
||||
// props
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const { modelValue } = toRefs(props);
|
||||
const { modelValue, max } = toRefs(props);
|
||||
|
||||
// state
|
||||
|
||||
const timer = ref<NodeJS.Timeout | null>(null);
|
||||
|
||||
// emit
|
||||
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
|
||||
// state
|
||||
// computed
|
||||
|
||||
const currentQuantity = ref(modelValue.value);
|
||||
const currentQuantity = computed({
|
||||
get: () => modelValue.value ?? 0,
|
||||
set: (value: number) => {
|
||||
if (timer.value) clearTimeout(timer.value);
|
||||
timer.value = setTimeout(() => {
|
||||
emit("update:modelValue", value);
|
||||
}, 50);
|
||||
}
|
||||
});
|
||||
|
||||
// methods
|
||||
|
||||
const onInput = (e: any) => {
|
||||
currentQuantity.value = Number(e.target.value);
|
||||
const value = Number(e.target.value);
|
||||
if (value > 0 && value <= max.value) {
|
||||
currentQuantity.value = value;
|
||||
} else {
|
||||
currentQuantity.value = 1;
|
||||
}
|
||||
};
|
||||
|
||||
// watch
|
||||
|
||||
watch(() => currentQuantity.value, (newValue) => {
|
||||
emit("update:modelValue", newValue);
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="">
|
||||
<NumberFieldRoot
|
||||
class="rounded-full border-slate-200 border-[1.5px] flex items-center bg-white gap-4 p-4"
|
||||
v-model="currentQuantity"
|
||||
:min="1"
|
||||
:max="max"
|
||||
>
|
||||
<NumberFieldIncrement class="cursor-pointer">
|
||||
<Icon name="ci:plus" class="**:stroke-slate-500 size-5" />
|
||||
</NumberFieldIncrement>
|
||||
<NumberFieldInput
|
||||
@input="onInput"
|
||||
class="field-sizing-content bg-transparent outline-none typo-label-md text-black"
|
||||
/>
|
||||
<NumberFieldDecrement class="cursor-pointer">
|
||||
<Icon name="ci:minus" class="**:stroke-slate-500 size-5" />
|
||||
</NumberFieldDecrement>
|
||||
</NumberFieldRoot>
|
||||
</div>
|
||||
<NumberFieldRoot
|
||||
class="rounded-full border-slate-200 border-[1.5px] flex items-center bg-white gap-4 p-4"
|
||||
v-model="currentQuantity"
|
||||
:min="1"
|
||||
:max="max"
|
||||
>
|
||||
<NumberFieldIncrement class="cursor-pointer">
|
||||
<Icon name="ci:plus" class="**:stroke-slate-500 size-5" />
|
||||
</NumberFieldIncrement>
|
||||
<NumberFieldInput
|
||||
@input="onInput"
|
||||
class="field-sizing-content bg-transparent outline-none typo-label-md text-black"
|
||||
/>
|
||||
<NumberFieldDecrement class="cursor-pointer">
|
||||
<Icon name="ci:minus" class="**:stroke-slate-500 size-5" />
|
||||
</NumberFieldDecrement>
|
||||
</NumberFieldRoot>
|
||||
</template>
|
||||
@@ -15,12 +15,12 @@ defineProps<Props>();
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col gap-2 w-full">
|
||||
<p class="typo-p-sm text-slate-500">
|
||||
سریع باش فقط
|
||||
<span class="text-black">
|
||||
<p class="typo-p-md text-slate-500">
|
||||
تعداد
|
||||
<span class="text-black font-bold">
|
||||
{{ maxQuantity }}
|
||||
</span>
|
||||
عدد از این محصول باقی مانده
|
||||
عدد از این محصول موجود است
|
||||
</p>
|
||||
<div class="h-2 rounded-full relative bg-slate-200 w-full">
|
||||
<div
|
||||
|
||||
@@ -4,10 +4,7 @@
|
||||
|
||||
type Props = {
|
||||
selectedSlide: number;
|
||||
slides: {
|
||||
id: number;
|
||||
picture: string;
|
||||
}[]
|
||||
slides: ProductImage[]
|
||||
}
|
||||
|
||||
// props
|
||||
@@ -41,8 +38,8 @@ const changeSlide = (id: number) => {
|
||||
<img
|
||||
:key="selectedSlideDetail.id"
|
||||
class="size-full absolute object-contain"
|
||||
:src="selectedSlideDetail.picture"
|
||||
:alt="String(selectedSlideDetail.id)"
|
||||
:src="selectedSlideDetail.image"
|
||||
:alt="selectedSlideDetail.name"
|
||||
/>
|
||||
</Transition>
|
||||
</div>
|
||||
@@ -56,7 +53,7 @@ const changeSlide = (id: number) => {
|
||||
>
|
||||
<img
|
||||
class="absolute object-cover size-full"
|
||||
:src="slide.picture"
|
||||
:src="slide.image"
|
||||
:alt="String(slide.id)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,16 +1,30 @@
|
||||
<script lang="ts" setup>
|
||||
|
||||
// provide / inject
|
||||
|
||||
import type { ProductVariantProvideType } from "~/pages/product/[id].vue";
|
||||
|
||||
const { selectedVariant } = inject("productVariant") as ProductVariantProvideType;
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full flex flex-col">
|
||||
<AccordionRoot
|
||||
class="w-full last:border-b last:border-slate-200"
|
||||
default-value="item-1"
|
||||
:default-value="'item' + selectedVariant.details[0].detail_category"
|
||||
type="single"
|
||||
:collapsible="true"
|
||||
>
|
||||
<AccordionItem value="item-1" class="overflow-hidden">
|
||||
<AccordionItem
|
||||
v-for="detailItem in selectedVariant.details"
|
||||
:value="'item' + detailItem.detail_category"
|
||||
class="overflow-hidden"
|
||||
>
|
||||
<AccordionHeader
|
||||
class="border-t border-slate-200 py-[1.5rem] flex justify-between items-center"
|
||||
>
|
||||
<span class="typo-sub-h-md text-black">مشخصات</span>
|
||||
<span class="typo-sub-h-md text-black">{{ detailItem.detail_category }}</span>
|
||||
<AccordionTrigger class="group">
|
||||
<Icon
|
||||
name="ci:plus"
|
||||
@@ -26,97 +40,20 @@
|
||||
class="w-full grid grid-cols-2 gap-y-[1.5rem] gap-x-[1rem]"
|
||||
>
|
||||
<div
|
||||
v-for="i in 4"
|
||||
v-for="item in detailItem.detail"
|
||||
class="flex flex-col gap-y-[1.5rem]"
|
||||
>
|
||||
<span
|
||||
class="typo-sub-h-lg text-black w-full pt-[1.5rem]"
|
||||
>صفحه نمایش</span
|
||||
>
|
||||
{{ item.title }}
|
||||
</span>
|
||||
<ul class="list-disc w-full ps-5">
|
||||
<li class="text-slate-500 typo-p-md">
|
||||
روشنایی :3000mn
|
||||
</li>
|
||||
<li class="text-slate-500 typo-p-md">
|
||||
روشنایی :3000mn
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
<AccordionItem value="item-2" class="overflow-hidden">
|
||||
<AccordionHeader
|
||||
class="border-t border-slate-200 py-[1.5rem] flex justify-between items-center"
|
||||
>
|
||||
<span class="typo-sub-h-md text-black">مشخصات</span>
|
||||
<AccordionTrigger class="group">
|
||||
<Icon
|
||||
name="ci:plus"
|
||||
size="24"
|
||||
class="group-data-[state=open]:rotate-45 transition-transform"
|
||||
/>
|
||||
</AccordionTrigger>
|
||||
</AccordionHeader>
|
||||
<AccordionContent
|
||||
class="data-[state=open]:animate-slide-down pb-[1.5rem] data-[state=closed]:animate-slide-up overflow-hidden"
|
||||
>
|
||||
<div
|
||||
class="w-full grid grid-cols-2 gap-y-[1.5rem] gap-x-[1rem]"
|
||||
>
|
||||
<div
|
||||
v-for="i in 4"
|
||||
class="flex flex-col gap-y-[1.5rem]"
|
||||
>
|
||||
<span
|
||||
class="typo-sub-h-lg text-black w-full pt-[1.5rem]"
|
||||
>صفحه نمایش</span
|
||||
>
|
||||
<ul class="list-disc w-full ps-5">
|
||||
<li class="text-slate-500 typo-p-md">
|
||||
روشنایی :3000mn
|
||||
</li>
|
||||
<li class="text-slate-500 typo-p-md">
|
||||
روشنایی :3000mn
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
<AccordionItem value="item-3" class="overflow-hidden">
|
||||
<AccordionHeader
|
||||
class="border-t border-slate-200 py-[1.5rem] flex justify-between items-center"
|
||||
>
|
||||
<span class="typo-sub-h-md text-black">مشخصات</span>
|
||||
<AccordionTrigger class="group">
|
||||
<Icon
|
||||
name="ci:plus"
|
||||
size="24"
|
||||
class="group-data-[state=open]:rotate-45 transition-transform"
|
||||
/>
|
||||
</AccordionTrigger>
|
||||
</AccordionHeader>
|
||||
<AccordionContent
|
||||
class="data-[state=open]:animate-slide-down pb-[1.5rem] data-[state=closed]:animate-slide-up overflow-hidden"
|
||||
>
|
||||
<div
|
||||
class="w-full grid grid-cols-2 gap-y-[1.5rem] gap-x-[1rem]"
|
||||
>
|
||||
<div
|
||||
v-for="i in 4"
|
||||
class="flex flex-col gap-y-[1.5rem]"
|
||||
>
|
||||
<span
|
||||
class="typo-sub-h-lg text-black w-full pt-[1.5rem]"
|
||||
>صفحه نمایش</span
|
||||
>
|
||||
<ul class="list-disc w-full ps-5">
|
||||
<li class="text-slate-500 typo-p-md">
|
||||
روشنایی :3000mn
|
||||
</li>
|
||||
<li class="text-slate-500 typo-p-md">
|
||||
روشنایی :3000mn
|
||||
<li
|
||||
v-for="detail in [ item.detail_text1, item.detail_text2, item.detail_text3, item.detail_text4 ]"
|
||||
class="text-slate-500 typo-p-md"
|
||||
>
|
||||
{{ detail }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -125,8 +62,4 @@
|
||||
</AccordionItem>
|
||||
</AccordionRoot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<style scoped></style>
|
||||
</template>
|
||||
@@ -22,9 +22,12 @@ const { picture, price, title, color } = toRefs(props);
|
||||
<div class="relative size-[100px] rounded-100 overflow-hidden border-[0.5px] border-slate-200">
|
||||
<img :src="picture" :alt="title" class="object-cover absolute" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<span class="typo-sub-h-md text-black">{{ title }}</span>
|
||||
<span class="typo-p-sm text-slate-500">{{ color }}</span>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="typo-p-sm text-slate-500">رنگ</span>
|
||||
<ColorCircle class="!size-5" :style="{backgroundColor: color}" />
|
||||
</div>
|
||||
<span class="typo-p-md text-black">{{ price }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user