Виджет на управляемой форме 1С: HTML-поле, данные и обновление

· 9 мин

Содержание

Виджет в 1С собирают двумя способами. Первый: штатные элементы формы — диаграмма, индикатор, таблица с условным оформлением. Он надёжный, но внешний вид ограничен. Второй: поле HTML-документа на управляемой форме, куда вы сами выводите разметку. Здесь можно нарисовать карточку с цифрой, столбики, цветные статусы — всё, что умеет HTML и CSS.

Ниже — второй способ, потому что именно его обычно имеют в виду, когда говорят «сделать виджет для панели мониторинга». Разберём всё от запроса до автообновления. Речь о платформе 8.3 и управляемых формах с интерфейсом «Такси».

Что выбрать: штатные элементы или HTML-поле

Сначала решите, нужен ли вам HTML вообще. Для простого графика хватит диаграммы.

СпособКогда подходитМинусы
Поле диаграммы или индикаторОдин график, стандартный видМало гибкости в оформлении
Таблица с условным оформлениемСветофор по строкам, списки показателейНе получится «карточка» с крупной цифрой
Поле HTML-документаСвой дизайн, карточки, мини-графикиБольше кода, нужна проверка в каждом клиенте

Если на панели десять одинаковых карточек, сделайте одну функцию, которая строит HTML карточки по структуре данных. Копировать разметку десять раз — путь к правкам в десяти местах.

Готовим данные и выбираем вид показа

Начните с вопроса: что человек должен понять за три секунды? Остатки на счетах, выручка за неделю, число просроченных документов. Для каждого показателя определите тип отображения:

  • 🔢 одно число — карточка с крупным шрифтом;
  • 📊 динамика по дням — столбики;
  • 🚦 состояние «в норме / внимание / плохо» — цветная плашка;
  • 📋 короткий список — до пяти строк, иначе лучше обычная таблица.

Данные получайте запросом на сервере и отдавайте на клиент уже готовыми, в виде массива структур. Не передавайте сырые выборки и не строите HTML в клиентском коде: на сервере проще кэшировать и контролировать нагрузку.

Пример: выручка по дням за последнюю неделю. Берём проведённые реализации; имена документа и реквизитов проверьте в своей конфигурации.

&НаСервереБезКонтекста
Функция ПолучитьДанныеВиджета()

	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	НАЧАЛОПЕРИОДА(Реализация.Дата, ДЕНЬ) КАК День,
	|	СУММА(Реализация.СуммаДокумента) КАК Сумма
	|ИЗ
	|	Документ.РеализацияТоваровУслуг КАК Реализация
	|ГДЕ
	|	Реализация.Проведен
	|	И Реализация.Дата >= &Начало
	|СГРУППИРОВАТЬ ПО
	|	НАЧАЛОПЕРИОДА(Реализация.Дата, ДЕНЬ)
	|УПОРЯДОЧИТЬ ПО
	|	День";
	Запрос.УстановитьПараметр("Начало", НачалоДня(ТекущаяДатаСеанса()) - 6 * 86400);

	Результат = Новый Массив;
	Выборка = Запрос.Выполнить().Выбрать();
	Пока Выборка.Следующий() Цикл
		Строка = Новый Структура("День, Сумма", Выборка.День, Выборка.Сумма);
		Результат.Добавить(Строка);
	КонецЦикла;

	Возврат Результат;

КонецФункции

Дни без продаж в выборку не попадут. Если нужна ровная неделя из семи столбиков, добавьте недостающие даты при построении разметки.

Создаём форму и размещаем HTML-поле

Откройте конфигуратор и создайте обычную управляемую форму — отдельную обработку или форму существующего объекта. Дальше три действия:

  1. В редакторе формы добавьте реквизит типа Строка, назовите его, например, ТекстВиджета. В нём будет лежать разметка.
  2. Перетащите реквизит в дерево элементов. В свойстве «Вид» выберите Поле HTML-документа.
  3. У элемента отключите заголовок и задайте размеры. Для панели обычно ставят растягивание по ширине и фиксированную высоту.

Для панели из нескольких виджетов делайте на форме группу без заголовка и кладите в неё по одному 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 в цикле на тысячи элементов, на панель столько всё равно не поместится.

Короткий чек-лист перед тем, как отдавать виджет пользователям:

Проверка виджета
Выполнено: 0 / 5

Как не перегрузить панель

Несколько правил, которые экономят время сопровождения:

  • 🧱 одна функция получения данных на один виджет, одна функция построения разметки;
  • 🎨 стили вынесите в общий макет формы или обработки и подставляйте при сборке — тогда цвет меняется в одном месте;
  • 🔒 чувствительные данные ограничивайте правами на сервере, а не скрытием на клиенте;
  • 🕒 для медленных расчётов используйте кэш: регистр сведений с датой расчёта плюс чтение готового значения.

Если панель разрастается больше шести-восьми блоков, подумайте, нужна ли она в одной форме. Иногда лучше сделать два экрана: оперативный и аналитический.

Карточка с крупной цифрой и цветом статуса

Столбики подходят для динамики. Для сводки вроде «Просрочено счетов: 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 в формах, либо клиент работает на устройстве, где встроенный браузерный движок ведёт себя непредсказуемо. Тогда остаются штатные средства:

  • 📈 Диаграмма или Поле диаграммы для динамики — настройка серий и цветов без кода разметки;
  • 🌡 индикатор или полоса прогресса для показателей «выполнено из плана»;
  • 🧾 таблица значений с условным оформлением: строки красятся по правилам, крупные цифры задаются шрифтом ячейки.

Такой виджет выглядит скромнее, зато одинаково работает во всех клиентах и не требует проверки движка.

Частые вопросы

Можно ли подключить внешнюю JavaScript-библиотеку для графиков?
Технически можно положить скрипт в макет и подставить в разметку, но в тонком клиенте старый движок может не потянуть современные библиотеки. Для надёжности используйте чистый CSS или встроенную диаграмму.
Виджет работает в тонком клиенте, а в вебе пустой. Почему?
Чаще всего причина в разном поведении встроенных браузерных движков или в блокировке внешних ресурсов. Уберите внешние подключения и проверьте валидность разметки.
Как сделать виджет с разными данными для разных пользователей?
Передавайте в серверную функцию текущего пользователя и добавляйте отбор в запрос. Права на объекты платформа применит сама, если вы не используете привилегированный режим.
Нужно ли обновлять виджет при каждом открытии формы?
Да, один раз при открытии полезно. Дальнейшее обновление — по таймеру или по кнопке, в зависимости от того, как быстро меняются данные.
Можно ли перетаскивать виджеты, как в современных дашбордах?
Внутри одного HTML-поля перетаскивание придётся писать самому на скрипте, а на уровне формы пользователь может менять расположение элементов в режиме настройки формы.