Skip to content
  1. Компоненты
  2. MiniShop3
  3. Разработка
  4. Вкладки заказа

Интеграция вкладок заказа ​

Свою вкладку на страницу заказа в админке добавляет плагин MODX: он регистрирует Vue-компонент или панель ExtJS в window.MS3OrderTabsRegistry.

Аналогичный API

Для вкладок товара используйте MS3ProductTabsRegistry.

Архитектура ​

Страница заказа — полностью Vue-приложение (в отличие от товара, где Vue встроен в ExtJS):

Vue OrderView (корневой компонент)
├── Информация (position: 0)
├── Товары (position: 1, скрыта при создании)
├── Адрес (position: 2)
├── Отгрузка (position: 3, скрыта при создании)
├── История (position: 4, скрыта при создании)
└── [Ваши вкладки] (position: 100 по умолчанию)

Регистрация вкладки ​

Метод register(tabConfig) принимает:

ПараметрТипОбязательныйОписание
keystringДаУникальный идентификатор вкладки
titlestringДаЗаголовок вкладки
typestringНет'vue' (по умолчанию) или 'extjs'
componentobject/stringДля VueОбъект компонента (например, импортированный SFC) или имя, уже зарегистрированное в приложении через app.component(). Произвольные имена от плагинов ядро не регистрирует
xtypestringДля ExtJSxtype ExtJS компонента
extConfigobjectНетДополнительная конфигурация для ExtJS
propsobjectНетДополнительные props для Vue-компонента
positionnumberНетПозиция вкладки среди остальных (по умолчанию 100)
hideOnCreatebooleanНетСкрыть при создании нового заказа

Возвращает: boolean — успешность регистрации.

Зарезервированные ключи: info, products, address, ms3_shipment, history — использовать нельзя.

Что получает компонент ​

Эти данные вкладка получает автоматически:

ПараметрТипОписание
orderIdnumberID заказа (0 при создании)
orderobject | nullДанные заказа. null при создании и пока не пришёл ответ API — отсюда order?.num в примерах
configobjectКонфигурация ms3 (ms3.config)
isCreateModebooleanРежим создания нового заказа

Эти четыре значения подставляются последними, поэтому одноимённые ключи из props (у Vue) и extConfig (у ExtJS) будут перекрыты.

Подключение через MODX плагин ​

Плагин подключает скрипт с регистрацией на событии msOnManagerCustomCssJs, проверив page === 'order':

php
<?php
/**
 * Событие: msOnManagerCustomCssJs
 */

$page = $scriptProperties['page'] ?? '';
if ($page !== 'order') {
    return;
}

/** @var msManagerController $controller */
$controller = $scriptProperties['controller'];

$controller->addHtml('
<script>
// регистрация вкладки
</script>
');

Порядок загрузки

Скрипт из msOnManagerCustomCssJs выполняется до загрузки Vue-модуля (order.min.js). В этот момент MS3OrderTabsRegistry ещё не существует — необходимо создать заглушку с очередью. Vue-модуль при загрузке подхватит pendingTabs из заглушки.

Заглушка с очередью ​

javascript
if (!window.MS3OrderTabsRegistry) {
    window.MS3OrderTabsRegistry = {
        pendingTabs: [],
        register: function(c) { this.pendingTabs.push(c); return true; }
    };
}

В примерах ниже она обозначена комментарием.

Интеграция Vue-компонента ​

Inline-компонент ​

Подходит для простых вкладок без сборки:

php
<?php
// Плагин MODX — событие: msOnManagerCustomCssJs

$page = $scriptProperties['page'] ?? '';
if ($page !== 'order') {
    return;
}

$controller = $scriptProperties['controller'];

$controller->addHtml('
<script>
(function() {
    // Заглушка с очередью — код выше

    window.MS3OrderTabsRegistry.register({
        key: "delivery-tracking",
        title: "Отслеживание",
        type: "vue",
        component: {
            props: ["orderId", "order", "config", "isCreateMode"],
            template: "<div style=\"padding: 20px\">"
                + "<h3>Отслеживание доставки</h3>"
                + "<p>Заказ #{{ order?.num }}, ID: {{ orderId }}</p>"
                + "</div>"
        },
        position: 5,
        hideOnCreate: true
    });
})();
</script>
');

SFC-компонент (рекомендуемый вариант) ​

Для полноценных компонентов со сборкой через Vite/Webpack:

1. Создайте Vue-компонент:

vue
<!-- my-addon/src/OrderTrackingTab.vue -->
<template>
  <div class="tracking-tab">
    <h3>Отслеживание доставки</h3>
    <p>Заказ #{{ order?.num }}</p>
    <p>Статус: {{ trackingStatus }}</p>
    <button @click="refreshTracking">Обновить</button>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'

const props = defineProps({
  orderId: { type: Number, required: true },
  order: { type: Object, default: null },
  config: { type: Object, default: () => ({}) },
  isCreateMode: { type: Boolean, default: false }
})

const trackingStatus = ref('Загрузка...')

async function refreshTracking() {
  // Ваша логика получения данных
  const response = await fetch(`/api/tracking/${props.orderId}`)
  const data = await response.json()
  trackingStatus.value = data.status
}

onMounted(() => {
  if (props.orderId) {
    refreshTracking()
  }
})
</script>

2. Создайте точку входа:

javascript
// my-addon/src/entries/order-tab.js
import OrderTrackingTab from '../components/OrderTrackingTab.vue'

;(function() {
  // Заглушка с очередью — код выше

  window.MS3OrderTabsRegistry.register({
    key: 'tracking',
    title: 'Отслеживание',
    type: 'vue',
    component: OrderTrackingTab, // объект SFC, не строка
    position: 5,
    hideOnCreate: true
  })
})()

3. Подключите в MODX плагине:

php
<?php
// Событие: msOnManagerCustomCssJs

$page = $scriptProperties['page'] ?? '';
if ($page !== 'order') {
    return;
}

$controller = $scriptProperties['controller'];
$assetsUrl = MODX_ASSETS_URL . 'components/myaddon/';

// ES-модуль со сборкой
$controller->addHtml(
    '<script type="module" src="' . $assetsUrl . 'js/order-tab.min.js"></script>'
);

Реактивность

Vue-вкладки получают реактивные props: при загрузке заказа, сохранении и редактировании они обновляются автоматически.

Компонент вкладки монтируется только тогда, когда вкладка открыта, и размонтируется при переходе на другую. Отсюда два следствия:

  • mounted срабатывает при первом открытии вкладки, а не при загрузке страницы заказа;
  • состояние внутри компонента между переключениями не сохраняется — то, что нужно сохранить, держите снаружи компонента или запрашивайте заново.

Интеграция ExtJS-компонента ​

1. Создайте панель ExtJS ​

javascript
// assets/components/myaddon/js/mgr/delivery-panel.js
Ext.define('MyAddon.panel.DeliveryInfo', {
  extend: 'Ext.Panel',
  xtype: 'myaddon-delivery-panel',

  initComponent: function() {
    // this.orderId, this.order, this.config, this.isCreateMode
    // доступны из конфигурации
    Ext.apply(this, {
      border: false,
      bodyStyle: 'padding: 15px',
      html: '<h3>Информация о доставке</h3>'
          + '<p>Заказ ID: ' + this.orderId + '</p>'
    })

    MyAddon.panel.DeliveryInfo.superclass.initComponent.call(this)
  }
})

2. Зарегистрируйте вкладку и подключите скрипты ​

php
<?php
// Плагин — событие: msOnManagerCustomCssJs

$page = $scriptProperties['page'] ?? '';
if ($page !== 'order') {
    return;
}

$controller = $scriptProperties['controller'];
$assetsUrl = MODX_ASSETS_URL . 'components/myaddon/';

// Сначала панель
$controller->addJavascript($assetsUrl . 'js/mgr/delivery-panel.js');

// Затем регистрация
$controller->addHtml('
<script>
(function() {
    // Заглушка с очередью — код выше

    window.MS3OrderTabsRegistry.register({
        key: "delivery-info",
        title: "Доставка",
        type: "extjs",
        xtype: "myaddon-delivery-panel",
        extConfig: {
            customParam: "value"
        },
        position: 6,
        hideOnCreate: true
    });
})();
</script>
');

Снимок данных

ExtJS-вкладка создаётся один раз при первом переключении на неё и живёт до закрытия страницы заказа: при переходе на другие вкладки панель не уничтожается и состояние в ней сохраняется. Значения order, orderId, config, isCreateMode — это снимок на момент создания. Если заказ будет сохранён или изменён, ExtJS-панель не получит обновлённые данные автоматически. Для обновления данных реализуйте собственную логику (polling, подписка на события).

Позиционирование вкладок ​

Встроенные вкладки имеют фиксированные позиции:

ВкладкаКлючПозицияСкрыта при создании
Информацияinfo0Нет
Товарыproducts1Да
Адресaddress2Нет
Отгрузкаms3_shipment3Да
Историяhistory4Да

Своя вкладка без указанного position получает 100. Значение из диапазона 5–99 поставит её после встроенных, но раньше вкладок с позицией по умолчанию.

Встроенные вкладки занимают целые числа 0–4, поэтому встать между ними целым значением нельзя — сортировка идёт по числу, и 2.5 встанет между «Адресом» и «Отгрузкой».

Вкладки с одинаковой позицией сортируются в порядке регистрации.

Если вкладка не появилась ​

Вкладка отклоняется, когда:

  • не задан key или title;
  • key совпадает с зарезервированным (info, products, address, ms3_shipment, history);
  • type не vue и не extjs;
  • при type: 'vue' не задан component, при type: 'extjs' — xtype;
  • key уже зарегистрирован.

Первые четыре случая пишут причину в console.error, повторный ключ — в console.warn. Отклонённая вкладка просто не появляется, страница заказа при этом работает.

Заглушка ничего не проверяет

register() заглушки складывает конфигурацию в pendingTabs и всегда возвращает true. Проверка происходит позже, когда загрузившийся Vue-модуль разбирает очередь, — поэтому в сценарии с плагином вы не увидите false в своём коде, а причина появится в консоли при загрузке страницы.

По префиксу сообщения видно, на каком этапе отказ: [OrderTabsRegistry] — при разборе очереди, [OrderView] — при вызове register() на уже открытой странице.

Что смотреть в консоли браузера:

javascript
// Состояние MS3OrderTabsRegistry целиком
console.log(window.MS3OrderTabsRegistry)

// Очередь до монтирования Vue — после загрузки страницы должна быть пуста
console.log(window.MS3OrderTabsRegistry.pendingTabs)