Merge branch 'main' of https://github.com/Byeto-Company/hossein_por_shop
This commit is contained in:
@@ -8,9 +8,19 @@ from django.contrib.auth.models import AnonymousUser
|
||||
|
||||
|
||||
class DetailSerializer(serializers.ModelSerializer):
|
||||
texts = serializers.SerializerMethodField()
|
||||
class Meta:
|
||||
model = DetailModel
|
||||
exclude = ['detail_model']
|
||||
exclude = ['detail_model', 'detail_text1', 'detail_text2', 'detail_text3', 'detail_text4']
|
||||
def get_texts(self, obj):
|
||||
return [
|
||||
text for text in [
|
||||
obj.detail_text1,
|
||||
obj.detail_text2,
|
||||
obj.detail_text3,
|
||||
obj.detail_text4,
|
||||
] if text
|
||||
]
|
||||
|
||||
class ProductDetailSerializer(serializers.ModelSerializer):
|
||||
details = DetailSerializer(many=True, read_only=True)
|
||||
|
||||
@@ -131,8 +131,6 @@
|
||||
--breakpoint-3xl: 1700px;
|
||||
|
||||
/* ANIMATIONS */
|
||||
--animate-marquee: marquee 20s linear infinite;
|
||||
--animate-marquee-reverse: marquee 20s linear infinite reverse;
|
||||
--animate-fade-in: fadeIn 350ms ease-in-out;
|
||||
|
||||
--animate-slide-down: slideDown 300ms ease-out;
|
||||
@@ -149,12 +147,6 @@
|
||||
--animate-toast-in: toastSlideIn 600ms cubic-bezier(0.16, 1, 0.3, 1);
|
||||
--animate-toast-out: toastSlideOut 200ms ease-out;
|
||||
|
||||
@keyframes marquee {
|
||||
to {
|
||||
transform: translateX(50%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
|
||||
@@ -9,6 +9,15 @@ type Props = {
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const {} = toRefs(props);
|
||||
|
||||
const brands = ref([
|
||||
"/img/brands/brand-1.png",
|
||||
"/img/brands/brand-2.png",
|
||||
"/img/brands/brand-3.png",
|
||||
"/img/brands/brand-4.png",
|
||||
"/img/brands/brand-5.png",
|
||||
"/img/brands/brand-6.png",
|
||||
]);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -20,136 +29,64 @@ const {} = toRefs(props);
|
||||
متون بلکه روزنامه و مجله در ستون و سطرآنچنان که
|
||||
</p>
|
||||
</div>
|
||||
<div class="-rotate-z-2 z-20">
|
||||
<div
|
||||
class="bg-black flex items-center pr-20 gap-12 sm:gap-20 w-max animate-marquee-reverse h-[90px] sm:h-[140px]"
|
||||
<div class="-rotate-z-2 z-20 w-[110%]">
|
||||
<Marquee
|
||||
class="bg-black h-full"
|
||||
:clone="true"
|
||||
dir="ltr"
|
||||
:duration="3"
|
||||
>
|
||||
<template v-for="i in 10">
|
||||
<div class="text-[30px] lg:text-[40px] text-white whitespace-nowrap font-semibold opacity-85">
|
||||
<div class="flex items-center gap-12 sm:gap-20 px-6 sm:px-10 h-[90px] sm:h-[140px]">
|
||||
<div class="text-[30px] lg:text-[40px] mt-2 text-white whitespace-nowrap font-semibold opacity-85">
|
||||
HEYMLZ
|
||||
</div>
|
||||
<NuxtImg
|
||||
src="/img/heymlz/heymlz-logo.png"
|
||||
class="h-[25px] sm:h-[45px] invert opacity-85"
|
||||
/>
|
||||
</template>
|
||||
<template v-for="i in 10">
|
||||
<div class="text-[30px] lg:text-[40px] text-white whitespace-nowrap font-semibold opacity-85">
|
||||
HEYMLZ
|
||||
</div>
|
||||
<NuxtImg
|
||||
src="/img/heymlz/heymlz-logo.png"
|
||||
class="h-[25px] sm:h-[45px] invert opacity-85"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</Marquee>
|
||||
</div>
|
||||
|
||||
<div class="rotate-z-2 z-10">
|
||||
<div
|
||||
class="bg-slate-100/70 flex items-center pr-20 gap-12 sm:gap-20 w-max animate-marquee h-[90px] sm:h-[140px]"
|
||||
<div class="rotate-z-2 z-10 w-[110%]">
|
||||
<Marquee
|
||||
class="bg-slate-100/70"
|
||||
:direction="'reverse'"
|
||||
:clone="true"
|
||||
dir="ltr"
|
||||
:duration="10"
|
||||
>
|
||||
<template v-for="i in 1">
|
||||
<div
|
||||
v-for="brand in brands"
|
||||
:key="brand"
|
||||
class="flex items-center px-6 sm:px-10 h-[90px] sm:h-[140px]"
|
||||
>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-1.png"
|
||||
:src="brand"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-2.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-3.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-4.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-5.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-6.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-1.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-2.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-3.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-4.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-5.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-6.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
</template>
|
||||
<template v-for="i in 1">
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-1.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-2.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-3.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-4.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-5.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-6.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-1.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-2.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-3.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-4.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-5.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-6.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</Marquee>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- <NuxtImg
|
||||
src="/img/brands/brand-2.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-3.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-4.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-5.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/>
|
||||
<NuxtImg
|
||||
src="/img/brands/brand-6.png"
|
||||
class="h-[25px] sm:h-[45px]"
|
||||
/> -->
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
|
||||
import { Swiper, SwiperSlide } from "swiper/vue";
|
||||
import type { SwiperClass } from "swiper/react";
|
||||
import useHomeData from "~/composables/api/home/useHomeData";
|
||||
|
||||
// types
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
products: ProductListItem[];
|
||||
};
|
||||
|
||||
// props
|
||||
@@ -17,14 +17,8 @@ defineProps<Props>();
|
||||
|
||||
// state
|
||||
|
||||
const { data: homeData, suspense } = useHomeData();
|
||||
|
||||
const swiper_instance = ref<SwiperClass | null>(null);
|
||||
|
||||
// queries
|
||||
|
||||
await suspense();
|
||||
|
||||
// methods
|
||||
|
||||
const onSwiper = (swiper: SwiperClass) => {
|
||||
@@ -100,7 +94,7 @@ const onSwiper = (swiper: SwiperClass) => {
|
||||
}"
|
||||
>
|
||||
<SwiperSlide
|
||||
v-for="product in [...homeData!.products,...homeData!.products]"
|
||||
v-for="product in products"
|
||||
:key="product.id"
|
||||
>
|
||||
<ProductCard
|
||||
|
||||
@@ -45,106 +45,111 @@ const emptySlidesCount = computed(() => {
|
||||
const changeSlide = (id: number) => {
|
||||
emit("update:selectedSlide", id);
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col relative gap-6">
|
||||
<div
|
||||
class="bg-white brightness-[97%] w-full relative aspect-square overflow-hidden rounded-[12px] md:rounded-200"
|
||||
>
|
||||
<Transition name="zoom" mode="out-in">
|
||||
<NuxtImg
|
||||
:key="selectedSlideDetail.id"
|
||||
class="size-full absolute object-contain"
|
||||
:src="selectedSlideDetail.image"
|
||||
:alt="selectedSlideDetail.name"
|
||||
/>
|
||||
</Transition>
|
||||
</div>
|
||||
|
||||
<div class="relative w-full">
|
||||
<Swiper
|
||||
:slides-per-view="3"
|
||||
:space-between="20"
|
||||
@swiper="onSwiper"
|
||||
class="w-full"
|
||||
<div class="sticky top-10">
|
||||
<div class="flex flex-col relative gap-6">
|
||||
<div
|
||||
class="bg-white brightness-[97%] w-full relative aspect-square overflow-hidden rounded-[12px] md:rounded-200"
|
||||
>
|
||||
<SwiperSlide
|
||||
class="py-4"
|
||||
v-for="slide in slides"
|
||||
:key="slide.id"
|
||||
<Transition
|
||||
name="zoom"
|
||||
mode="out-in"
|
||||
>
|
||||
<div
|
||||
@click="changeSlide(slide.id)"
|
||||
:class="
|
||||
selectedSlide === slide.id
|
||||
? '!border-black'
|
||||
: 'border-transparent'
|
||||
"
|
||||
class="active:scale-95 hover:border-slate-200 transition-all cursor-pointer brightness-[97%] bg-white aspect-square border-2 rounded-[12px] md:rounded-200 w-full overflow-hidden relative"
|
||||
<NuxtImg
|
||||
:key="selectedSlideDetail.id"
|
||||
class="size-full absolute object-contain"
|
||||
:src="selectedSlideDetail.image"
|
||||
:alt="selectedSlideDetail.name"
|
||||
/>
|
||||
</Transition>
|
||||
</div>
|
||||
|
||||
<div class="relative w-full">
|
||||
<Swiper
|
||||
:slides-per-view="3"
|
||||
:space-between="20"
|
||||
@swiper="onSwiper"
|
||||
class="w-full"
|
||||
>
|
||||
<SwiperSlide
|
||||
class="py-4"
|
||||
v-for="slide in slides"
|
||||
:key="slide.id"
|
||||
>
|
||||
<NuxtImg
|
||||
class="absolute object-cover size-full"
|
||||
:src="slide.image"
|
||||
:alt="String(slide.id)"
|
||||
/>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide
|
||||
v-if="emptySlidesCount > 0"
|
||||
v-for="slide in emptySlidesCount"
|
||||
:key="slide"
|
||||
class="py-4"
|
||||
<div
|
||||
@click="changeSlide(slide.id)"
|
||||
:class="selectedSlide === slide.id ? '!border-black' : 'border-transparent'"
|
||||
class="active:scale-95 hover:border-slate-200 transition-all cursor-pointer brightness-[97%] bg-white aspect-square border-2 rounded-[12px] md:rounded-200 w-full overflow-hidden relative"
|
||||
>
|
||||
<NuxtImg
|
||||
class="absolute object-cover size-full"
|
||||
:src="slide.image"
|
||||
:alt="String(slide.id)"
|
||||
/>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide
|
||||
v-if="emptySlidesCount > 0"
|
||||
v-for="slide in emptySlidesCount"
|
||||
:key="slide"
|
||||
class="py-4"
|
||||
>
|
||||
<div
|
||||
class="brightness-[97%] flex-center bg-white aspect-square rounded-[12px] md:rounded-200 w-full"
|
||||
>
|
||||
<Icon
|
||||
name="ci:image-slash"
|
||||
class="size-[40px] sm:size-[60px] **:fill-black/20"
|
||||
/>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
</Swiper>
|
||||
|
||||
<div
|
||||
v-if="slides.length > 3"
|
||||
@click="swiper_instance?.slidePrev()"
|
||||
class="absolute z-20 -right-4 shadow-lg cursor-pointer shadow-black/10 bottom-[50%] translate-y-1/2 bg-white rounded-full size-10 xs:size-11.5 flex justify-center items-center"
|
||||
>
|
||||
<div class="brightness-[97%] flex-center bg-white aspect-square rounded-[12px] md:rounded-200 w-full">
|
||||
<Icon name="ci:image-slash" class="size-[40px] sm:size-[60px] **:fill-black/20" />
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
</Swiper>
|
||||
<Icon
|
||||
name="ci:arrow-right"
|
||||
class="**:stroke-black size-5 xs:size-6"
|
||||
:class="swiper_instance?.isBeginning ? '**:stroke-black/30' : ''"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="slides.length > 3"
|
||||
@click="swiper_instance?.slidePrev()"
|
||||
class="absolute z-20 -right-4 shadow-lg cursor-pointer shadow-black/10 bottom-[50%] translate-y-1/2 bg-white rounded-full size-10 xs:size-11.5 flex justify-center items-center"
|
||||
>
|
||||
<Icon
|
||||
name="ci:arrow-right"
|
||||
class="**:stroke-black size-5 xs:size-6"
|
||||
:class="swiper_instance?.isBeginning ? '**:stroke-black/30' : ''"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="slides.length > 3"
|
||||
@click="swiper_instance?.slideNext()"
|
||||
class="absolute z-20 -left-4 shadow-lg cursor-pointer shadow-black/10 bottom-[50%] translate-y-1/2 bg-white rounded-full size-10 xs:size-11.5 flex justify-center items-center"
|
||||
>
|
||||
<Icon
|
||||
name="ci:arrow-left"
|
||||
class="**:stroke-black size-5 xs:size-6"
|
||||
:class="swiper_instance?.isEnd ? '**:stroke-black/30' : ''"
|
||||
/>
|
||||
<div
|
||||
v-if="slides.length > 3"
|
||||
@click="swiper_instance?.slideNext()"
|
||||
class="absolute z-20 -left-4 shadow-lg cursor-pointer shadow-black/10 bottom-[50%] translate-y-1/2 bg-white rounded-full size-10 xs:size-11.5 flex justify-center items-center"
|
||||
>
|
||||
<Icon
|
||||
name="ci:arrow-left"
|
||||
class="**:stroke-black size-5 xs:size-6"
|
||||
:class="swiper_instance?.isEnd ? '**:stroke-black/30' : ''"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="grid grid-cols-3 gap-6">-->
|
||||
<!-- <div-->
|
||||
<!-- @click="changeSlide(slide.id)"-->
|
||||
<!-- v-for="slide in slides"-->
|
||||
<!-- :class="-->
|
||||
<!-- selectedSlide === slide.id-->
|
||||
<!-- ? '!ring-black'-->
|
||||
<!-- : 'ring-transparent'-->
|
||||
<!-- "-->
|
||||
<!-- class="active:scale-95 hover:ring-slate-200 transition-all cursor-pointer brightness-[97%] bg-white aspect-square ring-2 ring-offset-4 rounded-[12px] md:rounded-200 w-full overflow-hidden relative"-->
|
||||
<!-- :key="slide.id"-->
|
||||
<!-- >-->
|
||||
<!-- <img-->
|
||||
<!-- class="absolute object-cover size-full"-->
|
||||
<!-- :src="slide.image"-->
|
||||
<!-- :alt="String(slide.id)"-->
|
||||
<!-- />-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
<!-- <div class="grid grid-cols-3 gap-6">-->
|
||||
<!-- <div-->
|
||||
<!-- @click="changeSlide(slide.id)"-->
|
||||
<!-- v-for="slide in slides"-->
|
||||
<!-- :class="-->
|
||||
<!-- selectedSlide === slide.id-->
|
||||
<!-- ? '!ring-black'-->
|
||||
<!-- : 'ring-transparent'-->
|
||||
<!-- "-->
|
||||
<!-- class="active:scale-95 hover:ring-slate-200 transition-all cursor-pointer brightness-[97%] bg-white aspect-square ring-2 ring-offset-4 rounded-[12px] md:rounded-200 w-full overflow-hidden relative"-->
|
||||
<!-- :key="slide.id"-->
|
||||
<!-- >-->
|
||||
<!-- <img-->
|
||||
<!-- class="absolute object-cover size-full"-->
|
||||
<!-- :src="slide.image"-->
|
||||
<!-- :alt="String(slide.id)"-->
|
||||
<!-- />-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -23,16 +23,16 @@ const onSwiper = (swiper: SwiperClass) => {
|
||||
class="flex flex-col justify-center gap-4 bg-black sm:min-h-[110svh] relative overflow-hidden shrink-0 py-24 lg:py-32"
|
||||
>
|
||||
<div class="w-full relative flex-center z-10 container">
|
||||
<span class="text-white typo-h-6 md:typo-h-5 lg:typo-h-4"> دسته بندی ها </span>
|
||||
<span class="text-white typo-h-6 md:typo-h-5 lg:typo-h-4 2xl:typo-h-2"> دسته بندی ها </span>
|
||||
</div>
|
||||
|
||||
<div class="w-full mt-44 lg:mt-64 relative">
|
||||
<NuxtImg
|
||||
class="aspect-square w-[240px] md:w-[300px] lg:w-[350px] translate-y-[-164px] md:translate-y-[-206px] lg:translate-y-[-240px] absolute left-1/2 -translate-x-1/2 z-10"
|
||||
class="aspect-square w-[210px] sm:w-[240px] md:w-[300px] lg:w-[350px] 2xl:w-[420px] translate-y-[-136px] sm:translate-y-[-156px] md:translate-y-[-195px] lg:translate-y-[-228px] 2xl:translate-y-[-273px] absolute left-1/2 -translate-x-1/2 z-10"
|
||||
:style="{
|
||||
filter: 'drop-shadow(0px 0px 20px rgba(0, 0, 0, 0.4))',
|
||||
}"
|
||||
src="/img/heymlz/heymlz-seat.gif"
|
||||
src="/img/heymlz/heymlz-category-seat.gif"
|
||||
/>
|
||||
<Swiper
|
||||
:loop="true"
|
||||
|
||||
@@ -179,9 +179,11 @@ onUnmounted(() => {
|
||||
<div
|
||||
id="header-slider-wrapper"
|
||||
class="relative"
|
||||
:class="swiper_instance ? '' : 'bg-black min-h-svh'"
|
||||
>
|
||||
<Swiper
|
||||
ref="observerTarget"
|
||||
:class="swiper_instance ? '' : 'opacity-0'"
|
||||
:slides-per-view="slidesPerView"
|
||||
:loop="true"
|
||||
:centered-slides="true"
|
||||
@@ -242,18 +244,32 @@ onUnmounted(() => {
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<h3 class="typo-h-6 md:typo-h-4 lg:typo-h-1 tracking-[-2px] text-white">
|
||||
<NuxtLink
|
||||
:to="slide.link"
|
||||
class="typo-h-6 md:typo-h-4 lg:typo-h-1 tracking-[-2px] text-white"
|
||||
>
|
||||
{{ slide.title }}
|
||||
</h3>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="truncate typo-p-xs md:typo-p-sm lg:typo-p-lg text-white">
|
||||
{{ slide.description }}
|
||||
</span>
|
||||
<NuxtLink :to="slide.link">
|
||||
<NuxtLink
|
||||
:to="slide.link"
|
||||
class="relative max-sm:hidden"
|
||||
>
|
||||
<NuxtImg
|
||||
class="aspect-square w-[110px] lg:w-[120px] translate-y-[-75px] lg:translate-y-[-82px] absolute left-1/2 -translate-x-1/2 z-10"
|
||||
:style="{
|
||||
filter: 'drop-shadow(0px 0px 20px rgba(0, 0, 0, 0.4))',
|
||||
}"
|
||||
src="/img/heymlz/heymlz-seat.gif"
|
||||
/>
|
||||
|
||||
<Button
|
||||
variant="primary"
|
||||
class="max-sm:hidden max-lg:typo-label-xs px-7 rounded-full hover:bg-transparent"
|
||||
class="max-lg:typo-label-xs px-12 rounded-full hover:bg-blue-500 hover:text-white"
|
||||
>
|
||||
مشاهده
|
||||
</Button>
|
||||
@@ -270,7 +286,9 @@ onUnmounted(() => {
|
||||
@click="swiper_instance?.slidePrev()"
|
||||
class="relative"
|
||||
>
|
||||
<div class="size-8 blur-xl bg-white absolute ping-animation max-sm:hidden"></div>
|
||||
<div
|
||||
class="size-8 blur-xl bg-white absolute ping-animation max-sm:hidden"
|
||||
></div>
|
||||
<Icon
|
||||
class="**:stroke-white cursor-pointer size-6 md:size-8"
|
||||
name="ci:arrow-right"
|
||||
@@ -289,7 +307,9 @@ onUnmounted(() => {
|
||||
@click="swiper_instance?.slideNext()"
|
||||
class="relative"
|
||||
>
|
||||
<div class="size-8 blur-xl bg-white absolute ping-animation max-sm:hidden"></div>
|
||||
<div
|
||||
class="size-8 blur-xl bg-white absolute ping-animation max-sm:hidden"
|
||||
></div>
|
||||
<Icon
|
||||
class="**:stroke-white cursor-pointer size-6 md:size-8"
|
||||
name="ci:arrow-left"
|
||||
|
||||
@@ -14,12 +14,12 @@ const activeSlideVideo = ref<"left" | "right" | "none">("none");
|
||||
const draggableEl = ref<HTMLElement | null>(null);
|
||||
const previewContainerEl = ref<HTMLElement | null>(null);
|
||||
|
||||
const heymlzElement = useTemplateRef<HTMLDivElement>("heymlzElement");
|
||||
const heymlzElementIsVisible = useElementVisibility(heymlzElement, {
|
||||
rootMargin: "0px 0px -40% 0px",
|
||||
});
|
||||
// const heymlzElement = useTemplateRef<HTMLDivElement>("heymlzElement");
|
||||
// const heymlzElementIsVisible = useElementVisibility(heymlzElement, {
|
||||
// rootMargin: "0px 0px -40% 0px",
|
||||
// });
|
||||
|
||||
const showHeymlzAnimation = ref(false);
|
||||
// const showHeymlzAnimation = ref(false);
|
||||
|
||||
const { x: dragAxisX } = useDraggable(draggableEl, {
|
||||
initialValue: { x: 0, y: 0 },
|
||||
@@ -28,22 +28,22 @@ const { x: dragAxisX } = useDraggable(draggableEl, {
|
||||
|
||||
// watch
|
||||
|
||||
watch(
|
||||
heymlzElementIsVisible,
|
||||
(newValue) => {
|
||||
if (newValue) {
|
||||
setTimeout(() => {
|
||||
showHeymlzAnimation.value = true;
|
||||
setTimeout(() => {
|
||||
showHeymlzAnimation.value = false;
|
||||
}, 3200);
|
||||
}, 400);
|
||||
}
|
||||
},
|
||||
{
|
||||
once: true,
|
||||
}
|
||||
);
|
||||
// watch(
|
||||
// heymlzElementIsVisible,
|
||||
// (newValue) => {
|
||||
// if (newValue) {
|
||||
// setTimeout(() => {
|
||||
// showHeymlzAnimation.value = true;
|
||||
// setTimeout(() => {
|
||||
// showHeymlzAnimation.value = false;
|
||||
// }, 3200);
|
||||
// }, 400);
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// once: true,
|
||||
// }
|
||||
// );
|
||||
|
||||
watch(
|
||||
() => clipPathPercent.value,
|
||||
@@ -64,7 +64,7 @@ watch(
|
||||
const clientRect = previewContainerEl.value?.getBoundingClientRect()!;
|
||||
const percent = clientRect.width / 100;
|
||||
const clipPercent = (newValue + draggableEl.value!.clientWidth / 2 - clientRect.x - 8) / percent;
|
||||
if (clipPercent >= 5 && clipPercent <= 95) {
|
||||
if (clipPercent >= 1 && clipPercent <= 99) {
|
||||
clipPathPercent.value = clipPercent;
|
||||
}
|
||||
}
|
||||
@@ -72,7 +72,7 @@ watch(
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container mb-40 lg:mb-40 max-lg:mt-20 lg:-mt-32">
|
||||
<div class="container select-none mb-40 lg:mb-40 max-lg:mt-20 lg:-mt-32">
|
||||
<div>
|
||||
<div class="flex flex-col items-center gap-3 mb-10 lg:mb-16">
|
||||
<span class="typo-p-sm md:typo-p-md text-slate-500"> مقایسه محصولات </span>
|
||||
@@ -86,8 +86,7 @@ watch(
|
||||
<NuxtImg
|
||||
v-if="activeSlideVideo !== 'right'"
|
||||
:src="homeData!.difreance_section.image1"
|
||||
:class="showHeymlzAnimation ? 'brightness-25 blur-sm' : 'brightness-[95%] blur-[0px]'"
|
||||
class="select-none absolute size-full object-cover transition-[filter] duration-250"
|
||||
class="select-none absolute size-full object-cover transition-[filter] duration-250 brightness-[95%]"
|
||||
:alt="homeData!.difreance_section.title1"
|
||||
/>
|
||||
<video
|
||||
@@ -109,8 +108,7 @@ watch(
|
||||
<NuxtImg
|
||||
v-if="activeSlideVideo !== 'left'"
|
||||
:src="homeData!.difreance_section.image2"
|
||||
:class="showHeymlzAnimation ? 'brightness-25 blur-sm' : 'brightness-[95%] blur-[0px]'"
|
||||
class="overlay-image select-none absolute object-cover size-full transition-[filter] duration-250"
|
||||
class="overlay-image select-none absolute object-cover size-full transition-[filter] duration-250 brightness-[95%]"
|
||||
:alt="homeData!.difreance_section.title2"
|
||||
/>
|
||||
<video
|
||||
@@ -124,47 +122,28 @@ watch(
|
||||
/>
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
name="fade"
|
||||
:duration="250"
|
||||
>
|
||||
<NuxtImg
|
||||
v-if="showHeymlzAnimation"
|
||||
src="/img/heymlz/heymlz-pullingg.gif"
|
||||
class="size-[250px] sm:size-[400px] absolute translate-x-[-62px] sm:translate-x-[-107px] z-10 top-[50%] -translate-y-1/2"
|
||||
:style="{
|
||||
left: `${clipPathPercent}%`,
|
||||
filter: 'drop-shadow(0px 0px 20px rgba(0, 0, 0, 0.3))',
|
||||
}"
|
||||
/>
|
||||
</Transition>
|
||||
|
||||
<div
|
||||
:style="{
|
||||
left: `${clipPathPercent}%`,
|
||||
}"
|
||||
:class="[
|
||||
activeSlideVideo !== 'none' ? 'opacity-10' : '',
|
||||
showHeymlzAnimation ? 'bg-neutral-200' : 'bg-black',
|
||||
]"
|
||||
class="select-none w-[5px] sm:w-2 h-full absolute left-0 flex items-center justify-center transition-opacity duration-250"
|
||||
class="select-none w-[5px] sm:w-2 bg-black h-full absolute left-0 flex items-center justify-center transition-opacity duration-250"
|
||||
>
|
||||
<div
|
||||
ref="draggableEl"
|
||||
:class="showHeymlzAnimation ? 'bg-neutral-200' : 'bg-black'"
|
||||
class="touch-none cursor-grab hover:scale-115 transition-transform rounded-full absolute size-9 sm:size-11 flex items-center justify-center"
|
||||
class="touch-none cursor-grab bg-black hover:scale-115 transition-transform rounded-full absolute size-9 sm:size-11 flex items-center justify-center"
|
||||
>
|
||||
<Icon
|
||||
name="ci:arrows"
|
||||
class="transition-all size-5 sm:size-6"
|
||||
:class="showHeymlzAnimation ? '**:stroke-black' : '**:stroke-white'"
|
||||
class="transition-all size-5 sm:size-6 **:stroke-white"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
:class="showHeymlzAnimation ? 'opacity-0' : 'opacity-100'"
|
||||
class="absolute bottom-0 p-6 md:p-10 w-full flex justify-between items-end transition-opacity"
|
||||
>
|
||||
<div
|
||||
|
||||
@@ -10,7 +10,7 @@ import { useAuth } from "~/composables/api/auth/useAuth";
|
||||
|
||||
// provide-inject
|
||||
|
||||
const { isOpen } = inject("isOpen") as any;
|
||||
const { isOpen, closeChat } = inject("isOpen") as any;
|
||||
|
||||
// state
|
||||
|
||||
@@ -128,7 +128,7 @@ whenever(
|
||||
<Transition :name="isMobile ? 'fade-down' : 'fade-right-to-left'">
|
||||
<div
|
||||
v-if="isOpen"
|
||||
class="fixed z-50 max-xs:inset-0 xs:right-8 xs:bottom-8 w-full xs:w-[450px] transition-all duration-500 overflow-hidden h-svh xs:h-[700px] xs:rounded-250 shadow-2xl shadow-black/30 pt-[40px] bg-white"
|
||||
class="fixed z-9999 max-xs:inset-0 xs:right-8 xs:bottom-8 w-full xs:w-[450px] transition-all duration-500 overflow-hidden h-svh xs:h-[700px] xs:rounded-250 shadow-2xl shadow-black/30 pt-[40px] bg-white"
|
||||
>
|
||||
<CloseButton :disabled="!!isCreateMessagePending" />
|
||||
|
||||
@@ -185,7 +185,7 @@ whenever(
|
||||
class="w-full h-full flex items-center justify-center absolute inset-0"
|
||||
>
|
||||
<NuxtImg
|
||||
class="size-[250px] drop-shadow-2xl"
|
||||
class="size-[225px] sm:size-[250px] drop-shadow-2xl"
|
||||
src="/img/heymlz/heymlz-small-idle.gif"
|
||||
alt=""
|
||||
/>
|
||||
@@ -197,19 +197,32 @@ whenever(
|
||||
v-else
|
||||
>
|
||||
<NuxtImg
|
||||
class="size-[250px] drop-shadow-2xl"
|
||||
class="size-[225px] sm:size-[250px] drop-shadow-2xl"
|
||||
src="/img/heymlz/heymlz-small-idle.gif"
|
||||
alt=""
|
||||
/>
|
||||
<div class="flex flex-col gap-4 items-center">
|
||||
<span class="text-center typo-p-xl font-bold">سلام دوست عزیز!</span>
|
||||
<p class="text-center typo-p-md">
|
||||
من میتونم هر سوالی رو درمورد این محصول جواب بدم اگه میخوای شروع کنیم روی دکمه زیر کلیک کن
|
||||
من میتونم هر سوالی رو درمورد این محصول جواب بدم اگه میخوای شروع کنیم وارد وبسایت شو
|
||||
</p>
|
||||
</div>
|
||||
<NuxtLink to="/signin">
|
||||
<Button class="mt-8 rounded-full px-10">ورود به فروشگاه</Button>
|
||||
</NuxtLink>
|
||||
<div class="flex-center gap-4">
|
||||
<Button
|
||||
@click="closeChat"
|
||||
class="mt-8 rounded-full px-10"
|
||||
>
|
||||
بستن
|
||||
</Button>
|
||||
<NuxtLink to="/signin">
|
||||
<Button
|
||||
variant="outlined"
|
||||
class="mt-8 rounded-full px-10"
|
||||
>
|
||||
ورود به فروشگاه
|
||||
</Button>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
@@ -62,7 +62,7 @@ const limitedComments = computed(() => {
|
||||
>
|
||||
<textarea
|
||||
:disabled="!token"
|
||||
class="w-full min-h-[200px] field-sizing-content rounded-xl bg-white p-4 border border-slate-200 placeholder:text-xs lg:placeholder:text-sm placeholder:font-normal"
|
||||
class="w-full min-h-[125px] resize-none sm:min-h-[200px] field-sizing-content rounded-xl bg-white p-4 border border-slate-200 placeholder:text-xs lg:placeholder:text-sm placeholder:font-normal"
|
||||
v-model="userComment"
|
||||
placeholder="نظر خود را بنویسید..."
|
||||
/>
|
||||
@@ -98,15 +98,6 @@ const limitedComments = computed(() => {
|
||||
:username="'منصور مرزبان'"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="h-[400px] lg:flex-grow w-full border-[0.5px] flex-col-center border-slate-200 bg-white rounded-xl"
|
||||
>
|
||||
<NuxtImg
|
||||
src="/img/heymlz/heymlz-contact-us.gif"
|
||||
class="w-[200px] lg:w-[300px] translate-y-[-25px]"
|
||||
/>
|
||||
<span class="text-xl text-black font-semibold translate-y-[-25px]"> هیچ نظری ثبت نشده است </span>
|
||||
</div>
|
||||
<div
|
||||
v-if="comments!.count > 0"
|
||||
class="flex items-center justify-center w-full"
|
||||
@@ -127,6 +118,16 @@ const limitedComments = computed(() => {
|
||||
نمایش همه
|
||||
</Button>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="h-[400px] lg:flex-grow w-full border-[0.5px] flex-col-center border-slate-200 bg-white rounded-xl"
|
||||
>
|
||||
<NuxtImg
|
||||
src="/img/heymlz/heymlz-contact-us.gif"
|
||||
class="w-[200px] lg:w-[300px] translate-y-[-25px]"
|
||||
/>
|
||||
<span class="text-xl text-black font-semibold translate-y-[-25px]"> هیچ نظری ثبت نشده است </span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -162,7 +162,7 @@ watch(
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="py-8 typo-sm max-sm:leading-[175%] sm:typo-p-md text-slate-500 text-justify [&_a]:text-blue-400 [&_strong]:font-bold [&_u]:text-red-400"
|
||||
class="py-8 leading-[200%] max -sm:text-sm text-slate-500 text-justify [&_a]:text-blue-400 [&_strong]:font-bold [&_u]:text-red-400"
|
||||
v-html="product!.description"
|
||||
/>
|
||||
|
||||
|
||||
@@ -39,8 +39,8 @@ const useGetProducts = (params?: ComputedRef<GetProductsFilters>) => {
|
||||
category: params?.category,
|
||||
price_gte: params?.price_gte,
|
||||
price_lte: params?.price_lte,
|
||||
offset: Number(params?.page) * 9 - 9,
|
||||
limit: 9
|
||||
offset: Number(params?.page) * 12 - 12,
|
||||
limit: 12
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -36,7 +36,7 @@ onMounted(() => {
|
||||
<ProductsShowcase class="lg:mb-12" />
|
||||
<ProductsGrid
|
||||
title="محصولات پرفروش"
|
||||
:products="[...homeData!.products,...homeData!.products]"
|
||||
:products="homeData!.products"
|
||||
/>
|
||||
<Categories class="mt-12" />
|
||||
<Brands />
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import ChatButton from "~/components/product/ChatBox/ChatButton.vue";
|
||||
import useGetProduct from "~/composables/api/product/useGetProduct";
|
||||
import useGetComments from "~/composables/api/product/useGetComments";
|
||||
import ProductsSlider from "~/components/global/product-detail/ProductsSlider.vue";
|
||||
|
||||
// state
|
||||
|
||||
@@ -52,7 +53,7 @@ if (productResponse.isError || commentsResponse.isError) {
|
||||
<ProductVideo v-model:showChatButton="showChatButton" />
|
||||
<ProductComments />
|
||||
<ProductDetails />
|
||||
<ProductsGrid
|
||||
<ProductsSlider
|
||||
title="محصولات مشابه"
|
||||
:products="product!.related_products"
|
||||
/>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
Reference in New Issue
Block a user