Розробка компонентів відгуків та рейтингів на 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С-Бітрікс

Стандартний підхід до відгуків на Бітрікс — PHP-компонент з повним оновленням сторінки при додаванні відгуку. Користувач заповнює форму, натискає "Відправити", сторінка оновлюється. Для 2014 року це було нормально, для сучасної електронної комерції — ні. Vue.js-компонент вирішує задачу інакше: форма, список відгуків, пагінація, сортування — все працює без оновлення, з миттєвим зворотним зв'язком.

Архітектура інтеграції

Бітрікс залишається бекендом та джерелом правди. Vue працює в браузері поверх PHP-сторінки. Інтеграція відбувається через:

  1. PHP передає вхідні дані до Vue — перші N відгуків рендеруються на сервері (або передаються як JSON), Vue гідрує їх та бере управління
  2. REST API — усі подальші дії (завантажити ще, додати відгук, проголосувати за корисність) йдуть через AJAX до PHP-контролера

Серверна сторона: PHP-контролер

Клас контролера на основі \Bitrix\Main\Engine\Controller:

// /local/components/custom/reviews/controller.php
namespace Custom\Reviews;

class ReviewController extends \Bitrix\Main\Engine\Controller {

    public function getListAction(int $productId, int $page = 1, string $sort = 'date'): array {
        $limit  = 10;
        $offset = ($page - 1) * $limit;

        $reviews = ReviewTable::getList([
            'filter' => ['=PRODUCT_ID' => $productId, '=STATUS' => 'approved'],
            'order'  => $sort === 'rating' ? ['RATING' => 'DESC'] : ['DATE_CREATE' => 'DESC'],
            'limit'  => $limit,
            'offset' => $offset,
            'select' => ['ID', 'AUTHOR_NAME', 'RATING', 'TITLE', 'BODY', 'DATE_CREATE', 'HELPFUL_YES'],
        ])->fetchAll();

        $total = ReviewTable::getCount(['=PRODUCT_ID' => $productId, '=STATUS' => 'approved']);

        return ['reviews' => $reviews, 'total' => $total, 'page' => $page];
    }

    public function addAction(int $productId, int $rating, string $title, string $body): array {
        global $USER;
        // Перевірка: користувач купив цей товар
        if (!$this->hasPurchased($USER->GetID(), $productId)) {
            return $this->addError(new \Bitrix\Main\Error('Відгук доступний тільки для покупців'));
        }
        // ...збереження відгуку зі статусом 'очікування'
        return ['success' => true, 'message' => 'Відгук відправлено на модерацію'];
    }

    public function voteHelpfulAction(int $reviewId, string $type): array {
        // type: 'yes' | 'no'
        // ...
    }
}

Контролер реєструється в init.php:

\Bitrix\Main\EventManager::getInstance()->addEventHandler(
    'main', 'OnProlog',
    fn() => \Custom\Reviews\ReviewController::register()
);

Vue-компонент: Reviews.vue

<template>
  <div class="reviews-widget">
    <!-- Зведений рейтинг -->
    <div class="rating-summary">
      <StarRating :value="summary.avg" :readonly="true" />
      <span>{{ summary.avg.toFixed(1) }} з 5 ({{ summary.total }} відгуків)</span>
      <RatingBars :distribution="summary.distribution" />
    </div>

    <!-- Форма нового відгуку -->
    <ReviewForm v-if="canReview" @submitted="onReviewSubmitted" />

    <!-- Сортування -->
    <select v-model="sort" @change="loadReviews(1)">
      <option value="date">За датою</option>
      <option value="rating">За рейтингом</option>
      <option value="helpful">За корисністю</option>
    </select>

    <!-- Список відгуків -->
    <ReviewCard
      v-for="review in reviews"
      :key="review.ID"
      :review="review"
      @helpful-vote="voteHelpful"
    />

    <!-- Пагінація -->
    <button v-if="hasMore" @click="loadMore" :disabled="loading">
      {{ loading ? 'Завантаження...' : 'Показати ще' }}
    </button>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import type { Review, ReviewSummary } from './types'

const props = defineProps<{ productId: number; initialReviews: Review[] }>()

const reviews  = ref<Review[]>(props.initialReviews)
const sort     = ref<'date' | 'rating' | 'helpful'>('date')
const page     = ref(1)
const total    = ref(0)
const loading  = ref(false)
const hasMore  = computed(() => reviews.value.length < total.value)

async function loadReviews(resetPage = 1) {
    loading.value = true
    const res = await fetch(`/local/api/reviews/?product=${props.productId}&page=${resetPage}&sort=${sort.value}`)
    const data = await res.json()
    reviews.value = resetPage === 1 ? data.reviews : [...reviews.value, ...data.reviews]
    total.value   = data.total
    page.value    = resetPage
    loading.value = false
}

async function loadMore() {
    await loadReviews(page.value + 1)
}

async function voteHelpful(reviewId: number, type: 'yes' | 'no') {
    await fetch('/local/api/reviews/vote/', { method: 'POST', body: JSON.stringify({ id: reviewId, type }) })
    // оптимістичне оновлення лічильника
    const review = reviews.value.find(r => r.ID === reviewId)
    if (review) review.HELPFUL_YES += type === 'yes' ? 1 : 0
}

onMounted(() => { if (!props.initialReviews.length) loadReviews() })
</script>

StarRating.vue: компонент зіркового рейтингу

<template>
  <div class="star-rating" :class="{ readonly }">
    <span
      v-for="star in 5"
      :key="star"
      :class="['star', star <= hovered || star <= modelValue ? 'filled' : 'empty']"
      @mouseenter="!readonly && (hovered = star)"
      @mouseleave="!readonly && (hovered = 0)"
      @click="!readonly && $emit('update:modelValue', star)"
    >★</span>
  </div>
</template>

<script setup lang="ts">
const props = defineProps<{ modelValue: number; readonly?: boolean }>()
const hovered = ref(0)
</script>

Інтеграція з PHP-шаблоном

Компонент монтується в шаблон детальної сторінки товара:

// template.php
$initialReviews = json_encode($arResult['REVIEWS'] ?? [], JSON_UNESCAPED_UNICODE);
?>
<div id="reviews-app"
     data-product-id="<?= (int)$arResult['ID'] ?>"
     data-initial-reviews="<?= htmlspecialchars($initialReviews) ?>">
</div>
<script>
// Ініціалізація Vue-приложення
import { createApp } from 'vue'
import ReviewsWidget from './Reviews.vue'

const el = document.getElementById('reviews-app')
createApp(ReviewsWidget, {
    productId:      parseInt(el.dataset.productId),
    initialReviews: JSON.parse(el.dataset.initialReviews),
}).mount(el)
</script>

Вхідні відгуки (перші 10) рендеруються на сервері та передаються в атрибут data — користувач видить їх миттєво без додаткового запиту.

Модерація в інтерфейсі

Для модераторів — додатковий Vue-компонент у розділі адміністрування. Черга відгуків, що очікують затвердження, з кнопками "Затвердити" / "Відхилити" та полем причини відхилення. Зміна статусу — через той же API-контролер з перевіркою прав ($USER->IsAdmin()).

Строки

Обсяг Що входить Строк
Базові відгуки Форма, список, зірки, AJAX 2–3 тижні
Повнофункціональний віджет + пагінація, сортування, голосування 3–5 тижнів
+ Фото, модерація, Schema.org + UGC, админ-інтерфейс, розмітка +1–2 тижні

Vue-компонент відгуків — інвестиція в UX. Можливість додати відгук без оновлення сторінки знижує bounce rate після відправки форми та збільшує кількість залишених відгуків.