Розробка CRM-карток на Vue.js для Бітрікс24
Стандартна картка угоди, контакту або ліда у Бітрікс24 покриває 80% потреб. Проблема починається в решті 20%: потрібно відобразити дані із зовнішньої системи прямо в картці, додати кнопку з нетривіальною дією, показати пов'язані об'єкти у вигляді таблиці або зробити поля залежними один від одного. Бітрікс24 надає механізм вбудовування — placement — через який Vue-застосунок монтується прямо всередину картки CRM.
Механізм placement у CRM
BX24.placement.getInterface() повертає інформацію про поточний контекст — де саме відкрита картка і який об'єкт відображається:
window.BX24.init(() => {
BX24.placement.getInterface((result) => {
// result: { ID, ENTITY_TYPE_NAME, ENTITY_TYPE_ID }
// наприклад: { ID: 123, ENTITY_TYPE_NAME: 'deal', ENTITY_TYPE_ID: 2 }
initApp(result)
})
})
Типи placement для CRM:
-
CRM_DEAL_DETAIL_TAB— вкладка в картці угоди -
CRM_CONTACT_DETAIL_TAB— вкладка в картці контакту -
CRM_LEAD_DETAIL_TAB— вкладка в картці ліда -
CRM_DEAL_DETAIL_ACTIVITY— блок активностей
Архітектура Vue-картки
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia'
window.BX24.init(async () => {
const placement = await getPlacement()
const auth = BX24.getAuth()
const app = createApp(App)
app.use(createPinia())
app.provide('entityId', placement.ID)
app.provide('entityType', placement.ENTITY_TYPE_NAME)
app.provide('auth', auth)
app.mount('#app')
})
function getPlacement() {
return new Promise(resolve => BX24.placement.getInterface(resolve))
}
Pinia для керування станом картки — оптимальний вибір: підтримує DevTools, TypeScript, працює без this.
Завантаження даних сутності
// stores/dealStore.js
import { defineStore } from 'pinia'
import { inject } from 'vue'
export const useDealStore = defineStore('deal', {
state: () => ({
deal: null,
relatedContacts: [],
externalData: null,
loading: false,
}),
actions: {
async loadDeal(id) {
this.loading = true
const [deal, contacts] = await Promise.all([
bx24Call('crm.deal.get', { id }),
bx24Call('crm.deal.contact.items.get', { id }),
])
this.deal = deal
this.relatedContacts = contacts
this.loading = false
},
async loadExternalData(dealId) {
// Дані із зовнішньої системи через власний API
const res = await fetch(`/api/deals/${dealId}/external`)
this.externalData = await res.json()
}
}
})
Оновлення полів картки
Оновлення CRM-об'єкта через REST:
async function updateDeal(id, fields) {
return new Promise((resolve, reject) => {
BX24.callMethod('crm.deal.update', {
id,
fields,
}, (result) => {
if (result.error()) reject(result.error())
else resolve(result.data())
})
})
}
Для кількох полів одночасно — батч-запит. Не оновлюйте поля по одному в циклі — це створює чергу запитів і сповільнює інтерфейс.
Користувацькі кнопки та дії
Додавання кнопки в тулбар картки через BX24.placement.bindEvent:
BX24.placement.bindEvent('onAppOptionsSave', () => {
// Реакція на збереження налаштувань застосунку
})
// Кнопка дії реєструється через callMethod
BX24.callMethod('placement.bind', {
PLACEMENT: 'CRM_DEAL_DETAIL_TAB',
HANDLER: 'https://my-app.example.com/',
TITLE: 'Моя вкладка',
DESCRIPTION: 'Розширення CRM-картки',
})
У самому Vue-застосунку кнопки — звичайні компоненти з емітом подій і викликом REST-методів.
Відображення даних із зовнішніх систем
Типовий кейс — показати в картці угоди історію замовлень клієнта із 1С або ERP:
<template>
<div class="external-orders">
<div v-if="store.loading" class="loader">Завантаження...</div>
<table v-else>
<thead>
<tr>
<th>Номер замовлення</th>
<th>Дата</th>
<th>Сума</th>
<th>Статус</th>
</tr>
</thead>
<tbody>
<tr v-for="order in store.externalData?.orders" :key="order.id">
<td>{{ order.number }}</td>
<td>{{ formatDate(order.date) }}</td>
<td>{{ formatMoney(order.total) }}</td>
<td>
<span :class="statusClass(order.status)">
{{ order.statusLabel }}
</span>
</td>
</tr>
</tbody>
</table>
</div>
</template>
Серверна частина — проксі-ендпоінт, який авторизується в 1С, отримує дані і повертає JSON. Бітрікс24 не ходить у 1С напряму з фронту.
Реактивне оновлення при змінах
Якщо користувач змінив поле в стандартній картці і перейшов на вашу вкладку — потрібно перезавантажити дані:
// Перевіряємо актуальність при фокусі на вкладці
document.addEventListener('visibilitychange', () => {
if (!document.hidden) store.loadDeal(entityId)
})
Або — підписка на події через BX24.placement.bindEvent('onAppOptionsSave', callback).
Стилізація під інтерфейс Бітрікс24
Використовуйте CSS-змінні та нейтральні компоненти. Бітрікс24 має власну дизайн-систему, і кастомна вкладка повинна виглядати органічно. Мінімум тіней, тонкі межі, шрифти без serif. Headless UI або власні компоненти — краще за важкі UI-бібліотеки з яскравими темами.
Терміни виконання
Проста вкладка з відображенням даних із REST і кнопкою дії — 2–4 робочі дні. Повноцінна вкладка з даними із зовнішньої системи, редагуванням і синхронізацією — 1–3 тижні залежно від кількості сутностей і складності логіки.







