Создание виджета (руководство)

Это пошаговое руководство, которое показывает, как создать простой виджет информационной панели. Вы можете скачать все файлы этого виджета в виде ZIP-архива: lesson_gauge_chart.zip.

Что вы создадите

В ходе этого руководства вы сначала создадите базовый виджет "Hello, world!", а затем преобразуете его в более продвинутый виджет, который отображает значение элемента данных в виде диаграммы-датчика. Вот как будет выглядеть готовый виджет:

Часть I - "Hello, world!"

В этом разделе вы узнаете, как создать минимально необходимые элементы виджета и добавить новый виджет в веб-интерфейс Zabbix.

Добавление пустого виджета во веб-интерфейс Zabbix

  1. Создайте каталог lesson_gauge_chart в каталоге modules вашей установки веб-интерфейса Zabbix (например, zabbix/ui/modules).

Все пользовательские виджеты рассматриваются как внешние модули и должны быть добавлены в каталог modules вашей установки веб-интерфейса Zabbix (например, zabbix/ui/modules). Каталог zabbix/ui/widgets зарезервирован для встроенных виджетов Zabbix и обновляется вместе с веб-интерфейсом Zabbix.

  1. Создайте файл manifest.json с базовыми метаданными виджета (см. описание поддерживаемых параметров).

ui/modules/lesson_gauge_chart/manifest.json

{
    "manifest_version": 2.0,
    "id": "lesson_gauge_chart",
    "type": "widget",
    "name": "Gauge chart",
    "namespace": "LessonGaugeChart",
    "version": "1.1",
    "author": "Zabbix"
}
  1. В веб-интерфейсе Zabbix перейдите в раздел Administration → General → Modules и нажмите кнопку Scan directory.

  1. Найдите в списке новый модуль Gauge chart и нажмите на гиперссылку "Disabled", чтобы изменить статус модуля с "Disabled" на "Enabled" (если модуль не отображается в списке, см. раздел troubleshooting).

  1. Откройте дашборд, переключите его в режим редактирования и добавьте новый виджет. В поле Type выберите "Gauge chart".

  1. На этом этапе конфигурация виджета Gauge chart содержит только общие поля виджета Name и Refresh interval. Нажмите Add, чтобы добавить виджет на дашборд.

  1. На дашборде должен появиться пустой виджет. Нажмите Save changes в правом верхнем углу, чтобы сохранить дашборд.

Добавление представления виджета

Файл view виджета должен находиться в каталоге views (для этого руководства — ui/modules/lesson_gauge_chart/views/). Если файл имеет имя по умолчанию widget.view.php, регистрировать его в файле manifest.json не нужно. Если файл имеет другое имя, укажите его в разделе actions/widget.lesson_gauge_chart.view файла manifest.json.

  1. Создайте каталог views в каталоге lesson_gauge_chart.

  2. Создайте файл widget.view.php в каталоге views.

ui/modules/lesson_gauge_chart/views/widget.view.php

<?php

/**
 * Представление виджета Gauge chart.
 *
 * @var CView $this
 * @var array $data
 */

(new CWidgetView($data))
    ->addItem(
        new CTag('h1', true, 'Hello, world!')
    )
    ->show();
  1. Обновите панель мониторинга. Теперь виджет Gauge chart отображает "Hello, world!".

Часть II - Диаграмма измерений

Добавление настроек в представление конфигурации и использование их в представлении виджета

В этом разделе вы узнаете, как добавить поле конфигурации виджета и вывести введенное значение в представлении виджета в виде текста.

Конфигурация виджета состоит из формы (Zabbix\Widgets\CWidgetForm) и представления формы виджета (widget.edit.php). Чтобы добавить поля (Zabbix\Widgets\CWidgetField), нужно создать класс WidgetForm, который будет расширять Zabbix\Widgets\CWidgetForm.

Форма содержит набор полей (Zabbix\Widgets\CWidgetField) различных типов, которые используются для проверки значений, введенных пользователем. Поле формы (Zabbix\Widgets\CWidgetField) для каждого типа элемента ввода преобразует значение в единый формат для хранения в базе данных.

Файл form виджета должен находиться в каталоге includes (для этого руководства — ui/modules/lesson_gauge_chart/includes/). Если файл имеет имя по умолчанию WidgetForm.php, регистрировать его в файле manifest.json не нужно. Если файл имеет другое имя, укажите его в разделе widget/form_class файла manifest.json.

  1. Создайте новый каталог includes в каталоге lesson_gauge_chart.

  2. Создайте файл WidgetForm.php в каталоге includes.

ui/modules/lesson_gauge_chart/includes/WidgetForm.php

<?php

namespace Modules\LessonGaugeChart\Includes;

use Zabbix\Widgets\CWidgetForm;

class WidgetForm extends CWidgetForm {
}
  1. Добавьте поле Description в форму конфигурации виджета. Это обычное текстовое поле, в которое пользователь может ввести любой набор символов. Для него можно использовать класс CWidgetFieldTextBox.

ui/modules/lesson_gauge_chart/includes/WidgetForm.php

<?php

namespace Modules\LessonGaugeChart\Includes;

use Zabbix\Widgets\CWidgetForm;

use Zabbix\Widgets\Fields\CWidgetFieldTextBox;

class WidgetForm extends CWidgetForm {

    public function addFields(): self {
        return $this
            ->addField(
               new CWidgetFieldTextBox('description', _('Description'))
            );
   }
}
  1. В каталоге views создайте файл представления конфигурации виджета widget.edit.php и добавьте представление для нового поля Description. Для класса поля CWidgetFieldTextBox представление — CWidgetFieldTextBoxView.

ui/modules/lesson_gauge_chart/views/widget.edit.php

<?php

/**
 * Представление формы виджета Gauge chart.
 *
 * @var CView $this
 * @var array $data
 */

(new CWidgetFormView($data))
    ->addField(
        new CWidgetFieldTextBoxView($data['fields']['description'])
    )
    ->show();
  1. Перейдите на панель и нажмите на значок шестеренки в виджете, чтобы открыть форму конфигурации виджета.

  2. Теперь форма конфигурации виджета содержит новое текстовое поле Description. Введите любое значение, например Gauge chart description.

  1. Нажмите Apply в форме конфигурации виджета. Затем нажмите Save changes в правом верхнем углу, чтобы сохранить панель. Обратите внимание, что новое описание нигде не отображается, и виджет по-прежнему показывает "Hello, world!".

Чтобы новое описание появилось в виджете, значение поля Description нужно получить из базы данных и передать в представление виджета. Для этого необходимо создать класс действия.

  1. Создайте новый каталог actions в каталоге lesson_gauge_chart.

  2. Создайте файл WidgetView.php в каталоге actions. Класс действия WidgetView будет расширять класс CControllerDashboardWidgetView.

Значения полей конфигурации виджета хранятся в свойстве $fields_values класса действия.

ui/modules/lesson_gauge_chart/actions/WidgetView.php

<?php

namespace Modules\LessonGaugeChart\Actions;

use CControllerDashboardWidgetView,
    CControllerResponseData;

class WidgetView extends CControllerDashboardWidgetView {

    protected function doAction(): void {
        $this->setResponse(new CControllerResponseData([
            'name' => $this->getInput('name', $this->widget->getName()),
            'description' => $this->fields_values['description'],
            'user' => [
                'debug_mode' => $this->getDebugMode()
            ]
        ]));
    }
}
  1. Откройте manifest.json и зарегистрируйте WidgetView как класс действия в разделе actions/widget.lesson_gauge_chart.view.

ui/modules/lesson_gauge_chart/manifest.json

{
    "manifest_version": 2.0,
    "id": "lesson_gauge_chart",
    "type": "widget",
    "name": "Gauge chart",
    "namespace": "LessonGaugeChart",
    "version": "1.0",
    "author": "Zabbix",
    "actions": {
        "widget.lesson_gauge_chart.view": {
            "class": "WidgetView"
        }
    }
}
  1. Теперь вы можете использовать значение поля описания, содержащееся в $data['description'], в представлении виджета. Откройте views/widget.view.php и замените статический текст "Hello, world!" на $data['description'].

ui/modules/lesson_gauge_chart/views/widget.view.php

<?php

/**
 * Представление виджета Gauge chart.
 *
 * @var CView $this
 * @var array $data
 */

(new CWidgetView($data))
    ->addItem(
        new CTag('h1', true, $data['description'])
    )
    ->show();
  1. Обновите страницу панели. Теперь вместо "Hello, world!" вы должны увидеть текст описания виджета.

Получение значения элемента данных через API

Виджет должен отображать последнее значение элемента данных, выбранного пользователем. Для этого нужно добавить возможность выбора элементов данных в конфигурации виджета.

В этом разделе вы узнаете, как добавить поле выбора элемента данных в форму виджета и как добавить визуальную часть этого поля в представление конфигурации. Затем контроллер виджета сможет получать данные элемента данных и его значение через запрос API. После получения значение можно отобразить в представлении виджета.

  1. Откройте includes/WidgetForm.php и добавьте поле CWidgetFieldMultiSelectItem. Это позволит выбирать элемент данных в форме конфигурации.

ui/modules/lesson_gauge_chart/includes/WidgetForm.php

<?php

namespace Modules\LessonGaugeChart\Includes;

use Zabbix\Widgets\{
    CWidgetField,
    CWidgetForm
};

use Zabbix\Widgets\Fields\{
    CWidgetFieldMultiSelectItem,
    CWidgetFieldTextBox
};

/**
 * Форма виджета диаграммы-датчика.
 */
class WidgetForm extends CWidgetForm {

    public function addFields(): self {
        return $this
            ->addField(
                (new CWidgetFieldMultiSelectItem('itemid', _('Item')))
                    ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK)
                    ->setMultiple(false)
            )
            ->addField(
                new CWidgetFieldTextBox('description', _('Description'))
            );
    }
}
  1. Откройте views/widget.edit.php и добавьте визуальный компонент поля в представление конфигурации.

ui/modules/lesson_gauge_chart/views/widget.edit.php

<?php

/**
 * Представление формы виджета диаграммы-датчика.
 *
 * @var CView $this
 * @var array $data
 */

(new CWidgetFormView($data))
    ->addField(
        new CWidgetFieldMultiSelectItemView($data['fields']['itemid'])
    )
    ->addField(
        new CWidgetFieldTextBoxView($data['fields']['description'])
    )
    ->show();
  1. Вернитесь на панель и нажмите значок шестеренки в виджете, чтобы открыть форму конфигурации виджета.

  2. Теперь форма конфигурации виджета содержит новое поле ввода Item. Выберите узел сети "Zabbix server" и элемент данных "Load average (1m avg)".

  1. Нажмите Apply в форме конфигурации виджета. Затем нажмите Save changes в правом верхнем углу, чтобы сохранить панель.

  2. Откройте и измените actions/WidgetView.php.

Теперь идентификатор элемента данных будет доступен в контроллере виджета в $this->fields_values['itemid']. Метод контроллера doAction() получает данные элемента данных (имя, тип значения, единицы измерения) с помощью метода API item.get и последнее значение элемента данных с помощью метода API history.get.

ui/modules/lesson_gauge_chart/actions/WidgetView.php

<?php

namespace Modules\LessonGaugeChart\Actions;

use API,
    CControllerDashboardWidgetView,
    CControllerResponseData;

class WidgetView extends CControllerDashboardWidgetView {

    protected function doAction(): void {
        $db_items = API::Item()->get([
            'output' => ['itemid', 'value_type', 'name', 'units'],
            'itemids' => $this->fields_values['itemid'],
            'webitems' => true,
            'filter' => [
                'value_type' => [ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_FLOAT]
            ]
        ]);

        $value = null;

        if ($db_items) {
            $item = $db_items[0];

            $history = API::History()->get([
                'output' => API_OUTPUT_EXTEND,
                'itemids' => $item['itemid'],
                'history' => $item['value_type'],
                'sortfield' => 'clock',
                'sortorder' => ZBX_SORT_DOWN,
                'limit' => 1
            ]);

            if ($history) {
                $value = convertUnitsRaw([
                    'value' => $history[0]['value'],
                    'units' => $item['units']
                ]);
            }
        }

        $this->setResponse(new CControllerResponseData([
            'name' => $this->getInput('name', $this->widget->getName()),
            'value' => $value,
            'description' => $this->fields_values['description'],
            'user' => [
                'debug_mode' => $this->getDebugMode()
            ]
        ]));
    }
}
  1. Откройте views/widget.view.php и добавьте значение элемента данных в представление виджета.

ui/modules/lesson_gauge_chart/views/widget.view.php

<?php

/**
 * Представление виджета диаграммы-датчика.
 *
 * @var CView $this
 * @var array $data
 */

(new CWidgetView($data))
    ->addItem([
        new CTag('h1', true, $data['description']),
        new CDiv($data['value'] !== null ? $data['value']['value'] : _('No data'))
    ])
    ->show();
  1. Обновите страницу панели. Виджет будет отображать последнее значение элемента данных.

Добавление расширенных настроек конфигурации в представление конфигурации

В этом разделе вы узнаете, как добавить разворачиваемый/сворачиваемый раздел Расширенная конфигурация с дополнительными параметрами, такими как цвет, минимальное и максимальное значения, единицы измерения и поле Описание, созданное ранее.

  1. Создайте файл Widget.php в основном каталоге виджетов lesson_gauge_chart, чтобы создать новый класс Widget.

Класс Widget расширит базовый класс CWidget, чтобы добавить/переопределить настройки виджета по умолчанию (в данном случае — переводы). JavaScript, представленный ниже, отображает строку "Нет данных" в случае отсутствия данных. Строка "Нет данных" присутствует в файлах перевода Zabbix UI.

Если есть какие-либо константы виджета, рекомендуется также указать их в классе Widget.

ui/modules/lesson_gauge_chart/Widget.php

<?php

namespace Modules\LessonGaugeChart;

use Zabbix\Core\CWidget;

class Widget extends CWidget {

    public const UNIT_AUTO = 0;
    public const UNIT_STATIC = 1;

    public function getTranslationStrings(): array {
        return [
            'class.widget.js' => [
                'No data' => _('No data')
            ]
        ];
    }
}
  1. Откройте includes/WidgetForm.php и добавьте новые поля Color (выбор цвета), Min (числовое поле), Max (числовое поле) и Units (выберите) и определите цветовая палитра по умолчанию для палитры цветов, чтобы ее можно было использовать на следующих шагах.

ui/modules/lesson_gauge_chart/includes/WidgetForm.php

<?php

namespace Modules\LessonGaugeChart\Includes;

use Modules\LessonGaugeChart\Widget;

use Zabbix\Widgets\{
    CWidgetField,
    CWidgetForm
};

use Zabbix\Widgets\Fields\{
    CWidgetFieldColor,
    CWidgetFieldMultiSelectItem,
    CWidgetFieldNumericBox,
    CWidgetFieldSelect,
    CWidgetFieldTextBox
};

/**
 * Gauge chart widget form.
 */
class WidgetForm extends CWidgetForm {

    public const DEFAULT_COLOR_PALETTE = [
        'FF465C', 'B0AF07', '0EC9AC', '524BBC', 'ED1248', 'D1E754', '2AB5FF', '385CC7', 'EC1594', 'BAE37D',
        '6AC8FF', 'EE2B29', '3CA20D', '6F4BBC', '00A1FF', 'F3601B', '1CAE59', '45CFDB', '894BBC', '6D6D6D'
    ];

    public function addFields(): self {
        return $this
            ->addField(
                (new CWidgetFieldMultiSelectItem('itemid', _('Item')))
                    ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK)
                    ->setMultiple(false)
            )
            ->addField(
                (new CWidgetFieldColor('chart_color', _('Color')))->setDefault('FF0000')
            )
            ->addField(
                (new CWidgetFieldNumericBox('value_min', _('Min')))
                    ->setDefault(0)
                    ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK)
            )
            ->addField(
                (new CWidgetFieldNumericBox('value_max', _('Max')))
                    ->setDefault(100)
                    ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK)
            )
            ->addField(
                (new CWidgetFieldSelect('value_units', _('Units'), [
                    Widget::UNIT_AUTO => _x('Auto', 'history source selection method'),
                    Widget::UNIT_STATIC => _x('Static', 'history source selection method')
                ]))->setDefault(Widget::UNIT_AUTO)
            )
            ->addField(
                (new CWidgetFieldTextBox('value_static_units'))
            )
            ->addField(
                new CWidgetFieldTextBox('description', _('Description'))
            );
    }
}
  1. Откройте views/widget.edit.php и добавьте визуальные компоненты поля в представление конфигурации.

ui/modules/lesson_gauge_chart/views/widget.edit.php

<?php

/**
 * Gauge chart widget form view.
 *
 * @var CView $this
 * @var array $data
 */

$lefty_units = new CWidgetFieldSelectView($data['fields']['value_units']);
$lefty_static_units = (new CWidgetFieldTextBoxView($data['fields']['value_static_units']))
    ->setPlaceholder(_('value'))
    ->setWidth(ZBX_TEXTAREA_TINY_WIDTH);

(new CWidgetFormView($data))
    ->addField(
        (new CWidgetFieldMultiSelectItemView($data['fields']['itemid']))
            ->setPopupParameter('numeric', true)
    )
    ->addFieldset(
        (new CWidgetFormFieldsetCollapsibleView(_('Advanced configuration')))
            ->addField(
                new CWidgetFieldColorView($data['fields']['chart_color'])
            )
            ->addField(
                new CWidgetFieldNumericBoxView($data['fields']['value_min'])
            )
            ->addField(
                new CWidgetFieldNumericBoxView($data['fields']['value_max'])
            )
            ->addItem([
                $lefty_units->getLabel(),
                (new CFormField([
                    $lefty_units->getView()->addClass(ZBX_STYLE_FORM_INPUT_MARGIN),
                    $lefty_static_units->getView()
                ]))
            ])
            ->addField(
                new CWidgetFieldTextBoxView($data['fields']['description'])
            )
    )
    ->show();

Метод addField() класса CWidgetFormView принимает строку класса CSS в качестве второго параметра.

  1. Вернитесь на панель, переключитесь в режим редактирования и нажмите на значок шестеренки в виджете, чтобы открыть форму настройки виджета. Форма настройки виджета теперь содержит новый расширяемый/сворачиваемый раздел Расширенная конфигурация.

  1. Разверните раздел Расширенная конфигурация, чтобы увидеть дополнительные поля конфигурации виджета. Обратите внимание, что поле Цвет пока не имеет палитры цветов. Это связано с тем, что палитра цветов должна быть инициализирована с помощью JavaScript, который будет добавлен в следующем разделе — Добавьте JavaScript в виджет.

Добавление JavaScript в виджет

В этом разделе вы узнаете, как добавить диаграмму-датчик, созданную с помощью JavaScript, которая показывает, является ли последнее значение нормальным или слишком высоким/слишком низким.

  1. Создайте файл widget.edit.js.php в каталоге views.

JavaScript будет отвечать за инициализацию палитры цветов в представлении конфигурации.

ui/modules/lesson_gauge_chart/views/widget.edit.js.php

<?php

use Modules\LessonGaugeChart\Widget;

?>

window.widget_lesson_gauge_chart_form = new class {

    init({color_palette}) {
        this._unit_select = document.getElementById('value_units');
        this._unit_value = document.getElementById('value_static_units');

        this._unit_select.addEventListener('change', () => this.updateForm());

        colorPalette.setThemeColors(color_palette);

        for (const colorpicker of jQuery('.<?= ZBX_STYLE_COLOR_PICKER ?> input')) {
            jQuery(colorpicker).colorpicker();
        }

        const overlay = overlays_stack.getById('widget_properties');

        for (const event of ['overlay.reload', 'overlay.close']) {
            overlay.$dialogue[0].addEventListener(event, () => { jQuery.colorpicker('hide'); });
        }

        this.updateForm();
    }

    updateForm() {
        this._unit_value.disabled = this._unit_select.value == <?= Widget::UNIT_AUTO ?>;
    }
};
  1. Откройте views/widget.edit.php и добавьте файл widget.edit.js.php с JavaScript в представление конфигурации. Для этого используйте метод includeJsFile(). Чтобы добавить встроенный JavaScript, используйте метод addJavaScript().

ui/modules/lesson_gauge_chart/views/widget.edit.php

<?php

/**
 * Gauge chart widget form view.
 *
 * @var CView $this
 * @var array $data
 */

use Modules\LessonGaugeChart\Includes\WidgetForm;

$lefty_units = new CWidgetFieldSelectView($data['fields']['value_units']);
$lefty_static_units = (new CWidgetFieldTextBoxView($data['fields']['value_static_units']))
    ->setPlaceholder(_('value'))
    ->setWidth(ZBX_TEXTAREA_TINY_WIDTH);

(new CWidgetFormView($data))
    ->addField(
        (new CWidgetFieldMultiSelectItemView($data['fields']['itemid']))
            ->setPopupParameter('numeric', true)
    )
    ->addFieldset(
        (new CWidgetFormFieldsetCollapsibleView(_('Advanced configuration')))
            ->addField(
                new CWidgetFieldColorView($data['fields']['chart_color'])
            )
            ->addField(
                new CWidgetFieldNumericBoxView($data['fields']['value_min'])
            )
            ->addField(
                new CWidgetFieldNumericBoxView($data['fields']['value_max'])
            )
            ->addItem([
                $lefty_units->getLabel(),
                (new CFormField([
                    $lefty_units->getView()->addClass(ZBX_STYLE_FORM_INPUT_MARGIN),
                    $lefty_static_units->getView()
                ]))
            ])
            ->addField(
                new CWidgetFieldTextBoxView($data['fields']['description'])
            )
    )
    ->includeJsFile('widget.edit.js.php')
    ->addJavaScript('widget_lesson_gauge_chart_form.init('.json_encode([
        'color_palette' => WidgetForm::DEFAULT_COLOR_PALETTE
    ], JSON_THROW_ON_ERROR).');')
    ->show();
  1. Вернитесь на панель, нажмите на значок шестеренки в виджете, чтобы открыть форму конфигурации виджета. Теперь разверните раздел Advanced configuration, чтобы увидеть инициализированную палитру цветов. Заполните поля значениями и выберите цвет для диаграммы-датчика.

  1. Нажмите Apply в форме конфигурации виджета. Затем нажмите Save changes в правом верхнем углу, чтобы сохранить панель.

  2. Откройте actions/WidgetView.php и обновите контроллер.

Свойство $this->fields_values теперь содержит значения всех полей Advanced configuration. Завершите настройку контроллера, чтобы передавать конфигурацию и выбранное значение элемента данных в представление виджета.

ui/modules/lesson_gauge_chart/actions/WidgetView.php

<?php

namespace Modules\LessonGaugeChart\Actions;

use API,
    CControllerDashboardWidgetView,
    CControllerResponseData;

class WidgetView extends CControllerDashboardWidgetView {

    protected function doAction(): void {
        $db_items = API::Item()->get([
            'output' => ['itemid', 'value_type', 'name', 'units'],
            'itemids' => $this->fields_values['itemid'],
            'webitems' => true,
            'filter' => [
                'value_type' => [ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_FLOAT]
            ]
        ]);

        $history_value = null;

        if ($db_items) {
            $item = $db_items[0];

            $history = API::History()->get([
                'output' => API_OUTPUT_EXTEND,
                'itemids' => $item['itemid'],
                'history' => $item['value_type'],
                'sortfield' => 'clock',
                'sortorder' => ZBX_SORT_DOWN,
                'limit' => 1
            ]);

            if ($history) {
                $history_value = convertUnitsRaw([
                    'value' => $history[0]['value'],
                    'units' => $item['units']
                ]);
            }
        }

        $this->setResponse(new CControllerResponseData([
            'name' => $this->getInput('name', $this->widget->getName()),
            'history' => $history_value,
            'fields_values' => $this->fields_values,
            'user' => [
                'debug_mode' => $this->getDebugMode()
            ]
        ]));
    }
}
  1. Откройте и измените views/widget.view.php.

Вам нужно создать контейнер для диаграммы-датчика, которую вы нарисуете на следующих шагах, и контейнер для описания.

Чтобы передать значения в JavaScript как объект JSON, используйте метод setVar().

ui/modules/lesson_gauge_chart/views/widget.view.php

<?php

/**
 * Gauge chart widget view.
 *
 * @var CView $this
 * @var array $data
 */

(new CWidgetView($data))
    ->addItem([
        (new CDiv())->addClass('chart'),
        $data['fields_values']['description']
            ? (new CDiv($data['fields_values']['description']))->addClass('description')
            : null
    ])
    ->setVar('history', $data['history'])
    ->setVar('fields_values', $data['fields_values'])
    ->show();
  1. Создайте новый каталог assets в каталоге lesson_gauge_chart. Этот каталог будет использоваться для хранения JavaScript, CSS и, возможно, других ресурсов, таких как шрифты или изображения.

  2. Для JavaScript представления виджета создайте каталог js в каталоге assets.

  3. Создайте файл class.widget.js в каталоге assets/js.

Этот JavaScript-класс виджета будет расширять базовый JavaScript-класс всех виджетов панели - CWidget.

Панель зависит от корректной реализации виджета и передает всю необходимую информацию в виджет через вызов соответствующих методов JavaScript. Панель также ожидает, что виджет будет генерировать события при возникновении определенного взаимодействия. Поэтому класс CWidget содержит набор методов с реализацией поведения виджета по умолчанию, которое можно настроить путем расширения класса.

В данном случае требуется некоторая настройка, поэтому собственная логика будет реализована для следующего поведения виджета:

  • инициализация виджета, которая отвечает за определение начального состояния виджета (см. метод onInitialize());
  • отображение содержимого виджета (то есть отрисовка диаграммы-датчика), если процесс обновления виджета завершился успешно и без ошибок (см. метод processUpdateResponse(response) и связанные с ним методы _resizeChart() и _updatedChart());
  • изменение размера виджета (см. метод onResize() и связанный с ним метод _resizeChart())

Для других аспектов виджета диаграммы-датчика будет использоваться реализация поведения виджета по умолчанию. Подробнее о методах JavaScript класса CWidget см.: JavaScript.

Поскольку этот JavaScript требуется для представления виджета, он должен загружаться вместе со страницей панели. Чтобы включить загрузку JavaScript, необходимо обновить параметры assets/js и js_class в файле manifest.json, как показано в шаге 10.

ui/modules/lesson_gauge_chart/assets/js/class.widget.js

class WidgetLessonGaugeChart extends CWidget {

    static UNIT_AUTO = 0;
    static UNIT_STATIC = 1;

    onInitialize() {
        super.onInitialize();

        this._refresh_frame = null;
        this._chart_container = null;
        this._canvas = null;
        this._chart_color = null;
        this._min = null;
        this._max = null;
        this._value = null;
        this._last_value = null;
        this._units = '';
    }

    processUpdateResponse(response) {
        if (response.history === null) {
            this._value = null;
            this._units = '';
        }
        else {
            this._value = Number(response.history.value);
            this._units = response.fields_values.value_units == WidgetLessonGaugeChart.UNIT_AUTO
                ? response.history.units
                : response.fields_values.value_static_units;
        }

        this._chart_color = response.fields_values.chart_color;
        this._min = Number(response.fields_values.value_min);
        this._max = Number(response.fields_values.value_max);

        super.processUpdateResponse(response);
    }

    setContents(response) {
        if (this._canvas === null) {
            super.setContents(response);

            this._chart_container = this._body.querySelector('.chart');
            this._chart_container.style.height =
                `${this._getContentsSize().height - this._body.querySelector('.description').clientHeight}px`;
            this._canvas = document.createElement('canvas');

            this._chart_container.appendChild(this._canvas);

            this._resizeChart();
        }

        this._updatedChart();
    }

    onResize() {
        super.onResize();

        if (this._state === WIDGET_STATE_ACTIVE) {
            this._resizeChart();
        }
    }

    _resizeChart() {
        const ctx = this._canvas.getContext('2d');
        const dpr = window.devicePixelRatio;

        this._canvas.style.display = 'none';
        const size = Math.min(this._chart_container.offsetWidth, this._chart_container.offsetHeight);
        this._canvas.style.display = '';

        this._canvas.width = size * dpr;
        this._canvas.height = size * dpr;

        ctx.scale(dpr, dpr);

        this._canvas.style.width = `${size}px`;
        this._canvas.style.height = `${size}px`;

        this._refresh_frame = null;

        this._updatedChart();
    }

    _updatedChart() {
        if (this._last_value === null) {
            this._last_value = this._min;
        }

        const start_time = Date.now();
        const end_time = start_time + 400;

        const animate = () => {
            const time = Date.now();

            if (time <= end_time) {
                const progress = (time - start_time) / (end_time - start_time);
                const smooth_progress = 0.5 + Math.sin(Math.PI * (progress - 0.5)) / 2;
                let value = this._value !== null ? this._value : this._min;
                value = (this._last_value + (value - this._last_value) * smooth_progress - this._min) / (this._max - this._min);

                const ctx = this._canvas.getContext('2d');
                const size = this._canvas.width;
                const char_weight = size / 12;
                const char_shadow = 3;
                const char_x = size / 2;
                const char_y = size / 2;
                const char_radius = (size - char_weight) / 2 - char_shadow;

                const font_ratio = 32 / 100;

                ctx.clearRect(0, 0, size, size);

                ctx.beginPath();
                ctx.shadowBlur = char_shadow;
                ctx.shadowColor = '#bbb';
                ctx.strokeStyle = '#eee';
                ctx.lineWidth = char_weight;
                ctx.lineCap = 'round';
                ctx.arc(char_x, char_y, char_radius, Math.PI * 0.749, Math.PI * 2.251, false);
                ctx.stroke();

                ctx.beginPath();
                ctx.strokeStyle = `#${this._chart_color}`;
                ctx.lineWidth = char_weight - 2;
                ctx.lineCap = 'round';
                ctx.arc(char_x, char_y, char_radius, Math.PI * 0.75,
                    Math.PI * (0.75 + (1.5 * Math.min(1, Math.max(0, value)))), false
                    );
                ctx.stroke();

                ctx.shadowBlur = 2;
                ctx.fillStyle = '#1f2c33';
                ctx.font = `${(char_radius * font_ratio)|0}px Arial`;
                ctx.textAlign = 'center';
                ctx.textBaseline = 'middle';
                ctx.fillText(`${this._value !== null ? this._value : t('No data')}${this._units}`,
                    char_x, char_y, size - char_shadow * 4 - char_weight * 2
                );

                ctx.fillStyle = '#768d99';
                ctx.font = `${(char_radius * font_ratio * .5)|0}px Arial`;
                ctx.textBaseline = 'top';

                ctx.textAlign = 'left';
                ctx.fillText(`${this._min}${this._min != '' ? this._units : ''}`,
                    char_weight * .75, size - char_weight * 1.25, size / 2 - char_weight
                );

                ctx.textAlign = 'right';
                ctx.fillText(`${this._max}${this._max != '' ? this._units : ''}`,
                    size - char_weight * .75, size - char_weight * 1.25, size / 2 - char_weight
                );

                requestAnimationFrame(animate);
            }
            else {
                this._last_value = this._value;
            }
        };

        requestAnimationFrame(animate);
    }
}
  1. Откройте manifest.json и добавьте:
  • имя файла (class.widget.js) в массив в разделе assets/js;
  • имя класса (WidgetLessonGaugeChart) в параметр js_class в разделе widget.

Теперь класс WidgetLessonGaugeChart будет автоматически загружаться вместе с панелью.

ui/modules/lesson_gauge_chart/manifest.json

{
    "manifest_version": 2.0,
    "id": "lesson_gauge_chart",
    "type": "widget",
    "name": "Gauge chart",
    "namespace": "LessonGaugeChart",
    "version": "1.0",
    "author": "Zabbix",
    "actions": {
        "widget.lesson_gauge_chart.view": {
            "class": "WidgetView"
        }
    },
    "widget": {
        "js_class": "WidgetLessonGaugeChart"
    },
    "assets": {
        "js": ["class.widget.js"]
    }
}

Добавление стилей CSS в виджет

В этом разделе вы узнаете, как добавлять собственные стили CSS, чтобы виджет выглядел более привлекательно.

  1. Для стилей виджетов создайте новый каталог css в каталоге assets.

  2. Создайте файл widget.css в каталоге assets/css. Чтобы стилизовать элементы виджета, используйте селектор div.dashboard-widget-{widget id}. Чтобы настроить CSS для всего виджета, используйте селектор form.dashboard-widget-{idget id}

ui/modules/lesson_gauge_chart/assets/css/widget.css

div.dashboard-widget-lesson_gauge_chart {
    display: grid;
    grid-template-rows: 1fr;
    padding: 0;
}

div.dashboard-widget-lesson_gauge_chart .chart {
    display: grid;
    align-items: center;
    justify-items: center;
}

div.dashboard-widget-lesson_gauge_chart .chart canvas {
    background: white;
}

div.dashboard-widget-lesson_gauge_chart .description {
    padding-bottom: 8px;
    font-size: 1.750em;
    line-height: 1.2;
    text-align: center;
}

.dashboard-grid-widget-hidden-header div.dashboard-widget-lesson_gauge_chart .chart {
    margin-top: 8px;
}
  1. Откройте manifest.json и добавьте имя файла CSS (widget.css) в массив в разделе assets/css. Это позволит стилям CSS, определенным в widget.css, загружаться вместе со страницей панели.

ui/modules/lesson_gauge_chart/manifest.json

{
    "manifest_version": 2.0,
    "id": "lesson_gauge_chart",
    "type": "widget",
    "name": "Gauge chart",
    "namespace": "LessonGaugeChart",
    "version": "1.0",
    "author": "Zabbix",
    "actions": {
        "widget.lesson_gauge_chart.view": {
            "class": "WidgetView"
        }
    },
    "widget": {
        "js_class": "WidgetLessonGaugeChart"
    },
    "assets": {
        "css": ["widget.css"],
        "js": ["class.widget.js"]
    }
}
  1. Обновите страницу панели, чтобы увидеть готовую версию виджета.