forked from CITKParus/P8-Panels
ЦИТК-1042 - API для работы с настройками панелей
This commit is contained in:
parent
35ff204c07
commit
dd338e38a1
394
README.md
394
README.md
@ -111,9 +111,10 @@ git clone https://git.citpb.ru/CITKParus/P8-Panels.git
|
||||
|
||||
При компиляции учитывайте следующее:
|
||||
|
||||
- объекты `PKG_P8PANELS`, `PKG_P8PANELS_BASE`, `PKG_P8PANELS_VISUAL` и `P8PNL_SELECTLIST` - обязательны к компиляции. **Это ядро серверной части расширения. Без этих объектов его работа невозможна. Все остальные, перечисленные ниже объекты серверной части - прикладные, не обязательны к компиляции и на функционирование непосредственно фреймворка влияния не оказывают.**
|
||||
- объекты `PKG_P8PANELS`, `PKG_P8PANELS_BASE`, `PKG_P8PANELS_VISUAL`, `PKG_P8PANELS_SETTINGS`, `P8PNL_SETTINGS` и `P8PNL_SELECTLIST` и - обязательны к компиляции. **Это ядро серверной части расширения. Без этих объектов его работа невозможна. Все остальные, перечисленные ниже объекты серверной части - прикладные, не обязательны к компиляции и на функционирование непосредственно фреймворка влияния не оказывают.**
|
||||
- объекты `PKG_P8PANELS_SAMPLES`, `P8PNL_SMPL_CYCLOGRAM` и `P8PNL_SMPL_GANTT` - обеспечивают работу панели "Samples" ("Примеры для разработчиков"). Это абстрактный набор примеров применения фреймворка, не привязанный жестко к комплектации Системы. Должен откомпилироваться практически в любой комплектации Системы (единственное требование - наличие словаря "Контрагенты").
|
||||
- объекты `PKG_P8PANELS_PROJECTS`, `P8PNL_JB_JOBS`, `P8PNL_JB_JOBSPREV`, `P8PNL_JB_PERIODS`, `P8PNL_JB_PRJCTS` и `P8PNL_JB_PRMS` - могут быть откомпилированы в Системе, имеющей в комлектации приложения "Планирование и учёт в проектах" и "Управление закупками, складом и реализацией". Эти объекты отвечают за работу панелей группы "Планирование и учёт в проектах" ("PrjFin" - "Экономика проектов", "PrjJobs" - "Работы проектов", "PrjGraph" - "Графики проектов", "PrjInfo" - "Информация о проектах"). Компилируйте только если комлектация Системы включает указанные приложения.
|
||||
- объекты `I_P8PNL_*` - содержат индексы для загружаемых таблиц, **перед созданием индексов необходимо заменить в тексте файла шаблон `<ИМЯ_ИНДЕКСНОГО_ТАБЛИЧНОГО_ПРОСТРАНСТВА>` на имя индексного табличного пространства экземпляра БД**.
|
||||
- объект `PKG_P8PANELS_CLNTTSKBRD` - может быть откомпилирован в Системе, имеющей в комлектации приложение "Управление деловыми процессами". Этот объект отвечает за работу панелей группы "Управление деловыми процессами" ("ClntTaskBoard" - "Доски задач"). Компилируйте только если комлектация Системы включает указанное приложение.
|
||||
- объект `PKG_P8PANELS_MECHREC` - может быть откомпилирован в Системе, имеющей в комлектации приложение "Планирование и учёт в дискретном производстве". Этот объект отвечает за работу панелей группы "Планирование и учёт в дискретном производстве" ("MechRecCostProdPlans" - "Производственная программа", "MechRecDeptCostProdPlans" - "Производственный план цеха", "MechRecCostJobsManage" - "Выдача сменного задания", "MechRecCostJobsManageMP" - "Выдача сменного задания на участок", "MechRecDeptCostJobs" - "Загрузка цеха", "MechRecAssemblyMon" - "Мониторинг сборки изделий"). Компилируйте только если комлектация Системы включает указанное приложение.
|
||||
- объект `PKG_P8PANELS_EQUIPSRV` - может быть откомпилирован в Системе, имеющей в комлектации приложение "Управление техническим обслуживанием и ремонтами". Этот объект отвечает за работу панелей группы "Управление техническим обслуживанием и ремонтами" ("EqsPrfrm" - "Выполнение работ по ТОиР"). Компилируйте только если комлектация Системы включает указанное приложение.
|
||||
@ -986,6 +987,397 @@ const P8Online = ({ title }) => {
|
||||
|
||||
```
|
||||
|
||||
### API для взаимодействия с параметрами фреймворка
|
||||
|
||||
Для работы с параметрами панелей в составе расширения предусмотрен специальный API. Его подключение к компоненте панели осуществляется через контекст `SettingsCtx` ("app/context/settings.js"). Данный контекст обеспечивает инициализацию, считывание и обновление параметров различных видов (системные, панельные, пользовательские).
|
||||
|
||||
В состав API входят:
|
||||
|
||||
- `SETTINGS_KINDS` - объект с константами видов параметров
|
||||
- `initSettings` - функция, инициализация параметров панелей
|
||||
- `getSettings` - функция, считывание параметров
|
||||
- `putSettings` - функция, добавление/обновление параметров
|
||||
- `showSettingsDialog` - функция, отображение диалога настройки параметров
|
||||
|
||||
#### `SETTINGS_KINDS`
|
||||
|
||||
Объект, содержащий константы видов параметров:
|
||||
|
||||
```
|
||||
{
|
||||
SYSTEM: 0,
|
||||
PANEL: 1,
|
||||
USER: 2
|
||||
}
|
||||
```
|
||||
|
||||
- `SYSTEM` - Системные параметры, общие для всех панелей. Не требуют указания панели при работе.
|
||||
- `PANEL` - Параметры конкретной панели. Требуют указания панели (`panel`).
|
||||
- `USER` - Пользовательские параметры. Требуют указания панели (`panel`).
|
||||
|
||||
#### `async Object initSettings(Object)`
|
||||
|
||||
Инициализирует параметры панелей в БД Системы. Если параметры с указанными ключами уже существуют - будут перезаписаны.
|
||||
|
||||
**Входные параметры:**
|
||||
|
||||
```
|
||||
{
|
||||
kind,
|
||||
settings,
|
||||
panel = null,
|
||||
full = false,
|
||||
loader = true
|
||||
}
|
||||
```
|
||||
|
||||
`kind` - обязательный, число, вид параметров (`SETTINGS_KINDS.SYSTEM`, `SETTINGS_KINDS.PANEL` или `SETTINGS_KINDS.USER`)\
|
||||
`settings` - обязательный, объект, параметры для инициализации вида `{ "МНЕМОКОД_ПАРАМЕТРА": { name: <НАИМЕНОВАНИЕ>, dataType: <ТИП_ДАННЫХ>, value: <ЗНАЧЕНИЕ>, desc: <ОПИСАНИЕ>, options: {...} }, ... }`\
|
||||
`panel` - необязательный, строка, имя панели (требуется для `PANEL` и `USER`, игнорируется для `SYSTEM`)\
|
||||
`full` - необязательный, логический, признак возврата полного ответа сервера (по умолчанию `false`)\
|
||||
`loader` - необязательный, логический, признак отображения типового индикатора процесса
|
||||
|
||||
**Результат:** объект с инициализированными параметрами (если `full = false`) или полный типовой ответ сервера (если `full = true`).
|
||||
|
||||
**Особенности:**
|
||||
|
||||
- Для вида `SYSTEM` параметр `panel` не требуется и игнорируется
|
||||
- Для видов `PANEL` и `USER` параметр `panel` обязателен, иначе будет выведена ошибка
|
||||
- Параметр `options` является необязательным, но если указан, то должен быть объект вида `{<КЛЮЧ>:<ЗНАЧЕНИЕ>, ...}`. Параметры поддерживают привязку к разделам системы, для этого требуется указать следующие `options` `{unitcode: <МНЕМОКОД_РАЗДЕЛА>, showMethod: <МЕТОД_РАЗДЕЛА>, inParameter: <ИМЯ_ВХОДНОГО_ПАРАМЕТРА>, outParameter: <ИМЯ_ВЫХОДНОГО_ПАРАМЕТРА>}`, все параметры являются обязательными
|
||||
- Функция выполняет обновление существующих параметров, обновляемые значения: `options`, `name`, `desc` и анализирует необходимость обновления значения, если `dataType` не соответствует инициализируемому - обновляет значение `value`, в ином случае значение не обновляется
|
||||
|
||||
**Пример:**
|
||||
|
||||
```
|
||||
import React, { useContext } from "react";
|
||||
import { SettingsCtx } from "../../context/settings";
|
||||
import { P8P_DATA_TYPES } from "../../core/data_types";
|
||||
|
||||
//Инициализируемые параметры
|
||||
const INIT_SETTINGS = {
|
||||
INFO: {
|
||||
name: "Дополнительная информация",
|
||||
dataType: P8P_DATA_TYPES.STR,
|
||||
value: "Необходима проверка документов.",
|
||||
desc: "Дополнительная информация для контрагента"
|
||||
},
|
||||
COUNT_DOCS: {
|
||||
name: "Количество отображаемых документов",
|
||||
dataType: P8P_DATA_TYPES.NUMB,
|
||||
value: 10,
|
||||
desc: ""
|
||||
},
|
||||
START_DATE: {
|
||||
name: "Дата начала",
|
||||
dataType: P8P_DATA_TYPES.DATE,
|
||||
value: "2026-02-15",
|
||||
desc: "Дата начала по умолчанию"
|
||||
},
|
||||
AGENT: {
|
||||
name: "Выбранный контрагент",
|
||||
dataType: P8P_DATA_TYPES.STR,
|
||||
value: "",
|
||||
desc: "Мнемокод выбранного контрагента",
|
||||
options: {
|
||||
unitcode: "AGNLIST",
|
||||
showMethod: "main",
|
||||
inParameter: "in_AGNABBR",
|
||||
outParameter: "out_AGNABBR"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const MyPanel = () => {
|
||||
//Собственное состояние - параметры
|
||||
const [state, setState] = useState({});
|
||||
//Подключение к контексту параметров
|
||||
const { initSettings, SETTINGS_KINDS } = useContext(SettingsCtx);
|
||||
|
||||
//Инициализация параметров
|
||||
const initializeSettings = async () => {
|
||||
// Инициализация пользовательских параметров
|
||||
const res = await initSettings({
|
||||
kind: SETTINGS_KINDS.USER,
|
||||
panel: "PanelName",
|
||||
settings: INIT_SETTINGS
|
||||
});
|
||||
//Устанавливаем параметры
|
||||
setState({ ...res });
|
||||
};
|
||||
|
||||
return <div>...</div>;
|
||||
};
|
||||
```
|
||||
|
||||
#### `async Object getSettings(Object)`
|
||||
|
||||
Считывает параметры панелей из БД Системы.
|
||||
|
||||
**Входные параметры:**
|
||||
|
||||
```
|
||||
{
|
||||
kind,
|
||||
code = null,
|
||||
panel = null,
|
||||
full = false,
|
||||
loader = true
|
||||
}
|
||||
```
|
||||
|
||||
`kind` - обязательный, число, вид параметров (`SETTINGS_KINDS.SYSTEM`, `SETTINGS_KINDS.PANEL` или `SETTINGS_KINDS.USER`)\
|
||||
`code` - необязательный, строка, мнемокод параметра для отбора, при пустом значении считывает все доступные параметры\
|
||||
`panel` - необязательный, строка, имя панели (требуется для `PANEL` и `USER`, игнорируется для `SYSTEM`)\
|
||||
`full` - необязательный, логический, признак возврата полного ответа сервера с информацией о параметре (по умолчанию `false`)\
|
||||
`loader` - необязательный, логический, признак отображения типового индикатора процесса
|
||||
|
||||
**Результат:** объект с считанными параметрами вида `{ <МНЕМОКОД_ПАРАМЕТРА>: <ЗНАЧЕНИЕ>, ... }` (если `full = false`) или полный ответ сервера (если `full = true`).
|
||||
|
||||
**Особенности:**
|
||||
|
||||
- Для вида `SYSTEM` параметр `panel` не требуется и игнорируется
|
||||
- Для видов `PANEL` и `USER` параметр `panel` рекомендуется указать для получения параметров конкретной панели
|
||||
- Возвращает только значения параметров, без метаданных (если `full = false`)
|
||||
|
||||
**Пример:**
|
||||
|
||||
```
|
||||
import React, { useContext, useState, useEffect } from "react";
|
||||
import { SettingsCtx } from "../../context/settings";
|
||||
|
||||
const MyPanel = () => {
|
||||
//Собственное состояние - параметры
|
||||
const [state, setState] = useState({});
|
||||
//Подключение к контексту параметров
|
||||
const { getSettings, SETTINGS_KINDS } = useContext(SettingsCtx);
|
||||
|
||||
//Загрузка параметров пользователя
|
||||
const handleLoadSettings = async () => {
|
||||
// Считывание параметров панели
|
||||
const settings = await getSettings({
|
||||
kind: SETTINGS_KINDS.USER,
|
||||
panel: "PanelName"
|
||||
});
|
||||
//Устанавливаем параметры
|
||||
setState({ ...settings });
|
||||
};
|
||||
|
||||
return <div>...</div>;
|
||||
};
|
||||
```
|
||||
|
||||
#### `async Object putSettings(Object)`
|
||||
|
||||
Добавляет или обновляет параметры панелей в БД Системы.
|
||||
|
||||
**Входные параметры:**
|
||||
|
||||
```
|
||||
{
|
||||
kind,
|
||||
settings,
|
||||
panel = null,
|
||||
loader = true
|
||||
}
|
||||
```
|
||||
|
||||
`kind` - обязательный, число, вид параметров (`SETTINGS_KINDS.SYSTEM`, `SETTINGS_KINDS.PANEL` или `SETTINGS_KINDS.USER`)\
|
||||
`settings` - обязательный, объект, параметры для добавления/обновления вида `{ <МНЕМОКОД_ПАРАМЕТРА>: { name: <НАИМЕНОВАНИЕ>, dataType: <ТИП_ДАННЫХ>, value: <ЗНАЧЕНИЕ>, desc: <ОПИСАНИЕ>, options: {...} }, ... }`\
|
||||
`panel` - необязательный, строка, имя панели (требуется для `PANEL` и `USER`, игнорируется для `SYSTEM`)\
|
||||
`loader` - необязательный, логический, признак отображения типового индикатора процесса
|
||||
|
||||
**Результат:** функция не возвращает значимого результата.
|
||||
|
||||
**Особенности:**
|
||||
|
||||
- Для вида `SYSTEM` параметр `panel` не требуется и игнорируется
|
||||
- Для видов `PANEL` и `USER` параметр `panel` обязателен, иначе будет выведена ошибка и функция вернётся без выполнения
|
||||
- При обновлении параметра обновляются следующие значения: `options`, `name`, `desc`, `dataType`, `value`
|
||||
|
||||
**Пример:**
|
||||
|
||||
```
|
||||
import React, { useContext } from "react";
|
||||
import { SettingsCtx } from "../../context/settings";
|
||||
import { formatDateJSONDateOnly } from "../../core/utils";
|
||||
|
||||
//Инициализируемые параметры
|
||||
const PUT_SETTINGS = {
|
||||
LAST_UPDATE: {
|
||||
name: "Дата последнего обновления",
|
||||
dataType: "DATE",
|
||||
value: formatDateJSONDateOnly(new Date()),
|
||||
desc: ""
|
||||
},
|
||||
COUNT_DOCS: {
|
||||
name: "Количество отображаемых документов",
|
||||
dataType: P8P_DATA_TYPES.NUMB,
|
||||
value: 15,
|
||||
desc: ""
|
||||
}
|
||||
};
|
||||
|
||||
const MyPanel = () => {
|
||||
//Собственное состояние - параметры
|
||||
const [state, setState] = useState({});
|
||||
//Подключение к контексту параметров
|
||||
const { putSettings, SETTINGS_KINDS } = useContext(SettingsCtx);
|
||||
|
||||
//Добавление/обновление параметров пользователя
|
||||
const handlePutSettings = async () => {
|
||||
//Обновление системных параметров
|
||||
await putSettings({
|
||||
kind: SETTINGS_KINDS.USER,
|
||||
panel: "PanelName",
|
||||
settings: PUT_SETTINGS
|
||||
});
|
||||
};
|
||||
|
||||
return <div>...</div>;
|
||||
};
|
||||
```
|
||||
|
||||
#### `async undefined showSettingsDialog(Object)`
|
||||
|
||||
Отображает диалог настройки параметров пользователя. Диалог позволяет пользователю изменить значения параметров.
|
||||
|
||||
**Входные параметры:**
|
||||
|
||||
```
|
||||
{
|
||||
panel = null,
|
||||
onOk = null,
|
||||
onCancel = null
|
||||
}
|
||||
```
|
||||
|
||||
`panel` - необязательный, строка, имя панели (если не указан - открываются все параметры с разделением по панелям)\
|
||||
`onOk` - необязательный, функция вида `f({ settings })`, будет вызвана при нажатии "ОК" в диалоге. В параметр `settings` передаются изменённые параметры\
|
||||
`onCancel` - необязательный, функция вида `f()`, будет вызвана при нажатии "Отмена" или закрытии диалога
|
||||
|
||||
**Результат:** функция возвращает следующие объекты:\
|
||||
|
||||
- `settings` - объект, содержащий информацию о всех параметрах с учетом изменений
|
||||
- `isChanged` - признак изменений данных (`true` - параметры были изменены, `false` - изменений не было)
|
||||
|
||||
**Особенности:**
|
||||
|
||||
- Диалог реализован компонентом `P8PSettingsDialog` ("app/components/p8p_settings_dialog.js")
|
||||
- При нажатии "ОК" параметры автоматически сохраняются через `putSettings`
|
||||
- При нажатии "Отмена" параметры не сохраняются
|
||||
- Диалог закрывается автоматически после обработки нажатий
|
||||
|
||||
**Пример:**
|
||||
|
||||
```
|
||||
import React, { useContext } from "react";
|
||||
import { SettingsCtx } from "../../context/settings";
|
||||
import { Button } from "@mui/material";
|
||||
|
||||
const MyPanel = () => {
|
||||
//Подключение к контексту параметров
|
||||
const { showSettingsDialog, SETTINGS_KINDS } = useContext(SettingsCtx);
|
||||
|
||||
//При отображении параметров панели
|
||||
const handleSettingsDialog = async () => {
|
||||
//Отображаем параметры панели
|
||||
showSettingsDialog({
|
||||
panel: "PanelName",
|
||||
onOk: ({ settings }) => {
|
||||
console.log("Параметры сохранены:", { settings, isChanged });
|
||||
},
|
||||
onCancel: () => {
|
||||
console.log("Изменения отменены");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Button variant="contained" onClick={handleSettingsDialog}>
|
||||
Настроить параметры
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
#### Формат параметров
|
||||
|
||||
Параметры имеют следующую структуру:
|
||||
|
||||
```
|
||||
{
|
||||
"МНЕМОКОД_ПАРАМЕТРА": {
|
||||
name: <НАИМЕНОВАНИЕ>,
|
||||
dataType: <ТИП_ДАННЫХ>,
|
||||
value: <ЗНАЧЕНИЕ>,
|
||||
desc: <ОПИСАНИЕ>,
|
||||
options: { ... } // необязательный
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Поля параметра:**
|
||||
|
||||
- `name` - обязательный, строка, наименование параметра
|
||||
- `dataType` - обязательный, строка, тип данных параметра (`STR` - строка, `NUMB` - число, `DATE` - дата)
|
||||
- `value` - необязательный, значение параметра (тип должен соответствовать `dataType`)
|
||||
- `desc` - необязательный, строка, описание параметра (может использоваться в диалоге настроек как подсказка)
|
||||
- `options` - необязательный, объект, дополнительные настройки параметра
|
||||
|
||||
**Настройки привязки к разделу (`options`):**
|
||||
|
||||
Параметр может быть привязан к разделу Системы для автоматического заполнения значения через словарь. Для этого в `options` указываются:
|
||||
|
||||
- `unitcode` - строка, код раздела Системы (например, `"AGNLIST"` для словаря "Контрагенты")
|
||||
- `showMethod` - строка, метод вызова раздела
|
||||
- `inParameter` - строка, имя входного параметра метода вызова для отбора/позиционирования (например, `"in_AGNABBR"`)
|
||||
- `outParameter` - строка, имя выходного параметра метода вызова для получения значения (например, `"out_AGNABBR"`)
|
||||
|
||||
**Пример параметра с привязкой к словарю:**
|
||||
|
||||
```javascript
|
||||
{
|
||||
AGENT: {
|
||||
name: "Выбранный контрагент",
|
||||
dataType: "STR",
|
||||
value: "",
|
||||
desc: "Мнемокод контрагента для отчёта",
|
||||
options: {
|
||||
unitcode: "AGNLIST",
|
||||
showMethod: "main",
|
||||
inParameter: "in_AGNABBR",
|
||||
outParameter: "out_AGNABBR"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
При использовании такого параметра в диалоге настроек (`showSettingsDialog`) будет автоматически предложен выбор из словаря "Контрагенты", и значение параметра будет заполнено после выбора записи.
|
||||
|
||||
**Пример:**
|
||||
|
||||
Полный актуальный исходный код примера можно увидеть в "app/panels/samples/settings.js" данного репозитория.
|
||||
|
||||
#### Примечания
|
||||
|
||||
1. **Наименование панели:** при использовании параметра `panel` в него необходимо передавать соответствующее наименование панели из `p8panels.config`.
|
||||
|
||||
2. **Формат параметров:** каждый параметр должен быть объектом со структурой `{ name, dataType, value, desc, options }`.
|
||||
|
||||
3. **Типы данных (`dataType`):** поддерживаются следующие типы:
|
||||
- `STR` - строка
|
||||
- `NUMB` - число
|
||||
- `DATE` - дата (в формате ISO 8601, например `"2024-01-15"`)
|
||||
- `BOOL` - логическое (`true`/`false`)
|
||||
|
||||
4. **Имена параметров:** мнемокоды параметров должны быть уникальными в рамках одной панели/системы.
|
||||
|
||||
5. **Привязка к разделу:** для параметров, которые должны заполняться через выбор из словаря/раздела Системы, обязательно заполняется `options` с полями `unitcode`, `showMethod`, `inParameter`, `outParameter`.
|
||||
|
||||
6. **Метаданные:** параметр `full = true` возвращает не только значения параметров, но и все метаданные (`name`, `dataType`, `desc`, `options`), что полезно для построения динамических форм редактирования.
|
||||
|
||||
### Компоненты пользовательского интерфейса
|
||||
|
||||
#### Типовые интерфейсные примитивы
|
||||
|
||||
@ -33,6 +33,7 @@ export const TEXTS = {
|
||||
export const BUTTONS = {
|
||||
NAVIGATE_HOME: "Домой", //Переход к домашней странице
|
||||
NAVIGATE_BACK: "Назад", //Возврат назад по навигации
|
||||
NAVIGATE_SETTINGS: "Параметры", //Переход к параметрам панелей
|
||||
NAVIGATE: "Перейти", //Переход к разделу/панели/адресу
|
||||
OK: "ОК", //Ок
|
||||
CANCEL: "Отмена", //Отмена
|
||||
|
||||
@ -11,6 +11,7 @@ import React, { useState, useContext, useEffect } from "react"; //Классы R
|
||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||
import { createHashRouter, RouterProvider, useRouteError } from "react-router-dom"; //Роутер
|
||||
import { ApplicationCtx } from "./context/application"; //Контекст приложения
|
||||
import { SettingsCtx } from "./context/settings"; //Контекст параметров
|
||||
import { NavigationContext, NavigationCtx, getRootLocation } from "./context/navigation"; //Контекст навигации
|
||||
import { P8PAppErrorPage } from "./components/p8p_app_error_page"; //Страница с ошибкой
|
||||
import { P8PAppWorkspace } from "./components/p8p_app_workspace"; //Рабочее пространство панели
|
||||
@ -89,6 +90,12 @@ const Workspace = ({ panels = [], selectedPanel, children } = {}) => {
|
||||
//Подключение к контексту приложения
|
||||
const { appState } = useContext(ApplicationCtx);
|
||||
|
||||
//Подключение к контексту параметров
|
||||
const { showSettingsDialog } = useContext(SettingsCtx);
|
||||
|
||||
//При открытии диалога параметров панели
|
||||
const handleSettingsDialog = panel => showSettingsDialog({ panel });
|
||||
|
||||
//Отработка действия навигации домой
|
||||
const handleHomeNavigate = () => navigateRoot();
|
||||
|
||||
@ -103,8 +110,10 @@ const Workspace = ({ panels = [], selectedPanel, children } = {}) => {
|
||||
selectedPanel={selectedPanel}
|
||||
caption={appState.appBarTitle}
|
||||
showAppBar={appState.appBarShow}
|
||||
showAppBarSettings={appState.appBarSettingsShow}
|
||||
onHomeNavigate={handleHomeNavigate}
|
||||
onItemNavigate={handleItemNavigate}
|
||||
onSettingsDialog={handleSettingsDialog}
|
||||
>
|
||||
{children}
|
||||
</P8PAppWorkspace>
|
||||
|
||||
@ -21,7 +21,8 @@ import {
|
||||
List,
|
||||
ListItemButton,
|
||||
ListItemIcon,
|
||||
ListItemText
|
||||
ListItemText,
|
||||
Divider
|
||||
} from "@mui/material"; //Интерфейсные компоненты
|
||||
import { P8PPanelsMenuDrawer, P8P_PANELS_MENU_PANEL_SHAPE } from "./p8p_panels_menu"; //Меню
|
||||
import { APP_STYLES } from "../../app.styles"; //Типовые стили
|
||||
@ -38,6 +39,9 @@ const STYLES = {
|
||||
DRAWER: { [`& .MuiDrawer-paper`]: { ...APP_STYLES.SCROLL } },
|
||||
ROOT_BOX: { display: "flex" },
|
||||
APP_BAR: { position: "fixed" },
|
||||
APP_BAR_MAIN_BOX: { display: "flex", width: "100vw", alignItems: "center", justifyContent: "space-between" },
|
||||
APP_BAR_LEFT_SIDE: { display: "flex", alignItems: "center", justifyContent: "flex-start" },
|
||||
APP_BAR_RIGHT_SIDE: { display: "flex", alignItems: "center", justifyContent: "flex-end" },
|
||||
APP_BAR_BUTTON: { mr: 2 },
|
||||
MAIN: { flexGrow: 1 }
|
||||
};
|
||||
@ -53,10 +57,13 @@ const P8PAppWorkspace = ({
|
||||
selectedPanel,
|
||||
caption,
|
||||
showAppBar = true,
|
||||
showAppBarSettings = true,
|
||||
closeCaption,
|
||||
homeCaption,
|
||||
settingsCaption,
|
||||
onHomeNavigate,
|
||||
onItemNavigate
|
||||
onItemNavigate,
|
||||
onSettingsDialog
|
||||
} = {}) => {
|
||||
//Собственное состояния
|
||||
const [open, setOpen] = useState(false);
|
||||
@ -80,6 +87,9 @@ const P8PAppWorkspace = ({
|
||||
onItemNavigate ? onItemNavigate(panel) : null;
|
||||
};
|
||||
|
||||
//Отработка нажатия на элемент открытия параметров
|
||||
const handleSettingsDialog = (panel = null) => onSettingsDialog(panel);
|
||||
|
||||
//Генерация содержимого
|
||||
return (
|
||||
<Box sx={STYLES.ROOT_BOX}>
|
||||
@ -88,18 +98,35 @@ const P8PAppWorkspace = ({
|
||||
<CssBaseline />
|
||||
<AppBar sx={STYLES.APP_BAR}>
|
||||
<Toolbar>
|
||||
<IconButton
|
||||
color="inherit"
|
||||
aria-label="open drawer"
|
||||
onClick={open ? handleDrawerClose : handleDrawerOpen}
|
||||
edge="start"
|
||||
sx={STYLES.APP_BAR_BUTTON}
|
||||
>
|
||||
<Icon>{open ? "chevron_left" : "menu"}</Icon>
|
||||
</IconButton>
|
||||
<Typography variant="h6" noWrap component="div">
|
||||
{caption || selectedPanel?.caption}
|
||||
</Typography>
|
||||
<Box sx={STYLES.APP_BAR_MAIN_BOX}>
|
||||
<Box sx={STYLES.APP_BAR_LEFT_SIDE}>
|
||||
<IconButton
|
||||
color="inherit"
|
||||
aria-label="open drawer"
|
||||
onClick={open ? handleDrawerClose : handleDrawerOpen}
|
||||
edge="start"
|
||||
sx={STYLES.APP_BAR_BUTTON}
|
||||
>
|
||||
<Icon>{open ? "chevron_left" : "menu"}</Icon>
|
||||
</IconButton>
|
||||
<Typography variant="h6" noWrap component="div">
|
||||
{caption || selectedPanel?.caption}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={STYLES.APP_BAR_RIGHT_SIDE}>
|
||||
{showAppBarSettings && selectedPanel.showUserSettings ? (
|
||||
<IconButton
|
||||
color="inherit"
|
||||
aria-label="open drawer"
|
||||
onClick={() => handleSettingsDialog(selectedPanel.name)}
|
||||
edge="end"
|
||||
sx={STYLES.APP_BAR_BUTTON}
|
||||
>
|
||||
<Icon>settings</Icon>
|
||||
</IconButton>
|
||||
) : null}
|
||||
</Box>
|
||||
</Box>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
<Drawer anchor="left" open={open} onClose={handleDrawerClose} sx={STYLES.DRAWER}>
|
||||
@ -116,6 +143,13 @@ const P8PAppWorkspace = ({
|
||||
</ListItemIcon>
|
||||
<ListItemText primary={homeCaption} />
|
||||
</ListItemButton>
|
||||
<Divider component="li" />
|
||||
<ListItemButton onClick={() => handleSettingsDialog()}>
|
||||
<ListItemIcon>
|
||||
<Icon>settings</Icon>
|
||||
</ListItemIcon>
|
||||
<ListItemText primary={settingsCaption} />
|
||||
</ListItemButton>
|
||||
</List>
|
||||
<P8PPanelsMenuDrawer panels={panels} selectedPanel={selectedPanel} onItemNavigate={handleItemNavigate} />
|
||||
</Drawer>
|
||||
@ -136,10 +170,13 @@ P8PAppWorkspace.propTypes = {
|
||||
selectedPanel: P8P_PANELS_MENU_PANEL_SHAPE,
|
||||
caption: PropTypes.string,
|
||||
showAppBar: PropTypes.bool,
|
||||
showAppBarSettings: PropTypes.bool,
|
||||
closeCaption: PropTypes.string.isRequired,
|
||||
homeCaption: PropTypes.string.isRequired,
|
||||
settingsCaption: PropTypes.string.isRequired,
|
||||
onHomeNavigate: PropTypes.func,
|
||||
onItemNavigate: PropTypes.func
|
||||
onItemNavigate: PropTypes.func,
|
||||
onSettingsDialog: PropTypes.func
|
||||
};
|
||||
|
||||
//----------------
|
||||
|
||||
153
app/components/p8p_settings_dialog.js
Normal file
153
app/components/p8p_settings_dialog.js
Normal file
@ -0,0 +1,153 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга
|
||||
Компонент: Диалог отображения параметров
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import React, { useState, useContext, useMemo } from "react"; //Классы React
|
||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||
import { Stack, List, ListItem, ListItemButton, ListItemText, Typography, Box, Divider } from "@mui/material"; //Интерфейсные элементы
|
||||
import { P8PDialog, P8P_DIALOG_WIDTH } from "./p8p_dialog"; //Типовой диалог
|
||||
import { APP_STYLES } from "../../app.styles"; //Типовые стили
|
||||
import { P8PSettingsList } from "./p8p_settings_list"; //Список параметров
|
||||
import { ApplicationCtx } from "../context/application"; //Контекст приложения
|
||||
import { deepCopyObject, hasValue } from "../core/utils"; //Вспомогательные функции
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Стили
|
||||
const STYLES = {
|
||||
CONTAINER: { display: "flex", flexDirection: "row", alignItems: "flex-start" },
|
||||
BOX_PANELS: { width: "300px", height: "500px", overflow: "auto", ...APP_STYLES.SCROLL },
|
||||
BOX_SETTINGS: { width: "520px", height: "500px", overflow: "auto", ...APP_STYLES.SCROLL }
|
||||
};
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Диалог отображения параметров
|
||||
const P8PSettingsDialog = ({ settings, panel = null, onOk, onClose }) => {
|
||||
//Собственное состояние - параметры панелей
|
||||
const [panelSettings, setPanelSettings] = useState(settings);
|
||||
|
||||
//Собственное состояние - мнемокод выбранной панели
|
||||
const [selectedPanel, setSelectedPanel] = useState(panel);
|
||||
|
||||
//Собственное состояние - наличие отображаемых данных
|
||||
const isSettingsExists = useMemo(() => {
|
||||
return selectedPanel
|
||||
? panelSettings && Object.keys(panelSettings).length > 0 && Object.keys(panelSettings[selectedPanel]).length > 0
|
||||
: Object.keys(panelSettings).length > 0;
|
||||
}, [selectedPanel, panelSettings]);
|
||||
|
||||
//Подключение к контексту приложения
|
||||
const { appState } = useContext(ApplicationCtx);
|
||||
|
||||
//При нажатии на "ОК"
|
||||
const handleOk = () => onOk && onOk(panelSettings);
|
||||
|
||||
//При нажатии на "Отменить"
|
||||
const handleCancel = () => onClose && onClose();
|
||||
|
||||
//При изменении параметра
|
||||
const handleSettingChange = (settingCode, newSettings) => {
|
||||
//Определяем панели
|
||||
const newPanelSettings = deepCopyObject(panelSettings);
|
||||
//Изменяем настройку у панели
|
||||
newPanelSettings[selectedPanel][settingCode] = deepCopyObject(newSettings);
|
||||
//Обновляем панели
|
||||
setPanelSettings(newPanelSettings);
|
||||
};
|
||||
|
||||
//При нажатии на панель
|
||||
const handlePanelSelect = panelName => setSelectedPanel(selectedPanel === panelName ? null : panelName);
|
||||
|
||||
//Генерация области выбора панели
|
||||
const panelsListRender = () => {
|
||||
//Генерация содержимого
|
||||
return (
|
||||
<>
|
||||
<Box sx={STYLES.BOX_PANELS}>
|
||||
<List>
|
||||
{Object.keys(panelSettings).map((panel, i) => {
|
||||
//Считываем информацию о панели
|
||||
const panelInfo = appState.panels.find(appPanel => appPanel.name == panel);
|
||||
//Элемент панели
|
||||
return (
|
||||
<ListItem key={i}>
|
||||
<ListItemButton onClick={() => handlePanelSelect(panel)} selected={panel === selectedPanel}>
|
||||
<ListItemText
|
||||
primary={panelInfo.name}
|
||||
secondaryTypographyProps={{ component: "div" }}
|
||||
secondary={
|
||||
<Stack direction={"row"} justifyContent={"space-between"} gap={2}>
|
||||
<Typography
|
||||
variant={"caption"}
|
||||
noWrap={true}
|
||||
title={panelInfo.desc ? panelInfo.desc : "Описание отсутствует"}
|
||||
>{`${panelInfo.desc ? panelInfo.desc : "Описание отсутствует"}`}</Typography>
|
||||
</Stack>
|
||||
}
|
||||
/>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
</Box>
|
||||
<Divider orientation="vertical" flexItem />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
//Формирование представления
|
||||
return (
|
||||
<P8PDialog
|
||||
title={`Параметры ${panel ? `"${appState.panels.find(appPanel => appPanel.name == panel).caption}"` : "панелей"}`}
|
||||
width={P8P_DIALOG_WIDTH.LG}
|
||||
onCancel={handleCancel}
|
||||
onOk={handleOk}
|
||||
scrollContent={false}
|
||||
okDisabled={Object.keys(panelSettings).length === 0}
|
||||
>
|
||||
{isSettingsExists ? (
|
||||
<Box sx={STYLES.CONTAINER}>
|
||||
{!hasValue(panel) ? panelsListRender() : null}
|
||||
<Box sx={STYLES.BOX_SETTINGS}>
|
||||
{hasValue(selectedPanel) ? (
|
||||
<P8PSettingsList settings={panelSettings[selectedPanel]} onSettingChange={handleSettingChange} />
|
||||
) : (
|
||||
<Typography align="center" variant="subtitle1">
|
||||
Выберите панель для отображения параметров
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
) : (
|
||||
<Typography align="center" variant="subtitle1">
|
||||
Отсутствуют доступные параметры
|
||||
</Typography>
|
||||
)}
|
||||
</P8PDialog>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств компонента - Диалог отображения параметров
|
||||
P8PSettingsDialog.propTypes = {
|
||||
settings: PropTypes.object.isRequired,
|
||||
panel: PropTypes.string,
|
||||
onOk: PropTypes.func,
|
||||
onClose: PropTypes.func
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { P8PSettingsDialog };
|
||||
178
app/components/p8p_settings_list.js
Normal file
178
app/components/p8p_settings_list.js
Normal file
@ -0,0 +1,178 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга
|
||||
Компонент: Список параметров
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import React, { useState, useContext, useMemo } from "react"; //Классы React
|
||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||
import { Stack, List, ListItem, ListItemButton, ListItemText, Typography } from "@mui/material"; //Интерфейсные элементы
|
||||
import { P8PDialog } from "./p8p_dialog"; //Типовой диалог
|
||||
import { deepCopyObject } from "../core/utils"; //Вспомогательные функции
|
||||
import { ApplicationCtx } from "../context/application"; //Контекст приложения
|
||||
import { P8P_DATA_TYPES } from "../core/data_types"; //Типы данных
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Стили
|
||||
const STYLES = {
|
||||
LIST: { width: "510px", bgcolor: "background.paper", overflowY: "auto" },
|
||||
TYPOGRAPHY_VALUE: { maxWidth: "200px" },
|
||||
TEXT_FIELD_STR_VALUE: { minWidth: "400px" }
|
||||
};
|
||||
|
||||
//--------------------------------
|
||||
//Вспомогательные классы и функции
|
||||
//--------------------------------
|
||||
|
||||
//Диалог изменения параметра
|
||||
const P8PSettingChangeDialog = ({ initSetting, onSave, onClose }) => {
|
||||
//Собственное состояние - параметр
|
||||
const [setting, setSetting] = useState(deepCopyObject(initSetting));
|
||||
|
||||
//Собственное состояние - доступность выбора из словаря
|
||||
const isDictionary = useMemo(() => {
|
||||
//Проверяем наличие обязательных настроек
|
||||
return (
|
||||
setting.options &&
|
||||
setting.options?.unitcode &&
|
||||
setting.options?.showMethod &&
|
||||
setting.options?.inParameter &&
|
||||
setting.options?.outParameter
|
||||
);
|
||||
}, [setting.options]);
|
||||
|
||||
//Подключение к контексту приложения
|
||||
const { pOnlineShowDictionary } = useContext(ApplicationCtx);
|
||||
|
||||
//При закрытии диалога
|
||||
const handleClose = () => onClose && onClose();
|
||||
|
||||
//При нажатии "ОК"
|
||||
const handleOk = () => onSave && onSave(setting);
|
||||
|
||||
//При изменении значения параметра
|
||||
const handleSettingChange = (name, value) => {
|
||||
setSetting(pv => ({ ...pv, [name]: value }));
|
||||
};
|
||||
|
||||
//При выборе значения из справочника
|
||||
const handleDictionarySelect = (currentFormValues, setFormValues) => {
|
||||
//Открываем справочник с учетом параметров
|
||||
pOnlineShowDictionary({
|
||||
unitCode: initSetting.options.unitcode,
|
||||
showMethod: initSetting.options.showMethod,
|
||||
inputParameters: [{ name: initSetting.options.inParameter, value: currentFormValues.value }],
|
||||
callBack: res => {
|
||||
res.success === true ? setFormValues([{ name: "value", value: res.outParameters[initSetting.options.outParameter] }]) : null;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
//Формирование представления
|
||||
return (
|
||||
<P8PDialog
|
||||
title={setting.name}
|
||||
width="xs"
|
||||
fullWidth
|
||||
onCancel={handleClose}
|
||||
onOk={handleOk}
|
||||
inputs={[
|
||||
{
|
||||
name: "value",
|
||||
value: setting.value,
|
||||
label: "Значение",
|
||||
dictionary: isDictionary ? handleDictionarySelect : null,
|
||||
type: setting.dataType === P8P_DATA_TYPES.NUMB ? "number" : setting.dataType === P8P_DATA_TYPES.DATE ? "date" : "text"
|
||||
}
|
||||
]}
|
||||
onInputChange={handleSettingChange}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств компонента - Диалог изменения параметра
|
||||
P8PSettingChangeDialog.propTypes = {
|
||||
initSetting: PropTypes.object,
|
||||
onSave: PropTypes.func,
|
||||
onClose: PropTypes.func
|
||||
};
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Список параметров
|
||||
const P8PSettingsList = ({ settings, onSettingChange }) => {
|
||||
//Собственное состояние - изменяемый параметр
|
||||
const [changingSetting, setChangingSetting] = useState(null);
|
||||
|
||||
//При нажатии на параметр
|
||||
const handleSettingClick = settingCode => setChangingSetting(settingCode);
|
||||
|
||||
//При очистки изменяемого параметра
|
||||
const handleChangingSettingClear = () => setChangingSetting(null);
|
||||
|
||||
//При изменении значения параметра
|
||||
const handleSettingChange = newSetting => {
|
||||
//Вызываем изменение параметра
|
||||
onSettingChange && onSettingChange(changingSetting, newSetting);
|
||||
//Очищаем изменяемый параметр
|
||||
handleChangingSettingClear();
|
||||
};
|
||||
|
||||
//Формирование представления
|
||||
return (
|
||||
<>
|
||||
<List sx={STYLES.LIST}>
|
||||
{Object.keys(settings).map((setting, i) => {
|
||||
return (
|
||||
<ListItem key={i}>
|
||||
<ListItemButton onClick={() => handleSettingClick(setting)}>
|
||||
<ListItemText
|
||||
primary={settings[setting].name}
|
||||
secondaryTypographyProps={{ component: "div" }}
|
||||
secondary={
|
||||
<Stack direction={"row"} justifyContent={"space-between"} gap={2}>
|
||||
<Typography
|
||||
variant={"caption"}
|
||||
noWrap={true}
|
||||
title={settings[setting].desc ? settings[setting].desc : "Описание отсутствует"}
|
||||
>{`${settings[setting].desc ? settings[setting].desc : "Описание отсутствует"}`}</Typography>
|
||||
<Typography
|
||||
variant={"caption"}
|
||||
sx={STYLES.TYPOGRAPHY_VALUE}
|
||||
noWrap={true}
|
||||
title={settings[setting].value}
|
||||
>{`${settings[setting].value}`}</Typography>
|
||||
</Stack>
|
||||
}
|
||||
/>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
{changingSetting ? (
|
||||
<P8PSettingChangeDialog initSetting={settings[changingSetting]} onSave={handleSettingChange} onClose={handleChangingSettingClear} />
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств компонента - Список параметров
|
||||
P8PSettingsList.propTypes = {
|
||||
settings: PropTypes.object,
|
||||
onSettingChange: PropTypes.func
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { P8PSettingsList };
|
||||
@ -37,6 +37,7 @@ import {
|
||||
import { useTheme } from "@mui/material/styles"; //Взаимодействие со стилями MUI
|
||||
import { P8PAppInlineError, P8PHintDialog } from "./p8p_app_message"; //Встраиваемое сообщение об ошибке
|
||||
import { P8P_TABLE_AT, HEADER_INITIAL_STATE, hasValue, p8pTableReducer } from "./p8p_table_reducer"; //Редьюсер состояния
|
||||
import { P8P_DATA_TYPES } from "../core/data_types"; //Типы данных
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
@ -50,9 +51,9 @@ const P8P_TABLE_SIZE = {
|
||||
|
||||
//Типы данных
|
||||
const P8P_TABLE_DATA_TYPE = {
|
||||
STR: "STR",
|
||||
NUMB: "NUMB",
|
||||
DATE: "DATE"
|
||||
STR: P8P_DATA_TYPES.STR,
|
||||
NUMB: P8P_DATA_TYPES.NUMB,
|
||||
DATE: P8P_DATA_TYPES.DATE
|
||||
};
|
||||
|
||||
//Направления сортировки
|
||||
|
||||
@ -30,7 +30,8 @@ const P8P_PANELS_MENU_GRID_CONFIG_PROPS = {
|
||||
//Конфигурируемые свойства "Рабочего пространства" (P8PAppWorkspace)
|
||||
const P8P_APP_WORKSPACE_CONFIG_PROPS = {
|
||||
closeCaption: BUTTONS.CLOSE,
|
||||
homeCaption: BUTTONS.NAVIGATE_HOME
|
||||
homeCaption: BUTTONS.NAVIGATE_HOME,
|
||||
settingsCaption: BUTTONS.NAVIGATE_SETTINGS
|
||||
};
|
||||
|
||||
//Конфигурируемые свойства "Таблицы" (P8PTable)
|
||||
|
||||
@ -39,7 +39,7 @@ export const ApplicationContext = ({ errors, displaySizeGetter, guidGenerator, c
|
||||
const [state, dispatch] = useReducer(applicationReducer, INITIAL_STATE(displaySizeGetter));
|
||||
|
||||
//Подключение к контексту взаимодействия с сервером
|
||||
const { getConfig, getRespPayload } = useContext(BackEndCtx);
|
||||
const { getConfig, getRespPayload, executeStored } = useContext(BackEndCtx);
|
||||
|
||||
//Подключение к контексту отображения сообщений
|
||||
const { showMsgErr } = useContext(MessagingCtx);
|
||||
@ -54,7 +54,7 @@ export const ApplicationContext = ({ errors, displaySizeGetter, guidGenerator, c
|
||||
const setUrlBase = urlBase => dispatch({ type: APP_AT.SET_URL_BASE, payload: urlBase });
|
||||
|
||||
//Установка списка панелей
|
||||
const setPanels = panels => dispatch({ type: APP_AT.LOAD_PANELS, payload: panels });
|
||||
const setPanels = (panels, panelsSettings) => dispatch({ type: APP_AT.LOAD_PANELS, payload: { panels, panelsSettings } });
|
||||
|
||||
//Установка заголовка в шапке приложения
|
||||
const setAppBarTitle = useCallback(appBarTitle => dispatch({ type: APP_AT.SET_APP_BAR_TITLE, payload: appBarTitle }), []);
|
||||
@ -62,6 +62,12 @@ export const ApplicationContext = ({ errors, displaySizeGetter, guidGenerator, c
|
||||
//Установка флага отображения шапки приложения
|
||||
const setAppBarShow = useCallback(appBarShow => dispatch({ type: APP_AT.SET_APP_BAR_SHOW, payload: appBarShow }), []);
|
||||
|
||||
//Установка флага отображения настройки параметров в шапке приложения
|
||||
const setAppBarSettingsShow = useCallback(
|
||||
appBarSettingsShow => dispatch({ type: APP_AT.SET_APP_BAR_SETTINGS_SHOW, payload: appBarSettingsShow }),
|
||||
[]
|
||||
);
|
||||
|
||||
//Поиск раздела по имени
|
||||
const findPanelByName = name => state.panels.find(panel => panel.name == name);
|
||||
|
||||
@ -147,17 +153,36 @@ export const ApplicationContext = ({ errors, displaySizeGetter, guidGenerator, c
|
||||
//Получение базового URL приложения
|
||||
const configUrlBase = useMemo(() => state.urlBase, [state.urlBase]);
|
||||
|
||||
//Инициализация информации о параметрах панелей
|
||||
const initPanelsParams = useCallback(async () => {
|
||||
//Инициализируем параметры панелей
|
||||
try {
|
||||
const res = await executeStored({
|
||||
stored: "PKG_P8PANELS_SETTINGS.PANELS_INIT",
|
||||
respArg: "COUT",
|
||||
loader: false
|
||||
});
|
||||
return res;
|
||||
} catch (e) {
|
||||
showMsgErr(`Ошибка инициализации параметров панелей.`, null, e.message);
|
||||
}
|
||||
}, [executeStored, showMsgErr]);
|
||||
|
||||
//Инициализация приложения
|
||||
const initApp = useCallback(async () => {
|
||||
//Читаем конфигурацию с сервера
|
||||
let res = await getConfig();
|
||||
//Сохраняем базовый URL приложения
|
||||
setUrlBase(getRespPayload(res)?.Panels?.urlBase);
|
||||
//Инициализируем панели
|
||||
const panels = getRespPayload(res)?.Panels?.Panel;
|
||||
//Инициализируем информацию о параметрах панелей
|
||||
const panelsSettings = await initPanelsParams();
|
||||
//Сохраняем список панелей
|
||||
setPanels(getRespPayload(res)?.Panels?.Panel);
|
||||
setPanels(panels, panelsSettings);
|
||||
//Установим флаг завершения инициализации
|
||||
setInitialized();
|
||||
}, [getConfig, getRespPayload]);
|
||||
}, [getConfig, getRespPayload, initPanelsParams]);
|
||||
|
||||
//Обработка подключения контекста к странице
|
||||
useEffect(() => {
|
||||
@ -177,6 +202,7 @@ export const ApplicationContext = ({ errors, displaySizeGetter, guidGenerator, c
|
||||
value={{
|
||||
setAppBarTitle,
|
||||
setAppBarShow,
|
||||
setAppBarSettingsShow,
|
||||
findPanelByName,
|
||||
pOnlineShowTab,
|
||||
pOnlineShowUnit,
|
||||
|
||||
@ -14,7 +14,8 @@ const APP_AT = {
|
||||
SET_INITIALIZED: "SET_INITIALIZED", //Установка флага инициализированности приложения
|
||||
SET_DISPLAY_SIZE: "SET_DISPLAY_SIZE", //Установка текущего типового размера экрана
|
||||
SET_APP_BAR_TITLE: "SET_APP_BAR_TITLE", //Установка заголовка в шапке приложения
|
||||
SET_APP_BAR_SHOW: "SET_APP_BAR_SHOW" //Установка флага отображения шапки приложения
|
||||
SET_APP_BAR_SHOW: "SET_APP_BAR_SHOW", //Установка флага отображения шапки приложения
|
||||
SET_APP_BAR_SETTINGS_SHOW: "SET_APP_BAR_SETTINGS_SHOW" //Установка флага отображения настройки параметров в шапке приложения
|
||||
};
|
||||
|
||||
//Состояние приложения по умолчанию
|
||||
@ -22,6 +23,7 @@ const INITIAL_STATE = displaySizeGetter => ({
|
||||
displaySize: displaySizeGetter(),
|
||||
appBarTitle: "",
|
||||
appBarShow: true,
|
||||
appBarSettingsShow: true,
|
||||
urlBase: "",
|
||||
panels: [],
|
||||
panelsLoaded: false,
|
||||
@ -39,7 +41,8 @@ const handlers = {
|
||||
//Загрузка списка панелей
|
||||
[APP_AT.LOAD_PANELS]: (state, { payload }) => {
|
||||
let panels = [];
|
||||
if (payload && Array.isArray(payload)) for (let p of payload) panels.push({ ...p });
|
||||
if (payload && Array.isArray(payload.panels))
|
||||
for (let p of payload.panels) panels.push({ ...p, showUserSettings: payload?.panelsSettings?.[p.name]?.userSettings === 1 ? true : false });
|
||||
return {
|
||||
...state,
|
||||
panels,
|
||||
@ -54,6 +57,8 @@ const handlers = {
|
||||
[APP_AT.SET_APP_BAR_TITLE]: (state, { payload }) => ({ ...state, appBarTitle: payload }),
|
||||
//Установка флага отображения шапки приложения
|
||||
[APP_AT.SET_APP_BAR_SHOW]: (state, { payload }) => ({ ...state, appBarShow: payload }),
|
||||
//Установка флага отображения настройки параметров в шапке приложения
|
||||
[APP_AT.SET_APP_BAR_SETTINGS_SHOW]: (state, { payload }) => ({ ...state, appBarSettingsShow: payload }),
|
||||
//Обработчик по умолчанию
|
||||
DEFAULT: state => state
|
||||
};
|
||||
|
||||
172
app/context/settings.js
Normal file
172
app/context/settings.js
Normal file
@ -0,0 +1,172 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга
|
||||
Контекст: Взаимодействие с API параметров
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import React, { createContext, useContext, useCallback, useState, useRef } from "react"; //ReactJS
|
||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||
import { MessagingCtx } from "./messaging"; //Контекст сообщений
|
||||
import { P8PSettingsDialog } from "../components/p8p_settings_dialog"; //Диалог отображения параметров
|
||||
import { useSettingsContext } from "./settings_hooks"; //Вспомогательные хуки параметров
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Доступные виды параметров
|
||||
const SETTINGS_KINDS = {
|
||||
SYSTEM: 0,
|
||||
PANEL: 1,
|
||||
USER: 2
|
||||
};
|
||||
|
||||
//Доступные действия параметров
|
||||
const SETTINGS_ACTIONS = {
|
||||
INIT: "INIT",
|
||||
GET: "GET",
|
||||
PUT: "PUT"
|
||||
};
|
||||
|
||||
//Псевдокод панели для системных параметров
|
||||
const SETTINGS_SYSTEM_PSEUDO = "__P8_SYSTEM";
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
//Контекст взаимодействия с API параметров
|
||||
export const SettingsCtx = createContext();
|
||||
|
||||
//Провайдер контекста взаимодействия с API параметров
|
||||
export const SettingsContext = ({ children }) => {
|
||||
//Подключение к контексту сообщений
|
||||
const { showMsgErr } = useContext(MessagingCtx);
|
||||
|
||||
//Собственное состояние - действия параметров
|
||||
const { putPanelSettings, getPanelSettings, initPanelSettings } = useSettingsContext(showMsgErr);
|
||||
//Собственное состояние - отображаемые параметры
|
||||
const [settings, setSettings] = useState({ isOpen: false, data: {}, panel: null });
|
||||
//Собственное состояние - колбэки для обработки событий
|
||||
const settingsCallbacks = useRef({ onOk: null, onCancel: null });
|
||||
|
||||
//Формирование структуры параметров панели
|
||||
const buildPanelSettings = (kind, panel, settings) =>
|
||||
kind === SETTINGS_KINDS.SYSTEM ? { [SETTINGS_SYSTEM_PSEUDO]: { ...settings } } : { [panel]: { ...settings } };
|
||||
|
||||
//Извлечение настроек из ответа сервера
|
||||
const extractSettings = (res, kind, panel) =>
|
||||
kind === SETTINGS_KINDS.SYSTEM ? { ...(res[SETTINGS_SYSTEM_PSEUDO] || {}) } : { ...(panel ? res[panel] : res) };
|
||||
|
||||
//Валидация параметров настройки
|
||||
const validateSettingsParams = useCallback(
|
||||
({ action, kind, panel }) => {
|
||||
if ([SETTINGS_KINDS.PANEL, SETTINGS_KINDS.USER].includes(kind) && !panel) {
|
||||
//Формируем текст ошибки
|
||||
const errMsg = `Ошибка ${
|
||||
action === SETTINGS_ACTIONS.INIT ? "инициализации" : action === SETTINGS_ACTIONS.PUT ? "добавления" : "действия"
|
||||
} параметров. Для панельных/пользовательских параметров необходимо указать панель.`;
|
||||
//Отображаем ошибку
|
||||
showMsgErr(errMsg);
|
||||
throw new Error(errMsg);
|
||||
}
|
||||
},
|
||||
[showMsgErr]
|
||||
);
|
||||
|
||||
//Добавление параметров панелей
|
||||
const putSettings = useCallback(
|
||||
async ({ kind, settings, panel = null, loader = true }) => {
|
||||
//Проверка корректности параметров
|
||||
validateSettingsParams({ action: SETTINGS_ACTIONS.PUT, kind, panel });
|
||||
//Оборачиваем параметры в панель
|
||||
const panelSettings = buildPanelSettings(kind, panel, settings);
|
||||
//Добавляем параметры панели
|
||||
const res = await putPanelSettings({ kind, panelSettings, loader });
|
||||
//Возвращаем результат
|
||||
return res;
|
||||
},
|
||||
[putPanelSettings, validateSettingsParams]
|
||||
);
|
||||
|
||||
//Считывание параметров
|
||||
const getSettings = useCallback(
|
||||
async ({ kind, code = null, panel = null, full = false, loader = true }) => {
|
||||
//Считываем параметры панели
|
||||
const res = await getPanelSettings({ kind, code, panel, full, loader });
|
||||
//Возвращаем результат
|
||||
return extractSettings(res, kind, panel);
|
||||
},
|
||||
[getPanelSettings]
|
||||
);
|
||||
|
||||
//Инициализация параметров
|
||||
const initSettings = useCallback(
|
||||
async ({ kind, settings, panel = null, full = false, loader = true }) => {
|
||||
//Проверка корректности параметров
|
||||
validateSettingsParams({ action: SETTINGS_ACTIONS.INIT, kind, panel });
|
||||
//Оборачиваем параметры в панель
|
||||
const panelSettings = buildPanelSettings(kind, panel, settings);
|
||||
//Добавляем параметры панели
|
||||
const res = await initPanelSettings({ kind, panelSettings, full, loader });
|
||||
//Возвращаем результат
|
||||
return extractSettings(res, kind, panel);
|
||||
},
|
||||
[initPanelSettings, validateSettingsParams]
|
||||
);
|
||||
|
||||
//При нажатии "ОК" диалога параметров
|
||||
const handleOk = async newSettings => {
|
||||
//Признак наличия изменений параметров
|
||||
const isChanged = JSON.stringify(newSettings) !== JSON.stringify(settings.data);
|
||||
//Если параметры изменились - обновляем на сервере
|
||||
if (isChanged) await putPanelSettings({ kind: SETTINGS_KINDS.USER, panelSettings: newSettings });
|
||||
//Если есть колбэк на нажатие "ОК" - вызываем его
|
||||
if (settingsCallbacks.current.onOk)
|
||||
settingsCallbacks.current.onOk({ settings: settings.panel ? newSettings[settings.panel] : newSettings, isChanged });
|
||||
//Сбрасываем отображаемые данные
|
||||
setSettings({ isOpen: false, data: {}, panel: null });
|
||||
};
|
||||
|
||||
//При нажатии "Отменить" диалога параметров
|
||||
const handleClose = () => {
|
||||
//Если есть колбэк на нажатие "Отменить" - вызываем его
|
||||
if (settingsCallbacks.current.onCancel) settingsCallbacks.current.onCancel();
|
||||
//Сбрасываем отображаемые данные
|
||||
setSettings({ isOpen: false, data: {}, panel: null });
|
||||
};
|
||||
|
||||
//Открытие диалога параметров
|
||||
const showSettingsDialog = async ({ panel = null, onOk = null, onCancel = null } = {}) => {
|
||||
//Загружаем информацию о параметрах
|
||||
const panelSettings = await getPanelSettings({ kind: SETTINGS_KINDS.USER, panel, full: true });
|
||||
//Устанавливаем отображаемые данные
|
||||
setSettings({ isOpen: true, data: panelSettings, panel });
|
||||
//Устанавливаем колбэки
|
||||
settingsCallbacks.current = { onOk, onCancel };
|
||||
};
|
||||
|
||||
//Вернём компонент провайдера
|
||||
return (
|
||||
<SettingsCtx.Provider
|
||||
value={{
|
||||
SETTINGS_KINDS,
|
||||
initSettings,
|
||||
getSettings,
|
||||
putSettings,
|
||||
showSettingsDialog
|
||||
}}
|
||||
>
|
||||
{settings.isOpen ? <P8PSettingsDialog settings={settings.data} panel={settings.panel} onOk={handleOk} onClose={handleClose} /> : null}
|
||||
{children}
|
||||
</SettingsCtx.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Провайдер контекста взаимодействия с API параметров
|
||||
SettingsContext.propTypes = {
|
||||
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node])
|
||||
};
|
||||
120
app/context/settings_hooks.js
Normal file
120
app/context/settings_hooks.js
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга
|
||||
Контекст: Параметры - вспомогательные хуки
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import { useCallback, useContext } from "react"; //Классы React
|
||||
import { BackEndCtx } from "./backend"; //Контекст взаимодействия с сервером
|
||||
import { object2Base64XML, formatErrorMessage } from "../core/utils"; //Вспомогательные функции
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Хук для SettingsContext
|
||||
const useSettingsContext = showMsgErr => {
|
||||
//Подключение к контексту взаимодействия с сервером
|
||||
const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndCtx);
|
||||
|
||||
//Добавление параметров панели
|
||||
const putPanelSettings = useCallback(
|
||||
async ({ kind, panelSettings, loader = true }) => {
|
||||
//Добавляем параметры
|
||||
try {
|
||||
await executeStored({
|
||||
stored: "PKG_P8PANELS_SETTINGS.PUT",
|
||||
args: {
|
||||
NKIND: kind,
|
||||
CPANELS: {
|
||||
VALUE: object2Base64XML({
|
||||
XPANELS: panelSettings
|
||||
}),
|
||||
SDATA_TYPE: SERV_DATA_TYPE_CLOB
|
||||
}
|
||||
},
|
||||
loader
|
||||
});
|
||||
} catch (e) {
|
||||
//Разбираем текст ошибки
|
||||
let errMsg = formatErrorMessage(e.message);
|
||||
//Отображаем ошибку
|
||||
showMsgErr(errMsg.text, null, errMsg.fullErrorText);
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
[SERV_DATA_TYPE_CLOB, executeStored, showMsgErr]
|
||||
);
|
||||
|
||||
//Считывание параметров
|
||||
const getPanelSettings = useCallback(
|
||||
async ({ kind, code = null, panel = null, full = false, loader = true }) => {
|
||||
//Считываем параметры
|
||||
try {
|
||||
const res = await executeStored({
|
||||
stored: "PKG_P8PANELS_SETTINGS.GET",
|
||||
args: {
|
||||
NKIND: kind,
|
||||
SCODE: code,
|
||||
SPANEL: panel,
|
||||
NFULL: full ? 1 : 0
|
||||
},
|
||||
loader,
|
||||
respArg: "COUT"
|
||||
});
|
||||
return res || {};
|
||||
} catch (e) {
|
||||
//Разбираем текст ошибки
|
||||
let errMsg = formatErrorMessage(e.message);
|
||||
//Отображаем ошибку
|
||||
showMsgErr(errMsg.text, null, errMsg.fullErrorText);
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
[executeStored, showMsgErr]
|
||||
);
|
||||
|
||||
//Инициализация параметров панелей
|
||||
const initPanelSettings = useCallback(
|
||||
async ({ kind, panelSettings, full = false, loader = true }) => {
|
||||
//Инициализируем параметры
|
||||
try {
|
||||
const res = await executeStored({
|
||||
stored: "PKG_P8PANELS_SETTINGS.INIT",
|
||||
args: {
|
||||
NKIND: kind,
|
||||
CPANELS: {
|
||||
VALUE: object2Base64XML({
|
||||
XPANELS: panelSettings
|
||||
}),
|
||||
SDATA_TYPE: SERV_DATA_TYPE_CLOB
|
||||
},
|
||||
NFULL: full ? 1 : 0
|
||||
},
|
||||
loader,
|
||||
respArg: "COUT"
|
||||
});
|
||||
return res;
|
||||
} catch (e) {
|
||||
//Разбираем текст ошибки
|
||||
let errMsg = formatErrorMessage(e.message);
|
||||
//Отображаем ошибку
|
||||
showMsgErr(errMsg.text, null, errMsg.fullErrorText);
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
[SERV_DATA_TYPE_CLOB, executeStored, showMsgErr]
|
||||
);
|
||||
|
||||
//Возвращаем результат
|
||||
return { putPanelSettings, getPanelSettings, initPanelSettings };
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { useSettingsContext };
|
||||
15
app/core/data_types.js
Normal file
15
app/core/data_types.js
Normal file
@ -0,0 +1,15 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга
|
||||
Ядро: Типы данных
|
||||
*/
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Типы данных
|
||||
export const P8P_DATA_TYPES = {
|
||||
STR: "STR",
|
||||
NUMB: "NUMB",
|
||||
DATE: "DATE"
|
||||
};
|
||||
@ -7,7 +7,7 @@
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import React, { useState } from "react"; //Классы React
|
||||
import React, { useState, useEffect, useContext } from "react"; //Классы React
|
||||
import { Button, Fab, Icon } from "@mui/material"; //Интерфейсные элементы
|
||||
import { BUTTONS } from "../../../app.text"; //Текстовые ресурсы и константы
|
||||
import { P8Online } from "./p8online"; //Пример: API для взаимодействия с "ПАРУС 8 Онлайн"
|
||||
@ -20,6 +20,8 @@ import { Gantt } from "./gantt"; //Пример: Диаграмма Ганта "
|
||||
import { Svg } from "./svg"; //Пример: Интерактивные изображения "P8PSVG"
|
||||
import { Cyclogram } from "./cyclogram"; //Пример: Циклограмма "P8PCyclogram"
|
||||
import { Indicator } from "./indicator"; //Пример: Индикатор "P8PIndicator"
|
||||
import { Settings } from "./settings"; //Пример: Параметры панелей
|
||||
import { ApplicationCtx } from "../../context/application"; //Контекст приложения
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
@ -36,7 +38,8 @@ const MODES = {
|
||||
GANTT: { name: "GANTT", caption: 'Диаграмма Ганта "P8PGantt"', component: Gantt },
|
||||
SVG: { name: "SVG", caption: 'Интерактивные изображения "P8PSVG"', component: Svg },
|
||||
CYCLOGRAM: { name: "CYCLOGRAM", caption: 'Циклограмма "P8PCyclogram"', component: Cyclogram },
|
||||
INDICATOR: { name: "INDICATOR", caption: 'Индикатор "P8PIndicator"', component: Indicator }
|
||||
INDICATOR: { name: "INDICATOR", caption: 'Индикатор "P8PIndicator"', component: Indicator },
|
||||
SETTINGS: { name: "SETTINGS", caption: "Параметры панелей", component: Settings }
|
||||
};
|
||||
|
||||
//Стили
|
||||
@ -55,6 +58,15 @@ const Samples = () => {
|
||||
//Собственное состояние
|
||||
const [mode, setMode] = useState("");
|
||||
|
||||
//Подключение к контексту приложения
|
||||
const { setAppBarSettingsShow } = useContext(ApplicationCtx);
|
||||
|
||||
//При загрузке/открытии режима
|
||||
useEffect(() => {
|
||||
if (mode === MODES.SETTINGS.name) setAppBarSettingsShow(true);
|
||||
else setAppBarSettingsShow(false);
|
||||
}, [mode, setAppBarSettingsShow]);
|
||||
|
||||
//Генерация содержимого
|
||||
return (
|
||||
<div style={STYLES.ROOT}>
|
||||
|
||||
235
app/panels/samples/settings.js
Normal file
235
app/panels/samples/settings.js
Normal file
@ -0,0 +1,235 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга - Примеры для разработчиков
|
||||
Пример: Сообщения
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import React, { useContext, useState, useEffect } from "react"; //Классы React
|
||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||
import { Typography, Divider, Button, Box } from "@mui/material"; //Интерфейсные элементы
|
||||
import { SettingsCtx } from "../../context/settings"; //Контекст параметров
|
||||
import { P8P_DATA_TYPES } from "../../core/data_types"; //Типы данных
|
||||
import { formatDateJSONDateOnly } from "../../core/utils"; //Вспомогательные функции
|
||||
import { ApplicationCtx } from "../../context/application"; //Контекст приложения
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Стили
|
||||
const STYLES = {
|
||||
CONTAINER: { textAlign: "center", paddingTop: "20px" },
|
||||
TITLE: { paddingBottom: "15px" },
|
||||
DIVIDER: { margin: "15px" },
|
||||
BOX_SETTINGS_VALUES: { display: "flex", justifyContent: "space-around" },
|
||||
SETTING_CARD: {
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
padding: "12px 16px",
|
||||
borderRadius: 1,
|
||||
border: "1px solid",
|
||||
borderColor: "divider"
|
||||
},
|
||||
SETTING_NAME: {
|
||||
fontWeight: 500,
|
||||
color: "text.primary"
|
||||
},
|
||||
SETTING_VALUE: {
|
||||
fontWeight: 600,
|
||||
color: "primary.main",
|
||||
marginLeft: 2
|
||||
}
|
||||
};
|
||||
|
||||
//Наименование панели
|
||||
const PANEL_NAME = "Samples";
|
||||
|
||||
//Параметры панели
|
||||
const PANEL_PARAMS = {
|
||||
STR_PARAM: {
|
||||
name: "Строковый параметр",
|
||||
dataType: P8P_DATA_TYPES.STR,
|
||||
//value: "Строковое значение",
|
||||
desc: "Описание строкового параметра"
|
||||
},
|
||||
NUM_PARAM: {
|
||||
name: "Числовой параметр",
|
||||
dataType: P8P_DATA_TYPES.NUMB,
|
||||
value: 10,
|
||||
desc: "Описание числового параметра"
|
||||
},
|
||||
DATE_PARAM: {
|
||||
name: "Параметр дата",
|
||||
dataType: P8P_DATA_TYPES.DATE,
|
||||
value: "2026-02-15",
|
||||
desc: ""
|
||||
},
|
||||
AGENT: {
|
||||
name: "Привязка к контрагенту",
|
||||
dataType: P8P_DATA_TYPES.STR,
|
||||
value: "",
|
||||
desc: "Мнемокод выбранного контрагента",
|
||||
options: {
|
||||
unitcode: "AGNLIST",
|
||||
showMethod: "main",
|
||||
inParameter: "in_AGNABBR",
|
||||
outParameter: "out_AGNABBR"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//Параметр "Дата последнего обновления"
|
||||
const LAST_UPDATE_PARAM = {
|
||||
LAST_UPDATE: {
|
||||
name: "Дата последнего обновления",
|
||||
dataType: "DATE",
|
||||
value: formatDateJSONDateOnly(new Date()),
|
||||
desc: ""
|
||||
}
|
||||
};
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Пример: Параметры
|
||||
const Settings = ({ title }) => {
|
||||
//Собственное состояние - параметры
|
||||
const [state, setState] = useState({});
|
||||
//Собственное состояние - отображение кнопки параметров панели
|
||||
const [showPanelSettings, setShowPanelSettings] = useState(true);
|
||||
|
||||
//Подключение к контексту параметров
|
||||
const { getSettings, initSettings, putSettings, showSettingsDialog, SETTINGS_KINDS } = useContext(SettingsCtx);
|
||||
//Подключение к контексту приложения
|
||||
const { setAppBarSettingsShow } = useContext(ApplicationCtx);
|
||||
|
||||
//Инициализация параметров пользователя
|
||||
const handleInitSettings = async () => {
|
||||
// Инициализация пользовательских параметров
|
||||
const settings = await initSettings({
|
||||
kind: SETTINGS_KINDS.USER,
|
||||
panel: PANEL_NAME,
|
||||
settings: PANEL_PARAMS
|
||||
});
|
||||
//Устанавливаем параметры
|
||||
setState({ ...settings });
|
||||
};
|
||||
|
||||
//Загрузка параметров пользователя
|
||||
const handleLoadSettings = async () => {
|
||||
// Считывание параметров панели
|
||||
const settings = await getSettings({
|
||||
kind: SETTINGS_KINDS.USER,
|
||||
panel: PANEL_NAME
|
||||
});
|
||||
//Устанавливаем параметры
|
||||
setState({ ...settings });
|
||||
};
|
||||
|
||||
//Добавление/обновление параметров пользователя
|
||||
const handlePutSettings = async () => {
|
||||
//Добавляем параметр
|
||||
await putSettings({
|
||||
kind: SETTINGS_KINDS.USER,
|
||||
panel: PANEL_NAME,
|
||||
settings: LAST_UPDATE_PARAM
|
||||
});
|
||||
//Перечитываем параметры
|
||||
handleLoadSettings();
|
||||
};
|
||||
|
||||
//При отображении параметров панели
|
||||
const handleSettingsDialog = async () => {
|
||||
//Отображаем параметры панели
|
||||
showSettingsDialog({
|
||||
panel: PANEL_NAME,
|
||||
onOk: ({ settings, isChanged }) => {
|
||||
console.log("Параметры сохранены:", { settings, isChanged });
|
||||
},
|
||||
onCancel: () => {
|
||||
console.log("Изменения отменены");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
//Отображение/сокрытие кнопки открытия параметров панели
|
||||
const handleShowPanelSettingsToggle = () => {
|
||||
//Переключаем состояние
|
||||
setShowPanelSettings(!showPanelSettings);
|
||||
//Скрываем/отображаем кнопку параметров
|
||||
setAppBarSettingsShow(!showPanelSettings);
|
||||
};
|
||||
|
||||
//При открытии панели
|
||||
useEffect(() => {
|
||||
//Инициализируем параметры
|
||||
handleInitSettings();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
//Генерация содержимого
|
||||
return (
|
||||
<div style={STYLES.CONTAINER}>
|
||||
<Typography sx={STYLES.TITLE} variant={"h6"}>
|
||||
{title}
|
||||
</Typography>
|
||||
<Box sx={STYLES.BOX_BUTTON_GROUP}>
|
||||
<Button variant="contained" onClick={handleInitSettings}>
|
||||
Инициализация
|
||||
</Button>
|
||||
</Box>
|
||||
<Divider sx={STYLES.DIVIDER} />
|
||||
<Button variant="contained" onClick={handleLoadSettings}>
|
||||
Считывание
|
||||
</Button>
|
||||
<Divider sx={STYLES.DIVIDER} />
|
||||
<Button variant="contained" onClick={handlePutSettings}>
|
||||
Добавление
|
||||
</Button>
|
||||
<Divider sx={STYLES.DIVIDER} />
|
||||
<Button variant="contained" onClick={handleSettingsDialog}>
|
||||
Диалог параметров панели
|
||||
</Button>
|
||||
<Divider sx={STYLES.DIVIDER} />
|
||||
<Button variant="contained" onClick={handleShowPanelSettingsToggle}>
|
||||
{`${showPanelSettings ? "Скрыть" : "Отобразить"} кнопку открытия параметров в шапке`}
|
||||
</Button>
|
||||
{Object.keys(state).length > 0 ? (
|
||||
<>
|
||||
<Divider sx={STYLES.DIVIDER} />
|
||||
<Typography pt={2} sx={STYLES.TITLE} variant={"h6"}>
|
||||
Значения параметров
|
||||
</Typography>
|
||||
<Box sx={STYLES.BOX_SETTINGS_VALUES}>
|
||||
{Object.keys(state).map((item, index) => (
|
||||
<Box key={index} sx={STYLES.SETTING_CARD}>
|
||||
<Typography variant="body2" sx={STYLES.SETTING_NAME}>
|
||||
{`${Object.keys(PANEL_PARAMS).includes(item) ? PANEL_PARAMS[item].name : LAST_UPDATE_PARAM[item].name}:`}
|
||||
</Typography>
|
||||
<Typography variant="body2" sx={STYLES.SETTING_VALUE}>
|
||||
{state[item]}
|
||||
</Typography>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Пример: параметры
|
||||
Settings.propTypes = {
|
||||
title: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { Settings };
|
||||
30
db/P8PNL_SETTINGS.sql
Normal file
30
db/P8PNL_SETTINGS.sql
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга
|
||||
Параметры панелей
|
||||
*/
|
||||
create table P8PNL_SETTINGS
|
||||
(
|
||||
RN number(17) not null, -- Рег. номер записи
|
||||
CODE varchar2(40) not null, -- Мнемокод параметра
|
||||
NAME varchar2(240) not null, -- Наименование параметра
|
||||
KIND number(1) not null -- Вид параметра
|
||||
constraint C_P8PNL_SETTINGS_KIND_VAL check ( KIND in (0,1,2) ),
|
||||
PANEL varchar2(100) default null, -- Код панели
|
||||
COMPANY number(17) default null, -- Рег. номер организации
|
||||
AUTHID varchar2(30) default null, -- Пользователь
|
||||
OPTIONS clob, -- Дополнительные параметры
|
||||
DATA_TYPE number(1) default 0 -- Тип данных
|
||||
constraint C_P8PNL_SETTINGS_DT_VAL check ( DATA_TYPE in (0,1,2) ),
|
||||
VALUE_STR varchar2(4000) default null, -- Значение (строка)
|
||||
VALUE_NUM number default null, -- Значение (число)
|
||||
VALUE_DATE date default null, -- Значение (дата)
|
||||
DESCRIPTION varchar2(240) default null, -- Примечание
|
||||
constraint C_P8PNL_SETTINGS_PK primary key (RN),
|
||||
constraint C_P8PNL_SETTINGS_UK unique (CODE, PANEL, COMPANY, AUTHID),
|
||||
constraint C_P8PNL_SETTINGS_DATA_TYPE_CH check (((DATA_TYPE = 0) and ((VALUE_NUM is null) and (VALUE_DATE is null)))
|
||||
or ((DATA_TYPE = 1) and ((VALUE_STR is null) and (VALUE_DATE is null)))
|
||||
or ((DATA_TYPE = 2) and ((VALUE_STR is null) and (VALUE_NUM is null)))),
|
||||
constraint C_P8PNL_SETTINGS_KIND_CH check (((KIND = 0) and ((PANEL is null) and (COMPANY is null) and (AUTHID is null)))
|
||||
or ((KIND = 1) and ((PANEL is not null) and (COMPANY is null) and (AUTHID is not null)))
|
||||
or ((KIND = 2) and ((PANEL is not null) and (COMPANY is not null) and (AUTHID is not null))))
|
||||
);
|
||||
1524
db/PKG_P8PANELS_SETTINGS.pck
Normal file
1524
db/PKG_P8PANELS_SETTINGS.pck
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user