feat: product options, speedup home page, themes
This commit is contained in:
@@ -1,18 +1,24 @@
|
||||
<template>
|
||||
<div ref="goodsRef">
|
||||
<CategoriesInline/>
|
||||
<ProductsList/>
|
||||
<ProductsList
|
||||
:products="productsStore.homeProducts.data"
|
||||
:meta="productsStore.homeProducts.meta"
|
||||
:isLoading="productsStore.isLoading"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref} from "vue";
|
||||
import ProductsList from "@/components/ProductsList.vue";
|
||||
import CategoriesInline from "../components/CategoriesInline.vue";
|
||||
import {useProductsStore} from "@/stores/ProductsStore.js";
|
||||
|
||||
const productsStore = useProductsStore();
|
||||
|
||||
const goodsRef = ref();
|
||||
function scrollToProducts() {
|
||||
goodsRef.value?.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
|
||||
import ProductsList from "./ProductsList.vue";
|
||||
import CategoriesInline from "../components/CategoriesInline.vue";
|
||||
</script>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="pb-10">
|
||||
<div>
|
||||
<ProductImageSwiper :images="product.images"/>
|
||||
|
||||
<!-- Product info -->
|
||||
<div
|
||||
class="mx-auto max-w-2xl px-4 pt-3 pb-16 sm:px-6 lg:grid lg:max-w-7xl lg:grid-cols-3 lg:grid-rows-[auto_auto_1fr] lg:gap-x-8 lg:px-8 lg:pt-16 lg:pb-24">
|
||||
class="mx-auto max-w-2xl px-4 pt-3 pb-16 sm:px-6 lg:grid lg:max-w-7xl lg:grid-cols-3 lg:grid-rows-[auto_auto_1fr] lg:gap-x-8 lg:px-8 lg:pt-16 lg:pb-24 rounded-t-lg">
|
||||
<div class="lg:col-span-2 lg:border-r lg:pr-8">
|
||||
<h1 class="text-2xl font-bold tracking-tight sm:text-3xl">{{ product.name }}</h1>
|
||||
</div>
|
||||
@@ -34,14 +34,21 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-4 pb-10 pt-4 fixed bottom-0 left-0 w-full bg-info-content z-50 flex justify-between gap-2 border-t-1 border-t-success-content">
|
||||
<button
|
||||
class="btn btn-lg flex-1"
|
||||
:class="isInCartNow ? 'btn-success' : 'btn-primary'"
|
||||
@click="actionBtnClick"
|
||||
>
|
||||
{{ buttonText }}
|
||||
</button>
|
||||
<div v-if="product.id" class="px-4 pb-10 pt-4 fixed bottom-0 left-0 w-full bg-base-200 z-50 flex justify-between gap-2 border-t-1 border-t-base-300">
|
||||
<div class="flex-1">
|
||||
<button
|
||||
class="btn btn-lg w-full"
|
||||
:class="isInCartNow ? 'btn-success' : 'btn-primary'"
|
||||
:disabled="canAddToCart === false"
|
||||
@click="actionBtnClick"
|
||||
>
|
||||
<span>{{ buttonText }}</span><br>
|
||||
</button>
|
||||
|
||||
<div v-if="canAddToCart === false" class="text-error text-center text-xs mt-1">
|
||||
Выберите обязательные опции
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Quantity
|
||||
v-if="quantity > 0"
|
||||
@@ -76,6 +83,22 @@ const buttonText = computed(() => {
|
||||
: 'Добавить в корзину'
|
||||
});
|
||||
|
||||
const canAddToCart = computed(() => {
|
||||
if (!product.value || product.value.options === undefined || product.value.options?.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const required = product.value.options.filter(item => {
|
||||
return ['checkbox', 'radio', 'select', 'text', 'textarea'].indexOf(item.type) !== -1
|
||||
&& item.required === true
|
||||
&& !item.value;
|
||||
});
|
||||
|
||||
console.log(required);
|
||||
|
||||
return required.length === 0;
|
||||
});
|
||||
|
||||
const isInCartNow = computed(() => {
|
||||
return cart.hasProduct(productId.value);
|
||||
});
|
||||
@@ -108,17 +131,4 @@ onMounted(async () => {
|
||||
const {data} = await $fetch(`/index.php?route=extension/tgshop/handle&api_action=product_show&id=${productId.value}`);
|
||||
product.value = data;
|
||||
});
|
||||
|
||||
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>
|
||||
</script>
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
<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">{{ productsMeta.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}`">
|
||||
<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 {onMounted, ref} from "vue";
|
||||
import {useHapticFeedback} from 'vue-tg';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import ftch from "../utils/ftch.js";
|
||||
import NoProducts from "../components/NoProducts.vue";
|
||||
import ProductImageSwiper from "../components/ProductImageSwiper.vue";
|
||||
|
||||
const hapticFeedback = useHapticFeedback();
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const categoryId = route.params.id;
|
||||
|
||||
const isLoading = ref(true);
|
||||
const products = ref([]);
|
||||
const productsMeta = ref([]);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
const {data, meta} = await ftch('products', {
|
||||
categoryId: categoryId,
|
||||
});
|
||||
productsMeta.value = meta;
|
||||
products.value = data;
|
||||
isLoading.value = false;
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user