From a9e4b74179bf356a7cbd9f8b4d9a7ae8a306e570 Mon Sep 17 00:00:00 2001 From: Bivekich Date: Wed, 2 Jul 2025 17:52:55 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D1=8B=20VehicleSearchResults=20=D0=B8=20InfoVin=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D0=BF=D0=BE=D0=B4=D0=B4=D0=B5=D1=80=D0=B6=D0=BA?= =?UTF-8?q?=D0=B8=20=D0=BE=D1=82=D0=BE=D0=B1=D1=80=D0=B0=D0=B6=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=B4=D0=BE=D0=BF=D0=BE=D0=BB=D0=BD=D0=B8=D1=82?= =?UTF-8?q?=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D1=85=20=D0=B0=D1=82=D1=80=D0=B8?= =?UTF-8?q?=D0=B1=D1=83=D1=82=D0=BE=D0=B2=20=D0=B0=D0=B2=D1=82=D0=BE=D0=BC?= =?UTF-8?q?=D0=BE=D0=B1=D0=B8=D0=BB=D1=8F.=20=D0=92=20VehicleSearchResults?= =?UTF-8?q?=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0=20?= =?UTF-8?q?=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA=D0=B0=20=D0=B4=D0=BB=D1=8F=20?= =?UTF-8?q?=D0=BE=D1=82=D0=BE=D0=B1=D1=80=D0=B0=D0=B6=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=B0=D1=82=D1=80=D0=B8=D0=B1=D1=83=D1=82=D0=BE=D0=B2?= =?UTF-8?q?=20=D0=B8=D0=B7=20API,=20=D0=B0=20=D0=B2=20InfoVin=20=D1=80?= =?UTF-8?q?=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D0=BE=D0=B2=D0=B0=D0=BD=20=D0=B2?= =?UTF-8?q?=D1=8B=D0=B2=D0=BE=D0=B4=20=D0=BE=D1=81=D0=BD=D0=BE=D0=B2=D0=BD?= =?UTF-8?q?=D1=8B=D1=85=20=D0=BF=D0=B0=D1=80=D0=B0=D0=BC=D0=B5=D1=82=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=20=D0=B8=20tooltip=20=D1=81=20=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D0=BD=D0=BE=D0=B9=20=D0=B8=D0=BD=D1=84=D0=BE=D1=80=D0=BC=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D0=B5=D0=B9=20=D0=BE=D0=B1=20=D0=B0=D0=B2=D1=82?= =?UTF-8?q?=D0=BE=D0=BC=D0=BE=D0=B1=D0=B8=D0=BB=D0=B5.=20=D0=9E=D0=B1?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20GraphQL=20=D0=B7?= =?UTF-8?q?=D0=B0=D0=BF=D1=80=D0=BE=D1=81=D1=8B=20=D0=B4=D0=BB=D1=8F=20?= =?UTF-8?q?=D0=BF=D0=BE=D0=BB=D1=83=D1=87=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B0?= =?UTF-8?q?=D1=82=D1=80=D0=B8=D0=B1=D1=83=D1=82=D0=BE=D0=B2.=20=D0=98?= =?UTF-8?q?=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20=D1=82?= =?UTF-8?q?=D0=B8=D0=BF=D1=8B=20=D0=B4=D0=BB=D1=8F=20LaximoVehicleSearchRe?= =?UTF-8?q?sult.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/VehicleSearchResults.tsx | 95 +++--- src/components/vin/InfoVin.tsx | 270 +++++++++++++++--- src/lib/graphql.ts | 25 ++ .../vehicle-search/[brand]/[vehicleId].tsx | 1 + src/types/laximo.ts | 1 + 5 files changed, 318 insertions(+), 74 deletions(-) diff --git a/src/components/VehicleSearchResults.tsx b/src/components/VehicleSearchResults.tsx index 2746907..c74deb8 100644 --- a/src/components/VehicleSearchResults.tsx +++ b/src/components/VehicleSearchResults.tsx @@ -90,53 +90,68 @@ const VehicleSearchResults: React.FC = ({
Основные характеристики
{renderAttribute('Марка', vehicle.brand)} {renderAttribute('Модель', vehicle.model)} - {renderAttribute('Год', vehicle.year)} - {renderAttribute('Кузов', vehicle.bodytype)} {renderAttribute('Двигатель', vehicle.engine)} - {renderAttribute('Трансмиссия', vehicle.transmission)} - {/* Дополнительные характеристики */} -
-
Дополнительные характеристики
- {renderAttribute('Класс', vehicle.grade)} - {renderAttribute('Цвет кузова', vehicle.framecolor)} - {renderAttribute('Цвет салона', vehicle.trimcolor)} - {renderAttribute('Рынок', vehicle.market)} - {renderAttribute('Регион производства', vehicle.creationregion)} - {renderAttribute('Регион назначения', vehicle.destinationregion)} -
- - {/* Технические характеристики */} -
-
Технические характеристики
- {renderAttribute('Информация о двигателе', vehicle.engine_info)} - {renderAttribute('Номер двигателя', vehicle.engineno)} - {renderAttribute('Дата производства', vehicle.date)} - {renderAttribute('Произведен', vehicle.manufactured)} - {renderAttribute('Период производства', vehicle.prodPeriod)} - {renderAttribute('Диапазон производства', vehicle.prodRange)} -
- - {/* Даты и периоды */} -
-
Даты и периоды
- {renderAttribute('Дата с', vehicle.datefrom)} - {renderAttribute('Дата по', vehicle.dateto)} - {renderAttribute('Модельный год с', vehicle.modelyearfrom)} - {renderAttribute('Модельный год по', vehicle.modelyearto)} -
- - {/* Опции и описание */} - {(vehicle.options || vehicle.description || vehicle.notes) && ( + {/* Все атрибуты из API */} + {vehicle.attributes && vehicle.attributes.length > 0 && (
-
Опции и описание
- {renderAttribute('Опции', vehicle.options)} - {renderAttribute('Описание', vehicle.description)} - {renderAttribute('Примечания', vehicle.notes)} +
Дополнительные характеристики
+ {vehicle.attributes.map((attr, attrIndex) => ( +
+ {attr.name || attr.key}: + {attr.value} +
+ ))}
)} + {/* Технические характеристики (fallback для старых данных) */} + {(!vehicle.attributes || vehicle.attributes.length === 0) && ( + <> +
+
Дополнительные характеристики
+ {renderAttribute('Год', vehicle.year)} + {renderAttribute('Кузов', vehicle.bodytype)} + {renderAttribute('Трансмиссия', vehicle.transmission)} + {renderAttribute('Класс', vehicle.grade)} + {renderAttribute('Цвет кузова', vehicle.framecolor)} + {renderAttribute('Цвет салона', vehicle.trimcolor)} + {renderAttribute('Рынок', vehicle.market)} + {renderAttribute('Регион производства', vehicle.creationregion)} + {renderAttribute('Регион назначения', vehicle.destinationregion)} +
+ +
+
Технические характеристики
+ {renderAttribute('Информация о двигателе', vehicle.engine_info)} + {renderAttribute('Номер двигателя', vehicle.engineno)} + {renderAttribute('Дата производства', vehicle.date)} + {renderAttribute('Произведен', vehicle.manufactured)} + {renderAttribute('Период производства', vehicle.prodPeriod)} + {renderAttribute('Диапазон производства', vehicle.prodRange)} +
+ +
+
Даты и периоды
+ {renderAttribute('Дата с', vehicle.datefrom)} + {renderAttribute('Дата по', vehicle.dateto)} + {renderAttribute('Модельный год с', vehicle.modelyearfrom)} + {renderAttribute('Модельный год по', vehicle.modelyearto)} +
+ + {/* Опции и описание */} + {(vehicle.options || vehicle.description || vehicle.notes) && ( +
+
Опции и описание
+ {renderAttribute('Опции', vehicle.options)} + {renderAttribute('Описание', vehicle.description)} + {renderAttribute('Примечания', vehicle.notes)} +
+ )} + + )} + {/* Системная информация */}
diff --git a/src/components/vin/InfoVin.tsx b/src/components/vin/InfoVin.tsx index 97d3cd4..84c654e 100644 --- a/src/components/vin/InfoVin.tsx +++ b/src/components/vin/InfoVin.tsx @@ -1,47 +1,249 @@ -import React from "react"; +import React, { useState, useEffect, useRef } from "react"; + +interface VehicleAttribute { + key: string; + name: string; + value: string; +} interface InfoVinProps { vehicleName?: string; vehicleInfo?: string; + vehicleAttributes?: VehicleAttribute[]; } const InfoVin: React.FC = ({ vehicleName = "VIN декодирование", - vehicleInfo = "Поиск запчастей по VIN номеру автомобиля" -}) => ( -
-
-
- -
-
-

{vehicleName}

+ vehicleInfo = "Поиск запчастей по VIN номеру автомобиля", + vehicleAttributes = [] +}) => { + const [showTooltip, setShowTooltip] = useState(false); + const [tooltipPosition, setTooltipPosition] = useState({ x: 0, y: 0 }); + const timeoutRef = useRef(null); + const iconRef = useRef(null); + + // Отладочный вывод атрибутов + useEffect(() => { + if (vehicleAttributes.length > 0) { + console.log('🚗 Атрибуты автомобиля:', vehicleAttributes); + console.log('🔍 Ключи атрибутов:', vehicleAttributes.map(attr => ({ key: attr.key, name: attr.name }))); + } + }, [vehicleAttributes]); + + // Определяем основные параметры для отображения + const getMainParameters = (attributes: VehicleAttribute[]) => { + // Приоритетные ключи для основных параметров + const priorityKeys = [ + // Двигатель + { + keys: ['engine', 'enginetype', 'engine_type', 'двигатель', 'тип двигателя', 'motor'], + priority: 1 + }, + // VIN + { + keys: ['vin', 'вин', 'vin_code'], + priority: 2 + }, + // Год выпуска + { + keys: ['year', 'год', 'год выпуска', 'production_year', 'model_year'], + priority: 3 + }, + // Топливо + { + keys: ['fuel', 'топливо', 'тип топлива', 'fuel_type', 'fueltype'], + priority: 4 + }, + // Коробка передач + { + keys: ['transmission', 'коробка', 'кпп', 'gearbox', 'transmissiontype'], + priority: 5 + } + ]; + + const foundParams: Array<{ attr: VehicleAttribute; priority: number }> = []; + + // Ищем атрибуты по приоритетным ключам + for (const priorityGroup of priorityKeys) { + const foundAttr = attributes.find(attr => + priorityGroup.keys.some(key => + attr.key.toLowerCase().includes(key.toLowerCase()) || + attr.name.toLowerCase().includes(key.toLowerCase()) + ) + ); + + if (foundAttr) { + foundParams.push({ attr: foundAttr, priority: priorityGroup.priority }); + } + } + + // Сортируем по приоритету и берем максимум 4 параметра + foundParams.sort((a, b) => a.priority - b.priority); + const mainParams = foundParams.slice(0, 4).map(item => item.attr); + + // Если основных параметров меньше 3, добавляем первые доступные + if (mainParams.length < 3) { + const additionalParams = attributes + .filter(attr => !mainParams.includes(attr)) + .slice(0, 3 - mainParams.length); + return [...mainParams, ...additionalParams]; + } + + return mainParams; + }; + + const mainParameters = getMainParameters(vehicleAttributes); + const displayText = mainParameters.length > 0 + ? mainParameters.map(attr => attr.value).join(' · ') + : vehicleInfo; + + // Отладочный вывод выбранных параметров + useEffect(() => { + if (mainParameters.length > 0) { + console.log('✅ Выбранные основные параметры:', mainParameters); + console.log('📝 Отображаемый текст:', displayText); + } + }, [mainParameters, displayText]); + + // Вычисляем позицию tooltip + const calculateTooltipPosition = () => { + if (iconRef.current) { + const rect = iconRef.current.getBoundingClientRect(); + const tooltipWidth = 500; + const tooltipHeight = 300; // примерная высота + + let x = rect.left + rect.width / 2 - tooltipWidth / 2; + let y = rect.bottom + 8; + + // Проверяем, не выходит ли tooltip за границы экрана + if (x < 10) x = 10; + if (x + tooltipWidth > window.innerWidth - 10) { + x = window.innerWidth - tooltipWidth - 10; + } + + // Если tooltip не помещается снизу, показываем сверху + if (y + tooltipHeight > window.innerHeight - 10) { + y = rect.top - tooltipHeight - 8; + } + + setTooltipPosition({ x, y }); + } + }; + + const handleMouseEnter = () => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + timeoutRef.current = setTimeout(() => { + calculateTooltipPosition(); + setShowTooltip(true); + }, 300); // Задержка 300ms + }; + + const handleMouseLeave = () => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + timeoutRef.current = setTimeout(() => { + setShowTooltip(false); + }, 100); // Небольшая задержка перед скрытием + }; + + // Очищаем таймеры при размонтировании + useEffect(() => { + return () => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + }; + }, []); + + return ( + <> +
+
+ +
+
{displayText}
+
+
+ + + +
+
-
-
-
{vehicleInfo}
-
- {/* SVG icon */} - - - +
+ + {/* Tooltip с фиксированным позиционированием */} + {showTooltip && vehicleAttributes.length > 0 && ( +
+ {/* Заголовок */} +
+

+ Полная информация об автомобиле +

+

{vehicleName}

+
+ + {/* Атрибуты в сетке */} +
+ {vehicleAttributes.map((attr, index) => ( +
+
{attr.name}
+
{attr.value}
+
+ ))} +
+ + {/* Подвал */} +
+
+ Всего параметров: {vehicleAttributes.length} +
+
-
-
- -); + )} + + ); +}; export default InfoVin; \ No newline at end of file diff --git a/src/lib/graphql.ts b/src/lib/graphql.ts index 3d03d1c..b55045f 100644 --- a/src/lib/graphql.ts +++ b/src/lib/graphql.ts @@ -569,6 +569,11 @@ export const FIND_LAXIMO_VEHICLE_BY_WIZARD = gql` options description grade + attributes { + key + name + value + } } } `; @@ -606,6 +611,11 @@ export const FIND_LAXIMO_VEHICLE_BY_PLATE = gql` options description grade + attributes { + key + name + value + } } } `; @@ -643,6 +653,11 @@ export const FIND_LAXIMO_VEHICLE_BY_PLATE_GLOBAL = gql` options description grade + attributes { + key + name + value + } } } `; @@ -686,6 +701,11 @@ export const FIND_LAXIMO_APPLICABLE_VEHICLES = gql` options description grade + attributes { + key + name + value + } } } `; @@ -731,6 +751,11 @@ export const FIND_LAXIMO_VEHICLES_BY_PART_NUMBER = gql` options description grade + attributes { + key + name + value + } } } } diff --git a/src/pages/vehicle-search/[brand]/[vehicleId].tsx b/src/pages/vehicle-search/[brand]/[vehicleId].tsx index 3782d37..ab4f9d7 100644 --- a/src/pages/vehicle-search/[brand]/[vehicleId].tsx +++ b/src/pages/vehicle-search/[brand]/[vehicleId].tsx @@ -312,6 +312,7 @@ const VehicleDetailsPage = () => { ? vehicleInfo.attributes.map(attr => attr.value).join(' · ') : '' } + vehicleAttributes={vehicleInfo.attributes || []} />
diff --git a/src/types/laximo.ts b/src/types/laximo.ts index dbc0717..6a78445 100644 --- a/src/types/laximo.ts +++ b/src/types/laximo.ts @@ -108,6 +108,7 @@ export interface LaximoVehicleSearchResult { // Дополнительные атрибуты (могут приходить в виде массива attributes) sales_code?: string + attributes: LaximoVehicleAttribute[] } export interface LaximoVehicleInfo {