Developing an interactive pricing table in Vue.js for 1C-Bitrix

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1175
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    747
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

Interactive Price Table Development on Vue.js for 1C-Bitrix

A price list in Bitrix looks at first glance like a task solvable with bitrix:catalog.section using a table template. Problems appear as soon as interactive requirements are added to the static output: filtering rows without a page reload, column sorting, switching price types (retail/wholesale/dealer), calculating the total amount when selecting items. Server-side rendering and jQuery post-processing scale poorly here — every new interactive element requires a separate handler and manual DOM synchronization.

Price Table Architecture

The Vue component PriceTable receives an array of items from Bitrix via window.BX_STATE.products. The data is assembled in result_modifier.php from the catalog module:

$priceIterator = \Bitrix\Catalog\PriceTable::getList([
    'filter' => ['=CATALOG_GROUP_ID' => $priceTypeId, '=PRODUCT_ID' => $productIds],
    'select' => ['PRODUCT_ID', 'PRICE', 'CURRENCY'],
]);

Each item in the array contains: id, name, article, unit, a set of prices by type, warehouse stock, and characteristics for filtering.

Reactive table capabilities:

Row filtering via 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);
});

Everything is computed locally — no server requests on every interaction. For a price list with 500–2,000 items this works instantly.

Sorting via sortKey and sortDirection refs. Clicking a column header toggles the direction. Numeric and string columns are handled separately.

Price type switching. Bitrix stores multiple prices per product in the b_catalog_price table (field CATALOG_GROUP_ID). All price types are passed in the JSON:

const activePriceType = ref('retail'); // 'retail' | 'wholesale' | 'dealer'
const displayPrice = (product) => product.prices[activePriceType.value];

Switching the price type is an instant reactive change with no server involvement.

Item Selection and Total Calculation

B2B price lists often need the ability to select items and get a total or submit a request. Checkboxes on each row, selectedIds — a Set in 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)
);

The quantity field per row — v-model on quantities[product.id]. Changing the quantity instantly recalculates the total. Clicking "Submit request" — POST to a Bitrix handler with an array of {id, qty, price}.

Virtual Scroll for Large Price Lists

A price list with 5,000+ items cannot be fully rendered — the browser will stall. Virtualization via @vueuse/virtual-list or vue-virtual-scroller: only 50–100 visible rows are in the DOM at any time; the rest are computed on the fly. Filtering and search operate on the full in-memory array — only the rendering is virtual.

Case: Interactive Distributor Price List

A construction materials distributor, price list with 3,800 items, three price types (retail, small wholesale, large wholesale), filter by brand and category, search by article number.

Previous solution: an Excel file for download, updated manually once a week. An attempt to build an HTML table via bitrix:catalog.section hit a wall: the page took 8 seconds to load (3,800 rows in the DOM), and there was no search at all.

Solution: a Vue component with virtual scrolling. Data is loaded in a single AJAX request when the component mounts — a Bitrix controller returns a JSON with the full price list (~400 KB, ~80 KB gzipped). The request is cached in sessionStorage — subsequent page opens are instant.

Price type switching is only available to authorized users in the appropriate group — checked in Vue via window.BX_STATE.userGroups, plus server-side on every request. Unauthorized users only see the retail price.

Result: time to interactive — 1.2 seconds (JSON load + render). Article search — instant. Managers stopped sending Excel files.

Export and Print

"Download price list" — not a separate page, but CSV generation in the browser from the current filtered and sorted dataset:

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
}

The BOM \uFEFF is mandatory for correct opening in Excel on Windows.

Stages and Timelines

Feature Estimated timeline
Basic table with sorting and search 2-3 days
Filtering + price type switching 3-5 days
Item selection + total calculation + submission 4-6 days
Virtualization for 3,000+ items +2-3 days
CSV/Excel export +1 day

Development is iterative — core features first, extensions after review.