diff --git a/README.md b/README.md
index c6a6461..65ccda3 100644
--- a/README.md
+++ b/README.md
@@ -68,13 +68,13 @@
> **Внимание:**
>
+> - **Проверьте версию "ПАРУС 8 Онлайн"**
+>
+> Перед копированием расширения из репозитория убедитесь, что в [релизах расширения](https://git.citpb.ru/CITKParus/P8-Panels-ParusOnlineExt/releases) нет специальной сборки для Вашей версии "ПАРУС 8 Онлайн". **Если таковая есть - необходимо устанавливать именно её, а не актуальную сборку из основной ветки репозитория!**
+>
> - **Для релиза "ПАРУС 8 Онлайн" от 30.08.2024**
>
-> Требуется [патч до промежуточной сборки 02.09.2024 или старше](https://cloud.mail.ru/public/nEZb/y4oQa1N6D). Установка расширения на данный релиз не рекомендуется, по возможности - пропустите его.
->
-> - **Для релизов "ПАРУС 8 Онлайн" до 30.08.2024**
->
-> Содержимое папки "bin" следует брать из специальной сборки расширения - [Для сборок Парус-Онлайн до 30.08.2024](https://git.citpb.ru/CITKParus/P8-Panels-ParusOnlineExt/releases/tag/FOR_PARUS_ONLINE_BEFORE_20240830)
+> Требуется [патч для "ПАРУС 8 Онлайн" до промежуточной сборки 02.09.2024](https://cloud.mail.ru/public/nEZb/y4oQa1N6D). Установка расширения на данный релиз не рекомендуется, по возможности - пропустите его.
3. Подключите библиотеку расширения к серверу приложений "ПАРУС 8 Онлайн". Для этого добавьте ссылку на библиотеку в файл "Config\extensions.config" сервера приложений:
@@ -647,8 +647,8 @@ const MyPanel = () => {
В состав API входят:
- `pOnlineShowTab` - функция, отображение типовой закладки "ПАРУС 8 Онлайн"
-- `pOnlineShowUnit` - функция, отображение раздела "ПАРУС 8 Онлайн" в модальном режиме
-- `pOnlineShowDocument` - функция, отображение раздела "ПАРУС 8 Онлайн" в модальном режиме с позиционированием/отбором по документу
+- `pOnlineShowUnit` - функция, отображение раздела "ПАРУС 8 Онлайн" в модальном режиме или на закладке
+- `pOnlineShowDocument` - функция, отображение раздела "ПАРУС 8 Онлайн" в модальном режиме (или на закладке) с позиционированием/отбором по документу
- `pOnlineShowDictionary` - функция, отображение раздела "ПАРУС 8 Онлайн" в режиме словаря
- `pOnlineUserProcedure` - функция, исполнение "Пользовательской процедуры"
- `pOnlineUserReport` - функция, печать "Пользовательского отчёта"
@@ -684,13 +684,15 @@ const MyPanel = () => {
{
unitCode,
showMethod = "main",
- inputParameters
+ inputParameters,
+ modal = true
}
```
`unitCode` - обязательный, строка, код раздела Системы\
`showMethod` - необязательный, строка, метод вызова раздела Системы (если не указан - будет использован метод вызова "main")\
-`inputParameters` - необязательный, массив объектов вида `[{name: ИМЯ_ПАРАМЕТРА, value: ЗНАЧЕНИЕ_ПАРАМЕТРА},...]`, параметры метода вызова раздела Системы
+`inputParameters` - необязательный, массив объектов вида `[{name: ИМЯ_ПАРАМЕТРА, value: ЗНАЧЕНИЕ_ПАРАМЕТРА},...]`, параметры метода вызова раздела Системы\
+`modal` - необязательный, логический, true (по умолчанию) - открыть в модальном окне, false - открыть в закладке (если закладка ранее была открыта с таким же набором параметров - она будет активирована), поддерживается для "ПАРУС 8 Онлайн" с версии 26.12.2024
**Результат:** функция не возвращает значимого результата
@@ -705,14 +707,16 @@ const MyPanel = () => {
unitCode,
document,
showMethod = "main",
- inRnParameter = "in_RN"
+ inRnParameter = "in_RN",
+ modal = true
}
```
`unitCode` - обязательный, строка, код раздела Системы\
`document` - обязательный, число, регистрационный номер документа или иной его идентификатор
`showMethod` - необязательный, строка, метод вызова раздела Системы (если не указан - будет использован метод вызова "main")\
-`inRnParameter` - необязательный, строка, имя параметра метода вызова для позиционирования/отбора (если не указан, будет применён параметр метода вызова с именем "in_RN")
+`inRnParameter` - необязательный, строка, имя параметра метода вызова для позиционирования/отбора (если не указан, будет применён параметр метода вызова с именем "in_RN")\
+`modal` - необязательный, логический, true (по умолчанию) - открыть в модальном окне, false - открыть в закладке (если закладка ранее была открыта с таким же набором параметров - она будет активирована), поддерживается для "ПАРУС 8 Онлайн" с версии 26.12.2024
**Результат:** функция не возвращает значимого результата
@@ -1287,15 +1291,15 @@ const MyPanel = () => {
**Свойства**
-`columnsDef` - обязательный, массив, описание колонок таблицы, содержит объекты вида `{caption: <ЗАГОЛОВОК_КОЛОНКИ>, dataType: <ТИП_ДАННЫХ - NUMB|STR|DATE>, filter: <ПРИЗНАК_ВОЗМОЖНОСТИ_ОТБОРА - true|false>, hint: <ОПИСАНИЕ_КОЛОНКИ_МОЖЕТ_СОДЕРЖАТЬ_HTML_РАЗМЕТКУ>, name: <НАИМЕНОВАНИЕ_КОЛОНКИ>, order: <ПРИЗНАК_ВОЗМОЖНОСТИ_СОРТИРОВКИ - true|false>, values: <МАССИВ_ПРЕДОПРЕДЕЛЁННЫХ_ЗНАЧЕНИЙ>, visible: <ПРИЗНАК_ВИДИМОСТИ_КОЛОНКИ - true|false>,expandable: <ПРИЗНАК_РАЗВОРАЧИВАЕМОСТИ_ГРУППОВОГО_ЗАГОЛОВКА - true|false>, expanded: <ПРИЗНАК_РАЗВЕРНУТОСТИ_ГРУППОВОГО_ЗАГОЛОВКА - true|false>, parent: <НАИМЕНОВАНИЕ_РОДИТЕЛЬСКОЙ_КОЛОНКИ_В_ГРУППОВОМ_ЗАГОЛОВКЕ>, width: <ШИРИНА_КОЛОНКИ>}`\
+`columnsDef` - необязательный, массив, описание колонок таблицы, содержит объекты вида `{caption: <ЗАГОЛОВОК_КОЛОНКИ>, dataType: <ТИП_ДАННЫХ - NUMB|STR|DATE>, filter: <ПРИЗНАК_ВОЗМОЖНОСТИ_ОТБОРА - true|false>, hint: <ОПИСАНИЕ_КОЛОНКИ_МОЖЕТ_СОДЕРЖАТЬ_HTML_РАЗМЕТКУ>, name: <НАИМЕНОВАНИЕ_КОЛОНКИ>, order: <ПРИЗНАК_ВОЗМОЖНОСТИ_СОРТИРОВКИ - true|false>, values: <МАССИВ_ПРЕДОПРЕДЕЛЁННЫХ_ЗНАЧЕНИЙ>, visible: <ПРИЗНАК_ВИДИМОСТИ_КОЛОНКИ - true|false>,expandable: <ПРИЗНАК_РАЗВОРАЧИВАЕМОСТИ_ГРУППОВОГО_ЗАГОЛОВКА - true|false>, expanded: <ПРИЗНАК_РАЗВЕРНУТОСТИ_ГРУППОВОГО_ЗАГОЛОВКА - true|false>, parent: <НАИМЕНОВАНИЕ_РОДИТЕЛЬСКОЙ_КОЛОНКИ_В_ГРУППОВОМ_ЗАГОЛОВКЕ>, width: <ШИРИНА_КОЛОНКИ>}`\
`filtersInitial` - необязательныей, массив, начальное состояние фильтров таблицы, содержит объекты вида `{name: <НАИМЕНОВАНИЕ_КОЛОНКИ>, from: <НАЧАЛО_ДИАПАЗОНА_ЗНАЧЕНИЙ_ФИЛЬТРА>, to: <ОКОНЧАНИЕ_ДИАПАЗОНА_ЗНАЧЕНИЙ_ФИЛЬТРА>}`\
`groups` - необязательный, массив групп данных, содержит объекты вида `{name: <ИМЯ_ГРУППЫ>, caption: <ЗАГОЛОВОК_ГРУППЫ>, expandable: <ПРИЗНАК_РАЗВОРАЧИВАЕМОСТИ_ГРУППЫ - true|false>, expanded: <ПРИЗНАК_РАЗВЕРНУТОСТИ_ГРУППЫ - true|false>}`\
-`rows` - обязательный, массив, отображаемые таблицой строки данных, содержит объекты вида `{groupName: <ИМЯ_ГРУППЫ_СОДЕРЖАЩЕЙ_СТРОКУ>, <ИМЯ_КОЛОНКИ>: <ЗНАЧЕНИЕ>}`\
+`rows` - необязательный, массив, отображаемые таблицой строки данных, содержит объекты вида `{groupName: <ИМЯ_ГРУППЫ_СОДЕРЖАЩЕЙ_СТРОКУ>, <ИМЯ_КОЛОНКИ>: <ЗНАЧЕНИЕ>}`\
`size` - необязательный, строка, размер отступов при вёрстке таблицы, `small|medium` (см. константу `P8P_DATA_GRID_SIZE` в исходном коде компонента)\
-`fixedHeader` - необязательный, логический, признак фиксации заголовка таблицы\
-`fixedColumns` - необязательный, число, количество фиксированных колонок слева
-`morePages` - обязательный, логический, признак отображения кнопки догрузки данных\
-`reloading` - обязательный, логический, признак выполнения обновления данных таблицы (служит для корректной выдачи сообщения об отсуствии данных и корректного отображения "разворачивающихся" строк)\
+`fixedHeader` - необязательный, логический, признак фиксации заголовка таблицы, по умолчанию - `false`\
+`fixedColumns` - необязательный, число, количество фиксированных колонок слева, по умолчанию - 0\
+`morePages` - необязательный, логический, признак отображения кнопки догрузки данных, по умолчанию - `false`\
+`reloading` - необязательный, логический, признак выполнения обновления данных таблицы (служит для корректной выдачи сообщения об отсуствии данных и корректного отображения "разворачивающихся" строк), по умолчанию - `false`\
`expandable` - необязательный, логический, признак необходимости формирования "разворачивающихся" строк, по умолчанию - `false`\
`orderAscMenuItemCaption` - обязательный, строка, текст для пункта меню сортировки колонки по возрастанию\
`orderDescMenuItemCaption` - обязательный, строка, текст для пункта меню сортировки колонки по убыванию\
@@ -1341,18 +1345,18 @@ const MyPanel = () => {
Такие свойства как `columnsDef`, `groups`, `rows` компонента `P8PDataGrid` требуют от разработчика передачи данных в определённом формате. Это не обязательно должна быть информация из БД Системы, можно, например, просто объявить переменные в коде панели, задать им соответствующие значения и передать в компонент. Но изначально, таблица данных задумывалась для отображения сведений, полученных их учётных регистров Системы. Такие сведения, как правило, собираются хранимым объектом БД, исполняемым из панели посредством вызова `executeStored`. С целью снижения трудозатрат на приведение собранных хранимым объектом данных к форматам, потребляемым `P8PDataGrid`, реализован специальный API на стороне сервера БД.
Для таблицы данных это (см. детальные описания программных интерфейсов в пакете `PKG_P8PANELS_VISUAL`):
-`PKG_P8PANELS_VISUAL.TDATA_GRID_MAKE` - функция, инициализация таблицы данных, возвращает объект для хранения описания таблицы\
-`PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF` - процедура, добавление описания колонки в таблицу, принимает на вход объект с описанием таблицы и параметры, описывающие добавляемую колонку (её имя, заголовок, тип данных, видимость, доступность отбора и сортировки, набор предопределённых значений и т.д.)\
-`PKG_P8PANELS_VISUAL.TCOL_VALS_ADD` - процедура, служит для формирования коллекции предопределённых значений колонки таблицы (подготовленная коллекция передаётся в `RCOL_VALS` вызова `TDATA_GRID_ADD_COL_DEF`, если необходимо)\
-`PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_GROUP` - процедура, служит для добавления описания группы в таблицу данных, на вход принимает объект для хранения описания таблицы и параметры добавляемой группы\
-`PKG_P8PANELS_VISUAL.TROW_ADD_COL` - процедура, добавляет значение колонки к строке таблицы (значение указывается явно в `[S|N|D]VALUE`)\
-`PKG_P8PANELS_VISUAL.TROW_ADD_CUR_COL[S|N|D]` - процедура, добавляет значение колонки к строке таблицы (значение указывается через ссылку на номер колонки `NPOSITION` в курсоре `ICURSOR` динамического SQL)\
-`PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_ROW` - процедура, добавляет сформированную строку со значениями колонок в таблицу данных, на вход принимает объект для хранения описания таблицы и описание строки, сформированное вызовами `TROW_ADD_COL` и `TROW_ADD_CUR_COL[S|N|D]`, а так же год группы, в которую должна быть включена строка\
-`PKG_P8PANELS_VISUAL.TDATA_GRID_TO_XML` - функция, производит сериализацию объекта, описывающего таблицу данных, в специальный XML-формат, корректно интерпретируемый клиентским компонентом `P8PDataGrid` при передаче в WEB-приложение\
-`PKG_P8PANELS_VISUAL.TORDERS_FROM_XML` - функция, служит для десериализации (как правило, полученного от клиентского приложения) состояния сортировок в коллекцию формата `TORDERS`, на вход принимает `CLOB` с сериализованным состоянием сортировок таблицы в виде `BASE64(ИМЯASC|DESC...)` (клиентское приложение должно обеспечить передачу состояния сортировок в этом формате, см. пример ниже)\
-`PKG_P8PANELS_VISUAL.TORDERS_SET_QUERY` - процедура, вспомогательная утилита, производит в тексте SQL-запроса, поданного на вход, замену указанного шаблона на конструкцию `order by`, сформированную с учётом переданной коллекции `RORDERS`\
-`PKG_P8PANELS_VISUAL.TFILTERS_FROM_XML` - функция, служит для десериализации (как правило, полученного от клиентского приложения) состояния фильтров в коллекцию формата `TFILTERS`, на вход принимает `CLOB` с сериализованным состоянием фильтров таблицы в виде `BASE64(ИМЯЗНАЧЕНИЕЗНАЧЕНИЕ...)` (клиентское приложение должно обеспечить передачу состояния фильтров в этом формате, см. пример ниже)\
-`PKG_P8PANELS_VISUAL.TFILTERS_SET_QUERY` - процедура, вспомогательная утилита, производит вызов указанной серверной процедуры отбора с учётом переданных переменных окружения и значений в `RFILTERS`\
+`PKG_P8PANELS_VISUAL.TDG_MAKE` - функция, инициализация таблицы данных, возвращает объект для хранения описания таблицы\
+`PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF` - процедура, добавление описания колонки в таблицу, принимает на вход объект с описанием таблицы и параметры, описывающие добавляемую колонку (её имя, заголовок, тип данных, видимость, доступность отбора и сортировки, набор предопределённых значений и т.д.)\
+`PKG_P8PANELS_VISUAL.TDG_COL_VALS_ADD` - процедура, служит для формирования коллекции предопределённых значений колонки таблицы (подготовленная коллекция передаётся в `RCOL_VALS` вызова `TDG_ADD_COL_DEF`, если необходимо)\
+`PKG_P8PANELS_VISUAL.TDG_ADD_GROUP` - процедура, служит для добавления описания группы в таблицу данных, на вход принимает объект для хранения описания таблицы и параметры добавляемой группы\
+`PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL` - процедура, добавляет значение колонки к строке таблицы (значение указывается явно в `[S|N|D]VALUE`)\
+`PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COL[S|N|D]` - процедура, добавляет значение колонки к строке таблицы (значение указывается через ссылку на номер колонки `NPOSITION` в курсоре `ICURSOR` динамического SQL)\
+`PKG_P8PANELS_VISUAL.TDG_ADD_ROW` - процедура, добавляет сформированную строку со значениями колонок в таблицу данных, на вход принимает объект для хранения описания таблицы и описание строки, сформированное вызовами `TDG_ROW_ADD_COL` и `TDG_ROW_ADD_CUR_COL[S|N|D]`, а так же год группы, в которую должна быть включена строка\
+`PKG_P8PANELS_VISUAL.TDG_TO_XML` - функция, производит сериализацию объекта, описывающего таблицу данных, в специальный XML-формат, корректно интерпретируемый клиентским компонентом `P8PDataGrid` при передаче в WEB-приложение\
+`PKG_P8PANELS_VISUAL.TDG_ORDERS_FROM_XML` - функция, служит для десериализации (как правило, полученного от клиентского приложения) состояния сортировок в коллекцию формата `TORDERS`, на вход принимает `CLOB` с сериализованным состоянием сортировок таблицы в виде `BASE64(ИМЯASC|DESC...)` (клиентское приложение должно обеспечить передачу состояния сортировок в этом формате, см. пример ниже)\
+`PKG_P8PANELS_VISUAL.TDG_ORDERS_SET_QUERY` - процедура, вспомогательная утилита, производит в тексте SQL-запроса, поданного на вход, замену указанного шаблона на конструкцию `order by`, сформированную с учётом переданной коллекции `RORDERS`\
+`PKG_P8PANELS_VISUAL.TDG_FILTERS_FROM_XML` - функция, служит для десериализации (как правило, полученного от клиентского приложения) состояния фильтров в коллекцию формата `TDG_FILTERS`, на вход принимает `CLOB` с сериализованным состоянием фильтров таблицы в виде `BASE64(ИМЯЗНАЧЕНИЕЗНАЧЕНИЕ...)` (клиентское приложение должно обеспечить передачу состояния фильтров в этом формате, см. пример ниже)\
+`PKG_P8PANELS_VISUAL.TDG_FILTERS_SET_QUERY` - процедура, вспомогательная утилита, производит вызов указанной серверной процедуры отбора с учётом переданных переменных окружения и значений в `RFILTERS`\
`PKG_P8PANELS_VISUAL.UTL_ROWS_LIMITS_CALC` - процедура, вспомогательная утилита, служит для конвертации номера страницы данных и размера страницы данных в границы диапазона строк выборки (как правило, клиентскому приложению удобнее прислать на сервер текущий номер страницы и её размер, в то время к в запросах, для выборки, удобнее применять границы диапазонов строк)
**Пример**
@@ -1372,11 +1376,11 @@ const MyPanel = () => {
is
NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Организация сеанса
NIDENT PKG_STD.TREF := GEN_IDENT(); -- Идентификатор отбора
- RF PKG_P8PANELS_VISUAL.TFILTERS; -- Фильтры
- RO PKG_P8PANELS_VISUAL.TORDERS; -- Сортировки
- RDG PKG_P8PANELS_VISUAL.TDATA_GRID; -- Описание таблицы
- RAGN_TYPES PKG_P8PANELS_VISUAL.TCOL_VALS; -- Предопределенные значения "Типа контрагентов"
- RDG_ROW PKG_P8PANELS_VISUAL.TROW; -- Строка таблицы
+ RF PKG_P8PANELS_VISUAL.TDG_FILTERS; -- Фильтры
+ RO PKG_P8PANELS_VISUAL.TDG_ORDERS; -- Сортировки
+ RDG PKG_P8PANELS_VISUAL.TDG; -- Описание таблицы
+ RAGN_TYPES PKG_P8PANELS_VISUAL.TDG_COL_VALS; -- Предопределенные значения "Типа контрагентов"
+ RDG_ROW PKG_P8PANELS_VISUAL.TDG_ROW; -- Строка таблицы
NROW_FROM PKG_STD.TREF; -- Номер строки с
NROW_TO PKG_STD.TREF; -- Номер строки по
CSQL clob; -- Буфер для запроса
@@ -1387,71 +1391,71 @@ const MyPanel = () => {
NAGNTYPE PKG_STD.TREF; -- Буфер для "Типа"
begin
/* Читаем фильтры */
- RF := PKG_P8PANELS_VISUAL.TFILTERS_FROM_XML(CFILTERS => CFILTERS);
+ RF := PKG_P8PANELS_VISUAL.TDG_FILTERS_FROM_XML(CFILTERS => CFILTERS);
/* Читем сортировки */
- RO := PKG_P8PANELS_VISUAL.TORDERS_FROM_XML(CORDERS => CORDERS);
+ RO := PKG_P8PANELS_VISUAL.TDG_ORDERS_FROM_XML(CORDERS => CORDERS);
/* Преобразуем номер и размер страницы в номер строк с и по */
PKG_P8PANELS_VISUAL.UTL_ROWS_LIMITS_CALC(NPAGE_NUMBER => NPAGE_NUMBER,
NPAGE_SIZE => NPAGE_SIZE,
NROW_FROM => NROW_FROM,
NROW_TO => NROW_TO);
/* Инициализируем таблицу данных */
- RDG := PKG_P8PANELS_VISUAL.TDATA_GRID_MAKE(BFIXED_HEADER => true, NFIXED_COLUMNS => 2);
+ RDG := PKG_P8PANELS_VISUAL.TDG_MAKE(BFIXED_HEADER => true, NFIXED_COLUMNS => 2);
/* Описываем колонки таблицы данных */
- PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
- SNAME => 'SAGNABBR',
- SCAPTION => 'Мнемокод',
- SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
- SCOND_FROM => 'AgentAbbr',
- BVISIBLE => true,
- BORDER => true,
- BFILTER => true,
- NWIDTH => 150);
- PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
- SNAME => 'SAGNINFO',
- SCAPTION => 'Сведения',
- SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
- BVISIBLE => true,
- BORDER => false,
- BFILTER => false,
- BEXPANDABLE => true,
- NWIDTH => 300);
- PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
- SNAME => 'SAGNNAME',
- SCAPTION => 'Наименование',
- SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
- SCOND_FROM => 'AgentName',
- BVISIBLE => true,
- BORDER => true,
- BFILTER => true,
- SPARENT => 'SAGNINFO',
- NWIDTH => 200);
- PKG_P8PANELS_VISUAL.TCOL_VALS_ADD(RCOL_VALS => RAGN_TYPES, NVALUE => 0);
- PKG_P8PANELS_VISUAL.TCOL_VALS_ADD(RCOL_VALS => RAGN_TYPES, NVALUE => 1);
- PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
- SNAME => 'NAGNTYPE',
- SCAPTION => 'Тип',
- SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
- SCOND_FROM => 'AgentType',
- BVISIBLE => true,
- BORDER => true,
- BFILTER => true,
- SPARENT => 'SAGNINFO',
- NWIDTH => 100,
- RCOL_VALS => RAGN_TYPES,
- SHINT => 'В Системе бывают контрагенты двух типов: ' ||
- 'Юридическое лицо - организация, которая имеет в собственности, хозяйственном ведении ' ||
- 'или оперативном управлении обособленное имущество, отвечает по своим обязательствам этим имуществом, может от своего ' ||
- 'имени приобретать и осуществлять имущественные и личные неимущественные права, отвечать по своим обязанностям. ' ||
- 'Физическое лицо - субъект правовых отношений, представляющий собой одного человека.');
- PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
- SNAME => 'SFULLNAME',
- SCAPTION => 'Полное наименование',
- SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR);
- PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
- SNAME => 'SAGNIDNUMB',
- SCAPTION => 'ИНН',
- SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'SAGNABBR',
+ SCAPTION => 'Мнемокод',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
+ SCOND_FROM => 'AgentAbbr',
+ BVISIBLE => true,
+ BORDER => true,
+ BFILTER => true,
+ NWIDTH => 150);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'SAGNINFO',
+ SCAPTION => 'Сведения',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
+ BVISIBLE => true,
+ BORDER => false,
+ BFILTER => false,
+ BEXPANDABLE => true,
+ NWIDTH => 300);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'SAGNNAME',
+ SCAPTION => 'Наименование',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
+ SCOND_FROM => 'AgentName',
+ BVISIBLE => true,
+ BORDER => true,
+ BFILTER => true,
+ SPARENT => 'SAGNINFO',
+ NWIDTH => 200);
+ PKG_P8PANELS_VISUAL.TDG_COL_VALS_ADD(RCOL_VALS => RAGN_TYPES, NVALUE => 0);
+ PKG_P8PANELS_VISUAL.TDG_COL_VALS_ADD(RCOL_VALS => RAGN_TYPES, NVALUE => 1);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'NAGNTYPE',
+ SCAPTION => 'Тип',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
+ SCOND_FROM => 'AgentType',
+ BVISIBLE => true,
+ BORDER => true,
+ BFILTER => true,
+ SPARENT => 'SAGNINFO',
+ NWIDTH => 100,
+ RCOL_VALS => RAGN_TYPES,
+ SHINT => 'В Системе бывают контрагенты двух типов: ' ||
+ 'Юридическое лицо - организация, которая имеет в собственности, хозяйственном ведении ' ||
+ 'или оперативном управлении обособленное имущество, отвечает по своим обязательствам этим имуществом, может от своего ' ||
+ 'имени приобретать и осуществлять имущественные и личные неимущественные права, отвечать по своим обязанностям. ' ||
+ 'Физическое лицо - субъект правовых отношений, представляющий собой одного человека.');
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'SFULLNAME',
+ SCAPTION => 'Полное наименование',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR);
+ PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
+ SNAME => 'SAGNIDNUMB',
+ SCAPTION => 'ИНН',
+ SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR);
/* Обходим данные */
begin
/* Добавляем подсказку совместимости */
@@ -1477,14 +1481,17 @@ const MyPanel = () => {
PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and AG.RN in (select ID from COND_BROKER_IDSMART where IDENT = :NIDENT) %ORDER_BY%) D) F');
PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' where F.NROW between :NROW_FROM and :NROW_TO');
/* Учтём сортировки */
- PKG_P8PANELS_VISUAL.TORDERS_SET_QUERY(RDATA_GRID => RDG, RORDERS => RO, SPATTERN => '%ORDER_BY%', CSQL => CSQL);
+ PKG_P8PANELS_VISUAL.TDG_ORDERS_SET_QUERY(RDATA_GRID => RDG,
+ RORDERS => RO,
+ SPATTERN => '%ORDER_BY%',
+ CSQL => CSQL);
/* Учтём фильтры */
- PKG_P8PANELS_VISUAL.TFILTERS_SET_QUERY(NIDENT => NIDENT,
- NCOMPANY => NCOMPANY,
- SUNIT => 'AGNLIST',
- SPROCEDURE => 'P_AGNLIST_BASE_COND',
- RDATA_GRID => RDG,
- RFILTERS => RF);
+ PKG_P8PANELS_VISUAL.TDG_FILTERS_SET_QUERY(NIDENT => NIDENT,
+ NCOMPANY => NCOMPANY,
+ SUNIT => 'AGNLIST',
+ SPROCEDURE => 'P_AGNLIST_BASE_COND',
+ RDATA_GRID => RDG,
+ RFILTERS => RF);
/* Разбираем его */
ICURSOR := PKG_SQL_DML.OPEN_CURSOR(SWHAT => 'SELECT');
PKG_SQL_DML.PARSE(ICURSOR => ICURSOR, SQUERY => CSQL);
@@ -1512,29 +1519,38 @@ const MyPanel = () => {
if (NAGNTYPE = 0) then
SGROUP := 'JUR';
SAGNINFO := SAGNNAME || ', ЮЛ';
- PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_GROUP(RDATA_GRID => RDG,
- SNAME => SGROUP,
- SCAPTION => 'Юридические лица',
- BEXPANDABLE => true,
- BEXPANDED => false);
+ PKG_P8PANELS_VISUAL.TDG_ADD_GROUP(RDATA_GRID => RDG,
+ SNAME => SGROUP,
+ SCAPTION => 'Юридические лица',
+ BEXPANDABLE => true,
+ BEXPANDED => false);
else
SGROUP := 'PERS';
SAGNINFO := SAGNNAME || ', ФЛ';
- PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_GROUP(RDATA_GRID => RDG,
- SNAME => SGROUP,
- SCAPTION => 'Физические лица',
- BEXPANDABLE => true,
- BEXPANDED => false);
+ PKG_P8PANELS_VISUAL.TDG_ADD_GROUP(RDATA_GRID => RDG,
+ SNAME => SGROUP,
+ SCAPTION => 'Физические лица',
+ BEXPANDABLE => true,
+ BEXPANDED => false);
end if;
- RDG_ROW := PKG_P8PANELS_VISUAL.TROW_MAKE(SGROUP => SGROUP);
- PKG_P8PANELS_VISUAL.TROW_ADD_CUR_COLS(RROW => RDG_ROW, SNAME => 'SAGNABBR', ICURSOR => ICURSOR, NPOSITION => 1);
- PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SAGNINFO', SVALUE => SAGNINFO);
- PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SAGNNAME', SVALUE => SAGNNAME);
- PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NAGNTYPE', NVALUE => NAGNTYPE);
- PKG_P8PANELS_VISUAL.TROW_ADD_CUR_COLS(RROW => RDG_ROW, SNAME => 'SFULLNAME', ICURSOR => ICURSOR, NPOSITION => 4);
- PKG_P8PANELS_VISUAL.TROW_ADD_CUR_COLS(RROW => RDG_ROW, SNAME => 'SAGNIDNUMB', ICURSOR => ICURSOR, NPOSITION => 5);
+ RDG_ROW := PKG_P8PANELS_VISUAL.TDG_ROW_MAKE(SGROUP => SGROUP);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLS(RROW => RDG_ROW,
+ SNAME => 'SAGNABBR',
+ ICURSOR => ICURSOR,
+ NPOSITION => 1);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SAGNINFO', SVALUE => SAGNINFO);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SAGNNAME', SVALUE => SAGNNAME);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NAGNTYPE', NVALUE => NAGNTYPE);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLS(RROW => RDG_ROW,
+ SNAME => 'SFULLNAME',
+ ICURSOR => ICURSOR,
+ NPOSITION => 4);
+ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLS(RROW => RDG_ROW,
+ SNAME => 'SAGNIDNUMB',
+ ICURSOR => ICURSOR,
+ NPOSITION => 5);
/* Добавляем строку в таблицу */
- PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW);
+ PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW);
end loop;
/* Освобождаем курсор */
PKG_SQL_DML.CLOSE_CURSOR(ICURSOR => ICURSOR);
@@ -1544,7 +1560,7 @@ const MyPanel = () => {
raise;
end;
/* Сериализуем описание */
- COUT := PKG_P8PANELS_VISUAL.TDATA_GRID_TO_XML(RDATA_GRID => RDG, NINCLUDE_DEF => NINCLUDE_DEF);
+ COUT := PKG_P8PANELS_VISUAL.TDG_TO_XML(RDATA_GRID => RDG, NINCLUDE_DEF => NINCLUDE_DEF);
end DATA_GRID;
```
@@ -1617,18 +1633,14 @@ export const groupCellRender = () => ({ cellStyle: { padding: "2px" } });
//Пример: Таблица данных "P8PDataGrid"
const DataGrid = ({ title }) => {
//Собственное состояние - таблица данных
- const [dataGrid, setdataGrid] = useState({
+ const [dataGrid, setDataGrid] = useState({
dataLoaded: false,
- columnsDef: [],
filters: null,
orders: null,
- groups: [],
- rows: [],
- reload: true,
pageNumber: 1,
morePages: true,
- fixedHeader: false,
- fixedColumns: 0
+ expandable: true,
+ reloading: true
});
//Подключение к контексту взаимодействия с сервером
@@ -1639,7 +1651,7 @@ const DataGrid = ({ title }) => {
//Загрузка данных таблицы с сервера
const loadData = useCallback(async () => {
- if (dataGrid.reload) {
+ if (dataGrid.reloading) {
const data = await executeStored({
stored: "PKG_P8PANELS_SAMPLES.DATA_GRID",
args: {
@@ -1651,32 +1663,31 @@ const DataGrid = ({ title }) => {
},
respArg: "COUT"
});
- setdataGrid(pv => ({
+ setDataGrid(pv => ({
...pv,
- fixedHeader: data.XDATA_GRID.fixedHeader,
- fixedColumns: data.XDATA_GRID.fixedColumns,
- columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
- rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
- groups: data.XGROUPS
+ ...data.XDATA_GRID,
+ columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef || [],
+ rows: pv.pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...(pv.rows || []), ...(data.XDATA_GRID.rows || [])],
+ groups: data.XDATA_GRID.groups
? pv.pageNumber == 1
- ? [...data.XGROUPS]
- : [...pv.groups, ...data.XGROUPS.filter(g => !pv.groups.find(pg => pg.name == g.name))]
- : [...pv.groups],
+ ? [...data.XDATA_GRID.groups]
+ : [...(pv.groups || []), ...data.XDATA_GRID.groups.filter(g => !pv.groups.find(pg => pg.name == g.name))]
+ : [...(pv.groups || [])],
dataLoaded: true,
- reload: false,
- morePages: (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE
+ reloading: false,
+ morePages: (data.XDATA_GRID.rows || []).length >= DATA_GRID_PAGE_SIZE
}));
}
- }, [dataGrid.reload, dataGrid.filters, dataGrid.orders, dataGrid.dataLoaded, dataGrid.pageNumber, executeStored, SERV_DATA_TYPE_CLOB]);
+ }, [dataGrid.reloading, dataGrid.filters, dataGrid.orders, dataGrid.dataLoaded, dataGrid.pageNumber, executeStored, SERV_DATA_TYPE_CLOB]);
//При изменении состояния фильтра
- const handleFilterChanged = ({ filters }) => setdataGrid(pv => ({ ...pv, filters: [...filters], pageNumber: 1, reload: true }));
+ const handleFilterChanged = ({ filters }) => setDataGrid(pv => ({ ...pv, filters: [...filters], pageNumber: 1, reloading: true }));
//При изменении состояния сортировки
- const handleOrderChanged = ({ orders }) => setdataGrid(pv => ({ ...pv, orders: [...orders], pageNumber: 1, reload: true }));
+ const handleOrderChanged = ({ orders }) => setDataGrid(pv => ({ ...pv, orders: [...orders], pageNumber: 1, reloading: true }));
//При изменении количества отображаемых страниц
- const handlePagesCountChanged = () => setdataGrid(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reload: true }));
+ const handlePagesCountChanged = () => setDataGrid(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reloading: true }));
//При нажатии на копку контрагента
const handleAgnButtonClicked = agnCode => pOnlineShowDocument({ unitCode: "AGNLIST", document: agnCode, inRnParameter: "in_AGNABBR" });
@@ -1684,7 +1695,7 @@ const DataGrid = ({ title }) => {
//При необходимости обновить данные таблицы
useEffect(() => {
loadData();
- }, [dataGrid.reload, loadData]);
+ }, [dataGrid.reloading, loadData]);
//Генерация содержимого
return (
@@ -1698,16 +1709,9 @@ const DataGrid = ({ title }) => {
{dataGrid.dataLoaded ? (
{
onOrderChanged={handleOrderChanged}
onFilterChanged={handleFilterChanged}
onPagesCountChanged={handlePagesCountChanged}
- expandable={true}
rowExpandRender={({ row }) => (
)}
@@ -1759,7 +1762,7 @@ const MyPanel = () => {
`title` - необязательный, строка, заголовок графика, если не указано - заголовок не отображается\
`legendPosition` - необязательный, строка, расположение легенды, может принимать значения `left|right|top|bottom`, если не указано - легенда не отображается\
`options` - необязательный, объект, дополнительные параметры графика, формат и допустимый состав атрибутов определены в документации к библиотеке [ChartJS](https://www.chartjs.org/docs/latest/), будет объединён с параметрами графика уже зафиксированными в компоненте `P8PChart` (см. `useEffect` при подключении компонента к старице в его исходном коде, параметры графика, зафиксированные в компоненте, имеют более высокий приоритет по сравнению с данным свойством)
-`labels` - обязательный, массив строк, список меток для значений графика\
+`labels` - необязательный, массив строк, список меток для значений графика\
`datasets` - необязательный, массив объектов, данные для отображения на диаграмме, каждый элемент массива - серия данных для отображения, содержит объекты вида `{label: <ЗАГОЛОВОК_СЕРИИ>, borderColor: <ЦВЕТ_ГРАНИЦЫ_СЕРИИ_НА_ГРАФИКЕ>, backgroundColor: <ЦВЕТ_ЗАЛИВКИ_СЕРИИ_НА_ГРАФИКЕ>, data: <МАССИВ_ЗНАЧЕНИЙ_СЕРИИ_ДАННЫХ>, items: <МАССИВ_ОБЪЕКТОВ_ПРОИЗВОЛЬНОЙ_СТРУКТУРЫ_ДЛЯ_ОПИСАНИЯ_СЕРИИ_ДАННЫХ>}`\
`onClick` - необязательный, функция, будет вызвана при нажатии на элемент графика, сигнатура функции `f({datasetIndex, itemIndex, item})`, результат функции не интерпретируется. Функции будет передан объект, поле `datasetIndex` которого, будет содержать индекс серии данных, `itemIndex` - индекс элемента серии данных, а `item` - описание элмента данных серии, на котором было зафиксировано нажатие.\
`style` - необязательный, объект, стили, которые будут применены к контейнеру `div` графика
@@ -1851,7 +1854,7 @@ const STYLES = {
//Пример: Графики "P8PChart"
const Chart = ({ title }) => {
//Собственное состояние - график
- const [chart, setChart] = useState({ loaded: false, labels: [], datasets: [] });
+ const [chart, setChart] = useState({ loaded: false });
//Подключение к контексту взаимодействия с сервером
const { executeStored } = useContext(BackEndСtx);
@@ -2126,12 +2129,10 @@ const taskDialogRenderer = ({ task, close }) => {
//Пример: Диаграмма Ганта "P8Gantt"
const Gantt = ({ title }) => {
//Собственное состояние
- const [state, setState] = useState({
+ const [gantt, setGantt] = useState({
init: false,
dataLoaded: false,
ident: null,
- ganttDef: {},
- ganttTasks: [],
useCustomTaskDialog: false
});
@@ -2142,21 +2143,21 @@ const Gantt = ({ title }) => {
const loadData = useCallback(async () => {
const data = await executeStored({
stored: "PKG_P8PANELS_SAMPLES.GANTT",
- args: { NIDENT: state.ident },
+ args: { NIDENT: gantt.ident },
attributeValueProcessor: (name, val) =>
name == "numb" ? undefined : ["start", "end"].includes(name) ? formatDateJSONDateOnly(val) : val,
respArg: "COUT"
});
- setState(pv => ({ ...pv, dataLoaded: true, ganttDef: { ...data.XGANTT_DEF }, ganttTasks: [...data.XGANTT_TASKS] }));
- }, [state.ident, executeStored]);
+ setGantt(pv => ({ ...pv, dataLoaded: true, ...data.XGANTT }));
+ }, [gantt.ident, executeStored]);
//Инициализация данных диаграммы
const initData = useCallback(async () => {
- if (!state.init) {
- const data = await executeStored({ stored: "PKG_P8PANELS_SAMPLES.GANTT_INIT", args: { NIDENT: state.ident } });
- setState(pv => ({ ...pv, init: true, ident: data.NIDENT }));
+ if (!gantt.init) {
+ const data = await executeStored({ stored: "PKG_P8PANELS_SAMPLES.GANTT_INIT", args: { NIDENT: gantt.ident } });
+ setGantt(pv => ({ ...pv, init: true, ident: data.NIDENT }));
}
- }, [state.init, state.ident, executeStored]);
+ }, [gantt.init, gantt.ident, executeStored]);
//Изменение данных диаграммы
const modifyData = useCallback(
@@ -2164,13 +2165,13 @@ const Gantt = ({ title }) => {
try {
await executeStored({
stored: "PKG_P8PANELS_SAMPLES.GANTT_MODIFY",
- args: { NIDENT: state.ident, NRN: rn, DDATE_FROM: new Date(start), DDATE_TO: new Date(end) }
+ args: { NIDENT: gantt.ident, NRN: rn, DDATE_FROM: new Date(start), DDATE_TO: new Date(end) }
});
} finally {
loadData();
}
},
- [state.ident, executeStored, loadData]
+ [gantt.ident, executeStored, loadData]
);
//Обработка измненения сроков задачи в диаграмме Гантта
@@ -2180,8 +2181,8 @@ const Gantt = ({ title }) => {
//При необходимости обновить данные таблицы
useEffect(() => {
- if (state.ident) loadData();
- }, [state.ident, loadData]);
+ if (gantt.ident) loadData();
+ }, [gantt.ident, loadData]);
//При подключении компонента к странице
useEffect(() => {
@@ -2196,23 +2197,21 @@ const Gantt = ({ title }) => {
{title}
setState(pv => ({ ...pv, useCustomTaskDialog: !pv.useCustomTaskDialog }))} />}
+ sx={STYLES.CONTROL}
+ control={ setGantt(pv => ({ ...pv, useCustomTaskDialog: !pv.useCustomTaskDialog }))} />}
label="Отображать пользовательский диалог задачи"
/>
-
+
- {state.dataLoaded ? (
-
-
-
+ {gantt.dataLoaded ? (
+
) : null}
@@ -2366,6 +2365,503 @@ const Svg = ({ title }) => {
Полные актуальные исходные коды примера можно увидеть в "app/panels/samples/svg.js" данного репозитория соответственно.
+##### Циклограмма "P8PCyclogram"
+
+Компонент предназначен для отображения данных в виде циклограммы. Поддерживается:
+
+- Группировка задач с отображением описания группы при наведении
+- Форматирование цвета заливки задачи, текста задачи и цвета при наведении на задачу/группу
+- Дополнение задачи произвольными учётными атрибутами
+- Диалоговый редактор задачи, отображающий её дополнительные атрибуты с возможностью настройки их форматирования
+- Отображение произвольного пользовательского диалога в качестве карточки задачи/редактора задачи
+- Масштабирование визуального представления
+
+
+
+
+**Подключение**
+
+Клиентская часть циклограммы реализована в компоненте `P8PCyclogram`, объявленном в "app/components/p8p_cyclogram". Для использования компонента на панели его необходимо импортировать:
+
+```
+import { P8PCyclogram } from "../../components/p8p_cyclogram";
+
+const MyPanel = () => {
+ return (
+
+
+
+ );
+}
+```
+
+**Свойства**
+
+`containerStyle` - необязательный, объект, стили, которые будут применены к компонету `div`, являющемуся контейнером циклограммы\
+`lineHeight` - необязательный, число, высота строки в пикселях (по умолчанию 20)\
+`title` - необязательный, строка, заголовок циклограммы (если не указан - не отображается)\
+`titleStyle` - необязательный, объект, стили, которые будут применены к компонету `Typography` заголовка циклограммы\
+`onTitleClick` - необязательный, функция, будет вызвана при нажатии пользователем на заголовок (если указана - заголовок формируется в виде гиперссылки), сигнатура функции `f()`, результат функции не интерпретируется\
+`zoomBar` - необязательный, логический, признак отображения панели управления масштабом (по умолчанию - не отображается)\
+`zoom` - необязательный, число, масштаб циклограммы\
+`columns` - обязательный, массив, колонки, отображаемые на циклограмме, должен состоять из объектов вида `{name: <НАИМЕНОВАНИЕ>, start: <ПОЗИЦИЯ_НАЧАЛА_КОЛОНКИ>, end: <ПОЗИЦИЯ_ОКОНЧАНИЯ_КОЛОНКИ>}` (см. константу `P8P_CYCLOGRAM_COLUMN_SHAPE` в коде компонента)\
+`columnRenderer` - необязательный, функция формирования представления колонки (если не указана - отображение по умолчанию). Сигнатура функции: `f({column})`. Будет вызвана для каждой колонки циклограммы, в функцию будет передан объект, в поле `column` которого будет содержаться описание текущей генерируемой колонки (элемент массива `columns`, см. выше описание полей). Должна возвращать значение или React-компонент.\
+`groups` - необязательный, массив, группы задач, которые отображаются на циклограмме, должен состоять из объектов вида `{name: <НАИМЕНОВАНИЕ>, height: <ВЫСОТА_ОТОБРАЖЕНИЯ_ГРУППЫ>, width: <ШИРИНА_ОТОБРАЖЕНИЯ_ГРУППЫ>, visible: <ПРИЗНАК_ОТОБРАЖЕНИЯ_ГРУППЫ>}` (см. константу `P8P_CYCLOGRAM_GROUP_SHAPE` в коде компонента). Группа отображается только при наведении на соответствующую задачу, которая относится к данной группе.\
+`groupHeaderRenderer` - необязательный, функция формирования представления всплывающей информации о группе (если не указана - отображение по умолчанию). Сигнатура функции: `f({group})`. Будет вызвана для каждой группы циклограммы, в функцию будет передан объект, в поле `group` которого будет содержаться описание текущей генерируемой группы (элемент массив `groups`, см. выше описание полей). Должна возвращать значение или React-компонент.\
+`tasks` - обязательный, массив, задачи, отображаемые на циклограмме, должен состоять из объектов вида `{id: <УНИКАЛЬНЫЙ_ИДЕНТИФИКАТОР>, rn: <ССЫЛКА_НА_ЗАПИСЬ_В_СИСТЕМЕ>, name: <НАИМЕНОВАНИЕ>, fullName: <ПОЛНОЕ_НАИМЕНОВАНИЕ>, lineNumb: <НОМЕР_СТРОКИ_ЗАДАЧИ>, start: <ПОЗИЦИЯ_НАЧАЛА_ЗАДАЧИ>, end: <ПОЗИЦИЯ_ОКОНЧАНИЯ_ЗАДАЧИ>, group: <НАИМЕНОВАНИЕ_ГРУППЫ>, bgColor: <ЦВЕТ_ЗАЛИВКИ>, textColor: <ЦВЕТ_ТЕКСТА>, highlightColor: <ЦВЕТ_НАВЕДЕНИЯ>, [<ИМЯ_ДОПОЛНИТЕЛЬНОГО_АТРИБУТА1>:<ЗНАЧЕНИЕ1>, <ИМЯ_ДОПОЛНИТЕЛЬНОГО_АТРИБУТА2>:<ЗНАЧЕНИЕ2>,...]}` (см. константу `P8P_CYCLOGRAM_TASK_SHAPE` в коде компонента).\
+`taskRenderer` - необязательный, функция формирования представления задачи на циклограмме (если не указана - отображение по умолчанию). Сигнатура функции: `f({task, taskHeight, taskWidth})`. Будет вызвана для каждой задачи циклограммы, в функцию будет передан объект, в поле `task` которого будет содержаться описание текущей генерируемой задаче (элемент массив `tasks`, см. выше описание полей), в поле `taskHeight` описание высоты задачи, в поле `taskWidth` описание ширины задачи. Должна возвращать объект вида `{taskStyle: <СТИЛИ_ДЛЯ_ЭЛЕМЕНТА_ЗАДАЧИ>, taskProps: <СВОЙСТВА_ДЛЯ_ЭЛЕМЕНТА_ЗАДАЧИ>, data: <ЗНАЧЕНИЕ_ИЛИ_КОМПОНЕНТ_React_ДЛЯ_СОДЕРЖИМОГО_ЭЛЕМЕНТА_ЗАДАЧИ>}` или `undefined`, если для задачи не предполагается специального представления.\
+`taskAttributes` - необязательный, массив, состав (не значения) дополнительных атрибутутов задач, должен состоять из объектов вида `{name: <ИМЯ_ДОПОЛНИТЕЛЬНОГО_АТРИБУТА>, caption: <ЗАГОЛОВОК_ДОПОЛНИТЕЛЬНОГО_АТРИБУТА>, visible: <ПРИЗНАК_ОТОБРАЖЕНИЯ_ДОПОЛНИТЕЛЬНОГО_АТРИБУТА - true|false>}` (см. константу `P8P_CYCLOGRAM_TASK_ATTRIBUTE_SHAPE` в коде компонента)\
+`taskAttributeRenderer` - необязательный, функция, если указана - будет вызвана при отображении диалога редактора здачи, результат функции будет применён для отображения области дополнительных атрибутов задачи в диалоге редактора, если не указана - дополнительные атрибуты будут отображены с форматированием по умолчанию. Сигнатура функции - `f({task, attribute})`, в функцию будет передан объект в поле `task`, которого, будет содержаться описание задачи для которой отображается редактор (элемент массива `tasks`, см. выше описание полей), в поле `attribute` - описание дополнительного атрибута формируемого в диалоге редактора (элемент массива `taskAttributes`, см. выше описание полей). Должна возвращать значение или React-компонент.\
+`taskDialogRenderer` - необязательный, функция, если указана - будет вызвана до отображения диалога редактора задачи. Результат функции будет показан в качестве содержимого диалога редактора, вместо типовой формы. Сигнатура функции - `f({task, taskAttributes, close})`, в функцию будет передан объект в поле `task`, которого, будет содержаться описание задачи для которой отображается редактор (элемент массива `tasks`, см. выше описание полей), в поле `taskAttributes` - массив `taskAttributes` (см. выше описание полей), описывающий состав полей задачи, в поле `close` - функция закрытия диалога задачи, может быть вызвана возвращаемым Reac-компонентом для сокрытия диалога. Должна возвращать значение или React-компонент.\
+`noDataFoundText` - обязательный, строка, текст для отображения ошибки об отсутствии данных\
+`nameTaskEditorCaption` - обязательный, строка, подпись стандартного атрибута `name` в диалоге редактора задачи\
+`okTaskEditorBtnCaption` - обязательный, строка, подпись кнопки "ОК" диалога редактора задачи\
+`cancelTaskEditorBtnCaption` - обязательный, строка, подпись кнопки "ОТМЕНА" диалога редактора задачи
+
+Некоторые параметры циклограммы вынесены в свойства компонента `P8PCyclogram` для минимизации его связи с фреймворком и поддержания возможности стороннего использования (например, свойства `noDataFoundText`, `okTaskEditorBtnCaption`, `cancelTaskEditorBtnCaption` и т.п.) . Тем не менее, в настройках фреймворка и его окружении уже есть реализации для данных свойств. Например, в "app.text.js" уже содержатся объявления типовых констант для текстов подписей кнопок и пунктов меню. Поэтому, в "app/config_wrapper.js" для привязки свойств `P8PCyclogram` к контексту фреймворка реализованы специальные декораторы и объекты-шаблоны, облегчающие подключение экземпляра `P8PCyclogram` к панели и снимающие с разработчика необходимость указывать некоторые из перечисленных выше обязательных свойств. В предложенном ниже примере, из модуля "config_wrapper" в панель импортируется объект `P8P_CYCLOGRAM_CONFIG_PROPS`, который уже содержт преднастроенное описание свойств `noDataFoundText`,`nameTaskEditorCaption`, `okTaskEditorBtnCaption` и `cancelTaskEditorBtnCaption`, полученное из окружения фреймворка. Таким образом, прикладной разработчик может не указывать их значения при использовании `P8PCyclogram` (если по каким-то причинам не хочет их переопределить, конечно).
+
+```
+import { P8PCyclogram } from "../../components/p8p_cyclogram";
+import { P8P_CYCLOGRAM_CONFIG_PROPS } from "../../config_wrapper";
+
+const MyPanel = () => {
+ return (
+
+
+
+ );
+}
+```
+
+**API на сервере БД**
+
+Компонент `P8PCyclogram` требует от разработчика передачи данных в определённом формате. С целью снижения трудозатрат на приведение собранных хранимым объектом данных Системы к форматам, потребляемым `P8PCyclogram`, реализован специальный API на стороне сервера БД.
+
+Для циклограммы это (см. детальные описания программных интерфейсов в пакете `PKG_P8PANELS_VISUAL`):
+`PKG_P8PANELS_VISUAL.TCYCLOGRAM_MAKE` - функция, инициализация циклограммы, возвращает объект для хранения её описания\
+`PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_TASK_ATTR` - процедура, добавляет, к указанному объекту описания циклограммы, описатель дополнительного атрибута задачи\
+`PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_MAKE` - функция, инициализирует и возвращает объект для описания задачи в циклограмме (поставщик данных для `TCYCLOGRAM_ADD_TASK`)\
+`PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL` - процедура, добавляет, к указанному объекту описания задачи, значение дополнительного атрибута\
+`PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_COLUMN` - процедура, добавляет, к указанному объекту описания циклограммы, новую колонку\
+`PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_GROUP` - процедура, добавляет, к указанному объекту описания циклограммы, новую группу\
+`PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_TASK` - процедура, добавляет, к указанному объекту описания циклограммы, новую задачу, ранее описанную через `TCYCLOGRAM_TASK_MAKE`\
+`PKG_P8PANELS_VISUAL.TCYCLOGRAM_TO_XML` - функция, производит сериализацию объекта, описывающего циклограмму, в специальный XML-формат, корректно интерпретируемый клиентским компонентом `P8PCyclogram` при передаче в WEB-приложение
+
+**Пример**
+
+Код на стороне сервера БД (хранимая процедура в клиентском пакете `PKG_P8PANELS_SAMPLES`, требует наличия таблицы `P8PNL_SMPL_CYCLOGRAM`, см. "db/P8PNL_SMPL_CYCLOGRAM.sql"):
+
+```
+ procedure CYCLOGRAM
+ (
+ NIDENT in number, -- Идентификатор процесса
+ COUT out clob -- Сериализованные данные для циклограммы
+ )
+ is
+ CG PKG_P8PANELS_VISUAL.TCYCLOGRAM; -- Описание циклограммы
+ RTASK PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK; -- Описание задачи циклограммы
+ NLINE_NUMB PKG_STD.TNUMBER := 0; -- Номер строки
+ NLINE_NUMB_TMP PKG_STD.TNUMBER := 0; -- Номер строки (буфер)
+ DTASK_DATE_START PKG_STD.TLDATE; -- Дата начала этапа
+ DTASK_DATE_END PKG_STD.TLDATE; -- Дата окончания этапа
+ NTASK_START PKG_STD.TNUMBER := 0; -- Позиция начала этапа
+ NTASK_END PKG_STD.TNUMBER := 0; -- Позиция окончания этапа
+ STASK_NAME PKG_STD.TSTRING; -- Наименование задачи
+ SCOLOR_WHITE PKG_STD.TSTRING := 'white'; -- Цвет - белый
+ SBG_TASK_COLOR_W_GRP PKG_STD.TSTRING := '#6bc982'; -- Цвет задачи с группой
+ SHL_TASK_COLOR_W_GRP PKG_STD.TSTRING := '#7dd592'; -- Цвет наведения задачи с группой
+ SBG_TASK_COLOR_WO_GRP PKG_STD.TSTRING := '#e36d6d'; -- Цвет задачи без группы
+ STEXT_COLOR_TASK_WO_GRP PKG_STD.TSTRING := '#e5e5e5'; -- Цвет текста задачи без группы
+ SBG_TASK_COLOR_GRP PKG_STD.TSTRING := 'cadetblue'; -- Цвет групповой задачи
+ SHL_TASK_COLOR_GRP PKG_STD.TSTRING := '#6fadaf'; -- Цвет наведения групповой задачи
+
+ /* Считывание значений группирующей задачи */
+ procedure GROUP_TASK_GET
+ (
+ NIDENT in number, -- Идентификатор процесса
+ NGROUP in number := null, -- Рег. номер группы
+ NFLAG_WO_GROUP in number := 0, -- Признак отбора задач без групп (0 - нет, 1 - да)
+ DTASK_DATE_START out date, -- Дата начала этапа
+ DTASK_DATE_END out date, -- Дата окончания этапа
+ NTASK_START out number, -- Позиция начала этапа
+ NTASK_END out number -- Позиция окончания этапа
+ )
+ is
+ begin
+ ...
+ end GROUP_TASK_GET;
+ begin
+ /* Инициализируем циклограмму */
+ CG := PKG_P8PANELS_VISUAL.TCYCLOGRAM_MAKE(STITLE => 'Задачи на ' || TO_CHAR(EXTRACT(year from sysdate)) || ' год');
+ /* Добавляем атрибуты */
+ PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_TASK_ATTR(RCYCLOGRAM => CG,
+ SNAME => 'ddate_start',
+ SCAPTION => 'Дата начала',
+ BVISIBLE => true,
+ BCLEAR => true);
+ PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_TASK_ATTR(RCYCLOGRAM => CG,
+ SNAME => 'ddate_end',
+ SCAPTION => 'Дата окончания',
+ BVISIBLE => true);
+ PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_TASK_ATTR(RCYCLOGRAM => CG,
+ SNAME => 'type',
+ SCAPTION => 'Тип',
+ BVISIBLE => false);
+ /* Обходим колонки */
+ for CLMN in (select T.NAME,
+ T.POS_START,
+ T.POS_END
+ from P8PNL_SMPL_CYCLOGRAM T
+ where T.IDENT = NIDENT
+ and T.TYPE = 0)
+ loop
+ /* Добавляем колонку */
+ PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_COLUMN(RCYCLOGRAM => CG,
+ SNAME => CLMN.NAME,
+ NSTART => CLMN.POS_START,
+ NEND => CLMN.POS_END);
+ end loop;
+ /* Считываем значения для задач проекта */
+ GROUP_TASK_GET(NIDENT => NIDENT,
+ NFLAG_WO_GROUP => 0,
+ DTASK_DATE_START => DTASK_DATE_START,
+ DTASK_DATE_END => DTASK_DATE_END,
+ NTASK_START => NTASK_START,
+ NTASK_END => NTASK_END);
+ /* Формируем задачу (этап) */
+ RTASK := PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_MAKE(NRN => 1,
+ SCAPTION => 'Задачи проекта',
+ SNAME => 'Задачи проекта',
+ NLINE_NUMB => NLINE_NUMB,
+ NSTART => NTASK_START,
+ NEND => NTASK_END,
+ SBG_COLOR => SCOLOR_WHITE);
+ /* Добавляем атрибуты */
+ PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG,
+ RTASK => RTASK,
+ SNAME => 'ddate_start',
+ SVALUE => PKG_XCONVERT.DATE_TO_XML(DVALUE => DTASK_DATE_START),
+ BCLEAR => true);
+ PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG,
+ RTASK => RTASK,
+ SNAME => 'ddate_end',
+ SVALUE => PKG_XCONVERT.DATE_TO_XML(DVALUE => DTASK_DATE_END));
+ PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG, RTASK => RTASK, SNAME => 'type', SVALUE => 0);
+ /* Добавляем задачу в циклограмму */
+ PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_TASK(RCYCLOGRAM => CG, RTASK => RTASK);
+ /* Указываем следующую строку */
+ NLINE_NUMB := NLINE_NUMB + 1;
+ /* Обходим группы */
+ for GRP in (select T.RN,
+ T.NAME,
+ ROWNUM RNUM
+ from P8PNL_SMPL_CYCLOGRAM T
+ where T.IDENT = NIDENT
+ and T.TYPE = 1
+ order by T.RN asc)
+ loop
+ ...
+ /* Добавляем группу */
+ PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_GROUP(RCYCLOGRAM => CG,
+ SNAME => GRP.NAME,
+ NHEADER_HEIGHT => 30,
+ NHEADER_WIDTH => 200);
+ /* Считываем значения этапа группы */
+ GROUP_TASK_GET(NIDENT => NIDENT,
+ NGROUP => GRP.RN,
+ DTASK_DATE_START => DTASK_DATE_START,
+ DTASK_DATE_END => DTASK_DATE_END,
+ NTASK_START => NTASK_START,
+ NTASK_END => NTASK_END);
+ /* Формируем задачу (этап) */
+ RTASK := PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_MAKE(NRN => GRP.RN,
+ SCAPTION => 'Этап ' || TO_CHAR(GRP.RNUM),
+ SNAME => 'Этап ' || TO_CHAR(GRP.RNUM),
+ NLINE_NUMB => NLINE_NUMB,
+ NSTART => NTASK_START,
+ NEND => NTASK_END,
+ SBG_COLOR => SBG_TASK_COLOR_GRP,
+ STEXT_COLOR => SCOLOR_WHITE,
+ SHIGHLIGHT_COLOR => SHL_TASK_COLOR_GRP);
+ /* Добавляем атрибуты */
+ PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG,
+ RTASK => RTASK,
+ SNAME => 'ddate_start',
+ SVALUE => PKG_XCONVERT.DATE_TO_XML(DVALUE => DTASK_DATE_START),
+ BCLEAR => true);
+ PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG,
+ RTASK => RTASK,
+ SNAME => 'ddate_end',
+ SVALUE => PKG_XCONVERT.DATE_TO_XML(DVALUE => DTASK_DATE_END));
+ PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG, RTASK => RTASK, SNAME => 'type', SVALUE => 1);
+ /* Добавляем задачу в циклограмму */
+ PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_TASK(RCYCLOGRAM => CG, RTASK => RTASK);
+ /* Обходим задачи группы */
+ for TASK in (select T.RN,
+ T.NAME,
+ T.POS_START,
+ T.POS_END,
+ T.DATE_FROM,
+ T.DATE_TO,
+ ROWNUM RNUM
+ from P8PNL_SMPL_CYCLOGRAM T
+ where T.IDENT = NIDENT
+ and T.TYPE = 2
+ and T.TASK_GROUP = GRP.RN)
+ loop
+ ...
+ end loop;
+ /* Указываем следующую строку */
+ NLINE_NUMB := NLINE_NUMB + 1;
+ end loop;
+ /* Указываем следующую строку */
+ NLINE_NUMB := NLINE_NUMB + 1;
+ /* Считываем значения для обособленных задач */
+ GROUP_TASK_GET(NIDENT => NIDENT,
+ NFLAG_WO_GROUP => 1,
+ DTASK_DATE_START => DTASK_DATE_START,
+ DTASK_DATE_END => DTASK_DATE_END,
+ NTASK_START => NTASK_START,
+ NTASK_END => NTASK_END);
+ /* Формируем задачу (этап) */
+ RTASK := PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_MAKE(NRN => 1,
+ SCAPTION => 'Обособленные задачи',
+ SNAME => 'Обособленные задачи',
+ NLINE_NUMB => NLINE_NUMB,
+ NSTART => NTASK_START,
+ NEND => NTASK_END,
+ SBG_COLOR => SCOLOR_WHITE);
+ /* Добавляем атрибуты */
+ PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG,
+ RTASK => RTASK,
+ SNAME => 'ddate_start',
+ SVALUE => PKG_XCONVERT.DATE_TO_XML(DVALUE => DTASK_DATE_START),
+ BCLEAR => true);
+ PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG,
+ RTASK => RTASK,
+ SNAME => 'ddate_end',
+ SVALUE => PKG_XCONVERT.DATE_TO_XML(DVALUE => DTASK_DATE_END));
+ PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG, RTASK => RTASK, SNAME => 'type', SVALUE => 0);
+ /* Добавляем задачу в циклограмму */
+ PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_TASK(RCYCLOGRAM => CG, RTASK => RTASK);
+ /* Указываем следующую строку */
+ NLINE_NUMB := NLINE_NUMB + 1;
+ /* Цикл по обособленным задачам */
+ for REC in (select T.RN,
+ T.NAME,
+ T.POS_START,
+ T.POS_END,
+ T.DATE_FROM,
+ T.DATE_TO,
+ ROWNUM RNUM
+ from P8PNL_SMPL_CYCLOGRAM T
+ where T.IDENT = NIDENT
+ and T.TYPE = 2
+ and T.TASK_GROUP is null)
+ loop
+ ...
+ end loop;
+ /* Формируем список */
+ COUT := PKG_P8PANELS_VISUAL.TCYCLOGRAM_TO_XML(RCYCLOGRAM => CG);
+ end CYCLOGRAM;
+```
+
+Код панели на стороне клиента (WEB-приложения):
+
+```
+/*
+ Парус 8 - Панели мониторинга - Примеры для разработчиков
+ Пример: Циклограмма "P8PCyclogram"
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React, { useState, useContext, useCallback, useEffect } from "react"; //Классы React
+import PropTypes from "prop-types"; //Контроль свойств компонента
+import {
+ Typography,
+ Grid,
+ Button,
+ Box,
+ DialogContent,
+ List,
+ ListItem,
+ ListItemText,
+ Divider,
+ TextField,
+ DialogActions,
+ Stack,
+ Icon
+} from "@mui/material"; //Интерфейсные элементы
+import { formatDateJSONDateOnly, formatDateRF } from "../../core/utils"; //Вспомогательные функции
+import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
+import { P8PCyclogram } from "../../components/p8p_cyclogram"; //Циклограмма
+import { P8P_CYCLOGRAM_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
+import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
+
+//---------
+//Константы
+//---------
+
+//Отступ контейнера страницы от заголовка
+const CONTAINER_PADDING_TOP = "20px";
+
+//Высота заголовка страницы
+const TITLE_HEIGHT = "47px";
+
+//Высота строк
+const LINE_HEIGHT = 30;
+
+//Стили
+const STYLES = {
+ CONTAINER: { textAlign: "center", paddingTop: CONTAINER_PADDING_TOP },
+ TITLE: { paddingBottom: "15px", height: TITLE_HEIGHT },
+ CYCLOGRAM_CONTAINER: {
+ height: `calc(100vh - ${APP_BAR_HEIGHT} - ${TITLE_HEIGHT} - ${CONTAINER_PADDING_TOP})`,
+ width: "100vw",
+ paddingTop: "5px"
+ },
+ TASK_EDITOR_CONTENT: { minWidth: 400, overflowX: "auto" },
+ TASK_EDITOR_LIST: { width: "100%", minWidth: 300, maxWidth: 700, bgcolor: "background.paper" },
+ GROUP_HEADER: height => ({
+ border: "1px solid",
+ backgroundColor: "#ecf8fb",
+ height: height,
+ borderRadius: "10px",
+ display: "flex",
+ alignItems: "center",
+ justifyContent: "space-around"
+ })
+};
+
+//---------------------------------------------
+//Вспомогательные функции форматирования данных
+//---------------------------------------------
+
+//Диалог открытия задачи
+const CustomTaskDialog = ({ task, ident, handleReload, close }) => {
+ ...
+};
+
+//Контроль свойств - Диалог открытия задачи
+CustomTaskDialog.propTypes = {
+ task: PropTypes.object.isRequired,
+ ident: PropTypes.number.isRequired,
+ handleReload: PropTypes.func.isRequired,
+ close: PropTypes.func.isRequired
+};
+
+//Заголовок группы
+const CustomGroupHeader = ({ group }) => {
+ ...
+};
+
+//Контроль свойств - Заголовок группы
+CustomGroupHeader.propTypes = {
+ group: PropTypes.object.isRequired
+};
+
+//Отображение задачи
+const taskRenderer = ({ task }) => {
+ ...
+};
+
+//-----------
+//Тело модуля
+//-----------
+
+//Пример: Циклограмма "P8PCyclogram"
+const Cyclogram = ({ title }) => {
+ //Собственное состояние
+ const [state, setState] = useState({
+ init: false,
+ dataLoaded: false,
+ reload: true,
+ ident: null
+ });
+
+ //Подключение к контексту взаимодействия с сервером
+ const { executeStored } = useContext(BackEndСtx);
+
+ //При необходимости перезагрузки
+ const handleReload = () => {
+ setState(pv => ({ ...pv, reload: true }));
+ };
+
+ //При необходимости обновить данные таблицы
+ useEffect(() => {
+ //Загрузка данных циклограммы с сервера
+ const loadData = async () => {
+ const data = await executeStored({
+ stored: "PKG_P8PANELS_SAMPLES.CYCLOGRAM",
+ args: { NIDENT: state.ident },
+ attributeValueProcessor: (name, val) =>
+ name === "name" ? undefined : ["ddate_start", "ddate_end"].includes(name) ? formatDateJSONDateOnly(val) : val,
+ respArg: "COUT"
+ });
+ setState(pv => ({ ...pv, dataLoaded: true, ...data.XCYCLOGRAM, reload: false }));
+ };
+ //Если указан идентификатор и требуется перезагрузить
+ if (state.ident && state.reload) loadData();
+ }, [state.ident, state.reload, executeStored]);
+
+ //При подключении компонента к странице
+ useEffect(() => {
+ //Инициализация данных циклограммы
+ const initData = async () => {
+ const data = await executeStored({ stored: "PKG_P8PANELS_SAMPLES.CYCLOGRAM_INIT", args: { NIDENT: state.ident } });
+ setState(pv => ({ ...pv, init: true, ident: data.NIDENT, reload: true }));
+ };
+ //Если требуется проинициализировать
+ if (!state.init) {
+ initData();
+ }
+ }, [executeStored, state.ident, state.init]);
+
+ return (
+
+