Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
426864a4b7 | |||
814a15c80e |
158
README.md
158
README.md
@ -104,9 +104,6 @@
|
|||||||
git clone https://git.citpb.ru/CITKParus/P8-Panels.git
|
git clone https://git.citpb.ru/CITKParus/P8-Panels.git
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Внимание:** если при клонировании репозитория возникает ошибка "Server certificate verification failed" - используйте ключ `http.sslVerify=false`:\
|
|
||||||
> `git clone https://git.citpb.ru/CITKParus/P8-Panels.git -c http.sslVerify=false`
|
|
||||||
|
|
||||||
6. Проведите компиляцию хранимых объектов БД из каталога "db" клонированного репозитория (компиляцию проводить под пользователем-владельцем схемы серверной части Системы, с последующей перекомпиляцией зависимых инвалидных объектов), затем исполните скрипт "grants.sql", размещённый в этом же каталоге.
|
6. Проведите компиляцию хранимых объектов БД из каталога "db" клонированного репозитория (компиляцию проводить под пользователем-владельцем схемы серверной части Системы, с последующей перекомпиляцией зависимых инвалидных объектов), затем исполните скрипт "grants.sql", размещённый в этом же каталоге.
|
||||||
|
|
||||||
7. Перезапустите сервер приложений "ПАРУС 8 Онлайн"
|
7. Перезапустите сервер приложений "ПАРУС 8 Онлайн"
|
||||||
@ -340,59 +337,6 @@ const MyPanel = () => {
|
|||||||
|
|
||||||
8. Выдайте права но новое действие в "Администраторе", при необходимости - начните новый сеанс в "ПАРУС 8 Онлайн" с очисткой системного кэша.
|
8. Выдайте права но новое действие в "Администраторе", при необходимости - начните новый сеанс в "ПАРУС 8 Онлайн" с очисткой системного кэша.
|
||||||
|
|
||||||
#### Настройка КОР-действия для вызова панели "Выдача сменного задания на участок" из раздела "Сменные задания"
|
|
||||||
|
|
||||||
Входящая в состав поставки фреймворка панель "Выдача сменного задания на участок" доступна для вызова из раздела "Сменные задания" (приложение "Планирование и учёт в дискретном производстве", главное меню > "Документы" > "Сменные задания").
|
|
||||||
|
|
||||||
Для настройки этой возможности:
|
|
||||||
|
|
||||||
1. Откройте раздел "Классы" приложения "Конструктор отраслевых расширений" (главное меню > "Учёт" > "Классы")
|
|
||||||
2. В дереве классов выберите "Сменные задания", а в списке классов - класс с кодом "CostJobs"
|
|
||||||
3. В спецификации "Методы", выбранного класса, зарегистрируйте новый метод со следующими атрибутами:
|
|
||||||
|
|
||||||
- `Мнемокод` - P8PANELS_OPEN
|
|
||||||
- `Наименование` - P8PANELS_OPEN
|
|
||||||
- `Тип метода` - Встроенный
|
|
||||||
- `Доступность` - Клиентский
|
|
||||||
|
|
||||||
4. Для добавленного метода `P8PANELS_OPEN` в спецификации "Параметры" зарегистрируйте следующий набор параметров:
|
|
||||||
|
|
||||||
| Имя | Наименование | Тип | Домен | Обязательный | Тип привязки | Контекст | Параметр действия |
|
|
||||||
| -------- | ------------------- | ------- | ------- | ------------ | ----------------- | -------------------- | ----------------- |
|
|
||||||
| NRN | Рег. номер записи | Входной | TRN | Нет | Контекст | Идентификатор записи | |
|
|
||||||
| SPANEL | Наименование панели | Входной | TSTRING | Да | Параметр действия | | SPANEL |
|
|
||||||
| SCAPTION | Заголовок вкладки | Входной | TSTRING | Нет | Параметр действия | | SCAPTION |
|
|
||||||
|
|
||||||
5. В спецификации "Действия", выбранного класса, зарегистрируйте новое действие со следующими атрибутами:
|
|
||||||
|
|
||||||
- `Тип` - Нестандартное
|
|
||||||
- `Код` - FCJOBS_OPEN_JOBS_MANAGE_MP
|
|
||||||
- `Наименование` - Открытие панели "Выдача сменного задания на участок"
|
|
||||||
- `Технология производства` - Конструктор
|
|
||||||
- `Реализующий метод` - P8PANELS_OPEN
|
|
||||||
- `Обработка записей` - Для одной текущей записи
|
|
||||||
- `Завершение транзакции` - После каждого вызова действия
|
|
||||||
- `Обновление выборки` - Не обновлять
|
|
||||||
|
|
||||||
6. Для добавленного действия `FCJOBS_OPEN_JOBS_MANAGE_MP` в спецификации "Параметры" зарегистрируйте следующий набор параметров:
|
|
||||||
|
|
||||||
| Имя | Домен | Тип привязки | Значение |
|
|
||||||
| -------- | ------- | ------------ | ---------------------------------- |
|
|
||||||
| SPANEL | TSTRING | Значение | MechRecCostJobsManageMP |
|
|
||||||
| SCAPTION | TSTRING | Значение | Выдача сменного задания на участок |
|
|
||||||
|
|
||||||
7. Откройте редактор формы представления данных класса "CostJobs" ("Сменные задания").
|
|
||||||
|
|
||||||
Для этого отметьте в списке классов запись с кодом "CostJobs", перейдите на закладку "Методы вызова", укажите метод вызова "main", в его контекстном меню укажите "Формы", в появившемся списке форм выполните действие "Редактор" для формы с наименованием "Форма просмотра".
|
|
||||||
|
|
||||||
В открывшемся редакторе формы перейдите в режим редактирования всплывающего меню заголовка (закладка "Таблицы", таблица "CostJobs", затем кнопка "Редактор источника", установить фокус на форме представления данных щелчком мыши, затем пункт "Всплывающее меню" в "Инспекторе объектов"). Найдите в меню пункт, созданный Системой для действия, зарегистрированного на шаге 5 (как правило имеет метку, совпадающую с наименованием действия). Расположите (перетаскиванием) этот пункт меню сразу после пункта "Отработать исполнение по штрих-кодам". Укажите для этого пункта следующие параметры в "Инспекторе объектов":
|
|
||||||
|
|
||||||
- `Заголовок` - Выдать сменное задание на участок…
|
|
||||||
|
|
||||||
Закройте окна редакторов с сохранением изменений.
|
|
||||||
|
|
||||||
8. Выдайте права но новое действие в "Администраторе", при необходимости - начните новый сеанс в "ПАРУС 8 Онлайн" с очисткой системного кэша.
|
|
||||||
|
|
||||||
#### Настройка КОР-действия для вызова панели "Производственная программа" из раздела "Планы и отчеты производства изделий"
|
#### Настройка КОР-действия для вызова панели "Производственная программа" из раздела "Планы и отчеты производства изделий"
|
||||||
|
|
||||||
Входящая в состав поставки фреймворка панель "Производственная программа" доступна для вызова из спецификации "Выпуск" раздела "Планы и отчеты производства изделий" (приложение "Планирование и учёт в дискретном производстве", главное меню > "Документы" > "Планы и отчеты производства изделий").
|
Входящая в состав поставки фреймворка панель "Производственная программа" доступна для вызова из спецификации "Выпуск" раздела "Планы и отчеты производства изделий" (приложение "Планирование и учёт в дискретном производстве", главное меню > "Документы" > "Планы и отчеты производства изделий").
|
||||||
@ -516,7 +460,7 @@ c:\inetpub\p8web20\WebClient\Modules\P8-Panels>npm run build
|
|||||||
- `isRespErr` - функция, проверка результата исполнения серверного объекта на наличие ошибок
|
- `isRespErr` - функция, проверка результата исполнения серверного объекта на наличие ошибок
|
||||||
- `getRespErrMessage` - функция, получение ошибки исполнения серверного объекта
|
- `getRespErrMessage` - функция, получение ошибки исполнения серверного объекта
|
||||||
- `getRespPayload` - функция, получение выходных значений, полученных после успешного исполнения
|
- `getRespPayload` - функция, получение выходных значений, полученных после успешного исполнения
|
||||||
- `executeStored` - функция, асинхронное исполнение хранимой процедуры/функции БД Системы
|
- `executeStored` -функция, асинхронное исполнение хранимой процедуры/функции БД Системы
|
||||||
- `getConfig` - функция, асинхронное считывание параметров конфигурации, определённых в "p8panels.config" (возвращает их JSON-представление)
|
- `getConfig` - функция, асинхронное считывание параметров конфигурации, определённых в "p8panels.config" (возвращает их JSON-представление)
|
||||||
|
|
||||||
При формировании ответов, функции, получающие данные с сервера, возвращают типовые значения:
|
При формировании ответов, функции, получающие данные с сервера, возвращают типовые значения:
|
||||||
@ -584,8 +528,7 @@ c:\inetpub\p8web20\WebClient\Modules\P8-Panels>npm run build
|
|||||||
throwError = true,
|
throwError = true,
|
||||||
showErrorMessage = true,
|
showErrorMessage = true,
|
||||||
fullResponse = false,
|
fullResponse = false,
|
||||||
spreadOutArguments = true,
|
spreadOutArguments = true
|
||||||
signal = null
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -598,8 +541,7 @@ c:\inetpub\p8web20\WebClient\Modules\P8-Panels>npm run build
|
|||||||
`throwError` - необязательный, логический, признак генерации исключения, если `false` - возвращает ошибку в типовом формате\
|
`throwError` - необязательный, логический, признак генерации исключения, если `false` - возвращает ошибку в типовом формате\
|
||||||
`showErrorMessage` - необязательный, логический, признак отображения типового клиентского сообщение об ошибке, в случае её возникновения (только если `throwError = true`)\
|
`showErrorMessage` - необязательный, логический, признак отображения типового клиентского сообщение об ошибке, в случае её возникновения (только если `throwError = true`)\
|
||||||
`fullResponse` - необязательный, логический, признак возврата полного типового ответа сервера, если `false` - возвращается только содержимое `XPAYLOAD`\
|
`fullResponse` - необязательный, логический, признак возврата полного типового ответа сервера, если `false` - возвращается только содержимое `XPAYLOAD`\
|
||||||
`spreadOutArguments` - необязательный, логический, признак "разделения" значений выходных параметров исполняемого обхекта (игнорируется при наличии `respArg`), если `true` - `XPAYLOAD` будет содержать ответ в виде `{"ВЫХОДНОЙ_ПАРАМЕТР1": "ЗНАЧЕНИЕ", "ВЫХОДНОЙ_ПАРАМЕТР2": "ЗНАЧЕНИЕ", ...}`, если `false` - `XPAYLOAD` будет содержать ответ в виде `{XOUT_ARGUMENTS: [{SNAME: "ВЫХОДНОЙ_ПАРАМЕТР1", VALUE: "ЗНАЧЕНИЕ"}, {SNAME: "ВЫХОДНОЙ_ПАРАМЕТР2", VALUE: "ЗНАЧЕНИЕ"}, ...]}`\
|
`spreadOutArguments` - необязательный, логический, признак "разделения" значений выходных параметров исполняемого обхекта (игнорируется при наличии `respArg`), если `true` - `XPAYLOAD` будет содержать ответ в виде `{"ВЫХОДНОЙ_ПАРАМЕТР1": "ЗНАЧЕНИЕ", "ВЫХОДНОЙ_ПАРАМЕТР2": "ЗНАЧЕНИЕ", ...}`, если `false` - `XPAYLOAD` будет содержать ответ в виде `{XOUT_ARGUMENTS: [{SNAME: "ВЫХОДНОЙ_ПАРАМЕТР1", VALUE: "ЗНАЧЕНИЕ"}, {SNAME: "ВЫХОДНОЙ_ПАРАМЕТР2", VALUE: "ЗНАЧЕНИЕ"}, ...]}`
|
||||||
`signal` - необязательный, объект, экземпляр `AbortSignal` (например, `AbortController.signal`) для управления прерыванием выполнения запроса
|
|
||||||
|
|
||||||
**Результат:** объект с данными, размещёнными в `XPAYLOAD` ответа сервера (если `fullResponse = false`) или полный типовой ответ (описан выше).
|
**Результат:** объект с данными, размещёнными в `XPAYLOAD` ответа сервера (если `fullResponse = false`) или полный типовой ответ (описан выше).
|
||||||
|
|
||||||
@ -1093,7 +1035,7 @@ const Mui = ({ title }) => {
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
###### `undefined showMsg(type, text, msgOnOk = null, msgOnCancel = null, fullErrorText = null)`
|
###### `undefined showMsg(type, text, msgOnOk = null, msgOnCancel = null)`
|
||||||
|
|
||||||
Отображает модальное окно сообщения заданного типа.
|
Отображает модальное окно сообщения заданного типа.
|
||||||
|
|
||||||
@ -1101,21 +1043,16 @@ const Mui = ({ title }) => {
|
|||||||
|
|
||||||
`type` - обязательный, строка, тип отображаемого сообщения, `information|warning|error` (см. константу `MSG_TYPE` в "app/context/messaging_reducer" и константу `P8P_APP_MESSAGE_VARIANT` в "app/components/p8p_app_message")\
|
`type` - обязательный, строка, тип отображаемого сообщения, `information|warning|error` (см. константу `MSG_TYPE` в "app/context/messaging_reducer" и константу `P8P_APP_MESSAGE_VARIANT` в "app/components/p8p_app_message")\
|
||||||
`text` - обязательный, строка, текст отображаемого сообщения\
|
`text` - обязательный, строка, текст отображаемого сообщения\
|
||||||
`msgOnOk` - необязательный, функция, будет вызвана при нажатии на "ОК"/"ЗАКРЫТЬ" в сообщении\
|
`msgOnOk` - необязательный, функция, будет вызвана при нажатии на "ОК"/"ЗАКРЫТЬ" в сообщении
|
||||||
`msgOnCancel` - необязательный, функция, будет вызвана при нажатии на "ОТМЕНА" в сообщении (только для сообщений типа `warning`)\
|
`msgOnCancel` - необязательный, функция, будет вызвана при нажатии на "ОТМЕНА" в сообщении (только для сообщений типа `warning`)
|
||||||
`fullErrorText` - необязательный, строка, полный текст ошибки, используется только при `type="error"`. Если параметр указан, то в окно ошибки выводится кнопка "Подробнее", по нажатию на которую будет отображаться текст, указанный в данном параметре
|
|
||||||
|
|
||||||
**Результат:** функция не возвращает значимого результата
|
**Результат:** функция не возвращает значимого результата
|
||||||
|
|
||||||
###### `undefined showMsgErr(text, msgOnOk = null, fullErrorText = null)`
|
###### `undefined showMsgErr(text, msgOnOk = null)`
|
||||||
|
|
||||||
Декоратор для `showMsg`, отображает модальное окно сообщения типа "Ошибка" (`type="error"`).
|
Декоратор для `showMsg`, отображает модальное окно сообщения типа "Ошибка" (`type="error"`).
|
||||||
|
|
||||||
**Входные параметры:**
|
**Входные параметры:** аналогично `showMsg`
|
||||||
|
|
||||||
`text` - обязательный, строка, текст отображаемого сообщения\
|
|
||||||
`msgOnOk` - необязательный, функция, будет вызвана при нажатии на "ЗАКРЫТЬ" в сообщении\
|
|
||||||
`fullErrorText` - необязательный, строка, полный текст ошибки. Если параметр указан, то в окно ошибки выводится кнопка "Подробнее", по нажатию на которую будет отображаться текст, указанный в данном параметре
|
|
||||||
|
|
||||||
**Результат:** аналогично `showMsg`
|
**Результат:** аналогично `showMsg`
|
||||||
|
|
||||||
@ -1203,20 +1140,6 @@ const Messages = ({ title }) => {
|
|||||||
Ошибка
|
Ошибка
|
||||||
</Button>
|
</Button>
|
||||||
<Divider sx={STYLES.DIVIDER} />
|
<Divider sx={STYLES.DIVIDER} />
|
||||||
{/* Сообщение об ошибке (диалог с подробностями) */}
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
onClick={() =>
|
|
||||||
showMsgErr(
|
|
||||||
"Что-то пошло не так :( ...но мы точно знаем что ;)",
|
|
||||||
null,
|
|
||||||
"Здесь подробная информация об ошибке (стек вызова СУБД, например)"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Ошибка с подробностями
|
|
||||||
</Button>
|
|
||||||
<Divider sx={STYLES.DIVIDER} />
|
|
||||||
{/* Предупреждение (диалог) */}
|
{/* Предупреждение (диалог) */}
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
@ -1267,7 +1190,7 @@ const Messages = ({ title }) => {
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
###### `undefined showLoader(message)`
|
###### `undefined showMsg(message)`
|
||||||
|
|
||||||
Отображает модальный индикатор процесса с указанным сообщением.
|
Отображает модальный индикатор процесса с указанным сообщением.
|
||||||
|
|
||||||
@ -1334,14 +1257,14 @@ const Loader = ({ title }) => {
|
|||||||
- состоят из значительного числа интерфейсных примитивов
|
- состоят из значительного числа интерфейсных примитивов
|
||||||
- имеют специальный API на стороне сервера БД Системы для управления их содержимым
|
- имеют специальный API на стороне сервера БД Системы для управления их содержимым
|
||||||
|
|
||||||
Необходимо понимать, что с одной стороны, наличие серверного API в БД значительно упрощает взаимодействие с компонентом, с другой стороны - ограничивает возможности его применения только теми прикладными задачами и функциональными возможностями, которые заложены в него. При этом "примитивы" HTML и MUI, хоть и сложнее в применении, но позволяют "собирать" практически любые интерфейсные решения на вкус разработчика.
|
Необходимо понимать, что с одной стороны, наличие серверного API в БД значительно упрощает взаимодействие с компонентом, с другой стороны - ограничивает возможности его примерения только теми прикладными задачами и функциональными возможностями, которые заложены в него. При этом "примитивы" HTML и MUI, хоть и сложнее в применении, но позволяют "собирать" практически любые интерфейсные решения на вкус разработчика.
|
||||||
|
|
||||||
##### Таблица данных "P8PDataGrid"
|
##### Таблица данных "P8PDataGrid"
|
||||||
|
|
||||||
Предназначена для формирования табличных представлений данных с поддержкой:
|
Предназначена для формирования табличных представлений данных с поддержкой:
|
||||||
|
|
||||||
- постраничного вывода данных
|
- постраничного вывода данных
|
||||||
- сортировки и отбора данных по колонкам на стороне сервера БД
|
- сортировки и отбора данных по колонкам на строне сервера БД
|
||||||
- сложных заголовков с возможностью отображения/сокрытия уровней
|
- сложных заголовков с возможностью отображения/сокрытия уровней
|
||||||
- разворачивающихся строк (accordion)
|
- разворачивающихся строк (accordion)
|
||||||
- группировки строк с возможностью отображения/сокрытия содержимого группы
|
- группировки строк с возможностью отображения/сокрытия содержимого группы
|
||||||
@ -1368,7 +1291,6 @@ const MyPanel = () => {
|
|||||||
|
|
||||||
**Свойства**
|
**Свойства**
|
||||||
|
|
||||||
`style` - необязательный, объект, если задан, то будет применён в качестве атрибута `style` коневого контейнера (`div`) компонента\
|
|
||||||
`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: <ОКОНЧАНИЕ_ДИАПАЗОНА_ЗНАЧЕНИЙ_ФИЛЬТРА>}`\
|
`filtersInitial` - необязательныей, массив, начальное состояние фильтров таблицы, содержит объекты вида `{name: <НАИМЕНОВАНИЕ_КОЛОНКИ>, from: <НАЧАЛО_ДИАПАЗОНА_ЗНАЧЕНИЙ_ФИЛЬТРА>, to: <ОКОНЧАНИЕ_ДИАПАЗОНА_ЗНАЧЕНИЙ_ФИЛЬТРА>}`\
|
||||||
`groups` - необязательный, массив групп данных, содержит объекты вида `{name: <ИМЯ_ГРУППЫ>, caption: <ЗАГОЛОВОК_ГРУППЫ>, expandable: <ПРИЗНАК_РАЗВОРАЧИВАЕМОСТИ_ГРУППЫ - true|false>, expanded: <ПРИЗНАК_РАЗВЕРНУТОСТИ_ГРУППЫ - true|false>}`\
|
`groups` - необязательный, массив групп данных, содержит объекты вида `{name: <ИМЯ_ГРУППЫ>, caption: <ЗАГОЛОВОК_ГРУППЫ>, expandable: <ПРИЗНАК_РАЗВОРАЧИВАЕМОСТИ_ГРУППЫ - true|false>, expanded: <ПРИЗНАК_РАЗВЕРНУТОСТИ_ГРУППЫ - true|false>}`\
|
||||||
@ -1978,7 +1900,7 @@ const Chart = ({ title }) => {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
Полные актуальные исходные коды примеров можно увидеть в "db/PKG_P8PANELS_SAMPLES.pck" и "app/panels/samples/chart.js" данного репозитория соответственно.
|
Полные актуальные исходные коды примеров можно увидеть в "db/PKG_P8PANELS_SAMPLES.pck" и "app/panels/samples/data_grid.js" данного репозитория соответственно.
|
||||||
|
|
||||||
##### Диаграмма ганта "P8PGantt"
|
##### Диаграмма ганта "P8PGantt"
|
||||||
|
|
||||||
@ -2031,7 +1953,6 @@ const MyPanel = () => {
|
|||||||
`onTaskProgressChange` - необязательный, функция, если указана - будет вызвана при изменении прогресса исполнения элемента диаграммы, сигнатура функции `f({task, progress})`, результат функции не интерпретируется. В функцию будет передан объект в поле `task`, которого, будет содержаться описание изменённой задачи (элемент массива `tasks`, см. выше описание полей), в поле `progress` - новое значение прогресса исполнения задачи.\
|
`onTaskProgressChange` - необязательный, функция, если указана - будет вызвана при изменении прогресса исполнения элемента диаграммы, сигнатура функции `f({task, progress})`, результат функции не интерпретируется. В функцию будет передан объект в поле `task`, которого, будет содержаться описание изменённой задачи (элемент массива `tasks`, см. выше описание полей), в поле `progress` - новое значение прогресса исполнения задачи.\
|
||||||
`taskAttributeRenderer` - необязательный, функция, если указана - будет вызвана при отображении диалога редактора здачи, результат функции будет применён для отображения области дополнительных атрибутов задачи в диалоге редактора, если не указана - дополнительные атрибуты будут отображены с форматированием по умолчанию. Сигнатура функции - `f({task, attribute})`, в функцию будет передан объект в поле `task`, которого, будет содержаться описание задачи для которой отображается редактор (элемент массива `tasks`, см. выше описание полей), в поле `attribute` - описание дополнительного атрибута формируемого в диалоге редактора (элемент массива `taskAttributes`, см. выше описание полей). Должна возвращать значение или React-компонент.\
|
`taskAttributeRenderer` - необязательный, функция, если указана - будет вызвана при отображении диалога редактора здачи, результат функции будет применён для отображения области дополнительных атрибутов задачи в диалоге редактора, если не указана - дополнительные атрибуты будут отображены с форматированием по умолчанию. Сигнатура функции - `f({task, attribute})`, в функцию будет передан объект в поле `task`, которого, будет содержаться описание задачи для которой отображается редактор (элемент массива `tasks`, см. выше описание полей), в поле `attribute` - описание дополнительного атрибута формируемого в диалоге редактора (элемент массива `taskAttributes`, см. выше описание полей). Должна возвращать значение или React-компонент.\
|
||||||
`taskDialogRenderer` - необязательный, функция, если указана - будет вызвана до отображения диалога редактора задачи. Результат функции будет показан в качестве содержимого диалога редактора, вместо типовой формы. Сигнатура функции - `f({task, taskAttributes, taskColors, close})`, в функцию будет передан объект в поле `task`, которого, будет содержаться описание задачи для которой отображается редактор (элемент массива `tasks`, см. выше описание полей), в поле `taskAttributes` - массив `taskAttributes` (см. выше описание полей), описывающий состав полей задачи, в поле `taskColors` - массив `taskColors` (см. выше описание полей), описывающий цвета заливки, определённые для задачи, в поле `close` - функция закрытия диалога задачи, может быть вызвана возвращаемым Reac-компонентом для сокрытия диалога. Должна возвращать значение или React-компонент.\
|
`taskDialogRenderer` - необязательный, функция, если указана - будет вызвана до отображения диалога редактора задачи. Результат функции будет показан в качестве содержимого диалога редактора, вместо типовой формы. Сигнатура функции - `f({task, taskAttributes, taskColors, close})`, в функцию будет передан объект в поле `task`, которого, будет содержаться описание задачи для которой отображается редактор (элемент массива `tasks`, см. выше описание полей), в поле `taskAttributes` - массив `taskAttributes` (см. выше описание полей), описывающий состав полей задачи, в поле `taskColors` - массив `taskColors` (см. выше описание полей), описывающий цвета заливки, определённые для задачи, в поле `close` - функция закрытия диалога задачи, может быть вызвана возвращаемым Reac-компонентом для сокрытия диалога. Должна возвращать значение или React-компонент.\
|
||||||
`taskDialogProps` - необязательный, объект, содержит свойства, которые будут переданы компоненту-контейнеру (`Dialog`) редактора задачи.\
|
|
||||||
`noDataFoundText` - обязательный, строка, текст для отображения ошибки об отсутствии данных\
|
`noDataFoundText` - обязательный, строка, текст для отображения ошибки об отсутствии данных\
|
||||||
`numbTaskEditorCaption` - обязательный, строка, подпись стандартного атрибута `numb` в диалоге редактора задачи\
|
`numbTaskEditorCaption` - обязательный, строка, подпись стандартного атрибута `numb` в диалоге редактора задачи\
|
||||||
`nameTaskEditorCaption` - обязательный, строка, подпись стандартного атрибута `name` в диалоге редактора задачи\
|
`nameTaskEditorCaption` - обязательный, строка, подпись стандартного атрибута `name` в диалоге редактора задачи\
|
||||||
@ -2941,61 +2862,6 @@ export { Cyclogram };
|
|||||||
|
|
||||||
Полные актуальные исходные коды примеров можно увидеть в "db/PKG_P8PANELS_SAMPLES.pck" и "app/panels/samples/cyclogram.js" данного репозитория соответственно.
|
Полные актуальные исходные коды примеров можно увидеть в "db/PKG_P8PANELS_SAMPLES.pck" и "app/panels/samples/cyclogram.js" данного репозитория соответственно.
|
||||||
|
|
||||||
##### Индикатор "P8PIndicator"
|
|
||||||
|
|
||||||
Компонент предназначен для отображения данных в виде индикатора. Поддерживается:
|
|
||||||
|
|
||||||
- Цветовая индикация предопределёнными цветами в зависимости от состояния (не определено, позитивное, негативное, пограничное)
|
|
||||||
- Цветовая индикация пользовательскими цветами
|
|
||||||
- Обработка нажатий
|
|
||||||
- Отображение иконки
|
|
||||||
- Упрвление внешним видом (парение, рамка)
|
|
||||||
- Интерактивные подсказки
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
**Подключение**
|
|
||||||
|
|
||||||
Клиентская часть индикатора реализована в компоненте `P8PIndicator`, объявленном в "app/components/p8p_indicator". Для использования компонента на панели его необходимо импортировать:
|
|
||||||
|
|
||||||
```
|
|
||||||
import { P8PIndicator } from "../../components/p8p_indicator";
|
|
||||||
|
|
||||||
const MyPanel = () => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<P8PIndicator .../>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Свойства**
|
|
||||||
|
|
||||||
`caption` - обязательный, строка, подпись индикатора\
|
|
||||||
`value` - обязательный, строка, значение индикатора\
|
|
||||||
`icon` - необязательный, строка, код иконки индикатора из символов шрифта [Google Material Icons](https://fonts.google.com/icons?icon.set=Material+Icons) (по умолчанию - не указана, если указана - отображается в левой части области индикатора)\
|
|
||||||
`state` - необязательный, строка, состояние индикатора, принимает значения `UNDEFINED|OK|ERR|WARN` (по умолчанию - `UNDEFINED`, см. константу `P8P_INDICATOR_STATE` в исходном коде компонента), определяет цвет заливки индикатора, если не указаны пользовательские цвета (см. ниже свойства `backgroundColor`и`color`)\
|
|
||||||
`square` - необязательный, логический, определяет необходимость скругления углов области индикатора (по умолчанию - `false`)\
|
|
||||||
`elevation` - необязательный, число, высота парения индикатора (по умолчанию - 3, используется только при `variant = 'elevation'`)\
|
|
||||||
`variant` - необязательный, строка, вариант исполнения, принимает значения `elevation|outlined` (по умолчанию - `elevation`, см. константу `P8P_INDICATOR_VARIANT` в исходном коде компонента), определяет внешний вид индикатора - парящая область или область с рамкой\
|
|
||||||
`hint` - необязательный, строка, текст подсказки для индикатора (если указан - слева от значения индикатора формируется кнопка открытия диалога с текстом подсказки, поддерживается HTML-форматирование)\
|
|
||||||
`onClick` - необязательный, функция, будет вызвана при нажатии пользователем на индикатор (если указана - индикатор формируется в виде кнопки), сигнатура функции `f()`, результат функции не интерпретируется\
|
|
||||||
`backgroundColor` - необязательный, строка, HTML-код пользовательского цвета фона, если указан - будет использован (вне зависимости от `state`) для заливки области индикатора (по умолчанию - не указан) \
|
|
||||||
`color` - необязательный, строка, HTML-код пользовательского цвета шрифта, если указан - будет использован (вне зависимости от `state`) для значения, подписи и иконки индикатора (по умолчанию - не указан)
|
|
||||||
|
|
||||||
**API на сервере БД**
|
|
||||||
|
|
||||||
Компонент `P8PIndicator` требует от разработчика передачи данных в определённом формате. С целью снижения трудозатрат на приведение собранных хранимым объектом данных Системы к форматам, потребляемым `P8PIndicator`, реализован специальный API на стороне сервера БД.
|
|
||||||
|
|
||||||
Для индикатора это (см. детальные описания программных интерфейсов в пакете `PKG_P8PANELS_VISUAL`):
|
|
||||||
`PKG_P8PANELS_VISUAL.TINDICATOR_MAKE` - функция, инициализация индикатора, возвращает объект для хранения его описания\
|
|
||||||
`PKG_P8PANELS_VISUAL.TINDICATOR_TO_XML` - функция, производит сериализацию объекта, описывающего индикатор, в специальный XML-формат, корректно интерпретируемый клиентским компонентом `P8PIndicator` при передаче в WEB-приложение
|
|
||||||
|
|
||||||
**Пример**
|
|
||||||
|
|
||||||
Полный актуальный исходный код примера можно увидеть в "app/panels/samples/indicator.js" данного репозитория.
|
|
||||||
|
|
||||||
### Ограничения дизайна пользовательского интерфейса
|
### Ограничения дизайна пользовательского интерфейса
|
||||||
|
|
||||||
Фреймворк позволяет реализовать любые пользовательские интерфейсы, вёрстка которых не противоречит возможностям современного HTML. Тем не менее, при разработке пользовательских интерфейсов панелей важно придерживаться предложенных ниже правил. Это позволит создавать их в едином ключе и упростит работу конечного пользователя при их освоении.
|
Фреймворк позволяет реализовать любые пользовательские интерфейсы, вёрстка которых не противоречит возможностям современного HTML. Тем не менее, при разработке пользовательских интерфейсов панелей важно придерживаться предложенных ниже правил. Это позволит создавать их в едином ключе и упростит работу конечного пользователя при их освоении.
|
||||||
|
@ -3,49 +3,10 @@
|
|||||||
Типовые стили
|
Типовые стили
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import { STATE } from "./app.text"; //Текстовые ресурсы и константы
|
|
||||||
import { red, green, orange, grey } from "@mui/material/colors";
|
|
||||||
|
|
||||||
//----------------
|
//----------------
|
||||||
//Интерфейс модуля
|
//Интерфейс модуля
|
||||||
//----------------
|
//----------------
|
||||||
|
|
||||||
//Цвета
|
|
||||||
export const APP_COLORS = {
|
|
||||||
[STATE.UNDEFINED]: {
|
|
||||||
color: "#dcdcdca0",
|
|
||||||
contrColor: "black"
|
|
||||||
},
|
|
||||||
[STATE.INFO]: {
|
|
||||||
color: "white",
|
|
||||||
contrColor: "black"
|
|
||||||
},
|
|
||||||
[STATE.OK]: {
|
|
||||||
color: green[200],
|
|
||||||
contrColor: green[900]
|
|
||||||
},
|
|
||||||
[STATE.ERR]: {
|
|
||||||
color: red[200],
|
|
||||||
contrColor: red[900]
|
|
||||||
},
|
|
||||||
[STATE.WARN]: {
|
|
||||||
color: orange[200],
|
|
||||||
contrColor: orange[900]
|
|
||||||
},
|
|
||||||
HOVER: {
|
|
||||||
color: grey[200],
|
|
||||||
contrColor: grey[900]
|
|
||||||
},
|
|
||||||
ACTIVE: {
|
|
||||||
color: grey[400],
|
|
||||||
contrColor: grey[900]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//Стили
|
//Стили
|
||||||
export const APP_STYLES = {
|
export const APP_STYLES = {
|
||||||
SCROLL: {
|
SCROLL: {
|
||||||
|
18
app.text.js
18
app.text.js
@ -18,8 +18,7 @@ export const TITLES = {
|
|||||||
//Текст
|
//Текст
|
||||||
export const TEXTS = {
|
export const TEXTS = {
|
||||||
LOADING: "Ожидайте...", //Ожидание завершения процесса
|
LOADING: "Ожидайте...", //Ожидание завершения процесса
|
||||||
NO_DATA_FOUND: "Данных не найдено", //Отсутствие данных
|
NO_DATA_FOUND: "Данных не найдено" //Отсутствие данных
|
||||||
NO_DATA_FOUND_SHORT: "Н.Д." //Отсутствие данных (кратко)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//Текст кнопок
|
//Текст кнопок
|
||||||
@ -30,15 +29,11 @@ export const BUTTONS = {
|
|||||||
OK: "ОК", //Ок
|
OK: "ОК", //Ок
|
||||||
CANCEL: "Отмена", //Отмена
|
CANCEL: "Отмена", //Отмена
|
||||||
CLOSE: "Закрыть", //Сокрытие
|
CLOSE: "Закрыть", //Сокрытие
|
||||||
DETAIL: "Подробнее", //Отображение подробностей/детализации
|
|
||||||
HIDE: "Скрыть", //Скрытие информации
|
|
||||||
CLEAR: "Очистить", //Очистка
|
CLEAR: "Очистить", //Очистка
|
||||||
ORDER_ASC: "По возрастанию", //Сортировка по возрастанию
|
ORDER_ASC: "По возрастанию", //Сортировка по возрастанию
|
||||||
ORDER_DESC: "По убыванию", //Сортировка по убыванию
|
ORDER_DESC: "По убыванию", //Сортировка по убыванию
|
||||||
FILTER: "Фильтр", //Фильтрация
|
FILTER: "Фильтр", //Фильтрация
|
||||||
MORE: "Ещё", //Догрузка данных
|
MORE: "Ещё" //Догрузка данных
|
||||||
APPLY: "Применить", //Сохранение без закрытия интерфейса ввода
|
|
||||||
SAVE: "Сохранить" //Сохранение
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//Метки атрибутов, сопроводительные надписи
|
//Метки атрибутов, сопроводительные надписи
|
||||||
@ -66,12 +61,3 @@ export const ERRORS = {
|
|||||||
export const ERRORS_HTTP = {
|
export const ERRORS_HTTP = {
|
||||||
404: "Адрес не найден"
|
404: "Адрес не найден"
|
||||||
};
|
};
|
||||||
|
|
||||||
//Типовые статусы
|
|
||||||
export const STATE = {
|
|
||||||
UNDEFINED: "UNDEFINED",
|
|
||||||
INFO: "INFORMATION",
|
|
||||||
OK: "OK",
|
|
||||||
ERR: "ERR",
|
|
||||||
WARN: "WARN"
|
|
||||||
};
|
|
||||||
|
@ -86,9 +86,6 @@ const Workspace = ({ panels = [], selectedPanel, children } = {}) => {
|
|||||||
//Подключение к контексту навигации
|
//Подключение к контексту навигации
|
||||||
const { navigateRoot, navigatePanel } = useContext(NavigationCtx);
|
const { navigateRoot, navigatePanel } = useContext(NavigationCtx);
|
||||||
|
|
||||||
//Подключение к контексту приложения
|
|
||||||
const { appState } = useContext(ApplicationСtx);
|
|
||||||
|
|
||||||
//Отработка действия навигации домой
|
//Отработка действия навигации домой
|
||||||
const handleHomeNavigate = () => navigateRoot();
|
const handleHomeNavigate = () => navigateRoot();
|
||||||
|
|
||||||
@ -101,7 +98,6 @@ const Workspace = ({ panels = [], selectedPanel, children } = {}) => {
|
|||||||
{...P8P_APP_WORKSPACE_CONFIG_PROPS}
|
{...P8P_APP_WORKSPACE_CONFIG_PROPS}
|
||||||
panels={panels}
|
panels={panels}
|
||||||
selectedPanel={selectedPanel}
|
selectedPanel={selectedPanel}
|
||||||
caption={appState.appBarTitle}
|
|
||||||
onHomeNavigate={handleHomeNavigate}
|
onHomeNavigate={handleHomeNavigate}
|
||||||
onItemNavigate={handleItemNavigate}
|
onItemNavigate={handleItemNavigate}
|
||||||
>
|
>
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
//Подключение библиотек
|
//Подключение библиотек
|
||||||
//---------------------
|
//---------------------
|
||||||
|
|
||||||
import React, { useState } from "react"; //Классы React
|
import React from "react"; //Классы React
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import Dialog from "@mui/material/Dialog"; //базовый класс диалога Material UI
|
import Dialog from "@mui/material/Dialog"; //базовый класс диалога Material UI
|
||||||
import DialogTitle from "@mui/material/DialogTitle"; //Заголовок диалога
|
import DialogTitle from "@mui/material/DialogTitle"; //Заголовок диалога
|
||||||
@ -18,8 +18,6 @@ import Typography from "@mui/material/Typography"; //Текст
|
|||||||
import Button from "@mui/material/Button"; //Кнопки
|
import Button from "@mui/material/Button"; //Кнопки
|
||||||
import Container from "@mui/material/Container"; //Контейнер
|
import Container from "@mui/material/Container"; //Контейнер
|
||||||
import Box from "@mui/material/Box"; //Обёртка
|
import Box from "@mui/material/Box"; //Обёртка
|
||||||
import { BUTTONS, STATE } from "../../app.text"; //Типовые текстовые ресурсы и константы
|
|
||||||
import { APP_COLORS } from "../../app.styles"; //Типовые стили
|
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
//Константы
|
//Константы
|
||||||
@ -27,9 +25,9 @@ import { APP_COLORS } from "../../app.styles"; //Типовые стили
|
|||||||
|
|
||||||
//Варианты исполнения
|
//Варианты исполнения
|
||||||
const P8P_APP_MESSAGE_VARIANT = {
|
const P8P_APP_MESSAGE_VARIANT = {
|
||||||
INFO: STATE.INFO,
|
INFO: "information",
|
||||||
WARN: STATE.WARN,
|
WARN: "warning",
|
||||||
ERR: STATE.ERR
|
ERR: "error"
|
||||||
};
|
};
|
||||||
|
|
||||||
//Стили
|
//Стили
|
||||||
@ -38,35 +36,28 @@ const STYLES = {
|
|||||||
wordBreak: "break-word"
|
wordBreak: "break-word"
|
||||||
},
|
},
|
||||||
INFO: {
|
INFO: {
|
||||||
titleText: {
|
titleText: {},
|
||||||
color: APP_COLORS[STATE.INFO].contrColor
|
bodyText: {}
|
||||||
},
|
|
||||||
bodyText: {
|
|
||||||
color: APP_COLORS[STATE.INFO].contrColor
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
WARN: {
|
WARN: {
|
||||||
titleText: {
|
titleText: {
|
||||||
color: APP_COLORS[STATE.WARN].contrColor
|
color: "orange"
|
||||||
},
|
},
|
||||||
bodyText: {
|
bodyText: {
|
||||||
color: APP_COLORS[STATE.WARN].contrColor
|
color: "orange"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ERR: {
|
ERR: {
|
||||||
titleText: {
|
titleText: {
|
||||||
color: APP_COLORS[STATE.ERR].contrColor
|
color: "red"
|
||||||
},
|
},
|
||||||
bodyText: {
|
bodyText: {
|
||||||
color: APP_COLORS[STATE.ERR].contrColor
|
color: "red"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
INLINE_MESSAGE: {
|
INLINE_MESSAGE: {
|
||||||
with: "100%",
|
with: "100%",
|
||||||
textAlign: "center"
|
textAlign: "center"
|
||||||
},
|
|
||||||
FULL_ERROR_TEXT_BUTTON: {
|
|
||||||
color: APP_COLORS[STATE.WARN].contrColor
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -75,25 +66,7 @@ const STYLES = {
|
|||||||
//-----------
|
//-----------
|
||||||
|
|
||||||
//Сообщение
|
//Сообщение
|
||||||
const P8PAppMessage = ({
|
const P8PAppMessage = ({ variant, title, titleText, cancelBtn, onCancel, cancelBtnCaption, okBtn, onOk, okBtnCaption, open, text }) => {
|
||||||
variant,
|
|
||||||
title,
|
|
||||||
titleText,
|
|
||||||
cancelBtn,
|
|
||||||
onCancel,
|
|
||||||
cancelBtnCaption,
|
|
||||||
okBtn,
|
|
||||||
onOk,
|
|
||||||
okBtnCaption,
|
|
||||||
open,
|
|
||||||
text,
|
|
||||||
fullErrorText,
|
|
||||||
showErrMoreCaption,
|
|
||||||
hideErrMoreCaption
|
|
||||||
}) => {
|
|
||||||
//Состояние подробной информации об ошибке
|
|
||||||
const [showFullErrorText, setShowFullErrorText] = useState(false);
|
|
||||||
|
|
||||||
//Подбор стиля и ресурсов
|
//Подбор стиля и ресурсов
|
||||||
let style = STYLES.INFO;
|
let style = STYLES.INFO;
|
||||||
switch (variant) {
|
switch (variant) {
|
||||||
@ -113,7 +86,12 @@ const P8PAppMessage = ({
|
|||||||
|
|
||||||
//Заголовок
|
//Заголовок
|
||||||
let titlePart;
|
let titlePart;
|
||||||
if (title && titleText) titlePart = <DialogTitle style={{ ...style.DEFAULT, ...style.titleText }}>{titleText}</DialogTitle>;
|
if (title && titleText)
|
||||||
|
titlePart = (
|
||||||
|
<DialogTitle id="message-dialog-title" style={{ ...style.DEFAULT, ...style.titleText }}>
|
||||||
|
{titleText}
|
||||||
|
</DialogTitle>
|
||||||
|
);
|
||||||
|
|
||||||
//Кнопка Отмена
|
//Кнопка Отмена
|
||||||
let cancelBtnPart;
|
let cancelBtnPart;
|
||||||
@ -124,26 +102,16 @@ const P8PAppMessage = ({
|
|||||||
let okBtnPart;
|
let okBtnPart;
|
||||||
if (okBtn && okBtnCaption)
|
if (okBtn && okBtnCaption)
|
||||||
okBtnPart = (
|
okBtnPart = (
|
||||||
<Button onClick={() => (onOk ? onOk() : null)} autoFocus>
|
<Button onClick={() => (onOk ? onOk() : null)} color="primary" autoFocus>
|
||||||
{okBtnCaption}
|
{okBtnCaption}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
|
||||||
//Кнопка Подробнее
|
|
||||||
let fullErrorTextBtn;
|
|
||||||
if (fullErrorText && showErrMoreCaption && hideErrMoreCaption && variant === P8P_APP_MESSAGE_VARIANT.ERR)
|
|
||||||
fullErrorTextBtn = (
|
|
||||||
<Button onClick={() => setShowFullErrorText(!showFullErrorText)} sx={STYLES.FULL_ERROR_TEXT_BUTTON} autoFocus>
|
|
||||||
{!showFullErrorText ? showErrMoreCaption : hideErrMoreCaption}
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
|
|
||||||
//Все действия
|
//Все действия
|
||||||
let actionsPart;
|
let actionsPart;
|
||||||
if (cancelBtnPart || okBtnPart)
|
if (cancelBtnPart || okBtnPart)
|
||||||
actionsPart = (
|
actionsPart = (
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
{fullErrorTextBtn}
|
|
||||||
{okBtnPart}
|
{okBtnPart}
|
||||||
{cancelBtnPart}
|
{cancelBtnPart}
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
@ -151,10 +119,17 @@ const P8PAppMessage = ({
|
|||||||
|
|
||||||
//Генерация содержимого
|
//Генерация содержимого
|
||||||
return (
|
return (
|
||||||
<Dialog open={open || false} onClose={() => (onCancel ? onCancel() : null)}>
|
<Dialog
|
||||||
|
open={open || false}
|
||||||
|
aria-labelledby="message-dialog-title"
|
||||||
|
aria-describedby="message-dialog-description"
|
||||||
|
onClose={() => (onCancel ? onCancel() : null)}
|
||||||
|
>
|
||||||
{titlePart}
|
{titlePart}
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogContentText style={style.bodyText}>{!showFullErrorText ? text : fullErrorText}</DialogContentText>
|
<DialogContentText id="message-dialog-description" style={style.bodyText}>
|
||||||
|
{text}
|
||||||
|
</DialogContentText>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
{actionsPart}
|
{actionsPart}
|
||||||
</Dialog>
|
</Dialog>
|
||||||
@ -173,10 +148,7 @@ P8PAppMessage.propTypes = {
|
|||||||
onOk: PropTypes.func,
|
onOk: PropTypes.func,
|
||||||
okBtnCaption: PropTypes.string,
|
okBtnCaption: PropTypes.string,
|
||||||
open: PropTypes.bool,
|
open: PropTypes.bool,
|
||||||
text: PropTypes.string,
|
text: PropTypes.string
|
||||||
fullErrorText: PropTypes.string,
|
|
||||||
showErrMoreCaption: PropTypes.string,
|
|
||||||
hideErrMoreCaption: PropTypes.string
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//Встроенное сообщение
|
//Встроенное сообщение
|
||||||
@ -186,19 +158,13 @@ const P8PAppInlineMessage = ({ variant, text, okBtn, onOk, okBtnCaption }) => {
|
|||||||
<Container style={STYLES.INLINE_MESSAGE}>
|
<Container style={STYLES.INLINE_MESSAGE}>
|
||||||
<Box p={1}>
|
<Box p={1}>
|
||||||
<Typography
|
<Typography
|
||||||
color={
|
color={variant === P8P_APP_MESSAGE_VARIANT.ERR ? "error" : variant === P8P_APP_MESSAGE_VARIANT.WARN ? "primary" : "textSecondary"}
|
||||||
variant === P8P_APP_MESSAGE_VARIANT.ERR
|
|
||||||
? APP_COLORS[STATE.ERR].contrColor
|
|
||||||
: variant === P8P_APP_MESSAGE_VARIANT.WARN
|
|
||||||
? APP_COLORS[STATE.WARN].contrColor
|
|
||||||
: APP_COLORS[STATE.INFO].contrColor
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{text}
|
{text}
|
||||||
</Typography>
|
</Typography>
|
||||||
{okBtn && okBtnCaption ? (
|
{okBtn && okBtnCaption ? (
|
||||||
<Box pt={1}>
|
<Box pt={1}>
|
||||||
<Button onClick={() => (onOk ? onOk() : null)} autoFocus>
|
<Button onClick={() => (onOk ? onOk() : null)} color="primary" autoFocus>
|
||||||
{okBtnCaption}
|
{okBtnCaption}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
@ -250,28 +216,6 @@ const P8PAppInlineWarn = props => buildVariantInlineMessage(props, P8P_APP_MESSA
|
|||||||
//Встраиваемое сообщение информации
|
//Встраиваемое сообщение информации
|
||||||
const P8PAppInlineInfo = props => buildVariantInlineMessage(props, P8P_APP_MESSAGE_VARIANT.INFO);
|
const P8PAppInlineInfo = props => buildVariantInlineMessage(props, P8P_APP_MESSAGE_VARIANT.INFO);
|
||||||
|
|
||||||
//Диалог подсказки
|
|
||||||
const P8PHintDialog = ({ title, hint, onOk }) => {
|
|
||||||
return (
|
|
||||||
<Dialog open={true} onClose={() => (onOk ? onOk() : null)}>
|
|
||||||
<DialogTitle>{title}</DialogTitle>
|
|
||||||
<DialogContent>
|
|
||||||
<div dangerouslySetInnerHTML={{ __html: hint }}></div>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button onClick={() => (onOk ? onOk() : null)}>{BUTTONS.OK}</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств - Диалог подсказки
|
|
||||||
P8PHintDialog.propTypes = {
|
|
||||||
title: PropTypes.string.isRequired,
|
|
||||||
hint: PropTypes.string.isRequired,
|
|
||||||
onOk: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
//----------------
|
||||||
//Интерфейс модуля
|
//Интерфейс модуля
|
||||||
//----------------
|
//----------------
|
||||||
@ -285,6 +229,5 @@ export {
|
|||||||
P8PAppInlineMessage,
|
P8PAppInlineMessage,
|
||||||
P8PAppInlineError,
|
P8PAppInlineError,
|
||||||
P8PAppInlineWarn,
|
P8PAppInlineWarn,
|
||||||
P8PAppInlineInfo,
|
P8PAppInlineInfo
|
||||||
P8PHintDialog
|
|
||||||
};
|
};
|
||||||
|
@ -47,7 +47,7 @@ const STYLES = {
|
|||||||
//-----------
|
//-----------
|
||||||
|
|
||||||
//Рабочее пространство
|
//Рабочее пространство
|
||||||
const P8PAppWorkspace = ({ children, panels = [], selectedPanel, caption, closeCaption, homeCaption, onHomeNavigate, onItemNavigate } = {}) => {
|
const P8PAppWorkspace = ({ children, panels = [], selectedPanel, closeCaption, homeCaption, onHomeNavigate, onItemNavigate } = {}) => {
|
||||||
//Собственное состояния
|
//Собственное состояния
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ const P8PAppWorkspace = ({ children, panels = [], selectedPanel, caption, closeC
|
|||||||
<Icon>{open ? "chevron_left" : "menu"}</Icon>
|
<Icon>{open ? "chevron_left" : "menu"}</Icon>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<Typography variant="h6" noWrap component="div">
|
<Typography variant="h6" noWrap component="div">
|
||||||
{caption || selectedPanel?.caption}
|
{selectedPanel?.caption}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
@ -120,7 +120,6 @@ P8PAppWorkspace.propTypes = {
|
|||||||
children: PropTypes.element,
|
children: PropTypes.element,
|
||||||
panels: PropTypes.arrayOf(P8P_PANELS_MENU_PANEL_SHAPE).isRequired,
|
panels: PropTypes.arrayOf(P8P_PANELS_MENU_PANEL_SHAPE).isRequired,
|
||||||
selectedPanel: P8P_PANELS_MENU_PANEL_SHAPE,
|
selectedPanel: P8P_PANELS_MENU_PANEL_SHAPE,
|
||||||
caption: PropTypes.string,
|
|
||||||
closeCaption: PropTypes.string.isRequired,
|
closeCaption: PropTypes.string.isRequired,
|
||||||
homeCaption: PropTypes.string.isRequired,
|
homeCaption: PropTypes.string.isRequired,
|
||||||
onHomeNavigate: PropTypes.func,
|
onHomeNavigate: PropTypes.func,
|
||||||
|
@ -36,7 +36,6 @@ const P8P_DATA_GRID_FILTERS_HEIGHT = P8P_TABLE_FILTERS_HEIGHT;
|
|||||||
|
|
||||||
//Таблица данных
|
//Таблица данных
|
||||||
const P8PDataGrid = ({
|
const P8PDataGrid = ({
|
||||||
style = {},
|
|
||||||
columnsDef = [],
|
columnsDef = [],
|
||||||
filtersInitial,
|
filtersInitial,
|
||||||
groups = [],
|
groups = [],
|
||||||
@ -115,7 +114,6 @@ const P8PDataGrid = ({
|
|||||||
//Генерация содержимого
|
//Генерация содержимого
|
||||||
return (
|
return (
|
||||||
<P8PTable
|
<P8PTable
|
||||||
style={style}
|
|
||||||
columnsDef={columnsDef}
|
columnsDef={columnsDef}
|
||||||
groups={groups}
|
groups={groups}
|
||||||
rows={rows}
|
rows={rows}
|
||||||
@ -156,7 +154,6 @@ const P8PDataGrid = ({
|
|||||||
|
|
||||||
//Контроль свойств - Таблица данных
|
//Контроль свойств - Таблица данных
|
||||||
P8PDataGrid.propTypes = {
|
P8PDataGrid.propTypes = {
|
||||||
style: PropTypes.object,
|
|
||||||
columnsDef: PropTypes.array,
|
columnsDef: PropTypes.array,
|
||||||
filtersInitial: PropTypes.arrayOf(P8P_DATA_GRID_FILTER_SHAPE),
|
filtersInitial: PropTypes.arrayOf(P8P_DATA_GRID_FILTER_SHAPE),
|
||||||
groups: PropTypes.array,
|
groups: PropTypes.array,
|
||||||
|
@ -139,7 +139,6 @@ const P8PGanttTaskEditor = ({
|
|||||||
onCancel,
|
onCancel,
|
||||||
taskAttributeRenderer,
|
taskAttributeRenderer,
|
||||||
taskDialogRenderer,
|
taskDialogRenderer,
|
||||||
taskDialogProps,
|
|
||||||
numbCaption,
|
numbCaption,
|
||||||
nameCaption,
|
nameCaption,
|
||||||
startCaption,
|
startCaption,
|
||||||
@ -187,7 +186,7 @@ const P8PGanttTaskEditor = ({
|
|||||||
|
|
||||||
//Генерация содержимого
|
//Генерация содержимого
|
||||||
return (
|
return (
|
||||||
<Dialog open onClose={handleCancel} {...(taskDialogProps ? taskDialogProps : {})}>
|
<Dialog open onClose={handleCancel}>
|
||||||
{taskDialogRenderer ? (
|
{taskDialogRenderer ? (
|
||||||
taskDialogRenderer({ task, taskAttributes, taskColors, close: handleCancel })
|
taskDialogRenderer({ task, taskAttributes, taskColors, close: handleCancel })
|
||||||
) : (
|
) : (
|
||||||
@ -316,7 +315,6 @@ P8PGanttTaskEditor.propTypes = {
|
|||||||
onCancel: PropTypes.func,
|
onCancel: PropTypes.func,
|
||||||
taskAttributeRenderer: PropTypes.func,
|
taskAttributeRenderer: PropTypes.func,
|
||||||
taskDialogRenderer: PropTypes.func,
|
taskDialogRenderer: PropTypes.func,
|
||||||
taskDialogProps: PropTypes.object,
|
|
||||||
numbCaption: PropTypes.string.isRequired,
|
numbCaption: PropTypes.string.isRequired,
|
||||||
nameCaption: PropTypes.string.isRequired,
|
nameCaption: PropTypes.string.isRequired,
|
||||||
startCaption: PropTypes.string.isRequired,
|
startCaption: PropTypes.string.isRequired,
|
||||||
@ -349,7 +347,6 @@ const P8PGantt = ({
|
|||||||
onTaskProgressChange,
|
onTaskProgressChange,
|
||||||
taskAttributeRenderer,
|
taskAttributeRenderer,
|
||||||
taskDialogRenderer,
|
taskDialogRenderer,
|
||||||
taskDialogProps,
|
|
||||||
noDataFoundText,
|
noDataFoundText,
|
||||||
numbTaskEditorCaption,
|
numbTaskEditorCaption,
|
||||||
nameTaskEditorCaption,
|
nameTaskEditorCaption,
|
||||||
@ -470,7 +467,6 @@ const P8PGantt = ({
|
|||||||
onCancel={handleTaskEditorCancel}
|
onCancel={handleTaskEditorCancel}
|
||||||
taskAttributeRenderer={taskAttributeRenderer}
|
taskAttributeRenderer={taskAttributeRenderer}
|
||||||
taskDialogRenderer={taskDialogRenderer}
|
taskDialogRenderer={taskDialogRenderer}
|
||||||
taskDialogProps={taskDialogProps}
|
|
||||||
numbCaption={numbTaskEditorCaption}
|
numbCaption={numbTaskEditorCaption}
|
||||||
nameCaption={nameTaskEditorCaption}
|
nameCaption={nameTaskEditorCaption}
|
||||||
startCaption={startTaskEditorCaption}
|
startCaption={startTaskEditorCaption}
|
||||||
@ -506,7 +502,6 @@ P8PGantt.propTypes = {
|
|||||||
onTaskProgressChange: PropTypes.func,
|
onTaskProgressChange: PropTypes.func,
|
||||||
taskAttributeRenderer: PropTypes.func,
|
taskAttributeRenderer: PropTypes.func,
|
||||||
taskDialogRenderer: PropTypes.func,
|
taskDialogRenderer: PropTypes.func,
|
||||||
taskDialogProps: PropTypes.object,
|
|
||||||
noDataFoundText: PropTypes.string.isRequired,
|
noDataFoundText: PropTypes.string.isRequired,
|
||||||
numbTaskEditorCaption: PropTypes.string.isRequired,
|
numbTaskEditorCaption: PropTypes.string.isRequired,
|
||||||
nameTaskEditorCaption: PropTypes.string.isRequired,
|
nameTaskEditorCaption: PropTypes.string.isRequired,
|
||||||
|
@ -1,186 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга
|
|
||||||
Компонент: Индикатор
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React, { useState } from "react"; //Классы React
|
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
|
||||||
import { IconButton, Icon, Typography, Paper, Stack } from "@mui/material"; //Интерфейсные компоненты MUI
|
|
||||||
import { P8PHintDialog } from "./p8p_app_message"; //Диалог подсказки
|
|
||||||
import { TEXTS, STATE } from "../../app.text"; //Типовые текстовые ресурсы и константы
|
|
||||||
import { APP_COLORS } from "../../app.styles"; //Типовые стили
|
|
||||||
|
|
||||||
//---------
|
|
||||||
//Константы
|
|
||||||
//---------
|
|
||||||
|
|
||||||
//Варианты исполнения
|
|
||||||
|
|
||||||
const P8P_INDICATOR_VARIANT = {
|
|
||||||
ELEVATION: "elevation",
|
|
||||||
OUTLINED: "outlined"
|
|
||||||
};
|
|
||||||
|
|
||||||
//Состояния
|
|
||||||
const P8P_INDICATOR_STATE = {
|
|
||||||
UNDEFINED: STATE.UNDEFINED,
|
|
||||||
OK: STATE.OK,
|
|
||||||
WARN: STATE.WARN,
|
|
||||||
ERR: STATE.ERR
|
|
||||||
};
|
|
||||||
//Цвета заливки
|
|
||||||
const BG_COLOR = {
|
|
||||||
[STATE.OK]: APP_COLORS[STATE.OK].color,
|
|
||||||
[STATE.ERR]: APP_COLORS[STATE.ERR].color,
|
|
||||||
[STATE.WARN]: APP_COLORS[STATE.WARN].color
|
|
||||||
};
|
|
||||||
|
|
||||||
//Цвета текста и иконок
|
|
||||||
const COLOR = {
|
|
||||||
[STATE.OK]: APP_COLORS[STATE.OK].contrColor,
|
|
||||||
[STATE.ERR]: APP_COLORS[STATE.ERR].contrColor,
|
|
||||||
[STATE.WARN]: APP_COLORS[STATE.WARN].contrColor
|
|
||||||
};
|
|
||||||
|
|
||||||
//Стили
|
|
||||||
const STYLES = {
|
|
||||||
CONTAINER: (state, clickable, userColor, userBackgroundColor) => ({
|
|
||||||
padding: "10px",
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
overflow: "hidden",
|
|
||||||
...getBackgroundColor(state, userBackgroundColor),
|
|
||||||
...getColor(state, userColor),
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
justifyContent: "center",
|
|
||||||
...(clickable
|
|
||||||
? {
|
|
||||||
cursor: "pointer",
|
|
||||||
"&:hover": { backgroundColor: APP_COLORS.HOVER.color },
|
|
||||||
"&:active": { backgroundColor: APP_COLORS.ACTIVE.color }
|
|
||||||
}
|
|
||||||
: {})
|
|
||||||
}),
|
|
||||||
ICON: (state, userColor) => ({ fontSize: "50px", ...getColor(state, userColor) }),
|
|
||||||
HINT_ICON: (state, userColor) => ({ fontSize: "1rem", ...getColor(state, userColor) }),
|
|
||||||
VALUE_CAPTION_STACK: { containerType: "inline-size", width: "100%", overflow: "hidden" },
|
|
||||||
CAPTION_TYPOGRAPHY: { width: "99cqw" }
|
|
||||||
};
|
|
||||||
|
|
||||||
//-----------------------
|
|
||||||
//Вспомогательные функции
|
|
||||||
//-----------------------
|
|
||||||
|
|
||||||
//Подбор цвета заливки
|
|
||||||
const getBackgroundColor = (state, userColor) =>
|
|
||||||
userColor ? { backgroundColor: userColor } : BG_COLOR[state] ? { backgroundColor: BG_COLOR[state] } : {};
|
|
||||||
|
|
||||||
//Подбор цвета текста
|
|
||||||
const getColor = (state, userColor) => (userColor ? { color: userColor } : COLOR[state] ? { color: COLOR[state] } : {});
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Индикатор
|
|
||||||
const P8PIndicator = ({
|
|
||||||
caption,
|
|
||||||
value,
|
|
||||||
icon = null,
|
|
||||||
state = STATE.UNDEFINED,
|
|
||||||
square = false,
|
|
||||||
elevation = 3,
|
|
||||||
variant = P8P_INDICATOR_VARIANT.ELEVATION,
|
|
||||||
hint = null,
|
|
||||||
onClick = null,
|
|
||||||
backgroundColor = null,
|
|
||||||
color = null
|
|
||||||
} = {}) => {
|
|
||||||
//Собственное состояние - отображение окна подсказки
|
|
||||||
const [showHint, setShowHint] = useState(false);
|
|
||||||
|
|
||||||
//При нажатии на индикатор
|
|
||||||
const handleClick = () => (onClick && !showHint ? onClick() : null);
|
|
||||||
|
|
||||||
//При нажатии на кнопку получения подсказки
|
|
||||||
const handleHintClick = e => {
|
|
||||||
setShowHint(true);
|
|
||||||
e.stopPropagation();
|
|
||||||
};
|
|
||||||
|
|
||||||
//При нажатии на кнопку закрытия подсказки
|
|
||||||
const handleHintClose = () => setShowHint(false);
|
|
||||||
|
|
||||||
//Представление текста значения индикатора
|
|
||||||
const valueTextView = <Typography variant={"h4"}>{[undefined, null, ""].includes(value) ? TEXTS.NO_DATA_FOUND_SHORT : value}</Typography>;
|
|
||||||
|
|
||||||
//Представление текста подписи индикатора
|
|
||||||
const captionView = (
|
|
||||||
<Typography align={"left"} noWrap={true} sx={STYLES.CAPTION_TYPOGRAPHY} title={caption}>
|
|
||||||
{caption}
|
|
||||||
</Typography>
|
|
||||||
);
|
|
||||||
|
|
||||||
//Представление подписи индикатора
|
|
||||||
const valueView = hint ? (
|
|
||||||
<>
|
|
||||||
{showHint && <P8PHintDialog title={caption} hint={hint} onOk={handleHintClose} />}
|
|
||||||
<Stack direction={"row"} alignItems={"start"}>
|
|
||||||
{valueTextView}
|
|
||||||
<IconButton onClick={handleHintClick}>
|
|
||||||
<Icon sx={STYLES.HINT_ICON(state, color)}>help_outline</Icon>
|
|
||||||
</IconButton>
|
|
||||||
</Stack>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
valueTextView
|
|
||||||
);
|
|
||||||
|
|
||||||
//Флаг активности индикатора
|
|
||||||
const clickable = onClick ? true : false;
|
|
||||||
|
|
||||||
//Представление
|
|
||||||
return (
|
|
||||||
<Paper
|
|
||||||
elevation={variant === P8P_INDICATOR_VARIANT.ELEVATION ? elevation : 0}
|
|
||||||
sx={STYLES.CONTAINER(state, clickable, color, backgroundColor)}
|
|
||||||
square={square}
|
|
||||||
variant={variant}
|
|
||||||
onClick={handleClick}
|
|
||||||
>
|
|
||||||
<Stack direction={"row"} alignItems={"center"} justifyContent={"space-between"}>
|
|
||||||
<Stack direction={"column"} alignItems={"start"} pr={2} sx={STYLES.VALUE_CAPTION_STACK}>
|
|
||||||
{valueView}
|
|
||||||
{captionView}
|
|
||||||
</Stack>
|
|
||||||
{icon ? <Icon sx={STYLES.ICON(state, color)}>{icon}</Icon> : null}
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств - Индикатор
|
|
||||||
P8PIndicator.propTypes = {
|
|
||||||
caption: PropTypes.string.isRequired,
|
|
||||||
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
||||||
icon: PropTypes.string,
|
|
||||||
state: PropTypes.oneOf(Object.values(P8P_INDICATOR_STATE)),
|
|
||||||
square: PropTypes.bool,
|
|
||||||
elevation: PropTypes.number,
|
|
||||||
variant: PropTypes.oneOf(Object.values(P8P_INDICATOR_VARIANT)),
|
|
||||||
hint: PropTypes.string,
|
|
||||||
onClick: PropTypes.func,
|
|
||||||
backgroundColor: PropTypes.string,
|
|
||||||
color: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export { P8P_INDICATOR_VARIANT, P8P_INDICATOR_STATE, P8PIndicator };
|
|
@ -34,7 +34,7 @@ import {
|
|||||||
Link
|
Link
|
||||||
} from "@mui/material"; //Интерфейсные компоненты
|
} from "@mui/material"; //Интерфейсные компоненты
|
||||||
import { useTheme } from "@mui/material/styles"; //Взаимодействие со стилями MUI
|
import { useTheme } from "@mui/material/styles"; //Взаимодействие со стилями MUI
|
||||||
import { P8PAppInlineError, P8PHintDialog } from "./p8p_app_message"; //Встраиваемое сообщение об ошибке
|
import { P8PAppInlineError } from "./p8p_app_message"; //Встраиваемое сообщение об ошибке
|
||||||
import { P8P_TABLE_AT, HEADER_INITIAL_STATE, hasValue, p8pTableReducer } from "./p8p_table_reducer"; //Редьюсер состояния
|
import { P8P_TABLE_AT, HEADER_INITIAL_STATE, hasValue, p8pTableReducer } from "./p8p_table_reducer"; //Редьюсер состояния
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
@ -89,7 +89,9 @@ const P8P_TABLE_FILTERS_HEIGHT = "48px";
|
|||||||
|
|
||||||
//Стили
|
//Стили
|
||||||
const STYLES = {
|
const STYLES = {
|
||||||
TABLE: {},
|
TABLE: {
|
||||||
|
with: "100%"
|
||||||
|
},
|
||||||
TABLE_HEAD_STICKY: {
|
TABLE_HEAD_STICKY: {
|
||||||
position: "sticky",
|
position: "sticky",
|
||||||
top: 0,
|
top: 0,
|
||||||
@ -288,6 +290,28 @@ P8PTableColumnMenu.propTypes = {
|
|||||||
onItemClick: PropTypes.func
|
onItemClick: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//Диалог подсказки
|
||||||
|
const P8PTableColumnHintDialog = ({ columnDef, okBtnCaption, onOk }) => {
|
||||||
|
return (
|
||||||
|
<Dialog open={true} aria-labelledby="filter-dialog-title" aria-describedby="filter-dialog-description" onClose={() => (onOk ? onOk() : null)}>
|
||||||
|
<DialogTitle id="filter-dialog-title">{columnDef.caption}</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<div dangerouslySetInnerHTML={{ __html: columnDef.hint }}></div>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={() => (onOk ? onOk() : null)}>{okBtnCaption}</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств - Диалог подсказки
|
||||||
|
P8PTableColumnHintDialog.propTypes = {
|
||||||
|
columnDef: PropTypes.object.isRequired,
|
||||||
|
okBtnCaption: PropTypes.string.isRequired,
|
||||||
|
onOk: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
//Диалог фильтра
|
//Диалог фильтра
|
||||||
const P8PTableColumnFilterDialog = ({
|
const P8PTableColumnFilterDialog = ({
|
||||||
columnDef,
|
columnDef,
|
||||||
@ -464,7 +488,6 @@ P8PTableFiltersChips.propTypes = {
|
|||||||
|
|
||||||
//Таблица
|
//Таблица
|
||||||
const P8PTable = ({
|
const P8PTable = ({
|
||||||
style = {},
|
|
||||||
columnsDef = [],
|
columnsDef = [],
|
||||||
groups = [],
|
groups = [],
|
||||||
rows = [],
|
rows = [],
|
||||||
@ -679,8 +702,10 @@ const P8PTable = ({
|
|||||||
|
|
||||||
//Генерация содержимого
|
//Генерация содержимого
|
||||||
return (
|
return (
|
||||||
<div style={{ ...(style || {}) }}>
|
<div>
|
||||||
{displayHintColumn ? <P8PHintDialog title={displayHintColumnDef.caption} hint={displayHintColumnDef.hint} onOk={handleHintOk} /> : null}
|
{displayHintColumn ? (
|
||||||
|
<P8PTableColumnHintDialog columnDef={displayHintColumnDef} okBtnCaption={okFilterBtnCaption} onOk={handleHintOk} />
|
||||||
|
) : null}
|
||||||
{filterColumn ? (
|
{filterColumn ? (
|
||||||
<P8PTableColumnFilterDialog
|
<P8PTableColumnFilterDialog
|
||||||
columnDef={filterColumnDef}
|
columnDef={filterColumnDef}
|
||||||
@ -875,7 +900,6 @@ const P8PTable = ({
|
|||||||
|
|
||||||
//Контроль свойств - Таблица
|
//Контроль свойств - Таблица
|
||||||
P8PTable.propTypes = {
|
P8PTable.propTypes = {
|
||||||
style: PropTypes.object,
|
|
||||||
columnsDef: PropTypes.arrayOf(
|
columnsDef: PropTypes.arrayOf(
|
||||||
PropTypes.shape({
|
PropTypes.shape({
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
|
@ -56,9 +56,6 @@ export const ApplicationContext = ({ errors, displaySizeGetter, guidGenerator, c
|
|||||||
//Установка списка панелей
|
//Установка списка панелей
|
||||||
const setPanels = panels => dispatch({ type: APP_AT.LOAD_PANELS, payload: panels });
|
const setPanels = panels => dispatch({ type: APP_AT.LOAD_PANELS, payload: panels });
|
||||||
|
|
||||||
//Установка заголовка в шапке приложения
|
|
||||||
const setAppBarTitle = appBarTitle => dispatch({ type: APP_AT.SET_APP_BAR_TITLE, payload: appBarTitle });
|
|
||||||
|
|
||||||
//Поиск раздела по имени
|
//Поиск раздела по имени
|
||||||
const findPanelByName = name => state.panels.find(panel => panel.name == name);
|
const findPanelByName = name => state.panels.find(panel => panel.name == name);
|
||||||
|
|
||||||
@ -172,7 +169,6 @@ export const ApplicationContext = ({ errors, displaySizeGetter, guidGenerator, c
|
|||||||
return (
|
return (
|
||||||
<ApplicationСtx.Provider
|
<ApplicationСtx.Provider
|
||||||
value={{
|
value={{
|
||||||
setAppBarTitle,
|
|
||||||
findPanelByName,
|
findPanelByName,
|
||||||
pOnlineShowTab,
|
pOnlineShowTab,
|
||||||
pOnlineShowUnit,
|
pOnlineShowUnit,
|
||||||
|
@ -12,14 +12,12 @@ const APP_AT = {
|
|||||||
SET_URL_BASE: "SET_URL_BASE", //Установка базового URL приложения
|
SET_URL_BASE: "SET_URL_BASE", //Установка базового URL приложения
|
||||||
LOAD_PANELS: "LOAD_PANELS", //Загрузка списка панелей
|
LOAD_PANELS: "LOAD_PANELS", //Загрузка списка панелей
|
||||||
SET_INITIALIZED: "SET_INITIALIZED", //Установка флага инициализированности приложения
|
SET_INITIALIZED: "SET_INITIALIZED", //Установка флага инициализированности приложения
|
||||||
SET_DISPLAY_SIZE: "SET_DISPLAY_SIZE", //Установка текущего типового размера экрана
|
SET_DISPLAY_SIZE: "SET_DISPLAY_SIZE" //Установка текущего типового размера экрана
|
||||||
SET_APP_BAR_TITLE: "SET_APP_BAR_TITLE" //Установка заголовка в шапке приложения
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//Состояние приложения по умолчанию
|
//Состояние приложения по умолчанию
|
||||||
const INITIAL_STATE = displaySizeGetter => ({
|
const INITIAL_STATE = displaySizeGetter => ({
|
||||||
displaySize: displaySizeGetter(),
|
displaySize: displaySizeGetter(),
|
||||||
appBarTitle: "",
|
|
||||||
urlBase: "",
|
urlBase: "",
|
||||||
panels: [],
|
panels: [],
|
||||||
panelsLoaded: false,
|
panelsLoaded: false,
|
||||||
@ -48,8 +46,6 @@ const handlers = {
|
|||||||
[APP_AT.SET_INITIALIZED]: state => ({ ...state, initialized: true }),
|
[APP_AT.SET_INITIALIZED]: state => ({ ...state, initialized: true }),
|
||||||
//Установка текущего типового размера экрана
|
//Установка текущего типового размера экрана
|
||||||
[APP_AT.SET_DISPLAY_SIZE]: (state, { payload }) => ({ ...state, displaySize: payload }),
|
[APP_AT.SET_DISPLAY_SIZE]: (state, { payload }) => ({ ...state, displaySize: payload }),
|
||||||
//Установка заголовка в шапке приложения
|
|
||||||
[APP_AT.SET_APP_BAR_TITLE]: (state, { payload }) => ({ ...state, appBarTitle: payload }),
|
|
||||||
//Обработчик по умолчанию
|
//Обработчик по умолчанию
|
||||||
DEFAULT: state => state
|
DEFAULT: state => state
|
||||||
};
|
};
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
import React, { createContext, useContext, useCallback } from "react"; //ReactJS
|
import React, { createContext, useContext, useCallback } from "react"; //ReactJS
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import { MessagingСtx } from "./messaging"; //Контекст сообщений
|
import { MessagingСtx } from "./messaging"; //Контекст сообщений
|
||||||
import { formatErrorMessage } from "../core/utils"; //Вспомогательные функции
|
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
//Константы
|
//Константы
|
||||||
@ -64,8 +63,7 @@ export const BackEndContext = ({ client, children }) => {
|
|||||||
throwError = true,
|
throwError = true,
|
||||||
showErrorMessage = true,
|
showErrorMessage = true,
|
||||||
fullResponse = false,
|
fullResponse = false,
|
||||||
spreadOutArguments = true,
|
spreadOutArguments = true
|
||||||
signal = null
|
|
||||||
} = {}) => {
|
} = {}) => {
|
||||||
try {
|
try {
|
||||||
if (loader !== false) showLoader(loaderMessage);
|
if (loader !== false) showLoader(loaderMessage);
|
||||||
@ -77,18 +75,12 @@ export const BackEndContext = ({ client, children }) => {
|
|||||||
tagValueProcessor,
|
tagValueProcessor,
|
||||||
attributeValueProcessor,
|
attributeValueProcessor,
|
||||||
throwError,
|
throwError,
|
||||||
spreadOutArguments,
|
spreadOutArguments
|
||||||
signal
|
|
||||||
});
|
});
|
||||||
if (fullResponse === true || isRespErr(result)) return result;
|
if (fullResponse === true || isRespErr(result)) return result;
|
||||||
else return result.XPAYLOAD;
|
else return result.XPAYLOAD;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (showErrorMessage) {
|
if (showErrorMessage) showMsgErr(e.message);
|
||||||
//Разбираем текст ошибки
|
|
||||||
let errMsg = formatErrorMessage(e.message);
|
|
||||||
//Отображаем ошибку
|
|
||||||
showMsgErr(errMsg.text, null, errMsg.fullErrorText);
|
|
||||||
}
|
|
||||||
throw e;
|
throw e;
|
||||||
} finally {
|
} finally {
|
||||||
if (loader !== false) hideLoader();
|
if (loader !== false) hideLoader();
|
||||||
|
@ -33,9 +33,7 @@ const MESSAGING_CONTEXT_TEXTS_SHAPE = PropTypes.shape({
|
|||||||
const MESSAGING_CONTEXT_BUTTONS_SHAPE = PropTypes.shape({
|
const MESSAGING_CONTEXT_BUTTONS_SHAPE = PropTypes.shape({
|
||||||
CLOSE: PropTypes.string.isRequired,
|
CLOSE: PropTypes.string.isRequired,
|
||||||
OK: PropTypes.string.isRequired,
|
OK: PropTypes.string.isRequired,
|
||||||
CANCEL: PropTypes.string.isRequired,
|
CANCEL: PropTypes.string.isRequired
|
||||||
DETAIL: PropTypes.string.isRequired,
|
|
||||||
HIDE: PropTypes.string.isRequired
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//----------------
|
//----------------
|
||||||
@ -58,16 +56,12 @@ export const MessagingContext = ({ titles, texts, buttons, children }) => {
|
|||||||
|
|
||||||
//Отображение сообщения
|
//Отображение сообщения
|
||||||
const showMsg = useCallback(
|
const showMsg = useCallback(
|
||||||
(type, text, msgOnOk = null, msgOnCancel = null, fullErrorText = null) =>
|
(type, text, msgOnOk = null, msgOnCancel = null) => dispatch({ type: MSG_AT.SHOW_MSG, payload: { type, text, msgOnOk, msgOnCancel } }),
|
||||||
dispatch({ type: MSG_AT.SHOW_MSG, payload: { type, text, msgOnOk, msgOnCancel, fullErrorText } }),
|
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
//Отображение сообщения - ошибка
|
//Отображение сообщения - ошибка
|
||||||
const showMsgErr = useCallback(
|
const showMsgErr = useCallback((text, msgOnOk = null) => showMsg(MSG_TYPE.ERR, text, msgOnOk), [showMsg]);
|
||||||
(text, msgOnOk = null, fullErrorText = null) => showMsg(MSG_TYPE.ERR, text, msgOnOk, null, fullErrorText),
|
|
||||||
[showMsg]
|
|
||||||
);
|
|
||||||
|
|
||||||
//Отображение сообщения - информация
|
//Отображение сообщения - информация
|
||||||
const showMsgInfo = useCallback((text, msgOnOk = null) => showMsg(MSG_TYPE.INFO, text, msgOnOk), [showMsg]);
|
const showMsgInfo = useCallback((text, msgOnOk = null) => showMsg(MSG_TYPE.INFO, text, msgOnOk), [showMsg]);
|
||||||
@ -132,7 +126,6 @@ export const MessagingContext = ({ titles, texts, buttons, children }) => {
|
|||||||
open={true}
|
open={true}
|
||||||
variant={state.msgType}
|
variant={state.msgType}
|
||||||
text={state.msgText}
|
text={state.msgText}
|
||||||
fullErrorText={state.msgFullErrorText}
|
|
||||||
title
|
title
|
||||||
titleText={state.msgType == MSG_TYPE.ERR ? titles.ERR : state.msgType == MSG_TYPE.WARN ? titles.WARN : titles.INFO}
|
titleText={state.msgType == MSG_TYPE.ERR ? titles.ERR : state.msgType == MSG_TYPE.WARN ? titles.WARN : titles.INFO}
|
||||||
okBtn={true}
|
okBtn={true}
|
||||||
@ -141,8 +134,6 @@ export const MessagingContext = ({ titles, texts, buttons, children }) => {
|
|||||||
cancelBtn={state.msgType == MSG_TYPE.WARN}
|
cancelBtn={state.msgType == MSG_TYPE.WARN}
|
||||||
onCancel={handleMessageCancelClick}
|
onCancel={handleMessageCancelClick}
|
||||||
cancelBtnCaption={buttons.CANCEL}
|
cancelBtnCaption={buttons.CANCEL}
|
||||||
showErrMoreCaption={buttons.DETAIL}
|
|
||||||
hideErrMoreCaption={buttons.HIDE}
|
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{children}
|
{children}
|
||||||
|
@ -35,7 +35,6 @@ const INITIAL_STATE = {
|
|||||||
msg: false,
|
msg: false,
|
||||||
msgType: MSG_TYPE.ERR,
|
msgType: MSG_TYPE.ERR,
|
||||||
msgText: null,
|
msgText: null,
|
||||||
msgFullErrorText: null,
|
|
||||||
msgOnOk: null,
|
msgOnOk: null,
|
||||||
msgOnCancel: null
|
msgOnCancel: null
|
||||||
};
|
};
|
||||||
@ -60,7 +59,6 @@ const handlers = {
|
|||||||
msg: true,
|
msg: true,
|
||||||
msgType: payload.type || MSG_TYPE.APP_ERR,
|
msgType: payload.type || MSG_TYPE.APP_ERR,
|
||||||
msgText: payload.text,
|
msgText: payload.text,
|
||||||
msgFullErrorText: payload.fullErrorText,
|
|
||||||
msgOnOk: payload.msgOnOk,
|
msgOnOk: payload.msgOnOk,
|
||||||
msgOnCancel: payload.msgOnCancel
|
msgOnCancel: payload.msgOnCancel
|
||||||
}),
|
}),
|
||||||
|
@ -41,7 +41,7 @@ export const NavigationContext = ({ children }) => {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
//Подключение к контексту приложения
|
//Подключение к контексту приложения
|
||||||
const { findPanelByName, setAppBarTitle } = useContext(ApplicationСtx);
|
const { findPanelByName } = useContext(ApplicationСtx);
|
||||||
|
|
||||||
//Проверка наличия параметров запроса
|
//Проверка наличия параметров запроса
|
||||||
const isNavigationSearch = () => (location.search ? true : false);
|
const isNavigationSearch = () => (location.search ? true : false);
|
||||||
@ -65,8 +65,6 @@ export const NavigationContext = ({ children }) => {
|
|||||||
const navigateTo = ({ path, search, state, replace = false }) => {
|
const navigateTo = ({ path, search, state, replace = false }) => {
|
||||||
//Если указано куда переходить
|
//Если указано куда переходить
|
||||||
if (path) {
|
if (path) {
|
||||||
//Сброс кастомного заголовка
|
|
||||||
setAppBarTitle("");
|
|
||||||
//Переходим к адресу
|
//Переходим к адресу
|
||||||
if (state) navigate(path, { state: JSON.stringify(state), replace });
|
if (state) navigate(path, { state: JSON.stringify(state), replace });
|
||||||
else navigate({ pathname: path, search: queryString.stringify(search), replace });
|
else navigate({ pathname: path, search: queryString.stringify(search), replace });
|
||||||
|
@ -34,7 +34,6 @@ const ERR_APPSERVER = "Ошибка сервера приложений"; //Об
|
|||||||
const ERR_UNEXPECTED = "Неожиданный ответ сервера"; //Неожиданный ответ сервера
|
const ERR_UNEXPECTED = "Неожиданный ответ сервера"; //Неожиданный ответ сервера
|
||||||
const ERR_NETWORK = "Ошибка соединения с сервером"; //Ошибка сети
|
const ERR_NETWORK = "Ошибка соединения с сервером"; //Ошибка сети
|
||||||
const ERR_UNAUTH = "Сеанс завершен. Пройдите аутентификацию повторно."; //Ошибка аутентификации
|
const ERR_UNAUTH = "Сеанс завершен. Пройдите аутентификацию повторно."; //Ошибка аутентификации
|
||||||
const ERR_ABORTED = "Запрос прерван принудительно";
|
|
||||||
|
|
||||||
//-----------
|
//-----------
|
||||||
//Тело модуля
|
//Тело модуля
|
||||||
@ -77,16 +76,7 @@ const getRespErrMessage = resp => (isRespErr(resp) && resp.SMESSAGE ? resp.SMESS
|
|||||||
const getRespPayload = resp => (resp && resp.XPAYLOAD ? resp.XPAYLOAD : null);
|
const getRespPayload = resp => (resp && resp.XPAYLOAD ? resp.XPAYLOAD : null);
|
||||||
|
|
||||||
//Исполнение действия на сервере
|
//Исполнение действия на сервере
|
||||||
const executeAction = async ({
|
const executeAction = async ({ serverURL, action, payload = {}, isArray, transformTagName, tagValueProcessor, attributeValueProcessor } = {}) => {
|
||||||
serverURL,
|
|
||||||
action,
|
|
||||||
payload = {},
|
|
||||||
isArray,
|
|
||||||
transformTagName,
|
|
||||||
tagValueProcessor,
|
|
||||||
attributeValueProcessor,
|
|
||||||
signal = null
|
|
||||||
} = {}) => {
|
|
||||||
console.log(`EXECUTING ${action ? action : ""} ON ${serverURL} WITH PAYLOAD:`);
|
console.log(`EXECUTING ${action ? action : ""} ON ${serverURL} WITH PAYLOAD:`);
|
||||||
console.log(payload ? payload : "NO PAYLOAD");
|
console.log(payload ? payload : "NO PAYLOAD");
|
||||||
let response = null;
|
let response = null;
|
||||||
@ -102,14 +92,11 @@ const executeAction = async ({
|
|||||||
body: await buildXML(rqBody),
|
body: await buildXML(rqBody),
|
||||||
headers: {
|
headers: {
|
||||||
"content-type": "application/xml"
|
"content-type": "application/xml"
|
||||||
},
|
}
|
||||||
...(signal ? { signal } : {})
|
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
//Прервано принудительно
|
|
||||||
if (signal?.aborted === true) throw new Error(ERR_ABORTED);
|
|
||||||
//Сетевая ошибка
|
//Сетевая ошибка
|
||||||
else throw new Error(`${ERR_NETWORK}: ${e.message || "неопределённая ошибка"}`);
|
throw new Error(`${ERR_NETWORK}: ${e.message}`);
|
||||||
}
|
}
|
||||||
//Проверим на наличие ошибок HTTP - если есть вернём их
|
//Проверим на наличие ошибок HTTP - если есть вернём их
|
||||||
if (!response.ok) throw new Error(`${ERR_APPSERVER}: ${response.statusText}`);
|
if (!response.ok) throw new Error(`${ERR_APPSERVER}: ${response.statusText}`);
|
||||||
@ -149,8 +136,7 @@ const executeStored = async ({
|
|||||||
tagValueProcessor,
|
tagValueProcessor,
|
||||||
attributeValueProcessor,
|
attributeValueProcessor,
|
||||||
throwError = true,
|
throwError = true,
|
||||||
spreadOutArguments = false,
|
spreadOutArguments = false
|
||||||
signal = null
|
|
||||||
} = {}) => {
|
} = {}) => {
|
||||||
let res = null;
|
let res = null;
|
||||||
try {
|
try {
|
||||||
@ -171,8 +157,7 @@ const executeStored = async ({
|
|||||||
payload: { SSTORED: stored, XARGUMENTS: serverArgs, SRESP_ARG: respArg },
|
payload: { SSTORED: stored, XARGUMENTS: serverArgs, SRESP_ARG: respArg },
|
||||||
isArray,
|
isArray,
|
||||||
tagValueProcessor,
|
tagValueProcessor,
|
||||||
attributeValueProcessor,
|
attributeValueProcessor
|
||||||
signal
|
|
||||||
});
|
});
|
||||||
if (spreadOutArguments === true && Array.isArray(res?.XPAYLOAD?.XOUT_ARGUMENTS)) {
|
if (spreadOutArguments === true && Array.isArray(res?.XPAYLOAD?.XOUT_ARGUMENTS)) {
|
||||||
let spreadArgs = {};
|
let spreadArgs = {};
|
||||||
@ -208,11 +193,6 @@ const getConfig = async ({ throwError = true } = {}) => {
|
|||||||
//----------------
|
//----------------
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
ERR_APPSERVER,
|
|
||||||
ERR_UNEXPECTED,
|
|
||||||
ERR_NETWORK,
|
|
||||||
ERR_UNAUTH,
|
|
||||||
ERR_ABORTED,
|
|
||||||
SERV_DATA_TYPE_STR,
|
SERV_DATA_TYPE_STR,
|
||||||
SERV_DATA_TYPE_NUMB,
|
SERV_DATA_TYPE_NUMB,
|
||||||
SERV_DATA_TYPE_DATE,
|
SERV_DATA_TYPE_DATE,
|
||||||
|
@ -102,7 +102,7 @@ const getDisplaySize = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
//Глубокое копирование объекта
|
//Глубокое копирование объекта
|
||||||
const deepCopyObject = obj => (structuredClone ? structuredClone(obj) : JSON.parse(JSON.stringify(obj)));
|
const deepCopyObject = obj => JSON.parse(JSON.stringify(obj));
|
||||||
|
|
||||||
//Конвертация объекта в Base64 XML
|
//Конвертация объекта в Base64 XML
|
||||||
const object2Base64XML = (obj, builderOptions) => {
|
const object2Base64XML = (obj, builderOptions) => {
|
||||||
@ -158,34 +158,6 @@ const formatDateJSONDateOnly = value => (value ? dayjs(value).format("YYYY-MM-DD
|
|||||||
//Форматирование числа в "Денежном" формате РФ
|
//Форматирование числа в "Денежном" формате РФ
|
||||||
const formatNumberRFCurrency = value => (hasValue(value) ? new Intl.NumberFormat("ru-RU", { minimumFractionDigits: 2 }).format(value) : null);
|
const formatNumberRFCurrency = value => (hasValue(value) ? new Intl.NumberFormat("ru-RU", { minimumFractionDigits: 2 }).format(value) : null);
|
||||||
|
|
||||||
//Форматирование текста ошибки
|
|
||||||
const formatErrorMessage = errorMsg => {
|
|
||||||
//Инициализируем текст заголовка ошибки
|
|
||||||
let text = "";
|
|
||||||
//Пробуем извлечь заголовок текста ошибки
|
|
||||||
try {
|
|
||||||
//Если это ошибка Oracle
|
|
||||||
if (errorMsg.match(/^ORA-/)) {
|
|
||||||
//Считываем первую строку с заголовочным текстом ошибки
|
|
||||||
text = errorMsg.match(/^.*(?=(\nORA-))/)[0];
|
|
||||||
//Убираем лишнюю информацию и пробелы
|
|
||||||
text = text.replace(/ORA-\d*:/g, "").trim();
|
|
||||||
}
|
|
||||||
//Если это ошибка PG
|
|
||||||
if (errorMsg.match(/^SQL Error/)) {
|
|
||||||
//Считываем первую строку с заголовочным текстом ошибки
|
|
||||||
text = errorMsg.match(/.*(?=(\n.*Where)|(.*Where))/)[0];
|
|
||||||
//Убираем лишнюю информацию и пробелы
|
|
||||||
text = text.replace(/SQL Error \[\d*\]: ERROR:/g, "").trim();
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
//Если произошла ошибка - оставляем полный текст ошибки
|
|
||||||
text = errorMsg;
|
|
||||||
}
|
|
||||||
//Возвращаем результат
|
|
||||||
return { text: text || errorMsg, fullErrorText: text ? errorMsg : null };
|
|
||||||
};
|
|
||||||
|
|
||||||
//Формирование уникального идентификатора
|
//Формирование уникального идентификатора
|
||||||
const genGUID = () =>
|
const genGUID = () =>
|
||||||
"10000000-1000-4000-8000-100000000000".replace(/[018]/g, c =>
|
"10000000-1000-4000-8000-100000000000".replace(/[018]/g, c =>
|
||||||
@ -206,6 +178,5 @@ export {
|
|||||||
formatDateTimeRF,
|
formatDateTimeRF,
|
||||||
formatDateJSONDateOnly,
|
formatDateJSONDateOnly,
|
||||||
formatNumberRFCurrency,
|
formatNumberRFCurrency,
|
||||||
formatErrorMessage,
|
|
||||||
genGUID
|
genGUID
|
||||||
};
|
};
|
||||||
|
@ -1,281 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - ПУП - Выдача сменного задания на участок
|
|
||||||
Кастомные хуки
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import { useState, useCallback, useEffect, useContext } from "react"; //Классы React
|
|
||||||
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
|
||||||
import { NavigationCtx } from "../../context/navigation"; //Контекст навигации
|
|
||||||
import { object2Base64XML } from "../../core/utils"; //Вспомогательные функции
|
|
||||||
|
|
||||||
//---------
|
|
||||||
//Константы
|
|
||||||
//---------
|
|
||||||
|
|
||||||
//Размер страницы данных
|
|
||||||
const DATA_GRID_PAGE_SIZE = 50;
|
|
||||||
|
|
||||||
//---------------------------------------------
|
|
||||||
//Вспомогательные функции форматирования данных
|
|
||||||
//---------------------------------------------
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Хук для основной таблицы
|
|
||||||
const useCostJobs = () => {
|
|
||||||
//Собственное состояние - таблица данных
|
|
||||||
const [state, setState] = useState({
|
|
||||||
init: false,
|
|
||||||
loaded: false,
|
|
||||||
jobInfo: {},
|
|
||||||
haveNote: false,
|
|
||||||
coeff: "1.0"
|
|
||||||
});
|
|
||||||
|
|
||||||
//Подключение к контексту взаимодействия с сервером
|
|
||||||
const { executeStored } = useContext(BackEndСtx);
|
|
||||||
//Подключение к контексту навигации
|
|
||||||
const { getNavigationSearch } = useContext(NavigationCtx);
|
|
||||||
|
|
||||||
//При подключении компонента к странице
|
|
||||||
useEffect(() => {
|
|
||||||
const initJob = async fcJob => {
|
|
||||||
const data = await executeStored({
|
|
||||||
stored: "PKG_P8PANELS_MECHREC.FCJOBS_MP_INIT",
|
|
||||||
args: { NFCJOBS: parseInt(fcJob) },
|
|
||||||
respArg: "COUT",
|
|
||||||
attributeValueProcessor: (name, val) => (["NHAVE_NOTE"].includes(name) ? val == 1 : val)
|
|
||||||
});
|
|
||||||
setState(pv => ({
|
|
||||||
...pv,
|
|
||||||
init: true,
|
|
||||||
jobInfo: data.XFCJOBS ? data.XFCJOBS : {},
|
|
||||||
loaded: true
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
if (!state.init) {
|
|
||||||
//Считаем параметры, переданные из действия
|
|
||||||
const actionPrms = getNavigationSearch();
|
|
||||||
//Иницализируем сменное задание
|
|
||||||
initJob(actionPrms.NRN);
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return [state, setState];
|
|
||||||
};
|
|
||||||
|
|
||||||
//Хук для таблицы операций
|
|
||||||
const useCostJobsSpecs = task => {
|
|
||||||
//Собственное состояние - таблица данных
|
|
||||||
const [costJobsSpecs, setCostJobsSpecs] = useState({
|
|
||||||
task: null,
|
|
||||||
dataLoaded: false,
|
|
||||||
columnsDef: [],
|
|
||||||
orders: null,
|
|
||||||
rows: [],
|
|
||||||
selectedRow: {},
|
|
||||||
reload: true,
|
|
||||||
pageNumber: 1,
|
|
||||||
morePages: true,
|
|
||||||
fixedHeader: false,
|
|
||||||
fixedColumns: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
//Подключение к контексту взаимодействия с сервером
|
|
||||||
const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
|
|
||||||
|
|
||||||
//Выдача задания
|
|
||||||
const issueCostJobsSpecs = useCallback(
|
|
||||||
async prms => {
|
|
||||||
try {
|
|
||||||
await executeStored({
|
|
||||||
stored: "PKG_P8PANELS_MECHREC.FCJOBSSP_MP_ISSUE",
|
|
||||||
args: { NFCJOBS: prms.NFCJOBS, NCOEFF: parseFloat(prms.NCOEFF) }
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
throw new Error(e.message);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[executeStored]
|
|
||||||
);
|
|
||||||
|
|
||||||
//При необходимости обновить данные таблицы
|
|
||||||
useEffect(() => {
|
|
||||||
//Если изменилось сменное задание - обновляем состояние
|
|
||||||
if (costJobsSpecs.dataLoaded && costJobsSpecs.task !== task) {
|
|
||||||
setCostJobsSpecs(pv => ({
|
|
||||||
...pv,
|
|
||||||
dataLoaded: false,
|
|
||||||
columnsDef: [],
|
|
||||||
orders: null,
|
|
||||||
rows: [],
|
|
||||||
selectedRow: {},
|
|
||||||
reload: true,
|
|
||||||
pageNumber: 1,
|
|
||||||
morePages: true
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
//Если необходимо перезагрузить
|
|
||||||
if (costJobsSpecs.reload && task) {
|
|
||||||
const loadData = async () => {
|
|
||||||
const data = await executeStored({
|
|
||||||
stored: "PKG_P8PANELS_MECHREC.FCJOBSSP_MP_DG_GET",
|
|
||||||
args: {
|
|
||||||
NFCJOBS: task,
|
|
||||||
NPAGE_NUMBER: costJobsSpecs.pageNumber,
|
|
||||||
NPAGE_SIZE: DATA_GRID_PAGE_SIZE,
|
|
||||||
CORDERS: { VALUE: object2Base64XML(costJobsSpecs.orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
|
|
||||||
NINCLUDE_DEF: costJobsSpecs.dataLoaded ? 0 : 1
|
|
||||||
},
|
|
||||||
respArg: "COUT",
|
|
||||||
attributeValueProcessor: (name, val) =>
|
|
||||||
name === "NSELECT" ? val === 1 : name === "SWORKERS_LIST" ? (val ? val.split(",").map(Number) : []) : val
|
|
||||||
});
|
|
||||||
setCostJobsSpecs(pv => ({
|
|
||||||
...pv,
|
|
||||||
...data.XDATA_GRID,
|
|
||||||
task: task,
|
|
||||||
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 || [])],
|
|
||||||
dataLoaded: true,
|
|
||||||
reload: false,
|
|
||||||
morePages: (data.XDATA_GRID.rows || []).length >= DATA_GRID_PAGE_SIZE
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
loadData();
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
SERV_DATA_TYPE_CLOB,
|
|
||||||
costJobsSpecs.dataLoaded,
|
|
||||||
costJobsSpecs.orders,
|
|
||||||
costJobsSpecs.pageNumber,
|
|
||||||
costJobsSpecs.reload,
|
|
||||||
costJobsSpecs.task,
|
|
||||||
executeStored,
|
|
||||||
task
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [costJobsSpecs, setCostJobsSpecs, issueCostJobsSpecs];
|
|
||||||
};
|
|
||||||
|
|
||||||
//Хук для рабочих
|
|
||||||
const useCostJobsWorkers = task => {
|
|
||||||
//Собственное состояние - таблица данных
|
|
||||||
const [costJobsWorkers, setCostJobsWorkers] = useState({
|
|
||||||
task: null,
|
|
||||||
dataLoaded: false,
|
|
||||||
columnsDef: [],
|
|
||||||
orders: null,
|
|
||||||
rows: [],
|
|
||||||
selectedRows: [],
|
|
||||||
reload: true,
|
|
||||||
pageNumber: 1,
|
|
||||||
morePages: true
|
|
||||||
});
|
|
||||||
|
|
||||||
//Подключение к контексту взаимодействия с сервером
|
|
||||||
const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
|
|
||||||
|
|
||||||
//Включение рабочего в строку сменного задания
|
|
||||||
const includeWorker = useCallback(
|
|
||||||
async prms => {
|
|
||||||
try {
|
|
||||||
await executeStored({
|
|
||||||
stored: "PKG_P8PANELS_MECHREC.FCJOBSSP_MP_INC_PERFORM",
|
|
||||||
args: {
|
|
||||||
NFCJOBSSP: prms.NFCJOBSSP,
|
|
||||||
SPERFORM_LIST: {
|
|
||||||
VALUE: Array.isArray(prms.SELECTED_WORKERS) ? prms.SELECTED_WORKERS.join(";") : prms.SELECTED_WORKERS,
|
|
||||||
SDATA_TYPE: SERV_DATA_TYPE_CLOB
|
|
||||||
},
|
|
||||||
NQUANT_PLAN: prms.NQUANT_PLAN
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
throw new Error(e.message);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[SERV_DATA_TYPE_CLOB, executeStored]
|
|
||||||
);
|
|
||||||
|
|
||||||
//Исключение рабочего из строки сменного задания
|
|
||||||
const excludeWorker = useCallback(
|
|
||||||
async prms => {
|
|
||||||
try {
|
|
||||||
await executeStored({
|
|
||||||
stored: "PKG_P8PANELS_MECHREC.FCJOBSSP_MP_EXC_PERFORM",
|
|
||||||
args: { NFCJOBSSP: prms.NFCJOBSSP }
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
throw new Error(e.message);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[executeStored]
|
|
||||||
);
|
|
||||||
|
|
||||||
//При необходимости обновить данные таблицы
|
|
||||||
useEffect(() => {
|
|
||||||
//Если изменилось сменное задание - обновляем состояние
|
|
||||||
if (costJobsWorkers.dataLoaded && costJobsWorkers.task !== task) {
|
|
||||||
setCostJobsWorkers(pv => ({
|
|
||||||
...pv,
|
|
||||||
dataLoaded: false,
|
|
||||||
columnsDef: [],
|
|
||||||
orders: null,
|
|
||||||
rows: [],
|
|
||||||
selectedRows: [],
|
|
||||||
reload: true,
|
|
||||||
pageNumber: 1,
|
|
||||||
morePages: true
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
//Если необходимо перезагрузить
|
|
||||||
if (costJobsWorkers.reload && task) {
|
|
||||||
const loadData = async () => {
|
|
||||||
const data = await executeStored({
|
|
||||||
stored: "PKG_P8PANELS_MECHREC.WORKERS_MP_DG_GET",
|
|
||||||
args: {
|
|
||||||
NFCJOBS: task,
|
|
||||||
NPAGE_NUMBER: costJobsWorkers.pageNumber,
|
|
||||||
NPAGE_SIZE: DATA_GRID_PAGE_SIZE,
|
|
||||||
CORDERS: { VALUE: object2Base64XML(costJobsWorkers.orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
|
|
||||||
NINCLUDE_DEF: costJobsWorkers.dataLoaded ? 0 : 1
|
|
||||||
},
|
|
||||||
respArg: "COUT",
|
|
||||||
attributeValueProcessor: (name, val) => (["NSELECT"].includes(name) ? val === 1 : val)
|
|
||||||
});
|
|
||||||
setCostJobsWorkers(pv => ({
|
|
||||||
...pv,
|
|
||||||
...data.XDATA_GRID,
|
|
||||||
task: task,
|
|
||||||
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 || [])],
|
|
||||||
dataLoaded: true,
|
|
||||||
reload: false,
|
|
||||||
morePages: (data.XDATA_GRID.rows || []).length >= DATA_GRID_PAGE_SIZE
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
loadData();
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
SERV_DATA_TYPE_CLOB,
|
|
||||||
costJobsWorkers.dataLoaded,
|
|
||||||
costJobsWorkers.orders,
|
|
||||||
costJobsWorkers.pageNumber,
|
|
||||||
costJobsWorkers.reload,
|
|
||||||
costJobsWorkers.task,
|
|
||||||
executeStored,
|
|
||||||
task
|
|
||||||
]);
|
|
||||||
|
|
||||||
return [costJobsWorkers, setCostJobsWorkers, includeWorker, excludeWorker];
|
|
||||||
};
|
|
||||||
|
|
||||||
export { useCostJobs, useCostJobsSpecs, useCostJobsWorkers };
|
|
@ -1,16 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - ПУП - Выдача сменного задания на участок
|
|
||||||
Панель мониторинга: Точка входа
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import { MechRecCostJobs } from "./mech_rec_cost_jobs_manage_mp"; //Корневая панель выдачи сменного задания на участок
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export const RootClass = MechRecCostJobs;
|
|
@ -1,484 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - ПУП - Выдача сменного задания на участок
|
|
||||||
Панель мониторинга: Корневая панель выдачи сменного задания на участок
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React, { useState } from "react"; //Классы React
|
|
||||||
import { Grid, Box, Typography, Checkbox, Icon, Stack, Button, Tooltip, TextField } from "@mui/material"; //Интерфейсные элементы
|
|
||||||
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
|
|
||||||
import { APP_STYLES } from "../../../app.styles"; //Типовые стили
|
|
||||||
import { P8PDataGrid, P8P_DATA_GRID_SIZE, P8P_DATA_GRID_MORE_HEIGHT } from "../../components/p8p_data_grid"; //Таблица данных
|
|
||||||
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
|
||||||
import { useCostJobs, useCostJobsSpecs, useCostJobsWorkers } from "./hooks"; //Вспомогательные хуки
|
|
||||||
import { CostJobsSpecsInclude } from "./worker_include_dialog"; //Компонент диалога включения в задание
|
|
||||||
import { hasValue } from "../../core/utils"; //Вспомогательные функции
|
|
||||||
|
|
||||||
//---------
|
|
||||||
//Константы
|
|
||||||
//---------
|
|
||||||
|
|
||||||
//Мнемокод раздела операций
|
|
||||||
const UNIT_COST_JOBS_SPECS = "CostJobsSpecs";
|
|
||||||
|
|
||||||
//Мнемокод раздела исполнений должности
|
|
||||||
const UNIT_WORKERS = "ClientPostPerform";
|
|
||||||
|
|
||||||
//Высота основного заголовка
|
|
||||||
const MAIN_HEADER_HEIGHT = "35px";
|
|
||||||
|
|
||||||
//Высота подзаголовка
|
|
||||||
const SUB_HEADER_HEIGHT = "35px";
|
|
||||||
|
|
||||||
//Высота заголовка таблицы
|
|
||||||
const TABLE_HEADER_HEIGHT = "35px";
|
|
||||||
|
|
||||||
//Высота панели кнопок таблицы
|
|
||||||
const TABLE_BUTTONS_HEIGHT = "35px";
|
|
||||||
|
|
||||||
//Отступ таблицы
|
|
||||||
const TABLE_PADDING_TOP = "15px";
|
|
||||||
|
|
||||||
//Формат для коэффициент выполнения норм
|
|
||||||
const issueCoeffFormat = /^(?!.*\..*\.)[0-9]{0,3}(\.[0-9]{0,1})?$/;
|
|
||||||
|
|
||||||
//Стили
|
|
||||||
const STYLES = {
|
|
||||||
MAIN_HEADER: { height: MAIN_HEADER_HEIGHT, overflow: "hidden" },
|
|
||||||
SUB_HEADER: { height: SUB_HEADER_HEIGHT, overflow: "hidden" },
|
|
||||||
CONTAINER: { textAlign: "center" },
|
|
||||||
TABLE: { paddingTop: TABLE_PADDING_TOP },
|
|
||||||
TABLE_HEADER: { height: TABLE_HEADER_HEIGHT, overflow: "hidden" },
|
|
||||||
TABLE_BUTTONS: { display: "flex", justifyContent: "flex-end", height: TABLE_BUTTONS_HEIGHT, overflow: "hidden", alignItems: "flex-end" },
|
|
||||||
DATA_GRID_CONTAINER: morePages => ({
|
|
||||||
height: `calc(100vh - ${APP_BAR_HEIGHT} - ${MAIN_HEADER_HEIGHT} - ${SUB_HEADER_HEIGHT} - ${TABLE_HEADER_HEIGHT} - ${TABLE_BUTTONS_HEIGHT} - ${TABLE_PADDING_TOP} - 32px - ${
|
|
||||||
morePages ? P8P_DATA_GRID_MORE_HEIGHT : "0px"
|
|
||||||
})`,
|
|
||||||
...APP_STYLES.SCROLL
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
//Цвета
|
|
||||||
const colors = {
|
|
||||||
LINKED: "#bce0de",
|
|
||||||
UNAVAILABLE: "#949494",
|
|
||||||
WITH_WORKER: "#82df83"
|
|
||||||
};
|
|
||||||
|
|
||||||
//------------------------------------
|
|
||||||
//Вспомогательные функции и компоненты
|
|
||||||
//------------------------------------
|
|
||||||
|
|
||||||
//Проверка правильности значения коэффициент выполнения норм
|
|
||||||
const isValidIssueCoeff = value => {
|
|
||||||
return issueCoeffFormat.test(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Форматирование значения ячейки
|
|
||||||
const dataCellRender = ({ row, columnDef, handleSelectChange, sUnit, selectedWorkerRows = [], selectedJobSpec }) => {
|
|
||||||
//Стиль
|
|
||||||
let cellStyle = {};
|
|
||||||
//Если это рабочие
|
|
||||||
if (sUnit === UNIT_WORKERS) {
|
|
||||||
//Признак недоступности
|
|
||||||
let disabled = true;
|
|
||||||
//Если в выбранной строке смены указан исполнитель факт
|
|
||||||
if (selectedJobSpec.NPERFORM_FACT) {
|
|
||||||
//Если это текущей исполнитель
|
|
||||||
if (selectedJobSpec.SWORKERS_LIST.includes(row["NRN"])) {
|
|
||||||
//Подсвечиваем строку рабочего
|
|
||||||
cellStyle = { backgroundColor: colors.LINKED };
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//Если выбрана строка смены
|
|
||||||
if (selectedJobSpec.NRN) {
|
|
||||||
//Если текущий рабочий может принять задание
|
|
||||||
if (row["NLOADING"] < 100) {
|
|
||||||
//Подсвечиваем строку рабочего
|
|
||||||
cellStyle = { backgroundColor: colors.LINKED };
|
|
||||||
disabled = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//Если уже выбрано достаточное количество рабочих и текущий рабочий не отмечен
|
|
||||||
if (selectedJobSpec.NRESOURCE_NUMB === selectedWorkerRows.length && !selectedWorkerRows.includes(row["NRN"])) {
|
|
||||||
//Устанавливаем признак недоступности
|
|
||||||
disabled = true;
|
|
||||||
}
|
|
||||||
//Если загрузка рабочего больше 100
|
|
||||||
if (row["NLOADING"] >= 100) {
|
|
||||||
//Если поле не поле выбора
|
|
||||||
if (columnDef.name !== "NSELECT") {
|
|
||||||
//Указываем, что рабочее место недоступно
|
|
||||||
cellStyle = { ...cellStyle, color: colors.UNAVAILABLE };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//Для колонки выбора
|
|
||||||
if (columnDef.name === "NSELECT") {
|
|
||||||
return {
|
|
||||||
cellStyle,
|
|
||||||
data: (
|
|
||||||
<Box sx={STYLES.CONTAINER}>
|
|
||||||
<Checkbox
|
|
||||||
disabled={disabled}
|
|
||||||
checked={selectedWorkerRows.includes(row["NRN"])}
|
|
||||||
onChange={() => handleSelectChange({ NRN: row["NRN"], SUNIT: sUnit, BFULL_LOADED: row["NLOADING"] >= 100 })}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
//Отформатированная колонка
|
|
||||||
return {
|
|
||||||
cellStyle,
|
|
||||||
data: row[columnDef.name]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
//Если это сменное задание
|
|
||||||
if (sUnit === UNIT_COST_JOBS_SPECS) {
|
|
||||||
//Если указан исполнитель факт
|
|
||||||
if (row["NPERFORM_FACT"]) {
|
|
||||||
//Подсвечиваем сменное задание зеленым
|
|
||||||
cellStyle = { ...cellStyle, backgroundColor: colors.WITH_WORKER };
|
|
||||||
}
|
|
||||||
//Для колонки выбора
|
|
||||||
if (columnDef.name === "NSELECT") {
|
|
||||||
return {
|
|
||||||
cellStyle,
|
|
||||||
data: (
|
|
||||||
<Box sx={STYLES.CONTAINER}>
|
|
||||||
<Checkbox
|
|
||||||
disabled={row["DBEG_FACT"] ? true : false}
|
|
||||||
checked={row["NRN"] === selectedJobSpec.NRN}
|
|
||||||
onChange={() =>
|
|
||||||
handleSelectChange({
|
|
||||||
NRN: row["NRN"],
|
|
||||||
SUNIT: sUnit,
|
|
||||||
NPERFORM_FACT: row["NPERFORM_FACT"],
|
|
||||||
NRESOURCE_NUMB: row["NRESOURCE_NUMB"],
|
|
||||||
NQUANT_PLAN: row["NQUANT_PLAN"],
|
|
||||||
SWORKERS_LIST: row["SWORKERS_LIST"]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
//Отформатированная колонка
|
|
||||||
return {
|
|
||||||
cellStyle,
|
|
||||||
data: row[columnDef.name]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
//Необрабатываемый раздел
|
|
||||||
return {
|
|
||||||
data: row[columnDef.name]
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
//Генерация представления ячейки заголовка группы
|
|
||||||
export const headCellRender = ({ columnDef }) => {
|
|
||||||
if (columnDef.name === "NSELECT") {
|
|
||||||
return {
|
|
||||||
stackStyle: { padding: "2px", justifyContent: "space-around" },
|
|
||||||
data: <Icon>done</Icon>
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
stackStyle: { padding: "2px" },
|
|
||||||
data: columnDef.caption
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Корневая панель выдачи сменного задания на участок
|
|
||||||
const MechRecCostJobs = () => {
|
|
||||||
//Состояние диалога включения в задание
|
|
||||||
const [showInclude, setShowInclude] = useState(false);
|
|
||||||
|
|
||||||
//Состояние информации о сменном задании
|
|
||||||
const [state, setState] = useCostJobs();
|
|
||||||
|
|
||||||
//Состояние таблицы сменных заданий
|
|
||||||
const [costJobsSpecs, setCostJobsSpecs, issueCostJobsSpecs] = useCostJobsSpecs(state.jobInfo.NRN);
|
|
||||||
|
|
||||||
//Состояние таблицы рабочих
|
|
||||||
const [costJobsWorkers, setCostJobsWorkers, includeWorker, excludeWorker] = useCostJobsWorkers(state.jobInfo.NRN);
|
|
||||||
|
|
||||||
//При изменении состояния сортировки операций
|
|
||||||
const handleCostJobsSpecOrderChanged = ({ orders }) => setCostJobsSpecs(pv => ({ ...pv, orders: [...orders], pageNumber: 1, reload: true }));
|
|
||||||
|
|
||||||
//При изменении количества отображаемых страниц операций
|
|
||||||
const handleCostJobsSpecPagesCountChanged = () => setCostJobsSpecs(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reload: true }));
|
|
||||||
|
|
||||||
//При изменении состояния сортировки рабочих
|
|
||||||
const handleCostJobsWorkersOrderChanged = ({ orders }) => setCostJobsWorkers(pv => ({ ...pv, orders: [...orders], pageNumber: 1, reload: true }));
|
|
||||||
|
|
||||||
//При изменении количества отображаемых страниц рабочих
|
|
||||||
const handleCostJobsWorkersPagesCountChanged = () => setCostJobsWorkers(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reload: true }));
|
|
||||||
|
|
||||||
//При исключении рабочих из строки сменного задания
|
|
||||||
const handleCostJobsSpecExcludeWorker = () => {
|
|
||||||
//Делаем асинхронно, чтобы при ошибке ничего не обновлять
|
|
||||||
const excludeAsync = async () => {
|
|
||||||
//Исключаем рабочего из строки сменного задания
|
|
||||||
try {
|
|
||||||
await excludeWorker({
|
|
||||||
NFCJOBSSP: costJobsSpecs.selectedRow.NRN
|
|
||||||
});
|
|
||||||
//Необходимо обновить данные
|
|
||||||
setCostJobsSpecs(pv => ({ ...pv, selectedRow: {}, pageNumber: 1, reload: true }));
|
|
||||||
setCostJobsWorkers(pv => ({ ...pv, selectedRows: [], pageNumber: 1, reload: true }));
|
|
||||||
} catch (e) {
|
|
||||||
throw new Error(e.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//Исключаем рабочего асинхронно
|
|
||||||
excludeAsync();
|
|
||||||
};
|
|
||||||
|
|
||||||
//Выдача задания операции
|
|
||||||
const handleCostJobsSpecIssue = () => {
|
|
||||||
//Делаем асинхронно, чтобы при ошибке ничего не обновлять
|
|
||||||
const issueAsync = async () => {
|
|
||||||
//Включаем рабочих в операции
|
|
||||||
try {
|
|
||||||
await issueCostJobsSpecs({ NFCJOBS: state.jobInfo.NRN, NCOEFF: state.coeff });
|
|
||||||
//Необходимо обновить данные
|
|
||||||
setCostJobsSpecs(pv => ({ ...pv, selectedRow: {}, pageNumber: 1, reload: true }));
|
|
||||||
setCostJobsWorkers(pv => ({ ...pv, selectedRows: [], pageNumber: 1, reload: true }));
|
|
||||||
} catch (e) {
|
|
||||||
throw new Error(e.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//Выдаем задание асинхронно
|
|
||||||
issueAsync();
|
|
||||||
};
|
|
||||||
|
|
||||||
//При изменение состояния выбора
|
|
||||||
const handleSelectChange = prms => {
|
|
||||||
//Выбранный элемент
|
|
||||||
let selectedRow = null;
|
|
||||||
//Буфер для выбранных рабочих
|
|
||||||
let selectedWorkers = [];
|
|
||||||
//Индекс рабочего в списке выбранных
|
|
||||||
let workerIndex = null;
|
|
||||||
//Исходим от раздела
|
|
||||||
switch (prms.SUNIT) {
|
|
||||||
//Сменное задание
|
|
||||||
case UNIT_COST_JOBS_SPECS:
|
|
||||||
//Определяем это новое отмеченное сменное задание или сброс старого
|
|
||||||
selectedRow = costJobsSpecs.selectedRow.NRN ? (costJobsSpecs.selectedRow.NRN === prms.NRN ? null : prms.NRN) : prms.NRN;
|
|
||||||
//Актуализируем строки
|
|
||||||
setCostJobsSpecs(pv => ({
|
|
||||||
...pv,
|
|
||||||
selectedRow: selectedRow
|
|
||||||
? {
|
|
||||||
NRN: selectedRow,
|
|
||||||
NPERFORM_FACT: prms.NPERFORM_FACT,
|
|
||||||
NRESOURCE_NUMB: prms.NRESOURCE_NUMB,
|
|
||||||
NQUANT_PLAN: prms.NQUANT_PLAN,
|
|
||||||
SWORKERS_LIST: prms.SWORKERS_LIST
|
|
||||||
}
|
|
||||||
: { NRN: null, NPERFORM_FACT: null, NRESOURCE_NUMB: null, NQUANT_PLAN: null, SWORKERS_LIST: [] }
|
|
||||||
}));
|
|
||||||
//Выходим
|
|
||||||
break;
|
|
||||||
//Рабочие центры
|
|
||||||
case UNIT_WORKERS:
|
|
||||||
//Инициализируем рабочими центрами
|
|
||||||
selectedWorkers = costJobsWorkers.selectedRows || [];
|
|
||||||
//Определяем индекс элемента в массиве
|
|
||||||
workerIndex = selectedWorkers.indexOf(prms.NRN);
|
|
||||||
//Если такого рег. номера нет в списке - добавляем, иначе удаляем
|
|
||||||
workerIndex > -1 ? selectedWorkers.splice(workerIndex, 1) : selectedWorkers.push(prms.NRN);
|
|
||||||
//Актуализируем строки
|
|
||||||
setCostJobsWorkers(pv => ({ ...pv, selectedRows: selectedWorkers }));
|
|
||||||
//Выходим
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//При открытии/закрытии диалога добавления
|
|
||||||
const handleShowIncludeChange = needShow => setShowInclude(needShow);
|
|
||||||
|
|
||||||
//При изменении коэффициент выполнения норм
|
|
||||||
const handleIssueCoeffChange = e => {
|
|
||||||
isValidIssueCoeff(e.target.value) ? setState(pv => ({ ...pv, coeff: e.target.value })) : null;
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box p={2}>
|
|
||||||
{state.loaded ? (
|
|
||||||
<Box sx={STYLES.CONTAINER}>
|
|
||||||
<Typography
|
|
||||||
sx={STYLES.MAIN_HEADER}
|
|
||||||
variant={"h6"}
|
|
||||||
>{`Сменное задание №${state.jobInfo.SDOC_NUMB} на ${state.jobInfo.SPERIOD}`}</Typography>
|
|
||||||
<Typography sx={STYLES.SUB_HEADER} variant={"h6"}>{`${state.jobInfo.SSUBDIV}`}</Typography>
|
|
||||||
<Box sx={STYLES.CONTAINER}>
|
|
||||||
<Grid container spacing={2}>
|
|
||||||
<Grid item sx={STYLES.CONTAINER} xs={6}>
|
|
||||||
<Typography sx={STYLES.TABLE_HEADER} variant={"h6"} color={"text.secondary"}>
|
|
||||||
Сменное задание
|
|
||||||
</Typography>
|
|
||||||
{costJobsWorkers.dataLoaded ? (
|
|
||||||
<>
|
|
||||||
<Box sx={STYLES.TABLE_BUTTONS}>
|
|
||||||
<Stack direction={"row"} spacing={1}>
|
|
||||||
<Tooltip
|
|
||||||
title={
|
|
||||||
state.jobInfo.NHAVE_NOTE
|
|
||||||
? "Сменное задание имеет строку с примечанием"
|
|
||||||
: "Коэффициент выполнения норм"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<TextField
|
|
||||||
name="editIssueValue"
|
|
||||||
variant="outlined"
|
|
||||||
sx={{ width: "68px" }}
|
|
||||||
inputProps={{ sx: { padding: "4.2px 14px" } }}
|
|
||||||
size="small"
|
|
||||||
value={state.coeff}
|
|
||||||
onChange={handleIssueCoeffChange}
|
|
||||||
disabled={state.jobInfo.NHAVE_NOTE}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip
|
|
||||||
title={
|
|
||||||
state.jobInfo.NHAVE_NOTE
|
|
||||||
? "Сменное задание имеет строку с примечанием"
|
|
||||||
: !hasValue(state.coeff)
|
|
||||||
? "Не указано значение коэффициент выполнения норм"
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Box>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
size="small"
|
|
||||||
disabled={state.jobInfo.NHAVE_NOTE || !hasValue(state.coeff)}
|
|
||||||
onClick={handleCostJobsSpecIssue}
|
|
||||||
>
|
|
||||||
Выдать задания
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
</Tooltip>
|
|
||||||
</Stack>
|
|
||||||
</Box>
|
|
||||||
<Box sx={STYLES.TABLE}>
|
|
||||||
<P8PDataGrid
|
|
||||||
{...P8P_DATA_GRID_CONFIG_PROPS}
|
|
||||||
containerComponentProps={{ sx: STYLES.DATA_GRID_CONTAINER(costJobsSpecs.morePages), elevation: 4 }}
|
|
||||||
columnsDef={costJobsSpecs.columnsDef}
|
|
||||||
rows={costJobsSpecs.rows}
|
|
||||||
size={P8P_DATA_GRID_SIZE.SMALL}
|
|
||||||
morePages={costJobsSpecs.morePages}
|
|
||||||
reloading={costJobsSpecs.reload}
|
|
||||||
onOrderChanged={handleCostJobsSpecOrderChanged}
|
|
||||||
onPagesCountChanged={handleCostJobsSpecPagesCountChanged}
|
|
||||||
dataCellRender={prms =>
|
|
||||||
dataCellRender({
|
|
||||||
...prms,
|
|
||||||
handleSelectChange,
|
|
||||||
sUnit: UNIT_COST_JOBS_SPECS,
|
|
||||||
selectedJobSpec: costJobsSpecs.selectedRow
|
|
||||||
})
|
|
||||||
}
|
|
||||||
headCellRender={prms => headCellRender({ ...prms })}
|
|
||||||
fixedHeader={costJobsSpecs.fixedHeader}
|
|
||||||
fixedColumns={costJobsSpecs.fixedColumns}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
) : null}
|
|
||||||
</Grid>
|
|
||||||
<Grid item sx={STYLES.CONTAINER} xs={6}>
|
|
||||||
<Typography sx={STYLES.TABLE_HEADER} variant={"h6"} color={"text.secondary"}>
|
|
||||||
Рабочие
|
|
||||||
</Typography>
|
|
||||||
{costJobsWorkers.dataLoaded ? (
|
|
||||||
<>
|
|
||||||
<Box sx={STYLES.TABLE_BUTTONS}>
|
|
||||||
<Stack direction={"row"} spacing={1}>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
size="small"
|
|
||||||
disabled={!(costJobsSpecs.selectedRow.NRESOURCE_NUMB === costJobsWorkers.selectedRows.length)}
|
|
||||||
onClick={() => handleShowIncludeChange(true)}
|
|
||||||
>
|
|
||||||
Включить в задание
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
size="small"
|
|
||||||
disabled={!costJobsSpecs.selectedRow.NRN || !costJobsSpecs.selectedRow.NPERFORM_FACT}
|
|
||||||
onClick={handleCostJobsSpecExcludeWorker}
|
|
||||||
>
|
|
||||||
Исключить из задания
|
|
||||||
</Button>
|
|
||||||
</Stack>
|
|
||||||
</Box>
|
|
||||||
<Box sx={STYLES.TABLE}>
|
|
||||||
<P8PDataGrid
|
|
||||||
{...P8P_DATA_GRID_CONFIG_PROPS}
|
|
||||||
containerComponentProps={{ sx: STYLES.DATA_GRID_CONTAINER(costJobsWorkers.morePages), elevation: 4 }}
|
|
||||||
columnsDef={costJobsWorkers.columnsDef}
|
|
||||||
rows={costJobsWorkers.rows}
|
|
||||||
size={P8P_DATA_GRID_SIZE.SMALL}
|
|
||||||
morePages={costJobsWorkers.morePages}
|
|
||||||
reloading={costJobsWorkers.reload}
|
|
||||||
onOrderChanged={handleCostJobsWorkersOrderChanged}
|
|
||||||
onPagesCountChanged={handleCostJobsWorkersPagesCountChanged}
|
|
||||||
dataCellRender={prms =>
|
|
||||||
dataCellRender({
|
|
||||||
...prms,
|
|
||||||
handleSelectChange,
|
|
||||||
sUnit: UNIT_WORKERS,
|
|
||||||
selectedWorkerRows: costJobsWorkers.selectedRows,
|
|
||||||
selectedJobSpec: costJobsSpecs.selectedRow
|
|
||||||
})
|
|
||||||
}
|
|
||||||
headCellRender={prms => headCellRender({ ...prms })}
|
|
||||||
fixedHeader={true}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
) : null}
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
) : null}
|
|
||||||
{showInclude ? (
|
|
||||||
<CostJobsSpecsInclude
|
|
||||||
includePrms={{
|
|
||||||
NFCJOBSSP: costJobsSpecs.selectedRow.NRN,
|
|
||||||
SELECTED_WORKERS: costJobsWorkers.selectedRows,
|
|
||||||
NQUANT_PLAN: costJobsSpecs.selectedRow.NQUANT_PLAN
|
|
||||||
}}
|
|
||||||
setShowInclude={setShowInclude}
|
|
||||||
setCostJobsSpecs={setCostJobsSpecs}
|
|
||||||
setCostJobsWorkers={setCostJobsWorkers}
|
|
||||||
includeWorker={includeWorker}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export { MechRecCostJobs };
|
|
@ -1,103 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - ПУП - Выдача сменного задания на участок
|
|
||||||
Панель мониторинга: Диалог включения рабочего в сменное задание
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React, { useState } from "react"; //Классы React
|
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
|
||||||
import { Box, Button, Dialog, DialogTitle, DialogContent, TextField, DialogActions } from "@mui/material"; //Интерфейсные элементы
|
|
||||||
import { BUTTONS } from "../../../app.text"; //Текстовые ресурсы
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Диалог включения рабочего в сменное задание
|
|
||||||
const CostJobsSpecsInclude = ({ includePrms, setShowInclude, setCostJobsSpecs, setCostJobsWorkers, includeWorker }) => {
|
|
||||||
//Собственное состояние - Значение приоритета
|
|
||||||
const [state, setState] = useState(includePrms.NQUANT_PLAN);
|
|
||||||
|
|
||||||
//При закрытии включения рабочего
|
|
||||||
const handlePriorEditClose = () => setShowInclude(false);
|
|
||||||
|
|
||||||
//При включении рабочего в строку сменного задания
|
|
||||||
const costJobsSpecIncludeCostEquipment = () => {
|
|
||||||
//Делаем асинхронно, чтобы при ошибке ничего не обновлять
|
|
||||||
const includeAsync = async () => {
|
|
||||||
//Включаем рабочего в строку сменного задания
|
|
||||||
try {
|
|
||||||
await includeWorker({
|
|
||||||
NFCJOBSSP: includePrms.NFCJOBSSP,
|
|
||||||
SELECTED_WORKERS: includePrms.SELECTED_WORKERS,
|
|
||||||
NQUANT_PLAN: state
|
|
||||||
});
|
|
||||||
//Необходимо обновить все данные
|
|
||||||
setCostJobsSpecs(pv => ({ ...pv, selectedRow: {}, pageNumber: 1, reload: true }));
|
|
||||||
setCostJobsWorkers(pv => ({ ...pv, selectedRows: [], pageNumber: 1, reload: true }));
|
|
||||||
handlePriorEditClose();
|
|
||||||
} catch (e) {
|
|
||||||
throw new Error(e.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//Включаем рабочего асинхронно
|
|
||||||
includeAsync();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog open onClose={() => handlePriorEditClose()}>
|
|
||||||
<DialogTitle>Включить в задание</DialogTitle>
|
|
||||||
<DialogContent>
|
|
||||||
<Box>
|
|
||||||
<TextField
|
|
||||||
name="editInculdeValue"
|
|
||||||
label="Количество"
|
|
||||||
variant="standard"
|
|
||||||
fullWidth
|
|
||||||
InputProps={{
|
|
||||||
type: "number",
|
|
||||||
inputProps: {
|
|
||||||
max: includePrms.NQUANT_PLAN,
|
|
||||||
min: 0
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
value={state}
|
|
||||||
onChange={event => {
|
|
||||||
var value = parseInt(event.target.value, 10);
|
|
||||||
if (value > includePrms.NQUANT_PLAN) {
|
|
||||||
value = includePrms.NQUANT_PLAN;
|
|
||||||
}
|
|
||||||
if (value < 0) {
|
|
||||||
value = 0;
|
|
||||||
}
|
|
||||||
setState(value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Box></Box>
|
|
||||||
</Box>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button onClick={costJobsSpecIncludeCostEquipment}>{BUTTONS.OK}</Button>
|
|
||||||
<Button onClick={() => handlePriorEditClose(null)}>{BUTTONS.CANCEL}</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств - Диалог включения рабочего в сменное задание
|
|
||||||
CostJobsSpecsInclude.propTypes = {
|
|
||||||
includePrms: PropTypes.object.isRequired,
|
|
||||||
setShowInclude: PropTypes.func.isRequired,
|
|
||||||
setCostJobsSpecs: PropTypes.func.isRequired,
|
|
||||||
setCostJobsWorkers: PropTypes.func.isRequired,
|
|
||||||
includeWorker: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export { CostJobsSpecsInclude };
|
|
@ -48,8 +48,7 @@ const useCostRouteLists = (task, taskType) => {
|
|||||||
NPAGE_SIZE: DATA_GRID_PAGE_SIZE,
|
NPAGE_SIZE: DATA_GRID_PAGE_SIZE,
|
||||||
NINCLUDE_DEF: costRouteLists.dataLoaded ? 0 : 1
|
NINCLUDE_DEF: costRouteLists.dataLoaded ? 0 : 1
|
||||||
},
|
},
|
||||||
attributeValueProcessor: (name, val) =>
|
attributeValueProcessor: (name, val) => (["DEXEC_DATE", "DREL_DATE"].includes(name) ? formatDateRF(val) : val),
|
||||||
["DEXEC_DATE", "DREL_DATE"].includes(name) ? formatDateRF(val) : ["SDOCPREF", "SDOCNUMB"].includes(name) ? undefined : val,
|
|
||||||
respArg: "COUT"
|
respArg: "COUT"
|
||||||
});
|
});
|
||||||
setCostRouteLists(pv => ({
|
setCostRouteLists(pv => ({
|
||||||
|
@ -84,26 +84,7 @@ const STYLES = {
|
|||||||
TASK_DIALOG_ACTION_CONTAINER: { border: 1, borderColor: "text.primary", borderRadius: "5px", width: "100%" },
|
TASK_DIALOG_ACTION_CONTAINER: { border: 1, borderColor: "text.primary", borderRadius: "5px", width: "100%" },
|
||||||
FILTERS: { display: "table", float: "right" },
|
FILTERS: { display: "table", float: "right" },
|
||||||
FILTERS_DATE: { display: "table-cell", verticalAlign: "middle" },
|
FILTERS_DATE: { display: "table-cell", verticalAlign: "middle" },
|
||||||
FILTERS_LEVEL: { display: "table-cell", verticalAlign: "middle", paddingLeft: "15px" },
|
FILTERS_LEVEL: { display: "table-cell", verticalAlign: "middle", paddingLeft: "15px" }
|
||||||
FILTERS_LEVEL_CAPTION: { display: "flex", alignItems: "center" },
|
|
||||||
FILTERS_LEVEL_LIMIT_ICON: { padding: "0px 8px", color: "#9f9c9c" },
|
|
||||||
FILTERS_LIMIT_SELECT: nOutOfLimit => {
|
|
||||||
return nOutOfLimit === 1
|
|
||||||
? {
|
|
||||||
".MuiOutlinedInput-notchedOutline": {
|
|
||||||
borderColor: "#e9863c"
|
|
||||||
},
|
|
||||||
"&:hover .MuiOutlinedInput-notchedOutline": {
|
|
||||||
borderColor: "#e9863c",
|
|
||||||
borderWidth: "0.15rem"
|
|
||||||
},
|
|
||||||
"&.Mui-focused .MuiOutlinedInput-notchedOutline": {
|
|
||||||
borderColor: "#e9863c",
|
|
||||||
borderWidth: "0.15rem"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
: {};
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//------------------------------------
|
//------------------------------------
|
||||||
@ -255,7 +236,6 @@ const MechRecCostProdPlans = () => {
|
|||||||
selectedPlanCtlg: null,
|
selectedPlanCtlg: null,
|
||||||
selectedPlanCtlgMaxLevel: null,
|
selectedPlanCtlgMaxLevel: null,
|
||||||
selectedPlanCtlgLevel: null,
|
selectedPlanCtlgLevel: null,
|
||||||
selectedPlanCtlgOutOfLimit: 0,
|
|
||||||
selectedPlanCtlgSort: null,
|
selectedPlanCtlgSort: null,
|
||||||
selectedPlanCtlgMenuItems: null,
|
selectedPlanCtlgMenuItems: null,
|
||||||
gantt: {},
|
gantt: {},
|
||||||
@ -278,9 +258,6 @@ const MechRecCostProdPlans = () => {
|
|||||||
//Подключение к контексту навигации
|
//Подключение к контексту навигации
|
||||||
const { getNavigationSearch } = useContext(NavigationCtx);
|
const { getNavigationSearch } = useContext(NavigationCtx);
|
||||||
|
|
||||||
//Подключение к контексту сообщений
|
|
||||||
const { showMsgInfo } = useContext(MessagingСtx);
|
|
||||||
|
|
||||||
//Инициализация каталогов планов
|
//Инициализация каталогов планов
|
||||||
const initPlanCtlgs = useCallback(async () => {
|
const initPlanCtlgs = useCallback(async () => {
|
||||||
if (!state.init) {
|
if (!state.init) {
|
||||||
@ -303,7 +280,6 @@ const MechRecCostProdPlans = () => {
|
|||||||
selectedPlanCtlgSpecsLoaded: false,
|
selectedPlanCtlgSpecsLoaded: false,
|
||||||
selectedPlanCtlgMaxLevel: null,
|
selectedPlanCtlgMaxLevel: null,
|
||||||
selectedPlanCtlgLevel: null,
|
selectedPlanCtlgLevel: null,
|
||||||
selectedPlanCtlgOutOfLimit: 0,
|
|
||||||
selectedPlanCtlgSort: null,
|
selectedPlanCtlgSort: null,
|
||||||
selectedPlanCtlgMenuItems: null,
|
selectedPlanCtlgMenuItems: null,
|
||||||
gantt: {},
|
gantt: {},
|
||||||
@ -321,7 +297,6 @@ const MechRecCostProdPlans = () => {
|
|||||||
selectedPlanCtlg: null,
|
selectedPlanCtlg: null,
|
||||||
selectedPlanCtlgMaxLevel: null,
|
selectedPlanCtlgMaxLevel: null,
|
||||||
selectedPlanCtlgLevel: null,
|
selectedPlanCtlgLevel: null,
|
||||||
selectedPlanCtlgOutOfLimit: 0,
|
|
||||||
selectedPlanCtlgSort: null,
|
selectedPlanCtlgSort: null,
|
||||||
selectedPlanCtlgMenuItems: null,
|
selectedPlanCtlgMenuItems: null,
|
||||||
gantt: {},
|
gantt: {},
|
||||||
@ -342,7 +317,6 @@ const MechRecCostProdPlans = () => {
|
|||||||
...pv,
|
...pv,
|
||||||
selectedPlanCtlgMaxLevel: data.NMAX_LEVEL,
|
selectedPlanCtlgMaxLevel: data.NMAX_LEVEL,
|
||||||
selectedPlanCtlgLevel: level || level === 0 ? level : data.NMAX_LEVEL,
|
selectedPlanCtlgLevel: level || level === 0 ? level : data.NMAX_LEVEL,
|
||||||
selectedPlanCtlgOutOfLimit: data.NOUT_OF_LIMIT,
|
|
||||||
selectedPlanCtlgSort: sort,
|
selectedPlanCtlgSort: sort,
|
||||||
selectedPlanCtlgMenuItems: state.selectedPlanCtlgMenuItems
|
selectedPlanCtlgMenuItems: state.selectedPlanCtlgMenuItems
|
||||||
? state.selectedPlanCtlgMenuItems
|
? state.selectedPlanCtlgMenuItems
|
||||||
@ -396,17 +370,6 @@ const MechRecCostProdPlans = () => {
|
|||||||
setState(pv => ({ ...pv, selectedTaskDetail: taskRn, selectedTaskDetailType: taskType }));
|
setState(pv => ({ ...pv, selectedTaskDetail: taskRn, selectedTaskDetailType: taskType }));
|
||||||
};
|
};
|
||||||
|
|
||||||
//При открытии окна информации об ограничении уровня
|
|
||||||
const handleLevelLimitInfoOpen = () => {
|
|
||||||
//Отображаем информацию
|
|
||||||
showMsgInfo(
|
|
||||||
`Размер производственной программы превышает предельно допустимый для одновременного отображения в виде диаграммы Ганта.
|
|
||||||
Доступные для просмотра уровни вложенности ограничены.
|
|
||||||
Вы можете просматривать производственную программу частями, используя действие "Открытие панели Производственная программа" в спецификации "Выпуск"
|
|
||||||
раздела "Планы и отчеты производства изделий".`
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Генерация содержимого
|
//Генерация содержимого
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
@ -471,16 +434,8 @@ const MechRecCostProdPlans = () => {
|
|||||||
</Select>
|
</Select>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={STYLES.FILTERS_LEVEL}>
|
<Box sx={STYLES.FILTERS_LEVEL}>
|
||||||
<Box sx={STYLES.FILTERS_LEVEL_CAPTION}>
|
<InputLabel id="select-label-level">До уровня</InputLabel>
|
||||||
<InputLabel id="select-label-level">До уровня</InputLabel>
|
|
||||||
{state.selectedPlanCtlgOutOfLimit === 1 ? (
|
|
||||||
<IconButton sx={STYLES.FILTERS_LEVEL_LIMIT_ICON} onClick={handleLevelLimitInfoOpen}>
|
|
||||||
<Icon>info</Icon>
|
|
||||||
</IconButton>
|
|
||||||
) : null}
|
|
||||||
</Box>
|
|
||||||
<Select
|
<Select
|
||||||
sx={STYLES.FILTERS_LIMIT_SELECT(state.selectedPlanCtlgOutOfLimit)}
|
|
||||||
labelId="select-label-level"
|
labelId="select-label-level"
|
||||||
id="select-level"
|
id="select-level"
|
||||||
value={state.selectedPlanCtlgLevel}
|
value={state.selectedPlanCtlgLevel}
|
||||||
|
@ -1,52 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - Редактор панелей
|
|
||||||
Редактор свойств компонента панели
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React from "react"; //Классы React
|
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
|
||||||
import { Box, Typography } from "@mui/material"; //Интерфейсные элементы
|
|
||||||
import { useComponentModule } from "./components/components_hooks"; //Хуки компонентов
|
|
||||||
import "./panels_editor.css"; //Стили редактора
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Редактор свойств компонента панели
|
|
||||||
const ComponentEditor = ({ id, path, settings = {}, valueProviders = {}, onSettingsChange = null } = {}) => {
|
|
||||||
//Подгрузка модуля редактора компонента (lazy здесь постоянно обновлялся при смене props, поэтому на хуке, от props независимого)
|
|
||||||
const [ComponentEditor, init] = useComponentModule({ path, module: "editor" });
|
|
||||||
|
|
||||||
//Расчёт флага наличия компонента
|
|
||||||
const haveComponent = path ? true : false;
|
|
||||||
|
|
||||||
//Формирование представления
|
|
||||||
return (
|
|
||||||
<Box className={"component-editor__wrap"}>
|
|
||||||
{haveComponent && init && (
|
|
||||||
<ComponentEditor.default id={id} {...settings} valueProviders={valueProviders} onSettingsChange={onSettingsChange} />
|
|
||||||
)}
|
|
||||||
{!haveComponent && <Typography align={"center"}>Компонент не определён</Typography>}
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств компонента - редактор свойств компонента панели
|
|
||||||
ComponentEditor.propTypes = {
|
|
||||||
id: PropTypes.string.isRequired,
|
|
||||||
path: PropTypes.string.isRequired,
|
|
||||||
settings: PropTypes.object,
|
|
||||||
valueProviders: PropTypes.object,
|
|
||||||
onSettingsChange: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export { ComponentEditor };
|
|
@ -1,72 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - Редактор панелей
|
|
||||||
Представление компонента панели
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React from "react"; //Классы React
|
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
|
||||||
import { Box, Typography } from "@mui/material"; //Интерфейсные элементы
|
|
||||||
import { useComponentModule } from "./components/components_hooks"; //Хуки компонентов
|
|
||||||
import "./panels_editor.css"; //Стили редактора
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Представление компонента панели
|
|
||||||
const ComponentView = ({ id, path, settings = {}, values = {}, onValuesChange = null } = {}) => {
|
|
||||||
//Подгрузка модуля представления компонента (lazy здесь постоянно обновлялся при смене props, поэтому на хуке, от props независимого)
|
|
||||||
const [ComponentView, init] = useComponentModule({ path, module: "view" });
|
|
||||||
|
|
||||||
//При смене значений
|
|
||||||
const handleValuesChange = values => onValuesChange && onValuesChange(id, values);
|
|
||||||
|
|
||||||
//Расчёт флага наличия компонента
|
|
||||||
const haveComponent = path ? true : false;
|
|
||||||
|
|
||||||
//Формирование представления
|
|
||||||
return (
|
|
||||||
<Box className={"component-view__wrap"}>
|
|
||||||
{haveComponent && init && <ComponentView.default id={id} {...settings} values={values} onValuesChange={handleValuesChange} />}
|
|
||||||
{!haveComponent && <Typography align={"center"}>Компонент не определён</Typography>}
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств компонента - компонент панели
|
|
||||||
ComponentView.propTypes = {
|
|
||||||
id: PropTypes.string.isRequired,
|
|
||||||
path: PropTypes.string.isRequired,
|
|
||||||
settings: PropTypes.object,
|
|
||||||
values: PropTypes.object,
|
|
||||||
onValuesChange: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export { ComponentView };
|
|
||||||
|
|
||||||
//--------------------------
|
|
||||||
//ВАЖНО: Можно на React.lazy
|
|
||||||
//--------------------------
|
|
||||||
|
|
||||||
//ПРИМЕР:
|
|
||||||
/*
|
|
||||||
import React, { Suspense, lazy } from "react"; //Классы React
|
|
||||||
const ComponentView = ({ path = null, props = {} } = {}) => {
|
|
||||||
const haveComponent = path ? true : false;
|
|
||||||
const ComponentView = haveComponent ? lazy(() => import(`./components/${path}/view`)) : null;
|
|
||||||
return (
|
|
||||||
<Paper sx={STYLES.CONTAINER}>
|
|
||||||
{haveComponent && (<Suspense fallback={null}><ComponentView {...props}/></Suspense>)}
|
|
||||||
{!haveComponent && <Typography align={"center"}>Компонент не определён</Typography>}
|
|
||||||
</Paper>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
*/
|
|
@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - Редактор панелей
|
|
||||||
Компоненты: График (редактор настроек)
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React, { useEffect, useState } from "react"; //Классы React
|
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
|
||||||
import { DATA_SOURCE_SHAPE, DataSource, EditorBox, EditorSubHeader } from "../editors_common"; //Общие компоненты редакторов
|
|
||||||
import "../../panels_editor.css"; //Стили редактора
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//График (редактор настроек)
|
|
||||||
const ChartEditor = ({ id, dataSource = null, valueProviders = {}, onSettingsChange = null } = {}) => {
|
|
||||||
//Собственное состояние - текущие настройки
|
|
||||||
const [settings, setSettings] = useState(null);
|
|
||||||
|
|
||||||
//При изменении компонента
|
|
||||||
useEffect(() => {
|
|
||||||
settings?.id != id && setSettings({ id, dataSource });
|
|
||||||
}, [settings, id, dataSource]);
|
|
||||||
|
|
||||||
//При сохранении изменений элемента
|
|
||||||
const handleDataSourceChange = dataSource => setSettings(pv => ({ ...pv, dataSource: { ...dataSource } }));
|
|
||||||
|
|
||||||
//При сохранении настроек
|
|
||||||
const handleSave = (closeEditor = false) => onSettingsChange && onSettingsChange({ id, settings, closeEditor });
|
|
||||||
|
|
||||||
//Формирование представления
|
|
||||||
return (
|
|
||||||
<EditorBox title={"Параметры графика"} onSave={handleSave}>
|
|
||||||
<EditorSubHeader title={"Источник данных"} />
|
|
||||||
<DataSource dataSource={settings?.dataSource} valueProviders={valueProviders} onChange={handleDataSourceChange} />
|
|
||||||
</EditorBox>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств компонента - График (редактор настроек)
|
|
||||||
ChartEditor.propTypes = {
|
|
||||||
id: PropTypes.string.isRequired,
|
|
||||||
dataSource: DATA_SOURCE_SHAPE,
|
|
||||||
valueProviders: PropTypes.object,
|
|
||||||
onSettingsChange: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export default ChartEditor;
|
|
@ -1,79 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - Редактор панелей
|
|
||||||
Компоненты: График (представление)
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React from "react"; //Классы React
|
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
|
||||||
import { Paper } from "@mui/material"; //Интерфейсные элементы
|
|
||||||
import { P8PChart } from "../../../../components/p8p_chart"; //График
|
|
||||||
import { useComponentDataSource } from "../components_hooks"; //Хуки для данных
|
|
||||||
import { DATA_SOURCE_SHAPE } from "../editors_common"; //Общие объекты компонентов
|
|
||||||
import { COMPONENT_MESSAGE_TYPE, COMPONENT_MESSAGES, ComponentInlineMessage } from "../views_common"; //Общие компоненты представлений
|
|
||||||
import "../../panels_editor.css"; //Стили редактора
|
|
||||||
|
|
||||||
//---------
|
|
||||||
//Константы
|
|
||||||
//---------
|
|
||||||
|
|
||||||
//Иконка компонента
|
|
||||||
const COMPONENT_ICON = "bar_chart";
|
|
||||||
|
|
||||||
//Наименование компонента
|
|
||||||
const COMPONENT_NAME = "График";
|
|
||||||
|
|
||||||
//Стили
|
|
||||||
const STYLES = {
|
|
||||||
CHART: { width: "100%", height: "100%", alignItems: "center", justifyContent: "center", display: "flex" }
|
|
||||||
};
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//График (представление)
|
|
||||||
const Chart = ({ dataSource = null, values = {} } = {}) => {
|
|
||||||
//Собственное состояние - данные
|
|
||||||
const [data, error] = useComponentDataSource({ dataSource, values });
|
|
||||||
|
|
||||||
//Флаг настроенности графика
|
|
||||||
const haveConfing = dataSource?.stored ? true : false;
|
|
||||||
|
|
||||||
//Флаг наличия данных
|
|
||||||
const haveData = data?.init === true && !error ? true : false;
|
|
||||||
|
|
||||||
//Данные графика
|
|
||||||
const chart = data?.XCHART || {};
|
|
||||||
|
|
||||||
//Формирование представления
|
|
||||||
return (
|
|
||||||
<Paper className={"component-view__container component-view__container__empty"} elevation={6}>
|
|
||||||
{haveConfing && haveData ? (
|
|
||||||
<P8PChart style={STYLES.CHART} {...chart} />
|
|
||||||
) : (
|
|
||||||
<ComponentInlineMessage
|
|
||||||
icon={COMPONENT_ICON}
|
|
||||||
name={COMPONENT_NAME}
|
|
||||||
message={!haveConfing ? COMPONENT_MESSAGES.NO_SETTINGS : error ? error : COMPONENT_MESSAGES.NO_DATA_FOUND}
|
|
||||||
type={error ? COMPONENT_MESSAGE_TYPE.ERROR : COMPONENT_MESSAGE_TYPE.COMMON}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Paper>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств компонента - График (представление)
|
|
||||||
Chart.propTypes = {
|
|
||||||
dataSource: DATA_SOURCE_SHAPE,
|
|
||||||
values: PropTypes.object
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export default Chart;
|
|
@ -1,129 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - Редактор панелей
|
|
||||||
Компоненты: Описание
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------
|
|
||||||
//Константы
|
|
||||||
//---------
|
|
||||||
|
|
||||||
const COMPONETNS = [
|
|
||||||
{
|
|
||||||
name: "Форма",
|
|
||||||
path: "form",
|
|
||||||
settings: {
|
|
||||||
title: "Параметры формирования",
|
|
||||||
autoApply: true,
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
name: "AGENT",
|
|
||||||
caption: "Контрагент",
|
|
||||||
unitCode2: "AGNLIST",
|
|
||||||
unitName: "Контрагенты",
|
|
||||||
showMethod: "main",
|
|
||||||
showMethodName: "main",
|
|
||||||
parameter: "Мнемокод",
|
|
||||||
inputParameter: "in_AGNABBR",
|
|
||||||
outputParameter: "out_AGNABBR"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "DOC_TYPE",
|
|
||||||
caption: "Тип документа",
|
|
||||||
unitCode2: "DOCTYPES",
|
|
||||||
unitName: "Типы документов",
|
|
||||||
showMethod: "main",
|
|
||||||
showMethodName: "main",
|
|
||||||
parameter: "Мнемокод",
|
|
||||||
inputParameter: "in_DOCCODE",
|
|
||||||
outputParameter: "out_DOCCODE"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "График",
|
|
||||||
path: "chart",
|
|
||||||
settings2: {
|
|
||||||
dataSource: {
|
|
||||||
type: "USER_PROC",
|
|
||||||
userProc: "ГрафТоп5ДогКонтрТип",
|
|
||||||
stored: "UDO_P_P8P_AGNCONTR_CHART",
|
|
||||||
respArg: "COUT",
|
|
||||||
arguments: [
|
|
||||||
{ name: "SAGENT", caption: "Контрагент", dataType: "STR", req: false, value: "", valueSource: "AGENT" },
|
|
||||||
{ name: "SDOC_TYPE", caption: "Тип документа", dataType: "STR", req: false, value: "", valueSource: "DOC_TYPE" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Таблица",
|
|
||||||
path: "table",
|
|
||||||
settings2: {
|
|
||||||
dataSource: {
|
|
||||||
type: "USER_PROC",
|
|
||||||
userProc: "ТаблицаДогКонтрТип",
|
|
||||||
stored: "UDO_P_P8P_AGNCONTR_TABLE",
|
|
||||||
respArg: "COUT",
|
|
||||||
arguments: [
|
|
||||||
{ name: "SAGENT", caption: "Контрагент", dataType: "STR", req: false, value: "", valueSource: "AGENT" },
|
|
||||||
{ name: "SDOC_TYPE", caption: "Тип документа", dataType: "STR", req: false, value: "", valueSource: "DOC_TYPE" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Индикатор",
|
|
||||||
path: "indicator",
|
|
||||||
settings: {
|
|
||||||
dataSource: {
|
|
||||||
type: "USER_PROC",
|
|
||||||
userProc: "ИндКолДогКонтрТип",
|
|
||||||
stored: "UDO_P_P8P_AGNCONTR_IND",
|
|
||||||
respArg: "COUT",
|
|
||||||
arguments: [
|
|
||||||
{ name: "SAGENT", caption: "Контрагент", dataType: "STR", req: false, value: "", valueSource: "AGENT" },
|
|
||||||
{ name: "SDOC_TYPE", caption: "Тип документа", dataType: "STR", req: false, value: "", valueSource: "DOC_TYPE" },
|
|
||||||
{
|
|
||||||
name: "NIND_TYPE",
|
|
||||||
caption: "Тип индикатора (0 - все, 1 - неутвержденные)",
|
|
||||||
dataType: "NUMB",
|
|
||||||
req: true,
|
|
||||||
value: "0",
|
|
||||||
valueSource: ""
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Индикатор2",
|
|
||||||
path: "indicator",
|
|
||||||
settings: {
|
|
||||||
dataSource: {
|
|
||||||
type: "USER_PROC",
|
|
||||||
userProc: "ИндКолДогКонтрТип",
|
|
||||||
stored: "UDO_P_P8P_AGNCONTR_IND",
|
|
||||||
respArg: "COUT",
|
|
||||||
arguments: [
|
|
||||||
{ name: "SAGENT", caption: "Контрагент", dataType: "STR", req: false, value: "", valueSource: "AGENT" },
|
|
||||||
{ name: "SDOC_TYPE", caption: "Тип документа", dataType: "STR", req: false, value: "", valueSource: "DOC_TYPE" },
|
|
||||||
{
|
|
||||||
name: "NIND_TYPE",
|
|
||||||
caption: "Тип индикатора (0 - все, 1 - неутвержденные)",
|
|
||||||
dataType: "NUMB",
|
|
||||||
req: true,
|
|
||||||
value: "1",
|
|
||||||
valueSource: ""
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export { COMPONETNS };
|
|
@ -1,174 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - Редактор панелей
|
|
||||||
Компоненты: Хуки компонентов
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import { useState, useContext, useEffect, useRef } from "react"; //Классы React
|
|
||||||
import client from "../../../core/client"; //Клиент взаимодействия с сервером приложений
|
|
||||||
import { formatErrorMessage } from "../../../core/utils"; //Общие вспомогательные функции
|
|
||||||
import { BackEndСtx } from "../../../context/backend"; //Контекст взаимодействия с сервером
|
|
||||||
import { DATA_SOURCE_TYPE, ARGUMENT_DATA_TYPE } from "./editors_common"; //Общие объекты редакторов
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Загрузка модуля компонента из модуля (можно применять как альтернативу React.lazy)
|
|
||||||
const useComponentModule = ({ path = null, module = "view" } = {}) => {
|
|
||||||
//Собственное состояние - импортированный модуль компонента
|
|
||||||
const [componentModule, setComponentModule] = useState(null);
|
|
||||||
|
|
||||||
//Собственное состояние - флаг готовности
|
|
||||||
const [init, setInit] = useState(false);
|
|
||||||
|
|
||||||
//При подмонтировании к странице
|
|
||||||
useEffect(() => {
|
|
||||||
//Динамическая загрузка модуля компонента из библиотеки
|
|
||||||
const importComponentModule = async () => {
|
|
||||||
setInit(false);
|
|
||||||
const moduleContent = await import(`./${path}/${module}`);
|
|
||||||
setComponentModule(moduleContent);
|
|
||||||
setInit(true);
|
|
||||||
};
|
|
||||||
if (path) importComponentModule();
|
|
||||||
}, [path, module]);
|
|
||||||
|
|
||||||
//Возвращаем интерфейс хука
|
|
||||||
return [componentModule, init];
|
|
||||||
};
|
|
||||||
|
|
||||||
//Описание пользовательской процедуры
|
|
||||||
const useUserProcDesc = ({ code, refresh }) => {
|
|
||||||
//Собственное состояние - флаг загрузки
|
|
||||||
const [isLoading, setLoading] = useState(false);
|
|
||||||
|
|
||||||
//Собственное состояние - данные
|
|
||||||
const [data, setData] = useState(null);
|
|
||||||
|
|
||||||
//Подключение к контексту взаимодействия с сервером
|
|
||||||
const { executeStored } = useContext(BackEndСtx);
|
|
||||||
|
|
||||||
//При необходимости обновить данные компонента
|
|
||||||
useEffect(() => {
|
|
||||||
//Загрузка данных с сервера
|
|
||||||
const loadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoading(true);
|
|
||||||
const data = await executeStored({
|
|
||||||
stored: "PKG_P8PANELS_EDITOR.USERPROCS_DESC",
|
|
||||||
args: { SCODE: code },
|
|
||||||
respArg: "COUT",
|
|
||||||
isArray: name => name === "arguments",
|
|
||||||
loader: false
|
|
||||||
});
|
|
||||||
setData(data?.XUSERPROC || null);
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//Если надо обновить и есть для чего получать данные
|
|
||||||
if (refresh > 0)
|
|
||||||
if (code) loadData();
|
|
||||||
else setData(null);
|
|
||||||
}, [refresh, code, executeStored]);
|
|
||||||
|
|
||||||
//Возвращаем интерфейс хука
|
|
||||||
return [data, isLoading];
|
|
||||||
};
|
|
||||||
|
|
||||||
//Получение данных компонента из источника
|
|
||||||
const useComponentDataSource = ({ dataSource, values }) => {
|
|
||||||
//Контроллер для прерывания запросов
|
|
||||||
const abortController = useRef(null);
|
|
||||||
|
|
||||||
//Собственное состояние - параметры исполнения
|
|
||||||
const [state, setState] = useState({ stored: null, storedArgs: [], respArg: null, reqSet: false });
|
|
||||||
|
|
||||||
//Собственное состояние - флаг загрузки
|
|
||||||
const [isLoading, setLoading] = useState(false);
|
|
||||||
|
|
||||||
//Собственное состояние - данные
|
|
||||||
const [data, setData] = useState({ init: false });
|
|
||||||
|
|
||||||
//Собственное состояние - ошибка получения данных
|
|
||||||
const [error, setError] = useState(null);
|
|
||||||
|
|
||||||
//Подключение к контексту взаимодействия с сервером
|
|
||||||
const { executeStored } = useContext(BackEndСtx);
|
|
||||||
|
|
||||||
//При необходимости обновить данные
|
|
||||||
useEffect(() => {
|
|
||||||
//Загрузка данных с сервера
|
|
||||||
const loadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoading(true);
|
|
||||||
abortController.current?.abort?.();
|
|
||||||
abortController.current = new AbortController();
|
|
||||||
const data = await executeStored({
|
|
||||||
stored: state.stored,
|
|
||||||
args: { ...(state.storedArgs ? state.storedArgs : {}) },
|
|
||||||
respArg: state.respArg,
|
|
||||||
loader: false,
|
|
||||||
signal: abortController.current.signal,
|
|
||||||
showErrorMessage: false
|
|
||||||
});
|
|
||||||
setError(null);
|
|
||||||
setData({ ...data, init: true });
|
|
||||||
} catch (e) {
|
|
||||||
if (e.message !== client.ERR_ABORTED) {
|
|
||||||
setError(formatErrorMessage(e.message).text);
|
|
||||||
setData({ init: false });
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (state.reqSet) {
|
|
||||||
if (state.stored) loadData();
|
|
||||||
} else setData({ init: false });
|
|
||||||
return () => abortController.current?.abort?.();
|
|
||||||
}, [state.stored, state.storedArgs, state.respArg, state.reqSet, executeStored]);
|
|
||||||
|
|
||||||
//При изменении свойств
|
|
||||||
useEffect(() => {
|
|
||||||
setState(pv => {
|
|
||||||
if (dataSource?.type == DATA_SOURCE_TYPE.USER_PROC) {
|
|
||||||
const { stored, respArg } = dataSource;
|
|
||||||
let reqSet = true;
|
|
||||||
const storedArgs = {};
|
|
||||||
dataSource.arguments.forEach(argument => {
|
|
||||||
let v = argument.valueSource ? values[argument.valueSource] : argument.value;
|
|
||||||
storedArgs[argument.name] =
|
|
||||||
argument.dataType == ARGUMENT_DATA_TYPE.NUMB
|
|
||||||
? isNaN(parseFloat(v))
|
|
||||||
? null
|
|
||||||
: parseFloat(v)
|
|
||||||
: argument.dataType == ARGUMENT_DATA_TYPE.DATE
|
|
||||||
? new Date(v)
|
|
||||||
: String(v === undefined ? "" : v);
|
|
||||||
if (argument.req === true && [undefined, null, ""].includes(storedArgs[argument.name])) reqSet = false;
|
|
||||||
});
|
|
||||||
if (pv.stored != stored || pv.respArg != respArg || JSON.stringify(pv.storedArgs) != JSON.stringify(storedArgs)) {
|
|
||||||
if (!reqSet) {
|
|
||||||
setError("Не заданы обязательные параметры источника данных");
|
|
||||||
setData({ init: false });
|
|
||||||
}
|
|
||||||
return { stored, respArg, storedArgs, reqSet };
|
|
||||||
} else return pv;
|
|
||||||
} else return pv;
|
|
||||||
});
|
|
||||||
}, [dataSource, values]);
|
|
||||||
|
|
||||||
//Возвращаем интерфейс хука
|
|
||||||
return [data, error, isLoading];
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export { useComponentModule, useUserProcDesc, useComponentDataSource };
|
|
@ -1,434 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - Редактор панелей
|
|
||||||
Общие компоненты редакторов свойств
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React, { useState, useContext, useEffect } from "react"; //Классы React
|
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
Stack,
|
|
||||||
IconButton,
|
|
||||||
Icon,
|
|
||||||
Typography,
|
|
||||||
Divider,
|
|
||||||
Chip,
|
|
||||||
Dialog,
|
|
||||||
DialogTitle,
|
|
||||||
DialogContent,
|
|
||||||
DialogActions,
|
|
||||||
Button,
|
|
||||||
TextField,
|
|
||||||
InputAdornment,
|
|
||||||
MenuItem,
|
|
||||||
Menu,
|
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
CardActions,
|
|
||||||
CardActionArea
|
|
||||||
} from "@mui/material"; //Интерфейсные элементы
|
|
||||||
import client from "../../../core/client"; //Клиент БД
|
|
||||||
import { ApplicationСtx } from "../../../context/application"; //Контекст приложения
|
|
||||||
import { BUTTONS } from "../../../../app.text"; //Общие текстовые ресурсы
|
|
||||||
import { useUserProcDesc } from "./components_hooks"; //Общие хуки компонентов
|
|
||||||
import "../panels_editor.css"; //Стили редактора
|
|
||||||
|
|
||||||
//---------
|
|
||||||
//Константы
|
|
||||||
//---------
|
|
||||||
|
|
||||||
//Стили
|
|
||||||
const STYLES = {
|
|
||||||
CHIP: (fullWidth = false, multiLine = false) => ({
|
|
||||||
...(multiLine ? { height: "auto" } : {}),
|
|
||||||
"& .MuiChip-label": {
|
|
||||||
...(multiLine
|
|
||||||
? {
|
|
||||||
display: "block",
|
|
||||||
whiteSpace: "normal"
|
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
...(fullWidth ? { width: "100%" } : {})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
//Типы даных аргументов
|
|
||||||
const ARGUMENT_DATA_TYPE = {
|
|
||||||
STR: client.SERV_DATA_TYPE_STR,
|
|
||||||
NUMB: client.SERV_DATA_TYPE_NUMB,
|
|
||||||
DATE: client.SERV_DATA_TYPE_DATE
|
|
||||||
};
|
|
||||||
|
|
||||||
//Типы источников данных
|
|
||||||
const DATA_SOURCE_TYPE = {
|
|
||||||
USER_PROC: "USER_PROC",
|
|
||||||
QUERY: "QUERY"
|
|
||||||
};
|
|
||||||
|
|
||||||
//Типы источников данных (наименования)
|
|
||||||
const DATA_SOURCE_TYPE_NAME = {
|
|
||||||
[DATA_SOURCE_TYPE.USER_PROC]: "Пользовательская процедура",
|
|
||||||
[DATA_SOURCE_TYPE.QUERY]: "Запрос"
|
|
||||||
};
|
|
||||||
|
|
||||||
//Структура аргумента источника данных
|
|
||||||
const DATA_SOURCE_ARGUMENT_SHAPE = PropTypes.shape({
|
|
||||||
name: PropTypes.string.isRequired,
|
|
||||||
caption: PropTypes.string.isRequired,
|
|
||||||
dataType: PropTypes.oneOf(Object.values(ARGUMENT_DATA_TYPE)),
|
|
||||||
req: PropTypes.bool.isRequired,
|
|
||||||
value: PropTypes.any,
|
|
||||||
valueSource: PropTypes.string
|
|
||||||
});
|
|
||||||
|
|
||||||
//Начальное состояние аргумента источника данных
|
|
||||||
const DATA_SOURCE_ARGUMENT_INITIAL = {
|
|
||||||
name: "",
|
|
||||||
caption: "",
|
|
||||||
dataType: "",
|
|
||||||
req: false,
|
|
||||||
value: "",
|
|
||||||
valueSource: ""
|
|
||||||
};
|
|
||||||
|
|
||||||
//Структура источника данных
|
|
||||||
const DATA_SOURCE_SHAPE = PropTypes.shape({
|
|
||||||
type: PropTypes.oneOf([...Object.values(DATA_SOURCE_TYPE), ""]),
|
|
||||||
userProc: PropTypes.string,
|
|
||||||
stored: PropTypes.string,
|
|
||||||
respArg: PropTypes.string,
|
|
||||||
arguments: PropTypes.arrayOf(DATA_SOURCE_ARGUMENT_SHAPE)
|
|
||||||
});
|
|
||||||
|
|
||||||
//Начальное состояние истоника данных
|
|
||||||
const DATA_SOURCE_INITIAL = {
|
|
||||||
type: "",
|
|
||||||
userProc: "",
|
|
||||||
stored: "",
|
|
||||||
respArg: "",
|
|
||||||
arguments: []
|
|
||||||
};
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Контейнер редактора
|
|
||||||
const EditorBox = ({ title, children, onSave }) => {
|
|
||||||
//При нажатии на "Сохранить"
|
|
||||||
const handleSaveClick = (closeEditor = false) => onSave && onSave(closeEditor);
|
|
||||||
|
|
||||||
//Формирование представления
|
|
||||||
return (
|
|
||||||
<Box className={"component-editor__container"}>
|
|
||||||
<Divider>{title}</Divider>
|
|
||||||
<Stack direction={"column"} spacing={1}>
|
|
||||||
{children}
|
|
||||||
</Stack>
|
|
||||||
<Stack direction={"row"} justifyContent={"right"} p={1}>
|
|
||||||
<IconButton onClick={() => handleSaveClick(false)} title={BUTTONS.APPLY}>
|
|
||||||
<Icon>done</Icon>
|
|
||||||
</IconButton>
|
|
||||||
<IconButton onClick={() => handleSaveClick(true)} title={BUTTONS.SAVE}>
|
|
||||||
<Icon>done_all</Icon>
|
|
||||||
</IconButton>
|
|
||||||
</Stack>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств компонента - контейнер редактора
|
|
||||||
EditorBox.propTypes = {
|
|
||||||
title: PropTypes.string.isRequired,
|
|
||||||
children: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf(PropTypes.node)]),
|
|
||||||
onSave: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
//Заголовок раздела редактора
|
|
||||||
const EditorSubHeader = ({ title }) => {
|
|
||||||
//Формирование представления
|
|
||||||
return (
|
|
||||||
<Divider className={"component-editor__divider"}>
|
|
||||||
<Chip label={title} size={"small"} />
|
|
||||||
</Divider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств компонента - заголовок раздела редактора
|
|
||||||
EditorSubHeader.propTypes = {
|
|
||||||
title: PropTypes.string.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
//Диалог настройки
|
|
||||||
const ConfigDialog = ({ title, children, onOk, onCancel }) => {
|
|
||||||
//Формирование представления
|
|
||||||
return (
|
|
||||||
<Dialog onClose={onCancel} open>
|
|
||||||
<DialogTitle>{title}</DialogTitle>
|
|
||||||
<DialogContent>{children}</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button onClick={() => onOk && onOk()}>{BUTTONS.OK}</Button>
|
|
||||||
<Button onClick={() => onCancel && onCancel()}>{BUTTONS.CANCEL}</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств компонента - диалог настройки
|
|
||||||
ConfigDialog.propTypes = {
|
|
||||||
title: PropTypes.string.isRequired,
|
|
||||||
children: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf(PropTypes.node)]),
|
|
||||||
onOk: PropTypes.func,
|
|
||||||
onCancel: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
//Диалог настройки источника данных
|
|
||||||
const ConfigDataSourceDialog = ({ dataSource = null, valueProviders = {}, onOk = null, onCancel = null } = {}) => {
|
|
||||||
//Собственное состояние - параметры элемента формы
|
|
||||||
const [state, setState] = useState({ ...DATA_SOURCE_INITIAL, ...dataSource });
|
|
||||||
|
|
||||||
//Собственное состояние - флаги обновление данных
|
|
||||||
const [refresh, setRefresh] = useState({ userProcDesc: 0 });
|
|
||||||
|
|
||||||
//Собственное состояние - элемент привязки меню выбора источника
|
|
||||||
const [valueProvidersMenuAnchorEl, setValueProvidersMenuAnchorEl] = useState(null);
|
|
||||||
|
|
||||||
//Описание выбранной пользовательской процедуры
|
|
||||||
const [userProcDesc] = useUserProcDesc({ code: state.userProc, refresh: refresh.userProcDesc });
|
|
||||||
|
|
||||||
//Подключение к контексту приложения
|
|
||||||
const { pOnlineShowDictionary } = useContext(ApplicationСtx);
|
|
||||||
|
|
||||||
//Установка значения/привязки аргумента
|
|
||||||
const setArgumentValueSource = (index, value, valueSource) =>
|
|
||||||
setState(pv => ({
|
|
||||||
...pv,
|
|
||||||
arguments: pv.arguments.map((argument, i) => ({ ...argument, ...(i == index ? { value, valueSource } : {}) }))
|
|
||||||
}));
|
|
||||||
|
|
||||||
//Открытие/сокрытие меню выбора источника
|
|
||||||
const toggleValueProvidersMenu = target => setValueProvidersMenuAnchorEl(target instanceof Element ? target : null);
|
|
||||||
|
|
||||||
//При нажатии на очистку наименования пользовательской процедуры
|
|
||||||
const handleUserProcClearClick = () => setState({ ...DATA_SOURCE_INITIAL });
|
|
||||||
|
|
||||||
//При нажатии на выбор пользовательской процедуры в качестве источника данных
|
|
||||||
const handleUserProcSelectClick = () => {
|
|
||||||
pOnlineShowDictionary({
|
|
||||||
unitCode: "UserProcedures",
|
|
||||||
showMethod: "main",
|
|
||||||
inputParameters: [{ name: "in_CODE", value: state.userProc }],
|
|
||||||
callBack: res => {
|
|
||||||
if (res.success) {
|
|
||||||
setState(pv => ({ ...pv, type: DATA_SOURCE_TYPE.USER_PROC, userProc: res.outParameters.out_CODE }));
|
|
||||||
setRefresh(pv => ({ ...pv, userProcDesc: pv.userProcDesc + 1 }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
//При закрытии дилога с сохранением
|
|
||||||
const handleOk = () => onOk && onOk({ ...state });
|
|
||||||
|
|
||||||
//При закртии диалога отменой
|
|
||||||
const handleCancel = () => onCancel && onCancel();
|
|
||||||
|
|
||||||
//При очистке значения/связывания аргумента
|
|
||||||
const handleArgumentClearClick = index => setArgumentValueSource(index, "", "");
|
|
||||||
|
|
||||||
//При отображении меню связывания аргумента с поставщиком данных
|
|
||||||
const handleArgumentLinkMenuClick = e => setValueProvidersMenuAnchorEl(e.currentTarget);
|
|
||||||
|
|
||||||
//При выборе элемента меню связывания аргумента с поставщиком данных
|
|
||||||
const handleArgumentLinkClick = valueSource => {
|
|
||||||
setArgumentValueSource(valueProvidersMenuAnchorEl.id, "", valueSource);
|
|
||||||
toggleValueProvidersMenu();
|
|
||||||
};
|
|
||||||
|
|
||||||
//При вводе значения аргумента
|
|
||||||
const handleArgumentChange = (index, value) => setArgumentValueSource(index, value, "");
|
|
||||||
|
|
||||||
//При изменении описания пользовательской процедуры
|
|
||||||
useEffect(() => {
|
|
||||||
if (userProcDesc)
|
|
||||||
setState(pv => ({
|
|
||||||
...pv,
|
|
||||||
stored: userProcDesc?.stored?.name,
|
|
||||||
respArg: userProcDesc?.stored?.respArg,
|
|
||||||
arguments: (userProcDesc?.arguments || []).map(argument => ({ ...DATA_SOURCE_ARGUMENT_INITIAL, ...argument }))
|
|
||||||
}));
|
|
||||||
}, [userProcDesc]);
|
|
||||||
|
|
||||||
//Список значений
|
|
||||||
const values = Object.keys(valueProviders).reduce((res, key) => [...res, ...Object.keys(valueProviders[key])], []);
|
|
||||||
|
|
||||||
//Наличие значений
|
|
||||||
const isValues = values && values.length > 0 ? true : false;
|
|
||||||
|
|
||||||
//Меню привязки к поставщикам значений
|
|
||||||
const valueProvidersMenu = isValues && (
|
|
||||||
<Menu anchorEl={valueProvidersMenuAnchorEl} open={Boolean(valueProvidersMenuAnchorEl)} onClose={toggleValueProvidersMenu}>
|
|
||||||
{values.map((value, i) => (
|
|
||||||
<MenuItem key={i} onClick={() => handleArgumentLinkClick(value)}>
|
|
||||||
{value}
|
|
||||||
</MenuItem>
|
|
||||||
))}
|
|
||||||
</Menu>
|
|
||||||
);
|
|
||||||
|
|
||||||
//Формирование представления
|
|
||||||
return (
|
|
||||||
<ConfigDialog title="Настройка источника данных" onOk={handleOk} onCancel={handleCancel}>
|
|
||||||
<Stack direction={"column"} spacing={1}>
|
|
||||||
{valueProvidersMenu}
|
|
||||||
<TextField
|
|
||||||
type={"text"}
|
|
||||||
variant={"standard"}
|
|
||||||
value={state.userProc}
|
|
||||||
label={"Пользовательская процедура"}
|
|
||||||
InputLabelProps={{ shrink: true }}
|
|
||||||
InputProps={{
|
|
||||||
readOnly: true,
|
|
||||||
endAdornment: (
|
|
||||||
<InputAdornment position="end">
|
|
||||||
<IconButton onClick={handleUserProcClearClick}>
|
|
||||||
<Icon>clear</Icon>
|
|
||||||
</IconButton>
|
|
||||||
<IconButton onClick={handleUserProcSelectClick}>
|
|
||||||
<Icon>list</Icon>
|
|
||||||
</IconButton>
|
|
||||||
</InputAdornment>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{Array.isArray(state?.arguments) &&
|
|
||||||
state.arguments.map((argument, i) => (
|
|
||||||
<TextField
|
|
||||||
key={i}
|
|
||||||
type={"text"}
|
|
||||||
variant={"standard"}
|
|
||||||
value={argument.value || argument.valueSource}
|
|
||||||
label={argument.caption}
|
|
||||||
onChange={e => handleArgumentChange(i, e.target.value)}
|
|
||||||
InputLabelProps={{ shrink: true }}
|
|
||||||
InputProps={{
|
|
||||||
endAdornment: (
|
|
||||||
<InputAdornment position="end">
|
|
||||||
<IconButton onClick={() => handleArgumentClearClick(i)}>
|
|
||||||
<Icon>clear</Icon>
|
|
||||||
</IconButton>
|
|
||||||
{isValues && (
|
|
||||||
<IconButton id={i} onClick={handleArgumentLinkMenuClick}>
|
|
||||||
<Icon>settings_ethernet</Icon>
|
|
||||||
</IconButton>
|
|
||||||
)}
|
|
||||||
</InputAdornment>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</Stack>
|
|
||||||
</ConfigDialog>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств компонента - Диалог настройки источника данных
|
|
||||||
ConfigDataSourceDialog.propTypes = {
|
|
||||||
dataSource: DATA_SOURCE_SHAPE,
|
|
||||||
valueProviders: PropTypes.object,
|
|
||||||
onOk: PropTypes.func,
|
|
||||||
onCancel: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
//Источник данных
|
|
||||||
const DataSource = ({ dataSource = null, valueProviders = {}, onChange = null } = {}) => {
|
|
||||||
//Собственное состояние - отображение диалога настройки
|
|
||||||
const [configDlg, setConfigDlg] = useState(false);
|
|
||||||
|
|
||||||
//Уведомление родителя о смене настроек источника данных
|
|
||||||
const notifyChange = settings => onChange && onChange(settings);
|
|
||||||
|
|
||||||
//При нажатии на настройку источника данных
|
|
||||||
const handleSetup = () => setConfigDlg(true);
|
|
||||||
|
|
||||||
//При нажатии на настройку источника данных
|
|
||||||
const handleSetupOk = dataSource => {
|
|
||||||
setConfigDlg(false);
|
|
||||||
notifyChange(dataSource);
|
|
||||||
};
|
|
||||||
|
|
||||||
//При нажатии на настройку источника данных
|
|
||||||
const handleSetupCancel = () => setConfigDlg(false);
|
|
||||||
|
|
||||||
//При удалении настроек источника данных
|
|
||||||
const handleDelete = () => notifyChange({ ...DATA_SOURCE_INITIAL });
|
|
||||||
|
|
||||||
//Расчет флага "настроенности"
|
|
||||||
const configured = dataSource?.type ? true : false;
|
|
||||||
|
|
||||||
//Список аргументов
|
|
||||||
const args =
|
|
||||||
configured &&
|
|
||||||
dataSource.arguments.map((argument, i) => (
|
|
||||||
<Chip
|
|
||||||
key={i}
|
|
||||||
label={`:${argument.name} = ${argument.valueSource || argument.value || "NULL"}`}
|
|
||||||
variant={"outlined"}
|
|
||||||
sx={STYLES.CHIP(true)}
|
|
||||||
/>
|
|
||||||
));
|
|
||||||
|
|
||||||
//Формирование представления
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{configDlg && (
|
|
||||||
<ConfigDataSourceDialog dataSource={dataSource} valueProviders={valueProviders} onOk={handleSetupOk} onCancel={handleSetupCancel} />
|
|
||||||
)}
|
|
||||||
{configured && (
|
|
||||||
<Card variant={"outlined"}>
|
|
||||||
<CardActionArea onClick={handleSetup}>
|
|
||||||
<CardContent>
|
|
||||||
<Typography variant={"subtitle1"} noWrap={true}>
|
|
||||||
{dataSource.type === DATA_SOURCE_TYPE.USER_PROC ? dataSource.userProc : "Источник без наименования"}
|
|
||||||
</Typography>
|
|
||||||
<Typography variant={"caption"} color={"text.secondary"} noWrap={true}>
|
|
||||||
{DATA_SOURCE_TYPE_NAME[dataSource.type] || "Неизвестный тип источника"}
|
|
||||||
</Typography>
|
|
||||||
<Stack direction={"column"} spacing={1} pt={2}>
|
|
||||||
{args}
|
|
||||||
</Stack>
|
|
||||||
</CardContent>
|
|
||||||
</CardActionArea>
|
|
||||||
<CardActions>
|
|
||||||
<IconButton onClick={handleDelete}>
|
|
||||||
<Icon>delete</Icon>
|
|
||||||
</IconButton>
|
|
||||||
</CardActions>
|
|
||||||
</Card>
|
|
||||||
)}
|
|
||||||
{!configured && (
|
|
||||||
<Button startIcon={<Icon>build</Icon>} onClick={handleSetup}>
|
|
||||||
Настроить
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств компонента - Источник данных
|
|
||||||
DataSource.propTypes = {
|
|
||||||
dataSource: DATA_SOURCE_SHAPE,
|
|
||||||
valueProviders: PropTypes.object,
|
|
||||||
onChange: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export { STYLES, ARGUMENT_DATA_TYPE, DATA_SOURCE_TYPE, DATA_SOURCE_SHAPE, DATA_SOURCE_INITIAL, EditorBox, EditorSubHeader, ConfigDialog, DataSource };
|
|
@ -1,49 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - Редактор панелей
|
|
||||||
Компоненты: Форма (общие константы)
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
//Структура элемента формы
|
|
||||||
export const ITEM_SHAPE = PropTypes.shape({
|
|
||||||
name: PropTypes.string.isRequired,
|
|
||||||
caption: PropTypes.string.isRequired,
|
|
||||||
unitCode: PropTypes.string,
|
|
||||||
unitName: PropTypes.string,
|
|
||||||
showMethod: PropTypes.string,
|
|
||||||
showMethodName: PropTypes.string,
|
|
||||||
parameter: PropTypes.string,
|
|
||||||
inputParameter: PropTypes.string,
|
|
||||||
outputParameter: PropTypes.string
|
|
||||||
});
|
|
||||||
|
|
||||||
//Начальное состояние элемента формы
|
|
||||||
export const ITEM_INITIAL = {
|
|
||||||
name: "",
|
|
||||||
caption: "",
|
|
||||||
unitCode: "",
|
|
||||||
unitName: "",
|
|
||||||
showMethod: "",
|
|
||||||
showMethodName: "",
|
|
||||||
parameter: "",
|
|
||||||
inputParameter: "",
|
|
||||||
outputParameter: ""
|
|
||||||
};
|
|
||||||
|
|
||||||
//Начальное состояние элементов формы
|
|
||||||
export const ITEMS_INITIAL = [];
|
|
||||||
|
|
||||||
//Ориентация элементов формы
|
|
||||||
export const ORIENTATION = {
|
|
||||||
H: "H",
|
|
||||||
V: "v"
|
|
||||||
};
|
|
@ -1,306 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - Редактор панелей
|
|
||||||
Компоненты: Форма (редактор настроек)
|
|
||||||
*/
|
|
||||||
|
|
||||||
//TODO: Контроль уникальности имени элемента формы
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React, { useEffect, useState, useContext } from "react"; //Классы React
|
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
|
||||||
import {
|
|
||||||
TextField,
|
|
||||||
Button,
|
|
||||||
Icon,
|
|
||||||
Select,
|
|
||||||
MenuItem,
|
|
||||||
FormControl,
|
|
||||||
InputLabel,
|
|
||||||
FormControlLabel,
|
|
||||||
Switch,
|
|
||||||
Chip,
|
|
||||||
Stack,
|
|
||||||
InputAdornment,
|
|
||||||
IconButton
|
|
||||||
} from "@mui/material"; //Интерфейсные элементы
|
|
||||||
import { ApplicationСtx } from "../../../../context/application"; //Контекст приложения
|
|
||||||
import { STYLES as COMMON_STYLES, EditorBox, EditorSubHeader, ConfigDialog } from "../editors_common"; //Общие компоненты редакторов
|
|
||||||
import { ITEM_SHAPE, ITEM_INITIAL, ITEMS_INITIAL, ORIENTATION } from "./common"; //Общие ресурсы и константы формы
|
|
||||||
|
|
||||||
//---------
|
|
||||||
//Константы
|
|
||||||
//---------
|
|
||||||
|
|
||||||
//Стили
|
|
||||||
const STYLES = {
|
|
||||||
CHIP_ITEM: { ...COMMON_STYLES.CHIP(true, false) }
|
|
||||||
};
|
|
||||||
|
|
||||||
//------------------------------------
|
|
||||||
//Вспомогательные функции и компоненты
|
|
||||||
//------------------------------------
|
|
||||||
|
|
||||||
//Редактор элемента
|
|
||||||
const ItemEditor = ({ item = null, onOk = null, onCancel = null } = {}) => {
|
|
||||||
//Собственное состояние - параметры элемента формы
|
|
||||||
const [state, setState] = useState({ ...ITEM_INITIAL, ...item });
|
|
||||||
|
|
||||||
//Подключение к контексту приложения
|
|
||||||
const { pOnlineShowDictionary } = useContext(ApplicationСtx);
|
|
||||||
|
|
||||||
//При закрытии редактора с сохранением
|
|
||||||
const handleOk = () => onOk && onOk({ ...state });
|
|
||||||
|
|
||||||
//При закрытии редактора с отменой
|
|
||||||
const handleCancel = () => onCancel && onCancel();
|
|
||||||
|
|
||||||
//При изменении параметра элемента
|
|
||||||
const handleChange = e => setState(pv => ({ ...pv, [e.target.id]: e.target.value }));
|
|
||||||
|
|
||||||
//При нажатии на очистку раздела
|
|
||||||
const handleClearUnitClick = () =>
|
|
||||||
setState(pv => ({
|
|
||||||
...pv,
|
|
||||||
unitCode: "",
|
|
||||||
unitName: "",
|
|
||||||
showMethod: "",
|
|
||||||
showMethodName: "",
|
|
||||||
parameter: "",
|
|
||||||
inputParameter: "",
|
|
||||||
outputParameter: ""
|
|
||||||
}));
|
|
||||||
|
|
||||||
//При нажатии на выбор раздела
|
|
||||||
const handleSelectUnitClick = () => {
|
|
||||||
pOnlineShowDictionary({
|
|
||||||
unitCode: "Units",
|
|
||||||
showMethod: "methods",
|
|
||||||
inputParameters: [
|
|
||||||
{ name: "pos_unit_name", value: state.unitName },
|
|
||||||
{ name: "pos_method_name", value: state.showMethodName }
|
|
||||||
],
|
|
||||||
callBack: res =>
|
|
||||||
res.success &&
|
|
||||||
setState(pv => ({
|
|
||||||
...pv,
|
|
||||||
unitCode: res.outParameters.unit_code,
|
|
||||||
unitName: res.outParameters.unit_name,
|
|
||||||
showMethod: res.outParameters.method_code,
|
|
||||||
showMethodName: res.outParameters.method_name,
|
|
||||||
parameter: "",
|
|
||||||
inputParameter: "",
|
|
||||||
outputParameter: ""
|
|
||||||
}))
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
//При нажатии на выбор параметра метода вызова
|
|
||||||
const handleSelectUnitParameterClick = () => {
|
|
||||||
state.unitCode &&
|
|
||||||
state.showMethod &&
|
|
||||||
pOnlineShowDictionary({
|
|
||||||
unitCode: "UnitParams",
|
|
||||||
showMethod: "main",
|
|
||||||
inputParameters: [
|
|
||||||
{ name: "in_UNITCODE", value: state.unitCode },
|
|
||||||
{ name: "in_PARENT_METHOD_CODE", value: state.showMethod },
|
|
||||||
{ name: "in_PARAMNAME", value: state.parameter }
|
|
||||||
],
|
|
||||||
callBack: res =>
|
|
||||||
res.success &&
|
|
||||||
setState(pv => ({
|
|
||||||
...pv,
|
|
||||||
parameter: res.outParameters.out_PARAMNAME,
|
|
||||||
inputParameter: res.outParameters.out_IN_CODE,
|
|
||||||
outputParameter: res.outParameters.out_OUT_CODE
|
|
||||||
}))
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
//Формирование представления
|
|
||||||
return (
|
|
||||||
<ConfigDialog title={`${item ? "Изменение" : "Добавление"} элемента`} onOk={handleOk} onCancel={handleCancel}>
|
|
||||||
<Stack direction={"column"} spacing={1}>
|
|
||||||
<TextField type={"text"} variant={"standard"} value={state.name} label={"Имя"} id={"name"} onChange={handleChange} />
|
|
||||||
<TextField type={"text"} variant={"standard"} value={state.caption} label={"Приглашение"} id={"caption"} onChange={handleChange} />
|
|
||||||
<TextField
|
|
||||||
type={"text"}
|
|
||||||
variant={"standard"}
|
|
||||||
value={state.unitName}
|
|
||||||
label={"Раздел"}
|
|
||||||
InputLabelProps={{ shrink: state.unitName ? true : false }}
|
|
||||||
InputProps={{
|
|
||||||
readOnly: true,
|
|
||||||
endAdornment: (
|
|
||||||
<InputAdornment position="end">
|
|
||||||
<IconButton onClick={handleClearUnitClick}>
|
|
||||||
<Icon>clear</Icon>
|
|
||||||
</IconButton>
|
|
||||||
<IconButton onClick={handleSelectUnitClick}>
|
|
||||||
<Icon>list</Icon>
|
|
||||||
</IconButton>
|
|
||||||
</InputAdornment>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<TextField
|
|
||||||
type={"text"}
|
|
||||||
variant={"standard"}
|
|
||||||
value={state.showMethodName}
|
|
||||||
label={"Метод вызова"}
|
|
||||||
InputLabelProps={{ shrink: state.showMethodName ? true : false }}
|
|
||||||
InputProps={{ readOnly: true }}
|
|
||||||
/>
|
|
||||||
<TextField
|
|
||||||
type={"text"}
|
|
||||||
variant={"standard"}
|
|
||||||
value={state.parameter}
|
|
||||||
label={"Параметр"}
|
|
||||||
InputLabelProps={{ shrink: state.parameter ? true : false }}
|
|
||||||
InputProps={{
|
|
||||||
readOnly: true,
|
|
||||||
endAdornment: (
|
|
||||||
<InputAdornment position="end">
|
|
||||||
<IconButton onClick={handleSelectUnitParameterClick}>
|
|
||||||
<Icon>list</Icon>
|
|
||||||
</IconButton>
|
|
||||||
</InputAdornment>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
</ConfigDialog>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств - редактор элемента
|
|
||||||
ItemEditor.propTypes = {
|
|
||||||
item: ITEM_SHAPE,
|
|
||||||
onOk: PropTypes.func,
|
|
||||||
onCancel: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Форма (редактор настроек)
|
|
||||||
const FormEditor = ({ id, title = "", orientation = ORIENTATION.V, autoApply = false, items = ITEMS_INITIAL, onSettingsChange = null } = {}) => {
|
|
||||||
//Собственное состояние - текущие настройки
|
|
||||||
const [settings, setSettings] = useState(null);
|
|
||||||
|
|
||||||
//Собственное состояние - предоставляемые в панель значения
|
|
||||||
const [providedValues, setProvidedValues] = useState([]);
|
|
||||||
|
|
||||||
//Собственное состояние - редактор элементов формы
|
|
||||||
const [itemEditor, setItemEditor] = useState({ display: false, index: null });
|
|
||||||
|
|
||||||
//При изменении значения настройки
|
|
||||||
const handleChange = e => setSettings({ ...settings, [e.target.name]: e.target.type === "checkbox" ? e.target.checked : e.target.value });
|
|
||||||
|
|
||||||
//При добавлении нового элемента
|
|
||||||
const handleItemAdd = () => setItemEditor({ display: true, index: null });
|
|
||||||
|
|
||||||
//При нажатии на элемент
|
|
||||||
const handleItemClick = i => setItemEditor({ display: true, index: i });
|
|
||||||
|
|
||||||
//При удалении элемента
|
|
||||||
const handleItemDelete = i => {
|
|
||||||
const items = [...settings.items];
|
|
||||||
items.splice(i, 1);
|
|
||||||
setSettings(pv => ({ ...pv, items }));
|
|
||||||
};
|
|
||||||
|
|
||||||
//При сохранении изменений элемента
|
|
||||||
const handleItemSave = item => {
|
|
||||||
const items = [...settings.items];
|
|
||||||
itemEditor.index == null ? items.push({ ...item }) : (items[itemEditor.index] = { ...item });
|
|
||||||
setSettings(pv => ({ ...pv, items }));
|
|
||||||
setItemEditor({ display: false, index: null });
|
|
||||||
};
|
|
||||||
|
|
||||||
//При отмене сохранения изменений элемента
|
|
||||||
const handleItemCancel = () => setItemEditor({ display: false, index: null });
|
|
||||||
|
|
||||||
//При сохранении настроек
|
|
||||||
const handleSave = (closeEditor = false) => onSettingsChange && onSettingsChange({ id, settings, providedValues, closeEditor });
|
|
||||||
|
|
||||||
//При изменении компонента
|
|
||||||
useEffect(() => {
|
|
||||||
settings?.id != id && setSettings({ id, title, orientation, autoApply, items });
|
|
||||||
}, [settings, id, title, orientation, autoApply, items]);
|
|
||||||
|
|
||||||
//При изменении состава элементов формы
|
|
||||||
useEffect(() => {
|
|
||||||
Array.isArray(settings?.items) && setProvidedValues(settings.items.map(item => item.name));
|
|
||||||
}, [settings?.items]);
|
|
||||||
|
|
||||||
//Формирование представления
|
|
||||||
return (
|
|
||||||
settings && (
|
|
||||||
<EditorBox title={"Параметры формы"} onSave={handleSave}>
|
|
||||||
{itemEditor.display && (
|
|
||||||
<ItemEditor
|
|
||||||
item={itemEditor.index !== null ? { ...settings.items[itemEditor.index] } : null}
|
|
||||||
onCancel={handleItemCancel}
|
|
||||||
onOk={handleItemSave}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<EditorSubHeader title={"Общие"} />
|
|
||||||
<TextField type={"text"} variant={"standard"} value={settings.title} label={"Заголовок"} name={"title"} onChange={handleChange} />
|
|
||||||
<FormControl variant={"standard"}>
|
|
||||||
<InputLabel id={"orientation-label"}>Ориентация</InputLabel>
|
|
||||||
<Select
|
|
||||||
name={"orientation"}
|
|
||||||
value={settings.orientation}
|
|
||||||
labelId={"orientation-label"}
|
|
||||||
label={"Ориентация"}
|
|
||||||
onChange={handleChange}
|
|
||||||
>
|
|
||||||
<MenuItem value={ORIENTATION.V}>Вертикально</MenuItem>
|
|
||||||
<MenuItem value={ORIENTATION.H}>Горизонтально</MenuItem>
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
<FormControlLabel
|
|
||||||
control={<Switch name={"autoApply"} checked={settings.autoApply} onChange={handleChange} />}
|
|
||||||
label={"Автоподтверждение"}
|
|
||||||
/>
|
|
||||||
<EditorSubHeader title={"Элементы"} />
|
|
||||||
{Array.isArray(settings?.items) &&
|
|
||||||
settings.items.length > 0 &&
|
|
||||||
settings.items.map((item, i) => (
|
|
||||||
<Chip
|
|
||||||
key={i}
|
|
||||||
label={item.caption}
|
|
||||||
variant={"outlined"}
|
|
||||||
onClick={() => handleItemClick(i)}
|
|
||||||
onDelete={() => handleItemDelete(i)}
|
|
||||||
sx={STYLES.CHIP_ITEM}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
<Button startIcon={<Icon>add</Icon>} onClick={handleItemAdd}>
|
|
||||||
Добавить элемент
|
|
||||||
</Button>
|
|
||||||
</EditorBox>
|
|
||||||
)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств компонента - Форма (редактор настроек)
|
|
||||||
FormEditor.propTypes = {
|
|
||||||
id: PropTypes.string.isRequired,
|
|
||||||
title: PropTypes.string,
|
|
||||||
orientation: PropTypes.oneOf(Object.values(ORIENTATION)),
|
|
||||||
autoApply: PropTypes.bool,
|
|
||||||
items: PropTypes.arrayOf(ITEM_SHAPE),
|
|
||||||
onSettingsChange: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export default FormEditor;
|
|
@ -1,168 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - Редактор панелей
|
|
||||||
Компоненты: Форма (представление)
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React, { useEffect, useState, useContext } from "react"; //Классы React
|
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
|
||||||
import { Paper, Stack, Typography, Icon, TextField, IconButton, InputAdornment } from "@mui/material"; //Интерфейсные элементы
|
|
||||||
import { ApplicationСtx } from "../../../../context/application"; //Контекст приложения
|
|
||||||
import { COMPONENT_MESSAGES, ComponentInlineMessage } from "../views_common"; //Общие компоненты представлений
|
|
||||||
import { ITEM_SHAPE, ITEMS_INITIAL, ORIENTATION } from "./common"; //Общие ресурсы и константы формы
|
|
||||||
import "../../panels_editor.css"; //Стили редактора
|
|
||||||
|
|
||||||
//---------
|
|
||||||
//Константы
|
|
||||||
//---------
|
|
||||||
|
|
||||||
//Иконка компонента
|
|
||||||
const COMPONENT_ICON = "fact_check";
|
|
||||||
|
|
||||||
//Наименование компонента
|
|
||||||
const COMPONENT_NAME = "Форма";
|
|
||||||
|
|
||||||
//------------------------------------
|
|
||||||
//Вспомогательные функции и компоненты
|
|
||||||
//------------------------------------
|
|
||||||
|
|
||||||
//Элемент формы
|
|
||||||
const FormItem = ({ item = null, fullWidth = false, value = "", onChange = null } = {}) => {
|
|
||||||
//Подключение к контексту приложения
|
|
||||||
const { pOnlineShowDictionary } = useContext(ApplicationСtx);
|
|
||||||
|
|
||||||
//При изменении значения элемента
|
|
||||||
const handleChange = e => onChange && onChange(e.target.id, e.target.value);
|
|
||||||
|
|
||||||
//При очистке значения элемента
|
|
||||||
const handleClear = () => onChange(item.name, "");
|
|
||||||
|
|
||||||
//При выборе значения из словаря
|
|
||||||
const handleDictionary = () =>
|
|
||||||
item.unitCode &&
|
|
||||||
item.showMethod &&
|
|
||||||
pOnlineShowDictionary({
|
|
||||||
unitCode: item.unitCode,
|
|
||||||
showMethod: item.showMethod,
|
|
||||||
inputParameters: [{ name: item.inputParameter, value }],
|
|
||||||
callBack: res => res.success && onChange && onChange(item.name, res.outParameters[item.outputParameter])
|
|
||||||
});
|
|
||||||
//Формирование представления
|
|
||||||
return (
|
|
||||||
item && (
|
|
||||||
<TextField
|
|
||||||
fullWidth={fullWidth}
|
|
||||||
type={"text"}
|
|
||||||
variant={"standard"}
|
|
||||||
value={value}
|
|
||||||
label={item.caption}
|
|
||||||
id={item.name}
|
|
||||||
onChange={handleChange}
|
|
||||||
{...(item.unitCode && {
|
|
||||||
InputLabelProps: { shrink: true },
|
|
||||||
InputProps: {
|
|
||||||
readOnly: true,
|
|
||||||
endAdornment: (
|
|
||||||
<InputAdornment position="end">
|
|
||||||
<IconButton onClick={handleClear}>
|
|
||||||
<Icon>clear</Icon>
|
|
||||||
</IconButton>
|
|
||||||
<IconButton onClick={handleDictionary}>
|
|
||||||
<Icon>list</Icon>
|
|
||||||
</IconButton>
|
|
||||||
</InputAdornment>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств - элемент формы
|
|
||||||
FormItem.propTypes = {
|
|
||||||
item: ITEM_SHAPE,
|
|
||||||
fullWidth: PropTypes.bool,
|
|
||||||
value: PropTypes.any,
|
|
||||||
onChange: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Форма (представление)
|
|
||||||
const Form = ({ title = null, orientation = ORIENTATION.V, autoApply = false, items = ITEMS_INITIAL, values = {}, onValuesChange = null } = {}) => {
|
|
||||||
//Собственное состояние - значения элементов
|
|
||||||
const [selfValues, setSelfValues] = useState({});
|
|
||||||
|
|
||||||
//При изменении состава элементов или значений
|
|
||||||
useEffect(() => setSelfValues(items.reduce((sV, item) => ({ ...sV, [item.name]: values[item.name] }), {})), [items, values]);
|
|
||||||
|
|
||||||
//При изменении значения элемента формы
|
|
||||||
const handleItemChange = (name, value) => {
|
|
||||||
setSelfValues(pv => ({ ...pv, [name]: value }));
|
|
||||||
autoApply && onValuesChange && onValuesChange({ ...selfValues, [name]: value });
|
|
||||||
};
|
|
||||||
|
|
||||||
//При подтверждении изменений формы
|
|
||||||
const handleOkClick = () => onValuesChange && onValuesChange({ ...selfValues });
|
|
||||||
|
|
||||||
//Флаг настроенности формы
|
|
||||||
const haveConfing = items && Array.isArray(items) && items.length > 0;
|
|
||||||
|
|
||||||
//Формирование представления
|
|
||||||
return (
|
|
||||||
<Paper className={`component-view__container ${!haveConfing && "component-view__container__empty"}`} elevation={6}>
|
|
||||||
{haveConfing ? (
|
|
||||||
<Stack direction={"column"}>
|
|
||||||
<Stack direction={"row"} justifyContent={"space-between"} alignItems={"center"}>
|
|
||||||
{title && (
|
|
||||||
<Typography align={"left"} color={"text.primary"} variant={"subtitle2"} noWrap={true}>
|
|
||||||
{title}
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
{!autoApply && (
|
|
||||||
<IconButton onClick={handleOkClick}>
|
|
||||||
<Icon>done</Icon>
|
|
||||||
</IconButton>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
<Stack direction={orientation == ORIENTATION.V ? "column" : "row"} spacing={1} pt={1} pb={1}>
|
|
||||||
{items.map((item, i) => (
|
|
||||||
<FormItem
|
|
||||||
key={i}
|
|
||||||
item={item}
|
|
||||||
value={selfValues?.[item.name] || ""}
|
|
||||||
onChange={handleItemChange}
|
|
||||||
fullWidth={orientation == ORIENTATION.V}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
) : (
|
|
||||||
<ComponentInlineMessage icon={COMPONENT_ICON} name={COMPONENT_NAME} message={COMPONENT_MESSAGES.NO_SETTINGS} />
|
|
||||||
)}
|
|
||||||
</Paper>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств компонента - Форма (представление)
|
|
||||||
Form.propTypes = {
|
|
||||||
id: PropTypes.string.isRequired,
|
|
||||||
title: PropTypes.string,
|
|
||||||
orientation: PropTypes.oneOf(Object.values(ORIENTATION)),
|
|
||||||
autoApply: PropTypes.bool,
|
|
||||||
items: PropTypes.arrayOf(ITEM_SHAPE),
|
|
||||||
values: PropTypes.object,
|
|
||||||
onValuesChange: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export default Form;
|
|
@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - Редактор панелей
|
|
||||||
Компоненты: Индикатор (редактор настроек)
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React, { useEffect, useState } from "react"; //Классы React
|
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
|
||||||
import { DATA_SOURCE_SHAPE, DataSource, EditorBox, EditorSubHeader } from "../editors_common"; //Общие компоненты редакторов
|
|
||||||
import "../../panels_editor.css"; //Стили редактора
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Индикатор (редактор настроек)
|
|
||||||
const IndicatorEditor = ({ id, dataSource = null, valueProviders = {}, onSettingsChange = null } = {}) => {
|
|
||||||
//Собственное состояние - текущие настройки
|
|
||||||
const [settings, setSettings] = useState(null);
|
|
||||||
|
|
||||||
//При изменении компонента
|
|
||||||
useEffect(() => {
|
|
||||||
settings?.id != id && setSettings({ id, dataSource });
|
|
||||||
}, [settings, id, dataSource]);
|
|
||||||
|
|
||||||
//При сохранении изменений элемента
|
|
||||||
const handleDataSourceChange = dataSource => setSettings(pv => ({ ...pv, dataSource: { ...dataSource } }));
|
|
||||||
|
|
||||||
//При сохранении настроек
|
|
||||||
const handleSave = (closeEditor = false) => onSettingsChange && onSettingsChange({ id, settings, closeEditor });
|
|
||||||
|
|
||||||
//Формирование представления
|
|
||||||
return (
|
|
||||||
<EditorBox title={"Параметры индикатора"} onSave={handleSave}>
|
|
||||||
<EditorSubHeader title={"Источник данных"} />
|
|
||||||
<DataSource dataSource={settings?.dataSource} valueProviders={valueProviders} onChange={handleDataSourceChange} />
|
|
||||||
</EditorBox>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств компонента - Индикатор (редактор настроек)
|
|
||||||
IndicatorEditor.propTypes = {
|
|
||||||
id: PropTypes.string.isRequired,
|
|
||||||
dataSource: DATA_SOURCE_SHAPE,
|
|
||||||
valueProviders: PropTypes.object,
|
|
||||||
onSettingsChange: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export default IndicatorEditor;
|
|
@ -1,84 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - Редактор панелей
|
|
||||||
Компоненты: Индикатор (представление)
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React from "react"; //Классы React
|
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
|
||||||
import { Paper } from "@mui/material"; //Интерфейсные элементы
|
|
||||||
import { P8PIndicator } from "../../../../components/p8p_indicator"; //Компонент индикатора
|
|
||||||
import { useComponentDataSource } from "../components_hooks"; //Хуки для данных
|
|
||||||
import { DATA_SOURCE_SHAPE } from "../editors_common"; //Общие объекты компонентов
|
|
||||||
import { COMPONENT_MESSAGE_TYPE, COMPONENT_MESSAGES, ComponentInlineMessage } from "../views_common"; //Общие компоненты представлений
|
|
||||||
import "../../panels_editor.css"; //Стили редактора
|
|
||||||
|
|
||||||
//---------
|
|
||||||
//Константы
|
|
||||||
//---------
|
|
||||||
|
|
||||||
//Иконка компонента
|
|
||||||
const COMPONENT_ICON = "speed";
|
|
||||||
|
|
||||||
//Наименование компонента
|
|
||||||
const COMPONENT_NAME = "Индикатор";
|
|
||||||
|
|
||||||
//Стили
|
|
||||||
const STYLES = {
|
|
||||||
CONTAINER: { height: "100%" }
|
|
||||||
};
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Индикатор (представление)
|
|
||||||
const Indicator = ({ dataSource = null, values = {} } = {}) => {
|
|
||||||
//Собственное состояние - данные
|
|
||||||
const [data, error] = useComponentDataSource({ dataSource, values });
|
|
||||||
|
|
||||||
//Флаг настроенности индикатора
|
|
||||||
const haveConfing = dataSource?.stored ? true : false;
|
|
||||||
|
|
||||||
//Флаг наличия данных
|
|
||||||
const haveData = data?.init === true && !error ? true : false;
|
|
||||||
|
|
||||||
//Данные индикатора
|
|
||||||
const indicator = data?.XINDICATOR || {};
|
|
||||||
|
|
||||||
//Формирование представления
|
|
||||||
return (
|
|
||||||
<Paper
|
|
||||||
{...(haveConfing && haveData
|
|
||||||
? { sx: { ...STYLES.CONTAINER } }
|
|
||||||
: { className: "component-view__container component-view__container__empty" })}
|
|
||||||
elevation={6}
|
|
||||||
>
|
|
||||||
{haveConfing && haveData ? (
|
|
||||||
<P8PIndicator {...indicator} elevation={0} />
|
|
||||||
) : (
|
|
||||||
<ComponentInlineMessage
|
|
||||||
icon={COMPONENT_ICON}
|
|
||||||
name={COMPONENT_NAME}
|
|
||||||
message={!haveConfing ? COMPONENT_MESSAGES.NO_SETTINGS : error ? error : COMPONENT_MESSAGES.NO_DATA_FOUND}
|
|
||||||
type={error ? COMPONENT_MESSAGE_TYPE.ERROR : COMPONENT_MESSAGE_TYPE.COMMON}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Paper>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств компонента - Индикатор (представление)
|
|
||||||
Indicator.propTypes = {
|
|
||||||
dataSource: DATA_SOURCE_SHAPE,
|
|
||||||
values: PropTypes.object
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export default Indicator;
|
|
@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - Редактор панелей
|
|
||||||
Компоненты: Таблица (редактор настроек)
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React, { useEffect, useState } from "react"; //Классы React
|
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
|
||||||
import { DATA_SOURCE_SHAPE, DataSource, EditorBox, EditorSubHeader } from "../editors_common"; //Общие компоненты редакторов
|
|
||||||
import "../../panels_editor.css"; //Стили редактора
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Таблица (редактор настроек)
|
|
||||||
const TableEditor = ({ id, dataSource = null, valueProviders = {}, onSettingsChange = null } = {}) => {
|
|
||||||
//Собственное состояние - текущие настройки
|
|
||||||
const [settings, setSettings] = useState(null);
|
|
||||||
|
|
||||||
//При изменении компонента
|
|
||||||
useEffect(() => {
|
|
||||||
settings?.id != id && setSettings({ id, dataSource });
|
|
||||||
}, [settings, id, dataSource]);
|
|
||||||
|
|
||||||
//При сохранении изменений элемента
|
|
||||||
const handleDataSourceChange = dataSource => setSettings(pv => ({ ...pv, dataSource: { ...dataSource } }));
|
|
||||||
|
|
||||||
//При сохранении настроек
|
|
||||||
const handleSave = (closeEditor = false) => onSettingsChange && onSettingsChange({ id, settings, closeEditor });
|
|
||||||
|
|
||||||
//Формирование представления
|
|
||||||
return (
|
|
||||||
<EditorBox title={"Параметры таблицы"} onSave={handleSave}>
|
|
||||||
<EditorSubHeader title={"Источник данных"} />
|
|
||||||
<DataSource dataSource={settings?.dataSource} valueProviders={valueProviders} onChange={handleDataSourceChange} />
|
|
||||||
</EditorBox>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств компонента - Таблица (редактор настроек)
|
|
||||||
TableEditor.propTypes = {
|
|
||||||
id: PropTypes.string.isRequired,
|
|
||||||
dataSource: DATA_SOURCE_SHAPE,
|
|
||||||
valueProviders: PropTypes.object,
|
|
||||||
onSettingsChange: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export default TableEditor;
|
|
@ -1,96 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - Редактор панелей
|
|
||||||
Компоненты: Таблица (представление)
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React from "react"; //Классы React
|
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
|
||||||
import { Paper } from "@mui/material"; //Интерфейсные элементы
|
|
||||||
import { APP_STYLES } from "../../../../../app.styles"; //Типовые стили
|
|
||||||
import { P8PDataGrid } from "../../../../components/p8p_data_grid"; //Таблица данных
|
|
||||||
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
|
||||||
import { useComponentDataSource } from "../components_hooks"; //Хуки для данных
|
|
||||||
import { DATA_SOURCE_SHAPE } from "../editors_common"; //Общие объекты компонентов
|
|
||||||
import { COMPONENT_MESSAGE_TYPE, COMPONENT_MESSAGES, ComponentInlineMessage } from "../views_common"; //Общие компоненты представлений
|
|
||||||
import "../../panels_editor.css"; //Стили редактора
|
|
||||||
|
|
||||||
//---------
|
|
||||||
//Константы
|
|
||||||
//---------
|
|
||||||
|
|
||||||
//Иконка компонента
|
|
||||||
const COMPONENT_ICON = "table_view";
|
|
||||||
|
|
||||||
//Наименование компонента
|
|
||||||
const COMPONENT_NAME = "Таблица";
|
|
||||||
|
|
||||||
//Стили
|
|
||||||
const STYLES = {
|
|
||||||
CONTAINER: { display: "flex", height: "100%", overflow: "hidden" },
|
|
||||||
DATA_GRID: { width: "100%" },
|
|
||||||
DATA_GRID_CONTAINER: {
|
|
||||||
height: `calc(100%)`,
|
|
||||||
...APP_STYLES.SCROLL
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Таблица (представление)
|
|
||||||
const Table = ({ dataSource = null, values = {} } = {}) => {
|
|
||||||
//Собственное состояние - данные
|
|
||||||
const [data, error] = useComponentDataSource({ dataSource, values });
|
|
||||||
|
|
||||||
//Флаг настроенности таблицы
|
|
||||||
const haveConfing = dataSource?.stored ? true : false;
|
|
||||||
|
|
||||||
//Флаг наличия данных
|
|
||||||
const haveData = data?.init === true && !error ? true : false;
|
|
||||||
|
|
||||||
//Данные таблицы
|
|
||||||
const dataGrid = data?.XDATA_GRID || {};
|
|
||||||
|
|
||||||
//Формирование представления
|
|
||||||
return (
|
|
||||||
<Paper
|
|
||||||
{...(haveConfing && haveData
|
|
||||||
? { sx: { ...STYLES.CONTAINER } }
|
|
||||||
: { className: "component-view__container component-view__container__empty" })}
|
|
||||||
elevation={6}
|
|
||||||
>
|
|
||||||
{haveConfing && haveData ? (
|
|
||||||
<P8PDataGrid
|
|
||||||
{...P8P_DATA_GRID_CONFIG_PROPS}
|
|
||||||
{...dataGrid}
|
|
||||||
style={STYLES.DATA_GRID}
|
|
||||||
containerComponentProps={{ sx: STYLES.DATA_GRID_CONTAINER, elevation: 0 }}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<ComponentInlineMessage
|
|
||||||
icon={COMPONENT_ICON}
|
|
||||||
name={COMPONENT_NAME}
|
|
||||||
message={!haveConfing ? COMPONENT_MESSAGES.NO_SETTINGS : error ? error : COMPONENT_MESSAGES.NO_DATA_FOUND}
|
|
||||||
type={error ? COMPONENT_MESSAGE_TYPE.ERROR : COMPONENT_MESSAGE_TYPE.COMMON}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Paper>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств компонента - Таблица (представление)
|
|
||||||
Table.propTypes = {
|
|
||||||
dataSource: DATA_SOURCE_SHAPE,
|
|
||||||
values: PropTypes.object
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export default Table;
|
|
@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - Редактор панелей
|
|
||||||
Общие компоненты представлений элементов панели
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React from "react"; //Классы React
|
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
|
||||||
import { Stack, Icon, Typography } from "@mui/material"; //Интерфейсные элементы
|
|
||||||
import { TEXTS } from "../../../../app.text"; //Общие текстовые ресурсы
|
|
||||||
|
|
||||||
//---------
|
|
||||||
//Константы
|
|
||||||
//---------
|
|
||||||
|
|
||||||
//Типы сообщений
|
|
||||||
const COMPONENT_MESSAGE_TYPE = {
|
|
||||||
COMMON: "COMMON",
|
|
||||||
ERROR: "ERROR"
|
|
||||||
};
|
|
||||||
|
|
||||||
//Типовые сообщения
|
|
||||||
const COMPONENT_MESSAGES = {
|
|
||||||
NO_DATA_FOUND: TEXTS.NO_DATA_FOUND,
|
|
||||||
NO_SETTINGS: "Настройте компонент"
|
|
||||||
};
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Информационное сообщение внутри компонента
|
|
||||||
const ComponentInlineMessage = ({ icon, name, message, type = COMPONENT_MESSAGE_TYPE.COMMON }) => {
|
|
||||||
//Формирование представления
|
|
||||||
return (
|
|
||||||
<Stack direction={"column"}>
|
|
||||||
<Stack direction={"row"} justifyContent={"center"} alignItems={"center"}>
|
|
||||||
{icon && <Icon color={"disabled"}>{icon}</Icon>}
|
|
||||||
{name && (
|
|
||||||
<Typography align={"center"} color={"text.secondary"} variant={"button"}>
|
|
||||||
{name}
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
<Typography align={"center"} color={type != COMPONENT_MESSAGE_TYPE.ERROR ? "text.secondary" : "error.dark"} variant={"caption"}>
|
|
||||||
{message}
|
|
||||||
</Typography>
|
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств - Информационное сообщение внутри компонента
|
|
||||||
ComponentInlineMessage.propTypes = {
|
|
||||||
icon: PropTypes.string,
|
|
||||||
name: PropTypes.string,
|
|
||||||
message: PropTypes.string.isRequired,
|
|
||||||
type: PropTypes.oneOf(Object.values(COMPONENT_MESSAGE_TYPE))
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export { COMPONENT_MESSAGE_TYPE, COMPONENT_MESSAGES, ComponentInlineMessage };
|
|
@ -1,16 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - Редактор панелей
|
|
||||||
Редактор панелей: точка входа
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import { PanelsEditor } from "./panels_editor"; //Корневая панель редактора
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export const RootClass = PanelsEditor;
|
|
@ -1,87 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - Редактор панелей
|
|
||||||
Элемент макета
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React from "react"; //Классы React
|
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
|
||||||
import { IconButton, Icon, Stack } from "@mui/material"; //Интерфейсные элементы
|
|
||||||
import "./panels_editor.css"; //Кастомные стили
|
|
||||||
|
|
||||||
//---------
|
|
||||||
//Константы
|
|
||||||
//---------
|
|
||||||
|
|
||||||
//Стили
|
|
||||||
const STYLES = {
|
|
||||||
CONTAINER: selected => ({ zIndex: 1100, ...(selected ? { border: "2px dotted green" } : {}) }),
|
|
||||||
STACK_TOOLS: { position: "absolute", zIndex: 1200, height: "100%", backgroundColor: "#c0c0c07f" }
|
|
||||||
};
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Элемент макета
|
|
||||||
// eslint-disable-next-line react/display-name
|
|
||||||
const LayoutItem = React.forwardRef(
|
|
||||||
(
|
|
||||||
{ style, className, onMouseDown, onMouseUp, onTouchEnd, children, onSettingsClick, onDeleteClick, item, editMode = false, selected = false },
|
|
||||||
ref
|
|
||||||
) => {
|
|
||||||
//При нажатии на настройки
|
|
||||||
const handleSettingsClick = () => onSettingsClick && onSettingsClick(item.i);
|
|
||||||
|
|
||||||
//При нажатии на удаление
|
|
||||||
const handleDeleteClick = () => onDeleteClick && onDeleteClick(item.i);
|
|
||||||
|
|
||||||
//Формирование представления
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
style={{ ...style, ...STYLES.CONTAINER(selected) }}
|
|
||||||
className={`${className} layout-item__container`}
|
|
||||||
ref={ref}
|
|
||||||
onMouseDown={onMouseDown}
|
|
||||||
onMouseUp={onMouseUp}
|
|
||||||
onTouchEnd={onTouchEnd}
|
|
||||||
>
|
|
||||||
{editMode && (
|
|
||||||
<Stack direction={"column"} sx={STYLES.STACK_TOOLS}>
|
|
||||||
<IconButton onClick={handleSettingsClick}>
|
|
||||||
<Icon>settings</Icon>
|
|
||||||
</IconButton>
|
|
||||||
<IconButton onClick={handleDeleteClick}>
|
|
||||||
<Icon>delete</Icon>
|
|
||||||
</IconButton>
|
|
||||||
</Stack>
|
|
||||||
)}
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
//Контроль свойств компонента - элемент макета
|
|
||||||
LayoutItem.propTypes = {
|
|
||||||
style: PropTypes.object,
|
|
||||||
className: PropTypes.string,
|
|
||||||
onMouseDown: PropTypes.func,
|
|
||||||
onMouseUp: PropTypes.func,
|
|
||||||
onTouchEnd: PropTypes.func,
|
|
||||||
children: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf(PropTypes.node)]),
|
|
||||||
onSettingsClick: PropTypes.func,
|
|
||||||
onDeleteClick: PropTypes.func,
|
|
||||||
item: PropTypes.object.isRequired,
|
|
||||||
editMode: PropTypes.bool,
|
|
||||||
selected: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export { LayoutItem };
|
|
@ -1,40 +0,0 @@
|
|||||||
:root {
|
|
||||||
--border-color: #dee2e6;
|
|
||||||
--layout-bg: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.layout {
|
|
||||||
background-color: var(--layout-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.layout-item__container {
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.component-editor__wrap {
|
|
||||||
}
|
|
||||||
|
|
||||||
.component-editor__container {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.component-editor__divider {
|
|
||||||
padding-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.component-view__wrap {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.component-view__container {
|
|
||||||
height: 100%;
|
|
||||||
overflow: auto;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.component-view__container__empty {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
@ -1,249 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - Редактор панелей
|
|
||||||
Корневой компонент
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React, { useEffect, useState, useContext } from "react"; //Классы React
|
|
||||||
import { Responsive, WidthProvider } from "react-grid-layout"; //Адаптивный макет
|
|
||||||
import { Box, Grid, Stack, Menu, MenuItem, IconButton, Icon, Fab } from "@mui/material"; //Интерфейсные элементы
|
|
||||||
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
|
||||||
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Рабочая область приложения
|
|
||||||
import { genGUID } from "../../core/utils"; //Общие вспомогательные функции
|
|
||||||
import { LayoutItem } from "./layout_item"; //Элемент макета
|
|
||||||
import { ComponentView } from "./component_view"; //Представление компонента панели
|
|
||||||
import { ComponentEditor } from "./component_editor"; //Редактор свойств компонента панели
|
|
||||||
import { COMPONETNS } from "./components/components"; //Описание доступных компонентов
|
|
||||||
import "react-grid-layout/css/styles.css"; //Стили для адаптивного макета
|
|
||||||
import "react-resizable/css/styles.css"; //Стили для адаптивного макета
|
|
||||||
|
|
||||||
//---------
|
|
||||||
//Константы
|
|
||||||
//---------
|
|
||||||
|
|
||||||
//Стили
|
|
||||||
const STYLES = {
|
|
||||||
CONTAINER: { display: "flex" },
|
|
||||||
GRID_CONTAINER: { height: `calc(100vh - ${APP_BAR_HEIGHT})` },
|
|
||||||
GRID_ITEM_INSPECTOR: { backgroundColor: "#e9ecef" },
|
|
||||||
FAB_EDIT: { position: "absolute", top: 12, right: 12, zIndex: 2000 }
|
|
||||||
};
|
|
||||||
|
|
||||||
//Заголовоки по умолчанию
|
|
||||||
const PANEL_CAPTION_EDIT_MODE = "Редактор панелей";
|
|
||||||
const PANEL_CAPTION_EXECUTE_MODE = "Исполнение панели";
|
|
||||||
|
|
||||||
//Начальное состояние размера макета
|
|
||||||
const INITIAL_BREAKPOINT = "lg";
|
|
||||||
|
|
||||||
//Начальное состояние макета
|
|
||||||
const INITIAL_LAYOUTS = {
|
|
||||||
[INITIAL_BREAKPOINT]: []
|
|
||||||
};
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Обёрдка для динамического макета
|
|
||||||
const ResponsiveGridLayout = WidthProvider(Responsive);
|
|
||||||
|
|
||||||
//Корневой компонент редактора панелей
|
|
||||||
const PanelsEditor = () => {
|
|
||||||
//Собственное состояние
|
|
||||||
const [components, setComponents] = useState({});
|
|
||||||
const [valueProviders, setValueProviders] = useState({});
|
|
||||||
const [layouts, setLayouts] = useState(INITIAL_LAYOUTS);
|
|
||||||
const [breakpoint, setBreakpoint] = useState(INITIAL_BREAKPOINT);
|
|
||||||
const [editMode, setEditMode] = useState(true);
|
|
||||||
const [editComponent, setEditComponent] = useState(null);
|
|
||||||
const [addMenuAnchorEl, setAddMenuAnchorEl] = useState(null);
|
|
||||||
|
|
||||||
//Подключение к контексту приложения
|
|
||||||
const { setAppBarTitle } = useContext(ApplicationСtx);
|
|
||||||
|
|
||||||
//Добвление компонента в макет
|
|
||||||
const addComponent = component => {
|
|
||||||
const id = genGUID();
|
|
||||||
setLayouts(pv => ({ ...pv, [breakpoint]: [...pv[breakpoint], { i: id, x: 0, y: 0, w: 4, h: 10 }] }));
|
|
||||||
setComponents(pv => ({ ...pv, [id]: { ...component } }));
|
|
||||||
};
|
|
||||||
|
|
||||||
//Удаление компонента из макета
|
|
||||||
const deleteComponent = id => {
|
|
||||||
setLayouts(pv => ({ ...pv, [breakpoint]: layouts[breakpoint].filter(item => item.i !== id) }));
|
|
||||||
setComponents(pv => ({ ...pv, [id]: { ...pv[id], deleted: true } }));
|
|
||||||
if (valueProviders[id]) {
|
|
||||||
const vPTmp = { ...valueProviders };
|
|
||||||
delete vPTmp[id];
|
|
||||||
setValueProviders(vPTmp);
|
|
||||||
}
|
|
||||||
editComponent === id && closeComponentSettingsEditor();
|
|
||||||
};
|
|
||||||
|
|
||||||
//Включение/выключение режима редиктирования
|
|
||||||
const toggleEditMode = () => {
|
|
||||||
if (!editMode) setAppBarTitle(PANEL_CAPTION_EDIT_MODE);
|
|
||||||
else setAppBarTitle(PANEL_CAPTION_EXECUTE_MODE);
|
|
||||||
setEditMode(!editMode);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Открытие редактора настроек компонента
|
|
||||||
const openComponentSettingsEditor = id => setEditComponent(id);
|
|
||||||
|
|
||||||
//Закрытие реактора настроек компонента
|
|
||||||
const closeComponentSettingsEditor = () => setEditComponent(null);
|
|
||||||
|
|
||||||
//Открытие/сокрытие меню добавления
|
|
||||||
const toggleAddMenu = target => setAddMenuAnchorEl(target instanceof Element ? target : null);
|
|
||||||
|
|
||||||
//При изменении размера холста
|
|
||||||
const handleBreakpointChange = breakpoint => setBreakpoint(breakpoint);
|
|
||||||
|
|
||||||
//При изменении состояния макета
|
|
||||||
const handleLayoutChange = (currentLayout, layouts) => setLayouts(layouts);
|
|
||||||
|
|
||||||
//При нажатии на кнопку добалвения
|
|
||||||
const handleAddClick = e => toggleAddMenu(e.currentTarget);
|
|
||||||
|
|
||||||
//При выборе элемента меню добавления
|
|
||||||
const handleAddMenuItemClick = component => {
|
|
||||||
toggleAddMenu();
|
|
||||||
addComponent(component);
|
|
||||||
};
|
|
||||||
|
|
||||||
//При изменении значений в компоненте
|
|
||||||
const handleComponentValuesChange = (id, values) => setValueProviders(pv => ({ ...pv, [id]: { ...values } }));
|
|
||||||
|
|
||||||
//При нажатии на настройки компонента
|
|
||||||
const handleComponentSettingsClick = id => (editComponent === id ? closeComponentSettingsEditor() : openComponentSettingsEditor(id));
|
|
||||||
|
|
||||||
//При изменении настроек компонента
|
|
||||||
const handleComponentSettingsChange = ({ id = null, settings = {}, providedValues = [], closeEditor = false } = {}) => {
|
|
||||||
if (id && components[id]) {
|
|
||||||
const providedValuesInit = providedValues.reduce((res, providedValue) => ({ ...res, [providedValue]: undefined }), {});
|
|
||||||
if (valueProviders[id]) {
|
|
||||||
const vPTmp = { ...valueProviders[id] };
|
|
||||||
Object.keys(valueProviders[id]).forEach(key => !providedValues.includes(key) && delete vPTmp[key]);
|
|
||||||
setValueProviders(pv => ({ ...pv, [id]: { ...providedValuesInit, ...vPTmp } }));
|
|
||||||
} else setValueProviders(pv => ({ ...pv, [id]: providedValuesInit }));
|
|
||||||
setComponents(pv => ({ ...pv, [editComponent]: { ...pv[editComponent], settings: { ...settings } } }));
|
|
||||||
if (closeEditor === true) closeComponentSettingsEditor();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//При удалении компоненета
|
|
||||||
const handleComponentDeleteClick = id => deleteComponent(id);
|
|
||||||
|
|
||||||
//При подключении к странице
|
|
||||||
useEffect(() => {
|
|
||||||
addComponent(COMPONETNS[0]);
|
|
||||||
addComponent(COMPONETNS[3]);
|
|
||||||
addComponent(COMPONETNS[4]);
|
|
||||||
//addComponent(COMPONETNS[1]);
|
|
||||||
//addComponent(COMPONETNS[2]);
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
//Текущие значения панели
|
|
||||||
const values = Object.keys(valueProviders).reduce((res, key) => ({ ...res, ...valueProviders[key] }), {});
|
|
||||||
|
|
||||||
//Меню добавления
|
|
||||||
const addMenu = (
|
|
||||||
<Menu anchorEl={addMenuAnchorEl} open={Boolean(addMenuAnchorEl)} onClose={toggleAddMenu}>
|
|
||||||
{COMPONETNS.map((comp, i) => (
|
|
||||||
<MenuItem key={i} onClick={() => handleAddMenuItemClick(comp)}>
|
|
||||||
{comp.name}
|
|
||||||
</MenuItem>
|
|
||||||
))}
|
|
||||||
</Menu>
|
|
||||||
);
|
|
||||||
|
|
||||||
//Кнопка редактирования
|
|
||||||
const editButton = !editMode && (
|
|
||||||
<Fab sx={STYLES.FAB_EDIT} size={"small"} color={"grey.700"} title={"Редактировать"} onClick={toggleEditMode}>
|
|
||||||
<Icon>edit</Icon>
|
|
||||||
</Fab>
|
|
||||||
);
|
|
||||||
|
|
||||||
//Панель инструмментов
|
|
||||||
const toolBar = (
|
|
||||||
<Stack direction={"row"} p={1}>
|
|
||||||
<IconButton onClick={toggleEditMode} title={"Запустить"}>
|
|
||||||
<Icon>play_arrow</Icon>
|
|
||||||
</IconButton>
|
|
||||||
<IconButton onClick={handleAddClick} title={"Добавить элемент"}>
|
|
||||||
<Icon>add</Icon>
|
|
||||||
</IconButton>
|
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
|
|
||||||
//Генерация содержимого
|
|
||||||
return (
|
|
||||||
<Box sx={STYLES.CONTAINER}>
|
|
||||||
{editButton}
|
|
||||||
{addMenu}
|
|
||||||
<Grid container sx={STYLES.GRID_CONTAINER} columns={25}>
|
|
||||||
<Grid item xs={editMode ? 20 : 25}>
|
|
||||||
<ResponsiveGridLayout
|
|
||||||
rowHeight={5}
|
|
||||||
className={"layout"}
|
|
||||||
layouts={layouts}
|
|
||||||
breakpoints={{ lg: 1200 }}
|
|
||||||
cols={{ lg: 12 }}
|
|
||||||
onBreakpointChange={handleBreakpointChange}
|
|
||||||
onLayoutChange={handleLayoutChange}
|
|
||||||
useCSSTransforms={true}
|
|
||||||
compactType={"vertical"}
|
|
||||||
isDraggable={editMode}
|
|
||||||
isResizable={editMode}
|
|
||||||
>
|
|
||||||
{layouts[breakpoint].map(item => (
|
|
||||||
<LayoutItem
|
|
||||||
key={item.i}
|
|
||||||
onSettingsClick={handleComponentSettingsClick}
|
|
||||||
onDeleteClick={handleComponentDeleteClick}
|
|
||||||
item={item}
|
|
||||||
editMode={editMode}
|
|
||||||
selected={editMode && editComponent === item.i}
|
|
||||||
>
|
|
||||||
<ComponentView
|
|
||||||
id={item.i}
|
|
||||||
path={components[item.i]?.path}
|
|
||||||
settings={components[item.i]?.settings}
|
|
||||||
values={values}
|
|
||||||
onValuesChange={handleComponentValuesChange}
|
|
||||||
/>
|
|
||||||
</LayoutItem>
|
|
||||||
))}
|
|
||||||
</ResponsiveGridLayout>
|
|
||||||
</Grid>
|
|
||||||
{editMode && (
|
|
||||||
<Grid item xs={5} sx={STYLES.GRID_ITEM_INSPECTOR}>
|
|
||||||
{toolBar}
|
|
||||||
{editComponent && (
|
|
||||||
<>
|
|
||||||
<ComponentEditor
|
|
||||||
id={editComponent}
|
|
||||||
path={components[editComponent].path}
|
|
||||||
settings={components[editComponent].settings}
|
|
||||||
valueProviders={valueProviders}
|
|
||||||
onSettingsChange={handleComponentSettingsChange}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Grid>
|
|
||||||
)}
|
|
||||||
</Grid>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export { PanelsEditor };
|
|
@ -1,170 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - ПУП - Информация о проектах
|
|
||||||
Фильтр
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React, { useState } from "react"; //Классы React
|
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
|
||||||
import { Grid, Chip, Stack, Input, InputAdornment, IconButton, Icon } from "@mui/material"; //Интерфейсные элементы
|
|
||||||
import { FILTER_INITIAL, FILTER_ITEMS, PRICE_STRUCT_STATUS, PROJECT_STATE, FilterDialog } from "./filter_dialog"; //Компонент "Диалог фильтра"
|
|
||||||
|
|
||||||
//---------
|
|
||||||
//Константы
|
|
||||||
//---------
|
|
||||||
|
|
||||||
//Стили
|
|
||||||
const STYLES = {
|
|
||||||
CONTAINER: { paddingTop: "10px" },
|
|
||||||
FILTER: { maxWidth: "99vw" },
|
|
||||||
SEARCH_GRID_ITEM: { paddingRight: "15px" }
|
|
||||||
};
|
|
||||||
|
|
||||||
//------------------------------------
|
|
||||||
//Вспомогательные функции и компоненты
|
|
||||||
//------------------------------------
|
|
||||||
|
|
||||||
//Элемент фильтра
|
|
||||||
const FilterItem = ({ caption, value, defaultValue, onClick, onDelete }) => {
|
|
||||||
//При нажатии на элемент
|
|
||||||
const handleClick = () => (onClick ? onClick() : null);
|
|
||||||
|
|
||||||
//При нажатии на удаление элемента
|
|
||||||
const handleDelete = () => (onDelete ? onDelete() : null);
|
|
||||||
|
|
||||||
//Генерация содержимого
|
|
||||||
return (
|
|
||||||
<Chip
|
|
||||||
label={
|
|
||||||
<Stack direction={"row"} alignItems={"center"}>
|
|
||||||
<strong>{caption}</strong>: {value || defaultValue}
|
|
||||||
</Stack>
|
|
||||||
}
|
|
||||||
variant="outlined"
|
|
||||||
onClick={handleClick}
|
|
||||||
onDelete={onDelete ? handleDelete : null}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств компонента - Элемент фильтра
|
|
||||||
FilterItem.propTypes = {
|
|
||||||
caption: PropTypes.string.isRequired,
|
|
||||||
value: PropTypes.any,
|
|
||||||
defaultValue: PropTypes.string.isRequired,
|
|
||||||
onClick: PropTypes.func,
|
|
||||||
onDelete: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Фильтр
|
|
||||||
const Filter = ({ values, onChange }) => {
|
|
||||||
//Собственное состояние - отображение диалога ввода значений фильтра
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
|
|
||||||
//Собственное состояние - строка поиска
|
|
||||||
const [search, setSearch] = useState(values.search);
|
|
||||||
|
|
||||||
//Передача сообщения об измении фильтра родителю
|
|
||||||
const notifyChange = values => (onChange ? onChange(values) : null);
|
|
||||||
|
|
||||||
//При закрытии диалога с сохранением значений
|
|
||||||
const handleFilterDialogOk = values => {
|
|
||||||
setIsOpen(false);
|
|
||||||
notifyChange(values);
|
|
||||||
};
|
|
||||||
|
|
||||||
//При закрытии диалога без сохранения значений
|
|
||||||
const handleFilterDialogCancel = () => setIsOpen(false);
|
|
||||||
|
|
||||||
//При нажатии на фильтр
|
|
||||||
const handleClick = () => setIsOpen(true);
|
|
||||||
|
|
||||||
//При выполнении поиска
|
|
||||||
const handleDoSearch = (clear = false) => {
|
|
||||||
if (clear === true) setSearch("");
|
|
||||||
notifyChange({ ...values, search: clear === true ? "" : search });
|
|
||||||
};
|
|
||||||
|
|
||||||
//При изменении значения в строке поиска
|
|
||||||
const handleSearchChange = e => setSearch(e.target.value);
|
|
||||||
|
|
||||||
//При нажатии клавиши в строке поиска
|
|
||||||
const handleSearchKeyPress = e => ([13, 27].includes(e.keyCode) ? handleDoSearch(e.keyCode == 27) : null);
|
|
||||||
|
|
||||||
//Формирование функции обработки очистки элемента фильтар
|
|
||||||
const buildFilterItemClearHandler = сode =>
|
|
||||||
values[сode] != FILTER_INITIAL[сode] ? () => notifyChange({ ...values, [сode]: FILTER_INITIAL[сode] }) : null;
|
|
||||||
|
|
||||||
//Генерация содержимого
|
|
||||||
return (
|
|
||||||
<Grid container sx={STYLES.CONTAINER}>
|
|
||||||
<Grid xs={10} item>
|
|
||||||
{isOpen ? <FilterDialog valuesInitial={values} onOk={handleFilterDialogOk} onCancel={handleFilterDialogCancel} /> : null}
|
|
||||||
<Stack direction="row" spacing={1} p={1} alignItems={"center"} sx={STYLES.FILTER} onClick={handleClick}>
|
|
||||||
<FilterItem
|
|
||||||
caption={"Тип заказа"}
|
|
||||||
value={values.prjType}
|
|
||||||
defaultValue={"Любой"}
|
|
||||||
onDelete={buildFilterItemClearHandler("prjType")}
|
|
||||||
/>
|
|
||||||
<FilterItem
|
|
||||||
caption={"Подразделение-ответственный"}
|
|
||||||
defaultValue={"Любое"}
|
|
||||||
value={values.insDep}
|
|
||||||
onDelete={buildFilterItemClearHandler("insDep")}
|
|
||||||
/>
|
|
||||||
<FilterItem
|
|
||||||
caption={"Статус структуры цены"}
|
|
||||||
defaultValue={"Неподдерживаемое значение"}
|
|
||||||
value={PRICE_STRUCT_STATUS.find(item => item.value == values.priceStructStatus)?.name}
|
|
||||||
onDelete={buildFilterItemClearHandler("priceStructStatus")}
|
|
||||||
/>
|
|
||||||
<FilterItem
|
|
||||||
caption={"Состояние проекта"}
|
|
||||||
defaultValue={"Неподдерживаемое значение"}
|
|
||||||
value={PROJECT_STATE.find(item => item.value == values.prjState)?.name}
|
|
||||||
onDelete={buildFilterItemClearHandler("prjState")}
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
</Grid>
|
|
||||||
<Grid xs={2} item sx={STYLES.SEARCH_GRID_ITEM}>
|
|
||||||
<Input
|
|
||||||
fullWidth
|
|
||||||
placeholder="Поиск..."
|
|
||||||
value={search}
|
|
||||||
endAdornment={
|
|
||||||
<InputAdornment position="end">
|
|
||||||
<IconButton onClick={() => handleDoSearch(true)}>
|
|
||||||
<Icon>clear</Icon>
|
|
||||||
</IconButton>
|
|
||||||
<IconButton onClick={handleDoSearch}>
|
|
||||||
<Icon>search</Icon>
|
|
||||||
</IconButton>
|
|
||||||
</InputAdornment>
|
|
||||||
}
|
|
||||||
onKeyDown={handleSearchKeyPress}
|
|
||||||
onChange={handleSearchChange}
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств компонента - Фильтр
|
|
||||||
Filter.propTypes = {
|
|
||||||
values: FILTER_ITEMS.isRequired,
|
|
||||||
onChange: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export { FILTER_INITIAL, Filter };
|
|
@ -1,147 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - ПУП - Информация о проектах
|
|
||||||
Диалог фильтра
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React, { useState, useContext } from "react"; //Классы React
|
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
|
||||||
import { Button, Dialog, DialogTitle, DialogContent, DialogActions } from "@mui/material"; //Интерфейсные элементы
|
|
||||||
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
|
||||||
import { BUTTONS } from "../../../app.text"; //Типовые тексты
|
|
||||||
import { APP_STYLES } from "../../../app.styles"; //Типовые стили
|
|
||||||
import { FormField } from "./layouts"; //Общие компоненты панели
|
|
||||||
|
|
||||||
//---------
|
|
||||||
//Константы
|
|
||||||
//---------
|
|
||||||
|
|
||||||
//Стили
|
|
||||||
const STYLES = {
|
|
||||||
DIALOG_CONTENT: { overflowY: "auto", ...APP_STYLES.SCROLL }
|
|
||||||
};
|
|
||||||
|
|
||||||
//Структура фильтра
|
|
||||||
const FILTER_ITEMS = PropTypes.shape({
|
|
||||||
prjType: PropTypes.string,
|
|
||||||
insDep: PropTypes.string,
|
|
||||||
priceStructStatus: PropTypes.number.isRequired,
|
|
||||||
prjState: PropTypes.number.isRequired,
|
|
||||||
search: PropTypes.string
|
|
||||||
});
|
|
||||||
|
|
||||||
//Начальное состояние фильтра
|
|
||||||
const FILTER_INITIAL = { prjType: "", insDep: "", priceStructStatus: 0, prjState: 0, search: "" };
|
|
||||||
|
|
||||||
//Статусы структуры цены
|
|
||||||
const PRICE_STRUCT_STATUS = [
|
|
||||||
{ value: 0, name: "Все" },
|
|
||||||
{ value: 1, name: "Есть статьи с расходом больше 90%" },
|
|
||||||
{ value: 2, name: "Есть статьи с перерасходом" }
|
|
||||||
];
|
|
||||||
|
|
||||||
//Состояния проекта
|
|
||||||
const PROJECT_STATE = [
|
|
||||||
{ value: 0, name: "Все" },
|
|
||||||
{ value: 1, name: "Открытые" },
|
|
||||||
{ value: 2, name: "Неоткрытые" }
|
|
||||||
];
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Диалог фильтра
|
|
||||||
const FilterDialog = ({ valuesInitial, onOk, onCancel }) => {
|
|
||||||
//Собственное состояние элементов фильтра
|
|
||||||
const [values, setValues] = useState({ ...valuesInitial });
|
|
||||||
|
|
||||||
//Подключение к контексту приложения
|
|
||||||
const { pOnlineShowDictionary } = useContext(ApplicationСtx);
|
|
||||||
|
|
||||||
//Изменение элемента формы фильтра
|
|
||||||
const handleValueChanged = (name, value) => setValues(pv => ({ ...pv, [name]: value }));
|
|
||||||
|
|
||||||
//Сброс настроек фильтра
|
|
||||||
const handleResetClick = () => setValues({ ...FILTER_INITIAL });
|
|
||||||
|
|
||||||
//Сохранение фильтра
|
|
||||||
const handleOkClick = () => (onOk ? onOk(values) : null);
|
|
||||||
|
|
||||||
//Отмена фильтра
|
|
||||||
const handleCancelClick = () => (onCancel ? onCancel() : null);
|
|
||||||
|
|
||||||
//Выбор значения элемента формы из словаря
|
|
||||||
const selectFromDictionary = (unitCode, name, applyValue) => {
|
|
||||||
pOnlineShowDictionary({
|
|
||||||
unitCode,
|
|
||||||
showMethod: "main",
|
|
||||||
inputParameters: [{ name: "in_CODE", value: values[name] }],
|
|
||||||
callBack: res => applyValue(res.success ? [{ name, value: res.outParameters.out_CODE }] : null)
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
//Генерация содержимого
|
|
||||||
return (
|
|
||||||
<Dialog open onClose={handleCancelClick} fullWidth maxWidth={"md"}>
|
|
||||||
<DialogTitle>Фильтр отбора</DialogTitle>
|
|
||||||
<DialogContent sx={STYLES.DIALOG_CONTENT}>
|
|
||||||
<FormField
|
|
||||||
elementCode={"prjType"}
|
|
||||||
elementValue={values.prjType}
|
|
||||||
labelText={"Тип заказа"}
|
|
||||||
onChange={handleValueChanged}
|
|
||||||
dictionary={applyValue => selectFromDictionary("ProjectTypes", "prjType", applyValue)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
elementCode={"insDep"}
|
|
||||||
elementValue={values.insDep}
|
|
||||||
labelText={"Подразделение-ответственный"}
|
|
||||||
onChange={handleValueChanged}
|
|
||||||
dictionary={applyValue => selectFromDictionary("INS_DEPARTMENT", "insDep", applyValue)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
elementCode={"priceStructStatus"}
|
|
||||||
elementValue={values.priceStructStatus}
|
|
||||||
labelText={"Статус структуры цены"}
|
|
||||||
onChange={handleValueChanged}
|
|
||||||
list={PRICE_STRUCT_STATUS}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
elementCode={"prjState"}
|
|
||||||
elementValue={values.prjState}
|
|
||||||
labelText={"Состояние проекта"}
|
|
||||||
onChange={handleValueChanged}
|
|
||||||
list={PROJECT_STATE}
|
|
||||||
/>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions sx={STYLES.DIALOG_ACTIONS}>
|
|
||||||
<Button variant="text" onClick={handleOkClick}>
|
|
||||||
{BUTTONS.OK}
|
|
||||||
</Button>
|
|
||||||
<Button variant="text" onClick={handleResetClick}>
|
|
||||||
{BUTTONS.CLEAR}
|
|
||||||
</Button>
|
|
||||||
<Button variant="text" onClick={handleCancelClick}>
|
|
||||||
{BUTTONS.CANCEL}
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств компонента - Диалог фильтра
|
|
||||||
FilterDialog.propTypes = {
|
|
||||||
valuesInitial: FILTER_ITEMS.isRequired,
|
|
||||||
onOk: PropTypes.func,
|
|
||||||
onCancel: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export { FILTER_ITEMS, FILTER_INITIAL, PRICE_STRUCT_STATUS, PROJECT_STATE, FilterDialog };
|
|
@ -1,16 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - ПУП - Информация о проектах
|
|
||||||
Панель мониторинга: точка входа
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import { PrjInfo } from "./prj_info"; //Корневая панель информации о проектах
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export const RootClass = PrjInfo;
|
|
@ -1,168 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - ПУП - Информация о проектах
|
|
||||||
Общие дополнительная разметка и вёрстка клиентских элементов
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React, { useState, useEffect } from "react"; //Классы React
|
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
|
||||||
import { Box, Icon, Input, InputAdornment, FormControl, Select, InputLabel, MenuItem, IconButton, Typography, Switch, Stack } from "@mui/material"; //Интерфейсные компоненты
|
|
||||||
|
|
||||||
//---------
|
|
||||||
//Константы
|
|
||||||
//---------
|
|
||||||
|
|
||||||
//Стили
|
|
||||||
const STYLES = {
|
|
||||||
STATE: value => ({ color: value === 1 ? "green" : "black" }),
|
|
||||||
COST_STATUS: color => ({ color, verticalAlign: "middle" }),
|
|
||||||
COST_READY: value => ({ color: value <= 30 ? "red" : value >= 80 ? "green" : "#e2af00" }),
|
|
||||||
TOGGLE_COLOR: checked => ({ color: checked ? "#006dd9" : "lightgrey" })
|
|
||||||
};
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Поле ввода формы
|
|
||||||
const FormField = ({ elementCode, elementValue, labelText, onChange, dictionary, list, type, ...other }) => {
|
|
||||||
//Значение элемента
|
|
||||||
const [value, setValue] = useState(elementValue);
|
|
||||||
|
|
||||||
//При получении нового значения из вне
|
|
||||||
useEffect(() => {
|
|
||||||
setValue(elementValue);
|
|
||||||
}, [elementValue]);
|
|
||||||
|
|
||||||
//Выбор значения из словаря
|
|
||||||
const handleDictionaryClick = () =>
|
|
||||||
dictionary ? dictionary(res => (res ? res.map(i => handleChange({ target: { name: i.name, value: i.value } })) : null)) : null;
|
|
||||||
|
|
||||||
//Изменение значения элемента (по событию)
|
|
||||||
const handleChange = e => {
|
|
||||||
setValue(e.target.value);
|
|
||||||
if (onChange) onChange(e.target.name, e.target.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Генерация содержимого
|
|
||||||
return (
|
|
||||||
<Box p={1}>
|
|
||||||
<FormControl variant="standard" fullWidth {...other}>
|
|
||||||
{list ? (
|
|
||||||
<>
|
|
||||||
<InputLabel id={`${elementCode}Lable`} shrink>
|
|
||||||
{labelText}
|
|
||||||
</InputLabel>
|
|
||||||
<Select
|
|
||||||
labelId={`${elementCode}Lable`}
|
|
||||||
id={elementCode}
|
|
||||||
name={elementCode}
|
|
||||||
label={labelText}
|
|
||||||
value={value || value == 0 ? value : ""}
|
|
||||||
onChange={handleChange}
|
|
||||||
displayEmpty
|
|
||||||
>
|
|
||||||
{list.map((item, i) => (
|
|
||||||
<MenuItem key={i} value={item.value || item.value == 0 ? item.value : ""}>
|
|
||||||
{item.name}
|
|
||||||
</MenuItem>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<InputLabel {...(type == "date" ? { shrink: true } : {})} htmlFor={elementCode}>
|
|
||||||
{labelText}
|
|
||||||
</InputLabel>
|
|
||||||
<Input
|
|
||||||
id={elementCode}
|
|
||||||
name={elementCode}
|
|
||||||
value={value || value == 0 ? value : ""}
|
|
||||||
endAdornment={
|
|
||||||
dictionary ? (
|
|
||||||
<InputAdornment position="end">
|
|
||||||
<IconButton aria-label={`${elementCode} select`} onClick={handleDictionaryClick} edge="end">
|
|
||||||
<Icon>list</Icon>
|
|
||||||
</IconButton>
|
|
||||||
</InputAdornment>
|
|
||||||
) : null
|
|
||||||
}
|
|
||||||
{...(type ? { type } : {})}
|
|
||||||
onChange={handleChange}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</FormControl>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств - Поле ввода формы
|
|
||||||
FormField.propTypes = {
|
|
||||||
elementCode: PropTypes.string.isRequired,
|
|
||||||
elementValue: PropTypes.oneOfType([PropTypes.number, PropTypes.string, PropTypes.instanceOf(Date)]),
|
|
||||||
labelText: PropTypes.string.isRequired,
|
|
||||||
onChange: PropTypes.func,
|
|
||||||
dictionary: PropTypes.func,
|
|
||||||
list: PropTypes.array,
|
|
||||||
type: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
//Переключатель
|
|
||||||
const Toggle = ({ labels, checked, onChange }) => {
|
|
||||||
//Обработка переключения
|
|
||||||
const handleChange = event => (onChange ? onChange(event.target.checked) : null);
|
|
||||||
|
|
||||||
//Генерация содержимого
|
|
||||||
return (
|
|
||||||
<Stack direction={"row"} spacing={1} alignItems={"center"} justifyContent={"center"}>
|
|
||||||
<Typography sx={STYLES.TOGGLE_COLOR(!checked)}>{labels[0]}</Typography>
|
|
||||||
<Switch checked={checked} size="small" onChange={handleChange} />
|
|
||||||
<Typography sx={STYLES.TOGGLE_COLOR(checked)}>{labels[1]}</Typography>
|
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств компонента - Переключатель
|
|
||||||
Toggle.propTypes = {
|
|
||||||
labels: PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
||||||
checked: PropTypes.bool.isRequired,
|
|
||||||
onChange: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
//Формирование значения для колонки "Статус структуры цены"
|
|
||||||
const formatCostStatusValue = ({ value, onClick, type = 1 }) => {
|
|
||||||
const [text, color] =
|
|
||||||
value == 0
|
|
||||||
? ["Без отклонений", "lightgreen"]
|
|
||||||
: value == 1
|
|
||||||
? [type == 1 ? "Есть статьи с расходом более 90%" : "Расход более 90%", "#ffdf71"]
|
|
||||||
: value == 2
|
|
||||||
? [type == 1 ? "Есть статьи с перерасходом" : "Перерасход", "#eb6b6b"]
|
|
||||||
: ["Не определено", "lightgray"];
|
|
||||||
return onClick ? (
|
|
||||||
<IconButton onClick={onClick}>
|
|
||||||
<Icon sx={STYLES.COST_STATUS(color)} title={`${text}\nНажмите для детальной информации`}>
|
|
||||||
circle
|
|
||||||
</Icon>
|
|
||||||
</IconButton>
|
|
||||||
) : (
|
|
||||||
<Icon sx={STYLES.COST_STATUS(color)} title={text}>
|
|
||||||
circle
|
|
||||||
</Icon>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Формирование значения для колонки "Готов (%, зтраты)"
|
|
||||||
const formatCostReadyValue = value => {
|
|
||||||
return <span style={STYLES.COST_READY(value)}>{value}</span>;
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export { STYLES as COMMON_STYLES, FormField, Toggle, formatCostStatusValue, formatCostReadyValue };
|
|
@ -1,27 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - ПУП - Информация о проектах
|
|
||||||
Корневой компонент панели
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React from "react"; //Классы React
|
|
||||||
import { Projects } from "./projects"; //Список проектов
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Корневой компонент панели "Информация о проектах"
|
|
||||||
const PrjInfo = () => {
|
|
||||||
//Генерация содержимого
|
|
||||||
return <Projects />;
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export { PrjInfo };
|
|
@ -1,70 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - ПУП - Информация о проектах
|
|
||||||
Список проектов
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React, { useState, useContext } from "react"; //Классы React
|
|
||||||
import { P8PDataGrid } from "../../components/p8p_data_grid"; //Таблица данных
|
|
||||||
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
|
||||||
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
|
||||||
import { useProjectsDataGrid } from "./projects_hooks"; //Хуки списка проектов
|
|
||||||
import { FILTER_INITIAL, Filter } from "./filter"; //Компонент "Фильтр"
|
|
||||||
import { PROJECTS_STYLES, projectDataCellRender, projectRowExpandRender } from "./projects_layouts"; //Дополнительная разметка и вёрстка клиентских элементов
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Список проектов
|
|
||||||
const Projects = () => {
|
|
||||||
//Собственное состояние
|
|
||||||
const [projects, setProjects] = useState({ pageNumber: 1, orders: [], filter: { ...FILTER_INITIAL } });
|
|
||||||
|
|
||||||
//Состояние таблицы проектов
|
|
||||||
const [projectsDataGrid] = useProjectsDataGrid({ ...projects.filter, pageNumber: projects.pageNumber, orders: projects.orders });
|
|
||||||
|
|
||||||
//Подключение к контексту приложения
|
|
||||||
const { pOnlineShowDocument } = useContext(ApplicationСtx);
|
|
||||||
|
|
||||||
//Отображение записи проекта в штатном разделе
|
|
||||||
const showProject = async rn => pOnlineShowDocument({ unitCode: "Projects", document: rn, modal: false });
|
|
||||||
|
|
||||||
//При изменении количества отображаемых страниц
|
|
||||||
const handlePagesCountChanged = () => setProjects(pv => ({ ...pv, pageNumber: pv.pageNumber + 1 }));
|
|
||||||
|
|
||||||
//При изменении состояния сортировки
|
|
||||||
const handleOrderChanged = ({ orders }) => setProjects(pv => ({ ...pv, orders: [...orders], pageNumber: 1 }));
|
|
||||||
|
|
||||||
//При изменении фильтра
|
|
||||||
const handleFilterChanged = values => setProjects(pv => ({ ...pv, filter: { ...values }, pageNumber: 1 }));
|
|
||||||
|
|
||||||
//Генерация содержимого
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Filter values={projects.filter} onChange={handleFilterChanged} />
|
|
||||||
{projectsDataGrid.init ? (
|
|
||||||
<P8PDataGrid
|
|
||||||
{...P8P_DATA_GRID_CONFIG_PROPS}
|
|
||||||
{...projectsDataGrid}
|
|
||||||
containerComponentProps={{ sx: PROJECTS_STYLES.DATA_GRID_CONTAINER(projectsDataGrid.morePages), elevation: 0 }}
|
|
||||||
expandable={true}
|
|
||||||
fixedHeader={true}
|
|
||||||
onPagesCountChanged={handlePagesCountChanged}
|
|
||||||
onOrderChanged={handleOrderChanged}
|
|
||||||
dataCellRender={prms => projectDataCellRender({ ...prms, showProject })}
|
|
||||||
rowExpandRender={projectRowExpandRender}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export { Projects };
|
|
@ -1,82 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - ПУП - Информация о проектах
|
|
||||||
Список проектов: пользовательские хуки для взаимодействия с сервером
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import { useState, useContext, useEffect } from "react"; //Классы React
|
|
||||||
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
|
||||||
import { object2Base64XML, formatDateRF } from "../../core/utils"; //Вспомогательные функции
|
|
||||||
import config from "../../../app.config"; //Настройки приложения
|
|
||||||
|
|
||||||
//---------
|
|
||||||
//Константы
|
|
||||||
//---------
|
|
||||||
|
|
||||||
//Размер страницы данных
|
|
||||||
const DATA_GRID_PAGE_SIZE = config.SYSTEM.PAGE_SIZE;
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Получение данных проектов с сервера
|
|
||||||
const useProjectsDataGrid = ({ prjType, insDep, priceStructStatus, prjState, search, pageNumber, orders }) => {
|
|
||||||
//Собственное состояние - флаг загрузки
|
|
||||||
const [isLoading, setLoading] = useState(false);
|
|
||||||
|
|
||||||
//Собственное состояние - таблица данных
|
|
||||||
const [data, setData] = useState({ init: false, morePages: true });
|
|
||||||
|
|
||||||
//Подключение к контексту взаимодействия с сервером
|
|
||||||
const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
|
|
||||||
|
|
||||||
//При необходимости обновить данные таблицы
|
|
||||||
useEffect(() => {
|
|
||||||
//Загрузка данных таблицы с сервера
|
|
||||||
const loadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoading(true);
|
|
||||||
const data = await executeStored({
|
|
||||||
stored: "PKG_P8PANELS_PROJECTS.INFO_PROJECTS_DG",
|
|
||||||
args: {
|
|
||||||
SPRJ_TYPE: prjType,
|
|
||||||
SINS_DEPARTMENT: insDep,
|
|
||||||
NCOST_STATUS: priceStructStatus,
|
|
||||||
NSTATE: prjState,
|
|
||||||
SSEARCH: search,
|
|
||||||
CORDERS: { VALUE: object2Base64XML(orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
|
|
||||||
NPAGE_NUMBER: pageNumber,
|
|
||||||
NPAGE_SIZE: DATA_GRID_PAGE_SIZE,
|
|
||||||
NINCLUDE_DEF: pageNumber == 1 ? 1 : 0
|
|
||||||
},
|
|
||||||
respArg: "COUT",
|
|
||||||
loader: true,
|
|
||||||
attributeValueProcessor: (name, val) => (["DBEGPLAN", "DENDPLAN"].includes(name) ? formatDateRF(val) : val)
|
|
||||||
});
|
|
||||||
setData(pv => ({
|
|
||||||
...pv,
|
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef || [],
|
|
||||||
rows: pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...(pv.rows || []), ...(data.XDATA_GRID.rows || [])],
|
|
||||||
morePages: DATA_GRID_PAGE_SIZE == 0 ? false : (data.XDATA_GRID.rows || []).length >= DATA_GRID_PAGE_SIZE,
|
|
||||||
init: true
|
|
||||||
}));
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
loadData();
|
|
||||||
}, [prjType, insDep, priceStructStatus, prjState, search, pageNumber, orders, executeStored, SERV_DATA_TYPE_CLOB]);
|
|
||||||
|
|
||||||
//Возвращаем интерфейс хука
|
|
||||||
return [data, isLoading];
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export { useProjectsDataGrid };
|
|
@ -1,96 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - ПУП - Информация о проектах
|
|
||||||
Список проектов: дополнительная разметка и вёрстка клиентских элементов
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React from "react"; //Классы React
|
|
||||||
import { Icon, Stack, Paper, Link } from "@mui/material"; //Интерфейсные элементы
|
|
||||||
import { P8P_DATA_GRID_MORE_HEIGHT } from "../../components/p8p_data_grid"; //Таблица данных
|
|
||||||
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
|
|
||||||
import { APP_STYLES } from "../../../app.styles"; //Типовые стили
|
|
||||||
import { COMMON_STYLES, formatCostStatusValue, formatCostReadyValue } from "./layouts"; //Общие стили и разметка панели
|
|
||||||
import { Stages } from "./stages"; //Компонент "Этапы проекта"
|
|
||||||
|
|
||||||
//---------
|
|
||||||
//Константы
|
|
||||||
//---------
|
|
||||||
|
|
||||||
//Высота фильтра (пиксели)
|
|
||||||
const FILTER_HEIGHT = "60px";
|
|
||||||
|
|
||||||
//Стили
|
|
||||||
const STYLES = {
|
|
||||||
DATA_GRID_CONTAINER: morePages => ({
|
|
||||||
height: `calc(100vh - ${APP_BAR_HEIGHT} - ${FILTER_HEIGHT} - ${morePages ? P8P_DATA_GRID_MORE_HEIGHT : "0px"} - 8px)`,
|
|
||||||
...APP_STYLES.SCROLL
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Формирование значения для колонки "Состояние" проекта
|
|
||||||
const formatPrjStateValue = value => {
|
|
||||||
const [text, icon] =
|
|
||||||
value == 0
|
|
||||||
? ["Зарегистрирован", "app_registration"]
|
|
||||||
: value == 1
|
|
||||||
? ["Открыт", "lock_open"]
|
|
||||||
: value == 2
|
|
||||||
? ["Остановлен", "do_not_disturb_on"]
|
|
||||||
: value == 3
|
|
||||||
? ["Закрыт", "lock_outline"]
|
|
||||||
: value == 4
|
|
||||||
? ["Согласован", "thumb_up_alt"]
|
|
||||||
: ["Исполнение прекращено", "block"];
|
|
||||||
return (
|
|
||||||
<Stack direction="row" gap={0.5} alignItems="center" justifyContent="center">
|
|
||||||
<Icon title={text} sx={COMMON_STYLES.STATE(value)}>
|
|
||||||
{icon}
|
|
||||||
</Icon>
|
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Форматирование ячеек таблицы "Проекты"
|
|
||||||
const projectDataCellRender = ({ row, columnDef, showProject }) => {
|
|
||||||
//Формирование представлений
|
|
||||||
switch (columnDef.name) {
|
|
||||||
case "NCOST_STATUS":
|
|
||||||
return { cellProps: { align: "center" }, data: formatCostStatusValue({ value: row[columnDef.name] }) };
|
|
||||||
case "NCOST_READY":
|
|
||||||
return { cellProps: { align: "center" }, data: formatCostReadyValue(row[columnDef.name]) };
|
|
||||||
case "NSTATE":
|
|
||||||
return { cellProps: { align: "center" }, data: formatPrjStateValue(row[columnDef.name]) };
|
|
||||||
case "SCODE":
|
|
||||||
return {
|
|
||||||
data: (
|
|
||||||
<Link component="button" align="left" underline="hover" onClick={() => showProject(row["NRN"])}>
|
|
||||||
{row[columnDef.name]}
|
|
||||||
</Link>
|
|
||||||
)
|
|
||||||
};
|
|
||||||
default:
|
|
||||||
return { data: row[columnDef.name] };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//Генерация представления расширения строки таблицы "Проектов"
|
|
||||||
const projectRowExpandRender = ({ row }) => {
|
|
||||||
return (
|
|
||||||
<Paper elevation={6}>
|
|
||||||
<Stages projectRn={row.NRN} projectCode={row.SCODE} />
|
|
||||||
</Paper>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export { STYLES as PROJECTS_STYLES, projectDataCellRender, projectRowExpandRender };
|
|
@ -1,173 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - ПУП - Информация о проектах
|
|
||||||
Детальная информация об этапе проекта
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React, { useState, useContext } from "react"; //Классы React
|
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
|
||||||
import { Grid, Box, Typography, Paper, Drawer, IconButton, Icon } from "@mui/material"; //Интерфейсные элементы
|
|
||||||
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
|
||||||
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
|
||||||
import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений
|
|
||||||
import { TEXTS } from "../../../app.text"; //Тектовые ресурсы и константы
|
|
||||||
import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../components/p8p_data_grid"; //Таблица данных
|
|
||||||
import { P8PChart } from "../../components/p8p_chart"; //График
|
|
||||||
import { P8PAppInlineError } from "../../components/p8p_app_message"; //Встраиваемое сообщение об ошибке
|
|
||||||
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
|
||||||
import { useStageDetailInfoDataGrid, useStageDetailArtsDataGrid, useStageDetailArtsChart } from "./stage_detail_hooks"; //Хуки детализации этапов проекта
|
|
||||||
import { Toggle } from "./layouts"; //Общая разметка и компоненты панели
|
|
||||||
import {
|
|
||||||
STAGE_DETAIL_STYLES,
|
|
||||||
stageDetailInfoHeadCellRender,
|
|
||||||
stageDetailInfoDataCellRender,
|
|
||||||
stageDetailArtsHeadCellRender,
|
|
||||||
stageDetailArtsDataCellRender
|
|
||||||
} from "./stage_detail_layouts"; //Дополнительная разметка и вёрстка клиентских элементов
|
|
||||||
|
|
||||||
//------------------------------------
|
|
||||||
//Вспомогательные функции и компоненты
|
|
||||||
//------------------------------------
|
|
||||||
|
|
||||||
//Данные этапа
|
|
||||||
const StageDetailData = ({ stageRn }) => {
|
|
||||||
//Собственное состояние
|
|
||||||
const [state, setState] = useState({ artsDisplayType: 0, artsChartType: 0 });
|
|
||||||
|
|
||||||
//Подключение к контексту взаимодействия с сервером
|
|
||||||
const { executeStored } = useContext(BackEndСtx);
|
|
||||||
|
|
||||||
//Подключение к контексту приложения
|
|
||||||
const { pOnlineShowUnit } = useContext(ApplicationСtx);
|
|
||||||
|
|
||||||
//Подключение к контексту сообщений
|
|
||||||
const { showMsgErr } = useContext(MessagingСtx);
|
|
||||||
|
|
||||||
//Отображение журнала затрат (фактического, по рег. номеру ЛС и статьи затрат)
|
|
||||||
const showCostNotesFact = async ({ faceAccRn, artclRn }) => {
|
|
||||||
const data = await executeStored({
|
|
||||||
stored: "PKG_P8PANELS_PROJECTS.INFO_FCCOSTNOTES_FACT_SELECT",
|
|
||||||
args: { NFACEACC: faceAccRn, NFPDARTCL: artclRn }
|
|
||||||
});
|
|
||||||
if (data.NIDENT) pOnlineShowUnit({ unitCode: "CostNotes", inputParameters: [{ name: "in_IDENT", value: data.NIDENT }] });
|
|
||||||
else showMsgErr(TEXTS.NO_DATA_FOUND);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Состояние таблицы с информацией об этапе
|
|
||||||
const [stageDeatilInfoDataGrid] = useStageDetailInfoDataGrid({ stageRn });
|
|
||||||
|
|
||||||
//Состояние таблицы с данными структуры цены
|
|
||||||
const [stageDeatilArtsDataGrid] = useStageDetailArtsDataGrid({ stageRn });
|
|
||||||
|
|
||||||
//Состояние графика с данными структуры цены
|
|
||||||
const [stageDeatilArtsChart] = useStageDetailArtsChart({ stageRn, display: state.artsDisplayType == 1, type: state.artsChartType });
|
|
||||||
|
|
||||||
//При изменении способа отображения структуры цены
|
|
||||||
const handleArtsDisplayTypeChange = checked => setState(pv => ({ ...pv, artsDisplayType: checked ? 1 : 0 }));
|
|
||||||
|
|
||||||
//При изменении типа данных графика структуры цены
|
|
||||||
const handleArtsChartTypeChange = checked => setState(pv => ({ ...pv, artsChartType: checked ? 1 : 0 }));
|
|
||||||
|
|
||||||
//Отработка нажатия на график
|
|
||||||
const handleChartClick = ({ item }) =>
|
|
||||||
state.artsChartType === 1 && item.NFACEACC && item.NFPDARTCL
|
|
||||||
? showCostNotesFact({ faceAccRn: item.NFACEACC, artclRn: item.NFPDARTCL })
|
|
||||||
: null;
|
|
||||||
|
|
||||||
//Генерация содержимого
|
|
||||||
return (
|
|
||||||
<Grid container spacing={2} sx={STAGE_DETAIL_STYLES.DATA_AREA_CONTAINER}>
|
|
||||||
<Grid item xs={5}>
|
|
||||||
<Typography variant={"h6"} sx={STAGE_DETAIL_STYLES.DATA_AREA_HEADER}>
|
|
||||||
Сведения
|
|
||||||
</Typography>
|
|
||||||
{stageDeatilInfoDataGrid.init ? (
|
|
||||||
<P8PDataGrid
|
|
||||||
{...P8P_DATA_GRID_CONFIG_PROPS}
|
|
||||||
containerComponentProps={{ sx: STAGE_DETAIL_STYLES.DATA_AREA, elevation: 6 }}
|
|
||||||
{...stageDeatilInfoDataGrid}
|
|
||||||
size={P8P_DATA_GRID_SIZE.SMALL}
|
|
||||||
fixedHeader={true}
|
|
||||||
headCellRender={stageDetailInfoHeadCellRender}
|
|
||||||
dataCellRender={stageDetailInfoDataCellRender}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={7}>
|
|
||||||
<Box sx={STAGE_DETAIL_STYLES.DATA_AREA_HEADER_CONTAINER}>
|
|
||||||
<Typography variant={"h6"} sx={STAGE_DETAIL_STYLES.DATA_AREA_HEADER}>
|
|
||||||
Структура цены
|
|
||||||
</Typography>
|
|
||||||
<Toggle labels={["Таблица", "График"]} checked={state.artsDisplayType === 1} onChange={handleArtsDisplayTypeChange} />
|
|
||||||
</Box>
|
|
||||||
{state.artsDisplayType === 0 ? (
|
|
||||||
stageDeatilArtsDataGrid.init ? (
|
|
||||||
<P8PDataGrid
|
|
||||||
{...P8P_DATA_GRID_CONFIG_PROPS}
|
|
||||||
containerComponentProps={{ sx: STAGE_DETAIL_STYLES.DATA_AREA, elevation: 6 }}
|
|
||||||
{...stageDeatilArtsDataGrid}
|
|
||||||
size={P8P_DATA_GRID_SIZE.SMALL}
|
|
||||||
fixedHeader={true}
|
|
||||||
headCellRender={stageDetailArtsHeadCellRender}
|
|
||||||
dataCellRender={prms => stageDetailArtsDataCellRender({ ...prms, showCostNotesFact })}
|
|
||||||
/>
|
|
||||||
) : null
|
|
||||||
) : (
|
|
||||||
<Paper elevation={6} sx={STAGE_DETAIL_STYLES.DATA_AREA}>
|
|
||||||
<Box sx={STAGE_DETAIL_STYLES.CHART_CONTAINER}>
|
|
||||||
<Toggle labels={["План", "Факт"]} checked={state.artsChartType === 1} onChange={handleArtsChartTypeChange} />
|
|
||||||
{stageDeatilArtsDataGrid?.rows?.length > 0 ? (
|
|
||||||
stageDeatilArtsChart.init ? (
|
|
||||||
<P8PChart style={STAGE_DETAIL_STYLES.CHART} {...stageDeatilArtsChart} onClick={handleChartClick} />
|
|
||||||
) : null
|
|
||||||
) : (
|
|
||||||
<P8PAppInlineError text={TEXTS.NO_DATA_FOUND} />
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
</Paper>
|
|
||||||
)}
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств компонента - Данные этапа
|
|
||||||
StageDetailData.propTypes = {
|
|
||||||
stageRn: PropTypes.number
|
|
||||||
};
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Детальная информация об этапе проекта
|
|
||||||
const StageDetail = ({ stageRn, stageName, isOpen, onClose }) => {
|
|
||||||
return (
|
|
||||||
<Drawer anchor={"right"} open={isOpen} onClose={onClose} sx={STAGE_DETAIL_STYLES.STAGE_DETAIL_DRAWER}>
|
|
||||||
<Box sx={STAGE_DETAIL_STYLES.STAGE_DETAIL_HEADER}>
|
|
||||||
<IconButton sx={STAGE_DETAIL_STYLES.STAGE_DETAIL_CLOSE_BUTTON} size={"small"} onClick={onClose}>
|
|
||||||
<Icon>close</Icon>
|
|
||||||
</IconButton>
|
|
||||||
<Typography variant={"h6"} color={"white"} pl={2}>{`Этап: ${stageName}`}</Typography>
|
|
||||||
</Box>
|
|
||||||
<StageDetailData stageRn={stageRn} />
|
|
||||||
</Drawer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств компонента - Детальная информация об этапе проекта
|
|
||||||
StageDetail.propTypes = {
|
|
||||||
stageRn: PropTypes.number,
|
|
||||||
stageName: PropTypes.string,
|
|
||||||
isOpen: PropTypes.bool.isRequired,
|
|
||||||
onClose: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export { StageDetail };
|
|
@ -1,126 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - ПУП - Информация о проектах
|
|
||||||
Детальная информация об этапе проекта: пользовательские хуки для взаимодействия с сервером
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import { useState, useContext, useEffect } from "react"; //Классы React
|
|
||||||
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Детали этапа проекта - информация об этапе
|
|
||||||
const useStageDetailInfoDataGrid = ({ stageRn }) => {
|
|
||||||
//Собственное состояние - флаг загрузки
|
|
||||||
const [isLoading, setLoading] = useState(false);
|
|
||||||
|
|
||||||
//Собственное состояние - таблица данных
|
|
||||||
const [data, setData] = useState({ init: false });
|
|
||||||
|
|
||||||
//Подключение к контексту взаимодействия с сервером
|
|
||||||
const { executeStored } = useContext(BackEndСtx);
|
|
||||||
|
|
||||||
//При необходимости обновить данные таблицы
|
|
||||||
useEffect(() => {
|
|
||||||
//Загрузка данных таблицы с сервера
|
|
||||||
const loadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoading(true);
|
|
||||||
const data = await executeStored({
|
|
||||||
stored: "PKG_P8PANELS_PROJECTS.INFO_STAGE_DTL_DG",
|
|
||||||
args: { NPROJECTSTAGE: stageRn },
|
|
||||||
respArg: "COUT",
|
|
||||||
loader: true
|
|
||||||
});
|
|
||||||
setData(pv => ({ ...pv, ...data.XDATA_GRID, init: true }));
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (stageRn) loadData();
|
|
||||||
}, [stageRn, executeStored]);
|
|
||||||
|
|
||||||
//Возвращаем интерфейс хука
|
|
||||||
return [data, isLoading];
|
|
||||||
};
|
|
||||||
|
|
||||||
//Детали этапа проекта - структура цены - таблица данных
|
|
||||||
const useStageDetailArtsDataGrid = ({ stageRn }) => {
|
|
||||||
//Собственное состояние - флаг загрузки
|
|
||||||
const [isLoading, setLoading] = useState(false);
|
|
||||||
|
|
||||||
//Собственное состояние - таблица данных
|
|
||||||
const [data, setData] = useState({ init: false });
|
|
||||||
|
|
||||||
//Подключение к контексту взаимодействия с сервером
|
|
||||||
const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
|
|
||||||
|
|
||||||
//При необходимости обновить данные таблицы
|
|
||||||
useEffect(() => {
|
|
||||||
//Загрузка данных таблицы с сервера
|
|
||||||
const loadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoading(true);
|
|
||||||
const artsData = await executeStored({
|
|
||||||
stored: "PKG_P8PANELS_PROJECTS.INFO_STAGE_ARTS_DG",
|
|
||||||
args: { NPROJECTSTAGE: stageRn },
|
|
||||||
respArg: "COUT",
|
|
||||||
loader: true
|
|
||||||
});
|
|
||||||
setData(pv => ({ ...pv, ...artsData.XDATA_GRID, init: true }));
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (stageRn) loadData();
|
|
||||||
}, [stageRn, executeStored, SERV_DATA_TYPE_CLOB]);
|
|
||||||
|
|
||||||
//Возвращаем интерфейс хука
|
|
||||||
return [data, isLoading];
|
|
||||||
};
|
|
||||||
|
|
||||||
//Детали этапа проекта - структура цены - график
|
|
||||||
const useStageDetailArtsChart = ({ stageRn, display, type }) => {
|
|
||||||
//Собственное состояние - флаг загрузки
|
|
||||||
const [isLoading, setLoading] = useState(false);
|
|
||||||
|
|
||||||
//Собственное состояние - график
|
|
||||||
const [data, setData] = useState({ init: false, currentType: null });
|
|
||||||
|
|
||||||
//Подключение к контексту взаимодействия с сервером
|
|
||||||
const { executeStored } = useContext(BackEndСtx);
|
|
||||||
|
|
||||||
//При необходимости обновить данные таблицы
|
|
||||||
useEffect(() => {
|
|
||||||
//Загрузка данных таблицы с сервера
|
|
||||||
const loadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoading(true);
|
|
||||||
const data = await executeStored({
|
|
||||||
stored: "PKG_P8PANELS_PROJECTS.INFO_STAGE_ARTS_CHART",
|
|
||||||
args: { NPROJECTSTAGE: stageRn, NTYPE: type },
|
|
||||||
respArg: "COUT",
|
|
||||||
loader: true
|
|
||||||
});
|
|
||||||
setData(pv => ({ ...pv, ...data.XCHART, currentType: type, init: true }));
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (stageRn && display && data.currentType != type) loadData();
|
|
||||||
}, [stageRn, display, type, data.currentType, executeStored]);
|
|
||||||
|
|
||||||
//Возвращаем интерфейс хука
|
|
||||||
return [data, isLoading];
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export { useStageDetailInfoDataGrid, useStageDetailArtsDataGrid, useStageDetailArtsChart };
|
|
@ -1,148 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - ПУП - Информация о проектах
|
|
||||||
Детальная информация об этапе проекта: дополнительная разметка и вёрстка клиентских элементов
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React from "react"; //Классы React
|
|
||||||
import { Link } from "@mui/material"; //Интерфейсные элементы
|
|
||||||
import { APP_STYLES } from "../../../app.styles"; //Типовые стили
|
|
||||||
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
|
|
||||||
import { formatNumberRFCurrency } from "../../core/utils"; //Вспомогательные функции
|
|
||||||
import { formatCostStatusValue } from "./layouts"; //Общие стили и разметка панели
|
|
||||||
import { formatStageStatusValue } from "./stages_layouts"; //Cтили и разметка списка этапов проекта
|
|
||||||
|
|
||||||
//---------
|
|
||||||
//Константы
|
|
||||||
//---------
|
|
||||||
|
|
||||||
//Высота заголовка информационного блока
|
|
||||||
const DATA_AREA_HEADER_HEIGHT = "52px";
|
|
||||||
|
|
||||||
//Стили
|
|
||||||
const STYLES = {
|
|
||||||
STAGE_DETAIL_DRAWER: { flexShrink: 0, [`& .MuiDrawer-paper`]: { width: "70%", boxSizing: "border-box", ...APP_STYLES.SCROLL } },
|
|
||||||
STAGE_DETAIL_HEADER: {
|
|
||||||
height: APP_BAR_HEIGHT,
|
|
||||||
paddingLeft: "24px",
|
|
||||||
backgroundColor: "#1976d2",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "flex-start"
|
|
||||||
},
|
|
||||||
STAGE_DETAIL_CLOSE_BUTTON: { color: "white", marginBottom: "3px" },
|
|
||||||
DATA_AREA_CONTAINER: { paddingLeft: "10px", paddingRight: "10px" },
|
|
||||||
DATA_AREA: { height: `calc(100vh - ${APP_BAR_HEIGHT} - ${DATA_AREA_HEADER_HEIGHT} - 10px)`, overflowY: "auto", ...APP_STYLES.SCROLL },
|
|
||||||
DATA_AREA_HEADER_CONTAINER: { display: "flex", justifyContent: "space-between" },
|
|
||||||
DATA_AREA_HEADER: { paddingTop: "10px", paddingBottom: "10px" },
|
|
||||||
DATA_GRID_HEADER: { fontSize: "10pt", padding: "6px 10px" },
|
|
||||||
DATA_GRID_CELL: value => ({ fontSize: "9pt", padding: "6px 10px", ...(value ? { color: value > 0 ? "green" : "red" } : {}) }),
|
|
||||||
CHART_CONTAINER: { paddingTop: "20px" },
|
|
||||||
CHART: { maxHeight: "60vh", display: "flex", justifyContent: "center" }
|
|
||||||
};
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Форматирование заголовков колонок таблицы "Сведения"
|
|
||||||
const stageDetailInfoHeadCellRender = ({ columnDef }) => {
|
|
||||||
//Инициализируем общий стиль ячеек
|
|
||||||
let cellStyle = STYLES.DATA_GRID_HEADER;
|
|
||||||
//Формирование представлений
|
|
||||||
switch (columnDef.name) {
|
|
||||||
case "SATTR":
|
|
||||||
return { cellStyle, stackProps: { justifyContent: "left" } };
|
|
||||||
case "SVALUE":
|
|
||||||
return { cellStyle, stackProps: { justifyContent: "right" } };
|
|
||||||
default:
|
|
||||||
return { cellStyle: cellStyle };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//Форматирование ячеек строк таблицы "Сведения"
|
|
||||||
const stageDetailInfoDataCellRender = ({ row, columnDef }) => {
|
|
||||||
//Инициализируем общий стиль ячеек
|
|
||||||
let cellStyle = STYLES.DATA_GRID_CELL();
|
|
||||||
//Формирование представлений
|
|
||||||
switch (columnDef.name) {
|
|
||||||
case "SATTR":
|
|
||||||
return { cellStyle: { ...cellStyle, color: "#1976d2" }, cellProps: { align: "left" } };
|
|
||||||
case "SVALUE": {
|
|
||||||
const res = { cellStyle, cellProps: { align: "right" } };
|
|
||||||
if (["NCOST_SUM", "NSTAGE_COST_SUM"].includes(row["SCODE"]))
|
|
||||||
res.data = row["SVALUE"] || row["SVALUE"] === 0 ? formatNumberRFCurrency(row["SVALUE"]) : "-";
|
|
||||||
if (row["SCODE"] == "NSTATE")
|
|
||||||
res.data = formatStageStatusValue({ value: parseInt(row["SVALUE"]), addText: true, justifyContent: "right" });
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return { cellStyle };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//Форматирование заголовков колонок таблицы "Структура затрат"
|
|
||||||
const stageDetailArtsHeadCellRender = ({ columnDef }) => {
|
|
||||||
//Инициализируем общий стиль ячеек
|
|
||||||
let cellStyle = STYLES.DATA_GRID_HEADER;
|
|
||||||
//Формирование представлений
|
|
||||||
switch (columnDef.name) {
|
|
||||||
case "NSTATE":
|
|
||||||
return { cellStyle: { ...cellStyle, justifyContent: "center" }, stackStyle: { justifyContent: "center" } };
|
|
||||||
default:
|
|
||||||
return { cellStyle: cellStyle };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//Форматирование ячеек строк таблицы "Структура затрат"
|
|
||||||
const stageDetailArtsDataCellRender = ({ row, columnDef, showCostNotesFact }) => {
|
|
||||||
//Инициализируем общий стиль ячеек
|
|
||||||
let cellStyle = STYLES.DATA_GRID_CELL;
|
|
||||||
//Формирование представлений
|
|
||||||
switch (columnDef.name) {
|
|
||||||
case "NCOST_STATUS":
|
|
||||||
return {
|
|
||||||
cellProps: { align: "center" },
|
|
||||||
data: formatCostStatusValue({ value: row[columnDef.name], type: 0 })
|
|
||||||
};
|
|
||||||
case "NPLAN_SUM":
|
|
||||||
case "NPLAN_FACT_SUM":
|
|
||||||
return {
|
|
||||||
cellStyle: cellStyle(columnDef.name == "NPLAN_FACT_SUM" ? row[columnDef.name] : null),
|
|
||||||
data: row[columnDef.name] || row[columnDef.name] === 0 ? formatNumberRFCurrency(row[columnDef.name]) : "-"
|
|
||||||
};
|
|
||||||
case "NFACT_SUM":
|
|
||||||
return {
|
|
||||||
cellStyle: cellStyle(),
|
|
||||||
data:
|
|
||||||
row[columnDef.name] || row[columnDef.name] === 0 ? (
|
|
||||||
row[columnDef.name] > 0 ? (
|
|
||||||
<Link component="button" onClick={() => showCostNotesFact({ faceAccRn: row["NFACEACC"], artclRn: row["NFPDARTCL"] })}>
|
|
||||||
{formatNumberRFCurrency(row[columnDef.name])}
|
|
||||||
</Link>
|
|
||||||
) : (
|
|
||||||
formatNumberRFCurrency(row[columnDef.name])
|
|
||||||
)
|
|
||||||
) : (
|
|
||||||
"-"
|
|
||||||
)
|
|
||||||
};
|
|
||||||
default:
|
|
||||||
return { cellStyle: cellStyle(), data: row[columnDef.name] };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export {
|
|
||||||
STYLES as STAGE_DETAIL_STYLES,
|
|
||||||
stageDetailInfoHeadCellRender,
|
|
||||||
stageDetailInfoDataCellRender,
|
|
||||||
stageDetailArtsHeadCellRender,
|
|
||||||
stageDetailArtsDataCellRender
|
|
||||||
};
|
|
@ -1,95 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - ПУП - Информация о проектах
|
|
||||||
Список этапов проекта
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React, { useState, useContext } from "react"; //Классы React
|
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
|
||||||
import { Typography } from "@mui/material"; //Интерфейсные элементы
|
|
||||||
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
|
||||||
import { P8PDataGrid } from "../../components/p8p_data_grid"; //Таблица данных
|
|
||||||
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
|
||||||
import { useStagesDataGrid } from "./stages_hooks"; //Хуки списка этапов проекта
|
|
||||||
import { STAGES_STYLES, projectStageDataCellRender } from "./stages_layouts"; //Дополнительная разметка и вёрстка клиентских элементов
|
|
||||||
import { StageDetail } from "./stage_detail"; //Компонент "Информация об этапе проекта"
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Список этапов проекта
|
|
||||||
const Stages = ({ projectRn, projectCode }) => {
|
|
||||||
//Собственное состояние
|
|
||||||
const [stages, setStages] = useState({ pageNumber: 1, orders: [] });
|
|
||||||
|
|
||||||
//Состояние таблицы этапов
|
|
||||||
const [stagesDataGrid] = useStagesDataGrid({ ...stages, projectRn });
|
|
||||||
|
|
||||||
//Состояние информации о этапе
|
|
||||||
const [stageInfo, setStageInfo] = useState({ showInfo: false, stage: null, sFaceAcc: null });
|
|
||||||
|
|
||||||
//Подключение к контексту приложения
|
|
||||||
const { pOnlineShowUnit } = useContext(ApplicationСtx);
|
|
||||||
|
|
||||||
//Отображение записи этапа проекта в штатном разделе
|
|
||||||
const showProjectStage = (prn, rn) => {
|
|
||||||
pOnlineShowUnit({
|
|
||||||
unitCode: "Projects",
|
|
||||||
inputParameters: [
|
|
||||||
{ name: "in_RN", value: prn },
|
|
||||||
{ name: "in_STAGE_RN", value: rn }
|
|
||||||
],
|
|
||||||
modal: false
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
//Отображение деталей этапа
|
|
||||||
const showStageDetails = stage => setStageInfo(pv => ({ ...pv, showInfo: true, stage: stage["NRN"], sFaceAcc: stage["SFACEACC"] }));
|
|
||||||
|
|
||||||
//При изменении количества отображаемых страниц
|
|
||||||
const handlePagesCountChanged = () => setStages(pv => ({ ...pv, pageNumber: pv.pageNumber + 1 }));
|
|
||||||
|
|
||||||
//При изменении состояния сортировки
|
|
||||||
const handleOrderChanged = ({ orders }) => setStages(pv => ({ ...pv, orders: [...orders], pageNumber: 1 }));
|
|
||||||
|
|
||||||
//Генерация содержимого
|
|
||||||
return stagesDataGrid.init ? (
|
|
||||||
<>
|
|
||||||
<div style={STAGES_STYLES.CONTAINER}>
|
|
||||||
<Typography variant={"subtitle2"} sx={STAGES_STYLES.TITLE}>
|
|
||||||
{`Этапы проекта "${projectCode}"`}
|
|
||||||
</Typography>
|
|
||||||
<P8PDataGrid
|
|
||||||
{...P8P_DATA_GRID_CONFIG_PROPS}
|
|
||||||
{...stagesDataGrid}
|
|
||||||
containerComponentProps={{ sx: STAGES_STYLES.DATA_GRID_CONTAINER, elevation: 0 }}
|
|
||||||
onPagesCountChanged={handlePagesCountChanged}
|
|
||||||
onOrderChanged={handleOrderChanged}
|
|
||||||
dataCellRender={prms => projectStageDataCellRender({ ...prms, showProjectStage, showStageDetails })}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<StageDetail
|
|
||||||
stageRn={stageInfo.stage}
|
|
||||||
stageName={stageInfo.sFaceAcc}
|
|
||||||
isOpen={stageInfo.showInfo}
|
|
||||||
onClose={() => setStageInfo(pv => ({ ...pv, showInfo: false, stage: null, sFaceAcc: null }))}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
) : null;
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств компонента - Список этапов проекта
|
|
||||||
Stages.propTypes = {
|
|
||||||
projectRn: PropTypes.number.isRequired,
|
|
||||||
projectCode: PropTypes.string.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export { Stages };
|
|
@ -1,78 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - ПУП - Информация о проектах
|
|
||||||
Список этапов проекта: пользовательские хуки для взаимодействия с сервером
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import { useState, useContext, useEffect } from "react"; //Классы React
|
|
||||||
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
|
||||||
import { object2Base64XML, formatDateRF } from "../../core/utils"; //Вспомогательные функции
|
|
||||||
import config from "../../../app.config"; //Настройки приложения
|
|
||||||
|
|
||||||
//---------
|
|
||||||
//Константы
|
|
||||||
//---------
|
|
||||||
|
|
||||||
//Размер страницы данных
|
|
||||||
const DATA_GRID_PAGE_SIZE = config.SYSTEM.PAGE_SIZE;
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Этапы проекта
|
|
||||||
const useStagesDataGrid = ({ projectRn, pageNumber, orders }) => {
|
|
||||||
//Собственное состояние - флаг загрузки
|
|
||||||
const [isLoading, setLoading] = useState(false);
|
|
||||||
|
|
||||||
//Собственное состояние - таблица данных
|
|
||||||
const [data, setData] = useState({ init: false, morePages: true });
|
|
||||||
|
|
||||||
//Подключение к контексту взаимодействия с сервером
|
|
||||||
const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
|
|
||||||
|
|
||||||
//При необходимости обновить данные таблицы
|
|
||||||
useEffect(() => {
|
|
||||||
//Загрузка данных таблицы с сервера
|
|
||||||
const loadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoading(true);
|
|
||||||
const data = await executeStored({
|
|
||||||
stored: "PKG_P8PANELS_PROJECTS.INFO_STAGES_DG",
|
|
||||||
args: {
|
|
||||||
NPROJECT: projectRn,
|
|
||||||
CORDERS: { VALUE: object2Base64XML(orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
|
|
||||||
NPAGE_NUMBER: pageNumber,
|
|
||||||
NPAGE_SIZE: DATA_GRID_PAGE_SIZE,
|
|
||||||
NINCLUDE_DEF: pageNumber == 1 ? 1 : 0
|
|
||||||
},
|
|
||||||
respArg: "COUT",
|
|
||||||
loader: true,
|
|
||||||
attributeValueProcessor: (name, val) => (["DBEGPLAN", "DENDPLAN"].includes(name) ? formatDateRF(val) : val)
|
|
||||||
});
|
|
||||||
setData(pv => ({
|
|
||||||
...pv,
|
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef || [],
|
|
||||||
rows: pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...(pv.rows || []), ...(data.XDATA_GRID.rows || [])],
|
|
||||||
morePages: DATA_GRID_PAGE_SIZE == 0 ? false : (data.XDATA_GRID.rows || []).length >= DATA_GRID_PAGE_SIZE,
|
|
||||||
init: true
|
|
||||||
}));
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (projectRn) loadData();
|
|
||||||
}, [projectRn, orders, pageNumber, executeStored, SERV_DATA_TYPE_CLOB]);
|
|
||||||
|
|
||||||
//Возвращаем интерфейс хука
|
|
||||||
return [data, isLoading];
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export { useStagesDataGrid };
|
|
@ -1,86 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - ПУП - Информация о проектах
|
|
||||||
Список этапов проекта: дополнительная разметка и вёрстка клиентских элементов
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React from "react"; //Классы React
|
|
||||||
import { Icon, Stack, Link } from "@mui/material"; //Интерфейсные элементы
|
|
||||||
import { formatNumberRFCurrency } from "../../core/utils"; //Спомогательные функции
|
|
||||||
import { COMMON_STYLES, formatCostStatusValue, formatCostReadyValue } from "./layouts"; //Общие стили и разметка панели
|
|
||||||
|
|
||||||
//---------
|
|
||||||
//Константы
|
|
||||||
//---------
|
|
||||||
|
|
||||||
//Стили
|
|
||||||
const STYLES = {
|
|
||||||
CONTAINER: { textAlign: "center", paddingTop: "10px", backgroundColor: "lightcyan" },
|
|
||||||
TITLE: { fontSize: "13pt", paddingBottom: "10px" },
|
|
||||||
DATA_GRID_CONTAINER: { backgroundColor: "lightcyan" }
|
|
||||||
};
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Формирование значения для колонки "Состояние" этапа
|
|
||||||
const formatStageStatusValue = ({ value, addText = false, justifyContent = "center" }) => {
|
|
||||||
const [text, icon] =
|
|
||||||
value == 0
|
|
||||||
? ["Зарегистрирован", "app_registration"]
|
|
||||||
: value == 1
|
|
||||||
? ["Открыт", "lock_open"]
|
|
||||||
: value == 2
|
|
||||||
? ["Закрыт", "lock_outline"]
|
|
||||||
: value == 3
|
|
||||||
? ["Согласован", "thumb_up_alt"]
|
|
||||||
: value == 4
|
|
||||||
? ["Исполнение прекращено", "block"]
|
|
||||||
: ["Остановлен", "do_not_disturb_on"];
|
|
||||||
return (
|
|
||||||
<Stack direction="row" gap={0.5} alignItems={"center"} justifyContent={justifyContent || "center"}>
|
|
||||||
<Icon title={text} sx={COMMON_STYLES.STATE(value)}>
|
|
||||||
{icon}
|
|
||||||
</Icon>
|
|
||||||
{addText == true ? text : null}
|
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Форматирование ячеек таблицы "Этапы проекта"
|
|
||||||
const projectStageDataCellRender = ({ row, columnDef, showProjectStage, showStageDetails }) => {
|
|
||||||
//Формирование представлений
|
|
||||||
switch (columnDef.name) {
|
|
||||||
case "NCOST_STATUS":
|
|
||||||
return {
|
|
||||||
cellProps: { align: "center" },
|
|
||||||
data: formatCostStatusValue({ value: row[columnDef.name], onClick: () => showStageDetails(row) })
|
|
||||||
};
|
|
||||||
case "NCOST_READY":
|
|
||||||
return { cellProps: { align: "center" }, data: formatCostReadyValue(row[columnDef.name]) };
|
|
||||||
case "NSTATE":
|
|
||||||
return { cellProps: { align: "center" }, data: formatStageStatusValue({ value: row[columnDef.name] }) };
|
|
||||||
case "SFACEACC":
|
|
||||||
return {
|
|
||||||
data: (
|
|
||||||
<Link component="button" align="left" underline="hover" onClick={() => showProjectStage(row["NPRN"], row["NRN"])}>
|
|
||||||
{row[columnDef.name]}
|
|
||||||
</Link>
|
|
||||||
)
|
|
||||||
};
|
|
||||||
case "NCOST_SUM":
|
|
||||||
return { data: formatNumberRFCurrency(row[columnDef.name]) };
|
|
||||||
default:
|
|
||||||
return { data: row[columnDef.name] };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export { STYLES as STAGES_STYLES, projectStageDataCellRender, formatStageStatusValue };
|
|
@ -9,23 +9,7 @@
|
|||||||
|
|
||||||
import React, { useState } from "react"; //Классы React
|
import React, { useState } from "react"; //Классы React
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import {
|
import { Box, IconButton, Icon, Dialog, DialogTitle, DialogContent, Typography, List, ListItem } from "@mui/material"; //Интерфейсные элементы
|
||||||
Box,
|
|
||||||
IconButton,
|
|
||||||
Icon,
|
|
||||||
Dialog,
|
|
||||||
DialogTitle,
|
|
||||||
DialogContent,
|
|
||||||
Button,
|
|
||||||
Typography,
|
|
||||||
List,
|
|
||||||
ListItem,
|
|
||||||
Select,
|
|
||||||
FormControl,
|
|
||||||
InputLabel,
|
|
||||||
MenuItem,
|
|
||||||
DialogActions
|
|
||||||
} from "@mui/material"; //Интерфейсные элементы
|
|
||||||
import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../../components/p8p_data_grid"; //Таблица данных
|
import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../../components/p8p_data_grid"; //Таблица данных
|
||||||
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
||||||
import { SectionTabPanel } from "./section_tab_panel"; //Компонент вкладки раздела
|
import { SectionTabPanel } from "./section_tab_panel"; //Компонент вкладки раздела
|
||||||
@ -83,8 +67,7 @@ const STYLES = {
|
|||||||
},
|
},
|
||||||
HELP_LIST_ITEM_DESC: {
|
HELP_LIST_ITEM_DESC: {
|
||||||
fontSize: "inherit"
|
fontSize: "inherit"
|
||||||
},
|
}
|
||||||
DIALOG_ACTIONS: { justifyContent: "end", paddingRight: "24px", paddingLeft: "24px" }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//------------------------------------
|
//------------------------------------
|
||||||
@ -153,89 +136,6 @@ HelpDialog.propTypes = {
|
|||||||
handleOpenHelpChange: PropTypes.func.isRequired
|
handleOpenHelpChange: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
//Диалог сортировки
|
|
||||||
const SortDialog = ({ init, handleOpenSortChange, onOrderChange }) => {
|
|
||||||
//Собственное состояние сортировки
|
|
||||||
const [order, setOrder] = useState({ row: init.rowOrder ? init.rowOrder : 0, column: init.columnOrder ? init.columnOrder : 0 });
|
|
||||||
|
|
||||||
//Изменеие сортировки
|
|
||||||
const handleOrderChange = e => setOrder(pv => ({ ...pv, [e.target.name]: e.target.value }));
|
|
||||||
|
|
||||||
//Нажатие на кнопку Ok
|
|
||||||
const handleOk = () => {
|
|
||||||
onOrderChange({ rowOrder: order.row, columnOrder: order.column });
|
|
||||||
handleOpenSortChange();
|
|
||||||
};
|
|
||||||
|
|
||||||
//Кнопка "Очистить", значения по умолчанию
|
|
||||||
const handleClear = () => {
|
|
||||||
setOrder({ row: 0, column: 0 });
|
|
||||||
};
|
|
||||||
|
|
||||||
//Кнопка "Отмена"
|
|
||||||
const handleCancel = () => {
|
|
||||||
handleOpenSortChange();
|
|
||||||
};
|
|
||||||
|
|
||||||
//Генерация содержимого
|
|
||||||
return (
|
|
||||||
<Dialog open onClose={handleOpenSortChange}>
|
|
||||||
<DialogTitle>
|
|
||||||
<Box display="flex" alignItems="center">
|
|
||||||
<Box flexGrow={1} textAlign="center">
|
|
||||||
Сортировка
|
|
||||||
</Box>
|
|
||||||
<Box>
|
|
||||||
<IconButton aria-label="close" onClick={handleCancel}>
|
|
||||||
<Icon>close</Icon>
|
|
||||||
</IconButton>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</DialogTitle>
|
|
||||||
<DialogContent>
|
|
||||||
<Box component="section" p={1}>
|
|
||||||
<FormControl variant="standard" fullWidth>
|
|
||||||
<InputLabel id="row-label">Строки</InputLabel>
|
|
||||||
<Select labelId="row-label" id="row" name="row" value={order.row} label="Строки" onChange={handleOrderChange}>
|
|
||||||
<MenuItem value={0}>Номер</MenuItem>
|
|
||||||
<MenuItem value={1}>Код</MenuItem>
|
|
||||||
<MenuItem value={2}>Мнемокод</MenuItem>
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
</Box>
|
|
||||||
<Box component="section" p={1}>
|
|
||||||
<FormControl variant="standard" fullWidth>
|
|
||||||
<InputLabel id="column-label">Графы</InputLabel>
|
|
||||||
<Select labelId="column-label" id="column" name="column" value={order.column} label="Графы" onChange={handleOrderChange}>
|
|
||||||
<MenuItem value={0}>Номер</MenuItem>
|
|
||||||
<MenuItem value={1}>Код</MenuItem>
|
|
||||||
<MenuItem value={2}>Мнемокод</MenuItem>
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
</Box>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions sx={STYLES.DIALOG_ACTIONS}>
|
|
||||||
<Button variant="text" onClick={handleOk}>
|
|
||||||
ОК
|
|
||||||
</Button>
|
|
||||||
<Button variant="text" onClick={handleClear}>
|
|
||||||
Очистить
|
|
||||||
</Button>
|
|
||||||
<Button variant="text" onClick={handleCancel}>
|
|
||||||
Отмена
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств - Диалог сортировки
|
|
||||||
SortDialog.propTypes = {
|
|
||||||
init: PropTypes.object.isRequired,
|
|
||||||
handleOpenSortChange: PropTypes.func.isRequired,
|
|
||||||
onOrderChange: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
//-----------
|
//-----------
|
||||||
//Тело модуля
|
//Тело модуля
|
||||||
//-----------
|
//-----------
|
||||||
@ -245,8 +145,6 @@ const SectionTab = ({
|
|||||||
section,
|
section,
|
||||||
tabValue,
|
tabValue,
|
||||||
index,
|
index,
|
||||||
order,
|
|
||||||
onOrderChange,
|
|
||||||
containerProps,
|
containerProps,
|
||||||
handleMarkAdd,
|
handleMarkAdd,
|
||||||
handleReload,
|
handleReload,
|
||||||
@ -263,14 +161,6 @@ const SectionTab = ({
|
|||||||
setOpenHelp(!openHelp);
|
setOpenHelp(!openHelp);
|
||||||
};
|
};
|
||||||
|
|
||||||
//Состояние - диалог сортировки
|
|
||||||
const [openSort, setOpenSort] = useState(false);
|
|
||||||
|
|
||||||
//Изменение состояния диалога сортировки
|
|
||||||
const handleOpenSortChange = () => {
|
|
||||||
setOpenSort(!openSort);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Генерация содержимого
|
//Генерация содержимого
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -285,9 +175,6 @@ const SectionTab = ({
|
|||||||
</IconButton>
|
</IconButton>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<IconButton onClick={() => handleOpenSortChange()}>
|
|
||||||
<Icon>sort</Icon>
|
|
||||||
</IconButton>
|
|
||||||
<IconButton onClick={() => handleOpenHelpChange()}>
|
<IconButton onClick={() => handleOpenHelpChange()}>
|
||||||
<Icon>help</Icon>
|
<Icon>help</Icon>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
@ -330,7 +217,6 @@ const SectionTab = ({
|
|||||||
</Box>
|
</Box>
|
||||||
) : null}
|
) : null}
|
||||||
</SectionTabPanel>
|
</SectionTabPanel>
|
||||||
{openSort ? <SortDialog init={order} handleOpenSortChange={handleOpenSortChange} onOrderChange={onOrderChange} /> : null}
|
|
||||||
{openHelp ? <HelpDialog handleOpenHelpChange={handleOpenHelpChange} /> : null}
|
{openHelp ? <HelpDialog handleOpenHelpChange={handleOpenHelpChange} /> : null}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@ -341,8 +227,6 @@ SectionTab.propTypes = {
|
|||||||
section: PropTypes.object.isRequired,
|
section: PropTypes.object.isRequired,
|
||||||
tabValue: PropTypes.number,
|
tabValue: PropTypes.number,
|
||||||
index: PropTypes.number,
|
index: PropTypes.number,
|
||||||
order: PropTypes.object.isRequired,
|
|
||||||
onOrderChange: PropTypes.func.isRequired,
|
|
||||||
containerProps: PropTypes.object,
|
containerProps: PropTypes.object,
|
||||||
handleMarkAdd: PropTypes.func,
|
handleMarkAdd: PropTypes.func,
|
||||||
handleReload: PropTypes.func,
|
handleReload: PropTypes.func,
|
||||||
|
@ -39,7 +39,7 @@ const useWindowResize = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
//Хук для настройки регламентированного отчета
|
//Хук для настройки регламентированного отчета
|
||||||
const useConf = (currentTab, handleSectionChange, order) => {
|
const useConf = (currentTab, handleSectionChange) => {
|
||||||
//Собственное состояние - таблица данных
|
//Собственное состояние - таблица данных
|
||||||
const dataGrid = {
|
const dataGrid = {
|
||||||
rn: 0,
|
rn: 0,
|
||||||
@ -58,7 +58,6 @@ const useConf = (currentTab, handleSectionChange, order) => {
|
|||||||
const [rrpConf, setRrpConf] = useState({
|
const [rrpConf, setRrpConf] = useState({
|
||||||
docLoaded: false,
|
docLoaded: false,
|
||||||
sections: [],
|
sections: [],
|
||||||
orderChanged: false,
|
|
||||||
reload: true
|
reload: true
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -77,116 +76,103 @@ const useConf = (currentTab, handleSectionChange, order) => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
//Загрузка данных разделов регламентированного отчёта
|
//Загрузка данных разделов регламентированного отчёта
|
||||||
const loadData = useCallback(
|
const loadData = useCallback(async () => {
|
||||||
async () => {
|
if (rrpConf.reload) {
|
||||||
if (rrpConf.reload) {
|
//Переменная номера раздела с фокусом
|
||||||
//Переменная номера раздела с фокусом
|
let tabFocus = currentTab ? currentTab : 0;
|
||||||
let tabFocus = currentTab ? currentTab : 0;
|
const data = await executeStored({
|
||||||
const data = await executeStored({
|
stored: "PKG_P8PANELS_RRPCONFED.RRPCONF_GET_SECTIONS",
|
||||||
stored: "PKG_P8PANELS_RRPCONFED.RRPCONF_GET_SECTIONS",
|
args: {
|
||||||
args: {
|
NRN_RRPCONF: Number(getNavigationSearch().NRN)
|
||||||
NRN_RRPCONF: Number(getNavigationSearch().NRN),
|
},
|
||||||
NROW_ORDER: Number(order.rowOrder),
|
respArg: "COUT"
|
||||||
NCOL_ORDER: Number(order.columnOrder)
|
});
|
||||||
},
|
//Флаг первой загрузки данных
|
||||||
respArg: "COUT"
|
let firstLoad = dataGrids.length == 0 ? true : false;
|
||||||
|
//Копирование массива уже загруженных разделов
|
||||||
|
let cloneDGs = dataGrids.slice();
|
||||||
|
//Массив из нескольких разделов и из одного
|
||||||
|
const sections = data.SECTIONS ? (data.SECTIONS.length ? data.SECTIONS : [data.SECTIONS]) : [];
|
||||||
|
//Заполнение очередного раздела по шаблону
|
||||||
|
sections.map(s => {
|
||||||
|
let dg = {};
|
||||||
|
Object.assign(dg, dataGrid, {
|
||||||
|
...s.XDATA.XDATA_GRID,
|
||||||
|
rn: s.NRN,
|
||||||
|
code: s.SCODE,
|
||||||
|
name: s.SNAME,
|
||||||
|
delete_allow: s.NDELETE_ALLOW,
|
||||||
|
dataLoaded: true,
|
||||||
|
columnsDef: [...(s.XDATA.XDATA_GRID.columnsDef || [])],
|
||||||
|
groups: [...(s.XDATA.XDATA_GRID.groups || [])],
|
||||||
|
rows: [...(s.XDATA.XDATA_GRID.rows || [])],
|
||||||
|
reload: false
|
||||||
});
|
});
|
||||||
//Флаг первой загрузки данных
|
//Если раздел имеет составы показателей
|
||||||
let firstLoad = dataGrids.length == 0 ? true : false;
|
if (s.MARK_CNS.MARK_CN) {
|
||||||
//Копирование массива уже загруженных разделов
|
//Обходим строки раздела
|
||||||
let cloneDGs = dataGrids.slice();
|
dg.rows.map(row => {
|
||||||
//Массив из нескольких разделов и из одного
|
//Цикл по ключам строки
|
||||||
const sections = data.SECTIONS ? (data.SECTIONS.length ? data.SECTIONS : [data.SECTIONS]) : [];
|
for (let key in row) {
|
||||||
//Заполнение очередного раздела по шаблону
|
//Если это ключ для группы составов показателей
|
||||||
sections
|
if (key.match(/MARK_CNS_.*/)) {
|
||||||
.sort((a, b) => a.SCODE - b.SCODE)
|
//Считываем рег. номер показателя
|
||||||
.map(s => {
|
let markRn = key.substring(9);
|
||||||
let dg = {};
|
//Переносим из раздела
|
||||||
Object.assign(dg, dataGrid, {
|
row[key] = Array.isArray(s.MARK_CNS.MARK_CN)
|
||||||
...s.XDATA.XDATA_GRID,
|
? [...s.MARK_CNS.MARK_CN].filter(el => el.NPRN === row[`NMARK_RN_${markRn}`])
|
||||||
rn: s.NRN,
|
: s.MARK_CNS.MARK_CN.NPRN === row[`NMARK_RN_${markRn}`]
|
||||||
code: s.SCODE,
|
? [s.MARK_CNS.MARK_CN]
|
||||||
name: s.SNAME,
|
: null;
|
||||||
delete_allow: s.NDELETE_ALLOW,
|
|
||||||
dataLoaded: true,
|
|
||||||
columnsDef: [...(s.XDATA.XDATA_GRID.columnsDef || [])],
|
|
||||||
groups: [...(s.XDATA.XDATA_GRID.groups || [])],
|
|
||||||
rows: [...(s.XDATA.XDATA_GRID.rows || [])],
|
|
||||||
reload: false
|
|
||||||
});
|
|
||||||
//Если раздел имеет составы показателей
|
|
||||||
if (s.MARK_CNS.MARK_CN) {
|
|
||||||
//Обходим строки раздела
|
|
||||||
dg.rows.map(row => {
|
|
||||||
//Цикл по ключам строки
|
|
||||||
for (let key in row) {
|
|
||||||
//Если это ключ для группы составов показателей
|
|
||||||
if (key.match(/MARK_CNS_.*/)) {
|
|
||||||
//Считываем рег. номер показателя
|
|
||||||
let markRn = key.substring(9);
|
|
||||||
//Переносим из раздела
|
|
||||||
row[key] = Array.isArray(s.MARK_CNS.MARK_CN)
|
|
||||||
? [...s.MARK_CNS.MARK_CN].filter(el => el.NPRN === row[`NMARK_RN_${markRn}`])
|
|
||||||
: s.MARK_CNS.MARK_CN.NPRN === row[`NMARK_RN_${markRn}`]
|
|
||||||
? [s.MARK_CNS.MARK_CN]
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
//Ищем загружен ли уже раздел с таким же ид.
|
|
||||||
const dgItem = dataGrids.find(x => x.rn === dg.rn);
|
|
||||||
//Его индекс, если нет соответствия, то -1
|
|
||||||
let index = dataGrids.indexOf(dgItem);
|
|
||||||
//Если было соответствие
|
|
||||||
if (dgItem) {
|
|
||||||
//Если в нём не найдено изменений
|
|
||||||
if (JSON.stringify(dgItem, null, 4) === JSON.stringify(dg, null, 4)) {
|
|
||||||
//То из копированного массива его удаляем
|
|
||||||
cloneDGs.splice(cloneDGs.indexOf(cloneDGs.find(x => x.rn === dgItem.rn)), 1);
|
|
||||||
} else {
|
|
||||||
//Иначе обновляем раздел в массиве
|
|
||||||
dataGrids[index] = dg;
|
|
||||||
//Удаляем из копированного массива
|
|
||||||
cloneDGs.splice(cloneDGs.indexOf(cloneDGs.find(x => x.rn === dg.rn)), 1);
|
|
||||||
//Устанавливаем фокус на обновлённый раздел, если был добавлен
|
|
||||||
tabFocus = rrpConf.orderChanged ? 0 : index;
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
//Если раздел новый, то добавляем его в массив данных
|
|
||||||
dataGrids.push(dg);
|
|
||||||
//И устанавливаем на него фокус, если флаг первой загрузки = false
|
|
||||||
tabFocus = !firstLoad ? dataGrids.length - 1 : 0;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
//Обходим разделы, что остались в копированном массиве (на удаление)
|
}
|
||||||
cloneDGs.map(s => {
|
//Ищем загружен ли уже раздел с таким же ид.
|
||||||
let curIndex = dataGrids.indexOf(dataGrids.find(x => x.rn === s.rn));
|
const dgItem = dataGrids.find(x => x.rn === dg.rn);
|
||||||
//Устаревший раздел удаляем из массива данных
|
//Его индекс, если нет соответствия, то -1
|
||||||
dataGrids.splice(curIndex, 1);
|
let index = dataGrids.indexOf(dgItem);
|
||||||
//Фокус на предшествующий раздел
|
//Если было соответствие
|
||||||
if (curIndex > 0) tabFocus = curIndex - 1;
|
if (dgItem) {
|
||||||
//Иначе фокус на следующий, если был удалён первый раздел
|
//Если в нём не найдено изменений
|
||||||
else tabFocus = curIndex;
|
if (JSON.stringify(dgItem, null, 4) === JSON.stringify(dg, null, 4)) {
|
||||||
});
|
//То из копированного массива его удаляем
|
||||||
setRrpConf(pv => ({
|
cloneDGs.splice(cloneDGs.indexOf(cloneDGs.find(x => x.rn === dgItem.rn)), 1);
|
||||||
...pv,
|
} else {
|
||||||
docLoaded: true,
|
//Иначе обновляем раздел в массиве
|
||||||
orderChanged: false,
|
dataGrids[index] = dg;
|
||||||
reload: false,
|
//Удаляем из копированного массива
|
||||||
sections: dataGrids
|
cloneDGs.splice(cloneDGs.indexOf(cloneDGs.find(x => x.rn === dg.rn)), 1);
|
||||||
}));
|
//Устанавливаем фокус на обновлённый раздел
|
||||||
handleSectionChange(tabFocus);
|
tabFocus = index;
|
||||||
}
|
}
|
||||||
},
|
} else {
|
||||||
|
//Если раздел новый, то добавляем его в массив данных
|
||||||
|
dataGrids.push(dg);
|
||||||
|
//И устанавливаем на него фокус, если флаг первой загрузки = false
|
||||||
|
tabFocus = !firstLoad ? dataGrids.length - 1 : 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//Обходим разделы, что остались в копированном массиве (на удаление)
|
||||||
|
cloneDGs.map(s => {
|
||||||
|
let curIndex = dataGrids.indexOf(dataGrids.find(x => x.rn === s.rn));
|
||||||
|
//Устаревший раздел удаляем из массива данных
|
||||||
|
dataGrids.splice(curIndex, 1);
|
||||||
|
//Фокус на предшествующий раздел
|
||||||
|
if (curIndex > 0) tabFocus = curIndex - 1;
|
||||||
|
//Иначе фокус на следующий, если был удалён первый раздел
|
||||||
|
else tabFocus = curIndex;
|
||||||
|
});
|
||||||
|
setRrpConf(pv => ({
|
||||||
|
...pv,
|
||||||
|
docLoaded: true,
|
||||||
|
reload: false,
|
||||||
|
sections: dataGrids
|
||||||
|
}));
|
||||||
|
handleSectionChange(tabFocus);
|
||||||
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
[rrpConf.reload, rrpConf.docLoaded, dataGrid.reload, dataGrid.docLoaded, executeStored]
|
}, [rrpConf.reload, rrpConf.docLoaded, dataGrid.reload, dataGrid.docLoaded, executeStored]);
|
||||||
);
|
|
||||||
|
|
||||||
//При изменении сортировок
|
|
||||||
useEffect(() => {
|
|
||||||
setRrpConf(pv => ({ ...pv, orderChanged: true, reload: true }));
|
|
||||||
}, [order]);
|
|
||||||
|
|
||||||
//При необходимости обновить данные таблицы
|
//При необходимости обновить данные таблицы
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -44,14 +44,8 @@ const RrpConfEditor = () => {
|
|||||||
//Состояние вкладки
|
//Состояние вкладки
|
||||||
const [tabValue, handleSectionChange] = useTab("");
|
const [tabValue, handleSectionChange] = useTab("");
|
||||||
|
|
||||||
//Состояние сортировки строк и граф
|
|
||||||
const [order, setOrder] = useState({ rowOrder: 0, columnOrder: 0 });
|
|
||||||
|
|
||||||
//Изменение состояния сортировки строк и граф
|
|
||||||
const handleOrder = newOrder => setOrder(newOrder);
|
|
||||||
|
|
||||||
//Состояние настройки
|
//Состояние настройки
|
||||||
const [rrpConf, handleReload] = useConf(tabValue, handleSectionChange, order);
|
const [rrpConf, handleReload] = useConf(tabValue, handleSectionChange);
|
||||||
|
|
||||||
//Функции открытия разделов
|
//Функции открытия разделов
|
||||||
const [handleMarkOpen, handleMarkCnOpen, handleMarkCnInsert] = useRecOpen(handleReload);
|
const [handleMarkOpen, handleMarkCnOpen, handleMarkCnInsert] = useRecOpen(handleReload);
|
||||||
@ -142,8 +136,6 @@ const RrpConfEditor = () => {
|
|||||||
section={s}
|
section={s}
|
||||||
tabValue={tabValue}
|
tabValue={tabValue}
|
||||||
index={i}
|
index={i}
|
||||||
order={order}
|
|
||||||
onOrderChange={handleOrder}
|
|
||||||
containerProps={{ height, pxOuterMenuH, pxPanelHeaderH, pxTabsH }}
|
containerProps={{ height, pxOuterMenuH, pxPanelHeaderH, pxTabsH }}
|
||||||
handleReload={handleReload}
|
handleReload={handleReload}
|
||||||
handleMarkOpen={handleMarkOpen}
|
handleMarkOpen={handleMarkOpen}
|
||||||
|
@ -1,156 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - Примеры для разработчиков
|
|
||||||
Пример: Индикатор "P8PIndicator"
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React, { useContext } from "react"; //Классы React
|
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
|
||||||
import { Typography, Stack, Divider } from "@mui/material"; //Интерфейсные элементы
|
|
||||||
import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений
|
|
||||||
import { P8P_INDICATOR_VARIANT, P8P_INDICATOR_STATE, P8PIndicator } from "../../components/p8p_indicator"; //Индикатор
|
|
||||||
|
|
||||||
//---------
|
|
||||||
//Константы
|
|
||||||
//---------
|
|
||||||
|
|
||||||
//Стили
|
|
||||||
const STYLES = {
|
|
||||||
CONTAINER: { textAlign: "center", paddingTop: "20px" },
|
|
||||||
TITLE: { paddingBottom: "15px" },
|
|
||||||
DIVIDER: { margin: "15px" }
|
|
||||||
};
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Пример: Индикатор "P8PIndicator"
|
|
||||||
const Indicator = ({ title }) => {
|
|
||||||
//Подключение к контексту сообщений
|
|
||||||
const { showMsgInfo } = useContext(MessagingСtx);
|
|
||||||
|
|
||||||
//Генерация содержимого
|
|
||||||
return (
|
|
||||||
<div style={STYLES.CONTAINER}>
|
|
||||||
<Typography sx={STYLES.TITLE} variant={"h6"}>
|
|
||||||
{title}
|
|
||||||
</Typography>
|
|
||||||
<Divider>Иконка</Divider>
|
|
||||||
<Stack direction={"row"} spacing={2} p={5}>
|
|
||||||
{/* Индикатор (без иконки) */}
|
|
||||||
<P8PIndicator caption={"Без иконки"} value={10} />
|
|
||||||
{/* Индикатор (с иконкой 1) */}
|
|
||||||
<P8PIndicator caption={"С иконкой - Back Hand"} value={20} icon={"back_hand"} />
|
|
||||||
{/* Индикатор (с иконкой 2) */}
|
|
||||||
<P8PIndicator caption={"С иконкой - Scoreboard"} value={30} icon={"scoreboard"} />
|
|
||||||
</Stack>
|
|
||||||
<Divider>Состояние</Divider>
|
|
||||||
<Stack direction={"row"} spacing={2} p={5}>
|
|
||||||
{/* Индикатор (нейтральный) */}
|
|
||||||
<P8PIndicator caption={"Нейтральное состояние"} value={10} icon={"sentiment_neutral"} />
|
|
||||||
{/* Индикатор (позитивный) */}
|
|
||||||
<P8PIndicator caption={"Позитивное состояние"} value={20} state={P8P_INDICATOR_STATE.OK} icon={"check_circle"} />
|
|
||||||
{/* Индикатор (пограничный) */}
|
|
||||||
<P8PIndicator caption={"Пограничное состояние"} value={30} state={P8P_INDICATOR_STATE.WARN} icon={"warning"} />
|
|
||||||
{/* Индикатор (негативный) */}
|
|
||||||
<P8PIndicator caption={"Негативное состояния"} value={40} state={P8P_INDICATOR_STATE.ERR} icon={"dangerous"} />
|
|
||||||
</Stack>
|
|
||||||
<Divider>Скругление</Divider>
|
|
||||||
<Stack direction={"row"} spacing={2} p={5}>
|
|
||||||
{/* Индикатор (скругленный) */}
|
|
||||||
<P8PIndicator caption={"Скругленный"} />
|
|
||||||
{/* Индикатор (квадратный) */}
|
|
||||||
<P8PIndicator caption={"Квадрадтный"} square={true} />
|
|
||||||
</Stack>
|
|
||||||
<Divider>Парение</Divider>
|
|
||||||
<Stack direction={"row"} spacing={2} p={5}>
|
|
||||||
{/* Индикатор (парение - 0) */}
|
|
||||||
<P8PIndicator caption={"Парение"} value={0} state={P8P_INDICATOR_STATE.OK} elevation={0} />
|
|
||||||
{/* Индикатор (парение - 3) */}
|
|
||||||
<P8PIndicator caption={"Парение (по умолчанию)"} value={3} state={P8P_INDICATOR_STATE.WARN} elevation={3} />
|
|
||||||
{/* Индикатор (парение - 6) */}
|
|
||||||
<P8PIndicator caption={"Парение"} value={6} state={P8P_INDICATOR_STATE.OK} elevation={6} />
|
|
||||||
{/* Индикатор (парение - 12) */}
|
|
||||||
<P8PIndicator caption={"Парение"} value={12} state={P8P_INDICATOR_STATE.OK} elevation={12} />
|
|
||||||
{/* Индикатор (парение - 18) */}
|
|
||||||
<P8PIndicator caption={"Парение"} value={18} state={P8P_INDICATOR_STATE.OK} elevation={18} />
|
|
||||||
</Stack>
|
|
||||||
<Divider>Исполнение</Divider>
|
|
||||||
<Stack direction={"row"} spacing={2} p={5}>
|
|
||||||
{/* Индикатор (парение) */}
|
|
||||||
<P8PIndicator caption={"Парящий (по умолчанию)"} value={123} />
|
|
||||||
{/* Индикатор (рамка) */}
|
|
||||||
<P8PIndicator caption={"Рамка"} value={321} variant={P8P_INDICATOR_VARIANT.OUTLINED} />
|
|
||||||
</Stack>
|
|
||||||
<Divider>Подсказка</Divider>
|
|
||||||
<Stack direction={"row"} spacing={2} p={5}>
|
|
||||||
{/* Индикатор (подсказка без форматирования) */}
|
|
||||||
<P8PIndicator
|
|
||||||
caption={"Подсказка (без форматирования)"}
|
|
||||||
value={42}
|
|
||||||
icon={"desktop_windows"}
|
|
||||||
hint={"Ответ на главный вопрос жизни, вселенной и всего такого..."}
|
|
||||||
/>
|
|
||||||
{/* Индикатор (подсказка с форматирование) */}
|
|
||||||
<P8PIndicator
|
|
||||||
caption={"Подсказка (с форматированием)"}
|
|
||||||
value={3.14}
|
|
||||||
icon={"radio_button_unchecked"}
|
|
||||||
hint={`Математическая <b>постоянная</b>, равная <b style='color:red'>отношению</b> <b style='color:green'>длины окружности</b>
|
|
||||||
к её <b style='color:blue'>диаметру</b>:
|
|
||||||
<p style='text-align: center'>π = <span style='color:green'>L</span>/<span style='color:blue'>d</span></p>`}
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
<Divider>Активность</Divider>
|
|
||||||
<Stack direction={"row"} spacing={2} p={5}>
|
|
||||||
{[P8P_INDICATOR_STATE.UNDEFINED, P8P_INDICATOR_STATE.OK, P8P_INDICATOR_STATE.WARN, P8P_INDICATOR_STATE.ERR].map(
|
|
||||||
(indicatorState, i) => (
|
|
||||||
<P8PIndicator
|
|
||||||
key={i}
|
|
||||||
caption={`Нажми на меня #${i + 1}`}
|
|
||||||
value={i + 1}
|
|
||||||
state={indicatorState}
|
|
||||||
icon={"chat"}
|
|
||||||
onClick={() => showMsgInfo(`Нажатие на индикатор #${i + 1}`)}
|
|
||||||
hint={`Подсказка индикатора #${i + 1}`}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
<Divider>Пользовательские цвета</Divider>
|
|
||||||
<Stack direction={"row"} spacing={2} p={5}>
|
|
||||||
{[
|
|
||||||
["yellow", "black"],
|
|
||||||
["darkred", "yellow"],
|
|
||||||
["orange", "darkblue"],
|
|
||||||
["magenta", "darkmagenta"]
|
|
||||||
].map((userColor, i) => (
|
|
||||||
<P8PIndicator
|
|
||||||
key={i}
|
|
||||||
caption={`Текст: ${userColor[0]}, Заливка: ${userColor[1]}`}
|
|
||||||
value={i + 1}
|
|
||||||
state={P8P_INDICATOR_STATE.WARN}
|
|
||||||
icon={"palette"}
|
|
||||||
color={userColor[0]}
|
|
||||||
backgroundColor={userColor[1]}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</Stack>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств - Пример: Индикатор "P8PIndicator"
|
|
||||||
Indicator.propTypes = {
|
|
||||||
title: PropTypes.string.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export { Indicator };
|
|
@ -46,20 +46,6 @@ const Messages = ({ title }) => {
|
|||||||
Ошибка
|
Ошибка
|
||||||
</Button>
|
</Button>
|
||||||
<Divider sx={STYLES.DIVIDER} />
|
<Divider sx={STYLES.DIVIDER} />
|
||||||
{/* Сообщение об ошибке (диалог с подробностями) */}
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
onClick={() =>
|
|
||||||
showMsgErr(
|
|
||||||
"Что-то пошло не так :( ...но мы точно знаем что ;)",
|
|
||||||
null,
|
|
||||||
"Здесь подробная информация об ошибке (стек вызова СУБД, например)"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Ошибка с подробностями
|
|
||||||
</Button>
|
|
||||||
<Divider sx={STYLES.DIVIDER} />
|
|
||||||
{/* Предупреждение (диалог) */}
|
{/* Предупреждение (диалог) */}
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
|
@ -19,7 +19,6 @@ import { Chart } from "./chart"; //Пример: Графики "P8PChart"
|
|||||||
import { Gantt } from "./gantt"; //Пример: Диаграмма Ганта "P8PGantt"
|
import { Gantt } from "./gantt"; //Пример: Диаграмма Ганта "P8PGantt"
|
||||||
import { Svg } from "./svg"; //Пример: Интерактивные изображения "P8PSVG"
|
import { Svg } from "./svg"; //Пример: Интерактивные изображения "P8PSVG"
|
||||||
import { Cyclogram } from "./cyclogram"; //Пример: Циклограмма "P8PCyclogram"
|
import { Cyclogram } from "./cyclogram"; //Пример: Циклограмма "P8PCyclogram"
|
||||||
import { Indicator } from "./indicator"; //Пример: Индикатор "P8PIndicator"
|
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
//Константы
|
//Константы
|
||||||
@ -35,8 +34,7 @@ const MODES = {
|
|||||||
CHART: { name: "CHART", caption: 'Графики "P8PChart"', component: Chart },
|
CHART: { name: "CHART", caption: 'Графики "P8PChart"', component: Chart },
|
||||||
GANTT: { name: "GANTT", caption: 'Диаграмма Ганта "P8PGantt"', component: Gantt },
|
GANTT: { name: "GANTT", caption: 'Диаграмма Ганта "P8PGantt"', component: Gantt },
|
||||||
SVG: { name: "SVG", caption: 'Интерактивные изображения "P8PSVG"', component: Svg },
|
SVG: { name: "SVG", caption: 'Интерактивные изображения "P8PSVG"', component: Svg },
|
||||||
CYCLOGRAM: { name: "CYCLOGRAM", caption: 'Циклограмма "P8PCyclogram"', component: Cyclogram },
|
CYCLOGRAM: { name: "CYCLOGRAM", caption: 'Циклограмма "P8PCyclogram"', component: Cyclogram }
|
||||||
INDICATOR: { name: "INDICATOR", caption: 'Индикатор "P8PIndicator"', component: Indicator }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//Стили
|
//Стили
|
||||||
|
@ -1,127 +0,0 @@
|
|||||||
create or replace package PKG_P8PANELS_EDITOR as
|
|
||||||
|
|
||||||
/* Список аргументов пользовательской процедуры */
|
|
||||||
procedure USERPROCS_DESC
|
|
||||||
(
|
|
||||||
SCODE in varchar2, -- Мнемокод пользовательской процедуры
|
|
||||||
COUT out clob -- Сериализованный список аргументов
|
|
||||||
);
|
|
||||||
|
|
||||||
end PKG_P8PANELS_EDITOR;
|
|
||||||
/
|
|
||||||
create or replace package body PKG_P8PANELS_EDITOR as
|
|
||||||
|
|
||||||
/* Описание пользовательской процедуры */
|
|
||||||
procedure USERPROCS_DESC
|
|
||||||
(
|
|
||||||
SCODE in varchar2, -- Мнемокод пользовательской процедуры
|
|
||||||
COUT out clob -- Сериализованный список аргументов
|
|
||||||
)
|
|
||||||
is
|
|
||||||
SRESP_ARG PKG_STD.TSTRING; -- Имя выходного визуализируемого параметра
|
|
||||||
begin
|
|
||||||
/* Обращаемся к процедуре */
|
|
||||||
for C in (select T.RN NRN,
|
|
||||||
T.PROCNAME SPROC_NAME,
|
|
||||||
T.PROCTYPE NPROC_TYPE
|
|
||||||
from USERPROCS T
|
|
||||||
where T.CODE = SCODE)
|
|
||||||
loop
|
|
||||||
/* Проверим возможность использования ПП в качестве источника данных */
|
|
||||||
if (C.NPROC_TYPE <> 0) then
|
|
||||||
P_EXCEPTION(0,
|
|
||||||
'Пользовательская процедура "%s" не может быть использована в качестве источника данных: должна иметь тип "Хранимая процедура".',
|
|
||||||
SCODE);
|
|
||||||
end if;
|
|
||||||
if (C.SPROC_NAME is null) then
|
|
||||||
P_EXCEPTION(0,
|
|
||||||
'Пользовательская процедура "%s" не может быть использована в качестве источника данных: не указана хранимая процедура.',
|
|
||||||
SCODE);
|
|
||||||
end if;
|
|
||||||
/* Начинаем формирование XML */
|
|
||||||
PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_);
|
|
||||||
/* Открываем корень */
|
|
||||||
PKG_XFAST.DOWN_NODE(SNAME => 'XDATA');
|
|
||||||
/* Открываем описание процедуры */
|
|
||||||
PKG_XFAST.DOWN_NODE(SNAME => 'XUSERPROC');
|
|
||||||
/* Обходим параметры */
|
|
||||||
for P in (select T.PARAMTYPE NTYPE,
|
|
||||||
T.PARAMNAME SNAME,
|
|
||||||
T.NAME SCAPTION,
|
|
||||||
T.DATATYPE NDATA_TYPE,
|
|
||||||
case T.DATATYPE
|
|
||||||
when 0 then
|
|
||||||
'STR'
|
|
||||||
when 1 then
|
|
||||||
'NUMB'
|
|
||||||
when 2 then
|
|
||||||
'DATE'
|
|
||||||
else
|
|
||||||
null
|
|
||||||
end SDATA_TYPE,
|
|
||||||
T.MANDATORY NREQ,
|
|
||||||
T.VISUALIZE NVISUALIZE
|
|
||||||
from USERPROCSPARAMS T
|
|
||||||
where T.PRN = C.NRN
|
|
||||||
order by T.POSITION)
|
|
||||||
loop
|
|
||||||
/* В результирующий список забираем только входные поддерживаемого типа */
|
|
||||||
if ((P.NTYPE = 0) and (P.SDATA_TYPE is not null)) then
|
|
||||||
/* Открываем описание аргумента */
|
|
||||||
PKG_XFAST.DOWN_NODE(SNAME => 'arguments');
|
|
||||||
/* Описываем аргумент */
|
|
||||||
PKG_XFAST.ATTR(SNAME => 'name', SVALUE => P.SNAME);
|
|
||||||
PKG_XFAST.ATTR(SNAME => 'caption', SVALUE => P.SCAPTION);
|
|
||||||
PKG_XFAST.ATTR(SNAME => 'dataType', SVALUE => P.SDATA_TYPE);
|
|
||||||
PKG_XFAST.ATTR(SNAME => 'req',
|
|
||||||
BVALUE => case P.NREQ
|
|
||||||
when 1 then
|
|
||||||
true
|
|
||||||
else
|
|
||||||
false
|
|
||||||
end);
|
|
||||||
/* Закрываем описание аргумента */
|
|
||||||
PKG_XFAST.UP();
|
|
||||||
end if;
|
|
||||||
/* Если встретился визуализируемый параметр типа CLOB */
|
|
||||||
if ((P.NVISUALIZE = 1) and (P.NDATA_TYPE = 4)) then
|
|
||||||
if (SRESP_ARG is null) then
|
|
||||||
SRESP_ARG := P.SNAME;
|
|
||||||
else
|
|
||||||
/* Это уже второй такой - ошибка */
|
|
||||||
P_EXCEPTION(0,
|
|
||||||
'Пользовательская процедура "%s" не может быть использована в качестве источника данных: имеет более одного выходного параметра типа "Текстовые данные" с признаком "Визуализировать после выполнения".',
|
|
||||||
SCODE);
|
|
||||||
end if;
|
|
||||||
end if;
|
|
||||||
end loop;
|
|
||||||
/* Сформируем описание хранимой процедуры */
|
|
||||||
PKG_XFAST.DOWN_NODE(SNAME => 'stored');
|
|
||||||
PKG_XFAST.ATTR(SNAME => 'name', SVALUE => C.SPROC_NAME);
|
|
||||||
PKG_XFAST.ATTR(SNAME => 'respArg', SVALUE => SRESP_ARG);
|
|
||||||
PKG_XFAST.UP();
|
|
||||||
/* Закрываем описание процедуры */
|
|
||||||
PKG_XFAST.UP();
|
|
||||||
/* Закрываем описание корня */
|
|
||||||
PKG_XFAST.UP();
|
|
||||||
/* Сериализуем */
|
|
||||||
COUT := PKG_XFAST.SERIALIZE_TO_CLOB();
|
|
||||||
/* Завершаем формирование XML */
|
|
||||||
PKG_XFAST.EPILOGUE();
|
|
||||||
end loop;
|
|
||||||
/* Если ничего не нашли */
|
|
||||||
if (COUT is null) then
|
|
||||||
P_EXCEPTION(0,
|
|
||||||
'Пользовательская процедура "%s" не определена.',
|
|
||||||
COALESCE(SCODE, '<НЕ УКАЗАНА>'));
|
|
||||||
end if;
|
|
||||||
/* Проверим возможность использования ПП в качестве источника данных - должен быть выходной визуализируемый параметр типа CLOB */
|
|
||||||
if (SRESP_ARG is null) then
|
|
||||||
P_EXCEPTION(0,
|
|
||||||
'Пользовательская процедура "%s" не может быть использована в качестве источника данных: должна иметь выходной параметр типа "Текстовые данные" с признаком "Визуализировать после выполнения".',
|
|
||||||
SCODE);
|
|
||||||
end if;
|
|
||||||
end USERPROCS_DESC;
|
|
||||||
|
|
||||||
end PKG_P8PANELS_EDITOR;
|
|
||||||
/
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -3,25 +3,23 @@ create or replace package PKG_P8PANELS_RRPCONFED as
|
|||||||
/* Получение разделов регламентированного отчёта */
|
/* Получение разделов регламентированного отчёта */
|
||||||
procedure RRPCONF_GET_SECTIONS
|
procedure RRPCONF_GET_SECTIONS
|
||||||
(
|
(
|
||||||
NRN_RRPCONF in number, -- Рег. номер настройки форм регламентированного отчёта
|
NRN_RRPCONF in number, -- Ид. нстройки форм регламентированного отчёта
|
||||||
NROW_ORDER in number := 0, -- Порядок сортировки строк
|
COUT out clob -- Список разделов
|
||||||
NCOL_ORDER in number := 0, -- Порядок сортировки граф
|
|
||||||
COUT out clob -- Список разделов
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Добавление раздела регламентированного отчёта */
|
/* Добавление раздела регламентированного отчёта */
|
||||||
procedure RRPCONFSCTN_INSERT
|
procedure RRPCONFSCTN_INSERT
|
||||||
(
|
(
|
||||||
NPRN in number, -- Рег. номер настройки форм регламентированного отчёта
|
NPRN in number, -- Ид. настройки форм регламентированного отчёта
|
||||||
SCODE in varchar2, -- Мнемокод
|
SCODE in varchar2, -- Мнемокод
|
||||||
SNAME in varchar2, -- Наименование
|
SNAME in varchar2, -- Наименование
|
||||||
NRN out number -- Рег. номер созданной записи
|
NRN out number -- Ид. созданной записи
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Исправление раздела регламентированного отчёта */
|
/* Исправление раздела регламентированного отчёта */
|
||||||
procedure RRPCONFSCTN_UPDATE
|
procedure RRPCONFSCTN_UPDATE
|
||||||
(
|
(
|
||||||
NRN in number, -- Рег. номер раздела
|
NRN in number, -- Ид. раздела
|
||||||
SCODE in varchar2, -- Мнемокод раздела
|
SCODE in varchar2, -- Мнемокод раздела
|
||||||
SNAME in varchar2 -- Наименование раздела
|
SNAME in varchar2 -- Наименование раздела
|
||||||
);
|
);
|
||||||
@ -29,16 +27,16 @@ create or replace package PKG_P8PANELS_RRPCONFED as
|
|||||||
/* Удаление раздела регламентированного отчёта */
|
/* Удаление раздела регламентированного отчёта */
|
||||||
procedure RRPCONFSCTN_DELETE
|
procedure RRPCONFSCTN_DELETE
|
||||||
(
|
(
|
||||||
NRN in number -- Рег. номер раздела
|
NRN in number -- Ид. раздела
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Получение кодов настройки, раздела и показателя раздела по ид. показателя раздела */
|
/* Получение кодов настройки, раздела и показателя раздела по ид. показателя раздела */
|
||||||
procedure RRPCONFSCTNMRK_GET_CODES
|
procedure RRPCONFSCTNMRK_GET_CODES
|
||||||
(
|
(
|
||||||
NRN in number, -- Рег. номер показателя раздела
|
NRN in number, -- Ид. показателя раздела
|
||||||
SRRPCONF out varchar2, -- Мнемокод настройки формы регламентированного отчёта
|
SRRPCONF out varchar2, -- Код настройки формы регламентированного отчёта
|
||||||
SRRPCONFSCTN out varchar2, -- Мнемокод раздела
|
SRRPCONFSCTN out varchar2, -- Код раздела
|
||||||
SRRPCONFSCTNMRK out varchar2 -- Мнемокод показателя раздела
|
SRRPCONFSCTNMRK out varchar2 -- Код показателя раздела
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Формирование кода и наименования показателя раздела регламентированного отчёта */
|
/* Формирование кода и наименования показателя раздела регламентированного отчёта */
|
||||||
@ -61,25 +59,25 @@ create or replace package PKG_P8PANELS_RRPCONFED as
|
|||||||
/* Добавление показателя раздела регламентированного отчёта */
|
/* Добавление показателя раздела регламентированного отчёта */
|
||||||
procedure RRPCONFSCTNMRK_INSERT
|
procedure RRPCONFSCTNMRK_INSERT
|
||||||
(
|
(
|
||||||
NPRN in number, -- Рег. номер раздела
|
NPRN in number, -- Ид. раздела
|
||||||
SCODE in varchar2, -- Мнемокод показателя раздела
|
SCODE in varchar2, -- Мнемокод показателя раздела
|
||||||
SNAME in varchar2, -- Наименование показателя раздела
|
SNAME in varchar2, -- Наименование показателя раздела
|
||||||
NRRPROW in number, -- Рег. номер строки
|
NRRPROW in number, -- Рег. номер строки
|
||||||
NRRPCOLUMN in number, -- Рег. номер графы
|
NRRPCOLUMN in number, -- Рег. номер графы
|
||||||
NRN out number -- Рег. номер созданной записи
|
NRN out number -- Ид. созданной записи
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Исправление показателя раздела регламентированного отчёта */
|
/* Исправление показателя раздела регламентированного отчёта */
|
||||||
procedure RRPCONFSCTNMRK_UPDATE
|
procedure RRPCONFSCTNMRK_UPDATE
|
||||||
(
|
(
|
||||||
NRN in number, -- Рег. номер показателя раздела
|
NRN in number, -- Ид. показателя раздела
|
||||||
SNAME in varchar2 -- Новое наименование
|
SNAME in varchar2 -- Новое наименование
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Удаление показателя раздела регламентированного отчёта */
|
/* Удаление показателя раздела регламентированного отчёта */
|
||||||
procedure RRPCONFSCTNMRK_DELETE
|
procedure RRPCONFSCTNMRK_DELETE
|
||||||
(
|
(
|
||||||
NRN in number -- Рег. номер показателя раздела
|
NRN in number -- Ид. показателя раздела
|
||||||
);
|
);
|
||||||
|
|
||||||
end PKG_P8PANELS_RRPCONFED;
|
end PKG_P8PANELS_RRPCONFED;
|
||||||
@ -780,9 +778,7 @@ create or replace package body PKG_P8PANELS_RRPCONFED as
|
|||||||
/* Получение разделов регламентированного отчёта */
|
/* Получение разделов регламентированного отчёта */
|
||||||
procedure RRPCONF_GET_SECTIONS
|
procedure RRPCONF_GET_SECTIONS
|
||||||
(
|
(
|
||||||
NRN_RRPCONF in number, -- Рег. номер настройки форм регламентированного отчёта
|
NRN_RRPCONF in number, -- Ид. нстройки форм регламентированного отчёта
|
||||||
NROW_ORDER in number := 0, -- Порядок сортировки строк
|
|
||||||
NCOL_ORDER in number := 0, -- Порядок сортировки граф
|
|
||||||
COUT out clob -- Список разделов
|
COUT out clob -- Список разделов
|
||||||
)
|
)
|
||||||
is
|
is
|
||||||
@ -798,140 +794,58 @@ create or replace package body PKG_P8PANELS_RRPCONFED as
|
|||||||
SSECTION_CODE PKG_STD.TSTRING; -- Мнемокод раздела настройки
|
SSECTION_CODE PKG_STD.TSTRING; -- Мнемокод раздела настройки
|
||||||
SSECTION_NAME PKG_STD.TSTRING; -- Наименование раздела настройки
|
SSECTION_NAME PKG_STD.TSTRING; -- Наименование раздела настройки
|
||||||
CSECTION_CLOB clob; -- Данные по разделу настройки
|
CSECTION_CLOB clob; -- Данные по разделу настройки
|
||||||
CSQL_ROWS clob; -- Запрос по строкам
|
|
||||||
SORDERS_R PKG_STD.TSTRING; -- Сортировка строк
|
|
||||||
ICURSOR_R integer; -- Курсор для исполнения запроса по строкам
|
|
||||||
NROW_RN PKG_STD.TREF; -- Рег. номер строки
|
|
||||||
NROW_POS PKG_STD.TNUMBER := 0; -- Позиция строки
|
|
||||||
SROW_CODE PKG_STD.TSTRING; -- Код строки
|
|
||||||
SROW_NAME PKG_STD.TSTRING; -- Наименование строки
|
|
||||||
CSQL_COLS clob; -- Запрос по графам
|
|
||||||
SORDERS_C PKG_STD.TSTRING; -- Сортировка граф
|
|
||||||
ICURSOR_C integer; -- Курсор для исполнения запроса по графам
|
|
||||||
NCOL_RN PKG_STD.TREF; -- Рег. номер графы
|
|
||||||
SCOL_CODE PKG_STD.TSTRING; -- Код графы
|
|
||||||
SCOL_NAME PKG_STD.TSTRING; -- Наименование графы
|
|
||||||
|
|
||||||
/* Формирование текстового представления сортировки */
|
|
||||||
function ORDER_TEXT_GET
|
|
||||||
(
|
|
||||||
NORDER in number, -- Порядок сортировки
|
|
||||||
SALIAS in varchar2, -- Алиас таблицы
|
|
||||||
NTYPE in number -- Тип таблицы (0 - графы, 1 - строки)
|
|
||||||
) return varchar2 -- Текстовое представление сортировки
|
|
||||||
is
|
|
||||||
SRESULT PKG_STD.TSTRING; -- Текстовое представление сортировки
|
|
||||||
STABLE_COLUMN_CODE PKG_STD.TSTRING; -- Наименование колонки таблицы, содержащую код
|
|
||||||
begin
|
|
||||||
/* Определяем наименование колонки, содержающую код */
|
|
||||||
if (NTYPE = 0) then
|
|
||||||
/* Код графы */
|
|
||||||
STABLE_COLUMN_CODE := 'COLUMN_CODE';
|
|
||||||
else
|
|
||||||
/* Код графы */
|
|
||||||
STABLE_COLUMN_CODE := 'ROW_CODE';
|
|
||||||
end if;
|
|
||||||
/* Формируем представление сортировки */
|
|
||||||
case NORDER
|
|
||||||
/* Код - номер сортировки - мнемокод */
|
|
||||||
when 1 then
|
|
||||||
SRESULT := PKG_SQL_BUILD.LPAD_() || '(' || SALIAS || '.' || STABLE_COLUMN_CODE || ', 40, ''0''),' || SALIAS ||
|
|
||||||
'.SORT_NUMB,' || PKG_SQL_BUILD.LPAD_() || '(' || SALIAS || '.CODE, 20, ''0'')';
|
|
||||||
/* Мнемокод - номер сортировки - код */
|
|
||||||
when 2 then
|
|
||||||
SRESULT := PKG_SQL_BUILD.LPAD_() || '(' || SALIAS || '.CODE, 20, ''0''),' || SALIAS || '.SORT_NUMB,' ||
|
|
||||||
PKG_SQL_BUILD.LPAD_() || '(' || SALIAS || '.' || STABLE_COLUMN_CODE || ', 40, ''0'')';
|
|
||||||
/* Номер сортировки - код - мнемокод */
|
|
||||||
else
|
|
||||||
SRESULT := SALIAS || '.SORT_NUMB,' || PKG_SQL_BUILD.LPAD_() || '(' || SALIAS || '.' || STABLE_COLUMN_CODE ||
|
|
||||||
', 40, ''0''),' || PKG_SQL_BUILD.LPAD_() || '(' || SALIAS || '.CODE, 20, ''0'')';
|
|
||||||
end case;
|
|
||||||
/* Возвращаем результат */
|
|
||||||
return SRESULT;
|
|
||||||
end ORDER_TEXT_GET;
|
|
||||||
|
|
||||||
/* Инициализация колонок граф показателей */
|
/* Инициализация колонок граф показателей */
|
||||||
procedure MARKS_COLUMNS_INIT
|
procedure MARKS_COLUMNS_INIT
|
||||||
(
|
(
|
||||||
RDG in out nocopy PKG_P8PANELS_VISUAL.TDG, -- Описание таблицы
|
RDG in out nocopy PKG_P8PANELS_VISUAL.TDG, -- Описание таблицы
|
||||||
NRRPCONFSCTN in number, -- Рег. номер раздела
|
NRRPCONFSCTN in number -- Рег. номер раздела
|
||||||
SORDER in varchar2 -- Сортировка
|
|
||||||
)
|
)
|
||||||
is
|
is
|
||||||
CSQL clob; -- Запрос по графам показателей раздела
|
|
||||||
ICURSOR integer; -- Курсор для исполнения запроса по графам показателей раздела
|
|
||||||
SCOL_CODE PKG_STD.TSTRING; -- Мнемокод графы
|
|
||||||
SCOL_NAME PKG_STD.TSTRING; -- Наименование графы
|
|
||||||
begin
|
begin
|
||||||
/* Добавляем подсказку совместимости */
|
/* Цикл по графам показателей раздела */
|
||||||
CSQL := null;
|
for REC in (select C.CODE,
|
||||||
CSQL := PKG_SQL_BUILD.COMPATIBLE(SSQL => CSQL);
|
C.NAME
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => 'select C.CODE,');
|
from RRPCONFSCTNMRK T,
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' C.NAME');
|
RRPCOLUMN C
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' from RRPCONFSCTNMRK T,');
|
where T.PRN = NRRPCONFSCTN
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' RRPCOLUMN C');
|
and T.RRPCOLUMN = C.RN
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' where T.PRN = ' || NRRPCONFSCTN);
|
group by C.RN,
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and T.RRPCOLUMN = C.RN');
|
C.CODE,
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' group by C.RN,');
|
C.NAME
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' C.CODE,');
|
order by C.CODE)
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' C.NAME,');
|
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' C.COLUMN_CODE,');
|
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' C.SORT_NUMB');
|
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' order by ' || SORDER);
|
|
||||||
/* Разбираем его */
|
|
||||||
ICURSOR := PKG_SQL_DML.OPEN_CURSOR(SWHAT => 'SELECT');
|
|
||||||
PKG_SQL_DML.PARSE(ICURSOR => ICURSOR, SQUERY => CSQL);
|
|
||||||
/* Описываем структуру записи курсора */
|
|
||||||
PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 1);
|
|
||||||
PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 2);
|
|
||||||
/* Делаем выборку */
|
|
||||||
if (PKG_SQL_DML.EXECUTE(ICURSOR => ICURSOR) = 0) then
|
|
||||||
null;
|
|
||||||
end if;
|
|
||||||
/* Обходим графы показателей раздела */
|
|
||||||
while (PKG_SQL_DML.FETCH_ROWS(ICURSOR => ICURSOR) > 0)
|
|
||||||
loop
|
loop
|
||||||
/* Считываем рег. номер строки */
|
|
||||||
PKG_SQL_DML.COLUMN_VALUE_STR(ICURSOR => ICURSOR, IPOSITION => 1, SVALUE => SCOL_CODE);
|
|
||||||
/* Считываем код строки */
|
|
||||||
PKG_SQL_DML.COLUMN_VALUE_STR(ICURSOR => ICURSOR, IPOSITION => 2, SVALUE => SCOL_NAME);
|
|
||||||
/* Наименование графы */
|
/* Наименование графы */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'SCOL_' || SCOL_CODE,
|
SNAME => 'SCOL_' || REC.CODE,
|
||||||
SCAPTION => SCOL_NAME,
|
SCAPTION => REC.NAME,
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
||||||
NWIDTH => 200);
|
NWIDTH => 200);
|
||||||
/* Рег. номер графы */
|
/* Рег. номер графы */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'NCOL_RN_' || SCOL_CODE,
|
SNAME => 'NCOL_RN_' || REC.CODE,
|
||||||
SCAPTION => SCOL_NAME || ' рег. номер',
|
SCAPTION => REC.NAME || ' рег. номер',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
|
||||||
BVISIBLE => false);
|
BVISIBLE => false);
|
||||||
/* Рег. номер показателя */
|
/* Рег. номер показателя */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'NMARK_RN_' || SCOL_CODE,
|
SNAME => 'NMARK_RN_' || REC.CODE,
|
||||||
SCAPTION => SCOL_NAME || ' рег. номер показателя',
|
SCAPTION => REC.NAME || ' рег. номер показателя',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
|
||||||
BVISIBLE => false);
|
BVISIBLE => false);
|
||||||
/* Мнемокод показателя */
|
/* Мнемокод показателя */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'SMARK_CODE_' || SCOL_CODE,
|
SNAME => 'SMARK_CODE_' || REC.CODE,
|
||||||
SCAPTION => SCOL_NAME || ' мнемокод показателя',
|
SCAPTION => REC.NAME || ' мнемокод показателя',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
||||||
BVISIBLE => false);
|
BVISIBLE => false);
|
||||||
/* Для составов показтелей */
|
/* Для составов показтелей */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'MARK_CNS_' || SCOL_CODE,
|
SNAME => 'MARK_CNS_' || REC.CODE,
|
||||||
SCAPTION => SCOL_NAME || ' состав показателя',
|
SCAPTION => REC.NAME || ' состав показателя',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
||||||
BVISIBLE => false);
|
BVISIBLE => false);
|
||||||
end loop;
|
end loop;
|
||||||
/* Освобождаем курсор */
|
|
||||||
PKG_SQL_DML.CLOSE_CURSOR(ICURSOR => ICURSOR);
|
|
||||||
exception
|
|
||||||
when others then
|
|
||||||
PKG_SQL_DML.CLOSE_CURSOR(ICURSOR => ICURSOR);
|
|
||||||
raise;
|
|
||||||
end MARKS_COLUMNS_INIT;
|
end MARKS_COLUMNS_INIT;
|
||||||
|
|
||||||
/* Считывание показателя по строке/графе */
|
/* Считывание показателя по строке/графе */
|
||||||
@ -984,159 +898,91 @@ create or replace package body PKG_P8PANELS_RRPCONFED as
|
|||||||
loop
|
loop
|
||||||
/* Инициализируем таблицу данных */
|
/* Инициализируем таблицу данных */
|
||||||
RDG := PKG_P8PANELS_VISUAL.TDG_MAKE(BFIXED_HEADER => true, NFIXED_COLUMNS => 1);
|
RDG := PKG_P8PANELS_VISUAL.TDG_MAKE(BFIXED_HEADER => true, NFIXED_COLUMNS => 1);
|
||||||
/* Наименование строки */
|
/* Формируем структуру заголовка */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'SROW_NAME',
|
SNAME => 'SROW_NAME',
|
||||||
SCAPTION => '',
|
SCAPTION => '',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
||||||
NWIDTH => 150);
|
NWIDTH => 150);
|
||||||
/* Позиция строки */
|
/* Формируем структуру заголовка */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
|
||||||
SNAME => 'NROW_POS',
|
|
||||||
SCAPTION => 'Позиция строки',
|
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
|
|
||||||
BVISIBLE => false);
|
|
||||||
/* Мнемокод строки */
|
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'SROW_CODE',
|
SNAME => 'SROW_CODE',
|
||||||
SCAPTION => 'Мнемокод строки',
|
SCAPTION => 'Мнемокод строки',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
||||||
BVISIBLE => false);
|
BVISIBLE => false);
|
||||||
/* Рег. номер строки */
|
/* Формируем структуру заголовка */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'NROW_RN',
|
SNAME => 'NROW_RN',
|
||||||
SCAPTION => 'Рег. номер строки',
|
SCAPTION => 'Рег. номер строки',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
|
||||||
BVISIBLE => false);
|
BVISIBLE => false);
|
||||||
/* Обнуляем счётчик позиции строки */
|
|
||||||
NROW_POS := 0;
|
|
||||||
/* Если раздел содержит показатели */
|
/* Если раздел содержит показатели */
|
||||||
if (S.NMARKS_EXISTS = 1) then
|
if (S.NMARKS_EXISTS = 1) then
|
||||||
/* Формируем текстовое представление сортировки графы */
|
|
||||||
SORDERS_C := ORDER_TEXT_GET(NORDER => NCOL_ORDER, SALIAS => 'C', NTYPE => 0);
|
|
||||||
/* Инициализируем колонки граф */
|
/* Инициализируем колонки граф */
|
||||||
MARKS_COLUMNS_INIT(RDG => RDG, NRRPCONFSCTN => S.NRN, SORDER => SORDERS_C);
|
MARKS_COLUMNS_INIT(RDG => RDG, NRRPCONFSCTN => S.NRN);
|
||||||
begin
|
/* Обходим строки раздела */
|
||||||
/* Формируем текстовое представление сортировки строки */
|
for R in (select R.RN,
|
||||||
SORDERS_R := ORDER_TEXT_GET(NORDER => NROW_ORDER, SALIAS => 'R', NTYPE => 1);
|
R.CODE,
|
||||||
/* Добавляем подсказку совместимости */
|
R.NAME
|
||||||
CSQL_ROWS := PKG_SQL_BUILD.COMPATIBLE();
|
from RRPCONFSCTNMRK T,
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL_ROWS, SELEMENT1 => 'select R.RN,');
|
RRPROW R
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL_ROWS, SELEMENT1 => ' R.CODE,');
|
where T.PRN = S.NRN
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL_ROWS, SELEMENT1 => ' R.NAME');
|
and R.RN = T.RRPROW
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL_ROWS, SELEMENT1 => ' from RRPCONFSCTNMRK T,');
|
group by R.RN,
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL_ROWS, SELEMENT1 => ' RRPROW R');
|
R.CODE,
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL_ROWS, SELEMENT1 => ' where T.PRN = ' || S.NRN);
|
R.NAME,
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL_ROWS, SELEMENT1 => ' and R.RN = T.RRPROW');
|
R.ROW_CODE,
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL_ROWS, SELEMENT1 => ' group by R.RN,');
|
R.SORT_NUMB
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL_ROWS, SELEMENT1 => ' R.CODE,');
|
order by R.SORT_NUMB,
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL_ROWS, SELEMENT1 => ' R.NAME,');
|
LPAD(R.ROW_CODE, 40, '0'),
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL_ROWS, SELEMENT1 => ' R.ROW_CODE,');
|
LPAD(R.CODE, 20, '0'))
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL_ROWS, SELEMENT1 => ' R.SORT_NUMB');
|
loop
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL_ROWS, SELEMENT1 => ' order by ' || SORDERS_R);
|
/* Заполняем наименование строки */
|
||||||
/* Разбираем его */
|
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SROW_NAME', SVALUE => R.NAME, BCLEAR => true);
|
||||||
ICURSOR_R := PKG_SQL_DML.OPEN_CURSOR(SWHAT => 'SELECT');
|
/* Заполняем мнемокод строки */
|
||||||
PKG_SQL_DML.PARSE(ICURSOR => ICURSOR_R, SQUERY => CSQL_ROWS);
|
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SROW_CODE', SVALUE => R.CODE);
|
||||||
/* Описываем структуру записи курсора */
|
/* Заполняем рег. номер строки */
|
||||||
PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR_R, IPOSITION => 1);
|
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NROW_RN', NVALUE => R.RN);
|
||||||
PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR_R, IPOSITION => 2);
|
/* Обходим графы раздела */
|
||||||
PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR_R, IPOSITION => 3);
|
for C in (select C.RN,
|
||||||
/* Делаем выборку */
|
C.CODE,
|
||||||
if (PKG_SQL_DML.EXECUTE(ICURSOR => ICURSOR_R) = 0) then
|
C.NAME
|
||||||
null;
|
from RRPCONFSCTNMRK T,
|
||||||
end if;
|
RRPCOLUMN C
|
||||||
/* Обходим выбранные записи */
|
where T.PRN = S.NRN
|
||||||
while (PKG_SQL_DML.FETCH_ROWS(ICURSOR => ICURSOR_R) > 0)
|
and C.RN = T.RRPCOLUMN
|
||||||
|
group by C.RN,
|
||||||
|
C.CODE,
|
||||||
|
C.NAME,
|
||||||
|
C.COLUMN_CODE,
|
||||||
|
C.SORT_NUMB
|
||||||
|
order by C.SORT_NUMB,
|
||||||
|
LPAD(C.COLUMN_CODE, 40, '0'),
|
||||||
|
LPAD(C.CODE, 20, '0'))
|
||||||
loop
|
loop
|
||||||
/* Считываем рег. номер строки */
|
/* Считываем показатель по строке/графе */
|
||||||
PKG_SQL_DML.COLUMN_VALUE_NUM(ICURSOR => ICURSOR_R, IPOSITION => 1, NVALUE => NROW_RN);
|
RRRPCONFSCTNMRK := RRPCONFSCTNMRK_GET_ROWCOL(NRRPCONFSCTN => S.NRN, NRRPROW => R.RN, NRRPCOLUMN => C.RN);
|
||||||
/* Считываем код строки */
|
/* Заполняем рег. номер графы */
|
||||||
PKG_SQL_DML.COLUMN_VALUE_STR(ICURSOR => ICURSOR_R, IPOSITION => 2, SVALUE => SROW_CODE);
|
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SCOL_' || C.CODE, SVALUE => C.NAME);
|
||||||
/* Считываем наименование строки */
|
/* Заполняем рег. номер графы */
|
||||||
PKG_SQL_DML.COLUMN_VALUE_STR(ICURSOR => ICURSOR_R, IPOSITION => 3, SVALUE => SROW_NAME);
|
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NCOL_RN_' || C.CODE, NVALUE => C.RN);
|
||||||
/* Заполняем наименование строки */
|
/* Заполняем рег. номер показателя */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW,
|
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW,
|
||||||
SNAME => 'SROW_NAME',
|
SNAME => 'NMARK_RN_' || C.CODE,
|
||||||
SVALUE => SROW_NAME,
|
NVALUE => RRRPCONFSCTNMRK.RN);
|
||||||
BCLEAR => true);
|
/* Если ошибка считывания показателя */
|
||||||
/* Заполняем позицию строки */
|
if (RRRPCONFSCTNMRK.RN is not null) then
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NROW_POS', NVALUE => NROW_POS);
|
/* Заполняем мнемокод показателя */
|
||||||
/* Заполняем мнемокод строки */
|
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SROW_CODE', SVALUE => SROW_CODE);
|
|
||||||
/* Заполняем рег. номер строки */
|
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NROW_RN', NVALUE => NROW_RN);
|
|
||||||
/* Добавляем подсказку совместимости */
|
|
||||||
CSQL_COLS := PKG_SQL_BUILD.COMPATIBLE();
|
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL_COLS, SELEMENT1 => 'select C.RN,');
|
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL_COLS, SELEMENT1 => ' C.CODE,');
|
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL_COLS, SELEMENT1 => ' C.NAME');
|
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL_COLS, SELEMENT1 => ' from RRPCONFSCTNMRK T,');
|
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL_COLS, SELEMENT1 => ' RRPCOLUMN C');
|
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL_COLS, SELEMENT1 => ' where T.PRN = ' || S.NRN);
|
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL_COLS, SELEMENT1 => ' and C.RN = T.RRPCOLUMN');
|
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL_COLS, SELEMENT1 => ' group by C.RN,');
|
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL_COLS, SELEMENT1 => ' C.CODE,');
|
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL_COLS, SELEMENT1 => ' C.NAME,');
|
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL_COLS, SELEMENT1 => ' C.COLUMN_CODE,');
|
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL_COLS, SELEMENT1 => ' C.SORT_NUMB');
|
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL_COLS, SELEMENT1 => ' order by ' || SORDERS_C);
|
|
||||||
/* Разбираем его */
|
|
||||||
ICURSOR_C := PKG_SQL_DML.OPEN_CURSOR(SWHAT => 'SELECT');
|
|
||||||
PKG_SQL_DML.PARSE(ICURSOR => ICURSOR_C, SQUERY => CSQL_COLS);
|
|
||||||
/* Описываем структуру записи курсора */
|
|
||||||
PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR_C, IPOSITION => 1);
|
|
||||||
PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR_C, IPOSITION => 2);
|
|
||||||
PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR_C, IPOSITION => 3);
|
|
||||||
/* Делаем выборку */
|
|
||||||
if (PKG_SQL_DML.EXECUTE(ICURSOR => ICURSOR_C) = 0) then
|
|
||||||
null;
|
|
||||||
end if;
|
|
||||||
/* Обходим выбранные записи */
|
|
||||||
while (PKG_SQL_DML.FETCH_ROWS(ICURSOR => ICURSOR_C) > 0)
|
|
||||||
loop
|
|
||||||
/* Считываем рег. номер графы */
|
|
||||||
PKG_SQL_DML.COLUMN_VALUE_NUM(ICURSOR => ICURSOR_C, IPOSITION => 1, NVALUE => NCOL_RN);
|
|
||||||
/* Считываем код графы */
|
|
||||||
PKG_SQL_DML.COLUMN_VALUE_STR(ICURSOR => ICURSOR_C, IPOSITION => 2, SVALUE => SCOL_CODE);
|
|
||||||
/* Считываем наименование графы */
|
|
||||||
PKG_SQL_DML.COLUMN_VALUE_STR(ICURSOR => ICURSOR_C, IPOSITION => 3, SVALUE => SCOL_NAME);
|
|
||||||
/* Считываем показатель по строке/графе */
|
|
||||||
RRRPCONFSCTNMRK := RRPCONFSCTNMRK_GET_ROWCOL(NRRPCONFSCTN => S.NRN,
|
|
||||||
NRRPROW => NROW_RN,
|
|
||||||
NRRPCOLUMN => NCOL_RN);
|
|
||||||
/* Заполняем наименование графы */
|
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SCOL_' || SCOL_CODE, SVALUE => SCOL_NAME);
|
|
||||||
/* Заполняем рег. номер графы */
|
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NCOL_RN_' || SCOL_CODE, NVALUE => NCOL_RN);
|
|
||||||
/* Заполняем рег. номер показателя */
|
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW,
|
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW,
|
||||||
SNAME => 'NMARK_RN_' || SCOL_CODE,
|
SNAME => 'SMARK_CODE_' || C.CODE,
|
||||||
NVALUE => RRRPCONFSCTNMRK.RN);
|
SVALUE => RRRPCONFSCTNMRK.CODE);
|
||||||
/* Если ошибка считывания показателя */
|
/* Добавляем атрибут состава показателей */
|
||||||
if (RRRPCONFSCTNMRK.RN is not null) then
|
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'MARK_CNS_' || C.CODE, SVALUE => null);
|
||||||
/* Заполняем мнемокод показателя */
|
end if;
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW,
|
|
||||||
SNAME => 'SMARK_CODE_' || SCOL_CODE,
|
|
||||||
SVALUE => RRRPCONFSCTNMRK.CODE);
|
|
||||||
/* Добавляем атрибут состава показателей */
|
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'MARK_CNS_' || SCOL_CODE, SVALUE => null);
|
|
||||||
end if;
|
|
||||||
end loop;
|
|
||||||
/* Освобождаем курсор */
|
|
||||||
PKG_SQL_DML.CLOSE_CURSOR(ICURSOR => ICURSOR_C);
|
|
||||||
/* Добавим строку для раздела */
|
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW);
|
|
||||||
/* Инкрементируем счётчик позиции строки */
|
|
||||||
NROW_POS := NROW_POS + 1;
|
|
||||||
end loop;
|
end loop;
|
||||||
/* Освобождаем курсор */
|
/* Добавим строку для раздела */
|
||||||
PKG_SQL_DML.CLOSE_CURSOR(ICURSOR => ICURSOR_R);
|
PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW);
|
||||||
exception
|
end loop;
|
||||||
when others then
|
|
||||||
PKG_SQL_DML.CLOSE_CURSOR(ICURSOR => ICURSOR_R);
|
|
||||||
PKG_SQL_DML.CLOSE_CURSOR(ICURSOR => ICURSOR_C);
|
|
||||||
raise;
|
|
||||||
end;
|
|
||||||
end if;
|
end if;
|
||||||
/* Сериализуем описание */
|
/* Сериализуем описание */
|
||||||
CDG := PKG_P8PANELS_VISUAL.TDG_TO_XML(RDATA_GRID => RDG, NINCLUDE_DEF => 1);
|
CDG := PKG_P8PANELS_VISUAL.TDG_TO_XML(RDATA_GRID => RDG, NINCLUDE_DEF => 1);
|
||||||
@ -1212,10 +1058,10 @@ create or replace package body PKG_P8PANELS_RRPCONFED as
|
|||||||
/* Добавление раздела регламентированного отчёта */
|
/* Добавление раздела регламентированного отчёта */
|
||||||
procedure RRPCONFSCTN_INSERT
|
procedure RRPCONFSCTN_INSERT
|
||||||
(
|
(
|
||||||
NPRN in number, -- Рег. номер настройки форм регламентированного отчёта
|
NPRN in number, -- Ид. настройки форм регламентированного отчёта
|
||||||
SCODE in varchar2, -- Мнемокод
|
SCODE in varchar2, -- Мнемокод
|
||||||
SNAME in varchar2, -- Наименование
|
SNAME in varchar2, -- Наименование
|
||||||
NRN out number -- Рег. номер созданной записи
|
NRN out number -- Ид. созданной записи
|
||||||
)
|
)
|
||||||
is
|
is
|
||||||
NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Рег. номер организации
|
NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Рег. номер организации
|
||||||
@ -1240,7 +1086,7 @@ create or replace package body PKG_P8PANELS_RRPCONFED as
|
|||||||
/* Исправление раздела регламентированного отчёта */
|
/* Исправление раздела регламентированного отчёта */
|
||||||
procedure RRPCONFSCTN_UPDATE
|
procedure RRPCONFSCTN_UPDATE
|
||||||
(
|
(
|
||||||
NRN in number, -- Рег. номер раздела
|
NRN in number, -- Ид. раздела
|
||||||
SCODE in varchar2, -- Мнемокод раздела
|
SCODE in varchar2, -- Мнемокод раздела
|
||||||
SNAME in varchar2 -- Наименование раздела
|
SNAME in varchar2 -- Наименование раздела
|
||||||
)
|
)
|
||||||
@ -1279,7 +1125,7 @@ create or replace package body PKG_P8PANELS_RRPCONFED as
|
|||||||
/* Удаление раздела регламентированного отчёта */
|
/* Удаление раздела регламентированного отчёта */
|
||||||
procedure RRPCONFSCTN_DELETE
|
procedure RRPCONFSCTN_DELETE
|
||||||
(
|
(
|
||||||
NRN in number -- Рег. номер раздела
|
NRN in number -- Ид. раздела
|
||||||
)
|
)
|
||||||
is
|
is
|
||||||
NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Рег. номер организации
|
NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Рег. номер организации
|
||||||
@ -1296,10 +1142,10 @@ create or replace package body PKG_P8PANELS_RRPCONFED as
|
|||||||
/* Получение кодов настройки, раздела и показателя раздела по ид. показателя раздела */
|
/* Получение кодов настройки, раздела и показателя раздела по ид. показателя раздела */
|
||||||
procedure RRPCONFSCTNMRK_GET_CODES
|
procedure RRPCONFSCTNMRK_GET_CODES
|
||||||
(
|
(
|
||||||
NRN in number, -- Рег. номер показателя раздела
|
NRN in number, -- Ид. показателя раздела
|
||||||
SRRPCONF out varchar2, -- Мнемокод настройки формы регламентированного отчёта
|
SRRPCONF out varchar2, -- Код настройки формы регламентированного отчёта
|
||||||
SRRPCONFSCTN out varchar2, -- Мнемокод раздела
|
SRRPCONFSCTN out varchar2, -- Код раздела
|
||||||
SRRPCONFSCTNMRK out varchar2 -- Мнемокод показателя раздела
|
SRRPCONFSCTNMRK out varchar2 -- Код показателя раздела
|
||||||
)
|
)
|
||||||
is
|
is
|
||||||
begin
|
begin
|
||||||
@ -1381,12 +1227,12 @@ create or replace package body PKG_P8PANELS_RRPCONFED as
|
|||||||
/* Добавление показателя раздела регламентированного отчёта */
|
/* Добавление показателя раздела регламентированного отчёта */
|
||||||
procedure RRPCONFSCTNMRK_INSERT
|
procedure RRPCONFSCTNMRK_INSERT
|
||||||
(
|
(
|
||||||
NPRN in number, -- Рег. номер раздела
|
NPRN in number, -- Ид. раздела
|
||||||
SCODE in varchar2, -- Мнемокод показателя раздела
|
SCODE in varchar2, -- Мнемокод показателя раздела
|
||||||
SNAME in varchar2, -- Наименование показателя раздела
|
SNAME in varchar2, -- Наименование показателя раздела
|
||||||
NRRPROW in number, -- Рег. номер строки
|
NRRPROW in number, -- Рег. номер строки
|
||||||
NRRPCOLUMN in number, -- Рег. номер графы
|
NRRPCOLUMN in number, -- Рег. номер графы
|
||||||
NRN out number -- Рег. номер созданной записи
|
NRN out number -- Ид. созданной записи
|
||||||
)
|
)
|
||||||
is
|
is
|
||||||
NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Рег. номер организации
|
NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Рег. номер организации
|
||||||
@ -1439,7 +1285,7 @@ create or replace package body PKG_P8PANELS_RRPCONFED as
|
|||||||
/* Исправление показателя раздела регламентированного отчёта */
|
/* Исправление показателя раздела регламентированного отчёта */
|
||||||
procedure RRPCONFSCTNMRK_UPDATE
|
procedure RRPCONFSCTNMRK_UPDATE
|
||||||
(
|
(
|
||||||
NRN in number, -- Рег. номер показателя раздела
|
NRN in number, -- Ид. показателя раздела
|
||||||
SNAME in varchar2 -- Новое наименование
|
SNAME in varchar2 -- Новое наименование
|
||||||
)
|
)
|
||||||
is
|
is
|
||||||
@ -1494,7 +1340,7 @@ create or replace package body PKG_P8PANELS_RRPCONFED as
|
|||||||
/* Удаление показателя раздела регламентированного отчёта */
|
/* Удаление показателя раздела регламентированного отчёта */
|
||||||
procedure RRPCONFSCTNMRK_DELETE
|
procedure RRPCONFSCTNMRK_DELETE
|
||||||
(
|
(
|
||||||
NRN in number -- Рег. номер показателя раздела
|
NRN in number -- Ид. показателя раздела
|
||||||
)
|
)
|
||||||
is
|
is
|
||||||
NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Рег. номер организации
|
NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Рег. номер организации
|
||||||
|
@ -1,56 +1,46 @@
|
|||||||
create or replace package PKG_P8PANELS_VISUAL as
|
create or replace package PKG_P8PANELS_VISUAL as
|
||||||
|
|
||||||
/* Константы - типы данных */
|
/* Константы - типы данных */
|
||||||
SDATA_TYPE_STR constant PKG_STD.TSTRING := 'STR'; -- Тип данных "строка"
|
SDATA_TYPE_STR constant PKG_STD.TSTRING := 'STR'; -- Тип данных "строка"
|
||||||
SDATA_TYPE_NUMB constant PKG_STD.TSTRING := 'NUMB'; -- Тип данных "число"
|
SDATA_TYPE_NUMB constant PKG_STD.TSTRING := 'NUMB'; -- Тип данных "число"
|
||||||
SDATA_TYPE_DATE constant PKG_STD.TSTRING := 'DATE'; -- Тип данных "дата"
|
SDATA_TYPE_DATE constant PKG_STD.TSTRING := 'DATE'; -- Тип данных "дата"
|
||||||
|
|
||||||
/* Константы - направление сортировки */
|
/* Константы - направление сортировки */
|
||||||
SORDER_DIRECTION_ASC constant PKG_STD.TSTRING := 'ASC'; -- По возрастанию
|
SORDER_DIRECTION_ASC constant PKG_STD.TSTRING := 'ASC'; -- По возрастанию
|
||||||
SORDER_DIRECTION_DESC constant PKG_STD.TSTRING := 'DESC'; -- По убыванию
|
SORDER_DIRECTION_DESC constant PKG_STD.TSTRING := 'DESC'; -- По убыванию
|
||||||
|
|
||||||
/* Константы - диаграмма Ганта - масштаб */
|
/* Константы - масштаб диаграммы Ганта */
|
||||||
NGANTT_ZOOM_QUARTER_DAY constant PKG_STD.TNUMBER := 0; -- Четверть дня
|
NGANTT_ZOOM_QUARTER_DAY constant PKG_STD.TNUMBER := 0; -- Четверть дня
|
||||||
NGANTT_ZOOM_HALF_DAY constant PKG_STD.TNUMBER := 1; -- Пол дня
|
NGANTT_ZOOM_HALF_DAY constant PKG_STD.TNUMBER := 1; -- Пол дня
|
||||||
NGANTT_ZOOM_DAY constant PKG_STD.TNUMBER := 2; -- День
|
NGANTT_ZOOM_DAY constant PKG_STD.TNUMBER := 2; -- День
|
||||||
NGANTT_ZOOM_WEEK constant PKG_STD.TNUMBER := 3; -- Неделя
|
NGANTT_ZOOM_WEEK constant PKG_STD.TNUMBER := 3; -- Неделя
|
||||||
NGANTT_ZOOM_MONTH constant PKG_STD.TNUMBER := 4; -- Месяц
|
NGANTT_ZOOM_MONTH constant PKG_STD.TNUMBER := 4; -- Месяц
|
||||||
|
|
||||||
/* Константы - график - тип */
|
/* Константы - масштаб циклограммы */
|
||||||
SCHART_TYPE_BAR constant PKG_STD.TSTRING := 'bar'; -- Столбчатая
|
NCYCLOGRAM_ZOOM_MIN constant PKG_STD.TLNUMBER := 0.2; -- Минимальный (0.2 от исходного)
|
||||||
SCHART_TYPE_LINE constant PKG_STD.TSTRING := 'line'; -- Линейная
|
NCYCLOGRAM_ZOOM_TINY constant PKG_STD.TLNUMBER := 0.4; -- Мелкий (0.4 от исходного)
|
||||||
SCHART_TYPE_PIE constant PKG_STD.TSTRING := 'pie'; -- Круговая
|
NCYCLOGRAM_ZOOM_SMALL constant PKG_STD.TLNUMBER := 0.7; -- Уменьшенный (0.7 от исходного)
|
||||||
SCHART_TYPE_DOUGHNUT constant PKG_STD.TSTRING := 'doughnut'; -- Кольцевая
|
NCYCLOGRAM_ZOOM_DEFAULT constant PKG_STD.TLNUMBER := 1; -- Исходный
|
||||||
|
NCYCLOGRAM_ZOOM_LARGE constant PKG_STD.TLNUMBER := 1.5; -- Увеличенный (1.5 от исходного)
|
||||||
|
NCYCLOGRAM_ZOOM_HUGE constant PKG_STD.TLNUMBER := 2; -- Большой (2 от исходного)
|
||||||
|
NCYCLOGRAM_ZOOM_MAX constant PKG_STD.TLNUMBER := 2.5; -- Максимальный (2.5 от исходного)
|
||||||
|
|
||||||
/* Константы - график - расположение легенды */
|
/* Константы - тип графика */
|
||||||
SCHART_LGND_POS_LEFT constant PKG_STD.TSTRING := 'left'; -- Слева
|
SCHART_TYPE_BAR constant PKG_STD.TSTRING := 'bar'; -- Столбчатая
|
||||||
SCHART_LGND_POS_RIGHT constant PKG_STD.TSTRING := 'right'; -- Справа
|
SCHART_TYPE_LINE constant PKG_STD.TSTRING := 'line'; -- Линейная
|
||||||
SCHART_LGND_POS_TOP constant PKG_STD.TSTRING := 'top'; -- Наверху
|
SCHART_TYPE_PIE constant PKG_STD.TSTRING := 'pie'; -- Круговая
|
||||||
SCHART_LGND_POS_BOTTOM constant PKG_STD.TSTRING := 'bottom'; -- Внизу
|
SCHART_TYPE_DOUGHNUT constant PKG_STD.TSTRING := 'doughnut'; -- Кольцевая
|
||||||
|
|
||||||
/* Константы - циклограмма - масштаб */
|
/* Константы - расположение легенды графика */
|
||||||
NCYCLOGRAM_ZOOM_MIN constant PKG_STD.TLNUMBER := 0.2; -- Минимальный (0.2 от исходного)
|
SCHART_LGND_POS_LEFT constant PKG_STD.TSTRING := 'left'; -- Слева
|
||||||
NCYCLOGRAM_ZOOM_TINY constant PKG_STD.TLNUMBER := 0.4; -- Мелкий (0.4 от исходного)
|
SCHART_LGND_POS_RIGHT constant PKG_STD.TSTRING := 'right'; -- Справа
|
||||||
NCYCLOGRAM_ZOOM_SMALL constant PKG_STD.TLNUMBER := 0.7; -- Уменьшенный (0.7 от исходного)
|
SCHART_LGND_POS_TOP constant PKG_STD.TSTRING := 'top'; -- Наверху
|
||||||
NCYCLOGRAM_ZOOM_DEFAULT constant PKG_STD.TLNUMBER := 1; -- Исходный
|
SCHART_LGND_POS_BOTTOM constant PKG_STD.TSTRING := 'bottom'; -- Внизу
|
||||||
NCYCLOGRAM_ZOOM_LARGE constant PKG_STD.TLNUMBER := 1.5; -- Увеличенный (1.5 от исходного)
|
|
||||||
NCYCLOGRAM_ZOOM_HUGE constant PKG_STD.TLNUMBER := 2; -- Большой (2 от исходного)
|
|
||||||
NCYCLOGRAM_ZOOM_MAX constant PKG_STD.TLNUMBER := 2.5; -- Максимальный (2.5 от исходного)
|
|
||||||
|
|
||||||
/* Константы - циклограмма - оформление */
|
/* Константы - циклограмма */
|
||||||
NCYCLOGRAM_GROUP_DEF_WIDTH constant PKG_STD.TNUMBER := 100; -- Высота заголовка группы (по умолчанию)
|
NCYCLOGRAM_GROUP_DEF_WIDTH constant PKG_STD.TNUMBER := 100; -- Высота заголовка группы (по умолчанию)
|
||||||
NCYCLOGRAM_GROUP_DEF_HEIGHT constant PKG_STD.TNUMBER := 42; -- Ширина заголовка группы (по умолчанию)
|
NCYCLOGRAM_GROUP_DEF_HEIGHT constant PKG_STD.TNUMBER := 42; -- Ширина заголовка группы (по умолчанию)
|
||||||
NCYCLOGRAM_LINE_HEIGHT constant PKG_STD.TNUMBER := 20; -- Высота строк циклограммы
|
NCYCLOGRAM_LINE_HEIGHT constant PKG_STD.TNUMBER := 20; -- Высота строк циклограммы
|
||||||
|
|
||||||
/* Константы - индикатор - состояния */
|
|
||||||
SINDICATOR_STATE_UNDEFINED constant PKG_STD.TSTRING := 'UNDEFINED'; -- Неопределено
|
|
||||||
SINDICATOR_STATE_OK constant PKG_STD.TSTRING := 'OK'; -- Позитивное
|
|
||||||
SINDICATOR_STATE_ERR constant PKG_STD.TSTRING := 'ERR'; -- Негативное
|
|
||||||
SINDICATOR_STATE_WARN constant PKG_STD.TSTRING := 'WARN'; -- Пограничное
|
|
||||||
|
|
||||||
/* Константы - индикатор - варианты исполнения */
|
|
||||||
SINDICATOR_VARIANT_ELEVATION constant PKG_STD.TSTRING := 'elevation'; -- Парящий
|
|
||||||
SINDICATOR_VARIANT_OUTLINED constant PKG_STD.TSTRING := 'outlined'; -- Плоский с рамкой
|
|
||||||
|
|
||||||
/* Типы данных - значение колонки таблицы данных */
|
/* Типы данных - значение колонки таблицы данных */
|
||||||
type TDG_COL_VAL is record
|
type TDG_COL_VAL is record
|
||||||
@ -343,21 +333,6 @@ create or replace package PKG_P8PANELS_VISUAL as
|
|||||||
RTASK_ATTRS TCYCLOGRAM_TASK_ATTRS -- Описание атрибутов карточки задачи
|
RTASK_ATTRS TCYCLOGRAM_TASK_ATTRS -- Описание атрибутов карточки задачи
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Типы данных - индикатор */
|
|
||||||
type TINDICATOR is record
|
|
||||||
(
|
|
||||||
SCAPTION PKG_STD.TSTRING, -- Подпись
|
|
||||||
SVALUE PKG_STD.TSTRING, -- Значение
|
|
||||||
SICON PKG_STD.TSTRING := null, -- Иконка (код шрифта "Google Material Icons" - https://fonts.google.com/icons?icon.set=Material+Icons)
|
|
||||||
SSTATE PKG_STD.TSTRING := SINDICATOR_STATE_UNDEFINED, -- Состояние (см. константы SINDICATOR_STATE_*)
|
|
||||||
BSQUARE boolean := false, -- Скруглять углы
|
|
||||||
NELEVATION PKG_STD.TNUMBER := 3, -- Высота парения (от 0 до 24, игнорируется для SINDICATOR_VARIANT_OUTLINED)
|
|
||||||
SVARIANT PKG_STD.TSTRING := SINDICATOR_VARIANT_ELEVATION, -- Вариант исполнения (см. константы SINDICATOR_VARIANT_*)
|
|
||||||
SHINT PKG_STD.TSTRING := null, -- Подсказка
|
|
||||||
SBACKGROUND_COLOR PKG_STD.TSTRING := null, -- Цвет заливки (HTML-код, null - использовать цвет по умолчанию для состояния)
|
|
||||||
SCOLOR PKG_STD.TSTRING := null -- Цвет шрифта и иконки (HTML-код, null - использовать цвет по умолчанию для состояния)
|
|
||||||
);
|
|
||||||
|
|
||||||
/* Расчет диапаона выдаваемых записей */
|
/* Расчет диапаона выдаваемых записей */
|
||||||
procedure UTL_ROWS_LIMITS_CALC
|
procedure UTL_ROWS_LIMITS_CALC
|
||||||
(
|
(
|
||||||
@ -775,92 +750,62 @@ create or replace package PKG_P8PANELS_VISUAL as
|
|||||||
NINCLUDE_DEF in number := 1 -- Включить описание колонок (0 - нет, 1 - да)
|
NINCLUDE_DEF in number := 1 -- Включить описание колонок (0 - нет, 1 - да)
|
||||||
) return clob; -- XML-описание
|
) return clob; -- XML-описание
|
||||||
|
|
||||||
/* Формирование индикатора */
|
|
||||||
function TINDICATOR_MAKE
|
|
||||||
(
|
|
||||||
SCAPTION in varchar2, -- Подпись
|
|
||||||
SVALUE in varchar2, -- Значение
|
|
||||||
SICON in varchar2 := null, -- Иконка (код шрифта "Google Material Icons" - https://fonts.google.com/icons?icon.set=Material+Icons)
|
|
||||||
SSTATE in varchar2 := SINDICATOR_STATE_UNDEFINED, -- Состояние (см. константы SINDICATOR_STATE_*)
|
|
||||||
BSQUARE in boolean := false, -- Скруглять углы
|
|
||||||
NELEVATION in number := 3, -- Высота парения (от 0 до 24, игнорируется для SINDICATOR_VARIANT_OUTLINED)
|
|
||||||
SVARIANT in varchar2 := SINDICATOR_VARIANT_ELEVATION, -- Вариант исполнения (см. константы SINDICATOR_VARIANT_*)
|
|
||||||
SHINT in varchar2 := null, -- Подсказка
|
|
||||||
SBACKGROUND_COLOR in varchar2 := null, -- Цвет заливки (HTML-код, null - использовать цвет по умолчанию для состояния)
|
|
||||||
SCOLOR in varchar2 := null -- Цвет шрифта и иконки (HTML-код, null - использовать цвет по умолчанию для состояния)
|
|
||||||
) return TINDICATOR; -- Результат работы
|
|
||||||
|
|
||||||
/* Сериализация индикатора */
|
|
||||||
function TINDICATOR_TO_XML
|
|
||||||
(
|
|
||||||
RINDICATOR in TINDICATOR -- Описание индикатора
|
|
||||||
) return clob; -- XML-описание
|
|
||||||
|
|
||||||
end PKG_P8PANELS_VISUAL;
|
end PKG_P8PANELS_VISUAL;
|
||||||
/
|
/
|
||||||
create or replace package body PKG_P8PANELS_VISUAL as
|
create or replace package body PKG_P8PANELS_VISUAL as
|
||||||
|
|
||||||
/* Константы - тэги запросов */
|
/* Константы - тэги запросов */
|
||||||
SRQ_TAG_XROOT constant PKG_STD.TSTRING := 'XROOT'; -- Тэг для корня данных запроса
|
SRQ_TAG_XROOT constant PKG_STD.TSTRING := 'XROOT'; -- Тэг для корня данных запроса
|
||||||
SRQ_TAG_XFILTERS constant PKG_STD.TSTRING := 'filters'; -- Тэг для строк данных
|
SRQ_TAG_XFILTERS constant PKG_STD.TSTRING := 'filters'; -- Тэг для строк данных
|
||||||
SRQ_TAG_XORDERS constant PKG_STD.TSTRING := 'orders'; -- Тэг для описания колонок
|
SRQ_TAG_XORDERS constant PKG_STD.TSTRING := 'orders'; -- Тэг для описания колонок
|
||||||
SRQ_TAG_SNAME constant PKG_STD.TSTRING := 'name'; -- Тэг для наименования
|
SRQ_TAG_SNAME constant PKG_STD.TSTRING := 'name'; -- Тэг для наименования
|
||||||
SRQ_TAG_SDIRECTION constant PKG_STD.TSTRING := 'direction'; -- Тэг для направления
|
SRQ_TAG_SDIRECTION constant PKG_STD.TSTRING := 'direction'; -- Тэг для направления
|
||||||
SRQ_TAG_SFROM constant PKG_STD.TSTRING := 'from'; -- Тэг для значения "с"
|
SRQ_TAG_SFROM constant PKG_STD.TSTRING := 'from'; -- Тэг для значения "с"
|
||||||
SRQ_TAG_STO constant PKG_STD.TSTRING := 'to'; -- Тэг для значения "по"
|
SRQ_TAG_STO constant PKG_STD.TSTRING := 'to'; -- Тэг для значения "по"
|
||||||
|
|
||||||
/* Константы - тэги ответов */
|
/* Константы - тэги ответов */
|
||||||
SRESP_TAG_XDATA constant PKG_STD.TSTRING := 'XDATA'; -- Тэг для корня описания данных
|
SRESP_TAG_XDATA constant PKG_STD.TSTRING := 'XDATA'; -- Тэг для корня описания данных
|
||||||
SRESP_TAG_XCHART constant PKG_STD.TSTRING := 'XCHART'; -- Тэг для описания графика
|
SRESP_TAG_XCHART constant PKG_STD.TSTRING := 'XCHART'; -- Тэг для описания графика
|
||||||
SRESP_TAG_XDATA_GRID constant PKG_STD.TSTRING := 'XDATA_GRID'; -- Тэг для описания таблицы данных
|
SRESP_TAG_XDATA_GRID constant PKG_STD.TSTRING := 'XDATA_GRID'; -- Тэг для описания таблицы данных
|
||||||
SRESP_TAG_XCYCLOGRAM constant PKG_STD.TSTRING := 'XCYCLOGRAM'; -- Тэг для описания циклограммы
|
SRESP_TAG_XCYCLOGRAM constant PKG_STD.TSTRING := 'XCYCLOGRAM'; -- Тэг для описания циклограммы
|
||||||
SRESP_TAG_XGANTT constant PKG_STD.TSTRING := 'XGANTT'; -- Тэг для описания диаграммы Ганта
|
SRESP_TAG_XGANTT constant PKG_STD.TSTRING := 'XGANTT'; -- Тэг для описания диаграммы Ганта
|
||||||
SRESP_TAG_XINDICATOR constant PKG_STD.TSTRING := 'XINDICATOR'; -- Тэг для описания индикатора
|
|
||||||
|
|
||||||
/* Константы - атрибуты ответов (универсальные) */
|
/* Константы - атрибуты ответов (универсальные) */
|
||||||
SRESP_ATTR_NAME constant PKG_STD.TSTRING := 'name'; -- Атрибут для наименования
|
SRESP_ATTR_NAME constant PKG_STD.TSTRING := 'name'; -- Атрибут для наименования
|
||||||
SRESP_ATTR_CAPTION constant PKG_STD.TSTRING := 'caption'; -- Атрибут для подписи
|
SRESP_ATTR_CAPTION constant PKG_STD.TSTRING := 'caption'; -- Атрибут для подписи
|
||||||
SRESP_ATTR_DATA_TYPE constant PKG_STD.TSTRING := 'dataType'; -- Атрибут для типа данных
|
SRESP_ATTR_DATA_TYPE constant PKG_STD.TSTRING := 'dataType'; -- Атрибут для типа данных
|
||||||
SRESP_ATTR_VISIBLE constant PKG_STD.TSTRING := 'visible'; -- Атрибут для флага видимости
|
SRESP_ATTR_VISIBLE constant PKG_STD.TSTRING := 'visible'; -- Атрибут для флага видимости
|
||||||
SRESP_ATTR_TITLE constant PKG_STD.TSTRING := 'title'; -- Атрибут для заголовка
|
SRESP_ATTR_TITLE constant PKG_STD.TSTRING := 'title'; -- Атрибут для заголовка
|
||||||
SRESP_ATTR_ZOOM constant PKG_STD.TSTRING := 'zoom'; -- Атрибут для масштаба
|
SRESP_ATTR_ZOOM constant PKG_STD.TSTRING := 'zoom'; -- Атрибут для масштаба
|
||||||
SRESP_ATTR_ID constant PKG_STD.TSTRING := 'id'; -- Атрибут для идентификатора
|
SRESP_ATTR_ID constant PKG_STD.TSTRING := 'id'; -- Атрибут для идентификатора
|
||||||
SRESP_ATTR_START constant PKG_STD.TSTRING := 'start'; -- Атрибут для даты начала
|
SRESP_ATTR_START constant PKG_STD.TSTRING := 'start'; -- Атрибут для даты начала
|
||||||
SRESP_ATTR_END constant PKG_STD.TSTRING := 'end'; -- Атрибут для даты окончания
|
SRESP_ATTR_END constant PKG_STD.TSTRING := 'end'; -- Атрибут для даты окончания
|
||||||
SRESP_ATTR_RN constant PKG_STD.TSTRING := 'rn'; -- Атрибут для рег. номера
|
SRESP_ATTR_RN constant PKG_STD.TSTRING := 'rn'; -- Атрибут для рег. номера
|
||||||
SRESP_ATTR_NUMB constant PKG_STD.TSTRING := 'numb'; -- Атрибут для номера
|
SRESP_ATTR_NUMB constant PKG_STD.TSTRING := 'numb'; -- Атрибут для номера
|
||||||
SRESP_ATTR_FULL_NAME constant PKG_STD.TSTRING := 'fullName'; -- Атрибут для полного наименования
|
SRESP_ATTR_FULL_NAME constant PKG_STD.TSTRING := 'fullName'; -- Атрибут для полного наименования
|
||||||
SRESP_ATTR_DESC constant PKG_STD.TSTRING := 'desc'; -- Атрибут для описания
|
SRESP_ATTR_DESC constant PKG_STD.TSTRING := 'desc'; -- Атрибут для описания
|
||||||
SRESP_ATTR_TYPE constant PKG_STD.TSTRING := 'type'; -- Атрибут для типа
|
SRESP_ATTR_TYPE constant PKG_STD.TSTRING := 'type'; -- Атрибут для типа
|
||||||
SRESP_ATTR_HINT constant PKG_STD.TSTRING := 'hint'; -- Атрибут для подсказки
|
SRESP_ATTR_HINT constant PKG_STD.TSTRING := 'hint'; -- Атрибут для подсказки
|
||||||
SRESP_ATTR_GROUP_NAME constant PKG_STD.TSTRING := 'groupName'; -- Атрибут для наименования группы
|
SRESP_ATTR_GROUP_NAME constant PKG_STD.TSTRING := 'groupName'; -- Атрибут для наименования группы
|
||||||
SRESP_ATTR_PARENT constant PKG_STD.TSTRING := 'parent'; -- Атрибут для ссылки на родителя
|
SRESP_ATTR_PARENT constant PKG_STD.TSTRING := 'parent'; -- Атрибут для ссылки на родителя
|
||||||
SRESP_ATTR_EXPANDABLE constant PKG_STD.TSTRING := 'expandable'; -- Атрибут для доступности действия сокрытия/отображения
|
SRESP_ATTR_EXPANDABLE constant PKG_STD.TSTRING := 'expandable'; -- Атрибут для доступности действия сокрытия/отображения
|
||||||
SRESP_ATTR_EXPANDED constant PKG_STD.TSTRING := 'expanded'; -- Атрибут для флага сокрытия/отображения
|
SRESP_ATTR_EXPANDED constant PKG_STD.TSTRING := 'expanded'; -- Атрибут для флага сокрытия/отображения
|
||||||
SRESP_ATTR_FIXED_HEADER constant PKG_STD.TSTRING := 'fixedHeader'; -- Атрибут для флага фиксации заголовка
|
SRESP_ATTR_FIXED_HEADER constant PKG_STD.TSTRING := 'fixedHeader'; -- Атрибут для флага фиксации заголовка
|
||||||
SRESP_ATTR_FIXED_COLUMNS constant PKG_STD.TSTRING := 'fixedColumns'; -- Атрибут для количества фиксированных колонок
|
SRESP_ATTR_FIXED_COLUMNS constant PKG_STD.TSTRING := 'fixedColumns'; -- Атрибут для количества фиксированных колонок
|
||||||
SRESP_ATTR_WIDTH constant PKG_STD.TSTRING := 'width'; -- Атрибут для ширины
|
SRESP_ATTR_WIDTH constant PKG_STD.TSTRING := 'width'; -- Атрибут для ширины
|
||||||
SRESP_ATTR_HEIGHT constant PKG_STD.TSTRING := 'height'; -- Атрибут для высоты
|
SRESP_ATTR_HEIGHT constant PKG_STD.TSTRING := 'height'; -- Атрибут для высоты
|
||||||
SRESP_ATTR_COLUMNS constant PKG_STD.TSTRING := 'columns'; -- Атрибут для колонок
|
SRESP_ATTR_COLUMNS constant PKG_STD.TSTRING := 'columns'; -- Атрибут для колонок
|
||||||
SRESP_ATTR_TASKS constant PKG_STD.TSTRING := 'tasks'; -- Атрибут для задач
|
SRESP_ATTR_TASKS constant PKG_STD.TSTRING := 'tasks'; -- Атрибут для задач
|
||||||
SRESP_ATTR_HL_COLOR constant PKG_STD.TSTRING := 'highlightColor'; -- Атрибут для цвета подсветки
|
SRESP_ATTR_HL_COLOR constant PKG_STD.TSTRING := 'highlightColor'; -- Атрибут для цвета подсветки
|
||||||
SRESP_ATTR_ZOOM_BAR constant PKG_STD.TSTRING := 'zoomBar'; -- Атрибут для флага отображения панели масштаба
|
SRESP_ATTR_ZOOM_BAR constant PKG_STD.TSTRING := 'zoomBar'; -- Атрибут для флага отображения панели масштаба
|
||||||
SRESP_ATTR_ROWS constant PKG_STD.TSTRING := 'rows'; -- Атрибут для строк данных
|
SRESP_ATTR_ROWS constant PKG_STD.TSTRING := 'rows'; -- Атрибут для строк данных
|
||||||
SRESP_ATTR_COLUMNS_DEF constant PKG_STD.TSTRING := 'columnsDef'; -- Атрибут для описания колонок
|
SRESP_ATTR_COLUMNS_DEF constant PKG_STD.TSTRING := 'columnsDef'; -- Атрибут для описания колонок
|
||||||
SRESP_ATTR_GROUPS constant PKG_STD.TSTRING := 'groups'; -- Атрибут для описания групп
|
SRESP_ATTR_GROUPS constant PKG_STD.TSTRING := 'groups'; -- Атрибут для описания групп
|
||||||
SRESP_ATTR_VALUE constant PKG_STD.TSTRING := 'value'; -- Атрибут для значения
|
|
||||||
SRESP_ATTR_ICON constant PKG_STD.TSTRING := 'icon'; -- Атрибут для иконки
|
|
||||||
SRESP_ATTR_STATE constant PKG_STD.TSTRING := 'state'; -- Атрибут для состояния
|
|
||||||
SRESP_ATTR_SQUARE constant PKG_STD.TSTRING := 'square'; -- Атрибут для флага скругления
|
|
||||||
SRESP_ATTR_ELEVATION constant PKG_STD.TSTRING := 'elevation'; -- Атрибут для высоты парения
|
|
||||||
SRESP_ATTR_VARIANT constant PKG_STD.TSTRING := 'variant'; -- Атрибут для варианта исполнения
|
|
||||||
SRESP_ATTR_BG_COLOR constant PKG_STD.TSTRING := 'backgroundColor'; -- Атрибут для цвета заливки
|
|
||||||
SRESP_ATTR_COLOR constant PKG_STD.TSTRING := 'color'; -- Атрибут для цвета
|
|
||||||
|
|
||||||
/* Константы - атрибуты ответов (таблица данных) */
|
/* Константы - атрибуты ответов (таблица данных) */
|
||||||
SRESP_ATTR_DT_ORDER constant PKG_STD.TSTRING := 'order'; -- Атрибут для флага сортировки
|
SRESP_ATTR_DT_ORDER constant PKG_STD.TSTRING := 'order'; -- Атрибут для флага сортировки
|
||||||
SRESP_ATTR_DT_FILTER constant PKG_STD.TSTRING := 'filter'; -- Атрибут для флага отбора
|
SRESP_ATTR_DT_FILTER constant PKG_STD.TSTRING := 'filter'; -- Атрибут для флага отбора
|
||||||
SRESP_ATTR_DT_COLUMN_VALUES constant PKG_STD.TSTRING := 'values'; -- Атрибут для предопределённых значений
|
SRESP_ATTR_DT_COLUMN_VALUES constant PKG_STD.TSTRING := 'values'; -- Атрибут для предопределённых значений
|
||||||
|
|
||||||
/* Константы - атрибуты ответов (диаграмма Ганта) */
|
/* Константы - атрибуты ответов (диаграмма Ганта) */
|
||||||
SRESP_ATTR_TASK_PROGRESS constant PKG_STD.TSTRING := 'progress'; -- Атрибут для прогресса задачи
|
SRESP_ATTR_TASK_PROGRESS constant PKG_STD.TSTRING := 'progress'; -- Атрибут для прогресса задачи
|
||||||
@ -875,23 +820,23 @@ create or replace package body PKG_P8PANELS_VISUAL as
|
|||||||
SRESP_ATTR_TASK_COLORS constant PKG_STD.TSTRING := 'taskColors'; -- Атрибут для коллекции цветов задачи
|
SRESP_ATTR_TASK_COLORS constant PKG_STD.TSTRING := 'taskColors'; -- Атрибут для коллекции цветов задачи
|
||||||
|
|
||||||
/* Константы - атрибуты ответов (графики) */
|
/* Константы - атрибуты ответов (графики) */
|
||||||
SRESP_ATTR_CHART_LGND_POS constant PKG_STD.TSTRING := 'legendPosition'; -- Атрибут для места размешения легенды графика
|
SRESP_ATTR_CHART_LGND_POS constant PKG_STD.TSTRING := 'legendPosition'; -- Атрибут для места размешения легенды графика
|
||||||
SRESP_ATTR_CHART_LABELS constant PKG_STD.TSTRING := 'labels'; -- Атрибут для меток графика
|
SRESP_ATTR_CHART_LABELS constant PKG_STD.TSTRING := 'labels'; -- Атрибут для меток графика
|
||||||
SRESP_ATTR_CHART_DATASETS constant PKG_STD.TSTRING := 'datasets'; -- Атрибут для наборов данных графика
|
SRESP_ATTR_CHART_DATASETS constant PKG_STD.TSTRING := 'datasets'; -- Атрибут для наборов данных графика
|
||||||
SRESP_ATTR_CHART_DS_LABEL constant PKG_STD.TSTRING := 'label'; -- Атрибут для метки набора данных графика
|
SRESP_ATTR_CHART_DS_LABEL constant PKG_STD.TSTRING := 'label'; -- Атрибут для метки набора данных графика
|
||||||
SRESP_ATTR_CHART_DS_BR_CLR constant PKG_STD.TSTRING := 'borderColor'; -- Атрибут для цвета границы элемента набора данных графика
|
SRESP_ATTR_CHART_DS_BR_CLR constant PKG_STD.TSTRING := 'borderColor'; -- Атрибут для цвета границы элемента набора данных графика
|
||||||
SRESP_ATTR_CHART_DS_BG_CLR constant PKG_STD.TSTRING := 'backgroundColor'; -- Атрибут для цвета заливки элемента набора данных графика
|
SRESP_ATTR_CHART_DS_BG_CLR constant PKG_STD.TSTRING := 'backgroundColor'; -- Атрибут для цвета заливки элемента набора данных графика
|
||||||
SRESP_ATTR_CHART_DS_DATA constant PKG_STD.TSTRING := 'data'; -- Атрибут для коллекции значений элементов набора данных
|
SRESP_ATTR_CHART_DS_DATA constant PKG_STD.TSTRING := 'data'; -- Атрибут для коллекции значений элементов набора данных
|
||||||
SRESP_ATTR_CHART_DS_ITEMS constant PKG_STD.TSTRING := 'items'; -- Атрибут для коллекции элементов набора данных
|
SRESP_ATTR_CHART_DS_ITEMS constant PKG_STD.TSTRING := 'items'; -- Атрибут для коллекции элементов набора данных
|
||||||
SRESP_ATTR_CHART_DS_I_VAL constant PKG_STD.TSTRING := 'value'; -- Атрибут для значения элемента набора данных
|
SRESP_ATTR_CHART_DS_I_VAL constant PKG_STD.TSTRING := 'value'; -- Атрибут для значения элемента набора данных
|
||||||
|
|
||||||
/* Константы - атрибуты ответов (циклограмма) */
|
/* Константы - атрибуты ответов (циклограмма) */
|
||||||
SRESP_ATTR_CG_TASK_LINE constant PKG_STD.TSTRING := 'lineNumb'; -- Атрибут для номера строки (по оси Y)
|
SRESP_ATTR_CG_TASK_LINE constant PKG_STD.TSTRING := 'lineNumb'; -- Атрибут для номера строки (по оси Y)
|
||||||
SRESP_ATTR_CG_LINE_HEIGHT constant PKG_STD.TSTRING := 'lineHeight'; -- Атрибут для высоты строк
|
SRESP_ATTR_CG_LINE_HEIGHT constant PKG_STD.TSTRING := 'lineHeight'; -- Атрибут для высоты строк
|
||||||
|
|
||||||
/* Константы - параметры условий отбора */
|
/* Константы - параметры условий отбора */
|
||||||
SCOND_FROM_POSTFIX constant PKG_STD.TSTRING := 'From'; -- Постфикс наименования нижней границы условия отбора
|
SCOND_FROM_POSTFIX constant PKG_STD.TSTRING := 'From'; -- Постфикс наименования нижней границы условия отбора
|
||||||
SCOND_TO_POSTFIX constant PKG_STD.TSTRING := 'To'; -- Постфикс наименования верхней границы условия отбора
|
SCOND_TO_POSTFIX constant PKG_STD.TSTRING := 'To'; -- Постфикс наименования верхней границы условия отбора
|
||||||
|
|
||||||
/* Расчет диапаона выдаваемых записей */
|
/* Расчет диапаона выдаваемых записей */
|
||||||
procedure UTL_ROWS_LIMITS_CALC
|
procedure UTL_ROWS_LIMITS_CALC
|
||||||
@ -3124,93 +3069,5 @@ create or replace package body PKG_P8PANELS_VISUAL as
|
|||||||
P_EXCEPTION(0, PKG_STATE.SQL_ERRM());
|
P_EXCEPTION(0, PKG_STATE.SQL_ERRM());
|
||||||
end TCYCLOGRAM_TO_XML;
|
end TCYCLOGRAM_TO_XML;
|
||||||
|
|
||||||
/* Формирование индикатора */
|
|
||||||
function TINDICATOR_MAKE
|
|
||||||
(
|
|
||||||
SCAPTION in varchar2, -- Подпись
|
|
||||||
SVALUE in varchar2, -- Значение
|
|
||||||
SICON in varchar2 := null, -- Иконка (код шрифта "Google Material Icons" - https://fonts.google.com/icons?icon.set=Material+Icons)
|
|
||||||
SSTATE in varchar2 := SINDICATOR_STATE_UNDEFINED, -- Состояние (см. константы SINDICATOR_STATE_*)
|
|
||||||
BSQUARE in boolean := false, -- Скруглять углы
|
|
||||||
NELEVATION in number := 3, -- Высота парения (от 0 до 24, игнорируется для SINDICATOR_VARIANT_OUTLINED)
|
|
||||||
SVARIANT in varchar2 := SINDICATOR_VARIANT_ELEVATION, -- Вариант исполнения (см. константы SINDICATOR_VARIANT_*)
|
|
||||||
SHINT in varchar2 := null, -- Подсказка
|
|
||||||
SBACKGROUND_COLOR in varchar2 := null, -- Цвет заливки (HTML-код, null - использовать цвет по умолчанию для состояния)
|
|
||||||
SCOLOR in varchar2 := null -- Цвет шрифта и иконки (HTML-код, null - использовать цвет по умолчанию для состояния)
|
|
||||||
) return TINDICATOR -- Результат работы
|
|
||||||
is
|
|
||||||
RRES TINDICATOR; -- Буфер для результата
|
|
||||||
begin
|
|
||||||
/* Проверим параметры */
|
|
||||||
if ((SSTATE is not null) and
|
|
||||||
(SSTATE not in (SINDICATOR_STATE_UNDEFINED, SINDICATOR_STATE_OK, SINDICATOR_STATE_ERR, SINDICATOR_STATE_WARN))) then
|
|
||||||
P_EXCEPTION(0, 'Некорректно указано значение "Состояние".');
|
|
||||||
end if;
|
|
||||||
if ((NELEVATION is not null) and ((NELEVATION <> TRUNC(NELEVATION)) or (NELEVATION < 0) or (NELEVATION > 24))) then
|
|
||||||
P_EXCEPTION(0,
|
|
||||||
'Некорректно указано значение "Высота парения" (ожидается целое число от 0 до 24).');
|
|
||||||
end if;
|
|
||||||
if ((SVARIANT is not null) and (SVARIANT not in (SINDICATOR_VARIANT_ELEVATION, SINDICATOR_VARIANT_OUTLINED))) then
|
|
||||||
P_EXCEPTION(0, 'Некорректно указано значение "Вариант исполнения".');
|
|
||||||
end if;
|
|
||||||
/* Формируем объект */
|
|
||||||
RRES.SCAPTION := SCAPTION;
|
|
||||||
RRES.SVALUE := SVALUE;
|
|
||||||
RRES.SICON := SICON;
|
|
||||||
RRES.SSTATE := COALESCE(SSTATE, SINDICATOR_STATE_UNDEFINED);
|
|
||||||
RRES.BSQUARE := BSQUARE;
|
|
||||||
RRES.NELEVATION := COALESCE(NELEVATION, 3);
|
|
||||||
RRES.SVARIANT := COALESCE(SVARIANT, SINDICATOR_VARIANT_ELEVATION);
|
|
||||||
RRES.SHINT := SHINT;
|
|
||||||
RRES.SBACKGROUND_COLOR := SBACKGROUND_COLOR;
|
|
||||||
RRES.SCOLOR := SCOLOR;
|
|
||||||
/* Возвращаем результат */
|
|
||||||
return RRES;
|
|
||||||
end TINDICATOR_MAKE;
|
|
||||||
|
|
||||||
/* Сериализация индикатора */
|
|
||||||
function TINDICATOR_TO_XML
|
|
||||||
(
|
|
||||||
RINDICATOR in TINDICATOR -- Описание индикатора
|
|
||||||
) return clob -- XML-описание
|
|
||||||
is
|
|
||||||
CRES clob; -- Буфер для результата
|
|
||||||
begin
|
|
||||||
/* Начинаем формирование XML */
|
|
||||||
PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_);
|
|
||||||
/* Открываем корень */
|
|
||||||
PKG_XFAST.DOWN_NODE(SNAME => SRESP_TAG_XDATA);
|
|
||||||
/* Открываем индикатор */
|
|
||||||
PKG_XFAST.DOWN_NODE(SNAME => SRESP_TAG_XINDICATOR);
|
|
||||||
/* Формируем атрибуты */
|
|
||||||
PKG_XFAST.ATTR(SNAME => SRESP_ATTR_CAPTION, SVALUE => RINDICATOR.SCAPTION);
|
|
||||||
PKG_XFAST.ATTR(SNAME => SRESP_ATTR_VALUE, SVALUE => RINDICATOR.SVALUE);
|
|
||||||
PKG_XFAST.ATTR(SNAME => SRESP_ATTR_ICON, SVALUE => RINDICATOR.SICON);
|
|
||||||
PKG_XFAST.ATTR(SNAME => SRESP_ATTR_STATE, SVALUE => RINDICATOR.SSTATE);
|
|
||||||
PKG_XFAST.ATTR(SNAME => SRESP_ATTR_SQUARE, BVALUE => RINDICATOR.BSQUARE);
|
|
||||||
PKG_XFAST.ATTR(SNAME => SRESP_ATTR_ELEVATION, NVALUE => RINDICATOR.NELEVATION);
|
|
||||||
PKG_XFAST.ATTR(SNAME => SRESP_ATTR_VARIANT, SVALUE => RINDICATOR.SVARIANT);
|
|
||||||
PKG_XFAST.ATTR(SNAME => SRESP_ATTR_HINT, SVALUE => RINDICATOR.SHINT);
|
|
||||||
PKG_XFAST.ATTR(SNAME => SRESP_ATTR_BG_COLOR, SVALUE => RINDICATOR.SBACKGROUND_COLOR);
|
|
||||||
PKG_XFAST.ATTR(SNAME => SRESP_ATTR_COLOR, SVALUE => RINDICATOR.SCOLOR);
|
|
||||||
/* Закрываем индикатор */
|
|
||||||
PKG_XFAST.UP();
|
|
||||||
/* Закрываем корень */
|
|
||||||
PKG_XFAST.UP();
|
|
||||||
/* Сериализуем */
|
|
||||||
CRES := PKG_XFAST.SERIALIZE_TO_CLOB();
|
|
||||||
/* Завершаем формирование XML */
|
|
||||||
PKG_XFAST.EPILOGUE();
|
|
||||||
/* Возвращаем полученное */
|
|
||||||
return CRES;
|
|
||||||
exception
|
|
||||||
when others then
|
|
||||||
/* Завершаем формирование XML */
|
|
||||||
PKG_XFAST.EPILOGUE();
|
|
||||||
/* Вернем ошибку */
|
|
||||||
PKG_STATE.DIAGNOSTICS_STACKED();
|
|
||||||
P_EXCEPTION(0, PKG_STATE.SQL_ERRM());
|
|
||||||
end TINDICATOR_TO_XML;
|
|
||||||
|
|
||||||
end PKG_P8PANELS_VISUAL;
|
end PKG_P8PANELS_VISUAL;
|
||||||
/
|
/
|
||||||
|
8327
dist/p8-panels.js
vendored
8327
dist/p8-panels.js
vendored
File diff suppressed because one or more lines are too long
Binary file not shown.
BIN
docs/img/74.png
BIN
docs/img/74.png
Binary file not shown.
Before Width: | Height: | Size: 145 KiB |
BIN
img/prj_info.jpg
BIN
img/prj_info.jpg
Binary file not shown.
Before Width: | Height: | Size: 134 KiB |
@ -8,7 +8,6 @@
|
|||||||
<MenuItem parent="{BA073333-DFBC-4BA3-8EA7-172F3F6B4FEE}" name="ShowPrjPanelFin" caption="Экономика проектов" panelName="PrjFin"/>
|
<MenuItem parent="{BA073333-DFBC-4BA3-8EA7-172F3F6B4FEE}" name="ShowPrjPanelFin" caption="Экономика проектов" panelName="PrjFin"/>
|
||||||
<MenuItem parent="{BA073333-DFBC-4BA3-8EA7-172F3F6B4FEE}" name="ShowPrjPanelJob" caption="Работы проектов" panelName="PrjJobs"/>
|
<MenuItem parent="{BA073333-DFBC-4BA3-8EA7-172F3F6B4FEE}" name="ShowPrjPanelJob" caption="Работы проектов" panelName="PrjJobs"/>
|
||||||
<MenuItem parent="{BA073333-DFBC-4BA3-8EA7-172F3F6B4FEE}" name="ShowPrjPanelGraph" caption="Графики проектов" panelName="PrjGraph"/>
|
<MenuItem parent="{BA073333-DFBC-4BA3-8EA7-172F3F6B4FEE}" name="ShowPrjPanelGraph" caption="Графики проектов" panelName="PrjGraph"/>
|
||||||
<MenuItem parent="{BA073333-DFBC-4BA3-8EA7-172F3F6B4FEE}" name="ShowPrjPanelInfo" caption="Информация о проектах" panelName="PrjInfo"/>
|
|
||||||
<MenuItem parent="{BA073333-DFBC-4BA3-8EA7-172F3F6B4FEE}" name="ShowPrjPanelHelp" caption="Инструкции ПУП" panelName="PrjHelp"/>
|
<MenuItem parent="{BA073333-DFBC-4BA3-8EA7-172F3F6B4FEE}" name="ShowPrjPanelHelp" caption="Инструкции ПУП" panelName="PrjHelp"/>
|
||||||
</App>
|
</App>
|
||||||
<App name="EquipSrv">
|
<App name="EquipSrv">
|
||||||
@ -60,16 +59,6 @@
|
|||||||
icon="insights"
|
icon="insights"
|
||||||
showInPanelsList="true"
|
showInPanelsList="true"
|
||||||
preview="./img/prj_graph.jpg"/>
|
preview="./img/prj_graph.jpg"/>
|
||||||
<Panel
|
|
||||||
name="PrjInfo"
|
|
||||||
group="Планирование и учёт в проектах"
|
|
||||||
caption="Информация о проектах"
|
|
||||||
desc="Информация о проектах"
|
|
||||||
url="prj_info"
|
|
||||||
path="prj_info"
|
|
||||||
icon="featured_play_list"
|
|
||||||
showInPanelsList="true"
|
|
||||||
preview="./img/prj_info.jpg"/>
|
|
||||||
<Panel
|
<Panel
|
||||||
name="PrjHelp"
|
name="PrjHelp"
|
||||||
group="Планирование и учёт в проектах"
|
group="Планирование и учёт в проектах"
|
||||||
@ -120,16 +109,6 @@
|
|||||||
icon="psychology"
|
icon="psychology"
|
||||||
showInPanelsList="true"
|
showInPanelsList="true"
|
||||||
preview="./img/mech_rec_cost_jobs_manage.jpg"/>
|
preview="./img/mech_rec_cost_jobs_manage.jpg"/>
|
||||||
<Panel
|
|
||||||
name="MechRecCostJobsManageMP"
|
|
||||||
group="Планирование и учёт в дискретном производстве"
|
|
||||||
caption="Выдача сменного задания на участок"
|
|
||||||
desc="Управление составом сменных заданий трудового ресурса"
|
|
||||||
url="mech_rec_cost_jobs_manage_mp"
|
|
||||||
path="mech_rec_cost_jobs_manage_mp"
|
|
||||||
icon=""
|
|
||||||
showInPanelsList="false"
|
|
||||||
preview=""/>
|
|
||||||
<Panel
|
<Panel
|
||||||
name="MechRecDeptCostJobs"
|
name="MechRecDeptCostJobs"
|
||||||
group="Планирование и учёт в дискретном производстве"
|
group="Планирование и учёт в дискретном производстве"
|
||||||
|
743
package-lock.json
generated
743
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -27,7 +27,6 @@
|
|||||||
"@mui/x-tree-view": "^7.11.0",
|
"@mui/x-tree-view": "^7.11.0",
|
||||||
"babel-loader": "^9.1.3",
|
"babel-loader": "^9.1.3",
|
||||||
"chart.js": "^4.4.0",
|
"chart.js": "^4.4.0",
|
||||||
"css-loader": "^7.1.2",
|
|
||||||
"dayjs": "^1.11.9",
|
"dayjs": "^1.11.9",
|
||||||
"eslint": "^8.46.0",
|
"eslint": "^8.46.0",
|
||||||
"eslint-plugin-react": "^7.33.1",
|
"eslint-plugin-react": "^7.33.1",
|
||||||
@ -36,11 +35,8 @@
|
|||||||
"file-loader": "^6.2.0",
|
"file-loader": "^6.2.0",
|
||||||
"query-string": "^8.1.0",
|
"query-string": "^8.1.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-beautiful-dnd": "^13.1.1",
|
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-grid-layout": "^1.5.1",
|
|
||||||
"react-router-dom": "^6.15.0",
|
"react-router-dom": "^6.15.0",
|
||||||
"style-loader": "^4.0.0",
|
|
||||||
"webpack": "^5.88.2",
|
"webpack": "^5.88.2",
|
||||||
"webpack-cli": "^5.1.4"
|
"webpack-cli": "^5.1.4"
|
||||||
}
|
}
|
||||||
|
@ -48,8 +48,7 @@ module.exports = {
|
|||||||
options: {
|
options: {
|
||||||
name: "[path][name].[hash].[ext]"
|
name: "[path][name].[hash].[ext]"
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
{ test: /\.css$/, use: ["style-loader", "css-loader"] }
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user