wip: product filters

This commit is contained in:
2025-10-03 22:32:31 +03:00
parent a051ff545e
commit da1b71fc89
48 changed files with 721 additions and 235 deletions

View File

@@ -1,12 +1,16 @@
<template>
<div class="app-container h-full">
<FullscreenViewport v-if="platform === 'ios' || platform === 'android'"/>
<RouterView v-slot="{ Component, route }">
<Transition name="route" appear>
<component :is="Component" :key="route.fullPath"/>
</Transition>
</RouterView>
<CartButton v-if="settings.store_enabled"/>
<header class="app-header w-full" v-if="platform === 'ios'"></header>
<section class="safe-top">
<FullscreenViewport v-if="platform === 'ios' || platform === 'android'"/>
<RouterView v-slot="{ Component, route }">
<Transition name="route" appear>
<component :is="Component" :key="route.fullPath"/>
</Transition>
</RouterView>
<CartButton v-if="settings.store_enabled"/>
</section>
</div>
</template>
@@ -50,3 +54,21 @@ watch(
{immediate: true}
);
</script>
<style scoped>
/* route transitions */
.route-enter-active,
.route-leave-active {
transition: opacity 0.25s ease, transform 0.25s ease;
}
.route-enter-from {
opacity: 0;
transform: translateY(10px);
}
.route-leave-to {
opacity: 0;
transform: translateY(-10px);
}
</style>

View File

@@ -2,7 +2,7 @@
<div v-if="isCartBtnShow" class="fixed right-2 bottom-30 z-50 opacity-90">
<div class="indicator">
<span class="indicator-item indicator-top indicator-start badge badge-secondary">{{ cart.productsCount }}</span>
<button class="btn btn-primary btn-lg btn-circle" @click="openCart">
<button class="btn btn-primary btn-xl btn-circle" @click="openCart">
<span v-if="cart.isLoading" class="loading loading-spinner"></span>
<template v-else>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">

View File

@@ -11,6 +11,7 @@
v-for="category in categoriesStore.topCategories"
class="btn btn-md max-w-[12rem]"
:to="{name: 'product.categories.show', params: {category_id: category.id}}"
@click="onCategoryClick"
>
<span class="overflow-hidden text-ellipsis whitespace-nowrap">{{ category.name }}</span>
</RouterLink>
@@ -20,4 +21,8 @@
<script setup>
import {useCategoriesStore} from "@/stores/CategoriesStore.js";
const categoriesStore = useCategoriesStore();
function onCategoryClick() {
window.Telegram.WebApp.HapticFeedback.impactOccurred('soft');
}
</script>

View File

@@ -1,5 +1,5 @@
<template>
<div style="z-index: 99999" class="fixed top-0 left-0 w-full h-full bg-base-100">
<div style="z-index: 99999" class="fixed left-0 w-full h-full bg-base-100 top-0">
<div class="flex flex-col items-center justify-center h-full">
<span class="loading loading-infinity loading-xl"></span>
<h1>{{ text }}</h1>

View File

@@ -1,5 +1,5 @@
<template>
<div class="search-wrapper px-5 w-full">
<div class="search-wrapper w-full">
<label class="input w-full">
<svg class="h-[1em] opacity-50" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g
@@ -15,7 +15,7 @@
</svg>
<input
readonly
class="grow input-lg"
class="grow input-lg w-full"
placeholder="Поиск по магазину"
@click="showSearchPage"
/>
@@ -32,6 +32,7 @@ const router = useRouter();
function showSearchPage() {
router.push({name: 'search'});
useSearchStore().reset();
window.Telegram.WebApp.HapticFeedback.impactOccurred('medium');
}
</script>

View File

@@ -21,7 +21,7 @@ export const useSettingsStore = defineStore('settings', {
'--product_list_title_max_lines': 2,
}
},
noMoreProductsMessage: '🔚 Ну всё, разгрузили всё, что было. Даже кладовщика разбудить не удалось.',
noMoreProductsMessage: '🔚 Это всё по текущему запросу. Попробуйте уточнить фильтры или поиск.',
}),
actions: {

View File

@@ -30,7 +30,7 @@ html {
}
.app-container {
/*padding-top: var(--tg-safe-area-inset-top);*/
/*padding-top: calc(var(--tg-content-safe-area-inset-top) + var(--tg-safe-area-inset-top));*/
padding-bottom: var(--tg-safe-area-inset-bottom);
padding-left: var(--tg-safe-area-inset-left);
padding-right: var(--tg-safe-area-inset-right);
@@ -40,6 +40,18 @@ html {
padding-top: calc(var(--tg-content-safe-area-inset-top) + var(--tg-safe-area-inset-top));
}
.app-header {
z-index: 100;
position: fixed;
background: var(--color-primary);
padding-top: calc(var(--tg-content-safe-area-inset-top) + var(--tg-safe-area-inset-top));
display: flex;
justify-content: center;
align-items: center;
box-shadow: 0px -1px 3px 1px #000000;
color: white;
}
.swiper-pagination-bullets > .swiper-pagination-bullet {
background-color: red;
color: red;

View File

@@ -1,5 +1,5 @@
<template>
<div class="max-w-3xl mx-auto p-4 space-y-6 pb-30 safe-top">
<div class="max-w-3xl mx-auto p-4 space-y-6 pb-30">
<h2 class="text-2xl">
Корзина
<span v-if="cart.isLoading" class="loading loading-spinner loading-md"></span>

View File

@@ -1,5 +1,5 @@
<template>
<div class="mx-auto max-w-2xl px-4 py-4 sm:px-6 sm:py-24 lg:max-w-7xl lg:px-8 mb-5 safe-top">
<div class="mx-auto max-w-2xl px-4 py-4 sm:px-6 sm:py-24 lg:max-w-7xl lg:px-8 mb-5">
<h2 class="text-3xl mb-5">Категории</h2>
<div v-if="categoriesStore.isLoading" class="flex flex-col gap-4">

View File

@@ -1,5 +1,5 @@
<template>
<div class="max-w-3xl mx-auto space-y-6 pb-30 safe-top">
<div class="max-w-3xl mx-auto space-y-6 pb-30">
<h2 class="text-2xl text-center">
Оформление заказа
</h2>

View File

@@ -1,12 +1,14 @@
<template>
<div ref="goodsRef" class="safe-top">
<div ref="goodsRef">
<CategoriesInline/>
<div class="flex justify-between px-5">
<button @click="showFilters" class="btn">
<IconFunnel/>
</button>
<SearchInput/>
<div class="px-5 fixed z-50 w-full opacity-90" style="bottom: var(--tg-safe-area-inset-bottom);">
<div class="bg-base-300 flex justify-between p-2 rounded-xl shadow-md">
<button @click="showFilters" class="btn mr-3">
<IconFunnel/>
</button>
<SearchInput/>
</div>
</div>
<ProductsList/>
@@ -28,7 +30,6 @@ import Filters from "@/components/ProductFilters/Filters.vue";
import {onMounted, onUnmounted, ref} from "vue";
import {useProductsStore} from "@/stores/ProductsStore.js";
import IconFunnel from "@/components/Icons/IconFunnel.vue";
import {useProductFiltersStore} from "@/stores/ProductFiltersStore.js";
import {FILTERS_MAIN_PAGE_DEFAULT} from "@/components/ProductFilters/filters.js";
import {useRoute} from "vue-router";
@@ -39,11 +40,13 @@ const isFiltersShow = ref(false);
const backButton = window.Telegram.WebApp.BackButton;
function showFilters() {
window.Telegram.WebApp.HapticFeedback.impactOccurred('soft');
isFiltersShow.value = true;
backButton.show();
}
function closeFilters() {
window.Telegram.WebApp.HapticFeedback.impactOccurred('rigid');
isFiltersShow.value = false;
}

View File

@@ -1,5 +1,5 @@
<template>
<div class="safe-top max-w-3xl mx-auto p-4 space-y-6 pb-30 flex flex-col items-center h-full justify-center">
<div class="max-w-3xl mx-auto p-4 space-y-6 pb-30 flex flex-col items-center h-full justify-center">
<div class="flex flex-col justify-center items-center px-5">
<div class="mb-3">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-25 text-success">

View File

@@ -1,5 +1,5 @@
<template>
<div class="safe-top">
<div>
<div>
<swiper-container ref="swiperEl" init="false">
<swiper-slide

View File

@@ -1,6 +1,10 @@
<template>
<div ref="goodsRef" class="safe-top">
<SearchInput/>
<div ref="goodsRef">
<div class="px-5 fixed z-50 w-full opacity-90" style="bottom: var(--tg-safe-area-inset-bottom);">
<div class="bg-base-300 flex justify-between p-2 rounded-xl shadow-md">
<SearchInput/>
</div>
</div>
<ProductsList/>
</div>
</template>
@@ -11,6 +15,7 @@ import SearchInput from "@/components/SearchInput.vue";
import {onMounted} from "vue";
import {useRoute} from "vue-router";
import {useProductsStore} from "@/stores/ProductsStore.js";
import IconFunnel from "@/components/Icons/IconFunnel.vue";
const route = useRoute();
const productsStore = useProductsStore();

View File

@@ -1,5 +1,5 @@
<template>
<div class="max-w-3xl mx-auto p-4 space-y-6 pb-20 safe-top">
<div class="max-w-3xl mx-auto p-4 space-y-6 pb-20">
<h2 class="text-2xl mb-3">Поиск</h2>
<div class="w-full">