Розробка компонента порівняння товарів на Vue.js для 1С-Бітрікс

Наша компанія займається розробкою, підтримкою та обслуговуванням рішень на Бітрікс та Бітрікс24 будь-якої складності. Від простих односторінкових сайтів до складних інтернет-магазинів, CRM систем з інтеграцією 1С та телефонії. Досвід розробників підтверджено сертифікатами від вендора.
Пропоновані послуги
Показано 1 з 1 послугУсі 1626 послуг
Розробка компонента порівняння товарів на Vue.js для 1С-Бітрікс
Середня
~1-2 тижні
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Розробка на базі Бітрікс, Бітрікс24, 1С для компанії Development of an Online
    585
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Розробка на базі 1С Підприємство для компанії МИРСАНБЕЛ
    751
  • image_crm_dolbimby_434_0.webp
    Розробка сайту на CRM Бітрікс24 для компанії DOLBIMBY
    657
  • image_crm_technotorgcomplex_453_0.webp
    Розробка на базі Бітрікс24 для компанії ТЕХНОТОРГКОМПЛЕКС
    989

Розробка компонента порівняння товарів на Vue.js для 1С-Бітрікс

Стандартний компонент порівняння Бітрікс (bitrix:catalog.compare.buy + bitrix:catalog.compare.result) працює через перезавантаження сторінки: користувач додає товар у порівняння, сторінка оновлюється, лічильник змінюється. На сторінці порівняння — статична таблиця. Все це працювало у 2010 році; сьогодні UX-очікування інші. Vue.js-версія: додавання в порівняння — миттєво, лічильник у шапці оновлюється без перезавантаження, таблиця порівняння вміє приховувати однакові характеристики.

Зберігання списку порівняння

Стандартний Бітрікс зберігає порівняння у сесії PHP ($_SESSION). Для Vue-версії краще localStorage із синхронізацією на сервер для авторизованих користувачів — так список зберігається між сесіями і пристроями.

// stores/compareStore.ts (Pinia)
export const useCompareStore = defineStore('compare', {
    state: () => ({
        items: [] as number[], // product IDs
    }),

    actions: {
        async add(productId: number) {
            if (this.items.includes(productId)) return
            if (this.items.length >= 4) {
                alert('Можна порівнювати до 4 товарів одночасно')
                return
            }
            this.items.push(productId)
            localStorage.setItem('compare_items', JSON.stringify(this.items))

            // Синхронізація з сервером для авторизованих
            if (isLoggedIn()) {
                await fetch('/local/api/compare/add/', {
                    method: 'POST',
                    body: JSON.stringify({ product_id: productId }),
                })
            }
        },

        remove(productId: number) {
            this.items = this.items.filter(id => id !== productId)
            localStorage.setItem('compare_items', JSON.stringify(this.items))
        },

        isInCompare: (state) => (productId: number) => state.items.includes(productId),
    },

    getters: {
        count: (state) => state.items.length,
    },
})

При ініціалізації застосунку список завантажується з localStorage (для гостей) або з API (для авторизованих, з мерджем між пристроями).

Кнопка «Порівняти» на картці товару

<!-- CompareButton.vue -->
<template>
  <button
    :class="['compare-btn', { 'compare-btn--active': isAdded }]"
    @click="toggle"
    :title="isAdded ? 'Прибрати з порівняння' : 'Додати до порівняння'"
  >
    <IconScale :filled="isAdded" />
    <span>{{ isAdded ? 'У порівнянні' : 'Порівняти' }}</span>
  </button>
</template>

<script setup lang="ts">
const props = defineProps<{ productId: number }>()
const store = useCompareStore()
const isAdded = computed(() => store.isInCompare(props.productId))

function toggle() {
    isAdded.value ? store.remove(props.productId) : store.add(props.productId)
}
</script>

Кнопка монтується на кожній картці товару. У Бітрікс-шаблоні:

// В template.php картки товару
?>
<div data-compare-btn data-product-id="<?= $arItem['ID'] ?>"></div>
<?php
// Після завантаження сторінки Vue монтує CompareButton у кожен [data-compare-btn]

Лічильник у шапці сайту

<!-- CompareCounter.vue (монтується в header) -->
<template>
  <a href="/compare/" class="header-compare">
    <IconScale />
    <span class="badge" v-if="count > 0">{{ count }}</span>
  </a>
</template>

<script setup lang="ts">
const store = useCompareStore()
const count = computed(() => store.count)
</script>

Лічильник реактивно оновлюється через Pinia — жодного глобального EventBus або DOM-маніпуляцій.

Сторінка порівняння

Найскладніша частина — таблиця порівняння. Дані про товари завантажуються з сервера за списком ID:

// В CompareTable.vue
async function loadCompareData(ids: number[]) {
    const res  = await fetch(`/local/api/compare/?ids=${ids.join(',')}`)
    const data = await res.json() // [{id, name, price, specs: {...}}, ...]
    products.value = data
    buildDiffMap()
}

// Визначаємо однакові характеристики для можливості їх приховати
function buildDiffMap() {
    const allSpecs = new Set(products.value.flatMap(p => Object.keys(p.specs)))
    diffMap.value  = {}

    allSpecs.forEach(spec => {
        const values = products.value.map(p => p.specs[spec])
        diffMap.value[spec] = new Set(values).size > 1 // true = є відмінності
    })
}

Фільтр «Показати тільки відмінності» — кнопка, яка приховує рядки, де всі значення однакові:

<template>
  <table class="compare-table">
    <thead>
      <tr>
        <th>Характеристика</th>
        <th v-for="p in products" :key="p.id">
          <ProductCard :product="p" @remove="store.remove(p.id)" />
        </th>
      </tr>
    </thead>
    <tbody>
      <tr
        v-for="spec in visibleSpecs"
        :key="spec"
        :class="{ 'row--different': diffMap[spec] }"
      >
        <td class="spec-name">{{ specLabels[spec] }}</td>
        <td v-for="p in products" :key="p.id">{{ p.specs[spec] ?? '—' }}</td>
      </tr>
    </tbody>
  </table>
</template>

<script setup lang="ts">
const showOnlyDiff = ref(false)
const visibleSpecs = computed(() =>
    showOnlyDiff.value
        ? Object.keys(diffMap.value).filter(k => diffMap.value[k])
        : Object.keys(diffMap.value)
)
</script>

PHP-контролер даних порівняння

// CompareController.php
public function getAction(string $ids): array {
    $idArr = array_filter(array_map('intval', explode(',', $ids)));
    if (!$idArr) return [];

    $products = [];
    $res = CIBlockElement::GetList(
        [],
        ['ID' => $idArr, 'IBLOCK_ID' => CATALOG_IBLOCK_ID, 'ACTIVE' => 'Y'],
        false, false,
        ['ID', 'NAME', 'PREVIEW_PICTURE', 'DETAIL_PAGE_URL', 'CATALOG_PRICE_1_']
    );

    while ($el = $res->GetNext()) {
        // Отримуємо властивості для порівняння
        $props = [];
        $prRes = CIBlockElement::GetProperty(CATALOG_IBLOCK_ID, $el['ID'], [], ['COMPARE' => 'Y']);
        while ($pr = $prRes->Fetch()) {
            $props[$pr['CODE']] = $pr['VALUE'];
        }
        $products[] = [
            'id'    => $el['ID'],
            'name'  => $el['NAME'],
            'price' => $el['CATALOG_PRICE_1_'],
            'image' => $el['PREVIEW_PICTURE'] ? CFile::GetPath($el['PREVIEW_PICTURE']) : null,
            'url'   => $el['DETAIL_PAGE_URL'],
            'specs' => $props,
        ];
    }

    return $products;
}

Властивості, що беруть участь у порівнянні, позначаються прапором COMPARE = 'Y' у налаштуваннях інфоблоку — стандартний механізм Бітрікс.

Зафіксована перша колонка на мобільному

Таблиця порівняння на мобільному — горизонтальне прокручування. Перша колонка (назви характеристик) має бути зафіксована:

.compare-table {
    display: grid;
    grid-template-columns: 180px repeat(4, 1fr);
    overflow-x: auto;
}

.compare-table td:first-child,
.compare-table th:first-child {
    position: sticky;
    left: 0;
    background: white;
    z-index: 1;
}

CSS Grid з position: sticky — надійніший підхід, ніж display: table з фіксованими колонками.

Терміни

Обсяг Що входить Термін
Базове порівняння Кнопки + лічильник + таблиця 1–2 тижні
Повноцінний компонент + diff-фільтр, localStorage, синхронізація 2–3 тижні
+ Мобільний адаптив, persist для ЛК + sticky колонка, збереження для юзера +1 тиждень

Порівняння товарів — інструмент для покупців, які вибирають серед схожих позицій. Правильно реалізоване порівняння допомагає прийняти рішення на сайті, а не піти на агрегатор.