Skip to content

Подмены блоков ​

Блок «Ничего не найдено» и витрину товаров в нём можно заменить своим компонентом — например, показать там рекомендации Mindbox или популярные товары раздела, в котором сейчас покупатель. Откуда брать товары и как их рисовать, решаете вы; рисуются они вашей же карточкой товара, со всеми её фото, метками и ценами.

Подмены пишутся в редакторе виджета, на вкладке «Блоки» блока «Код виджета». Нужна роль владельца аккаунта или Суперадмина.

Что можно подменить ​

БлокЧто заменяетсяГде
Блок «Ничего не найдено»весь экран пустой выдачи: сообщение с кнопкой выхода («Сбросить фильтры» / «Изменить запрос»), блок помощи, витринаотдельно для автодополнения и для полностраничного поиска
Популярные товары на пустой выдачетолько витрина товаров под сообщениемтак же

Подмена действует во всех темах поверхности: смените тему автодополнения — ваш блок останется на месте. Удалите подмену — вернётся блок темы.

В теме classic автодополнения витрина на пустой выдаче показывается на компьютере (на телефоне панель товаров не рисует вообще). Выключить её можно в настройках сборки — блок «Популярные товары на пустой выдаче».

Как добавить ​

  1. Вкладка «Блоки» → «Добавить подмену» → выберите блок. В списке только блоки включённых поверхностей.
  2. Редактор заполнится заготовкой: она рисует блок темы, поэтому сохранение без правок ничего на сайте не меняет.
  3. Замените разметку своей и нажмите «Сохранить и опубликовать».

Блок появляется, когда поиск окончательно ничего не нашёл — по набранному запросу или по выбранным фильтрам, — и живёт, пока выдача остаётся пустой. Внешний источник вызывается один раз на показ пустой выдачи. Свойство query — запрос, на который выдача пуста, одинаково во всех темах: пока покупатель набирает следующий, компонент видит прежний. Сменил запрос на другой, тоже пустой, — блок не пересоздаётся, новый запрос приходит в query один раз, когда поиск ответил; компоненту, которому он важен, стоит за ним следить (watch(() => props.query, …)).

Что доступно компоненту ​

Компонент импортирует всё из @searchforce/tenant-kit; карточку — из @tenant/card, как и остальной код виджета.

Свойства блока:

БлокСвойства
«Ничего не найдено» (NoResultsBlockProps)query — запрос; hasActiveFilters — пусто из-за фильтров, а не из-за запроса; clearFilters(), clearQuery() — выходы из тупика; surface — autocomplete или fullpage
«Популярные товары» (PopularProductsBlockProps)query; limit — сколько товаров вмещает раскладка темы (5 или 6); view — строки или сетка (тема modern); surface

Инструменты:

ЧтоЗачем
useProductsByIds(ids)список id из любого источника → ваши карточки. Порядок — как в списке; товары, которых нет в каталоге или которые скрыты правилами и настройками наличия, пропускаются. До 30 товаров
useCatalogProducts({ filters, limit })популярные товары каталога, суженные фильтрами: { category_names: 'Кроссовки' }
DefaultBlockблок темы на этом месте — запасной вариант, если источник не ответил
PopularProductsвитрина темы внутри вашего блока «Ничего не найдено»
ProductCarouselлента карточек со стрелками и свайпом
ContactHelp, useContactHelpблок помощи с контактами из настроек проекта

Оба загрузчика отдают { items, loading, error }. Товары в них — те же, что в выдаче: действует ограничение выдачи с сайта (scope), клики и заказы с этих карточек попадают в аналитику поиска как переходы из блока популярных товаров — в том числе когда товары подобраны по id или по разделу.

Рекомендации Mindbox ​

vue
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import {
  DefaultBlock,
  ProductCarousel,
  useProductsByIds,
  type Hit,
  type PopularProductsBlockProps,
} from '@searchforce/tenant-kit'

const props = defineProps<PopularProductsBlockProps>()
const ids = ref<string[] | null>(null)

type MindboxWindow = Window & { mindbox?: (mode: 'sync', request: Record<string, unknown>) => void }

onMounted(() => {
  const mindbox = (window as MindboxWindow).mindbox
  if (!mindbox) {
    ids.value = []
    return
  }

  // Не ответил за 2 секунды — показываем блок темы.
  const timer = setTimeout(() => {
    ids.value ??= []
  }, 2000)

  mindbox('sync', {
    operation: 'Website.GetPopularProducts', // имя операции из вашей точки интеграции
    data: { recommendation: { limit: props.limit } },
    onSuccess: (response: { recommendations?: Array<{ ids?: Record<string, string> }> }) => {
      clearTimeout(timer)
      ids.value = (response.recommendations ?? []).map((item) => item.ids?.website ?? '').filter(Boolean)
    },
    onError: () => {
      clearTimeout(timer)
      ids.value = []
    },
  })
})

const { items, loading } = useProductsByIds<Hit>(ids, { limit: props.limit })
</script>

<template>
  <!-- Блок темы — только когда ответ уже есть и пуст: пока идёт подстановка по
       id (loading), не рисуем ничего, иначе блок темы мигнёт своим запросом. -->
  <DefaultBlock v-if="ids !== null && !loading && items.length === 0" />
  <section v-else-if="items.length > 0" class="reco">
    <h3 class="reco__title">Рекомендуем вам</h3>
    <ProductCarousel :items="items" size="s" label="Рекомендуем вам" />
  </section>
</template>

Классы reco и reco__title — ваши: стили для них кладите в theme/tokens.css. Раскладка из набора (ProductCarousel) от темы не зависит, поэтому блок переживёт смену темы автодополнения.

Перед запуском проверьте:

  • Вызов — sync: в режиме async Mindbox ответа не возвращает.
  • Домен магазина указан в точке интеграции Mindbox («Сайты с JavaScript SDK»), а политика безопасности сайта (CSP) пропускает https://*.mindbox.ru — иначе запрос отклонят.
  • Ключ в ids — внешняя система, под которой Mindbox знает товары сайта (website, bitrixId или своя). Эти id должны совпадать с id офферов в вашем фиде: сверьте на паре товаров.
  • Ошибки внутри колбэков Mindbox обрабатывайте сами (onError): SearchForce их не видит.

Популярное в разделе страницы ​

vue
<script setup lang="ts">
import { computed } from 'vue'
import { DefaultBlock, ProductCarousel, useCatalogProducts, type PopularProductsBlockProps } from '@searchforce/tenant-kit'

const props = defineProps<PopularProductsBlockProps>()

// Раздел, где сейчас покупатель: последняя хлебная крошка страницы.
const section = document.querySelector('.breadcrumbs li:last-child')?.textContent?.trim() ?? ''

const { items, loading } = useCatalogProducts(() =>
  section ? { filters: { category_names: section }, limit: props.limit } : null,
)
const title = computed(() => `Популярное в разделе «${section}»`)
</script>

<template>
  <DefaultBlock v-if="!section || (!loading && items.length === 0)" />
  <section v-else-if="items.length > 0">
    <h2>{{ title }}</h2>
    <ProductCarousel :items="items" :label="title" />
  </section>
</template>

Значение фильтра — как в каталоге (название категории из фида). Если раздел на странице не определился, остаётся блок темы.

Если что-то пошло не так ​

  • Компонент упал при отрисовке или при запуске — на сайте останется блок темы, а в консоли браузера и в событии diagnostic появится сообщение с именем файла. Покупатель пустого места не увидит.
  • Ошибка в обработчике (клик по кнопке внутри вашего блока) блок не снимает: он уже нарисован, ошибка попадёт в консоль как обычно.
  • Ошибки внутри колбэков Mindbox SearchForce не видит — обрабатывайте их в onError, например показав DefaultBlock.
  • Опечатка в имени файла не пройдёт публикацию: имя блока выбирается из списка, а сборка не принимает файлы, которые ничего не подменяют.
  • Вспомогательные компоненты для подмены кладите в компоненты карточки и импортируйте относительным путём (../../card/Tile.vue).