Клиентская часть(мобильный и веб-клиент)¶
Для всех видов клиентов соблюдаются единые подходы и единый синтаксис, но все же, учитывая разные задачи, которые возлагаются на клиенты и разный функционал есть отличия между мобильным и веб-клиентом.
Возможности и отличия веб-клиента и мобильного клиента¶
С узлами можно работать напрямую на сервере, используя веб-клиент, которой непосредственно подключен к БД узлов и работает с сервером узлов напрямую через локальное хранение и исполнение кода обработчиков сразу на сервере. И также можно работать через мобильное приложение на Android, причем при работе через Андроид-приложение (мобильный клиент) по умолчанию узлы скачиваются локально и обработчики узлов выполняются также локально на устройстве. Но при наличии связи это происходит мгновенно, образуя что называется «псевдо-онлайн». Однако при отсутствии связи узлы также продолжают функционировать - им не нужен сервер. Отсылку данных с мобильного клиента на сервер разработчик организует сам, как вариант, это может быть исполнение метода узла на сервере и передача ему данных.
Таким образом можно сказать, что главное отличие веб-клиента от мобильного клиента заключается в том что веб-клиент работает только чисто онлайн, мобильный - в основном локально, независимо от связи с сервером.
Репозиторий¶
Для того чтобы начать работать с конфигурацией, ее нужно сохранить локально для клиента в репозиторий. Это касается и веб-клиента, не смотря на то, что он чисто онлайн.
И мобильный и веб-клиент работают со своим набором конфигураций - репозиторием. Для веб-клиента, пользователь может установить те конфигурации, для которых у него есть доступ. Работа с обработчиками на сервере имеет особенность - при сохранении конфигурации, файл обработчиков конфигурации генерируется и клиент работает с ним непосредственно (т.е. не достает каждый раз из репозитория), при этом сама конфигурация храниться в репозитории и клиент работает с ней, в случае изменений надо обновить ее в клиенте. Такой подход позволяет отлаживать обработчики.
Работа с несколькими комнатами и регистрация в комнатах¶
В то время как мобильный клиент может быть подключен только к одной комнате, веб-клиент может регистрировать изменения в разных комнатах (мобильный клиент все же может использовать команду register для разных комнат). У каждого класса на закладке Миграция можно определить альяс комнаты по умолчанию, а в разделе конфигурации «Комнаты» можно сопоставить псевдонимам реальные комнаты конкретного инстанса, тогда для каждого класса изменения будут уходить в свою комнату к которой подключена своя группа мобильных клиентов.
На вкладке Миграция можно задать регистрацию при записи - при записи объекта в клиентах будет происходить регистрация в комнате по умолчанию для класса (для веб-клиента) или в комнате из настроек (для мобильного клиента).
Также можно включить «Команда для регистрации» - будет добавлена кнопка «Регистраци» в блоке стандартных команд.
Обложки для мобильного и веб-клиента¶
Узлы отображаются в разделах, в списках узлов и им нужно некое представление для пользователя, нужно выводить какие то данные. Помимо списков разделов узлы могут быть представлены в других элементах - списках, полях выбора и т.д.
У узла в классе есть поле Обложка (Cover), в ней можно задать макет, по общим правилам разметки (общие правила для экранов, обложек, диалогов и т.д.) и он будет применяться ко всем узлам.
Но для веб клиента можно задать особую обложку «Особая обложка для веб-клиента» - по тем же принципам, просто она будет отлична от мобильного клиента.
Также для веб-клиента можно задать обложку для табличного представления клиента в списке (в списке узлов есть переключатель вида) в формате Загловок|ключ
И наконец, для каждого узла в _data можно задать непосредственно обложку для конкретного узла с помощью свойства _cover. Это имеет наивысший приоритет и перекрывает предыдущие способы. С помощью _cover можно делать свои обложки для каждого узла, т.е. играться не только содержанием, но и формой.
Активные обложки¶
В обложках можно выводить не только данные или статическую разметку, но и активные элементы, генерирующие события (например кнопки, галочки). Особенность заключается в том, что эти события направляются в тот узел, к которому принадлежит обложка. Т.е. события обработаются непосредсвенно в этом узле, даже если это например список узлов, в каком то другом узле.
Стандартные команды¶
В клиентах можно разместить «Стандартные команды» включив в классе галочку «Использовать стандартные команды». Их можно сделать и вручную - элементами и обработчиками, но так проще.
Становятся доступны:
Сохранить и закрыть
Сохранить
Регистрация (если настроена)
Удалить
Закрыть
Пользователи¶
Для веб-клиента можно завести пользователей и разграничить их доступ в разделе Пользователи. По умолчанию, единственный пользователь имеет полный доступ. Вообще управлять доступом можно только с уровнем доступа Конфигуратор.
Сейчас доступно:
доступ на уровне модулей: веб-клиент, API (имеется ввиду прием REST-запросов), Конфигуратор.
доступ к конфигурациям - можно выбрать конфигурации, доступные пользователю. Влияет на доступность выбора конфигураций, при добавлении в Репозиторий, т.е. на список выбора конфигураций
Будет доступно (планируется):
доступ на уровне классов
доступ на уровне узлов (RLS)
Работа с аппаратным сканером штрихкодов¶
Для получения событий onBarcode на мобильном устройстве и десктопе используются разные подходы.
На мобильном клиенте (в случае работы на терминале сбора данных, ТСД) используется соединение с программой-драйвером штрихкодов через подписку на события сканера. В настройках надо указать имя события, переменную, и, опционально, длину поля (если программа-драйвер выгружает данные не в виде строки, а в виде байт-массива).
В веб-клиенте на компьютере пользователя необходимо запустить драйвер, а в браузере настроить соединение с драйвером (драйвер поднимает веб-сокет как сервер локально на компьютере, браузер является его клиентом). Драйвер работает как сервис, резидентно, с запуском из командной строки.
Программу можно скомпиллировать pyinstaller под нужную ОС, скачав с GitHub или просто запустить под Python, установив зависимости. Либо можно скачать с exe-файл.
Параметры задаются в командной строке. По умолчанию настроен перехват в режиме эмуляции клавиатуры. Можно задать хост и порт.
Параметр –keyboard по умолчанию:
barcode_scanner_ws --keyboard --host 127.0.0.1 --port 8765
Для подключения через COM-порт:
barcode_scanner_ws --serial COM3 --baud 9600 --host 127.0.0.1 --port 8765
Общие функции мобильного и веб-клиента¶
Общее устройство интерфейса и событий¶
Все визуальные узлы (узлы данных и пользовательские процессы) располагаются в разделах.
Разделы задаются в секции конфигурации Разделы в виде ИД и заголовка. Также для раздела можно прописать команды, которые будут показаны в виде кнопок снизу раздела в виде списка через запятую <Заголовок команды>|<ид команды>. При нажатии на кнопку генерируется общее событие onStartMenuCommand в котором в качестве параметра передается ИД команды Класс узла в свою очередь относится к разделу через свойство Код раздела где нужно выбрать существующий раздел
Все узлы в разделах могут иметь обложку или так называемую обложку по умолчанию. Обложка задается в виде стандартной разметки (см. далее) в классе узла, но также может быть задана в _data (свойство «_cover») – таким образом узлы одного класса могут иметь разную разметку. И также может переопределяться для конкретного узла методом SetCover
При нажатии на узел в разделе открывается форма узла и генерируется событие onShow в котором можно прописать обработчик для отрисовки экрана, иначе он может оказаться пустым. С этого экрана пользователь может уйти, а потом вернуться (например открыв другой узел в другом экране), поэтому в таких случаях, в случае возврата на экран генерируется событие onResume в котором можно прописать тоже самое что и в onShow либо что то особенное.
Форма узла вместо списка раздела¶
Обычно раздел показывает список обложек узлов. В настройках раздела можно
назначить форму узла, тогда вместо списка открывается экран указанного узла.
Поле mobile_form_node задаёт форму Android-клиента, web_form_node —
веб-клиента. Эти поля независимы; можно выбрать разные узлы.
Пример определения раздела Деньги из конфигурации Money:
{
"name": "Деньги",
"code": "money",
"commands": "Добавить операцию|add_operation",
"mobile_form_node": "MoneyWorkspace$singleton",
"web_form_node": "MoneyWorkspace$singleton"
}
В примере MoneyWorkspace — скрытый служебный класс типа custom_process
с has_storage: false, содержащий разметку экрана. MoneyOperation —
отдельный сохраняемый класс с операциями. Статическую форму MoneyWorkspace
можно задать через init_screen_layout и init_screen_layout_web.
Разметка формы не обязана содержать самостоятельный список узлов раздела:
в ней может быть таблица nodes_source со ссылками на нужные узлы.
События формы раздела обрабатываются как общие события конфигурации
(CommonEvents), а не автоматически как events класса:
onInput— изменение полей на Android;onInputWeb— в вебе;onStartMenuCommand/onStartMenuCommandWeb— нажатие команды раздела (напримерadd_operation); ID команды приходит в данных события;сами методы могут быть общими функциями в соответствующих обработчиках Android и сервера, например
money_refresh(input_data=None)иadd_money_operation(input_data=None).
{"event":"onInput", "listener":"", "actions":[
{"action":"run", "source":"internal", "server":"internal", "method":"money_refresh"}
]}
Обработчик money_refresh может заполнить поля данных формы: operations
(массив UID операций), total_text, date_from и date_to, после чего
вернуть (True, data). На события из веб-клиента настройте отдельное
onInputWeb. Аналогично для команд раздела нужны события каждой платформы.
Небольшая практическая тонкость Android: если рядом с Spinner обычный Text
оказывается сдвинут по вертикали из-за выравнивания базовых линий, оберните
Text в VerticalLayout с одномерным value. Это приём компоновки,
а не необходимость переписывать обработчик.
Отборы раздела по индексам классов¶
Отборы на форме списка раздела формируются из индексов класса.
В редакторе класса откройте вкладку Indexes, создайте индекс по полю
и включите для него filter_enabled. Укажите подпись filter_label
и тип данных filter_type. Веб-клиент показывает доступные отборы над
списком, Android — в диалоге по кнопке отборов. При выборе нескольких
условий применяется пересечение условий (логическое AND).
"indexes": [
{"name":"closed", "kind":"hash_index", "keys":"closed",
"filter_enabled":true, "filter_label":"Closed", "filter_type":"boolean"},
{"name":"count_of_products", "kind":"hash_index", "keys":"count_of_products",
"filter_enabled":true, "filter_label":"Count of products", "filter_type":"number"},
{"name":"main_product", "kind":"hash_index", "keys":"main_product",
"filter_enabled":true, "filter_label":"Main product", "filter_type":"node"}
]
Поддерживаются типы отбора по булевым значениям, числам, строкам и ссылкам
на узлы. Доступные операторы зависят от типа (для чисел — в том числе
=, >, >=, <, <=, <>). Условие участвует в запросе,
только когда пользователь его включил. В Android используются локальные
индексы SQLite, а не выгрузка всего класса в память для фильтрации.
Индексы server_only и семантические индексы не подходят для автономных
отборов Android. Для старых локально сохранённых узлов может потребоваться
восстановление индекса.
Принципы разметки экрана и других визуальных форм.¶
Доступность: мобильный клиент и веб-клиент
В системе используется 2 альтернативных подхода к разметке, которые можно комбинировать. По умолчанию (будем называть это основной подход) используется принцип разметки «по строкам» т.е. список вертикальных «строк», каждая строка которого – это массив элементов в горизонтали (каждый элемент – визуальный объект какого то типа type). Понятно, что такой подход не исчерпывающий, но для большинства бизнес-приложений его хватает. Высоту и поведение как строк и элементов при желании можно настроить. Альтернативой этому можно считать разметку контейнерами – вертикальными, горизонтальными и вертикальными/горизонтальными скроллами. Причем оба подхода можно смешивать, а можно например просто в 1ю строку добавить контейнер и весь экран выстроить на контейнерах (полностью перешагнуть через разметку строками не удастся – разметка все равно должна иметь хотя бы 1 строку).
Для разметки экрана, обложки узла, диалога, элемента списка применяется единый подход в виде строки JSON-макета (в параметрах python-методов это внутренние типы list/dict) такого вида:
[ #общий вертикальный список сверху-вниз
[{"type":…},{"type":…}], #горизонтальная строка элементов
[{"type":…},{"type"":…}], #горизонтальная строка элементов
...
]
Пример:
Layout1 =[
[{"type":"Table","id":"l1","value":table_data,"layout":table_layout }],
[{"type":"Button","id":"btn_repl","caption":"@caption"}]
]
- В «строках» располагаются визуальные элементы. Каждый визуальный элемент имеет свойства:
«type» (string)- тип элемента,
«id» (string)- идентификатор элемента. Для активных элементов (с которыми взаимодействует пользователь) как правило генерируется событие со свойством listener, равным id элемента. Также по id можно обратиться к элементу и задать его свойства
«visible» (int) – видимость элемента: 1 – видим, 0- невидим (но занимает место на экране), -1- невидим (и не занимает место на экране)
«w» – вес элемента. Вес элемента по горизонтали/вертикали (в зависимости от родителя-контейнера) относительно других элементов. По умолчанию w=1. Пример:
[{"type":"Button","id":"back","caption":"Back"},{"type":"Button","id":"next","caption":"Forward","w":2}] #кнопка next в два раза шире, потому что у нее w=2 (у back w=1 по умолчанию).В сочетании с высотой/шириной можно управлять разметкой. Зависит от поведения родительского контейнера. Если контейнер «по размеру элементов» то вес не имеет смысла, имеет смысл толкьо если «на всю ширину» или 0.«width» – ширина. Может задаваться как в относительных числовых размерах, так и в виде абсолютных значений: -1 – на весь контейнер, -2 – по высоте элементов. Зависит от поведения корневого контейнера. Если контейнер имеет измерение «по ширине элементов», то «на всю ширину» не имеет смысла
«height» – высота. Может задаваться как в относительных числовых размерах, так и в виде абсолютных значений: -1 – на весь контейнер, -2 – по высоте элементов. Зависит от поведения корневого контейнера. Если контейнер имеет измерение «по ширине элементов», то «на всю ширину» не имеет смысла
padding - внутренние отступы
gravity - выравнивание по горизонтали. Значения: left, center, rigth
Правила по умолчанию такие:
Корневой вертикальный список растянут на весь экран без прокрутки.
Все элементы в строке по умолчанию имеют высоту «по высоте элементов», а сама строка растянута по ширине «на весь контейнер» (т.е. по горизонтали от края до края экрана) и не имеет веса.
Но можно поменять свойство горизонтальной строки с помощью добавления элемента «Parameters» с свойствами «w»,»width» и «height» – т.е. установить для горизонтальной строки (она является контейнером) вес ширину и высоту. Это важно, если требуется более сложная разметка.
Например в этой разметке нижняя строка занимает всю оставшуюся высоту, за счет того, что у нее вес = 1, тогда, в элементе Input в этой строке имеет смысл применить высоту = -1 (на всю высоту)
[
[{"type":"Input","id":"title","caption":"Subject","value":"@title"}],
[{"type":"Parameters","w":1},{"type":"Input","height":-1,"id":"body","caption":"Text","value":"@body","input_type":"multiline"}]
]
Сама горизонтальная строка является контейнером, но в нее также можно вписывать другие контейнеры. В принципе можно полностью отказаться от разметки строками и вывести только 1 строку с высотой – на весь экран, а внутри нее сделать все элементами -контейнерами.
Контейнеры нужны как для разметки так и для того, чтобы собрать несколько элементов в группу и управлять ими вместе (например видимостью)
Предупреждение
Крайне важно помнить – если вы используете контейнеры, у элементов обязательно надо задавать ширину и высоту. У самих контейнеров как правило тоже. И также возможно Parameters у «строки»
У контейнеров есть свойства:
Общие свойства (тип, вес, ширина, высота)
Свойство value – список элементов. Для Card в виде общей разметки «строками» (массив массивов)–[[]], а для остальных контейнеров – в виде массива/списка ( [] ) элементов. Которе соответственно выстраиваются по горизонтали или вертикали в зависимости от типа контейнера.
Существуют такие варианты контейнеров:
«VerticalLayout» – вертикальный не визуальный контейнер без прокрутки
«HorizontalLayout» – горизонтальный не визуальный контейнер без прокрутки
«VerticalScroll» – вертикальная прокрутка. Имейте ввиду, что прокрутка «уничтожает» свойства элемента «на всю высоту»
«HorizontalScroll» – горизонтальная прокрутка
«Card» – визуальный контейнер «карточка» служит для визуального оформления как элементов списка, так и группировки элементов на экране.
Статическая и динамическая разметка формы узла¶
Разметка узла может быть задана через команду Show (т.е. нужно сделать метод и подцепить его на событие onShow), переопределена другими динамическими методами. Этоназывается динамическая разметка Но иногда удобнее не делать этого, а просто указать в карточке класса. Это удобно если экран один. Если у узла есть какие то переключающиеся экраны, то их все равно придется выводить командой Show.Такая разметка называется статическая и есть несколько вариантов как ее задать:
Задать в классе узла непосредственно в виде разметки (JSON). Больше ничего не надо делать - узел при открытии отрисуется по этой разметке.
Создать элемент разметки (раздел «Общая разметка») и использовать его в классе. Там можно выбрать из имеющихся вариантов
В _data узла поместить разметку в ключ _layout. У этого варианта - наивысший приоритет.
Виды визуальных объектов и их уникальные свойства¶
Надпись¶
Доступность: мобильный клиент и веб-клиент
Вывод текстовой строки.
«type» : «Text»
«value»: строковая константа либо ссылка на переменную с префиксом @. Можно использовать html разметку (как и во всех остальных надписях)
«text_color» – цвет текста в HEX-формате (пример #F54927)
«radius» - закругление фона (число)
«stroke» - обводка линией (число)
«background» - цвет фона в HEX-формате (пример #F54927)
«size» – размер (целое число)
Картинка¶
Вывод картинки
«type» : «Picture»
«value» – абсолютный путь к файлу. Константа либо ссылка на переменную _data с префиксом @
Пример:
my_layout = [
[{"type":"Picture","value":"@image_path"}]
]
Дополнительные возможности Card и Picture (SVG, редактирование)¶
Card поддерживает SVG-иконку с теми же полями, что другие SVG-элементы:
svg (текст SVG), svg_size (размер), svg_color (цвет для
монохромной иконки). Свойство image_position определяет размещение:
left (по умолчанию), right, top, bottom. Наполнение карточки
остаётся в value; для переносимой разметки предпочтительны строки
(массив массивов), при этом существующий веб-рендерер допускает также
одномерный массив вложенных элементов.
{"type":"Card", "svg":"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='4' y='4' width='16' height='16' rx='3' fill='#4f7890'/></svg>",
"svg_size":42, "image_position":"left", "value":[[
{"type":"VerticalLayout", "value":[
{"type":"Text", "value":"Объект", "bold":true},
{"type":"Text", "value":"Насосная станция №3", "size":14}
]}
]]}
Picture также поддерживает svg / svg_size / svg_color для
статического рисунка. Фото через value:"@field" можно сделать
редактируемым: editable:true. Пользователь сможет заменить фотографию
или убрать её; картинка хранится в поле узла и отображается в заданной области.
Используйте radius (например, 12), fit (contain или cover),
width и height. Фото можно открыть в полном размере. Для редактируемого
Picture требуется привязка к полю; не совмещайте editable со
статическим svg.
{"type":"Picture", "id":"work_photo", "value":"@work_photo",
"editable":true, "width":250, "height":170,
"radius":12, "fit":"cover"}
Разделитель Divider¶
Divider (псевдоним Separator) — тонкая горизонтальная линия между
группами разметки без отдельной карточки или тени.
[[{"type":"Text", "value":"Файлы задания", "bold":true}],
[{"type":"Divider", "width":-1}],
[{"type":"Files", "id":"docs", "value":"@docs"}]]
Вертикальный разделитель VerticalDivider¶
VerticalDivider — тонкая вертикальная линия между элементами одной
горизонтальной строки. Поддерживается в веб-клиенте и на Android.
Divider и Separator по-прежнему означают горизонтальную линию.
Разделитель не имеет значения value, привязки к узлу или событий.
Высота без явного параметра подстраивается под соседние элементы строки;
при необходимости можно указать height в единицах разметки.
Ширина линии — около 1 px/dp, отступы слева и справа добавляются автоматически.
[
[
{"type":"Text", "value":"Объект: Насосная станция", "size":14},
{"type":"VerticalDivider", "height":32},
{"type":"Text", "value":"В работе", "size":14}
],
[{"type":"Divider", "width":-1}]
]
Можно также поместить VerticalDivider внутрь HorizontalLayout.value
(одномерный массив элементов). Для разграничения блоков сверху и снизу
используйте Divider.
Блок документов Files¶
Files отображает список вложений из поля узла — массив JSON-объектов.
У файла могут быть name, description, mime, size (байты),
storage (local / server / s3), path / url / uri
и sync_status. editable:true включает добавление и удаление
привязок. caption задаёт заголовок блока. sync выбирает способ
синхронизации вновь добавленных файлов (local, server, s3).
{"type":"Files", "id":"docs", "caption":"Документация",
"value":"@docs", "editable":true, "sync":"server"}
Пример одного элемента _data["docs"]:
{
"name": "Инструкция по монтажу.pdf",
"description": "Руководство для монтажника",
"mime": "application/pdf",
"size": 2457600,
"storage": "server",
"path": "instructions/manual.pdf",
"sync_status": "synced"
}
По расширению файла автоматически выбирается типовая иконка. Отдельный
маленький индикатор показывает офлайн-доступность, а не просто успешную
загрузку на сервер: зелёный означает наличие локальной копии на Android;
synced само по себе этого не гарантирует. Веб-клиент не может
определять состояние кеша Android по URL. Удаление из Files отвязывает
документ от узла, а не обязательно удаляет оригинал на сервере/S3.
Поступающие с сервера ссылки (например, из интеграции 1С) должны вести на
уже размещённые файлы в UserFiles Flask или S3. Мобильный клиент
может кешировать документы для офлайн-работы. При добавлении на Android
файл сначала сохраняется локально, затем используется настроенный вариант
синхронизации. Хранение base64 остаётся вариантом для отдельных сценариев,
но не является способом по умолчанию для произвольных вложений.
Для Android есть также кнопка выбора произвольного файла через PlugIn:
self.PlugIn([{"type":"FileButton", "id":"attach_doc", "caption":"Добавить файл",
"target":"docs", "sync":"server"}])
В примере target — поле массива вложений, не отдельная галерея.
Встраиваемые кнопки editable и PlugIn — альтернативные способы
инициировать выбор файла; без необходимости не добавляйте оба в одну форму.
Слайдер изображений¶
Вывод картинки
«type» : «ImageSlider»
«value» – список имен файлов (для мобильной - абсольюный путь, для веб-имя файла) либо ссылка на переменную в _data, содержащую такой список
«captions» - список заголовков (необязательный) либо ссылка на такой список
«cover» - (для web) - подстраивает пропорции под размер элемента
Пример:
my_slider = { "type": "ImageSlider", "value": "@pic_files_web", "width": 100, "height": 100 , "cover":true, "captions":["1","2","3"]}
Особенности работы с изображениями в мобильном и веб-клиенте¶
Изображение (файл, медиафайл) всегда храниться на диске.
В веб-клиенте: в папке UserFiles<uid конфигурации> сервера
В мобильном : во внутренней папке приложения, связанной с конфигурацией
Ссылка на него (в элементе Picture, ImageSlider) задается в виде имени файла, но по разному – в мобильном – это абсолютный путь, в веб-клиенте – имя файла (он сам добавляет путь).
На сервере, разместив файл каким либо образом в папке конфигурации (помимо обработчиков, этом можно сделать через галереи Медиа и Файлы или просто через API) обратиться в элементе к нему можно через имя файла
На сервере, для примера реализовано два роута для работы с картинками:
POST /api/userfiles/<config_uid>/images – принимает список файлов и сохраняет их в нужном формате. Пример обращения описан в Быстром старте
GET /api/userfiles/<config_uid>/raw/<path:filename> - получить картику по имени файла
Поле HTML¶
Доступность: мобильный клиент и веб-клиент
Вывод html-документа
«type» : «HTML»
«value» – строка в формате HTML
Кнопка¶
Доступность: мобильный клиент и веб-клиент
Вывод кнопки. При нажатии на кнопку экрана генерируется события с listener=<id элемента> . При использовании в списках см. раздел Активные элементы списков
«type» : «Button»
«caption» – надпись на кнопке
«small» - кнопка в виде маленькой круглой кнопки
«background» – цвет фона кнопки в HEX-формате
Пример:
{"type":"Button","id":"btn_update","caption":"Simple button"}
Список нижних кнопок¶
Доступность: мобильный клиент
Выводит горизонтальный массив кнопок внизу экрана. Только для размещения в экране.
«type» : «BottomButtons»
«value» – список кнопок с id. Параметры каждой кнопки такие же как у Button
Пример:
{"type":"BottomButtons","id":"bottom","value":[{"type":"Button","id":"back","background":"#F54927","caption":"Back"},{"type":"Button","id":"next","background":"#25a018","caption":"Forward","w":2}]}
Переключатель¶
Доступность: мобильный клиент и веб-клиент
Элемент переключателя. Генерирует событие и пишет в переменную (id элемента) текущее значение переключателя.
«type»: «Switch»
«caption» –(строка) заголовок элемента
«value» –(булево) значение элемента для инициализации – в виде константы или ссылки на переменную. Как правило сюда ставят ссылку на id
Пример:
{"type":"Switch","caption":"Setting 1","id":"sw1","value":"@sw1"}
Галочка¶
Доступность: мобильный клиент и веб-клиент
Переключатель
Элемент переключателя. Генерирует событие и пишет в переменную (id элемента) текущее значение переключателя.
«type» : «CheckBox»
«caption» –(строка) заголовок элемента
«value» –(булево) значение элемента для инициализации – в виде константы или ссылки на переменную. Как правило сюда ставят ссылку на id
Пример:
{"type":"CheckBox","caption":"Setting 2","id":"cb1","value":"@cb1"}
Списки¶
Доступность: мобильный клиент и веб-клиент
Выводит различные списки элементов. В качестве наполнения можно использовать как список, созданный в обработчике так и датасет и список узлов класса. В элементах возможно размещение активных элементов. Для более красивого оформления элементы списка можно упаковать в Card. По умолчанию макет элемента – автогенерируемый, но его можно переопределить как для всего списка, так и для любого элемента.
«type»: «Table»
«value» – источник данных списка. Может быть определен как просто «список словарей» в python что делает его хорошо совместимым с NoSQL такими как Pelican например
Пример такого определения:
table_data = [{"name": "element 1", "key":"el1"},{"name": "element 2", "key":"el2"}]
{"type":"Table","id":"l1","value":table_data}
Либо (если используются датасеты) может быть указана ссылка на датасет:
{"type":"Table","id":"l2","value":"goods" }
Либо, если используется свойство nodes_source можно вывести список узлов (тогда при нажатии будет открываться узел).Это может быть поиск (отбор) узлов или просто какой то произвольный список. Узлы перечисляются в виде uid-ов. Преобразовать список объектов в список uid-ов можно с помощью функции to_uid
Пример (вывод списка всех узлов класса Note):
{"type":"Table","id":"t1","nodes_source":True,"value":to_uid(Note.get_all())}
«layout» – макет списка в целом в стандартном «строчном» формате.
Пример:
table_layout = [
[{"type":"Text","value":"@_id","bold":True}],
[{"type":"Text","value":"@name"},{"type":"Text","value":"@barcode"}]
]
{"type":"Table","id":"l1","value":table_data, "layout": table_layout }
_layout в элементе списка – делает то же что и layout только для конкретного элемента списка. Это свойство просто добавляется в источник данных:
table_data = [{"name": "element 1", "key":"el1","_layout":layout2},{"name": "element 2", "key":"el2"}]
_background в элементе списка – раскрашивает фон в заданный HEX-цвет
table_data = [{"name": "element 1", "key":"el1","_background":"#701705" },{"name": "element 2", "key":"el2"}]
«columns_count» – количество столбцов таблицы. Выводит список в виде нескольких столбцов
«horizontal» – горизонтальная ориентация списка.
«search_enabled» – включает режим поиска
Для датасета своя секция настроек поиска dataset_search в которой есть method(метод поиска), keys(поля поиска) и min_length (необязательно) минимальная длина с которой начинается поиск.
Метод может быть:
text - для обычного поиска по вхождению строки
levenshtein - для нечеткого поиска по расстоянию Левенштейна (будут выведены результаты в порядке убывания точности, с отбором >75, сама точность добавляется в записи в поле _confidence
Также для датасета всегда включена пагинация (невидимая) но можно настроить размер страницы – свойство page_size
Пример:
dataset_search = {"dataset":"goods","keys":"name","method":"text","min_length":2}
self.Show([
[ {"type":"Table","id":"l2","value":"goods" ,"search_enabled":True,"dataset_search":dataset_search}]
])
Вывод списка в виде таблицы¶
Доступность: мобильный клиент и веб-клиент
В объекте Table можно определить свойство table=true и использовать свойство table_header c заголовками в формате ["Заголовок|Ключ"] либо ["Заголовок|Ключ|Вес"] тогда список будет выведен в виде плоской таблицы со столбцами. В остальном все тоже самое - используются те же источники (в случае источников - узлов будет выведена не обложка а поля в соответствии с table_header)
Пример:
{"type":"Table","id":"tab4","value":lines,"table":True,
"table_header":["#|n","Position|position","Qty|qty"]}
Список подчиненных узлов¶
Доступность: мобильный клиент и веб-клиент
Выводит иерархический список подчиненных узлов и их потомков
«type» : «NodeChildren»
Без параметров
Закладки/Страницы¶
Доступность: мобильный клиент и веб-клиент
«type»: «Tabs»
Группирует данные по закладкам (объектам «type»:»Tab»). Внутри каждой закладки – контейнер с о обычной строчной разверткой
- Общий синтаксис такой: {«type»:»Tabs»,»value»:[
{«type»:»Tab»,»id»:»tab1»,»caption»:»Base elements»,»layout»:tab1_layout}, {«type»:»Tab»,»id»:»tab12»,»caption»:»List as Table»,»layout»:tab2_layout}
]}
У таб есть одно свойство - value - список закладок Tab
У каждой Tab есть:
id
caption
layout - собстввенно, содержимое страницы
Пример
tab1_layout = [
[{"type":"Spinner","id":"my_spinner","caption":"my select:","value":"@my_spinner", "dataset":spinner_dataset}],
[{"type":"NodeInput","id":"ni","value":"@ni","caption":"Заметка","dataset":"Note"}]
]
lines = [
{"n":1,"position":"Position #1","qty":100},
{"n":2,"position":"Position #2","qty":120},
{"n":3,"position":"Position #3","qty":10}
]
tab2_layout = [
[{"type":"Table","id":"tab4","value":lines,"table":True,
"table_header":["#|n|1","Position|position|7","Qty|qty|1"]}]
]
self.Show([[{"type":"Tabs","value":[
{"type":"Tab","id":"tab1","caption":"Base elements","layout":tab1_layout},
{"type":"Tab","id":"tab12","caption":"List as Table","layout":tab2_layout}
]}]])
Доступность: мобильный клиент и веб-клиент
Вся палитра полей ввода (кроме полей датасета, которые настраиваются аналогично) имеет тип Input – обычные поля, многострочные, выпадающие списки и т.д. Все введенные символы сразу записываются в _data узла. Если стоит автосохранение в классе узла то узел сразу же сохраняется при этом.
«type» : «Input»
«input_type» тип значений ввода (по умолчанию – просто текст, input_type можно не указывать):
NUMBER – числа. Причем числа, при записи в переменную автоматически распознаются на float/integer в зависимости от наличия дробной части
PASSWORD – ввод пароля (закрывается звездочками)
MULTILINE – многострочный текст
DATE – дата. При выборе даты в _data попадает 2 значения – представление даты (в переменную=id) и в _d<id элемента> - дата в формате ISO
«events» – флаг, подключающий генерацию событий при вводе в поле ввода
«value» – отображаемое значение по умолчанию
«caption» заголовок поля
«spinner_mode» – режим выпадающего списка. Необходимо задать в таком случае значение свойства source – список значений в виде строки с разделителем «;»
«autocomplete» -режим подбора по первым символам. Также должен быть задан source
Доступность: мобильный клиент и веб-клиент
«type» : «Spinner»
Элемент позволяет отобразить датасет в виде выпадающего списка и получить выбранное значение.
- Датасет элемента задается в виде списка словарей с обязательными полями:
_id ключ варианта
_view представление выбранного значения
Остальные поля могут быть любые, они не отображаются но участвуют в результате
В переменную, равную id возвращается _id из выбранного элемента. Этим же _id можно инициализировать элемент.
Пример:
spinner_dataset = [{"_id":"item1","_view":"item 1","some_key1":"some_value1"},
{"_id":"item2","_view":"item 2","some_key1":"some_value2"},
{"_id":"item3","_view":"item 3","some_key1":"some_value3"}]
{"type":"Spinner","id":"my_spinner","caption":"my select:","value":"@my_spinner", "dataset":spinner_dataset}
Доступность: мобильный клиент и веб-клиент
«type» : «NodeInput»
«dataset» – имя класса узлов. При выборе будет открыта форма выбора с поиском, сортировкой, видимостью и обложками, заданными для данного класса.
Элемент позволяет выбрать узел в качестве значения в каком то другом узле, то есть указать ссылку на узел.
Значение возвращается/устанавливается в универсальном формате ссылки на узел <class_name>$<node_id>
Доступность: мобильный клиент и веб-клиент
Используются для выбора конкретного значения датасета на форме – элемента справочника, документа. Удобство заключается в том, что выбирается ссылка на объект по которой можно получить объект целиком или представление объекта. На картинке в разделе Поля ввода Production - поле датасета, в котором отображается имя продукции и код - такой задан шаблон
Представление объекта датасета задается в поле Шаблон записи в датасете. Там также можно использовать html
«type» : «DatasetField»
«dataset» – имя датасета
«spinner» – режим выбора из списка (исключает режим автоподстановки)
«autocomplete» – режим автоподстановки.
«caption» – заголовок поля
«value» – значение по умолчанию. Задается в виде ссылки на элемент датасета <имя датасета>$<id элемента датасета>
Доступность: мобильный клиент и веб-клиент
В разметка можно просто указать строку вместо объекта с type=Text, тогда будет выведена строка или , если указан префикс @ значение из _data, ["Hello world"]
Как вариант можно использовать строковую конструкцию <заголовок>|<значение> тогда также будет выведено Строкове значение но в виде своеобразной Card с заголовком ["title|Hello world"]
Подобные конструкции можно использовать для упрощения, вместо Text, Card
Активные элементы списков (в Таблица)¶
Доступность: мобильный клиент и веб-клиент
В разметке элементов списка можно использовать не только надписи и картинки но и некоторые активные элементы, например, кнопки. При взаимодействии с ними в узел поступают несколько другие (расширенные) данные чем при размещении в экране. Разработчик получает имя списка, имя активного элемента, позицию и значение (для элементов ввода значения)
Например Кнопка (Button). Переменные, при событии нажатия (onInput):
listener – поступает в формате <id таблицы>_input<id элемента>
<id таблицы>_input_position - в эту переменную возвращается позиция элемента списка, в котором произошло нажатие
Если в _data есть key тогда также возвращается ключ:
<id таблицы>_input_key - значение key элемента
Для полей ввода – CheckBox/Switch/Input добавляется еще само введенное значение в переменную _data - <id таблицы>_input<id элемента>
Доступны активные элементы:
Кнопка
Поля ввода (Input)
Галочка
Переключатель
Функции/методы UI/UX мобильного клиента¶
Методы узла (на устройстве)¶
Метод отрисовки экрана Show¶
Доступность: мобильный клиент и веб-клиент
Show(layout) отрисовка экрана на странице узла. Этот метод «очищает холст» и выводит разметку , принципы которой описаны в разделе «Разметка экрана». Как минимум его надо прописать в обработчике onShow чтобы что то появилось. Также он может быть вызван в других обработчиках чтобы обновить экран с новыми данными, перерисовать. Хотя для этого есть более экономная команда UpdateView, но иногда перерисовать целиком – проще.
self.Show([
[{"type":"Input","id":"title","caption":"Subject","value":"@title"}],
[{"type":"Parameters","w":1},{"type":"Input","height":-1,"id":"body","caption":"Text","value":"@body","input_type":"multiline"}]
])
Метод подключения механизмов экрана PlugIn¶
Доступность: мобильный клиент и веб-клиент(только указанные)
PlugIn(elements) подключает несколько элементов к экрану. Это и аппаратные механизмы, такие как аппаратный сканер и визуальные элементы вне разметки (иначе бы они были выведеные в Show). Параметр команды – одномерный массив объектов (в python- список) с объектами вида {«type»:»type_of_element»,»id»:»element_id»}.
Внимание
Команда очищает все элементы перед доабавлением (self.PlugIn([])- все очистит), поэтому в ней надо сразу перечислить все элементы.
Типы объектов(ключ type) бывают следующие:
**CameraBarcodeScannerButton* (только мобильный) – экранный элемент сканирования штрих-кода. Это кнопка на экране которая вызывает камеру для сканирования. При сканировании генеририруется событие с listener=<ИД элемента> и в _data[<ИД элемента>] будет помещен штрихкод
Пример:
self.PlugIn([{"type":"CameraBarcodeScannerButton", "id":"barcode_cam"}])
BarcodeScanner – подключение перехвата штрихкода с аппаратного сканера (для терминалов сбора данных). Аппаратный сканер должен быть настроен в Настройках в разделе Настройка аппаратного сканера. Должна быть активирована подписка на события аппаратного сканера и указаны данные, которые через Intent отдает ПО вашего сканера, а также в самом сканере надо настроить чтобы он отдавал штрихкоды в Intent broadcast а не в клавиатуру например. При сканировании генерируется событие с listener=<ИД элемента> и в _data[<ИД элемента>] будет помещен штрихкод
FloatingButton (только мобильный) – добавляет плавающую кнопку (их может быть несколько) справа внизу экрана. У этого элемента, помимо id (по которому как у всех элементов генерируется событие) есть поле caption для вывода текста кнопки и также есть поля, позволяющие настроить иконку svg (подробности смотри в разделе «svg-иконки»)
Пример:
self.PlugIn([{"type":"FloatingButton","id":"add_child","caption":"Add <b>line</b>"}])
ToolbarButton (только мобильный) – добавляет кнопку в тулбаре. У этого элемента, помимо id (по которому как у всех элементов генерируется событие) есть поле caption для вывода текста кнопки и также есть поля, позволяющие настроить иконку svg (подробности смотри в разделе «svg-иконки»)
Пример:
self.PlugIn([{"type":"ToolbarButton","id":"pin","caption":"Save","svg":svg2,"svg_size":24,"svg_color":"#FFFFFF"} ]])
PhotoButton (только мобильный) – открывает камеру для фотографирования. Снимок сохраняется в файл, путь к файлу – в _data[<ИД элемента>]. Если подключена MediaGallery то снимок автоматически добавляется в галерею. Автоматическое добавление можно отключить поместив флаг OFFAutoupdateMediaGallery в _data узла. Тогда фото будет перехватываться в обработчике и с ним можно проводить какую то обработку перед добавлением в галерею.
Пример ручной обработки
def CaptureImage(self, input_data=None):
gallery_array = self._data["pic_files"]
base64 = getBase64FromImageFile(this._data["result_file"],50,50) #ни к чему не ведет, просто получили кроп в base64
gallery_array.append(this._data["result_file"])
toast(self._data["result_file"])
UpdateMediaGallery(gallery_array )
return True,{}
GalleryButton (мобильный, ожидается на веб) – открывает камеру для прикрепления медиафайла из галереи. Снимок сохраняется в файл, путь к файлу – в _data[<ИД элемента>]. Аналогичен PhotoButton.
MediaGallery (мобильный, ожидается на веб) – галерея массива меддиафайлов внизу экрана с возможностью отркытия, удаления. По ключу = id элемента хранится массив путей к файлам, который отображается в галерее. С ним по умолчанию автоматически взаимодействуют PhotoButton и GalleryButton но также этот массив можно редактировать из обработчика (например поместить туда фото). Также доступно удаление пользователем. После удаления генерируется событие <имя_переменной>_delete в котором можно например забрать измененный массив файлов галереи.
Метод открытия узла в интефейсе _open()¶
Доступность: мобильный клиент и веб-клиент
_open(method=None) – открывает форму узла, как если бы его открыл пользователь. По умолчанию сработает onShow и метод, прописанный в конфигурации, но его можно переопределить в параметре method
Метод обновления элементов на экране UpdateView¶
Доступность: мобильный клиент
UpdateView(id,element=None) – команда для выборочного обновления элементов. Рекомендуется использовать для высоконагруженных интерфейсов (например ActiveCV)
Работает в нескольких режимах:
Просто перерисовывает элемент по id. Это имеет смысле если значение элемента задано переменной (через @) а не константой. Пример:
self.UpdateView("btn_repl",None)Меняет свойства элемента. Пример:
self.UpdateView("btn_repl",{"background":"#C82909"})Заменяет элемент на другой элемент. Можно например проделывать это с контейнерами. Пример :
self.UpdateView("btn_repl",{"type":"Input","id":"inp1","caption":"New input---"})
Общие методы UI/UX для клиента¶
SetTitle (title) (мобильный клиент) – устанавливает заголовок окна узла
RefreshTab() (мобильный клиент) – обновляет текущую вкладку(раздел), из которой открыт узел. Имеет смысл когда узел добавили/изменили и возвращаемся в список
CloseNode() (мобильный клиент) – закрывает форму узла
RunGPS/ StopGPS (мобильный клиент) – команды запуска/остановки GPS. Может потребоваться разрешение от пользователя. Запущенный GPS считывает данные с устройства через промежутки времени и первые данные будут не сразу, а также может меняться точность, поэтому для чтения данных GetLocation лучше использовать таймер
GetLocation (мобильный клиент) – получает данные GPS в формате сериализованного в строку JSON-объекта с полями:
altitude – высота latitude – широта longitude – долгота accuracy – точность, в метрах provider – провайдер данных
ScanBarcode(listener) (мобильный клиент) – запускает сканирование камерой, как если бы пользователь нажал на кнопку сканера, подключенную через PlugIn. По результату возвращается событие, указанное в параметре.
Dialog(id,title,yes_caption=»»,no_caption=»»,layout=None) (мобильный и веб-клиент) – метод для показа как простых диалогов, так и диалогов с разметкой. Минимально – только заголовок, также можно переопределить 2 кнопки и сделать любую разметку layout по общим правилам разметки.
Возвращает событие с listener=<id>_positive/<id>_negative в зависимости от того, какую кнопку нажал пользователь. Если диалог с разметкой, и там есть какие то инпуты, то данные пишутся прямо в _data
Примеры:
def simple_dialog(self, input_data=None):
Dialog("dlg1","To be or not to be?","To be","Not to be")
return True,{}
def layout_dialog(self, input_data=None):
Dialog("dlg2","Enter quantity","Ok",None,[[{"type":"Input","id":"qty_dialog","caption":"Quantity","input_type":"number"}]])
return True,{}
def input(self, input_data=None):
if self._data.get("listener") == "dlg1_positive":
toast("To be")
elif self._data.get("listener") == "dlg1_negative":
toast("Not to be")
elif self._data.get("listener") == "dlg2_positive":
toast(str(self._data.get("qty_dialog") ))
AddTimer(key, period)/ StopTimer(key) – запускает/останавливает таймер с нужным ключом.
Пример:
AddTimer("my_timer",5)
ShowProgressButton/ HideProgressButton – рисует колесико прогресс-бара на кнопке и делает ее неактивной пока не будет выполнено HideProgressButton
Пример:
def worker():
time.sleep(2)
HideProgressButton("button1")
ShowProgressButton("button1")
t = threading.Thread(target=worker)
t.start()
ShowProgressGlobal/ HideProgressGlobal (мобильный клиент) – показывает общий (закрывающий весь экран) прогресс-бар/ снимает его.
SetCover(node,layout) – устанавливает новый макет для узла. Это именно макет, а не данные. Если у вас значения в макете заданы через @ - они и так обновятся.
UpdateMediaGallery() (мобильный клиент) – обновляет галерею. Нужно выполнять после какиз либо действий с составом галереи.
Функции модуля android (мобильный клиент)¶
Интерфейсные команды:
toast(String toast) – вывести сообщение Андроид
message(String text) – вывести сообщение
speak(String text) – произнести текст (TTS engine)
listen() – запустить ожидание распознавания речи. По окончанию возникает событие с listeter либо voice_success либо voice_error. Распознанная строка лежит в _data в voice_result
vibrate() и vibrate(int duration) – вибрация и вибрация заданной длительности
beep()/beep(int tone)/ beep(int tone,int beep_duration,int beep_volume) – звуковой сигнал, т.ч. с возможностью выбрать тон (от 1 до 99), продолжительность и громкость (по умолчанию – 100)
notification(String message)/ notification (String message,String title)/ notification(String message,String title,int number) – уведомление в шторке уведомлений. Number – идентификатор уведомления, по которому к нему можно потом обратиться, чтобы либо убрать, либо перезаписать (обновить)
notification_progress(String message,String title,int number,int progress) – уведомление с прогресс-баром (от 0 до 100) notification_cancel(int number) – скрыть уведомление
Некоторые общие свойства элементов¶
html текст в надписях¶
Везде, где в интерфейсе присутствует текст, можно использовать html-теги для разметки текста. Например Привет <b>мир</b>
svg-иконки¶
Для элементов, где предусмотрены иконки, можно использовать иконки svg. Способ такой:
Скачиваете и сохраняете svg-файл
Открываете файл текстовым редактором и копируете текст в переменную
Можно использовать эту переменную в свойстве элемента svg (также можно установить размер и цвет svg_size и svg_color)
svg1 = '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="m720-120 160-160-56-56-64 64v-167h-80v167l-64-64-56 56 160 160ZM560 0v-80h320V0H560ZM240-160q-33 0-56.5-23.5T160-240v-560q0-33 23.5-56.5T240-880h280l240 240v121h-80v-81H480v-200H240v560h240v80H240Zm0-80v-560 560Z"/></svg>'
self.PlugIn([
{"type":"FloatingButton","id":"add","svg":svg1,"svg_size":48,"svg_color":"#000000"}
])
Функции для преобразования изображений и галереи¶
getBase64FromImageFile(path_to_image) - возращает строку base64 из файла.
convertImageFilesToBase64Array(paths_to_images_array) - удобно для конвератции «Галереи» в одну строку: конвентирует массив путей в массив строк base64
saveBase64ToFile (base64_string) - сохраняет base64 строку во временный файл и возвращает путь к нему
convertBase64ArrayToFilePaths - сохраняет массив строк base64 в масисив путей (временных файлов), подходящий для использования в Галерее.
Общие события клиента. Обработка событий приложения.¶
Некоторые события возникают не в узлах, а в целом в приложении, например событие при запуске платформы, при сканировании штрихкода вне узла и т.д. Они обрабатываются по тому же принципу что и события узлов – то есть имеют назначенное событие и обработчик, только не в структуре узла, а в структуре конфигурации на вкладке Общие события (в конфигурации это раздел CommonEvents)
Обработчики python для подобных событий, соответственно прописываются не в классах узлов, а просто как функции в коде обработчиков. Некоторые события также содержат данные, они передаются в input_data в соответствующий ключ/
Пример:
def onBarcode(input_data):
toast(input_data.get("barcode"))
return True,{}
Они должны иметь параметр input_data типа словарь.
Перечень типов общих событий (мобильный клиент):
onLaunch – событие при загрузке конфигурации (либо при перезапуске). Без параметров.
onTimer – событие таймера. В input_data ключ “timer_key” – ключ сработавшего таймера
onJSONFile – событие открытия JSON-файла приложением (через Открыть или Поделиться). В input_data ключ «content» – содержимое файла в виде строки
onTextFile – событие открытия текстового файла приложением (через Открыть или Поделиться). В input_data ключ «content» – содержимое файла в виде строки
onBarcode – событие штрихкода, сканируемого через ScanBarcode т.е. вне узла, из главного меню
onStartMenuCommand – нажатие на команду в разделе конфигурации (те команды, что добавляются через Разделы конфигурации)
onDialogResult – событие диалога, вызванного из главного меню. Т.к. диалог вызывается с определенным идентификатором то в result возвещается либо result_positive либо result_negative а в ключ result_data возвращается данные из элементов ввода диалога, если таковые были.
onRecognitionListenerResult - общее событие распознавания речи. Возникает когда процесс распознавания запустился не из экрана узла, а либо из фона либо из общего экрана(меню)
Перечень типов общих событий (веб-клиент):
onBarcode – событие штрихкода, сканируемого через ScanBarcode т.е. вне узла, из главного меню
onStartMenuCommand – нажатие на команду в разделе конфигурации (те команды, что добавляются через Разделы конфигурации)
onDialogResult – событие диалога, вызванного из главного меню. Т.к. диалог вызывается с определенным идентификатором то в result возвещается либо result_positive либо result_negative а в ключ result_data возвращается данные из элементов ввода диалога, если таковые были.
Поиск, сортировка и видимость узлов (клиент)¶
В мобильном клиенте NodaLogic есть встроенные механизмы поиска, сортировки и скрытия узлов, которые работают на стороне клиента и не требуют дополнительного кода или API.
Эти механизмы управляются через специальные служебные поля в _data узла.
Поиск по узлам¶
Поведение по умолчанию
Если в узле не задано специальное поле для поиска, приложение выполняет поиск:
по всем ключам словаря _data
поиск осуществляется по строковому представлению значений
Поле _search_index
Чтобы управлять поиском явно и повысить производительность, рекомендуется использовать поле:
_search_index
Если оно задано:
поиск выполняется только по этому полю
поиск становится быстрее и более контролируемым
Пример
self._data["_search_index"] = (
self._data.get("number", "") + " " +
self._data.get("customer_name", "")
)
_search_index должен быть обычной строкой
Сортировка узлов¶
Сортировка списков узлов также выполняется на стороне клиента.
Для этого используются специальные поля:
_sort_string — сортировка по возрастанию
_sort_string_desc — сортировка по убыванию
Пример сортировки
Сортировка по дате (по возрастанию):
self._data["_sort_string"] = self._data.get("plan_date")
Сортировка по дате (по убыванию):
self._data["_sort_string_desc"] = self._data.get("created_at")
Скрытие узлов¶
Узел можно скрыть из интерфейса, не удаляя его физически.
Для этого используется поле:
_hidden
Если установлено значение True:
узел не отображается в списках
узел остаётся доступным через код и API
Пример скрытия узла
self._data["_hidden"] = True
