Разработка 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 недели в зависимости от количества сущностей и сложности логики.







