WEBAPP: Рефакторинг "Редактора панелей" - разбит на типовые компоненты, которые вынесены в ядро приложения, за пределы панели
This commit is contained in:
parent
d792187ff9
commit
759fc763e2
18
app.text.js
18
app.text.js
@ -12,14 +12,18 @@ export const TITLES = {
|
|||||||
INFO: "Информация", //Информационный блок
|
INFO: "Информация", //Информационный блок
|
||||||
WARN: "Предупреждение", //Блок предупреждения
|
WARN: "Предупреждение", //Блок предупреждения
|
||||||
ERR: "Ошибка", //Информация об ошибке
|
ERR: "Ошибка", //Информация об ошибке
|
||||||
DEFAULT_PANELS_GROUP: "Без привязки к группе" //Заголовок группы панелей по умолчанию
|
DEFAULT_PANELS_GROUP: "Без привязки к группе", //Заголовок группы панелей по умолчанию
|
||||||
|
DATA_SOURCE_CONFIG: "Настройка источника данных" //Заголовок для настройки источника данных
|
||||||
};
|
};
|
||||||
|
|
||||||
//Текст
|
//Текст
|
||||||
export const TEXTS = {
|
export const TEXTS = {
|
||||||
LOADING: "Ожидайте...", //Ожидание завершения процесса
|
LOADING: "Ожидайте...", //Ожидание завершения процесса
|
||||||
NO_DATA_FOUND: "Данных не найдено", //Отсутствие данных
|
NO_DATA_FOUND: "Данных не найдено", //Отсутствие данных
|
||||||
NO_DATA_FOUND_SHORT: "Н.Д." //Отсутствие данных (кратко)
|
NO_DATA_FOUND_SHORT: "Н.Д.", //Отсутствие данных (кратко)
|
||||||
|
NO_SETTINGS: "Настройки не определены", //Отстутсвие настроек
|
||||||
|
UNKNOWN_SOURCE_TYPE: "Неизвестный тип источника", //Отсуствие типа источника
|
||||||
|
UNNAMED_SOURCE: "Источник без наименования" //Отсутствие наименования источника
|
||||||
};
|
};
|
||||||
|
|
||||||
//Текст кнопок
|
//Текст кнопок
|
||||||
@ -38,7 +42,8 @@ export const BUTTONS = {
|
|||||||
FILTER: "Фильтр", //Фильтрация
|
FILTER: "Фильтр", //Фильтрация
|
||||||
MORE: "Ещё", //Догрузка данных
|
MORE: "Ещё", //Догрузка данных
|
||||||
APPLY: "Применить", //Сохранение без закрытия интерфейса ввода
|
APPLY: "Применить", //Сохранение без закрытия интерфейса ввода
|
||||||
SAVE: "Сохранить" //Сохранение
|
SAVE: "Сохранить", //Сохранение
|
||||||
|
CONFIG: "Настроить" //Настройка
|
||||||
};
|
};
|
||||||
|
|
||||||
//Метки атрибутов, сопроводительные надписи
|
//Метки атрибутов, сопроводительные надписи
|
||||||
@ -51,7 +56,9 @@ export const CAPTIONS = {
|
|||||||
START: "Начало",
|
START: "Начало",
|
||||||
END: "Окончание",
|
END: "Окончание",
|
||||||
PROGRESS: "Прогресс",
|
PROGRESS: "Прогресс",
|
||||||
LEGEND: "Легенда"
|
LEGEND: "Легенда",
|
||||||
|
USER_PROC: "Пользовательская процедура",
|
||||||
|
QUERY: "Запрос"
|
||||||
};
|
};
|
||||||
|
|
||||||
//Типовые сообщения об ошибках
|
//Типовые сообщения об ошибках
|
||||||
@ -59,7 +66,8 @@ export const ERRORS = {
|
|||||||
UNDER_CONSTRUCTION: "Панель в разработке",
|
UNDER_CONSTRUCTION: "Панель в разработке",
|
||||||
P8O_API_UNAVAILABLE: '"ПАРУС 8 Онлайн" недоступен',
|
P8O_API_UNAVAILABLE: '"ПАРУС 8 Онлайн" недоступен',
|
||||||
P8O_API_UNSUPPORTED: 'Функция "ПАРУС 8 Онлайн" не поддерживается',
|
P8O_API_UNSUPPORTED: 'Функция "ПАРУС 8 Онлайн" не поддерживается',
|
||||||
DEFAULT: "Неожиданная ошибка"
|
DEFAULT: "Неожиданная ошибка",
|
||||||
|
DATA_SOURCE_NO_REQ_ARGS: "Не заданы обязательные параметры источника данных"
|
||||||
};
|
};
|
||||||
|
|
||||||
//Типовые сообщения для ошибок HTTP
|
//Типовые сообщения для ошибок HTTP
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Парус 8 - Панели мониторинга - Редактор панелей
|
Парус 8 - Панели мониторинга - Редакторы панелей
|
||||||
Общие компоненты представлений элементов панели
|
Компонент: Информационное сообщение внутри компонента
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//---------------------
|
//---------------------
|
||||||
@ -10,22 +10,22 @@
|
|||||||
import React from "react"; //Классы React
|
import React from "react"; //Классы React
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import { Stack, Icon, Typography } from "@mui/material"; //Интерфейсные элементы
|
import { Stack, Icon, Typography } from "@mui/material"; //Интерфейсные элементы
|
||||||
import { TEXTS } from "../../../../app.text"; //Общие текстовые ресурсы
|
import { TEXTS } from "../../../app.text"; //Общие текстовые ресурсы
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
//Константы
|
//Константы
|
||||||
//---------
|
//---------
|
||||||
|
|
||||||
//Типы сообщений
|
//Типы сообщений компонентов
|
||||||
const COMPONENT_MESSAGE_TYPE = {
|
const P8P_COMPONENT_INLINE_MESSAGE_TYPE = {
|
||||||
COMMON: "COMMON",
|
COMMON: "COMMON",
|
||||||
ERROR: "ERROR"
|
ERROR: "ERROR"
|
||||||
};
|
};
|
||||||
|
|
||||||
//Типовые сообщения
|
//Типовые сообщения компонентов
|
||||||
const COMPONENT_MESSAGES = {
|
const P8P_COMPONENT_INLINE_MESSAGE = {
|
||||||
NO_DATA_FOUND: TEXTS.NO_DATA_FOUND,
|
NO_DATA_FOUND: TEXTS.NO_DATA_FOUND,
|
||||||
NO_SETTINGS: "Настройте компонент"
|
NO_SETTINGS: TEXTS.NO_SETTINGS
|
||||||
};
|
};
|
||||||
|
|
||||||
//-----------
|
//-----------
|
||||||
@ -33,7 +33,7 @@ const COMPONENT_MESSAGES = {
|
|||||||
//-----------
|
//-----------
|
||||||
|
|
||||||
//Информационное сообщение внутри компонента
|
//Информационное сообщение внутри компонента
|
||||||
const ComponentInlineMessage = ({ icon, name, message, type = COMPONENT_MESSAGE_TYPE.COMMON }) => {
|
const P8PComponentInlineMessage = ({ icon, name, message, type = P8P_COMPONENT_INLINE_MESSAGE_TYPE.COMMON }) => {
|
||||||
//Формирование представления
|
//Формирование представления
|
||||||
return (
|
return (
|
||||||
<Stack direction={"column"}>
|
<Stack direction={"column"}>
|
||||||
@ -45,7 +45,11 @@ const ComponentInlineMessage = ({ icon, name, message, type = COMPONENT_MESSAGE_
|
|||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
<Typography align={"center"} color={type != COMPONENT_MESSAGE_TYPE.ERROR ? "text.secondary" : "error.dark"} variant={"caption"}>
|
<Typography
|
||||||
|
align={"center"}
|
||||||
|
color={type != P8P_COMPONENT_INLINE_MESSAGE_TYPE.ERROR ? "text.secondary" : "error.dark"}
|
||||||
|
variant={"caption"}
|
||||||
|
>
|
||||||
{message}
|
{message}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Stack>
|
</Stack>
|
||||||
@ -53,15 +57,15 @@ const ComponentInlineMessage = ({ icon, name, message, type = COMPONENT_MESSAGE_
|
|||||||
};
|
};
|
||||||
|
|
||||||
//Контроль свойств - Информационное сообщение внутри компонента
|
//Контроль свойств - Информационное сообщение внутри компонента
|
||||||
ComponentInlineMessage.propTypes = {
|
P8PComponentInlineMessage.propTypes = {
|
||||||
icon: PropTypes.string,
|
icon: PropTypes.string,
|
||||||
name: PropTypes.string,
|
name: PropTypes.string,
|
||||||
message: PropTypes.string.isRequired,
|
message: PropTypes.string.isRequired,
|
||||||
type: PropTypes.oneOf(Object.values(COMPONENT_MESSAGE_TYPE))
|
type: PropTypes.oneOf(Object.values(P8P_COMPONENT_INLINE_MESSAGE_TYPE))
|
||||||
};
|
};
|
||||||
|
|
||||||
//----------------
|
//----------------
|
||||||
//Интерфейс модуля
|
//Интерфейс модуля
|
||||||
//----------------
|
//----------------
|
||||||
|
|
||||||
export { COMPONENT_MESSAGE_TYPE, COMPONENT_MESSAGES, ComponentInlineMessage };
|
export { P8P_COMPONENT_INLINE_MESSAGE_TYPE, P8P_COMPONENT_INLINE_MESSAGE, P8PComponentInlineMessage };
|
40
app/components/editors/p8p_config_dialog.js
Normal file
40
app/components/editors/p8p_config_dialog.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - Редакторы панелей
|
||||||
|
Компонент: Диалог настройки
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React from "react"; //Классы React
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
import { P8PDialog } from "../p8p_dialog"; //Типовой диалог
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Диалог настройки
|
||||||
|
const P8PConfigDialog = ({ title, children, onOk, onCancel }) => {
|
||||||
|
//Формирование представления
|
||||||
|
return (
|
||||||
|
<P8PDialog title={title} onOk={onOk} onCancel={onCancel}>
|
||||||
|
{children}
|
||||||
|
</P8PDialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств компонента - Диалог настройки
|
||||||
|
P8PConfigDialog.propTypes = {
|
||||||
|
title: PropTypes.string.isRequired,
|
||||||
|
children: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf(PropTypes.node)]),
|
||||||
|
onOk: PropTypes.func,
|
||||||
|
onCancel: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { P8PConfigDialog };
|
113
app/components/editors/p8p_data_source.js
Normal file
113
app/components/editors/p8p_data_source.js
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - Редакторы панелей
|
||||||
|
Компонент: Источник данных
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React, { useState } from "react"; //Классы React
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
import { Stack, IconButton, Icon, Typography, Chip, Button, Card, CardContent, CardActions, CardActionArea } from "@mui/material"; //Интерфейсные элементы
|
||||||
|
import { BUTTONS, TEXTS } from "../../../app.text"; //Общие текстовые ресурсы
|
||||||
|
import { STYLES as COMMON_STYLES } from "./p8p_editors_common"; //Общие ресурсы редаторов
|
||||||
|
import { P8P_DATA_SOURCE_SHAPE, P8P_DATA_SOURCE_TYPE, P8P_DATA_SOURCE_TYPE_NAME, P8P_DATA_SOURCE_INITIAL } from "./p8p_data_source_common"; //Общие ресурсы компонента "Источник данных"
|
||||||
|
import { P8PDataSourceConfigDialog } from "./p8p_data_source_config_dialog"; //Диалог настройки источника данных
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Источник данных
|
||||||
|
const P8PDataSource = ({ 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({ ...P8P_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={COMMON_STYLES.CHIP(true)}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
|
||||||
|
//Формирование представления
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{configDlg && (
|
||||||
|
<P8PDataSourceConfigDialog
|
||||||
|
dataSource={dataSource}
|
||||||
|
valueProviders={valueProviders}
|
||||||
|
onOk={handleSetupOk}
|
||||||
|
onCancel={handleSetupCancel}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{configured && (
|
||||||
|
<Card variant={"outlined"}>
|
||||||
|
<CardActionArea onClick={handleSetup}>
|
||||||
|
<CardContent>
|
||||||
|
<Typography variant={"subtitle1"} noWrap={true}>
|
||||||
|
{dataSource.type === P8P_DATA_SOURCE_TYPE.USER_PROC ? dataSource.userProc : TEXTS.UNNAMED_SOURCE}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant={"caption"} color={"text.secondary"} noWrap={true}>
|
||||||
|
{P8P_DATA_SOURCE_TYPE_NAME[dataSource.type] || TEXTS.UNKNOWN_SOURCE_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}>
|
||||||
|
{BUTTONS.CONFIG}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств компонента - Источник данных
|
||||||
|
P8PDataSource.propTypes = {
|
||||||
|
dataSource: P8P_DATA_SOURCE_SHAPE,
|
||||||
|
valueProviders: PropTypes.object,
|
||||||
|
onChange: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { P8PDataSource };
|
86
app/components/editors/p8p_data_source_common.js
Normal file
86
app/components/editors/p8p_data_source_common.js
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - Редакторы панелей
|
||||||
|
Общие ресурсы компонента "Источник данных"
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
import client from "../../core/client"; //Клиент БД
|
||||||
|
import { CAPTIONS } from "../../../app.text"; //Общие текстовые ресурсы
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Типы даных аргументов
|
||||||
|
const P8P_DATA_SOURCE_ARGUMENT_DATA_TYPE = {
|
||||||
|
STR: client.SERV_DATA_TYPE_STR,
|
||||||
|
NUMB: client.SERV_DATA_TYPE_NUMB,
|
||||||
|
DATE: client.SERV_DATA_TYPE_DATE
|
||||||
|
};
|
||||||
|
|
||||||
|
//Структура аргумента источника данных
|
||||||
|
const P8P_DATA_SOURCE_ARGUMENT_SHAPE = PropTypes.shape({
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
caption: PropTypes.string.isRequired,
|
||||||
|
dataType: PropTypes.oneOf(Object.values(P8P_DATA_SOURCE_ARGUMENT_DATA_TYPE)),
|
||||||
|
req: PropTypes.bool.isRequired,
|
||||||
|
value: PropTypes.any,
|
||||||
|
valueSource: PropTypes.string
|
||||||
|
});
|
||||||
|
|
||||||
|
//Начальное состояние аргумента источника данных
|
||||||
|
const P8P_DATA_SOURCE_ARGUMENT_INITIAL = {
|
||||||
|
name: "",
|
||||||
|
caption: "",
|
||||||
|
dataType: "",
|
||||||
|
req: false,
|
||||||
|
value: "",
|
||||||
|
valueSource: ""
|
||||||
|
};
|
||||||
|
|
||||||
|
//Типы источников данных
|
||||||
|
const P8P_DATA_SOURCE_TYPE = {
|
||||||
|
USER_PROC: "USER_PROC",
|
||||||
|
QUERY: "QUERY"
|
||||||
|
};
|
||||||
|
|
||||||
|
//Типы источников данных (наименования)
|
||||||
|
const P8P_DATA_SOURCE_TYPE_NAME = {
|
||||||
|
[P8P_DATA_SOURCE_TYPE.USER_PROC]: CAPTIONS.USER_PROC,
|
||||||
|
[P8P_DATA_SOURCE_TYPE.QUERY]: CAPTIONS.QUERY
|
||||||
|
};
|
||||||
|
|
||||||
|
//Структура источника данных
|
||||||
|
const P8P_DATA_SOURCE_SHAPE = PropTypes.shape({
|
||||||
|
type: PropTypes.oneOf([...Object.values(P8P_DATA_SOURCE_TYPE), ""]),
|
||||||
|
userProc: PropTypes.string,
|
||||||
|
stored: PropTypes.string,
|
||||||
|
respArg: PropTypes.string,
|
||||||
|
arguments: PropTypes.arrayOf(P8P_DATA_SOURCE_ARGUMENT_SHAPE)
|
||||||
|
});
|
||||||
|
|
||||||
|
//Начальное состояние истоника данных
|
||||||
|
const P8P_DATA_SOURCE_INITIAL = {
|
||||||
|
type: "",
|
||||||
|
userProc: "",
|
||||||
|
stored: "",
|
||||||
|
respArg: "",
|
||||||
|
arguments: []
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export {
|
||||||
|
P8P_DATA_SOURCE_ARGUMENT_DATA_TYPE,
|
||||||
|
P8P_DATA_SOURCE_ARGUMENT_INITIAL,
|
||||||
|
P8P_DATA_SOURCE_SHAPE,
|
||||||
|
P8P_DATA_SOURCE_TYPE,
|
||||||
|
P8P_DATA_SOURCE_TYPE_NAME,
|
||||||
|
P8P_DATA_SOURCE_INITIAL
|
||||||
|
};
|
185
app/components/editors/p8p_data_source_config_dialog.js
Normal file
185
app/components/editors/p8p_data_source_config_dialog.js
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - Редакторы панелей
|
||||||
|
Компонент: Диалог настройки источника данных
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React, { useState, useEffect, useContext } from "react"; //Классы React
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
import { Stack, IconButton, Icon, TextField, InputAdornment, MenuItem, Menu } from "@mui/material"; //Интерфейсные элементы
|
||||||
|
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
||||||
|
import { TITLES, CAPTIONS } from "../../../app.text"; //Общие текстовые ресурсы
|
||||||
|
import { P8PConfigDialog } from "./p8p_config_dialog"; //Типовой диалог настройки
|
||||||
|
import { P8P_DATA_SOURCE_TYPE, P8P_DATA_SOURCE_SHAPE, P8P_DATA_SOURCE_ARGUMENT_INITIAL, P8P_DATA_SOURCE_INITIAL } from "./p8p_data_source_common"; //Общие ресурсы компонента "Источник данных"
|
||||||
|
import { useUserProcDesc } from "./p8p_data_source_hooks"; //Хуки источников данных
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Диалог настройки источника данных
|
||||||
|
const P8PDataSourceConfigDialog = ({ dataSource = null, valueProviders = {}, onOk = null, onCancel = null } = {}) => {
|
||||||
|
//Собственное состояние - параметры элемента формы
|
||||||
|
const [state, setState] = useState({ ...P8P_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({ ...P8P_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: P8P_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 => ({ ...P8P_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 (
|
||||||
|
<P8PConfigDialog title={TITLES.DATA_SOURCE_CONFIG} onOk={handleOk} onCancel={handleCancel}>
|
||||||
|
<Stack direction={"column"} spacing={1}>
|
||||||
|
{valueProvidersMenu}
|
||||||
|
<TextField
|
||||||
|
type={"text"}
|
||||||
|
variant={"standard"}
|
||||||
|
value={state.userProc}
|
||||||
|
label={CAPTIONS.USER_PROC}
|
||||||
|
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>
|
||||||
|
</P8PConfigDialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств компонента - Диалог настройки источника данных
|
||||||
|
P8PDataSourceConfigDialog.propTypes = {
|
||||||
|
dataSource: P8P_DATA_SOURCE_SHAPE,
|
||||||
|
valueProviders: PropTypes.object,
|
||||||
|
onOk: PropTypes.func,
|
||||||
|
onCancel: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { P8PDataSourceConfigDialog };
|
151
app/components/editors/p8p_data_source_hooks.js
Normal file
151
app/components/editors/p8p_data_source_hooks.js
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - Редакторы панелей
|
||||||
|
Пользовательские хуки компонента "Источник данных"
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import { useState, useContext, useEffect, useRef } from "react"; //Классы React
|
||||||
|
import client from "../../core/client"; //Клиент взаимодействия с сервером приложений
|
||||||
|
import { ERRORS } from "../../../app.text"; //Общие текстовые ресурсы
|
||||||
|
import { formatErrorMessage } from "../../core/utils"; //Общие вспомогательные функции
|
||||||
|
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
||||||
|
import { P8P_DATA_SOURCE_TYPE, P8P_DATA_SOURCE_ARGUMENT_DATA_TYPE } from "./p8p_data_source_common"; //Общие ресурсы источника данных
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Описание пользовательской процедуры
|
||||||
|
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_PE.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 useDataSource = ({ 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 == P8P_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 == P8P_DATA_SOURCE_ARGUMENT_DATA_TYPE.NUMB
|
||||||
|
? isNaN(parseFloat(v))
|
||||||
|
? null
|
||||||
|
: parseFloat(v)
|
||||||
|
: argument.dataType == P8P_DATA_SOURCE_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(ERRORS.DATA_SOURCE_NO_REQ_ARGS);
|
||||||
|
setData({ init: false });
|
||||||
|
}
|
||||||
|
return { stored, respArg, storedArgs, reqSet };
|
||||||
|
} else return pv;
|
||||||
|
} else return pv;
|
||||||
|
});
|
||||||
|
}, [dataSource, values]);
|
||||||
|
|
||||||
|
//Возвращаем интерфейс хука
|
||||||
|
return [data, error, isLoading];
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { useUserProcDesc, useDataSource };
|
54
app/components/editors/p8p_editor_box.js
Normal file
54
app/components/editors/p8p_editor_box.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - Редакторы панелей
|
||||||
|
Компонент: Контейнер редактора
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React from "react"; //Классы React
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
import { Box, Divider, IconButton, Icon, Stack } from "@mui/material"; //Интерфейсные компоненты MUI
|
||||||
|
import { BUTTONS } from "../../../app.text"; //Общие текстовые ресурсы
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Контейнер редактора
|
||||||
|
const P8PEditorBox = ({ title, children, onSave }) => {
|
||||||
|
//При нажатии на "Сохранить"
|
||||||
|
const handleSaveClick = (closeEditor = false) => onSave && onSave(closeEditor);
|
||||||
|
|
||||||
|
//Формирование представления
|
||||||
|
return (
|
||||||
|
<Box p={2}>
|
||||||
|
<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>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств компонента - Контейнер редактора
|
||||||
|
P8PEditorBox.propTypes = {
|
||||||
|
title: PropTypes.string.isRequired,
|
||||||
|
children: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf(PropTypes.node)]),
|
||||||
|
onSave: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { P8PEditorBox };
|
46
app/components/editors/p8p_editor_sub_header.js
Normal file
46
app/components/editors/p8p_editor_sub_header.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - Редакторы панелей
|
||||||
|
Компонент: Заголовок раздела редактора
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React from "react"; //Классы React
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
import { Divider, Chip } from "@mui/material"; //Интерфейсные компоненты MUI
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Стили
|
||||||
|
const STYLES = {
|
||||||
|
DIVIDER: { paddingTop: "20px" }
|
||||||
|
};
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Заголовок раздела редактора
|
||||||
|
const P8PEditorSubHeader = ({ title }) => {
|
||||||
|
//Формирование представления
|
||||||
|
return (
|
||||||
|
<Divider sx={STYLES.DIVIDER}>
|
||||||
|
<Chip label={title} size={"small"} />
|
||||||
|
</Divider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств компонента - Заголовок раздела редактора
|
||||||
|
P8PEditorSubHeader.propTypes = {
|
||||||
|
title: PropTypes.string.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { P8PEditorSubHeader };
|
52
app/components/editors/p8p_editor_toolbar.js
Normal file
52
app/components/editors/p8p_editor_toolbar.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - Редакторы панелей
|
||||||
|
Компонент: Панель инструментов редактора
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React from "react"; //Классы React
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
import { IconButton, Icon, Stack } from "@mui/material"; //Интерфейсные компоненты MUI
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Структура элемента панели инструментов редактора
|
||||||
|
const P8P_EDITOR_TOOL_BAR_ITEM_SHAPE = PropTypes.shape({
|
||||||
|
icon: PropTypes.string.isRequired,
|
||||||
|
title: PropTypes.string.isRequired,
|
||||||
|
onClick: PropTypes.func.isRequired
|
||||||
|
});
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Панель инструментов редактора
|
||||||
|
const P8PEditorToolBar = ({ items = [] }) => {
|
||||||
|
//Формирование представления
|
||||||
|
return (
|
||||||
|
<Stack direction={"row"} p={1}>
|
||||||
|
{items.map((item, i) => (
|
||||||
|
<IconButton key={i} onClick={item.onClick} title={item.title}>
|
||||||
|
<Icon>{item.icon}</Icon>
|
||||||
|
</IconButton>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств компонента - Панель инструментов редактора
|
||||||
|
P8PEditorToolBar.propTypes = {
|
||||||
|
items: PropTypes.arrayOf(P8P_EDITOR_TOOL_BAR_ITEM_SHAPE)
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { P8PEditorToolBar };
|
30
app/components/editors/p8p_editors_common.js
Normal file
30
app/components/editors/p8p_editors_common.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - Редакторы панелей
|
||||||
|
Общие ресурсы редакторов
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Стили
|
||||||
|
const STYLES = {
|
||||||
|
CHIP: (fullWidth = false, multiLine = false) => ({
|
||||||
|
...(multiLine ? { height: "auto" } : {}),
|
||||||
|
"& .MuiChip-label": {
|
||||||
|
...(multiLine
|
||||||
|
? {
|
||||||
|
display: "block",
|
||||||
|
whiteSpace: "normal"
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
...(fullWidth ? { width: "100%" } : {})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { STYLES };
|
@ -11,7 +11,6 @@ import React from "react"; //Классы React
|
|||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import { Box, Typography } from "@mui/material"; //Интерфейсные элементы
|
import { Box, Typography } from "@mui/material"; //Интерфейсные элементы
|
||||||
import { useComponentModule } from "./components/components_hooks"; //Хуки компонентов
|
import { useComponentModule } from "./components/components_hooks"; //Хуки компонентов
|
||||||
import "./panels_editor.css"; //Стили редактора
|
|
||||||
|
|
||||||
//-----------
|
//-----------
|
||||||
//Тело модуля
|
//Тело модуля
|
||||||
@ -27,7 +26,7 @@ const ComponentEditor = ({ id, path, settings = {}, valueProviders = {}, onSetti
|
|||||||
|
|
||||||
//Формирование представления
|
//Формирование представления
|
||||||
return (
|
return (
|
||||||
<Box className={"component-editor__wrap"}>
|
<Box>
|
||||||
{haveComponent && init && (
|
{haveComponent && init && (
|
||||||
<ComponentEditor.default id={id} {...settings} valueProviders={valueProviders} onSettingsChange={onSettingsChange} />
|
<ComponentEditor.default id={id} {...settings} valueProviders={valueProviders} onSettingsChange={onSettingsChange} />
|
||||||
)}
|
)}
|
||||||
|
@ -11,7 +11,6 @@ import React from "react"; //Классы React
|
|||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import { Box, Typography } from "@mui/material"; //Интерфейсные элементы
|
import { Box, Typography } from "@mui/material"; //Интерфейсные элементы
|
||||||
import { useComponentModule } from "./components/components_hooks"; //Хуки компонентов
|
import { useComponentModule } from "./components/components_hooks"; //Хуки компонентов
|
||||||
import "./panels_editor.css"; //Стили редактора
|
|
||||||
|
|
||||||
//-----------
|
//-----------
|
||||||
//Тело модуля
|
//Тело модуля
|
||||||
|
@ -9,8 +9,10 @@
|
|||||||
|
|
||||||
import React, { useEffect, useState } from "react"; //Классы React
|
import React, { useEffect, useState } from "react"; //Классы React
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import { DATA_SOURCE_SHAPE, DataSource, EditorBox, EditorSubHeader } from "../editors_common"; //Общие компоненты редакторов
|
import { P8PEditorBox } from "../../../../components/editors/p8p_editor_box"; //Контейнер редактора
|
||||||
import "../../panels_editor.css"; //Стили редактора
|
import { P8PEditorSubHeader } from "../../../../components/editors/p8p_editor_sub_header"; //Заголовок раздела редактора
|
||||||
|
import { P8P_DATA_SOURCE_SHAPE } from "../../../../components/editors/p8p_data_source_common"; //Общие ресурсы источника данных
|
||||||
|
import { P8PDataSource } from "../../../../components/editors/p8p_data_source"; //Источник данных
|
||||||
|
|
||||||
//-----------
|
//-----------
|
||||||
//Тело модуля
|
//Тело модуля
|
||||||
@ -34,17 +36,17 @@ const ChartEditor = ({ id, dataSource = null, valueProviders = {}, onSettingsCha
|
|||||||
|
|
||||||
//Формирование представления
|
//Формирование представления
|
||||||
return (
|
return (
|
||||||
<EditorBox title={"Параметры графика"} onSave={handleSave}>
|
<P8PEditorBox title={"Параметры графика"} onSave={handleSave}>
|
||||||
<EditorSubHeader title={"Источник данных"} />
|
<P8PEditorSubHeader title={"Источник данных"} />
|
||||||
<DataSource dataSource={settings?.dataSource} valueProviders={valueProviders} onChange={handleDataSourceChange} />
|
<P8PDataSource dataSource={settings?.dataSource} valueProviders={valueProviders} onChange={handleDataSourceChange} />
|
||||||
</EditorBox>
|
</P8PEditorBox>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
//Контроль свойств компонента - График (редактор настроек)
|
//Контроль свойств компонента - График (редактор настроек)
|
||||||
ChartEditor.propTypes = {
|
ChartEditor.propTypes = {
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
dataSource: DATA_SOURCE_SHAPE,
|
dataSource: P8P_DATA_SOURCE_SHAPE,
|
||||||
valueProviders: PropTypes.object,
|
valueProviders: PropTypes.object,
|
||||||
onSettingsChange: PropTypes.func
|
onSettingsChange: PropTypes.func
|
||||||
};
|
};
|
||||||
|
@ -11,10 +11,13 @@ import React from "react"; //Классы React
|
|||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import { Paper } from "@mui/material"; //Интерфейсные элементы
|
import { Paper } from "@mui/material"; //Интерфейсные элементы
|
||||||
import { P8PChart } from "../../../../components/p8p_chart"; //График
|
import { P8PChart } from "../../../../components/p8p_chart"; //График
|
||||||
import { useComponentDataSource } from "../components_hooks"; //Хуки для данных
|
import { useDataSource } from "../../../../components/editors/p8p_data_source_hooks"; //Хуки для данных
|
||||||
import { DATA_SOURCE_SHAPE } from "../editors_common"; //Общие объекты компонентов
|
import { P8P_DATA_SOURCE_SHAPE } from "../../../../components/editors/p8p_data_source_common"; //Общие ресурсы источника данных
|
||||||
import { COMPONENT_MESSAGE_TYPE, COMPONENT_MESSAGES, ComponentInlineMessage } from "../views_common"; //Общие компоненты представлений
|
import {
|
||||||
import "../../panels_editor.css"; //Стили редактора
|
P8P_COMPONENT_INLINE_MESSAGE_TYPE,
|
||||||
|
P8P_COMPONENT_INLINE_MESSAGE,
|
||||||
|
P8PComponentInlineMessage
|
||||||
|
} from "../../../../components/editors/p8p_component_inline_message"; //Информационное сообщение внутри компонента
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
//Константы
|
//Константы
|
||||||
@ -38,7 +41,7 @@ const STYLES = {
|
|||||||
//График (представление)
|
//График (представление)
|
||||||
const Chart = ({ dataSource = null, values = {} } = {}) => {
|
const Chart = ({ dataSource = null, values = {} } = {}) => {
|
||||||
//Собственное состояние - данные
|
//Собственное состояние - данные
|
||||||
const [data, error] = useComponentDataSource({ dataSource, values });
|
const [data, error] = useDataSource({ dataSource, values });
|
||||||
|
|
||||||
//Флаг настроенности графика
|
//Флаг настроенности графика
|
||||||
const haveConfing = dataSource?.stored ? true : false;
|
const haveConfing = dataSource?.stored ? true : false;
|
||||||
@ -55,11 +58,11 @@ const Chart = ({ dataSource = null, values = {} } = {}) => {
|
|||||||
{haveConfing && haveData ? (
|
{haveConfing && haveData ? (
|
||||||
<P8PChart style={STYLES.CHART} {...chart} />
|
<P8PChart style={STYLES.CHART} {...chart} />
|
||||||
) : (
|
) : (
|
||||||
<ComponentInlineMessage
|
<P8PComponentInlineMessage
|
||||||
icon={COMPONENT_ICON}
|
icon={COMPONENT_ICON}
|
||||||
name={COMPONENT_NAME}
|
name={COMPONENT_NAME}
|
||||||
message={!haveConfing ? COMPONENT_MESSAGES.NO_SETTINGS : error ? error : COMPONENT_MESSAGES.NO_DATA_FOUND}
|
message={!haveConfing ? P8P_COMPONENT_INLINE_MESSAGE.NO_SETTINGS : error ? error : P8P_COMPONENT_INLINE_MESSAGE.NO_DATA_FOUND}
|
||||||
type={error ? COMPONENT_MESSAGE_TYPE.ERROR : COMPONENT_MESSAGE_TYPE.COMMON}
|
type={error ? P8P_COMPONENT_INLINE_MESSAGE_TYPE.ERROR : P8P_COMPONENT_INLINE_MESSAGE_TYPE.COMMON}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Paper>
|
</Paper>
|
||||||
@ -68,7 +71,7 @@ const Chart = ({ dataSource = null, values = {} } = {}) => {
|
|||||||
|
|
||||||
//Контроль свойств компонента - График (представление)
|
//Контроль свойств компонента - График (представление)
|
||||||
Chart.propTypes = {
|
Chart.propTypes = {
|
||||||
dataSource: DATA_SOURCE_SHAPE,
|
dataSource: P8P_DATA_SOURCE_SHAPE,
|
||||||
values: PropTypes.object
|
values: PropTypes.object
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ const COMPONETNS = [
|
|||||||
{
|
{
|
||||||
name: "Форма",
|
name: "Форма",
|
||||||
path: "form",
|
path: "form",
|
||||||
settings: {
|
settings2: {
|
||||||
title: "Параметры формирования",
|
title: "Параметры формирования",
|
||||||
autoApply: true,
|
autoApply: true,
|
||||||
items: [
|
items: [
|
||||||
@ -75,7 +75,7 @@ const COMPONETNS = [
|
|||||||
{
|
{
|
||||||
name: "Индикатор",
|
name: "Индикатор",
|
||||||
path: "indicator",
|
path: "indicator",
|
||||||
settings: {
|
settings2: {
|
||||||
dataSource: {
|
dataSource: {
|
||||||
type: "USER_PROC",
|
type: "USER_PROC",
|
||||||
userProc: "ИндКолДогКонтрТип",
|
userProc: "ИндКолДогКонтрТип",
|
||||||
@ -95,7 +95,7 @@ const COMPONETNS = [
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
} /*,
|
||||||
{
|
{
|
||||||
name: "Индикатор2",
|
name: "Индикатор2",
|
||||||
path: "indicator",
|
path: "indicator",
|
||||||
@ -119,7 +119,7 @@ const COMPONETNS = [
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
];
|
];
|
||||||
|
|
||||||
//----------------
|
//----------------
|
||||||
|
@ -7,17 +7,13 @@
|
|||||||
//Подключение библиотек
|
//Подключение библиотек
|
||||||
//---------------------
|
//---------------------
|
||||||
|
|
||||||
import { useState, useContext, useEffect, useRef } from "react"; //Классы React
|
import { useState, useEffect } 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)
|
//Отложенная загрузка модуля компонента (как альтернативу можно применять React.lazy)
|
||||||
const useComponentModule = ({ path = null, module = "view" } = {}) => {
|
const useComponentModule = ({ path = null, module = "view" } = {}) => {
|
||||||
//Собственное состояние - импортированный модуль компонента
|
//Собственное состояние - импортированный модуль компонента
|
||||||
const [componentModule, setComponentModule] = useState(null);
|
const [componentModule, setComponentModule] = useState(null);
|
||||||
@ -41,134 +37,8 @@ const useComponentModule = ({ path = null, module = "view" } = {}) => {
|
|||||||
return [componentModule, init];
|
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_PE.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 };
|
export { useComponentModule };
|
||||||
|
@ -1,426 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - Редактор панелей
|
|
||||||
Общие компоненты редакторов свойств
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React, { useState, useContext, useEffect } from "react"; //Классы React
|
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
Stack,
|
|
||||||
IconButton,
|
|
||||||
Icon,
|
|
||||||
Typography,
|
|
||||||
Divider,
|
|
||||||
Chip,
|
|
||||||
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 { P8PDialog } from "../../../components/p8p_dialog"; //Типовой диалог
|
|
||||||
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 (
|
|
||||||
<P8PDialog title={title} onOk={onOk} onCancel={onCancel}>
|
|
||||||
{children}
|
|
||||||
</P8PDialog>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств компонента - диалог настройки
|
|
||||||
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 };
|
|
@ -27,7 +27,10 @@ import {
|
|||||||
IconButton
|
IconButton
|
||||||
} from "@mui/material"; //Интерфейсные элементы
|
} from "@mui/material"; //Интерфейсные элементы
|
||||||
import { ApplicationСtx } from "../../../../context/application"; //Контекст приложения
|
import { ApplicationСtx } from "../../../../context/application"; //Контекст приложения
|
||||||
import { STYLES as COMMON_STYLES, EditorBox, EditorSubHeader, ConfigDialog } from "../editors_common"; //Общие компоненты редакторов
|
import { P8PEditorBox } from "../../../../components/editors/p8p_editor_box"; //Контейнер редактора
|
||||||
|
import { P8PEditorSubHeader } from "../../../../components/editors/p8p_editor_sub_header"; //Заголовок раздела редактора
|
||||||
|
import { P8PConfigDialog } from "../../../../components/editors/p8p_config_dialog"; //Диалог настройки
|
||||||
|
import { STYLES as COMMON_STYLES } from "../../../../components/editors/p8p_editors_common"; //Общие ресурсы редакторов
|
||||||
import { ITEM_SHAPE, ITEM_INITIAL, ITEMS_INITIAL, ORIENTATION } from "./common"; //Общие ресурсы и константы формы
|
import { ITEM_SHAPE, ITEM_INITIAL, ITEMS_INITIAL, ORIENTATION } from "./common"; //Общие ресурсы и константы формы
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
@ -122,7 +125,7 @@ const ItemEditor = ({ item = null, onOk = null, onCancel = null } = {}) => {
|
|||||||
|
|
||||||
//Формирование представления
|
//Формирование представления
|
||||||
return (
|
return (
|
||||||
<ConfigDialog title={`${item ? "Изменение" : "Добавление"} элемента`} onOk={handleOk} onCancel={handleCancel}>
|
<P8PConfigDialog title={`${item ? "Изменение" : "Добавление"} элемента`} onOk={handleOk} onCancel={handleCancel}>
|
||||||
<Stack direction={"column"} spacing={1}>
|
<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.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.caption} label={"Приглашение"} id={"caption"} onChange={handleChange} />
|
||||||
@ -172,7 +175,7 @@ const ItemEditor = ({ item = null, onOk = null, onCancel = null } = {}) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
</ConfigDialog>
|
</P8PConfigDialog>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -241,7 +244,7 @@ const FormEditor = ({ id, title = "", orientation = ORIENTATION.V, autoApply = f
|
|||||||
//Формирование представления
|
//Формирование представления
|
||||||
return (
|
return (
|
||||||
settings && (
|
settings && (
|
||||||
<EditorBox title={"Параметры формы"} onSave={handleSave}>
|
<P8PEditorBox title={"Параметры формы"} onSave={handleSave}>
|
||||||
{itemEditor.display && (
|
{itemEditor.display && (
|
||||||
<ItemEditor
|
<ItemEditor
|
||||||
item={itemEditor.index !== null ? { ...settings.items[itemEditor.index] } : null}
|
item={itemEditor.index !== null ? { ...settings.items[itemEditor.index] } : null}
|
||||||
@ -249,7 +252,7 @@ const FormEditor = ({ id, title = "", orientation = ORIENTATION.V, autoApply = f
|
|||||||
onOk={handleItemSave}
|
onOk={handleItemSave}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<EditorSubHeader title={"Общие"} />
|
<P8PEditorSubHeader title={"Общие"} />
|
||||||
<TextField type={"text"} variant={"standard"} value={settings.title} label={"Заголовок"} name={"title"} onChange={handleChange} />
|
<TextField type={"text"} variant={"standard"} value={settings.title} label={"Заголовок"} name={"title"} onChange={handleChange} />
|
||||||
<FormControl variant={"standard"}>
|
<FormControl variant={"standard"}>
|
||||||
<InputLabel id={"orientation-label"}>Ориентация</InputLabel>
|
<InputLabel id={"orientation-label"}>Ориентация</InputLabel>
|
||||||
@ -268,7 +271,7 @@ const FormEditor = ({ id, title = "", orientation = ORIENTATION.V, autoApply = f
|
|||||||
control={<Switch name={"autoApply"} checked={settings.autoApply} onChange={handleChange} />}
|
control={<Switch name={"autoApply"} checked={settings.autoApply} onChange={handleChange} />}
|
||||||
label={"Автоподтверждение"}
|
label={"Автоподтверждение"}
|
||||||
/>
|
/>
|
||||||
<EditorSubHeader title={"Элементы"} />
|
<P8PEditorSubHeader title={"Элементы"} />
|
||||||
{Array.isArray(settings?.items) &&
|
{Array.isArray(settings?.items) &&
|
||||||
settings.items.length > 0 &&
|
settings.items.length > 0 &&
|
||||||
settings.items.map((item, i) => (
|
settings.items.map((item, i) => (
|
||||||
@ -284,7 +287,7 @@ const FormEditor = ({ id, title = "", orientation = ORIENTATION.V, autoApply = f
|
|||||||
<Button startIcon={<Icon>add</Icon>} onClick={handleItemAdd}>
|
<Button startIcon={<Icon>add</Icon>} onClick={handleItemAdd}>
|
||||||
Добавить элемент
|
Добавить элемент
|
||||||
</Button>
|
</Button>
|
||||||
</EditorBox>
|
</P8PEditorBox>
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -11,9 +11,8 @@ import React, { useEffect, useState, useContext } from "react"; //Классы R
|
|||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import { Paper, Stack, Typography, Icon, TextField, IconButton, InputAdornment } from "@mui/material"; //Интерфейсные элементы
|
import { Paper, Stack, Typography, Icon, TextField, IconButton, InputAdornment } from "@mui/material"; //Интерфейсные элементы
|
||||||
import { ApplicationСtx } from "../../../../context/application"; //Контекст приложения
|
import { ApplicationСtx } from "../../../../context/application"; //Контекст приложения
|
||||||
import { COMPONENT_MESSAGES, ComponentInlineMessage } from "../views_common"; //Общие компоненты представлений
|
import { P8P_COMPONENT_INLINE_MESSAGE, P8PComponentInlineMessage } from "../../../../components/editors/p8p_component_inline_message"; //Информационное сообщение внутри компонента
|
||||||
import { ITEM_SHAPE, ITEMS_INITIAL, ORIENTATION } from "./common"; //Общие ресурсы и константы формы
|
import { ITEM_SHAPE, ITEMS_INITIAL, ORIENTATION } from "./common"; //Общие ресурсы и константы формы
|
||||||
import "../../panels_editor.css"; //Стили редактора
|
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
//Константы
|
//Константы
|
||||||
@ -50,6 +49,7 @@ const FormItem = ({ item = null, fullWidth = false, value = "", onChange = null
|
|||||||
inputParameters: [{ name: item.inputParameter, value }],
|
inputParameters: [{ name: item.inputParameter, value }],
|
||||||
callBack: res => res.success && onChange && onChange(item.name, res.outParameters[item.outputParameter])
|
callBack: res => res.success && onChange && onChange(item.name, res.outParameters[item.outputParameter])
|
||||||
});
|
});
|
||||||
|
|
||||||
//Формирование представления
|
//Формирование представления
|
||||||
return (
|
return (
|
||||||
item && (
|
item && (
|
||||||
@ -144,7 +144,7 @@ const Form = ({ title = null, orientation = ORIENTATION.V, autoApply = false, it
|
|||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
) : (
|
) : (
|
||||||
<ComponentInlineMessage icon={COMPONENT_ICON} name={COMPONENT_NAME} message={COMPONENT_MESSAGES.NO_SETTINGS} />
|
<P8PComponentInlineMessage icon={COMPONENT_ICON} name={COMPONENT_NAME} message={P8P_COMPONENT_INLINE_MESSAGE.NO_SETTINGS} />
|
||||||
)}
|
)}
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
||||||
|
@ -9,8 +9,10 @@
|
|||||||
|
|
||||||
import React, { useEffect, useState } from "react"; //Классы React
|
import React, { useEffect, useState } from "react"; //Классы React
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import { DATA_SOURCE_SHAPE, DataSource, EditorBox, EditorSubHeader } from "../editors_common"; //Общие компоненты редакторов
|
import { P8PEditorBox } from "../../../../components/editors/p8p_editor_box"; //Контейнер редактора
|
||||||
import "../../panels_editor.css"; //Стили редактора
|
import { P8PEditorSubHeader } from "../../../../components/editors/p8p_editor_sub_header"; //Заголовок раздела редактора
|
||||||
|
import { P8P_DATA_SOURCE_SHAPE } from "../../../../components/editors/p8p_data_source_common"; //Общие ресурсы источника данных
|
||||||
|
import { P8PDataSource } from "../../../../components/editors/p8p_data_source"; //Источник данных
|
||||||
|
|
||||||
//-----------
|
//-----------
|
||||||
//Тело модуля
|
//Тело модуля
|
||||||
@ -34,17 +36,17 @@ const IndicatorEditor = ({ id, dataSource = null, valueProviders = {}, onSetting
|
|||||||
|
|
||||||
//Формирование представления
|
//Формирование представления
|
||||||
return (
|
return (
|
||||||
<EditorBox title={"Параметры индикатора"} onSave={handleSave}>
|
<P8PEditorBox title={"Параметры индикатора"} onSave={handleSave}>
|
||||||
<EditorSubHeader title={"Источник данных"} />
|
<P8PEditorSubHeader title={"Источник данных"} />
|
||||||
<DataSource dataSource={settings?.dataSource} valueProviders={valueProviders} onChange={handleDataSourceChange} />
|
<P8PDataSource dataSource={settings?.dataSource} valueProviders={valueProviders} onChange={handleDataSourceChange} />
|
||||||
</EditorBox>
|
</P8PEditorBox>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
//Контроль свойств компонента - Индикатор (редактор настроек)
|
//Контроль свойств компонента - Индикатор (редактор настроек)
|
||||||
IndicatorEditor.propTypes = {
|
IndicatorEditor.propTypes = {
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
dataSource: DATA_SOURCE_SHAPE,
|
dataSource: P8P_DATA_SOURCE_SHAPE,
|
||||||
valueProviders: PropTypes.object,
|
valueProviders: PropTypes.object,
|
||||||
onSettingsChange: PropTypes.func
|
onSettingsChange: PropTypes.func
|
||||||
};
|
};
|
||||||
|
@ -11,10 +11,13 @@ import React from "react"; //Классы React
|
|||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import { Paper } from "@mui/material"; //Интерфейсные элементы
|
import { Paper } from "@mui/material"; //Интерфейсные элементы
|
||||||
import { P8PIndicator } from "../../../../components/p8p_indicator"; //Компонент индикатора
|
import { P8PIndicator } from "../../../../components/p8p_indicator"; //Компонент индикатора
|
||||||
import { useComponentDataSource } from "../components_hooks"; //Хуки для данных
|
import { useDataSource } from "../../../../components/editors/p8p_data_source_hooks"; //Хуки для данных
|
||||||
import { DATA_SOURCE_SHAPE } from "../editors_common"; //Общие объекты компонентов
|
import { P8P_DATA_SOURCE_SHAPE } from "../../../../components/editors/p8p_data_source_common"; //Общие ресурсы источника данных
|
||||||
import { COMPONENT_MESSAGE_TYPE, COMPONENT_MESSAGES, ComponentInlineMessage } from "../views_common"; //Общие компоненты представлений
|
import {
|
||||||
import "../../panels_editor.css"; //Стили редактора
|
P8P_COMPONENT_INLINE_MESSAGE_TYPE,
|
||||||
|
P8P_COMPONENT_INLINE_MESSAGE,
|
||||||
|
P8PComponentInlineMessage
|
||||||
|
} from "../../../../components/editors/p8p_component_inline_message"; //Информационное сообщение внутри компонента
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
//Константы
|
//Константы
|
||||||
@ -38,7 +41,7 @@ const STYLES = {
|
|||||||
//Индикатор (представление)
|
//Индикатор (представление)
|
||||||
const Indicator = ({ dataSource = null, values = {} } = {}) => {
|
const Indicator = ({ dataSource = null, values = {} } = {}) => {
|
||||||
//Собственное состояние - данные
|
//Собственное состояние - данные
|
||||||
const [data, error] = useComponentDataSource({ dataSource, values });
|
const [data, error] = useDataSource({ dataSource, values });
|
||||||
|
|
||||||
//Флаг настроенности индикатора
|
//Флаг настроенности индикатора
|
||||||
const haveConfing = dataSource?.stored ? true : false;
|
const haveConfing = dataSource?.stored ? true : false;
|
||||||
@ -60,11 +63,11 @@ const Indicator = ({ dataSource = null, values = {} } = {}) => {
|
|||||||
{haveConfing && haveData ? (
|
{haveConfing && haveData ? (
|
||||||
<P8PIndicator {...indicator} elevation={0} />
|
<P8PIndicator {...indicator} elevation={0} />
|
||||||
) : (
|
) : (
|
||||||
<ComponentInlineMessage
|
<P8PComponentInlineMessage
|
||||||
icon={COMPONENT_ICON}
|
icon={COMPONENT_ICON}
|
||||||
name={COMPONENT_NAME}
|
name={COMPONENT_NAME}
|
||||||
message={!haveConfing ? COMPONENT_MESSAGES.NO_SETTINGS : error ? error : COMPONENT_MESSAGES.NO_DATA_FOUND}
|
message={!haveConfing ? P8P_COMPONENT_INLINE_MESSAGE.NO_SETTINGS : error ? error : P8P_COMPONENT_INLINE_MESSAGE.NO_DATA_FOUND}
|
||||||
type={error ? COMPONENT_MESSAGE_TYPE.ERROR : COMPONENT_MESSAGE_TYPE.COMMON}
|
type={error ? P8P_COMPONENT_INLINE_MESSAGE_TYPE.ERROR : P8P_COMPONENT_INLINE_MESSAGE_TYPE.COMMON}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Paper>
|
</Paper>
|
||||||
@ -73,7 +76,7 @@ const Indicator = ({ dataSource = null, values = {} } = {}) => {
|
|||||||
|
|
||||||
//Контроль свойств компонента - Индикатор (представление)
|
//Контроль свойств компонента - Индикатор (представление)
|
||||||
Indicator.propTypes = {
|
Indicator.propTypes = {
|
||||||
dataSource: DATA_SOURCE_SHAPE,
|
dataSource: P8P_DATA_SOURCE_SHAPE,
|
||||||
values: PropTypes.object
|
values: PropTypes.object
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -9,8 +9,10 @@
|
|||||||
|
|
||||||
import React, { useEffect, useState } from "react"; //Классы React
|
import React, { useEffect, useState } from "react"; //Классы React
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import { DATA_SOURCE_SHAPE, DataSource, EditorBox, EditorSubHeader } from "../editors_common"; //Общие компоненты редакторов
|
import { P8PEditorBox } from "../../../../components/editors/p8p_editor_box"; //Контейнер редактора
|
||||||
import "../../panels_editor.css"; //Стили редактора
|
import { P8PEditorSubHeader } from "../../../../components/editors/p8p_editor_sub_header"; //Заголовок раздела редактора
|
||||||
|
import { P8P_DATA_SOURCE_SHAPE } from "../../../../components/editors/p8p_data_source_common"; //Общие ресурсы источника данных
|
||||||
|
import { P8PDataSource } from "../../../../components/editors/p8p_data_source"; //Источник данных
|
||||||
|
|
||||||
//-----------
|
//-----------
|
||||||
//Тело модуля
|
//Тело модуля
|
||||||
@ -34,17 +36,17 @@ const TableEditor = ({ id, dataSource = null, valueProviders = {}, onSettingsCha
|
|||||||
|
|
||||||
//Формирование представления
|
//Формирование представления
|
||||||
return (
|
return (
|
||||||
<EditorBox title={"Параметры таблицы"} onSave={handleSave}>
|
<P8PEditorBox title={"Параметры таблицы"} onSave={handleSave}>
|
||||||
<EditorSubHeader title={"Источник данных"} />
|
<P8PEditorSubHeader title={"Источник данных"} />
|
||||||
<DataSource dataSource={settings?.dataSource} valueProviders={valueProviders} onChange={handleDataSourceChange} />
|
<P8PDataSource dataSource={settings?.dataSource} valueProviders={valueProviders} onChange={handleDataSourceChange} />
|
||||||
</EditorBox>
|
</P8PEditorBox>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
//Контроль свойств компонента - Таблица (редактор настроек)
|
//Контроль свойств компонента - Таблица (редактор настроек)
|
||||||
TableEditor.propTypes = {
|
TableEditor.propTypes = {
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
dataSource: DATA_SOURCE_SHAPE,
|
dataSource: P8P_DATA_SOURCE_SHAPE,
|
||||||
valueProviders: PropTypes.object,
|
valueProviders: PropTypes.object,
|
||||||
onSettingsChange: PropTypes.func
|
onSettingsChange: PropTypes.func
|
||||||
};
|
};
|
||||||
|
@ -13,10 +13,13 @@ import { Paper } from "@mui/material"; //Интерфейсные элемент
|
|||||||
import { APP_STYLES } from "../../../../../app.styles"; //Типовые стили
|
import { APP_STYLES } from "../../../../../app.styles"; //Типовые стили
|
||||||
import { P8PDataGrid } from "../../../../components/p8p_data_grid"; //Таблица данных
|
import { P8PDataGrid } from "../../../../components/p8p_data_grid"; //Таблица данных
|
||||||
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
||||||
import { useComponentDataSource } from "../components_hooks"; //Хуки для данных
|
import { useDataSource } from "../../../../components/editors/p8p_data_source_hooks"; //Хуки для данных
|
||||||
import { DATA_SOURCE_SHAPE } from "../editors_common"; //Общие объекты компонентов
|
import { P8P_DATA_SOURCE_SHAPE } from "../../../../components/editors/p8p_data_source_common"; //Общие ресурсы источника данных
|
||||||
import { COMPONENT_MESSAGE_TYPE, COMPONENT_MESSAGES, ComponentInlineMessage } from "../views_common"; //Общие компоненты представлений
|
import {
|
||||||
import "../../panels_editor.css"; //Стили редактора
|
P8P_COMPONENT_INLINE_MESSAGE_TYPE,
|
||||||
|
P8P_COMPONENT_INLINE_MESSAGE,
|
||||||
|
P8PComponentInlineMessage
|
||||||
|
} from "../../../../components/editors/p8p_component_inline_message"; //Информационное сообщение внутри компонента
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
//Константы
|
//Константы
|
||||||
@ -45,7 +48,7 @@ const STYLES = {
|
|||||||
//Таблица (представление)
|
//Таблица (представление)
|
||||||
const Table = ({ dataSource = null, values = {} } = {}) => {
|
const Table = ({ dataSource = null, values = {} } = {}) => {
|
||||||
//Собственное состояние - данные
|
//Собственное состояние - данные
|
||||||
const [data, error] = useComponentDataSource({ dataSource, values });
|
const [data, error] = useDataSource({ dataSource, values });
|
||||||
|
|
||||||
//Флаг настроенности таблицы
|
//Флаг настроенности таблицы
|
||||||
const haveConfing = dataSource?.stored ? true : false;
|
const haveConfing = dataSource?.stored ? true : false;
|
||||||
@ -72,11 +75,11 @@ const Table = ({ dataSource = null, values = {} } = {}) => {
|
|||||||
containerComponentProps={{ sx: STYLES.DATA_GRID_CONTAINER, elevation: 0 }}
|
containerComponentProps={{ sx: STYLES.DATA_GRID_CONTAINER, elevation: 0 }}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<ComponentInlineMessage
|
<P8PComponentInlineMessage
|
||||||
icon={COMPONENT_ICON}
|
icon={COMPONENT_ICON}
|
||||||
name={COMPONENT_NAME}
|
name={COMPONENT_NAME}
|
||||||
message={!haveConfing ? COMPONENT_MESSAGES.NO_SETTINGS : error ? error : COMPONENT_MESSAGES.NO_DATA_FOUND}
|
message={!haveConfing ? P8P_COMPONENT_INLINE_MESSAGE.NO_SETTINGS : error ? error : P8P_COMPONENT_INLINE_MESSAGE.NO_DATA_FOUND}
|
||||||
type={error ? COMPONENT_MESSAGE_TYPE.ERROR : COMPONENT_MESSAGE_TYPE.COMMON}
|
type={error ? P8P_COMPONENT_INLINE_MESSAGE_TYPE.ERROR : P8P_COMPONENT_INLINE_MESSAGE_TYPE.COMMON}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Paper>
|
</Paper>
|
||||||
@ -85,7 +88,7 @@ const Table = ({ dataSource = null, values = {} } = {}) => {
|
|||||||
|
|
||||||
//Контроль свойств компонента - Таблица (представление)
|
//Контроль свойств компонента - Таблица (представление)
|
||||||
Table.propTypes = {
|
Table.propTypes = {
|
||||||
dataSource: DATA_SOURCE_SHAPE,
|
dataSource: P8P_DATA_SOURCE_SHAPE,
|
||||||
values: PropTypes.object
|
values: PropTypes.object
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
import React from "react"; //Классы React
|
import React from "react"; //Классы React
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import { IconButton, Icon, Stack } from "@mui/material"; //Интерфейсные элементы
|
import { IconButton, Icon, Stack } from "@mui/material"; //Интерфейсные элементы
|
||||||
import "./panels_editor.css"; //Кастомные стили
|
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
//Константы
|
//Константы
|
||||||
|
@ -12,17 +12,6 @@
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.component-editor__wrap {
|
|
||||||
}
|
|
||||||
|
|
||||||
.component-editor__container {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.component-editor__divider {
|
|
||||||
padding-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.component-view__wrap {
|
.component-view__wrap {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,10 @@
|
|||||||
|
|
||||||
import React, { useEffect, useState, useContext } from "react"; //Классы React
|
import React, { useEffect, useState, useContext } from "react"; //Классы React
|
||||||
import { Responsive, WidthProvider } from "react-grid-layout"; //Адаптивный макет
|
import { Responsive, WidthProvider } from "react-grid-layout"; //Адаптивный макет
|
||||||
import { Box, Grid, Stack, Menu, MenuItem, IconButton, Icon, Fab } from "@mui/material"; //Интерфейсные элементы
|
import { Box, Grid, Menu, MenuItem, Icon, Fab } from "@mui/material"; //Интерфейсные элементы
|
||||||
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
||||||
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Рабочая область приложения
|
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Рабочая область приложения
|
||||||
|
import { P8PEditorToolBar } from "../../components/editors/p8p_editor_toolbar"; //Панель инструментов редактора
|
||||||
import { genGUID } from "../../core/utils"; //Общие вспомогательные функции
|
import { genGUID } from "../../core/utils"; //Общие вспомогательные функции
|
||||||
import { LayoutItem } from "./layout_item"; //Элемент макета
|
import { LayoutItem } from "./layout_item"; //Элемент макета
|
||||||
import { ComponentView } from "./component_view"; //Представление компонента панели
|
import { ComponentView } from "./component_view"; //Представление компонента панели
|
||||||
@ -19,6 +20,7 @@ import { ComponentEditor } from "./component_editor"; //Редактор сво
|
|||||||
import { COMPONETNS } from "./components/components"; //Описание доступных компонентов
|
import { COMPONETNS } from "./components/components"; //Описание доступных компонентов
|
||||||
import "react-grid-layout/css/styles.css"; //Стили для адаптивного макета
|
import "react-grid-layout/css/styles.css"; //Стили для адаптивного макета
|
||||||
import "react-resizable/css/styles.css"; //Стили для адаптивного макета
|
import "react-resizable/css/styles.css"; //Стили для адаптивного макета
|
||||||
|
import "./panels_editor.css"; //Стили редактора панелей
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
//Константы
|
//Константы
|
||||||
@ -140,9 +142,9 @@ const PanelsEditor = () => {
|
|||||||
|
|
||||||
//При подключении к странице
|
//При подключении к странице
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
addComponent(COMPONETNS[0]);
|
//addComponent(COMPONETNS[0]);
|
||||||
addComponent(COMPONETNS[3]);
|
//addComponent(COMPONETNS[3]);
|
||||||
addComponent(COMPONETNS[4]);
|
//addComponent(COMPONETNS[4]);
|
||||||
//addComponent(COMPONETNS[1]);
|
//addComponent(COMPONETNS[1]);
|
||||||
//addComponent(COMPONETNS[2]);
|
//addComponent(COMPONETNS[2]);
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@ -171,14 +173,16 @@ const PanelsEditor = () => {
|
|||||||
|
|
||||||
//Панель инструмментов
|
//Панель инструмментов
|
||||||
const toolBar = (
|
const toolBar = (
|
||||||
<Stack direction={"row"} p={1}>
|
<P8PEditorToolBar
|
||||||
<IconButton onClick={toggleEditMode} title={"Запустить"}>
|
items={[
|
||||||
<Icon>play_arrow</Icon>
|
{ icon: "play_arrow", title: "Запустить", onClick: toggleEditMode },
|
||||||
</IconButton>
|
{
|
||||||
<IconButton onClick={handleAddClick} title={"Добавить элемент"}>
|
icon: "add",
|
||||||
<Icon>add</Icon>
|
title: "Добавить элемент",
|
||||||
</IconButton>
|
onClick: handleAddClick
|
||||||
</Stack>
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
//Генерация содержимого
|
//Генерация содержимого
|
||||||
|
Loading…
x
Reference in New Issue
Block a user