barneo-search-widget-lib
Barneo Search Widget
Библиотека компонентов для поиска по каталогу Barneo с помощью Ensi
📋 Содержание
- Установка
- Подключение и конфигурация
- Использование библиотеки
- Компоненты
- Кастомные слоты
- Аналитика
- Дополнительные возможности
- TypeScript поддержка
- Поддержка
- Лицензия
📦 Установка
npm install barneo-search-widget-lib
# или
yarn add barneo-search-widget-lib
⚙️ Подключение и конфигурация
Vue 3
Глобальная регистрация (рекомендуется)
// main.ts
import { createApp } from "vue";
import App from "./App.vue";
import BarneoSearchPlugin from "barneo-search-widget-lib";
const app = createApp(App);
const config = {
api: {
baseUrl: "https://api.search.ensi.cloud",
token: "your-api-token",
customerId: "4",
locationId: "1",
apiVersion: "v1",
},
searchWidget: {
placeholder: "Найти товары",
debounceMs: 300,
minSearchLength: 2,
darkMode: false,
focusMode: false,
},
filtersWidget: {
maxValuesPerFilter: 5,
debounceMs: 300,
darkMode: false,
},
productsWidget: {
itemsPerPage: 20,
mobileItemWidth: 160,
desktopItemWidth: 200,
darkMode: false,
showPagination: true,
showTotalCount: true,
},
sortWidget: {
darkMode: false,
defaultSort: "relevance",
},
};
app.use(BarneoSearchPlugin, config);
app.mount("#app");
Примечание: URL менеджер инициализируется в компонентах через useUrlManagerInit() composable, а не автоматически в плагине.
Локальная регистрация
<template>
<div>
<barneo-search-widget v-model="query" />
<barneo-filters-widget />
<barneo-sort-widget />
<barneo-products-widget />
</div>
</template>
<script setup>
import { ref } from "vue";
import {
BarneoSearchWidget,
BarneoFiltersWidget,
BarneoSortWidget,
BarneoProductsWidget,
} from "barneo-search-widget-lib";
const query = ref("");
</script>
Nuxt 3
Создание плагина
// plugins/barneo-search.client.ts
import { defineNuxtPlugin } from "nuxt/app";
import BarneoSearchPlugin from "barneo-search-widget-lib";
export default defineNuxtPlugin((nuxtApp: any) => {
const config = {
api: {
baseUrl:
process.env.NUXT_BARNEO_ENSI_BASE_URL ||
"https://api.search.ensi.cloud",
token:
process.env.NUXT_BARNEO_ENSI_TOKEN ||
"95246bb090387d5c92186f1b6d25cc72",
customerId: process.env.NUXT_BARNEO_ENSI_CUSTOMER_ID || "4",
locationId: process.env.NUXT_BARNEO_ENSI_LOCATION_ID || "1",
apiVersion: process.env.NUXT_BARNEO_ENSI_API_VERSION || "v1",
},
searchWidget: {
placeholder: "Найти товары",
debounceMs: 300,
minSearchLength: 2,
darkMode: false,
focusMode: false,
},
filtersWidget: {
maxValuesPerFilter: 5,
debounceMs: 300,
darkMode: false,
},
productsWidget: {
itemsPerPage: 20,
mobileItemWidth: 160,
desktopItemWidth: 200,
darkMode: false,
showPagination: true,
showTotalCount: true,
},
sortWidget: {
darkMode: false,
defaultSort: "relevance",
},
};
// Используем плагин библиотеки для глобальной регистрации
nuxtApp.vueApp.use(BarneoSearchPlugin, config);
// URL менеджер инициализируется в компонентах через useUrlManagerInit()
});
Настройка в nuxt.config.ts
// nuxt.config.ts
export default defineNuxtConfig({
css: ["barneo-search-widget-lib/dist/style.css"],
runtimeConfig: {
public: {
barneoEnsiBaseUrl: process.env.NUXT_BARNEO_ENSI_BASE_URL,
barneoEnsiToken: process.env.NUXT_BARNEO_ENSI_TOKEN,
barneoEnsiCustomerId: process.env.NUXT_BARNEO_ENSI_CUSTOMER_ID,
barneoEnsiLocationId: process.env.NUXT_BARNEO_ENSI_LOCATION_ID,
barneoEnsiApiVersion: process.env.NUXT_BARNEO_ENSI_API_VERSION,
},
},
});
Описание конфигурации
API конфигурация
api: {
baseUrl: string, // Базовый URL API (обязательно)
token: string, // API токен для авторизации (обязательно)
customerId: string, // ID клиента (обязательно)
locationId: string, // ID локации (обязательно)
apiVersion?: string // Версия API (опционально, если не указана используется только baseUrl)
}
Конфигурация поиска
searchWidget: {
placeholder: string, // Placeholder для поля поиска (по умолчанию: 'Поиск...')
debounceMs: number, // Задержка перед отправкой запроса в мс (по умолчанию: 300)
minSearchLength: number, // Минимальная длина для поиска (по умолчанию: 2)
darkMode: boolean, // Принудительная темная тема (по умолчанию: false)
focusMode: boolean // Автофокус при монтировании (по умолчанию: false)
}
Конфигурация фильтров
filtersWidget: {
maxValuesPerFilter: number, // Максимум значений в фильтре (по умолчанию: 5)
debounceMs: number, // Задержка применения фильтров в мс (по умолчанию: 300)
darkMode: boolean // Принудительная темная тема (по умолчанию: false)
}
Конфигурация товаров
productsWidget: {
itemsPerPage: number, // Количество товаров на странице (по умолчанию: 20)
mobileItemWidth: number, // Ширина элемента на мобильных (по умолчанию: 160)
desktopItemWidth: number, // Ширина элемента на десктопе (по умолчанию: 200)
darkMode: boolean, // Принудительная темная тема (по умолчанию: false)
showPagination: boolean, // Показывать пагинацию (по умолчанию: true)
showTotalCount: boolean // Показывать общее количество (по умолчанию: true)
}
Конфигурация сортировки
sortWidget: {
darkMode: boolean, // Принудительная темная тема (по умолчанию: false)
defaultSort: string // Сортировка по умолчанию (по умолчанию: 'relevance')
}
🚀 Использование библиотеки
Базовое использование
<template>
<div>
<barneo-search-widget v-model="searchQuery" @search="handleSearch" />
<barneo-filters-widget @filter-applied="handleFilter" />
<barneo-sort-widget @sort-changed="handleSort" />
<barneo-products-widget @product-selected="handleProductSelect" />
<barneo-range-selector
ref="rangeSelector"
:min="0"
:max="10000"
@change="handleRangeChange"
/>
</div>
</template>
<script setup>
import { ref } from "vue";
const searchQuery = ref("");
const rangeSelector = ref();
const handleSearch = (query) => {
console.log("Поиск выполнен:", query);
};
const handleFilter = (filterCode, valueId, isSelected) => {
console.log("Фильтр применен:", filterCode, valueId, isSelected);
};
const handleSort = (sortValue) => {
console.log("Сортировка изменена:", sortValue);
};
const handleProductSelect = (product) => {
console.log("Товар выбран:", product);
};
const handleRangeChange = ([min, max]) => {
console.log("Диапазон изменен:", { min, max });
};
</script>
Использование composables
<template>
<div>
<input v-model="searchQuery" @input="handleSearch" />
<div v-if="isLoading">Загрузка...</div>
<div v-else>
<div v-for="result in searchResults" :key="result.id">
{{ result.name }}
</div>
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
import { useSearchWidget, SearchApiService } from "barneo-search-widget-lib";
const searchQuery = ref("");
const isLoading = ref(false);
const searchResults = ref([]);
const { performSearch } = useSearchWidget();
const apiService = new SearchApiService({
baseUrl: "https://api.search.ensi.cloud",
token: "your-token",
});
const handleSearch = async () => {
if (searchQuery.value.length < 2) return;
isLoading.value = true;
try {
const results = await performSearch(searchQuery.value, apiService);
searchResults.value = results.data?.full_products || [];
} catch (error) {
console.error("Ошибка поиска:", error);
} finally {
isLoading.value = false;
}
};
</script>
🧩 Компоненты
BarneoSearchWidget
Компонент поиска с автодополнением, быстрыми результатами и полным поиском.
Props
interface SearchWidgetProps {
modelValue?: string; // Значение поля поиска (v-model)
placeholder?: string; // Placeholder для поля ввода
debounceMs?: number; // Задержка перед отправкой запроса
minSearchLength?: number; // Минимальная длина для поиска
darkMode?: boolean; // Принудительная темная тема
focusMode?: boolean; // Автофокус при монтировании
apiService?: SearchApiService; // Кастомный API сервис
}
События
-
@search- выполнен поиск (автоматический или ручной)(query: string) => void -
@select- выбран товар из результатов(product: Product, event?: MouseEvent) => voidПримечание: Событие поддерживает
MouseEventдля обработки средней кнопки мыши. Приevent.button === 1рекомендуется открывать товар в новой вкладке. -
@searchResults- получены результаты поиска(results: SearchResults) => void -
@loading- изменение состояния загрузки(isLoading: boolean) => void -
@error- произошла ошибка(error: string) => void -
@focus- поле получило фокус() => void -
@blur- поле потеряло фокус() => void -
@input- изменение значения поля(value: string) => void
Слоты
search-header- заголовок компонента поиска
<template #search-header="{ config }">
<h1>🔍 Поиск товаров</h1>
</template>
search-input- кастомное поле ввода<template #search-input="{ value, onInput, onFocus, onBlur }"> <input :value="value" @input="onInput" @focus="onFocus" @blur="onBlur" /> </template>search-results- кастомные результаты поиска<template #search-results="{ results, isLoading, selectProduct, handleAuxClick }" > <div v-if="isLoading">Загрузка...</div> <div v-else> <div v-for="result in results" :key="result.id" @click="(event) => selectProduct(result, event)" @auxclick="(event) => handleAuxClick(result, event)" > {{ result.name }} </div> </div> </template>search-empty- пустое состояние<template #search-empty="{ query }"> <p>По запросу "{{ query }}" ничего не найдено</p> </template>loading- состояние загрузки<template #loading="{ isLoading }"> <div v-if="isLoading">⏳ Загрузка...</div> </template>error- состояние ошибки<template #error="{ error }"> <div v-if="error">❌ {{ error }}</div> </template>
BarneoFiltersWidget
Компонент фильтров с динамической загрузкой, автоматическим применением и кастомными слотами.
Props
interface FiltersWidgetProps {
loading?: boolean; // Состояние загрузки
error?: string | null; // Текст ошибки
activeFilters?: Record<string, string[]>; // Активные фильтры
config?: FiltersWidgetConfig; // Локальная конфигурация
apiService?: SearchApiService; // Кастомный API сервис
}
События
@filter-applied- применен фильтр(filterCode: string, valueId: string, isSelected: boolean) => void@filter-removed- удален фильтр(filterCode: string, valueId: string) => void@filters-reset- сброшены все фильтры() => void@filters-applied- применены фильтры(activeFilters: Record<string, string[]>) => void@active-filters-changed- изменились активные фильтры(activeFilters: Record<string, string[]>) => void@loading- изменение состояния загрузки(isLoading: boolean) => void@error- произошла ошибка(error: string) => void
Слоты
filters-title- заголовок фильтров
<template #filters-title="{ config }">
<h2>🎯 Фильтры ({{ config.maxValuesPerFilter }} значений)</h2>
</template>
filters-skeleton- скелетон загрузки<template #filters-skeleton="{ isLoading, skeletonData }"> <div v-if="isLoading"> <div v-for="group in skeletonData" :key="group.id"> <!-- Скелетон группы --> </div> </div> </template>
- `filter-group` - группа фильтров
```vue
<template
#filter-group="{ filter, visibleValues, isExpanded, toggleFilterExpansion }"
>
<div class="filter-group">
<h3>{{ filter.name }}</h3>
<div v-if="isExpanded">
<!-- Значения фильтров -->
</div>
<button @click="toggleFilterExpansion(filter.code)">
{{ isExpanded ? "Свернуть" : "Развернуть" }}
</button>
</div>
</template>
filter-value- значение фильтра<template #filter-value="{ filterCode, value, isActive, handleClick }"> <div @click="handleClick(filterCode, value.id)"> <input type="checkbox" :checked="isActive" /> <label>{{ value.name }}</label> </div> </template>filter-checkbox- кастомный чекбокс<template #filter-checkbox="{ filterCode, value, isActive, handleClick }"> <div class="custom-checkbox"> <input type="checkbox" :checked="isActive" @change="handleClick(filterCode, value.id)" /> <label>{{ value.name }}</label> </div> </template>
- `filter-toggle` - кнопка "Показать все"
```vue
<template #filter-toggle="{ filterCode, isExpanded, handleToggle }">
<button @click="handleToggle(filterCode)">
{{ isExpanded ? "Скрыть" : "Показать все" }}
</button>
</template>
-
filter-range- кастомный range фильтр с под-слотами<template #filter-range="{ filter, handleRangeSelectorChange, rangeSelectorValue, rangeStep, rangeFormatValue, isRangeActive, rangeMin, rangeMax, }" > <div class="custom-range-filter"> <!-- Заголовок range фильтра --> <template #filter-range-header="{ filter }"> <h4>{{ filter.description }}</h4> </template> <!-- Индикатор активного диапазона --> <template #filter-range-indicator="{ isRangeActive, rangeFormatValue }"> <div v-if="isRangeActive" class="range-indicator"> Выбран диапазон: {{ rangeFormatValue }} </div> </template> <!-- Кнопка сброса --> <template #filter-range-reset="{ handleRangeSelectorChange, rangeMin, rangeMax }" > <button @click="handleRangeSelectorChange(filter.code, [rangeMin, rangeMax])" > Сбросить </button> </template> </div> </template> -
filters-empty- пустое состояние<template #filters-empty="{ hasFilters }"> <div v-if="!hasFilters"> <p>🔍 Выполните поиск для получения фильтров</p> </div> </template> -
loading- состояние загрузки<template #loading="{ isLoading }"> <div v-if="isLoading">⏳ Загрузка фильтров...</div> </template> -
error- состояние ошибки<template #error="{ error }"> <div v-if="error">❌ {{ error }}</div> </template>
### BarneoSortWidget
Компонент сортировки с предустановленными опциями и кастомными слотами.
#### Props
```typescript
interface SortWidgetProps {
modelValue?: string; // Текущая сортировка (v-model)
options?: SortOption[]; // Кастомные опции сортировки
showLabels?: boolean; // Показывать метки опций
defaultSort?: string; // Сортировка по умолчанию
darkMode?: boolean; // Принудительная темная тема
disabled?: boolean; // Отключить компонент
}
События
@update:modelValue- изменена сортировка
(sortValue: string) => void
@sort-changed- изменена сортировка(sortValue: string) => void@loading- изменение состояния загрузки(isLoading: boolean) => void@error- произошла ошибка(error: string) => void
Слоты
sort-options- кастомный контейнер с опциями сортировки
<template
#sort-options="{ sortOptions, currentSort, isLoading, handleSortChange }"
>
<div class="custom-sort-container">
<div
v-for="option in sortOptions"
:key="option.value"
class="custom-sort-option"
:class="{ active: option.value === currentSort }"
@click="handleSortChange(option.value)"
>
{{ option.label }}
</div>
</div>
</template>
sort-option- кастомная опция сортировки
<template #sort-option="{ option, isActive, isDisabled }">
<div
class="custom-sort-option"
:class="{ active: isActive, disabled: isDisabled }"
>
<span>{{ option.label }}</span>
<span v-if="isActive" class="loading-indicator">⏳</span>
</div>
</template>
loading- состояние загрузки<template #loading="{ isLoading }"> <div v-if="isLoading">⏳ Обновление...</div> </template>error- состояние ошибки
<template #error="{ error }">
<div v-if="error">❌ {{ error }}</div>
</template>
BarneoRangeSelector
Компонент для выбора диапазонов с прогрессивной шкалой, двухползунковым слайдером и капсуловидными полями ввода.
Особенности
- Прогрессивная шкала: Более точный выбор в нижних диапазонах (первые 50% движения покрывают 25% значений)
- Двухползунковый слайдер: Интуитивный выбор минимального и максимального значения
- Капсуловидные поля ввода: Прямой ввод значений с метками "ОТ" и "ДО"
- Конвертация цен: Автоматическая конвертация копеек в рубли для ценовых фильтров
- Адаптивная разделительная линия: Линия между полями ввода растягивается на доступное пространство
- События при отпускании: События эмитятся только при отпускании ползунка для оптимизации производительности
Props
interface RangeSelectorProps {
min: number; // Минимальное значение
max: number; // Максимальное значение
value?: [number, number]; // Текущие значения [min, max]
step?: number; // Шаг изменения (по умолчанию: 1)
formatValue?: (value: number) => string; // Функция форматирования значений
}
События
@update:value- изменены значения(value: [number, number]) => void@change- изменены значения (эмитится при отпускании ползунка или потере фокуса)(value: [number, number]) => void
Методы (через ref)
interface RangeSelectorExpose {
setValue: (min: number, max: number) => void; // Установить значения
getValue: () => [number, number]; // Получить текущие значения
}
Пример использования
<template>
<barneo-range-selector
ref="rangeSelector"
:min="0"
:max="10000"
:value="[1000, 5000]"
:step="100"
:format-value="formatPrice"
@change="handleRangeChange"
/>
</template>
<script setup>
import { ref } from "vue";
const rangeSelector = ref();
const formatPrice = (value: number) => {
return `${Math.floor(value / 100)} ₽`; // Конвертация копеек в рубли
};
const handleRangeChange = ([min, max]: [number, number]) => {
console.log("Диапазон изменен:", { min, max });
};
// Программное изменение значений
const resetRange = () => {
rangeSelector.value?.setValue(0, 10000);
};
</script>
BarneoProductsWidget
Компонент отображения товаров с пагинацией, адаптивной сеткой и кастомными слотами.
Props
interface ProductsWidgetProps {
products?: Product[]; // Список товаров для отображения
loading?: boolean; // Состояние загрузки
error?: string | null; // Текст ошибки
pagination?: PaginationState; // Состояние пагинации
config?: ProductsWidgetConfig; // Локальная конфигурация
apiService?: SearchApiService; // Кастомный API сервис
}
События
@product-selected- выбран товар
(product: Product, event?: MouseEvent) => void
Примечание: Событие поддерживает MouseEvent для обработки средней кнопки мыши. При event.button === 1 рекомендуется открывать товар в новой вкладке.
@page-changed- изменена страница(page: number) => void@loading- изменение состояния загрузки(isLoading: boolean) => void@error- произошла ошибка(error: string) => void
Слоты
products-grid- кастомная сетка товаров
<template #products-grid="{ products, isLoading, itemWidth }">
<div class="custom-grid" :style="{ '--item-width': itemWidth + 'px' }">
<div v-for="product in products" :key="product.id" class="grid-item">
<!-- Кастомная карточка товара -->
</div>
</div>
</template>
product-card- кастомная карточка товара<template #product-card="{ product, index, onSelect, handleAuxClick }"> <div class="custom-product-card" @click="(event) => onSelect(product, event)" @auxclick="(event) => handleAuxClick(product, event)" > <img :src="https://raw.githubusercontent.com/denis-muhortov/barneo-search-widget-lib/HEAD/product.image" :alt="product.name" /> <h3>{{ product.name }}</h3> <p class="price">{{ formatPrice(product.price) }}</p> </div> </template>products-empty- пустое состояние<template #products-empty="{ hasProducts, query }"> <div v-if="!hasProducts && query"> <p>По запросу "{{ query }}" товары не найдены</p> </div> </template>products-pagination- кастомная пагинация<template #products-pagination="{ pagination, onPageChange }"> <div class="custom-pagination"> <button v-for="page in pagination.totalPages" :key="page" :class="{ active: page === pagination.currentPage }" @click="onPageChange(page)" > {{ page }} </button> </div> </template>loading- состояние загрузки<template #loading="{ isLoading }"> <div v-if="isLoading">⏳ Загрузка товаров...</div> </template>error- состояние ошибки<template #error="{ error }"> <div v-if="error">❌ {{ error }}</div> </template>
🎨 Кастомные слоты - примеры
Поиск с кастомными слотами
<barneo-search-widget v-model="query">
<template #search-header="{ config }">
<div class="custom-header">
<h1>🔍 Поиск товаров</h1>
<p>Конфигурация: {{ JSON.stringify(config) }}</p>
</div>
</template>
<template #search-results="{ results, isLoading }">
<div v-if="isLoading" class="loading">Загрузка...</div>
<div v-else class="results">
<div v-for="result in results" :key="result.id" class="result-item">
<h3>{{ result.name }}</h3>
<p>{{ result.description }}</p>
</div>
</div>
</template>
</barneo-search-widget>
Фильтры с кастомными слотами
<barneo-filters-widget>
<template #filters-title="{ config }">
<div class="custom-filters-title">
🎯 Фильтры ({{ config.maxValuesPerFilter }} значений)
</div>
</template>
<template #filter-value="{ filterCode, value, isActive, handleClick }">
<div class="custom-filter-value" @click="handleClick(filterCode, value.id)">
<input
type="checkbox"
:checked="isActive"
:id="`${filterCode}-${value.id}`"
/>
<label :for="`${filterCode}-${value.id}`">
{{ value.name }}
</label>
</div>
</template>
<template #filter-range="{
filter,
handleRangeSelectorChange,
rangeSelectorValue,
rangeStep,
rangeFormatValue,
isRangeActive,
rangeMin,
rangeMax
}">
<div class="custom-range-filter">
<!-- Кастомный заголовок range фильтра -->
<template #filter-range-header="{ filter }">
<h4 class="range-title">{{ filter.description }}</h4>
</template>
<!-- Кастомный индикатор активного диапазона -->
<template #filter-range-indicator="{ isRangeActive, rangeFormatValue }">
<div v-if="isRangeActive" class="custom-range-indicator">
<span class="indicator-icon">🎯</span>
<span class="indicator-text">{{ rangeFormatValue }}</span>
</div>
</template>
<!-- Кастомная кнопка сброса -->
<template #filter-range-reset="{ handleRangeSelectorChange, rangeMin, rangeMax }">
<button
class="custom-reset-button"
@click="handleRangeSelectorChange(filter.code, [rangeMin, rangeMax])"
>
<span class="reset-icon">🔄</span>
Сбросить
</button>
</template>
</div>
</template>
<template #filters-empty="{ hasFilters }">
<div v-if="!hasFilters" class="custom-empty">
<p>🔍 Выполните поиск для получения фильтров</p>
</div>
</template>
</barneo-filters-widget>
Сортировка с кастомными слотами
<barneo-sort-widget v-model="sortValue">
<template #sort-options="{ sortOptions, currentSort, isLoading, handleSortChange }">
<div class="custom-sort-container">
<div
v-for="option in sortOptions"
:key="option.value"
class="custom-sort-option"
:class="{ active: option.value === currentSort }"
@click="handleSortChange(option.value)"
>
{{ option.label }}
</div>
</div>
</template>
<template #sort-option="{ option, isActive, isDisabled }">
<div
class="custom-sort-option"
:class="{ active: isActive, disabled: isDisabled }"
>
<span class="sort-label">{{ option.label }}</span>
<span v-if="isActive" class="loading-spinner">⏳</span>
</div>
</template>
<template #loading="{ isLoading }">
<div v-if="isLoading" class="sort-loading">
Обновление результатов...
</div>
</template>
</barneo-sort-widget>
Товары с кастомными слотами
<barneo-products-widget>
<template #product-card="{ product, index, onSelect, handleAuxClick }">
<div
class="custom-product-card"
@click="(event) => onSelect(product, event)"
@auxclick="(event) => handleAuxClick(product, event)"
>
<div class="product-image">
<img :src="https://raw.githubusercontent.com/denis-muhortov/barneo-search-widget-lib/HEAD/product.image" :alt="product.name" />
</div>
<div class="product-info">
<h3 class="product-title">{{ product.name }}</h3>
<p class="product-price">{{ formatPrice(product.price) }}</p>
<p class="product-description">{{ product.description }}</p>
</div>
</div>
</template>
<template #products-pagination="{ pagination, onPageChange }">
<div class="custom-pagination">
<button
:disabled="pagination.currentPage === 1"
@click="onPageChange(pagination.currentPage - 1)"
>
← Назад
</button>
<span class="page-info">
Страница {{ pagination.currentPage }} из {{ pagination.totalPages }}
</span>
<button
:disabled="pagination.currentPage === pagination.totalPages"
@click="onPageChange(pagination.currentPage + 1)"
>
Вперед →
</button>
</div>
</template>
<template #products-empty="{ hasProducts, query }">
<div v-if="!hasProducts && query" class="custom-empty">
<p>😔 По запросу "{{ query }}" товары не найдены</p>
<p>Попробуйте изменить параметры поиска</p>
</div>
</template>
</barneo-products-widget>
Обработка средней кнопки мыши
Библиотека поддерживает открытие товаров в новой вкладке при нажатии средней кнопки мыши (колесико).
Пример обработки в BarneoSearchWidget
<template>
<barneo-search-widget v-model="query" @select="handleProductSelect" />
</template>
<script setup>
import { useRouter } from "vue-router";
const router = useRouter();
const handleProductSelect = (product, event) => {
console.log("Выбран товар:", product);
// Если это средняя кнопка мыши (колесико), открываем в новой вкладке
if (event && event.button === 1) {
window.open(`/product/${product.id}`, "_blank");
} else {
// Обычный клик - переходим в текущей вкладке
router.push(`/product/${product.id}`);
}
};
</script>
Пример обработки в BarneoProductsWidget
<template>
<barneo-products-widget @product-select="handleProductSelect" />
</template>
<script setup>
import { useRouter } from "vue-router";
const router = useRouter();
const handleProductSelect = (product, event) => {
console.log("Выбран товар из списка:", product);
// Если это средняя кнопка мыши (колесико), открываем в новой вкладке
if (event && event.button === 1) {
window.open(`/product/${product.id}`, "_blank");
} else {
// Обычный клик - переходим в текущей вкладке
router.push(`/product/${product.id}`);
}
};
</script>
Использование в кастомных слотах
<template>
<barneo-search-widget>
<template
#search-results="{ searchResults, selectProduct, handleAuxClick }"
>
<div v-for="product in searchResults" :key="product.id">
<div
class="product-item"
@click="(event) => selectProduct(product, event)"
@auxclick="(event) => handleAuxClick(product, event)"
>
<h3>{{ product.name }}</h3>
<p>{{ product.price }}</p>
</div>
</div>
</template>
</barneo-search-widget>
</template>
📊 Аналитика
Библиотека включает в себя модуль аналитики для отслеживания пользовательского поведения и взаимодействий с поисковым интерфейсом.
Использование аналитики
Базовое использование
import { useAnalytics } from "barneo-search-widget-lib";
// Инициализация аналитики (автоматически использует конфигурацию из BarneoConfigManager)
const analytics = useAnalytics();
// Проверка инициализации
console.log("Аналитика инициализирована:", analytics.isInitialized.value);
console.log("Конфигурация:", analytics.getConfigInfo.value);
Отслеживание событий
// Объединение неавторизованного и авторизованного пользователя
await analytics.mergeCustomers("guest-id-123", "authorized-id-456");
// Отслеживание поискового запроса
await analytics.trackQueryUse("сковорода");
// Отслеживание использования функций поиска
await analytics.trackFunctionUse("filter"); // фильтры
await analytics.trackFunctionUse("hint"); // подсказки
await analytics.trackFunctionUse("history"); // история
// Отслеживание применения подсказки
await analytics.trackHintUse("сков", "сковорода");
// Отслеживание конверсии товара
await analytics.trackProductConversion("product-123", "click", {
position: 1,
query: "сковорода",
source: "search",
});
// Отслеживание конверсии категории
await analytics.trackCategoryConversion("category-456", {
query: "посуда",
source: "search",
});
Быстрые методы (quickTrack)
// Быстрое отслеживание клика по товару
await analytics.quickTrack.productClick("product-123", {
position: 1,
query: "сковорода",
source: "search",
});
// Быстрое отслеживание добавления в корзину
await analytics.quickTrack.productBasket("product-123", {
position: 2,
query: "сковорода",
source: "search",
});
// Быстрое отслеживание заказа
await analytics.quickTrack.productOrder("product-123", {
position: 3,
query: "сковорода",
source: "search",
});
// Быстрое отслеживание клика по категории
await analytics.quickTrack.categoryClick("category-456", {
query: "посуда",
source: "search",
});
// Быстрое отслеживание использования функций
await analytics.quickTrack.filterUse(); // фильтры
await analytics.quickTrack.hintUse(); // подсказки
await analytics.quickTrack.historyUse(); // история
🎯 Рекомендации (Adviser)
Библиотека включает в себя модуль рекомендаций для получения умных рекомендаций товаров на основе поведения пользователей.
Использование рекомендаций
Базовое использование
import { useAdviser } from "barneo-search-widget-lib";
// Инициализация рекомендаций (автоматически использует конфигурацию из BarneoConfigManager)
const adviser = useAdviser({
enableCache: true,
cacheTTL: 300000, // 5 минут
apiLimit: 8,
});
// Проверка инициализации
console.log("Рекомендации инициализированы:", adviser.state.value);
Получение рекомендаций
// "People also search for this product" - что искали с этим товаром
const peopleAlsoSearch = await adviser.getPeopleAlsoSearch("product-123");
// "Searchers also viewed" - что смотрели искавшие
const searchersAlsoViewed = await adviser.getSearchersAlsoViewed("сковорода");
// "Cross Sell" - с этим товаром покупают
const crossSell = await adviser.getCrossSell("product-123");
// "Up Sell" - более дорогие альтернативы
const upSell = await adviser.getUpSell("product-123");
// "Up Sell New" - новая версия up sell
const upSellNew = await adviser.getUpSellNew("product-123");
// "Recently watched" - недавно просмотренные
const recentlyWatched = await adviser.getRecentlyWatched("customer-456");
// "Popular products" - популярные товары
const popularProducts = await adviser.getPopularProducts();
// "Similar products" - похожие товары
const similarProducts = await adviser.getSimilarProducts("product-123");
Состояние и кэширование
// Проверка состояния загрузки
if (adviser.state.value.loadingStatus === "loading") {
console.log("Загрузка рекомендаций...");
}
// Обработка ошибок
if (adviser.state.value.error) {
console.error("Ошибка рекомендаций:", adviser.state.value.error);
}
// Принудительная очистка кэша
adviser.forceClear();
// Получение всех рекомендаций сразу
const allRecommendations = adviser.state.value;
console.log("Все рекомендации:", allRecommendations);
Тестирование всех рекомендаций
// Тестирование всех типов рекомендаций
const testAdviser = async () => {
console.log("🎯 Тестирование модуля adviser...");
// Очищаем состояние для чистого теста
adviser.forceClear();
// Тестируем все типы рекомендаций
await adviser.getPeopleAlsoSearch("test-product");
await adviser.getSearchersAlsoViewed("тестовый запрос");
await adviser.getCrossSell("test-product");
await adviser.getUpSell("test-product");
await adviser.getUpSellNew("test-product");
await adviser.getRecentlyWatched("test-customer");
await adviser.getPopularProducts();
await adviser.getSimilarProducts("test-product");
console.log("✅ Все тесты рекомендаций завершены");
};
Использование аналитики
Базовое использование
import { useAnalytics } from "barneo-search-widget-lib";
// Инициализация аналитики (автоматически использует конфигурацию из BarneoConfigManager)
const analytics = useAnalytics();
// Проверка инициализации
console.log("Аналитика инициализирована:", analytics.isInitialized.value);
console.log("Конфигурация:", analytics.getConfigInfo.value);
Отслеживание событий
// Объединение неавторизованного и авторизованного пользователя
await analytics.mergeCustomers("guest-id-123", "authorized-id-456");
// Отслеживание поискового запроса
await analytics.trackQueryUse("сковорода");
// Отслеживание использования функций поиска
await analytics.trackFunctionUse("filter"); // фильтры
await analytics.trackFunctionUse("hint"); // подсказки
await analytics.trackFunctionUse("history"); // история
// Отслеживание применения подсказки
await analytics.trackHintUse("сков", "сковорода");
// Отслеживание конверсии товара
await analytics.trackProductConversion("product-123", "click", {
position: 1,
query: "сковорода",
source: "search",
});
// Отслеживание конверсии категории
await analytics.trackCategoryConversion("category-456", {
query: "посуда",
source: "search",
});
Быстрые методы (quickTrack)
// Быстрое отслеживание клика по товару
await analytics.quickTrack.productClick("product-123", {
position: 1,
query: "сковорода",
source: "search",
});
// Быстрое отслеживание добавления в корзину
await analytics.quickTrack.productBasket("product-123", {
position: 2,
query: "сковорода",
source: "search",
});
// Быстрое отслеживание заказа
await analytics.quickTrack.productOrder("product-123", {
position: 3,
query: "сковорода",
source: "search",
});
// Быстрое отслеживание клика по категории
await analytics.quickTrack.categoryClick("category-456", {
query: "посуда",
source: "search",
});
// Быстрое отслеживание использования функций
await analytics.quickTrack.filterUse(); // фильтры
await analytics.quickTrack.hintUse(); // подсказки
await analytics.quickTrack.historyUse(); // история
Состояние и обработка ошибок
// Проверка состояния загрузки
if (analytics.isLoading.value) {
console.log("Отправка аналитических данных...");
}
// Обработка ошибок
if (analytics.error.value) {
console.error("Ошибка аналитики:", analytics.error.value);
}
// Очистка ошибки
analytics.clearError();
Ручная инициализация
// Если нужно инициализировать аналитику с кастомной конфигурацией
analytics.initializeAnalytics({
baseUrl: "https://api.search.ensi.cloud",
token: "your-token",
customerId: "4",
locationId: "1",
apiVersion: "v1",
});
Доступные типы событий
SearchFunction (функции поиска)
filter- использование фильтровhint- использование подсказокhistory- использование истории поискаpopular_auto_queries- популярные автозапросыpopular_manual_queries- популярные ручные запросыrecommended_queries- рекомендуемые запросы
ProductConversionType (типы конверсии товаров)
click- клик по товаруbasket- добавление в корзинуorder- заказ товара
ProductConversionSource (источники конверсии товаров)
search- из поискаcategory- из категорииrecommendation- из рекомендаций
CategoryConversionSource (источники конверсии категорий)
search- из поискаnavigation- из навигации
Интеграция с компонентами
Аналитика автоматически интегрируется с компонентами библиотеки через BarneoConfigManager:
import { useBarneoConfig } from "barneo-search-widget-lib";
const { analyticsService } = useBarneoConfig();
// Прямой доступ к сервису аналитики
await analyticsService.trackQueryUse("тестовый запрос");
Пример полного использования
<template>
<div>
<barneo-search-widget v-model="query" @search="handleSearch" />
<barneo-products-widget @product-select="handleProductSelect" />
<!-- Кнопка для тестирования аналитики -->
<button @click="testAnalytics">🧪 Тестировать аналитику</button>
</div>
</template>
<script setup>
import { ref } from "vue";
import {
useAnalytics,
ProductConversionSource,
} from "barneo-search-widget-lib";
const query = ref("");
const analytics = useAnalytics();
const handleSearch = async (searchQuery) => {
console.log("Поиск:", searchQuery);
// Отслеживаем поисковый запрос
await analytics.trackQueryUse(searchQuery);
};
const handleProductSelect = async (product, event) => {
console.log("Выбран товар:", product);
// Отслеживаем клик по товару
await analytics.quickTrack.productClick(product.id, {
position: 1,
query: query.value,
source: ProductConversionSource.SEARCH,
});
// Обработка средней кнопки мыши
if (event && event.button === 1) {
window.open(`/product/${product.id}`, "_blank");
} else {
router.push(`/product/${product.id}`);
}
};
const testAnalytics = async () => {
try {
// Тестируем все функции аналитики
await analytics.mergeCustomers("guest-123", "auth-456");
await analytics.trackQueryUse("тестовый запрос");
await analytics.quickTrack.filterUse();
await analytics.quickTrack.productClick("test-product", {
position: 1,
source: ProductConversionSource.SEARCH,
});
console.log("✅ Все тесты аналитики завершены");
} catch (error) {
console.error("❌ Ошибка аналитики:", error);
}
};
</script>
🔧 Дополнительные возможности
URL менеджер
Библиотека поддерживает управление URL параметрами для сохранения состояния поиска. URL менеджер инициализируется в компонентах через composable.
Инициализация URL менеджера
<script setup>
import { useUrlManagerInit } from "barneo-search-widget-lib";
// Инициализируем URL менеджер для работы с query параметрами
const { urlManager } = useUrlManagerInit();
</script>
Использование в компонентах
<template>
<div>
<barneo-search-widget v-model="query" />
<barneo-filters-widget />
<barneo-sort-widget />
<barneo-products-widget />
</div>
</template>
<script setup>
import { useUrlManagerInit } from "barneo-search-widget-lib";
// Инициализируем URL менеджер в компоненте
const { urlManager } = useUrlManagerInit();
// URL менеджер автоматически синхронизирует состояние с URL параметрами
</script>
Управляемые URL параметры
q- поисковый запросsort- текущая сортировкаpage- текущая страницаfilters- активные фильтры (JSON)
Динамическое обновление конфигурации
import { BarneoConfigManager } from "barneo-search-widget-lib";
const configManager = BarneoConfigManager.getInstance();
// Обновление конфигурации поиска
configManager.updateSearchWidgetConfig({
placeholder: "Новый placeholder",
darkMode: true,
});
// Обновление конфигурации фильтров
configManager.updateFiltersWidgetConfig({
maxValuesPerFilter: 10,
darkMode: true,
});
// Обновление конфигурации товаров
configManager.updateProductsWidgetConfig({
itemsPerPage: 30,
showPagination: false,
});
// Обновление конфигурации сортировки
configManager.updateSortWidgetConfig({
defaultSort: "price",
darkMode: true,
});
Управление URL параметрами
Библиотека автоматически управляет URL параметрами для сохранения состояния поиска:
q- поисковый запросsort- текущая сортировкаpage- текущая страницаfilters- активные фильтры (JSON)
// Пример URL с параметрами
// https://example.com/search?q=сковорода&sort=price&page=2&filters=%7B%22brands%22%3A%5B%22Tefal%22%5D%7D
Использование composables
import {
useSearchWidget,
useFiltersWidget,
useSortWidget,
useProductsWidget,
useSharedState,
} from "barneo-search-widget-lib";
// Общее состояние
const { searchState } = useSharedState();
// Поиск
const { performSearch, searchQuery } = useSearchWidget();
// Фильтры
const { applyFilter, clearFilters, activeFilters } = useFiltersWidget();
// Сортировка
const { setSort, currentSort } = useSortWidget();
// Товары
const { products, pagination, changePage } = useProductsWidget();
📘 TypeScript поддержка
Автоматическая типизация глобальных компонентов
При использовании плагина библиотеки глобальные компоненты автоматически получают типизацию:
<template>
<!-- TypeScript автоматически распознает типы этих компонентов -->
<barneo-search-widget v-model="query" @search="handleSearch" />
<barneo-filters-widget @filter-applied="handleFilter" />
<barneo-sort-widget @sort-changed="handleSort" />
<barneo-products-widget @product-selected="handleProductSelect" />
<barneo-range-selector
ref="rangeSelector"
:min="0"
:max="10000"
@change="handleRangeChange"
/>
</template>
<script setup lang="ts">
// Типизация работает автоматически без дополнительных импортов
const query = ref("")
const handleSearch = (query: string) => {
console.log("Поиск:", query)
}
</script>
### Ручная типизация (если нужно)
Если вы используете локальную регистрацию компонентов, добавьте типизацию:
```typescript
// types/barneo.d.ts
import type { BarneoSearchWidget } from 'barneo-search-widget-lib'
import type { BarneoFiltersWidget } from 'barneo-search-widget-lib'
import type { BarneoSortWidget } from 'barneo-search-widget-lib'
import type { BarneoProductsWidget } from 'barneo-search-widget-lib'
import type { BarneoRangeSelector } from 'barneo-search-widget-lib'
declare module '@vue/runtime-core' {
export interface GlobalComponents {
'barneo-search-widget': typeof BarneoSearchWidget
'barneo-filters-widget': typeof BarneoFiltersWidget
'barneo-sort-widget': typeof BarneoSortWidget
'barneo-products-widget': typeof BarneoProductsWidget
'barneo-range-selector': typeof BarneoRangeSelector
BarneoSearchWidget: typeof BarneoSearchWidget
BarneoFiltersWidget: typeof BarneoFiltersWidget
BarneoSortWidget: typeof BarneoSortWidget
BarneoProductsWidget: typeof BarneoProductsWidget
BarneoRangeSelector: typeof BarneoRangeSelector
}
}
export {}
Импорт типов
import type {
BarneoSearchWidget,
BarneoFiltersWidget,
BarneoSortWidget,
BarneoProductsWidget,
BarneoRangeSelector,
SearchConfig,
SearchResult,
SearchHint,
SearchApiService,
SearchRequest,
SearchResponse,
// Типы аналитики
SearchFunction,
ProductConversionType,
ProductConversionSource,
CategoryConversionSource,
CategoryConversionType,
AuthorizeCustomerRequest,
QueryUseRequest,
FunctionalUseRequest,
HintsUseRequest,
ProductsUseRequest,
CategoriesUseRequest,
// Типы рекомендаций
AdviserBlockType,
AdviserFilterType,
AdviserSortType,
LoadingStatus,
IncludeType,
FullProduct,
ProductProperty,
} from "barneo-search-widget-lib";
🤝 Поддержка
- Vue 3.x
- TypeScript
- Nuxt 3
- Vite
- Webpack
📄 Лицензия
MIT License
Related Packages
Meta (Facebook) Pixel module for Nuxt 3 and 4. Auto-imported composable, per-rou...
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 2.2.1 | ^3.0.0 | MIT | |
| 2.2.0 | ^3.0.0 | MIT | |
| 2.1.0 | ^3.0.0 | MIT | |
| 2.0.19 | ^3.0.0 | MIT | |
| 2.0.18 | ^3.0.0 | MIT | |
| 2.0.17 | ^3.0.0 | MIT | |
| 2.0.16 | ^3.0.0 | MIT | |
| 2.0.15 | ^3.0.0 | MIT | |
| 2.0.14 | ^3.0.0 | MIT | |
| 2.0.13 | ^3.0.0 | MIT | |
| 2.0.12 | ^3.0.0 | MIT | |
| 2.0.11 | ^3.0.0 | MIT | |
| 2.0.10 | ^3.0.0 | MIT | |
| 2.0.9 | ^3.0.0 | MIT | |
| 2.0.8 | ^3.0.0 | MIT | |
| 2.0.7 | ^3.0.0 | MIT | |
| 2.0.6 | ^3.0.0 | MIT | |
| 2.0.5 | ^3.0.0 | MIT | |
| 2.0.4 | ^3.0.0 | MIT | |
| 2.0.3 | ^3.0.0 | MIT | |
| 2.0.2 | ^3.0.0 | MIT | |
| 2.0.1 | ^3.0.0 | MIT | |
| 2.0.0 | ^3.0.0 | MIT | |
| 1.0.1 | ^3.0.0 | MIT | |
| 1.0.0 | ^3.0.0 | MIT |