@@ -6,7 +6,6 @@ import {
|
|||||||
type CatalogProductTypeSettingsQuery,
|
type CatalogProductTypeSettingsQuery,
|
||||||
type ClientProductsQuery,
|
type ClientProductsQuery,
|
||||||
} from '~/composables/graphql/generated';
|
} from '~/composables/graphql/generated';
|
||||||
import { catalogProductImageSrc } from '~/utils/catalogProductImages';
|
|
||||||
import { useClientCart } from '~/composables/useClientCart';
|
import { useClientCart } from '~/composables/useClientCart';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@@ -129,10 +128,6 @@ function createProductCover(name: string, sku: string) {
|
|||||||
return `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`;
|
return `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function productImageSrc(name: string, sku: string) {
|
|
||||||
return catalogProductImageSrc(name) ?? createProductCover(name, sku);
|
|
||||||
}
|
|
||||||
|
|
||||||
function hydrateProduct(product: ProductNode): ParsedProduct {
|
function hydrateProduct(product: ProductNode): ParsedProduct {
|
||||||
const normalizedTags = product.tags.map((tag) => normalizeText(tag)).filter(Boolean).sort((a, b) => a.localeCompare(b, 'ru'));
|
const normalizedTags = product.tags.map((tag) => normalizeText(tag)).filter(Boolean).sort((a, b) => a.localeCompare(b, 'ru'));
|
||||||
|
|
||||||
@@ -638,9 +633,9 @@ function productDetailPath(group: ProductGroup) {
|
|||||||
class="absolute left-[-212px] top-28 z-10 hidden w-44 rounded-[28px] border border-[#e6efe9] bg-white p-3 shadow-[0_20px_40px_rgba(18,56,36,0.08)] transition hover:-translate-x-2 hover:shadow-[0_28px_48px_rgba(18,56,36,0.12)] 2xl:block"
|
class="absolute left-[-212px] top-28 z-10 hidden w-44 rounded-[28px] border border-[#e6efe9] bg-white p-3 shadow-[0_20px_40px_rgba(18,56,36,0.08)] transition hover:-translate-x-2 hover:shadow-[0_28px_48px_rgba(18,56,36,0.12)] 2xl:block"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
:src="productImageSrc(previousGroup.typeLabel, previousGroup.key)"
|
:src="createProductCover(previousGroup.typeLabel, previousGroup.key)"
|
||||||
:alt="`Перейти к товару ${previousGroup.typeLabel}`"
|
:alt="`Перейти к товару ${previousGroup.typeLabel}`"
|
||||||
class="aspect-square w-full rounded-[20px] bg-[#f7fbf8] object-contain"
|
class="aspect-square w-full rounded-[20px] object-cover"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
>
|
>
|
||||||
<p class="mt-3 text-sm font-semibold leading-5 text-[#163624]">{{ previousGroup.typeLabel }}</p>
|
<p class="mt-3 text-sm font-semibold leading-5 text-[#163624]">{{ previousGroup.typeLabel }}</p>
|
||||||
@@ -652,9 +647,9 @@ function productDetailPath(group: ProductGroup) {
|
|||||||
class="absolute right-[-212px] top-28 z-10 hidden w-44 rounded-[28px] border border-[#e6efe9] bg-white p-3 shadow-[0_20px_40px_rgba(18,56,36,0.08)] transition hover:translate-x-2 hover:shadow-[0_28px_48px_rgba(18,56,36,0.12)] 2xl:block"
|
class="absolute right-[-212px] top-28 z-10 hidden w-44 rounded-[28px] border border-[#e6efe9] bg-white p-3 shadow-[0_20px_40px_rgba(18,56,36,0.08)] transition hover:translate-x-2 hover:shadow-[0_28px_48px_rgba(18,56,36,0.12)] 2xl:block"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
:src="productImageSrc(nextGroup.typeLabel, nextGroup.key)"
|
:src="createProductCover(nextGroup.typeLabel, nextGroup.key)"
|
||||||
:alt="`Перейти к товару ${nextGroup.typeLabel}`"
|
:alt="`Перейти к товару ${nextGroup.typeLabel}`"
|
||||||
class="aspect-square w-full rounded-[20px] bg-[#f7fbf8] object-contain"
|
class="aspect-square w-full rounded-[20px] object-cover"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
>
|
>
|
||||||
<p class="mt-3 text-sm font-semibold leading-5 text-[#163624]">{{ nextGroup.typeLabel }}</p>
|
<p class="mt-3 text-sm font-semibold leading-5 text-[#163624]">{{ nextGroup.typeLabel }}</p>
|
||||||
@@ -681,9 +676,9 @@ function productDetailPath(group: ProductGroup) {
|
|||||||
class="flex items-center gap-3 rounded-[24px] border border-[#e6efe9] bg-white p-3 shadow-[0_14px_30px_rgba(18,56,36,0.06)]"
|
class="flex items-center gap-3 rounded-[24px] border border-[#e6efe9] bg-white p-3 shadow-[0_14px_30px_rgba(18,56,36,0.06)]"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
:src="productImageSrc(previousGroup.typeLabel, previousGroup.key)"
|
:src="createProductCover(previousGroup.typeLabel, previousGroup.key)"
|
||||||
:alt="`Перейти к товару ${previousGroup.typeLabel}`"
|
:alt="`Перейти к товару ${previousGroup.typeLabel}`"
|
||||||
class="h-16 w-16 rounded-2xl bg-[#f7fbf8] object-contain"
|
class="h-16 w-16 rounded-2xl object-cover"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
>
|
>
|
||||||
<span class="text-sm font-semibold text-[#163624]">{{ previousGroup.typeLabel }}</span>
|
<span class="text-sm font-semibold text-[#163624]">{{ previousGroup.typeLabel }}</span>
|
||||||
@@ -695,9 +690,9 @@ function productDetailPath(group: ProductGroup) {
|
|||||||
class="flex items-center gap-3 rounded-[24px] border border-[#e6efe9] bg-white p-3 shadow-[0_14px_30px_rgba(18,56,36,0.06)]"
|
class="flex items-center gap-3 rounded-[24px] border border-[#e6efe9] bg-white p-3 shadow-[0_14px_30px_rgba(18,56,36,0.06)]"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
:src="productImageSrc(nextGroup.typeLabel, nextGroup.key)"
|
:src="createProductCover(nextGroup.typeLabel, nextGroup.key)"
|
||||||
:alt="`Перейти к товару ${nextGroup.typeLabel}`"
|
:alt="`Перейти к товару ${nextGroup.typeLabel}`"
|
||||||
class="h-16 w-16 rounded-2xl bg-[#f7fbf8] object-contain"
|
class="h-16 w-16 rounded-2xl object-cover"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
>
|
>
|
||||||
<span class="text-sm font-semibold text-[#163624]">{{ nextGroup.typeLabel }}</span>
|
<span class="text-sm font-semibold text-[#163624]">{{ nextGroup.typeLabel }}</span>
|
||||||
@@ -708,9 +703,9 @@ function productDetailPath(group: ProductGroup) {
|
|||||||
<div class="space-y-3">
|
<div class="space-y-3">
|
||||||
<div class="overflow-hidden rounded-[32px] border border-[#e6efe9] bg-white p-4 shadow-[0_20px_40px_rgba(18,56,36,0.06)]">
|
<div class="overflow-hidden rounded-[32px] border border-[#e6efe9] bg-white p-4 shadow-[0_20px_40px_rgba(18,56,36,0.06)]">
|
||||||
<img
|
<img
|
||||||
:src="productImageSrc(selectedGroup.typeLabel, articleLabel(selectedGroup))"
|
:src="createProductCover(selectedGroup.typeLabel, articleLabel(selectedGroup))"
|
||||||
:alt="selectedGroup.typeLabel"
|
:alt="selectedGroup.typeLabel"
|
||||||
class="aspect-[5/4] w-full rounded-[26px] bg-[#f7fbf8] object-contain"
|
class="aspect-[5/4] w-full rounded-[26px] object-cover"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {
|
|||||||
ClientProductsDocument,
|
ClientProductsDocument,
|
||||||
type ClientProductsQuery,
|
type ClientProductsQuery,
|
||||||
} from '~/composables/graphql/generated';
|
} from '~/composables/graphql/generated';
|
||||||
import { catalogProductImageSrc } from '~/utils/catalogProductImages';
|
|
||||||
|
|
||||||
type ProductNode = ClientProductsQuery['clientProducts'][number];
|
type ProductNode = ClientProductsQuery['clientProducts'][number];
|
||||||
type ProductTypeCard = {
|
type ProductTypeCard = {
|
||||||
@@ -61,10 +60,6 @@ function createProductCover(name: string, sku: string) {
|
|||||||
return `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`;
|
return `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function productImageSrc(name: string, sku: string) {
|
|
||||||
return catalogProductImageSrc(name) ?? createProductCover(name, sku);
|
|
||||||
}
|
|
||||||
|
|
||||||
const productTypeCards = computed<ProductTypeCard[]>(() => {
|
const productTypeCards = computed<ProductTypeCard[]>(() => {
|
||||||
const products = productsQuery.result.value?.clientProducts ?? [];
|
const products = productsQuery.result.value?.clientProducts ?? [];
|
||||||
const query = search.value.trim().toLowerCase();
|
const query = search.value.trim().toLowerCase();
|
||||||
@@ -115,9 +110,9 @@ const productTypeCards = computed<ProductTypeCard[]>(() => {
|
|||||||
class="surface-card block rounded-3xl p-3 transition hover:-translate-y-0.5 hover:shadow-[0_22px_42px_rgba(18,56,36,0.12)]"
|
class="surface-card block rounded-3xl p-3 transition hover:-translate-y-0.5 hover:shadow-[0_22px_42px_rgba(18,56,36,0.12)]"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
:src="productImageSrc(card.typeLabel, card.key)"
|
:src="createProductCover(card.typeLabel, card.key)"
|
||||||
:alt="`Превью ${card.typeLabel}`"
|
:alt="`Превью ${card.typeLabel}`"
|
||||||
class="aspect-square w-full rounded-[24px] bg-[#f7fbf8] object-contain"
|
class="aspect-square w-full rounded-[24px] object-cover"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
>
|
>
|
||||||
|
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
const CATALOG_PRODUCT_IMAGE_BY_TYPE: Record<string, string> = {
|
|
||||||
'алюминиевый скотч': '/catalog-products/aluminum-tape.webp',
|
|
||||||
'армированный скотч': '/catalog-products/reinforced-tape.webp',
|
|
||||||
'вспененный скотч': '/catalog-products/double-sided-foam-tape.webp',
|
|
||||||
'двусторонний pvc': '/catalog-products/double-sided-superglue-foam-tape.webp',
|
|
||||||
'двусторонний пп': '/catalog-products/double-sided-polypropylene-tape.webp',
|
|
||||||
'джамбо-рулоны': '/catalog-products/jumbo-rolls.webp',
|
|
||||||
'крепп': '/catalog-products/masking-tape-indoor.webp',
|
|
||||||
'лента алюминиевая': '/catalog-products/aluminum-tape.webp',
|
|
||||||
'лента армированная': '/catalog-products/reinforced-tape.webp',
|
|
||||||
'лента двусторонняя': '/catalog-products/double-sided-polypropylene-tape.webp',
|
|
||||||
'лента малярная': '/catalog-products/masking-tape-indoor.webp',
|
|
||||||
'лента металлизированная': '/catalog-products/metallized-tape.webp',
|
|
||||||
'лента с логотипом': '/catalog-products/logo-tape.webp',
|
|
||||||
'лента сигнальная': '/catalog-products/signal-tape.webp',
|
|
||||||
'лента упаковочная': '/catalog-products/packaging-tape.webp',
|
|
||||||
'малярная лента': '/catalog-products/masking-tape-outdoor.webp',
|
|
||||||
'металлизированный скотч': '/catalog-products/metallized-tape.webp',
|
|
||||||
'перчатки хб': '/catalog-products/cotton-gloves.webp',
|
|
||||||
'сигнальная лента': '/catalog-products/signal-tape.webp',
|
|
||||||
'стретч-пленка': '/catalog-products/stretch-film.webp',
|
|
||||||
'упаковочный скотч': '/catalog-products/packaging-tape.webp',
|
|
||||||
};
|
|
||||||
|
|
||||||
function normalizeCatalogProductType(value: string | null | undefined) {
|
|
||||||
return String(value ?? '').replaceAll(/\s+/g, ' ').trim().toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function catalogProductImageSrc(productType: string | null | undefined) {
|
|
||||||
return CATALOG_PRODUCT_IMAGE_BY_TYPE[normalizeCatalogProductType(productType)] ?? null;
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 343 KiB |
|
Before Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 360 KiB |
|
Before Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 467 KiB |
|
Before Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 362 KiB |
|
Before Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 262 KiB |