feat(slider): add slider feature

This commit is contained in:
2025-11-01 17:32:28 +03:00
parent 0cccc7e3d7
commit 3049bd3101
37 changed files with 685 additions and 256 deletions

View File

@@ -1,100 +0,0 @@
<template>
<div v-if="slides.length > 0" class="app-banner px-4">
<Swiper
class="select-none"
:slides-per-view="1"
:space-between="50"
pagination
:pagination="{ clickable: true }"
@swiper="onSwiper"
@slideChange="onSlideChange"
>
<SwiperSlide v-for="slide in slides" :key="slide.id">
<RouterLink
v-if="slide?.link?.type === 'category'"
:to="{name: 'product.categories.show', params: {category_id: slide.link.value.category_id}}"
@click="sliderClick(slide)"
>
<img :src="slide.image" :alt="slide.title">
</RouterLink>
<RouterLink
v-else-if="slide?.link?.type === 'product'"
:to="{name: 'product.show', params: {id: slide.link.value.product_id}}"
@click="sliderClick(slide)"
>
<img :src="slide.image" :alt="slide.title">
</RouterLink>
<img
v-else-if="slide?.link?.type === 'url'"
:src="slide.image"
:alt="slide.title"
@click="openExternalLink(slide.link.value.url, slide)"
>
<img v-else :src="slide.image" :alt="slide.title"/>
</SwiperSlide>
</Swiper>
</div>
</template>
<script setup>
import {Swiper, SwiperSlide} from 'swiper/vue';
import 'swiper/css';
import 'swiper/css/navigation';
import {onMounted, ref} from "vue";
import {fetchBanner} from "@/utils/ftch.js";
import {YA_METRIKA_GOAL} from "@/constants/yaMetrikaGoals.js";
import {useYaMetrikaStore} from "@/stores/yaMetrikaStore.js";
const yaMetrika = useYaMetrikaStore();
const slides = ref([]);
const onSwiper = (swiper) => {
console.log(swiper);
};
const onSlideChange = () => {
console.log('slide change');
};
function sliderClick(slide) {
yaMetrika.reachGoal(YA_METRIKA_GOAL.SLIDER_HOME_CLICK, {
banner: slide.title,
});
}
function openExternalLink(link, slide) {
if (! link) {
return;
}
yaMetrika.reachGoal(YA_METRIKA_GOAL.SLIDER_HOME_CLICK, {
banner: slide.title,
});
window.Telegram.WebApp.openLink(link, {try_instant_view: false});
}
onMounted(async () => {
const response = await fetchBanner();
slides.value = response.data;
})
</script>
<style>
.app-banner .swiper-horizontal > .swiper-pagination-bullets {
position: relative;
bottom: 10px;
}
.app-banner .swiper-horizontal .swiper-slide {
display: flex;
align-items: center;
justify-content: center;
}
.app-banner .swiper-horizontal .swiper-slide img {
border-radius: var(--radius-box);
}
</style>

View File

@@ -0,0 +1,180 @@
<template>
<div
v-if="sliders.mainpage_slider.is_enabled && sliders.mainpage_slider.slides.length > 0"
class="app-banner"
:class="classList"
>
<Swiper
:effect="slideEffect"
class="select-none"
:slides-per-view="1"
:space-between="sliders.mainpage_slider.space_between"
:pagination="pagination"
:lazy="true"
:modules="modules"
:scrollbar="scrollbar"
:free-mode="sliders.mainpage_slider.free_mode"
:loop="sliders.mainpage_slider.loop"
:autoplay="autoplay"
@swiper="onSwiper"
@slideChange="onSlideChange"
>
<SwiperSlide v-for="slide in sliders.mainpage_slider.slides" :key="slide.id">
<RouterLink
v-if="slide?.link?.type === 'category'"
:to="{name: 'product.categories.show', params: {category_id: slide.link.value.category_id}}"
@click="sliderClick(slide)"
>
<img :src="slide.image" :alt="slide.title" loading="lazy">
</RouterLink>
<RouterLink
v-else-if="slide?.link?.type === 'product'"
:to="{name: 'product.show', params: {id: slide.link.value.product_id}}"
@click="sliderClick(slide)"
>
<img :src="slide.image" :alt="slide.title" loading="lazy">
</RouterLink>
<img
v-else-if="slide?.link?.type === 'url'"
:src="slide.image"
:alt="slide.title"
loading="lazy"
@click="openExternalLink(slide.link.value.url, slide)"
>
<img v-else :src="slide.image" :alt="slide.title" loading="lazy"/>
</SwiperSlide>
</Swiper>
</div>
</template>
<script setup>
import {Swiper, SwiperSlide} from 'swiper/vue';
import 'swiper/css';
import 'swiper/css/navigation';
import {YA_METRIKA_GOAL} from "@/constants/yaMetrikaGoals.js";
import {useYaMetrikaStore} from "@/stores/yaMetrikaStore.js";
import {EffectCoverflow, EffectCards, EffectCube, EffectFlip, Scrollbar, Autoplay} from 'swiper/modules';
import {computed, onMounted} from "vue";
import {useSlidersStore} from "@/stores/SlidersStore.js";
const sliders = useSlidersStore();
const yaMetrika = useYaMetrikaStore();
const modules = [
Autoplay,
EffectCards,
EffectFlip,
EffectCube,
Scrollbar,
EffectCoverflow,
];
const classList = computed(() => {
if (sliders.mainpage_slider.effect === 'cards') {
return ['px-8'];
}
if (sliders.mainpage_slider.effect === 'flip') {
return ['px-4', 'pb-4', 'pt-4'];
}
if (sliders.mainpage_slider.effect === 'cube') {
return ['px-4', 'pb-10'];
}
return ['px-4'];
});
const onSwiper = (swiper) => {
console.log(swiper);
};
const onSlideChange = () => {
console.log('slide change');
};
const slideEffect = computed(() => {
if (sliders.mainpage_slider.effect === 'slide') {
return null;
}
return sliders.mainpage_slider.effect;
});
const pagination = computed(() => {
if (sliders.mainpage_slider.pagination) {
return {
clickable: true, dynamicBullets: false,
};
}
return false;
});
const scrollbar = computed(() => {
if (sliders.mainpage_slider.scrollbar) {
return {
hide: true,
};
}
return false;
});
const autoplay = computed(() => {
if (sliders.mainpage_slider.autoplay) {
return {
delay: 3000,
reverseDirection: false,
};
}
return false;
});
function sliderClick(slide) {
yaMetrika.reachGoal(YA_METRIKA_GOAL.SLIDER_HOME_CLICK, {
banner: slide.title,
});
}
function openExternalLink(link, slide) {
if (!link) {
return;
}
yaMetrika.reachGoal(YA_METRIKA_GOAL.SLIDER_HOME_CLICK, {
banner: slide.title,
});
window.Telegram.WebApp.openLink(link, {try_instant_view: false});
}
onMounted(() => {
console.debug('[Mainpage Slider] Status: ', sliders.mainpage_slider);
});
</script>
<style>
.app-banner {
aspect-ratio: 740 / 400;
}
.app-banner .swiper {
overflow: visible;
}
.app-banner .swiper-horizontal > .swiper-pagination-bullets {
position: relative;
bottom: 10px;
}
.app-banner .swiper-horizontal .swiper-slide {
display: flex;
align-items: center;
justify-content: center;
}
.app-banner .swiper-horizontal .swiper-slide img {
border-radius: var(--radius-box);
}
</style>