feat: product options, speedup home page, themes
This commit is contained in:
@@ -7,22 +7,13 @@
|
||||
Каталог
|
||||
</RouterLink>
|
||||
|
||||
<RouterLink v-for="category in categories" class="btn" :to="`/category/${category.id}`">
|
||||
<RouterLink v-for="category in categoriesStore.topCategories" class="btn" :to="`/category/${category.id}`">
|
||||
{{ category.name }}
|
||||
</RouterLink>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {onMounted, ref} from "vue";
|
||||
import ftch from "../utils/ftch.js";
|
||||
|
||||
const categories = ref([]);
|
||||
|
||||
onMounted(async () => {
|
||||
const {data} = await ftch('categoriesList', {
|
||||
perPage: 7,
|
||||
});
|
||||
categories.value = data;
|
||||
});
|
||||
import {useCategoriesStore} from "@/stores/CategoriesStore.js";
|
||||
const categoriesStore = useCategoriesStore();
|
||||
</script>
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<label
|
||||
v-for="value in model.values"
|
||||
class="group relative flex items-center justify-center rounded-md border border-gray-300 bg-white p-2 has-checked:border-indigo-600 has-checked:bg-indigo-600 has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-indigo-600 has-disabled:border-gray-400 has-disabled:bg-gray-200 has-disabled:opacity-25">
|
||||
|
||||
class="group relative flex items-center justify-center btn btn-soft btn-secondary btn-sm"
|
||||
:class="value.selected ? 'btn-active' : ''"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
:value="value.product_option_value_id"
|
||||
@@ -36,6 +37,8 @@ function select(toggledValue) {
|
||||
}
|
||||
});
|
||||
|
||||
model.value.value = model.value.values.filter(item => item.selected === true);
|
||||
|
||||
emit('update:modelValue', model.value);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<label
|
||||
v-for="value in model.values"
|
||||
class="group relative flex items-center justify-center rounded-md border border-gray-300 bg-base-200 p-2 has-checked:border-indigo-600 has-checked:bg-primary has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-indigo-600 has-disabled:border-gray-400 has-disabled:bg-gray-200 has-disabled:opacity-25">
|
||||
class="group relative flex items-center justify-center btn btn-soft btn-secondary btn-sm"
|
||||
:class="value.selected ? 'btn-active' : ''"
|
||||
>
|
||||
|
||||
<input
|
||||
type="radio"
|
||||
@@ -14,7 +16,7 @@
|
||||
class="absolute inset-0 appearance-none focus:outline-none disabled:cursor-not-allowed"
|
||||
/>
|
||||
|
||||
<span class="text-xs font-medium group-has-checked:text-white">
|
||||
<span class="text-xs font-medium">
|
||||
{{ value.name }}<span v-if="value.price"> ({{ value.price_prefix }}{{ value.price }})</span>
|
||||
</span>
|
||||
</label>
|
||||
@@ -33,6 +35,8 @@ function select(selectedValue) {
|
||||
value.selected = (value === selectedValue);
|
||||
});
|
||||
|
||||
model.value.value = selectedValue;
|
||||
|
||||
emit('update:modelValue', model);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
class="select"
|
||||
@change="onChange"
|
||||
>
|
||||
<option value="" disabled>Выберите значение</option>
|
||||
<option
|
||||
v-for="value in model.values"
|
||||
:key="value.product_option_value_id"
|
||||
@@ -30,6 +31,8 @@ function onChange(event) {
|
||||
value.selected = (value.product_option_value_id === selectedId);
|
||||
});
|
||||
|
||||
model.value.value = model.value.values.find(value => value.product_option_value_id === selectedId);
|
||||
|
||||
emit('update:modelValue', model.value);
|
||||
}
|
||||
</script>
|
||||
|
||||
75
spa/src/components/ProductsList.vue
Normal file
75
spa/src/components/ProductsList.vue
Normal file
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<div class="mx-auto max-w-2xl px-4 py-4 sm:px-6 sm:py-6 lg:max-w-7xl lg:px-8">
|
||||
|
||||
<div v-if="isLoading" class="grid grid-cols-2 gap-x-6 gap-y-10 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 xl:gap-x-8">
|
||||
<div v-for="n in 8" :key="n" class="animate-pulse space-y-2">
|
||||
<div class="aspect-square bg-gray-200 rounded-md"></div>
|
||||
<div class="h-4 bg-gray-200 rounded w-3/4"></div>
|
||||
<div class="h-4 bg-gray-200 rounded w-1/2"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template v-else>
|
||||
<h2 class="text-lg font-bold mb-5 text-center">{{ meta.currentCategoryName }}</h2>
|
||||
|
||||
<div v-if="products.length > 0" class="grid grid-cols-2 gap-x-6 gap-y-10 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 xl:gap-x-8">
|
||||
<RouterLink
|
||||
v-for="product in products"
|
||||
:key="product.id"
|
||||
class="group"
|
||||
:to="`/product/${product.id}`"
|
||||
@click="haptic"
|
||||
>
|
||||
<ProductImageSwiper :images="product.images"/>
|
||||
<h3 class="mt-4 text-sm">{{ product.name }}</h3>
|
||||
<p class="mt-1 text-lg font-medium">{{ product.price }}</p>
|
||||
</RouterLink>
|
||||
</div>
|
||||
|
||||
<NoProducts v-else/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref} from "vue";
|
||||
import {useHapticFeedback} from 'vue-tg';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import NoProducts from "../components/NoProducts.vue";
|
||||
import ProductImageSwiper from "../components/ProductImageSwiper.vue";
|
||||
|
||||
const hapticFeedback = useHapticFeedback();
|
||||
|
||||
const props = defineProps({
|
||||
products: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
|
||||
meta: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
|
||||
isLoading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
}
|
||||
});
|
||||
|
||||
function haptic() {
|
||||
window.Telegram.WebApp.HapticFeedback.selectionChanged();
|
||||
}
|
||||
|
||||
const carouselRef = ref();
|
||||
let lastScrollLeft = 0;
|
||||
function onScroll(e) {
|
||||
const scrollLeft = e.target.scrollLeft;
|
||||
const delta = Math.abs(scrollLeft - lastScrollLeft);
|
||||
|
||||
if (delta > 30) {
|
||||
hapticFeedback.impactOccurred('soft');
|
||||
lastScrollLeft = scrollLeft;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class="flex items-center text-center">
|
||||
<button class="btn btn-lg btn-neutral" @click="inc">-</button>
|
||||
<div class="w-10 h-10 flex items-center justify-center bg-neutral font-bold">{{ model }}</div>
|
||||
<button class="btn btn-lg btn-neutral" @click="dec">+</button>
|
||||
<button class="btn btn-lg" @click="inc">-</button>
|
||||
<div class="w-10 h-10 flex items-center justify-center font-bold">{{ model }}</div>
|
||||
<button class="btn btn-lg" @click="dec">+</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user