Виджет на управляемой форме 1С: HTML-поле, данные и обновление
Содержание
Виджет в 1С собирают двумя способами. Первый: штатные элементы формы — диаграмма, индикатор, таблица с условным оформлением. Он надёжный, но внешний вид ограничен. Второй: поле HTML-документа на управляемой форме, куда вы сами выводите разметку. Здесь можно нарисовать карточку с цифрой, столбики, цветные статусы — всё, что умеет HTML и CSS.
Ниже — второй способ, потому что именно его обычно имеют в виду, когда говорят «сделать виджет для панели мониторинга». Разберём всё от запроса до автообновления. Речь о платформе 8.3 и управляемых формах с интерфейсом «Такси».
Что выбрать: штатные элементы или HTML-поле
Сначала решите, нужен ли вам HTML вообще. Для простого графика хватит диаграммы.
| Способ | Когда подходит | Минусы |
|---|---|---|
| Поле диаграммы или индикатор | Один график, стандартный вид | Мало гибкости в оформлении |
| Таблица с условным оформлением | Светофор по строкам, списки показателей | Не получится «карточка» с крупной цифрой |
| Поле HTML-документа | Свой дизайн, карточки, мини-графики | Больше кода, нужна проверка в каждом клиенте |
Если на панели десять одинаковых карточек, сделайте одну функцию, которая строит HTML карточки по структуре данных. Копировать разметку десять раз — путь к правкам в десяти местах.
Готовим данные и выбираем вид показа
Начните с вопроса: что человек должен понять за три секунды? Остатки на счетах, выручка за неделю, число просроченных документов. Для каждого показателя определите тип отображения:
- 🔢 одно число — карточка с крупным шрифтом;
- 📊 динамика по дням — столбики;
- 🚦 состояние «в норме / внимание / плохо» — цветная плашка;
- 📋 короткий список — до пяти строк, иначе лучше обычная таблица.
Данные получайте запросом на сервере и отдавайте на клиент уже готовыми, в виде массива структур. Не передавайте сырые выборки и не строите HTML в клиентском коде: на сервере проще кэшировать и контролировать нагрузку.
Пример: выручка по дням за последнюю неделю. Берём проведённые реализации; имена документа и реквизитов проверьте в своей конфигурации.
&НаСервереБезКонтекста
Функция ПолучитьДанныеВиджета()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| НАЧАЛОПЕРИОДА(Реализация.Дата, ДЕНЬ) КАК День,
| СУММА(Реализация.СуммаДокумента) КАК Сумма
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Реализация
|ГДЕ
| Реализация.Проведен
| И Реализация.Дата >= &Начало
|СГРУППИРОВАТЬ ПО
| НАЧАЛОПЕРИОДА(Реализация.Дата, ДЕНЬ)
|УПОРЯДОЧИТЬ ПО
| День";
Запрос.УстановитьПараметр("Начало", НачалоДня(ТекущаяДатаСеанса()) - 6 * 86400);
Результат = Новый Массив;
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Строка = Новый Структура("День, Сумма", Выборка.День, Выборка.Сумма);
Результат.Добавить(Строка);
КонецЦикла;
Возврат Результат;
КонецФункции
Дни без продаж в выборку не попадут. Если нужна ровная неделя из семи столбиков, добавьте недостающие даты при построении разметки.
Создаём форму и размещаем HTML-поле
Откройте конфигуратор и создайте обычную управляемую форму — отдельную обработку или форму существующего объекта. Дальше три действия:
- В редакторе формы добавьте реквизит типа
Строка, назовите его, например,ТекстВиджета. В нём будет лежать разметка. - Перетащите реквизит в дерево элементов. В свойстве «Вид» выберите
Поле HTML-документа. - У элемента отключите заголовок и задайте размеры. Для панели обычно ставят растягивание по ширине и фиксированную высоту.
Для панели из нескольких виджетов делайте на форме группу без заголовка и кладите в неё по одному HTML-полю на виджет. Так каждый можно обновлять отдельно и не перерисовывать всю панель.
Реквизит с HTML живёт в форме и уходит на клиент целиком. Не кладите в разметку данные, которые пользователю видеть не положено: ограничение прав на сервере не спасёт то, что вы уже выгрузили в строку.
Собираем HTML-разметку во встроенном языке
Разметку строят обычной конкатенацией строк. Две ловушки:
- кавычки внутри строки языка 1С приходится удваивать, поэтому для атрибутов HTML берите одинарные кавычки;
- число нельзя склеивать со строкой напрямую — оборачивайте в
Формат(Число, "ЧГ=0"), иначе получите разделители групп разрядов и неприятные сюрпризы вstyle.
Рисуем столбики без внешних библиотек, чистым CSS:
&НаКлиенте
Процедура ПостроитьВиджет(Данные)
Максимум = 0;
Для Каждого Строка Из Данные Цикл
Максимум = Макс(Максимум, Строка.Сумма);
КонецЦикла;
HTML = "<html><head><meta charset='utf-8'><style>"
+ "body{margin:0;font-family:Arial,sans-serif;background:#fff}"
+ ".box{display:flex;align-items:flex-end;height:120px;padding:8px}"
+ ".col{flex:1;text-align:center;font-size:11px}"
+ ".bar{background:#d61a20;margin:0 4px}"
+ "a{color:#333;text-decoration:none}"
+ "</style></head><body><div class='box'>";
Для Каждого Строка Из Данные Цикл
Высота = ?(Максимум = 0, 0, Окр(Строка.Сумма / Максимум * 90));
Метка = Формат(Строка.День, "ДФ=dd.MM");
Код = Формат(Строка.День, "ДФ=yyyyMMdd");
HTML = HTML + "<div class='col'><a href='day:" + Код + "'>"
+ "<div class='bar' style='height:" + Формат(Высота, "ЧГ=0") + "px'></div>"
+ Метка + "</a></div>";
КонецЦикла;
ТекстВиджета = HTML + "</div></body></html>";
КонецПроцедуры
Столбик с нулевой выручкой получит высоту 0 пикселей, подпись под ним останется — по ней видно, что день пустой. Максимум 90 пикселей оставляет место под подписи внутри блока высотой 120.
Проверка в уме: при максимуме 200 000 и сумме 100 000 высота равна Окр(100000 / 200000 * 90) = 45 пикселей. Для суммы 150 000 получим Окр(67,5) = 68.
Подключаем события и интерактивность
Клик по столбику должен открывать расшифровку. У поля HTML-документа есть событие ПриНажатии. Привяжите обработчик в палитре свойств элемента.
&НаКлиенте
Процедура ВиджетПриНажатии(Элемент, ДанныеСобытия, СтандартнаяОбработка)
Ссылка = ДанныеСобытия.Href;
Если Ссылка = Неопределено Или Лев(Ссылка, 4) <> "day:" Тогда
Возврат;
КонецЕсли;
СтандартнаяОбработка = Ложь;
КодДня = Сред(Ссылка, 5);
// Дальше откройте отчёт или список за выбранный день,
// передав дату параметром.
КонецПроцедуры
Что важно:
СтандартнаяОбработка = Ложьне даёт браузерному движку попытаться перейти по нестандартной ссылке;- содержимое
Hrefможет отличаться в зависимости от клиента, поэтому в первый раз выведите его в сообщение и посмотрите, что реально пришло; - разбирайте строку аккуратно:
Сред(Ссылка, 5)отрезает префиксday:из четырёх символов.
Чтобы получить доступ к самому документу в поле, дождитесь события ДокументСформирован. Раньше обращаться к Элементы.ВиджетHTML.Документ бессмысленно: объекта ещё нет.
Для открытия расшифровки проще всего открыть форму отчёта с отбором по дате или форму списка с нужным отбором — это обычные вызовы ОткрытьФорму с параметрами.
Автообновление данных по таймеру
Для панели мониторинга нужны свежие цифры без нажатия кнопки. В управляемой форме для этого служит обработчик ожидания:
&НаКлиенте
Процедура ПриОткрытии(Отказ)
ОбновитьВиджет();
ПодключитьОбработчикОжидания("ОбновитьВиджет", 60);
КонецПроцедуры
&НаКлиенте
Процедура ОбновитьВиджет()
Данные = ПолучитьДанныеВиджета();
ПостроитьВиджет(Данные);
КонецПроцедуры
&НаКлиенте
Процедура ПриЗакрытии(ЗавершениеРаботы)
ОтключитьОбработчикОжидания("ОбновитьВиджет");
КонецПроцедуры
Интервал в секундах. Минимум платформы мал, но ставить 5 секунд на панель, которую открыли тридцать человек, — плохая идея: каждый вызов — запрос к базе. Для финансовых показателей хватает 60–300 секунд, для очередей и статусов можно чаще.
Если цифры не меняются в течение дня, не обновляйте их по таймеру вовсе. Считайте раз при открытии и дайте кнопку «Обновить».
Не вызывайте тяжёлые отчёты из обработчика ожидания. Если расчёт идёт дольше интервала, запросы накапливаются и форма начинает подвисать. Тяжёлое считайте регламентным заданием и складывайте результат в регистр сведений, а виджет пусть читает готовое.
Отладка и производительность
Чаще всего виджет «ломается» тремя способами.
Пустое поле. Проверьте, что в ТекстВиджета действительно что-то лежит: выведите длину строки в сообщение. Затем убедитесь, что HTML валиден — незакрытый тег или забытая кавычка в style дают белый прямоугольник без ошибок.
Разный вид в клиентах. Тонкий клиент, веб-клиент и мобильный клиент рисуют HTML каждый своим движком. В тонком клиенте под Windows встроенный движок может оказаться старым, и современный CSS (сетки, переменные) работает не везде. Держитесь простой вёрстки: блоки, flex проверьте отдельно, внешние шрифты и скрипты с интернета не подключайте — на закрытых серверах они не загрузятся.
Медленное открытие формы. Смотрите в замер производительности или технологический журнал: если время тратится на запрос, добавьте отбор по периоду и индексируемым полям. Если на построении строки — не склеивайте HTML в цикле на тысячи элементов, на панель столько всё равно не поместится.
Короткий чек-лист перед тем, как отдавать виджет пользователям:
Как не перегрузить панель
Несколько правил, которые экономят время сопровождения:
- 🧱 одна функция получения данных на один виджет, одна функция построения разметки;
- 🎨 стили вынесите в общий макет формы или обработки и подставляйте при сборке — тогда цвет меняется в одном месте;
- 🔒 чувствительные данные ограничивайте правами на сервере, а не скрытием на клиенте;
- 🕒 для медленных расчётов используйте кэш: регистр сведений с датой расчёта плюс чтение готового значения.
Если панель разрастается больше шести-восьми блоков, подумайте, нужна ли она в одной форме. Иногда лучше сделать два экрана: оперативный и аналитический.
Карточка с крупной цифрой и цветом статуса
Столбики подходят для динамики. Для сводки вроде «Просрочено счетов: 7» нужна карточка. Принцип тот же: функция принимает заголовок, значение и уровень, возвращает кусок разметки, а общая процедура склеивает карточки в строку.
&НаКлиенте
Функция КарточкаHTML(Заголовок, Значение, Уровень)
Цвета = Новый Соответствие;
Цвета.Вставить("норма", "#2e7d32");
Цвета.Вставить("внимание", "#f9a825");
Цвета.Вставить("плохо", "#d61a20");
Цвет = Цвета.Получить(Уровень);
Если Цвет = Неопределено Тогда
Цвет = "#999";
КонецЕсли;
Возврат "<div style='display:inline-block;width:150px;margin:6px;padding:10px;"
+ "border-left:6px solid " + Цвет + ";background:#f5f5f5'>"
+ "<div style='font-size:12px;color:#555'>" + Заголовок + "</div>"
+ "<div style='font-size:28px;font-weight:bold'>" + Значение + "</div></div>";
КонецФункции
Значение передавайте уже строкой, отформатированной через Формат(Сумма, "ЧДЦ=0; ЧГ=3,0") для денег или Формат(Количество, "ЧГ=0") для штук. Тогда карточка не зависит от того, число пришло или строка.
Правило для уровней: пороги храните не в коде виджета, а в константах или регистре сведений. Пороги «внимание» и «плохо» бухгалтерия обычно пересматривает чаще, чем разработчик заходит в конфигурацию.
Пример логики: если просроченных счетов до пяти — «норма», от шести до пятнадцати — «внимание», больше пятнадцати — «плохо». Значение 7 попадает в «внимание», значение 16 — в «плохо». Границы включайте в условия явно (<= 5, <= 15), чтобы не гадать, куда попадёт ровно пять.
Если HTML-поле не подходит
Бывает, что корпоративная политика запрещает HTML в формах, либо клиент работает на устройстве, где встроенный браузерный движок ведёт себя непредсказуемо. Тогда остаются штатные средства:
- 📈
ДиаграммаилиПоле диаграммыдля динамики — настройка серий и цветов без кода разметки; - 🌡 индикатор или полоса прогресса для показателей «выполнено из плана»;
- 🧾 таблица значений с условным оформлением: строки красятся по правилам, крупные цифры задаются шрифтом ячейки.
Такой виджет выглядит скромнее, зато одинаково работает во всех клиентах и не требует проверки движка.