product-card

This commit is contained in:
Veselov 2025-09-16 23:14:47 +03:00
parent 2226fb2abe
commit e2ebf54d56
13 changed files with 77 additions and 35 deletions

View File

@ -0,0 +1,4 @@
import api from '~/api/instance'
export const getProductAttributesDetail = async (productId: number) =>
await api.wc.v3ProductsAttributesDetail(productId)

View File

@ -1,4 +1,4 @@
import api from '~/api/instance'
export const getProductsVariationsList = async (productId: number) =>
await api.wc.v3ProductsVariationsList(productId)
await api.wc.v3ProductsVariationsList(productId, { per_page: 99 })

View File

@ -1,3 +1,4 @@
export * from './getProductAttributesDetail'
export * from './getProductsDetail'
export * from './getProductsList'
export * from './getProductsVariationsList'

View File

@ -1,3 +1,4 @@
export * from './useGetProductAttributesDetail'
export * from './useGetProductsDetail'
export * from './useGetProductsList'
export * from './useGetProductsVariationsList'

View File

@ -0,0 +1,10 @@
import { useQuery } from '@tanstack/vue-query'
import { unref } from 'vue'
import { getProductAttributesDetail } from '~/api/endpoints'
export const useGetProductAttributesDetail = (productId: MaybeRef<number>) => {
return useQuery({
queryKey: ['get-products-detail', productId],
queryFn: () => getProductAttributesDetail(unref(productId)),
})
}

View File

@ -24,11 +24,6 @@ h3 {
@include h3('h3');
}
// Buttons
button {
cursor: pointer;
}
::-webkit-scrollbar {
width: 8px;
}

View File

@ -2,9 +2,9 @@
<div class="product-description">
<h1>{{ productsData?.name }}</h1>
<h2>{{ currentVariant?.price }} <Icon name="ph:currency-rub" /></h2>
<h2>{{ currentVariant?.options[0]?.price }} <Icon name="ph:currency-rub" /></h2>
<ProductVariations />
<ProductVariations v-if="colors?.length > 1" />
<div>
{{ `Цвет: ${t(`colors.${currentColor}`)}` }}
@ -20,12 +20,17 @@
<div class="product-description__sizes">
<div
v-for="size in sizes"
:key="size"
v-for="option in currentVariant?.options"
:key="option"
class="product-description__size"
@click="() => currentSize = size"
@click="() => currentSize = option"
>
<UButton block :label="size" :variant="currentSize === size ? undefined : 'outline'" />
<UButton
block
:label="getAttribute(option?.attributes, 'size')?.option"
:disabled="option?.stock_status === 'outofstock'"
:variant="currentSize === option ? undefined : 'outline'"
/>
</div>
</div>
@ -41,7 +46,7 @@ import { useCurrentProduct, useProduct } from '~/composables'
const { t } = useI18n()
const { productsData, sizes } = useProduct()
const { productsData, colors, getAttribute } = useProduct()
const { currentVariant, currentColor, currentMaterial } = useCurrentProduct()
const currentSize = ref(0)

View File

@ -1,12 +1,12 @@
<template>
<div class="product-variations">
<div
v-for="variation in productsVariationsData"
:key="variation?.id"
@click="() => currentVariantId = variation?.id"
v-for="variation in variations"
:key="variation?.option?.id"
@click="() => currentVariant = variation"
>
<div class="product-variations__variation">
<img width="80" :src="variation?.image?.src" :alt="variation?.image?.src">
<img width="80" :src="variation?.image[0]?.src" :alt="variation?.image[0]?.src">
</div>
</div>
</div>
@ -15,8 +15,8 @@
<script setup lang="ts">
import { useCurrentProduct, useProduct } from '~/composables'
const { productsVariationsData } = useProduct()
const { currentVariantId } = useCurrentProduct()
const { variations } = useProduct()
const { currentVariant } = useCurrentProduct()
</script>
<style lang="scss">

View File

@ -1,31 +1,28 @@
import { useProduct } from '#build/imports'
import { createSharedComposable } from '@vueuse/core'
import { computed, ref, watch } from 'vue'
import { computed, ref } from 'vue'
export const useCurrentProduct = createSharedComposable(() => {
const { defaultVariant, productsData, productsVariationsData, getAttribute } = useProduct()
const { variations, productsData } = useProduct()
const currentVariantId = ref(defaultVariant?.value?.id)
const currentVariant = ref(variations?.value[0])
const currentVariant = computed(() =>
productsVariationsData?.value?.find(variation => variation?.id === currentVariantId?.value))
const currentColor = computed(() => getAttribute(currentVariant?.value?.attributes, 'color')?.option)
const currentMaterial = computed(() => getAttribute(currentVariant?.value?.attributes, 'material')?.option)
const currentColor = computed(() => currentVariant?.value?.identifier?.split('_')[0])
const currentMaterial = computed(() => currentVariant?.value?.identifier?.split('_')[1])
const currentVariantImages = computed(() =>
productsData?.value?.images?.filter(img => img?.src?.includes(`${currentColor.value}_${currentMaterial.value}_`)))
productsData?.value?.images?.filter(img => img?.src?.includes(`${currentVariant?.value?.identifier}_`)))
watch(() => defaultVariant.value, (newValue) => {
watch(() => variations.value, (newValue) => {
if (newValue) {
currentVariantId.value = newValue.id
currentVariant.value = newValue[0]
}
})
return {
currentVariantId,
currentVariant,
currentColor,
currentMaterial,
currentVariant,
currentVariantImages,
}
})

View File

@ -24,6 +24,29 @@ export const useProduct = () => {
const materials = computed(() => getAttribute(productsData?.value?.attributes, 'material')?.options)
const sizes = computed(() => getAttribute(productsData?.value?.attributes, 'size')?.options)
function getIdentifier(productVariant) {
const color = getAttribute(productVariant?.attributes, 'color')?.option
const material = getAttribute(productVariant?.attributes, 'material')?.option
return `${color}_${material}`
}
const variations = computed(() =>
colors?.value?.map((color) => {
if (!productsVariationsData?.value) {
return []
}
const productAsColor = productsVariationsData?.value?.filter(variant => getAttribute(variant?.attributes, 'color')?.option === color)
const identifier = getIdentifier(productAsColor[0])
return {
identifier,
image: productsData?.value?.images?.filter(img => img?.src?.includes(`${identifier}_`)),
options: productAsColor,
}
}) ?? [])
return {
productsData,
productsVariationsData,
@ -37,6 +60,9 @@ export const useProduct = () => {
materials,
sizes,
variations,
getAttribute,
getIdentifier,
}
}

View File

@ -1,6 +1,7 @@
import { useGetProductsList } from '~/api/queries'
export const useProductsList = () => {
const { getAttribute } = useProduct()
const { data: productData } = useGetProductsList()
const productCardData = computed(() => productData?.value?.map(product => ({
@ -9,6 +10,7 @@ export const useProductsList = () => {
price: product?.price,
variations: product?.variations,
images: product?.images?.slice(0, 5),
colors: getAttribute(product?.attributes, 'color')?.options,
})) ?? [])
return {

View File

@ -2,7 +2,8 @@
"colors": {
"black": "черный",
"beige": "бежевый",
"grey": "серый"
"grey": "серый",
"grey-black": "серо-черный"
},
"materials": {
"cotton": "хлопок",

View File

@ -19,8 +19,8 @@
<Icon name="ph:currency-rub" />
</div>
<div v-if="product?.variations?.length">
{{ `+${product?.variations?.length} Цвета` }}
<div v-if="product?.colors?.length > 1">
{{ `+${product?.colors?.length} Цвета` }}
</div>
</div>
</div>