Разработка интерактивной таблицы цен на Vue.js для 1С-Битрикс
Прайс-лист на Битриксе — задача, которая на первый взгляд решается через bitrix:catalog.section с шаблоном таблицы. Проблемы появляются сразу, как только к статичному выводу добавляются требования: фильтрация строк без перезагрузки, сортировка по колонкам, переключение типа цен (розница/опт/дилер), расчёт итоговой суммы при выборе позиций. Серверный рендеринг и jQuery-постобработка здесь масштабируются плохо — каждый новый интерактивный элемент требует отдельного обработчика и ручной синхронизации DOM.
Архитектура таблицы цен
Vue-компонент PriceTable получает массив позиций из Битрикса через window.BX_STATE.products. Данные формируются в result_modifier.php из модуля catalog:
$priceIterator = \Bitrix\Catalog\PriceTable::getList([
'filter' => ['=CATALOG_GROUP_ID' => $priceTypeId, '=PRODUCT_ID' => $productIds],
'select' => ['PRODUCT_ID', 'PRICE', 'CURRENCY'],
]);
Каждая позиция в массиве содержит: id, name, article, unit, набор цен по типам, остаток на складе, характеристики для фильтрации.
Реактивные возможности таблицы:
Фильтрация строк через computed:
const filteredProducts = computed(() => {
return products.value
.filter(p => selectedCategory.value ? p.categoryId === selectedCategory.value : true)
.filter(p => searchQuery.value
? p.name.toLowerCase().includes(searchQuery.value.toLowerCase())
|| p.article.toLowerCase().includes(searchQuery.value.toLowerCase())
: true
)
.filter(p => showInStock.value ? p.stock > 0 : true);
});
Всё вычисляется локально — нет запросов к серверу при каждом взаимодействии. Для прайс-листа на 500-2000 позиций это работает мгновенно.
Сортировка через sortKey и sortDirection refs. Клик по заголовку колонки переключает направление. Числовые и строковые колонки обрабатываются отдельно.
Переключение типов цен. Битрикс хранит несколько цен на товар в таблице b_catalog_price (поле CATALOG_GROUP_ID). Все типы цен передаются в JSON:
const activePriceType = ref('retail'); // 'retail' | 'wholesale' | 'dealer'
const displayPrice = (product) => product.prices[activePriceType.value];
Смена типа цены — мгновенная реактивная замена без сервера.
Выбор позиций и расчёт суммы
Для B2B-прайсов часто нужна возможность отметить позиции и получить итоговую сумму или отправить запрос. Чекбоксы на каждой строке, selectedIds — Set в ref():
const total = computed(() =>
[...selectedIds.value]
.map(id => products.value.find(p => p.id === id))
.filter(Boolean)
.reduce((sum, p) => sum + displayPrice(p) * (quantities[p.id] || 1), 0)
);
Поле количества на строке — v-model на quantities[product.id]. Изменение количества мгновенно пересчитывает итог. При нажатии «Отправить запрос» — POST на битриксовый обработчик с массивом {id, qty, price}.
Виртуальный скролл для больших прайсов
Прайс-лист на 5000+ позиций нельзя рендерить полностью — браузер тормозит. Виртуализация через @vueuse/virtual-list или vue-virtual-scroller: в DOM одновременно присутствует только 50-100 видимых строк, остальные вычисляются на лету. Фильтрация и поиск работают по полному массиву в памяти — только рендер виртуальный.
Кейс: интерактивный прайс дистрибьютора
Дистрибьютор строительных материалов, прайс на 3800 позиций, три типа цен (розница, мелкий опт, крупный опт), фильтр по бренду и категории, поиск по артикулу.
Предыдущее решение: Excel-файл для скачивания, обновлялся вручную раз в неделю. Попытка сделать HTML-таблицу через bitrix:catalog.section упиралась в то, что страница грузилась 8 секунд (3800 строк в DOM), а поиска не было вообще.
Решение: Vue-компонент с виртуальным скроллом. Данные загружаются одним AJAX-запросом при монтировании компонента — Битрикс-контроллер возвращает JSON с полным прайсом (~400KB gzip ~80KB). Запрос кешируется в sessionStorage — повторное открытие страницы мгновенное.
Переключение типа цены доступно только авторизованным пользователям с нужной группой — проверка в Vue через window.BX_STATE.userGroups, дополнительно на сервере при каждом запросе. Неавторизованным показывается только розничная цена.
Результат: время до интерактивности — 1.2 секунды (загрузка JSON + рендер). Поиск по артикулу — мгновенный. Менеджеры перестали рассылать Excel.
Экспорт и печать
«Скачать прайс» — не отдельная страница, а генерация CSV в браузере из текущего отфильтрованного и отсортированного набора:
function exportCsv() {
const rows = filteredProducts.value.map(p =>
[p.article, p.name, displayPrice(p), p.unit].join(';')
);
const blob = new Blob(['\uFEFF' + rows.join('\n')], { type: 'text/csv;charset=utf-8' });
// ... download link
}
BOM \uFEFF — обязателен для корректного открытия в Excel на Windows.
Этапы и сроки
| Функциональность | Ориентировочный срок |
|---|---|
| Базовая таблица с сортировкой и поиском | 2-3 дня |
| Фильтрация + переключение типов цен | 3-5 дней |
| Выбор позиций + расчёт суммы + отправка | 4-6 дней |
| Виртуализация для 3000+ позиций | +2-3 дня |
| Экспорт CSV/Excel | +1 день |
Разработка ведётся итерационно — базовые функции в первую очередь, расширения после согласования.







