forked from CITKParus/P8-Panels
merge upstream
This commit is contained in:
commit
fef41f5186
23
app.text.js
23
app.text.js
@ -12,14 +12,20 @@ export const TITLES = {
|
|||||||
INFO: "Информация", //Информационный блок
|
INFO: "Информация", //Информационный блок
|
||||||
WARN: "Предупреждение", //Блок предупреждения
|
WARN: "Предупреждение", //Блок предупреждения
|
||||||
ERR: "Ошибка", //Информация об ошибке
|
ERR: "Ошибка", //Информация об ошибке
|
||||||
DEFAULT_PANELS_GROUP: "Без привязки к группе" //Заголовок группы панелей по умолчанию
|
DEFAULT_PANELS_GROUP: "Без привязки к группе", //Заголовок группы панелей по умолчанию
|
||||||
|
DATA_SOURCE_CONFIG: "Настройка источника данных", //Заголовок для настройки источника данных
|
||||||
|
INSERT: "Добавление", //Заголовок для диалогов/форм добавления
|
||||||
|
UPDATE: "Исправление" //Заголовок для диалогов/форм исправления
|
||||||
};
|
};
|
||||||
|
|
||||||
//Текст
|
//Текст
|
||||||
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 +44,11 @@ export const BUTTONS = {
|
|||||||
FILTER: "Фильтр", //Фильтрация
|
FILTER: "Фильтр", //Фильтрация
|
||||||
MORE: "Ещё", //Догрузка данных
|
MORE: "Ещё", //Догрузка данных
|
||||||
APPLY: "Применить", //Сохранение без закрытия интерфейса ввода
|
APPLY: "Применить", //Сохранение без закрытия интерфейса ввода
|
||||||
SAVE: "Сохранить" //Сохранение
|
SAVE: "Сохранить", //Сохранение
|
||||||
|
CONFIG: "Настроить", //Настройка
|
||||||
|
INSERT: "Добавить", //Добавление
|
||||||
|
UPDATE: "Исправить", //Исправление
|
||||||
|
DELETE: "Удалить" //Удаление
|
||||||
};
|
};
|
||||||
|
|
||||||
//Метки атрибутов, сопроводительные надписи
|
//Метки атрибутов, сопроводительные надписи
|
||||||
@ -51,7 +61,9 @@ export const CAPTIONS = {
|
|||||||
START: "Начало",
|
START: "Начало",
|
||||||
END: "Окончание",
|
END: "Окончание",
|
||||||
PROGRESS: "Прогресс",
|
PROGRESS: "Прогресс",
|
||||||
LEGEND: "Легенда"
|
LEGEND: "Легенда",
|
||||||
|
USER_PROC: "Пользовательская процедура",
|
||||||
|
QUERY: "Запрос"
|
||||||
};
|
};
|
||||||
|
|
||||||
//Типовые сообщения об ошибках
|
//Типовые сообщения об ошибках
|
||||||
@ -59,7 +71,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 };
|
59
app/components/editors/p8p_editor_box.js
Normal file
59
app/components/editors/p8p_editor_box.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
Парус 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);
|
||||||
|
|
||||||
|
//Флаг отображения кнопок сохранения
|
||||||
|
const showSaveBar = onSave ? true : false;
|
||||||
|
|
||||||
|
//Формирование представления
|
||||||
|
return (
|
||||||
|
<Box p={2}>
|
||||||
|
<Divider>{title}</Divider>
|
||||||
|
<Stack direction={"column"} spacing={1}>
|
||||||
|
{children}
|
||||||
|
</Stack>
|
||||||
|
{showSaveBar && (
|
||||||
|
<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 };
|
53
app/components/editors/p8p_editor_toolbar.js
Normal file
53
app/components/editors/p8p_editor_toolbar.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
Парус 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,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
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} disabled={item?.disabled === true}>
|
||||||
|
<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>
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
//Генерация содержимого
|
//Генерация содержимого
|
||||||
|
23
app/panels/query_editor/common.js
Normal file
23
app/panels/query_editor/common.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - Редактор запросов
|
||||||
|
Обще ресурсы и константы
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Типы данных
|
||||||
|
const DATA_TYPE = { STR: 0, NUMB: 1, DATE: 2 };
|
||||||
|
|
||||||
|
//Типы элементов диаграммы
|
||||||
|
const NODE_TYPE = {
|
||||||
|
ENTITY: "entity",
|
||||||
|
ATTRIBUTE: "attribute"
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { DATA_TYPE, NODE_TYPE };
|
104
app/panels/query_editor/components/attribute/attribute.js
Normal file
104
app/panels/query_editor/components/attribute/attribute.js
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - Редактор запросов
|
||||||
|
Компоненты: Атрибут сущности
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React from "react"; //Классы React
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
import { Handle, Position, useStore } from "reactflow"; //Библиотека редактора диаграмм
|
||||||
|
import { Box, Stack, Icon, Typography } from "@mui/material"; //Компоненты UI
|
||||||
|
import { DATA_TYPE } from "../../common"; //Общие ресурсы и константы редактора
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Типовые цвета точек привязки
|
||||||
|
const HANDLE_BORDER_COLOR = "#69db7c";
|
||||||
|
const HANDLE_BORDER_COLOR_DISABLED = "#adb5bd";
|
||||||
|
|
||||||
|
//Стили
|
||||||
|
const STYLES = {
|
||||||
|
CONTAINER: { display: "flex", width: "100%", height: "100%" },
|
||||||
|
HANDLE_SOURCE: isConnecting => ({
|
||||||
|
width: 14,
|
||||||
|
height: 14,
|
||||||
|
right: -10,
|
||||||
|
border: `2px solid ${isConnecting ? HANDLE_BORDER_COLOR_DISABLED : HANDLE_BORDER_COLOR}`,
|
||||||
|
borderRadius: 7,
|
||||||
|
background: "white"
|
||||||
|
}),
|
||||||
|
HANDLE_TARGET: isConnecting => ({
|
||||||
|
width: isConnecting ? 14 : 0,
|
||||||
|
height: 14,
|
||||||
|
left: isConnecting ? -7 : 0,
|
||||||
|
border: `2px solid ${HANDLE_BORDER_COLOR}`,
|
||||||
|
borderRadius: 7,
|
||||||
|
background: "white",
|
||||||
|
visibility: isConnecting ? "visible" : "hidden"
|
||||||
|
}),
|
||||||
|
CONTENT_STACK: { width: "100%" },
|
||||||
|
TITLE_NAME_STACK: { width: "100%", containerType: "inline-size" }
|
||||||
|
};
|
||||||
|
|
||||||
|
//Иконки
|
||||||
|
const ICONS = {
|
||||||
|
[DATA_TYPE.STR]: "format_align_left",
|
||||||
|
[DATA_TYPE.NUMB]: "pin",
|
||||||
|
[DATA_TYPE.DATE]: "calendar_month",
|
||||||
|
DEFAULT: "category"
|
||||||
|
};
|
||||||
|
|
||||||
|
//Структура данных об атрибуте сущности
|
||||||
|
const ATTRIBUTE_DATA_SHAPE = PropTypes.shape({
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
title: PropTypes.string.isRequired,
|
||||||
|
dataType: PropTypes.number.isRequired
|
||||||
|
});
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Атрибут сущности
|
||||||
|
const Attribute = ({ data }) => {
|
||||||
|
//Поиск идентификатора соединяемого элемента
|
||||||
|
const connectionNodeId = useStore(state => state.connectionNodeId);
|
||||||
|
|
||||||
|
//Флаг выполнения соединения сущностей
|
||||||
|
const isConnecting = Boolean(connectionNodeId);
|
||||||
|
|
||||||
|
//Формирование представления
|
||||||
|
return (
|
||||||
|
<Box p={1} sx={STYLES.CONTAINER}>
|
||||||
|
<Handle type={"source"} position={Position.Right} style={STYLES.HANDLE_SOURCE(isConnecting)} />
|
||||||
|
<Handle type={"target"} position={Position.Left} isConnectableStart={false} style={STYLES.HANDLE_TARGET(isConnecting)} />
|
||||||
|
<Stack direction={"row"} alignItems={"center"} spacing={1} sx={STYLES.CONTENT_STACK}>
|
||||||
|
<Icon color={"action"}>{ICONS[data.dataType] || ICONS.DEFAULT}</Icon>
|
||||||
|
<Stack direction={"column"} alignItems={"left"} sx={STYLES.TITLE_NAME_STACK}>
|
||||||
|
<Typography variant={"body2"} noWrap title={data.title}>
|
||||||
|
{data.title}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant={"caption"} color={"text.secondary"} noWrap title={data.name}>
|
||||||
|
{data.name}
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств компонента - Атрибут сущности
|
||||||
|
Attribute.propTypes = {
|
||||||
|
data: ATTRIBUTE_DATA_SHAPE
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { Attribute };
|
33
app/panels/query_editor/components/entity/entity.css
Normal file
33
app/panels/query_editor/components/entity/entity.css
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
.entity__wrapper {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: 1px solid var(--border-color-dark);
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: var(--shadow-entity);
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entity__wrapper[data-selected="true"] {
|
||||||
|
outline: 1px solid var(--outline-color);
|
||||||
|
border-color: var(--outline-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.entity__title {
|
||||||
|
width: 100%;
|
||||||
|
height: 50px;
|
||||||
|
align-content: center;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
font-weight: 900;
|
||||||
|
text-align: center;
|
||||||
|
background-color: var(--entity-title-bg);
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entity__name {
|
||||||
|
width: 100%;
|
||||||
|
align-content: center;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: gray;
|
||||||
|
}
|
50
app/panels/query_editor/components/entity/entity.js
Normal file
50
app/panels/query_editor/components/entity/entity.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - Редактор запросов
|
||||||
|
Компоненты: Сущность запроса
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React from "react"; //Классы React
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
import "./entity.css"; //Стили компомнента
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Структура данных о сущности запроса
|
||||||
|
const ENTITY_DATA_SHAPE = PropTypes.shape({
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
title: PropTypes.string.isRequired
|
||||||
|
});
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Сущность запроса
|
||||||
|
const Entity = ({ data, selected }) => {
|
||||||
|
return (
|
||||||
|
<div className="entity__wrapper" data-selected={selected}>
|
||||||
|
<div className="entity__title">
|
||||||
|
<span>{data.title}</span>
|
||||||
|
<div className="entity__name">{data.name}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств компонента - Сущность запроса
|
||||||
|
Entity.propTypes = {
|
||||||
|
data: ENTITY_DATA_SHAPE,
|
||||||
|
selected: PropTypes.bool.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { Entity };
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - Редактор запросов
|
||||||
|
Компонент: Диалог добавления сущности запроса
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React from "react"; //Классы React
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
import { P8PDialog } from "../../../../components/p8p_dialog"; //Типовой диалог
|
||||||
|
import { TITLES } from "../../../../../app.text"; //Общие текстовые ресурсы приложения
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Диалог добавления сущности запроса
|
||||||
|
const EntityAddDialog = ({ onOk, onCancel }) => {
|
||||||
|
//Нажатие на кнопку "Ok"
|
||||||
|
const handleOk = values => onOk && onOk({ ...values });
|
||||||
|
|
||||||
|
//Нажатие на кнопку "Отмена"
|
||||||
|
const handleCancel = () => onCancel && onCancel();
|
||||||
|
|
||||||
|
//Генерация содержимого
|
||||||
|
return (
|
||||||
|
<P8PDialog title={`${TITLES.INSERT} сущности`} inputs={[{ name: "name", value: "", label: "Имя" }]} onOk={handleOk} onCancel={handleCancel} />
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств - Диалог добавления сущности запроса
|
||||||
|
EntityAddDialog.propTypes = {
|
||||||
|
onOk: PropTypes.func,
|
||||||
|
onCancel: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { EntityAddDialog };
|
@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - Редактор запросов
|
||||||
|
Компонент: Список запросов
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React from "react"; //Классы React
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
import { Stack, List, ListItem, IconButton, Icon, ListItemButton, ListItemText, Typography } from "@mui/material"; //Интерфейсные компоненты MUI
|
||||||
|
import { BUTTONS } from "../../../../../app.text"; //Общие текстовые ресурсы приложения
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Стили
|
||||||
|
const STYLES = {
|
||||||
|
SMALL_TOOL_ICON: {
|
||||||
|
fontSize: 20
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Структура данных о сущности запроса
|
||||||
|
const QUERIES_LIST_ITEM_SHAPE = PropTypes.shape({
|
||||||
|
rn: PropTypes.number.isRequired,
|
||||||
|
code: PropTypes.string.isRequired,
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
author: PropTypes.string.isRequired,
|
||||||
|
chDate: PropTypes.string.isRequired,
|
||||||
|
modify: PropTypes.oneOf([0, 1]).isRequired,
|
||||||
|
pbl: PropTypes.oneOf([0, 1]).isRequired,
|
||||||
|
ready: PropTypes.oneOf([0, 1]).isRequired
|
||||||
|
});
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Диалог открытия запроса
|
||||||
|
const QueriesList = ({ queries = [], current = null, onSelect = null, onPbl = null, onReady = null, onEdit = null, onDelete = null } = {}) => {
|
||||||
|
//При выборе элемента списка
|
||||||
|
const handleSelectClick = query => {
|
||||||
|
onSelect && onSelect(query);
|
||||||
|
};
|
||||||
|
|
||||||
|
//При нажатии на общедоступность
|
||||||
|
const handlePblClick = (e, query) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
onPbl && onPbl(query);
|
||||||
|
};
|
||||||
|
|
||||||
|
//При нажатии на готовность
|
||||||
|
const handleReadyClick = (e, query) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
onReady && onReady(query);
|
||||||
|
};
|
||||||
|
|
||||||
|
//При нажатии на исправлении
|
||||||
|
const handleEditClick = (e, query) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
onEdit && onEdit(query);
|
||||||
|
};
|
||||||
|
|
||||||
|
//При нажатии на удаление
|
||||||
|
const handleDeleteClick = (e, query) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
onDelete && onDelete(query);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Формирование представления
|
||||||
|
return (
|
||||||
|
<List sx={{ height: "500px", width: "360px", bgcolor: "background.paper", overflowY: "auto" }}>
|
||||||
|
{queries.map((query, i) => {
|
||||||
|
const selected = query.rn === current;
|
||||||
|
const disabled = !query.modify;
|
||||||
|
const pblTitle = `${query.pbl === 1 ? "Общедоступный" : "Приватный"}${!disabled ? " - нажмите, чтобы изменить" : ""}`;
|
||||||
|
const pblIcon = query.pbl === 1 ? "groups" : "lock_person";
|
||||||
|
const readyTitle = `${query.ready === 1 ? "Готов" : "Не готов"}${!disabled ? " - нажмите, чтобы изменить" : ""}`;
|
||||||
|
const readyIcon = query.ready === 1 ? "touch_app" : "do_not_touch";
|
||||||
|
return (
|
||||||
|
<ListItem key={i}>
|
||||||
|
<ListItemButton onClick={() => handleSelectClick(query)} selected={selected}>
|
||||||
|
<ListItemText
|
||||||
|
primary={query.name}
|
||||||
|
secondaryTypographyProps={{ component: "div" }}
|
||||||
|
secondary={
|
||||||
|
<Stack direction={"column"}>
|
||||||
|
<Typography variant={"caption"}>{`${query.code}, ${query.author}, ${query.chDate}`}</Typography>
|
||||||
|
<Stack direction={"row"}>
|
||||||
|
<div title={pblTitle}>
|
||||||
|
<IconButton disabled={disabled} onClick={e => handlePblClick(e, query)}>
|
||||||
|
<Icon sx={STYLES.SMALL_TOOL_ICON}>{pblIcon}</Icon>
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
<div title={readyTitle}>
|
||||||
|
<IconButton disabled={disabled} onClick={e => handleReadyClick(e, query)}>
|
||||||
|
<Icon sx={STYLES.SMALL_TOOL_ICON}>{readyIcon}</Icon>
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Stack direction={"row"}>
|
||||||
|
<IconButton onClick={e => handleEditClick(e, query)} disabled={disabled} title={BUTTONS.UPDATE}>
|
||||||
|
<Icon>edit</Icon>
|
||||||
|
</IconButton>
|
||||||
|
<IconButton onClick={e => handleDeleteClick(e, query)} disabled={disabled} title={BUTTONS.DELETE}>
|
||||||
|
<Icon>delete</Icon>
|
||||||
|
</IconButton>
|
||||||
|
</Stack>
|
||||||
|
</ListItemButton>
|
||||||
|
</ListItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</List>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств компонента - Список запросов
|
||||||
|
QueriesList.propTypes = {
|
||||||
|
queries: PropTypes.arrayOf(QUERIES_LIST_ITEM_SHAPE),
|
||||||
|
current: PropTypes.number,
|
||||||
|
onSelect: PropTypes.func,
|
||||||
|
onPbl: PropTypes.func,
|
||||||
|
onReady: PropTypes.func,
|
||||||
|
onEdit: PropTypes.func,
|
||||||
|
onDelete: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { QueriesList };
|
@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - Редактор запросов
|
||||||
|
Компонент: Менеджер запросов
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React, { useState, useContext } from "react"; //Классы React
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
import { Button, Icon } from "@mui/material"; //Интерфейсные компоненты MUI
|
||||||
|
import { MessagingСtx } from "../../../../context/messaging"; //Контекст сообщений
|
||||||
|
import { BUTTONS } from "../../../../../app.text"; //Общие текстовые ресурсы приложения
|
||||||
|
import { P8PConfigDialog } from "../../../../components/editors/p8p_config_dialog"; //Типовой диалог настройки
|
||||||
|
import { useQuery } from "../../hooks"; //Пользовательские хуки
|
||||||
|
import { QueriesList } from "./queries_list"; //Список запросов
|
||||||
|
import { QueryIUDialog } from "./query_iu_dialog"; //Диалог добавления/исправления запроса
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Менеджер запросов
|
||||||
|
const QueriesManager = ({ current = null, onQuerySelect = null, onCancel = null } = {}) => {
|
||||||
|
//Собственное состояние - изменяемый запрос
|
||||||
|
const [modQuery, setModQuery] = useState(null);
|
||||||
|
|
||||||
|
//Работа со списком запросов
|
||||||
|
const [queries, insertQuery, updateQuery, deleteQuery, setQueryReady, setQueryPbl] = useQuery();
|
||||||
|
|
||||||
|
//Подключение к контексту сообщений
|
||||||
|
const { showMsgWarn } = useContext(MessagingСtx);
|
||||||
|
|
||||||
|
//При добавлении запроса
|
||||||
|
const handleQueryAdd = () => setModQuery(true);
|
||||||
|
|
||||||
|
//При выборе запроса
|
||||||
|
const handleQuerySelect = query => onQuerySelect && onQuerySelect(query.rn);
|
||||||
|
|
||||||
|
//При установке признака публичности
|
||||||
|
const handleQueryPblSet = query => setQueryPbl(query.rn, query.pbl === 1 ? 0 : 1);
|
||||||
|
|
||||||
|
//При установке признака готовности
|
||||||
|
const handleQueryReadySet = query => setQueryReady(query.rn, query.ready === 1 ? 0 : 1);
|
||||||
|
|
||||||
|
//При исправлении запроса
|
||||||
|
const handleQueryEdit = query => setModQuery({ ...query });
|
||||||
|
|
||||||
|
//При удалении запроса
|
||||||
|
const handleQueryDelete = query => showMsgWarn("Удалить запрос?", () => deleteQuery(query.rn));
|
||||||
|
|
||||||
|
//При закрытии диалога добавления/исправления по "ОК"
|
||||||
|
const handleIUDialogOk = async values => {
|
||||||
|
if (modQuery === true) await insertQuery(values.code, values.name);
|
||||||
|
else await updateQuery(modQuery.rn, values.code, values.name);
|
||||||
|
setModQuery(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
//При закрытии диалога добавления/исправления по "Отмена"
|
||||||
|
const handleIUDialogCancel = () => setModQuery(null);
|
||||||
|
|
||||||
|
//При закртии менеджера отменой
|
||||||
|
const handleCancel = () => onCancel && onCancel();
|
||||||
|
|
||||||
|
//Формирование представления
|
||||||
|
return (
|
||||||
|
<P8PConfigDialog title={"Запросы"} onCancel={handleCancel}>
|
||||||
|
{modQuery && (
|
||||||
|
<QueryIUDialog
|
||||||
|
code={modQuery?.code}
|
||||||
|
name={modQuery?.name}
|
||||||
|
insert={modQuery === true}
|
||||||
|
onOk={handleIUDialogOk}
|
||||||
|
onCancel={handleIUDialogCancel}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Button startIcon={<Icon>add</Icon>} onClick={handleQueryAdd}>
|
||||||
|
{BUTTONS.INSERT}
|
||||||
|
</Button>
|
||||||
|
<QueriesList
|
||||||
|
queries={queries || []}
|
||||||
|
current={current}
|
||||||
|
onSelect={handleQuerySelect}
|
||||||
|
onPbl={handleQueryPblSet}
|
||||||
|
onReady={handleQueryReadySet}
|
||||||
|
onEdit={handleQueryEdit}
|
||||||
|
onDelete={handleQueryDelete}
|
||||||
|
/>
|
||||||
|
</P8PConfigDialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств компонента - Менеджер запросов
|
||||||
|
QueriesManager.propTypes = {
|
||||||
|
current: PropTypes.number,
|
||||||
|
onQuerySelect: PropTypes.func,
|
||||||
|
onCancel: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { QueriesManager };
|
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - Редактор запросов
|
||||||
|
Компонент: Диалог добавления/исправления запроса
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React from "react"; //Классы React
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
import { P8PDialog } from "../../../../components/p8p_dialog"; //Типовой диалог
|
||||||
|
import { TITLES } from "../../../../../app.text"; //Общие текстовые ресурсы приложения
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Диалог добавления/исправления запроса
|
||||||
|
const QueryIUDialog = ({ code = "", name = "", insert = true, onOk, onCancel }) => {
|
||||||
|
//Нажатие на кнопку "Ok"
|
||||||
|
const handleOk = values => onOk && onOk({ ...values });
|
||||||
|
|
||||||
|
//Нажатие на кнопку "Отмена"
|
||||||
|
const handleCancel = () => onCancel && onCancel();
|
||||||
|
|
||||||
|
//Генерация содержимого
|
||||||
|
return (
|
||||||
|
<P8PDialog
|
||||||
|
title={`${insert === true ? TITLES.INSERT : TITLES.UPDATE} запроса`}
|
||||||
|
inputs={[
|
||||||
|
{ name: "code", value: code, label: "Мнемокод" },
|
||||||
|
{ name: "name", value: name, label: "Наименование" }
|
||||||
|
]}
|
||||||
|
onOk={handleOk}
|
||||||
|
onCancel={handleCancel}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств - Диалог добавления/исправления запроса
|
||||||
|
QueryIUDialog.propTypes = {
|
||||||
|
code: PropTypes.string,
|
||||||
|
name: PropTypes.string,
|
||||||
|
insert: PropTypes.bool,
|
||||||
|
onOk: PropTypes.func,
|
||||||
|
onCancel: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { QueryIUDialog };
|
@ -0,0 +1,9 @@
|
|||||||
|
.query_diagram {
|
||||||
|
--border-color: #dee2e6;
|
||||||
|
--border-color-dark: #adb5bd;
|
||||||
|
--outline-color: #74c0fc;
|
||||||
|
--entity-title-bg: #f1f3f5;
|
||||||
|
--shadow-entity: 0 -2px 5px 0 hsl(220 3% 15% / calc(1% + 2%)), 0 1px 1px -2px hsl(220 3% 15% / calc(1% + 3%)),
|
||||||
|
0 2px 2px -2px hsl(220 3% 15% / calc(1% + 3%)), 0 5px 5px -2px hsl(220 3% 15% / calc(1% + 4%)), 0 9px 9px -2px hsl(220 3% 15% / calc(1% + 5%)),
|
||||||
|
0 16px 16px -2px hsl(220 3% 15% / calc(1% + 6%));
|
||||||
|
}
|
@ -0,0 +1,160 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - Редактор запросов
|
||||||
|
Диаграмма запроса
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React, { useState, useCallback, useEffect } from "react"; //Классы React
|
||||||
|
import ReactFlow, { addEdge, Controls, getOutgoers, applyNodeChanges, applyEdgeChanges } from "reactflow"; //Библиотека редактора диаграмм
|
||||||
|
import { NODE_TYPE } from "../../common"; //Общие ресурсы и константы редактора
|
||||||
|
import { Entity } from "../entity/entity"; //Сущность запроса
|
||||||
|
import { Attribute } from "../attribute/attribute"; //Атрибут сущности
|
||||||
|
import "reactflow/dist/style.css"; //Типовые стили библиотеки редактора диаграмм
|
||||||
|
import "./query_diagram.css"; //Стили компонента
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Стили
|
||||||
|
const STYLES = {
|
||||||
|
CONNECTION_LINE: {
|
||||||
|
strokeWidth: 2,
|
||||||
|
stroke: "gray"
|
||||||
|
},
|
||||||
|
EDGE: {
|
||||||
|
strokeWidth: 2
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//Привязка компонтов диаграммы к типам
|
||||||
|
const NODE_TYPES_COMPONENTS = {
|
||||||
|
[NODE_TYPE.ENTITY]: Entity,
|
||||||
|
[NODE_TYPE.ATTRIBUTE]: Attribute
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------
|
||||||
|
//Вспомогательные функции и компоненты
|
||||||
|
//------------------------------------
|
||||||
|
|
||||||
|
const hasCycle = (connection, target, nodes, edges, visited = new Set()) => {
|
||||||
|
if (visited.has(target.id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
visited.add(target.id);
|
||||||
|
|
||||||
|
for (const outgoer of getOutgoers(target, nodes, edges)) {
|
||||||
|
if (outgoer.id === connection.source || hasCycle(connection, outgoer, nodes, edges, visited)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isValidConnection = (connection, nodes, edges) => {
|
||||||
|
if (!connection.source || !connection.target) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tableId = connection.source.split("-")[0];
|
||||||
|
const isSameTable = connection.target.startsWith(tableId);
|
||||||
|
if (isSameTable) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const target = nodes.find(node => node.id === connection.target);
|
||||||
|
if (!target || target.id === connection.source) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !hasCycle(connection, target, nodes, edges);
|
||||||
|
};
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Диаграмма запроса
|
||||||
|
const QueryDiagram = ({ entities, relations, onEntityPositionChange, onEntityRemove, onRelationAdd, onRelationRemove }) => {
|
||||||
|
//Собственное состояние - элементы
|
||||||
|
const [nodes, setNodes] = useState(entities);
|
||||||
|
|
||||||
|
//Собственное состояние - связи
|
||||||
|
const [edges, setEdges] = useState(relations);
|
||||||
|
|
||||||
|
//Собственное состояние - перемещённый элемент
|
||||||
|
const [movedNode, setMovedNode] = useState(null);
|
||||||
|
|
||||||
|
//При изменении элементов на диаграмме
|
||||||
|
const handleNodesChange = useCallback(
|
||||||
|
changes => {
|
||||||
|
setNodes(nodesSnapshot => applyNodeChanges(changes, nodesSnapshot));
|
||||||
|
if (changes.length == 1 && changes[0].type == "position" && changes[0].dragging)
|
||||||
|
setMovedNode({ id: changes[0].id, position: { ...changes[0].position } });
|
||||||
|
if (changes.length == 1 && changes[0].type == "position" && !changes[0].dragging && movedNode) {
|
||||||
|
if (onEntityPositionChange) onEntityPositionChange(movedNode.id, movedNode.position);
|
||||||
|
setMovedNode(null);
|
||||||
|
}
|
||||||
|
if (changes[0].type == "remove" && entities.find(e => e.id == changes[0].id && e.type == NODE_TYPE.ENTITY) && onEntityRemove)
|
||||||
|
onEntityRemove(changes[0].id);
|
||||||
|
},
|
||||||
|
[movedNode, entities, onEntityPositionChange, onEntityRemove]
|
||||||
|
);
|
||||||
|
|
||||||
|
//При связывании элементов на диаграмме
|
||||||
|
const handleConnect = connection => {
|
||||||
|
setEdges(state => addEdge({ ...connection, id: `${connection.source}-${connection.target}` }, state));
|
||||||
|
onRelationAdd && onRelationAdd(connection.source, connection.target);
|
||||||
|
};
|
||||||
|
|
||||||
|
//При изменении связей на диаграмме
|
||||||
|
const handleEdgesChange = useCallback(
|
||||||
|
changes => {
|
||||||
|
setEdges(edgesSnapshot => applyEdgeChanges(changes, edgesSnapshot));
|
||||||
|
if (changes.length == 1 && changes[0].type == "remove" && onRelationRemove) onRelationRemove(changes[0].id);
|
||||||
|
},
|
||||||
|
[onRelationRemove]
|
||||||
|
);
|
||||||
|
|
||||||
|
const validateConnection = connection => {
|
||||||
|
return isValidConnection(connection, nodes, edges);
|
||||||
|
};
|
||||||
|
|
||||||
|
//При изменении состава сущностей
|
||||||
|
useEffect(() => setNodes(entities), [entities]);
|
||||||
|
|
||||||
|
//При изменении состава связей
|
||||||
|
useEffect(() => setEdges(relations), [relations]);
|
||||||
|
|
||||||
|
//Генерация содержимого
|
||||||
|
return (
|
||||||
|
<ReactFlow
|
||||||
|
nodes={nodes}
|
||||||
|
nodeTypes={NODE_TYPES_COMPONENTS}
|
||||||
|
edges={edges}
|
||||||
|
onNodesChange={handleNodesChange}
|
||||||
|
onEdgesChange={handleEdgesChange}
|
||||||
|
defaultEdgeOptions={{
|
||||||
|
animated: true,
|
||||||
|
style: STYLES.EDGE
|
||||||
|
}}
|
||||||
|
connectionLineStyle={STYLES.CONNECTION_LINE}
|
||||||
|
onConnect={handleConnect}
|
||||||
|
isValidConnection={validateConnection}
|
||||||
|
className={"query_diagram"}
|
||||||
|
>
|
||||||
|
<Controls />
|
||||||
|
</ReactFlow>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { QueryDiagram };
|
290
app/panels/query_editor/hooks.js
Normal file
290
app/panels/query_editor/hooks.js
Normal file
@ -0,0 +1,290 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - Редактор запросов
|
||||||
|
Пользовательские хуки
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import { useState, useContext, useEffect, useCallback } from "react"; //Классы React
|
||||||
|
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
||||||
|
import { NODE_TYPE } from "./common"; //Общие ресурсы и константы редактора
|
||||||
|
|
||||||
|
//------------------------------------
|
||||||
|
//Вспомогательные функции и компоненты
|
||||||
|
//------------------------------------
|
||||||
|
|
||||||
|
//Конвертация серверного описания сущностей запроса в элементы диаграммы
|
||||||
|
const serverEntity2QueryDiagramNodes = entity => {
|
||||||
|
const groupWidth = 250;
|
||||||
|
const nameColumnHeight = 50;
|
||||||
|
const columns = entity?.XATTRS?.XATTR || [];
|
||||||
|
const columnsCount = columns.length;
|
||||||
|
const groupHeight = nameColumnHeight + columnsCount * 50;
|
||||||
|
|
||||||
|
const groupNode = {
|
||||||
|
id: entity.id,
|
||||||
|
type: NODE_TYPE.ENTITY,
|
||||||
|
data: { ...entity },
|
||||||
|
position: { x: entity.x, y: entity.y },
|
||||||
|
style: {
|
||||||
|
width: groupWidth,
|
||||||
|
height: groupHeight
|
||||||
|
},
|
||||||
|
draggable: true
|
||||||
|
};
|
||||||
|
|
||||||
|
const columnNodes = columns.map((column, index, columns) => {
|
||||||
|
const x = 1;
|
||||||
|
const y = 50 * (index + 1);
|
||||||
|
const width = groupWidth - 2;
|
||||||
|
const height = 50;
|
||||||
|
|
||||||
|
const isLast = index === columns.length - 1;
|
||||||
|
const defaultColumnStyles = {
|
||||||
|
borderBottom: "1px solid #dee2e6"
|
||||||
|
};
|
||||||
|
const lastColumnStyles = {
|
||||||
|
borderBottom: "none"
|
||||||
|
};
|
||||||
|
const otherStyles = isLast ? lastColumnStyles : defaultColumnStyles;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: column.id,
|
||||||
|
type: NODE_TYPE.ATTRIBUTE,
|
||||||
|
data: {
|
||||||
|
...column,
|
||||||
|
included: false,
|
||||||
|
parentEntity: entity.id
|
||||||
|
},
|
||||||
|
position: { x, y },
|
||||||
|
parentId: entity.id,
|
||||||
|
extent: "parent",
|
||||||
|
style: {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
...otherStyles
|
||||||
|
},
|
||||||
|
draggable: false,
|
||||||
|
selectable: true
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return [groupNode, ...columnNodes];
|
||||||
|
};
|
||||||
|
|
||||||
|
//Конвертация серверного описания запроса в данные для редактора диаграмм
|
||||||
|
const serverQueryData2QueryDiagram = (entities, relations) => {
|
||||||
|
const result = { entities: [], relations: [...relations] };
|
||||||
|
entities.forEach(entity => {
|
||||||
|
const nodes = serverEntity2QueryDiagramNodes(entity);
|
||||||
|
result.entities = [...result.entities, ...nodes];
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Работа с запросами
|
||||||
|
const useQuery = () => {
|
||||||
|
//Собственное состояние - флаг инициализированности
|
||||||
|
const [isInit, setInit] = useState(false);
|
||||||
|
|
||||||
|
//Собственное состояние - флаг загрузки
|
||||||
|
const [isLoading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
//Собственное состояние - флаг необходимости обновления
|
||||||
|
const [refresh, setRefresh] = useState(true);
|
||||||
|
|
||||||
|
//Собственное состояние - данные
|
||||||
|
const [data, setData] = useState(null);
|
||||||
|
|
||||||
|
//Подключение к контексту взаимодействия с сервером
|
||||||
|
const { executeStored } = useContext(BackEndСtx);
|
||||||
|
|
||||||
|
//Обновление данных
|
||||||
|
const doRefresh = () => setRefresh(true);
|
||||||
|
|
||||||
|
//Добавление запроса
|
||||||
|
const insertQuery = useCallback(
|
||||||
|
async (code, name) => {
|
||||||
|
await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_INSERT", args: { SCODE: code, SNAME: name }, loader: false });
|
||||||
|
setRefresh(true);
|
||||||
|
},
|
||||||
|
[executeStored]
|
||||||
|
);
|
||||||
|
|
||||||
|
//Изменение запроса
|
||||||
|
const updateQuery = useCallback(
|
||||||
|
async (query, code, name) => {
|
||||||
|
await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_UPDATE", args: { NRN: query, SCODE: code, SNAME: name }, loader: false });
|
||||||
|
setRefresh(true);
|
||||||
|
},
|
||||||
|
[executeStored]
|
||||||
|
);
|
||||||
|
|
||||||
|
//Удаление запроса
|
||||||
|
const deleteQuery = useCallback(
|
||||||
|
async query => {
|
||||||
|
await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_DELETE", args: { NRN: query }, loader: false });
|
||||||
|
setRefresh(true);
|
||||||
|
},
|
||||||
|
[executeStored]
|
||||||
|
);
|
||||||
|
|
||||||
|
//Установка флага готовности запроса
|
||||||
|
const setQueryReady = useCallback(
|
||||||
|
async (query, ready) => {
|
||||||
|
await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_READY_SET", args: { NRN: query, NREADY: ready }, loader: false });
|
||||||
|
setRefresh(true);
|
||||||
|
},
|
||||||
|
[executeStored]
|
||||||
|
);
|
||||||
|
|
||||||
|
//Установка флага публичности запроса
|
||||||
|
const setQueryPbl = useCallback(
|
||||||
|
async (query, pbl) => {
|
||||||
|
await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_PBL_SET", args: { NRN: query, NPBL: pbl }, loader: false });
|
||||||
|
setRefresh(true);
|
||||||
|
},
|
||||||
|
[executeStored]
|
||||||
|
);
|
||||||
|
|
||||||
|
//При необходимости получить/обновить данные
|
||||||
|
useEffect(() => {
|
||||||
|
//Загрузка данных с сервера
|
||||||
|
const loadData = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const data = await executeStored({
|
||||||
|
stored: "PKG_P8PANELS_QE.QUERY_LIST",
|
||||||
|
respArg: "COUT",
|
||||||
|
isArray: name => ["XQUERY"].includes(name),
|
||||||
|
attributeValueProcessor: (name, val) => (["code", "name"].includes(name) ? undefined : val),
|
||||||
|
loader: true
|
||||||
|
});
|
||||||
|
setData(data?.XQUERIES?.XQUERY || []);
|
||||||
|
setInit(true);
|
||||||
|
} finally {
|
||||||
|
setRefresh(false);
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//Если надо обновить
|
||||||
|
if (refresh)
|
||||||
|
//Получим данные
|
||||||
|
loadData();
|
||||||
|
}, [refresh, executeStored]);
|
||||||
|
|
||||||
|
//Возвращаем интерфейс хука
|
||||||
|
return [data, insertQuery, updateQuery, deleteQuery, setQueryReady, setQueryPbl, doRefresh, isLoading, isInit];
|
||||||
|
};
|
||||||
|
|
||||||
|
//Работа с содержимым запроса
|
||||||
|
const useQueryDesc = query => {
|
||||||
|
//Собственное состояние - флаг инициализированности
|
||||||
|
const [isInit, setInit] = useState(false);
|
||||||
|
|
||||||
|
//Собственное состояние - флаг загрузки
|
||||||
|
const [isLoading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
//Собственное состояние - флаг необходимости обновления
|
||||||
|
const [refresh, setRefresh] = useState(true);
|
||||||
|
|
||||||
|
//Собственное состояние - данные
|
||||||
|
const [data, setData] = useState(null);
|
||||||
|
|
||||||
|
//Подключение к контексту взаимодействия с сервером
|
||||||
|
const { executeStored } = useContext(BackEndСtx);
|
||||||
|
|
||||||
|
//Обновление данных
|
||||||
|
const doRefresh = () => setRefresh(true);
|
||||||
|
|
||||||
|
//Добавление сущности в запрос
|
||||||
|
const addEnt = useCallback(
|
||||||
|
async (name, type) => {
|
||||||
|
await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_ENT_ADD", args: { NRN: query, SNAME: name, STYPE: type }, loader: false });
|
||||||
|
setRefresh(true);
|
||||||
|
},
|
||||||
|
[query, executeStored]
|
||||||
|
);
|
||||||
|
|
||||||
|
//Удаление сущности из запроса
|
||||||
|
const removeEnt = useCallback(
|
||||||
|
async ent => {
|
||||||
|
await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_ENT_REMOVE", args: { NRN: query, SID: ent }, loader: false });
|
||||||
|
setRefresh(true);
|
||||||
|
},
|
||||||
|
[query, executeStored]
|
||||||
|
);
|
||||||
|
|
||||||
|
//Сохранение координат сущности на диаграммем
|
||||||
|
const setEntPosition = useCallback(
|
||||||
|
async (ent, x, y) => {
|
||||||
|
await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_ENT_POSITION_SET", args: { NRN: query, SID: ent, NX: x, NY: y }, loader: false });
|
||||||
|
},
|
||||||
|
[query, executeStored]
|
||||||
|
);
|
||||||
|
|
||||||
|
//Добавление отношения сущностей в запрос
|
||||||
|
const addRl = useCallback(
|
||||||
|
async (source, target) => {
|
||||||
|
await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_RL_ADD", args: { NRN: query, SSOURCE: source, STARGET: target }, loader: false });
|
||||||
|
setRefresh(true);
|
||||||
|
},
|
||||||
|
[query, executeStored]
|
||||||
|
);
|
||||||
|
|
||||||
|
//Удаление отношения сущностей из запроса
|
||||||
|
const removeRl = useCallback(
|
||||||
|
async rl => {
|
||||||
|
await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_RL_REMOVE", args: { NRN: query, SID: rl }, loader: false });
|
||||||
|
setRefresh(true);
|
||||||
|
},
|
||||||
|
[query, executeStored]
|
||||||
|
);
|
||||||
|
|
||||||
|
//При необходимости получить/обновить данные
|
||||||
|
useEffect(() => {
|
||||||
|
//Загрузка данных с сервера
|
||||||
|
const loadData = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const data = await executeStored({
|
||||||
|
stored: "PKG_P8PANELS_QE.QUERY_DESC",
|
||||||
|
args: { NRN: query },
|
||||||
|
respArg: "COUT",
|
||||||
|
isArray: name => ["XENT", "XATTR", "XRL"].includes(name),
|
||||||
|
loader: true
|
||||||
|
});
|
||||||
|
setData(serverQueryData2QueryDiagram(data?.XENTS?.XENT || [], data?.XRLS?.XRL || []));
|
||||||
|
setInit(true);
|
||||||
|
} finally {
|
||||||
|
setRefresh(false);
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//Если надо обновить
|
||||||
|
if (refresh)
|
||||||
|
if (query)
|
||||||
|
//Если есть для чего получать данные
|
||||||
|
loadData();
|
||||||
|
//Нет идентификатора запроса - нет данных
|
||||||
|
else setData(null);
|
||||||
|
}, [refresh, query, executeStored]);
|
||||||
|
|
||||||
|
//При изменении входных свойств - поднимаем флаг обновления
|
||||||
|
useEffect(() => setRefresh(true), [query]);
|
||||||
|
|
||||||
|
//Возвращаем интерфейс хука
|
||||||
|
return [data, addEnt, removeEnt, setEntPosition, addRl, removeRl, doRefresh, isLoading, isInit];
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { useQuery, useQueryDesc };
|
16
app/panels/query_editor/index.js
Normal file
16
app/panels/query_editor/index.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - Редактор запросов
|
||||||
|
Редактор запросов: точка входа
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import { QueryEditor } from "./query_editor"; //Корневая панель редактора
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export const RootClass = QueryEditor;
|
137
app/panels/query_editor/query_editor.js
Normal file
137
app/panels/query_editor/query_editor.js
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - Редактор запросов
|
||||||
|
Корневой компонент
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React, { useState } from "react"; //Классы React
|
||||||
|
import { Box, Grid, Button, Icon } from "@mui/material"; //Интерфейсные компоненты MUI
|
||||||
|
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Компоненты рабочего стола
|
||||||
|
import { P8PEditorToolBar } from "../../components/editors/p8p_editor_toolbar"; //Панель инструментов редактора
|
||||||
|
import { BUTTONS } from "../../../app.text"; //Общие текстовые ресурсы приложения
|
||||||
|
import { QueryDiagram } from "./components/query_diagram/query_diagram"; //Диаграмма запроса
|
||||||
|
import { QueriesManager } from "./components/queries_manager/queries_manager"; //Менеджер запросов
|
||||||
|
import { EntityAddDialog } from "./components/entity_add_dialog/entity_add_dialog"; //Диалог добавления сущности
|
||||||
|
import { P8PEditorBox } from "../../components/editors/p8p_editor_box"; //Контейнер параметров редактора
|
||||||
|
import { P8PEditorSubHeader } from "../../components/editors/p8p_editor_sub_header"; //Подзаголовок группы параметров редактора
|
||||||
|
import { useQueryDesc } from "./hooks"; //Пользовательские хуки
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Стили
|
||||||
|
const STYLES = {
|
||||||
|
CONTAINER: { display: "flex" },
|
||||||
|
GRID_CONTAINER: { height: `calc(100vh - ${APP_BAR_HEIGHT})` },
|
||||||
|
GRID_ITEM_INSPECTOR: { backgroundColor: "#e9ecef" }
|
||||||
|
};
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Корневой компонент редактора запросов
|
||||||
|
const QueryEditor = () => {
|
||||||
|
//Текущий запрос
|
||||||
|
const [query, setQuery] = useState(null);
|
||||||
|
|
||||||
|
//Отображения менеджера запросов
|
||||||
|
const [openQueriesManager, setOpenQueriesManager] = useState(true);
|
||||||
|
|
||||||
|
//Отображение диалога добавления сущности
|
||||||
|
const [openEntityAddDialog, setOpenEntityAddDialog] = useState(false);
|
||||||
|
|
||||||
|
//Получение данных запроса
|
||||||
|
const [queryDiagram, addEnt, removeEnt, setEntPosition, addRl, removeRl] = useQueryDesc(query);
|
||||||
|
|
||||||
|
//Обработка изменения положения сущности на диаграмме
|
||||||
|
const handleEntityPositionChange = (ent, position) => setEntPosition(ent, position.x, position.y);
|
||||||
|
|
||||||
|
//Обработка удаления сущности из запроса
|
||||||
|
const handleEntityRemove = ent => removeEnt(ent);
|
||||||
|
|
||||||
|
//Обработка добавления отношения cущностей
|
||||||
|
const handleRelationAdd = (source, target) => addRl(source, target);
|
||||||
|
|
||||||
|
//Обработка удаления отношения cущностей
|
||||||
|
const handleRelationRemove = rl => removeRl(rl);
|
||||||
|
|
||||||
|
//Открытие менеджера запросов
|
||||||
|
const handleOpenQueriesManager = () => setOpenQueriesManager(true);
|
||||||
|
|
||||||
|
//Закрытие менеджера запросов
|
||||||
|
const handleCancelQueriesManager = () => setOpenQueriesManager(false);
|
||||||
|
|
||||||
|
//Закрытие запроса
|
||||||
|
const handleQueryClose = () => setQuery(null);
|
||||||
|
|
||||||
|
//При выборе запроса
|
||||||
|
const handleQuerySelect = query => {
|
||||||
|
setQuery(query);
|
||||||
|
setOpenQueriesManager(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
//При добавлении сущности в запрос
|
||||||
|
const handleEntityAdd = () => setOpenEntityAddDialog(true);
|
||||||
|
|
||||||
|
//Закрытие диалога добавления сущности по "ОК"
|
||||||
|
const handleEntityAddDialogOk = async values => {
|
||||||
|
await addEnt(values.name, "VIEW");
|
||||||
|
setOpenEntityAddDialog(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Закрытие диалога добавления сущности по "ОК"
|
||||||
|
const handleEntityAddDialogCancel = () => setOpenEntityAddDialog(false);
|
||||||
|
|
||||||
|
//Панель инструмментов
|
||||||
|
const toolBar = (
|
||||||
|
<P8PEditorToolBar
|
||||||
|
items={[
|
||||||
|
{ icon: "file_open", title: "Менеджер запросов", onClick: handleOpenQueriesManager },
|
||||||
|
{ icon: "close", title: "Закрыть запрос", onClick: handleQueryClose, disabled: !query }
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
//Генерация содержимого
|
||||||
|
return (
|
||||||
|
<Box sx={STYLES.CONTAINER}>
|
||||||
|
{openQueriesManager && <QueriesManager current={query} onQuerySelect={handleQuerySelect} onCancel={handleCancelQueriesManager} />}
|
||||||
|
{openEntityAddDialog && <EntityAddDialog onOk={handleEntityAddDialogOk} onCancel={handleEntityAddDialogCancel} />}
|
||||||
|
<Grid container sx={STYLES.GRID_CONTAINER} columns={25}>
|
||||||
|
<Grid item xs={20}>
|
||||||
|
{queryDiagram && (
|
||||||
|
<QueryDiagram
|
||||||
|
{...queryDiagram}
|
||||||
|
onEntityPositionChange={handleEntityPositionChange}
|
||||||
|
onEntityRemove={handleEntityRemove}
|
||||||
|
onRelationAdd={handleRelationAdd}
|
||||||
|
onRelationRemove={handleRelationRemove}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={5} sx={STYLES.GRID_ITEM_INSPECTOR}>
|
||||||
|
{toolBar}
|
||||||
|
{query && (
|
||||||
|
<P8PEditorBox title={"Параметры запроса"}>
|
||||||
|
<P8PEditorSubHeader title={"Сущности"} />
|
||||||
|
<Button startIcon={<Icon>add</Icon>} onClick={handleEntityAdd}>
|
||||||
|
{BUTTONS.INSERT}
|
||||||
|
</Button>
|
||||||
|
</P8PEditorBox>
|
||||||
|
)}
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { QueryEditor };
|
25
db/P8PNL_QE_QUERY.sql
Normal file
25
db/P8PNL_QE_QUERY.sql
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - Общие - Редактор запросов
|
||||||
|
Запросы
|
||||||
|
*/
|
||||||
|
create table P8PNL_QE_QUERY
|
||||||
|
(
|
||||||
|
RN number(17) not null, -- Рег. номер записи
|
||||||
|
CODE varchar2(100) not null, -- Мнемокод
|
||||||
|
NAME varchar2(2000) not null, -- Наименование
|
||||||
|
AUTHOR varchar2(30) not null, -- Автор
|
||||||
|
CH_DATE date not null, -- Дата последнего изменения
|
||||||
|
READY number(1) default 0 not null, -- Флаг готовности к использованию (0 - нет, 1 - да)
|
||||||
|
PBL number(1) default 0 not null, -- Флаг публичности (0 - нет, 1 - да)
|
||||||
|
OPTS clob, -- Параметры запроса
|
||||||
|
ENTS clob, -- Сущности запроса
|
||||||
|
RLS clob, -- Отношения сущностей запроса
|
||||||
|
QRY clob, -- Запрос
|
||||||
|
constraint C_P8PNL_QE_QUERY_RN_PK primary key (RN),
|
||||||
|
constraint C_P8PNL_QE_QUERY_CODE_NB check (rtrim(CODE) is not null),
|
||||||
|
constraint C_P8PNL_QE_QUERY_NAME_NB check (rtrim(NAME) is not null),
|
||||||
|
constraint C_P8PNL_QE_QUERY_AUTHOR_FK foreign key (AUTHOR) references USERLIST (AUTHID) on delete cascade,
|
||||||
|
constraint C_P8PNL_QE_QUERY_READY_VAL check (READY in (0, 1)),
|
||||||
|
constraint C_P8PNL_QE_QUERY_PBL_VAL check (PBL in (0, 1)),
|
||||||
|
constraint C_P8PNL_QE_QUERY_UN unique (CODE)
|
||||||
|
);
|
342
db/PKG_P8PANELS_QE.pck
Normal file
342
db/PKG_P8PANELS_QE.pck
Normal file
@ -0,0 +1,342 @@
|
|||||||
|
create or replace package PKG_P8PANELS_QE as
|
||||||
|
|
||||||
|
/* Получение списка запросов */
|
||||||
|
procedure QUERY_LIST
|
||||||
|
(
|
||||||
|
COUT out clob -- Сериализованный список запросов
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Добавление запроса */
|
||||||
|
procedure QUERY_INSERT
|
||||||
|
(
|
||||||
|
SCODE in varchar2, -- Мнемокод
|
||||||
|
SNAME in varchar2, -- Наименование
|
||||||
|
NRN out number -- Рег. номер добавленного запроса
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Исправление запроса */
|
||||||
|
procedure QUERY_UPDATE
|
||||||
|
(
|
||||||
|
NRN in number, -- Рег. номер запроса
|
||||||
|
SCODE in varchar2, -- Мнемокод
|
||||||
|
SNAME in varchar2 -- Наименование
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Удаление запроса */
|
||||||
|
procedure QUERY_DELETE
|
||||||
|
(
|
||||||
|
NRN in number -- Рег. номер запроса
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Получение данных о запросе */
|
||||||
|
procedure QUERY_DESC
|
||||||
|
(
|
||||||
|
NRN in number, -- Рег. номер запроса
|
||||||
|
COUT out clob -- Сериализованное описание запроса
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Добавление сущности в запрос */
|
||||||
|
procedure QUERY_ENT_ADD
|
||||||
|
(
|
||||||
|
NRN in number, -- Рег. номер запроса
|
||||||
|
SNAME in varchar2, -- Имя
|
||||||
|
STYPE in varchar2 -- Тип (см. константы SENT_TYPE_*)
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Удаление сущности из запроса */
|
||||||
|
procedure QUERY_ENT_REMOVE
|
||||||
|
(
|
||||||
|
NRN in number, -- Рег. номер запроса
|
||||||
|
SID in varchar2 -- Идентификатор сущности
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Установка координат сущности в редакторе диаграммы запроса */
|
||||||
|
procedure QUERY_ENT_POSITION_SET
|
||||||
|
(
|
||||||
|
NRN in number, -- Рег. номер запроса
|
||||||
|
SID in varchar2, -- Идентификатор сущности
|
||||||
|
NX in number, -- Координата по оси абсцисс
|
||||||
|
NY in number -- Координата по оси ординат
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Добавление связи в запрос */
|
||||||
|
procedure QUERY_RL_ADD
|
||||||
|
(
|
||||||
|
NRN in number, -- Рег. номер запроса
|
||||||
|
SSOURCE in varchar2, -- Идентификатор атрибута-источника
|
||||||
|
STARGET in varchar2 -- Идентификатор атрибута-приёмника
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Удаление связи из запроса */
|
||||||
|
procedure QUERY_RL_REMOVE
|
||||||
|
(
|
||||||
|
NRN in number, -- Рег. номер запроса
|
||||||
|
SID in varchar2 -- Идентификатор связи
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Установка признака "готовности" запроса */
|
||||||
|
procedure QUERY_READY_SET
|
||||||
|
(
|
||||||
|
NRN in number, -- Рег. номер запроса
|
||||||
|
NREADY in number -- Флаг готовности к использованию (0 - нет, 1 - да)
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Установка признака "публичности" запроса */
|
||||||
|
procedure QUERY_PBL_SET
|
||||||
|
(
|
||||||
|
NRN in number, -- Рег. номер запроса
|
||||||
|
NPBL in number -- Флаг публичности (0 - приватный, 1 - публичный)
|
||||||
|
);
|
||||||
|
|
||||||
|
end PKG_P8PANELS_QE;
|
||||||
|
/
|
||||||
|
create or replace package body PKG_P8PANELS_QE as
|
||||||
|
|
||||||
|
/* Получение списка запросов */
|
||||||
|
procedure QUERY_LIST
|
||||||
|
(
|
||||||
|
COUT out clob -- Сериализованный список запросов
|
||||||
|
)
|
||||||
|
is
|
||||||
|
begin
|
||||||
|
/* Базово сформируем список */
|
||||||
|
COUT := PKG_P8PANELS_QE_BASE.QUERY_LIST_GET(SUSER => UTILIZER());
|
||||||
|
end QUERY_LIST;
|
||||||
|
|
||||||
|
/* Добавление запроса */
|
||||||
|
procedure QUERY_INSERT
|
||||||
|
(
|
||||||
|
SCODE in varchar2, -- Мнемокод
|
||||||
|
SNAME in varchar2, -- Наименование
|
||||||
|
NRN out number -- Рег. номер добавленного запроса
|
||||||
|
)
|
||||||
|
is
|
||||||
|
begin
|
||||||
|
/* Базовое добавление */
|
||||||
|
PKG_P8PANELS_QE_BASE.QUERY_INSERT(SCODE => SCODE, SNAME => SNAME, NRN => NRN);
|
||||||
|
end QUERY_INSERT;
|
||||||
|
|
||||||
|
/* Исправление запроса */
|
||||||
|
procedure QUERY_UPDATE
|
||||||
|
(
|
||||||
|
NRN in number, -- Рег. номер запроса
|
||||||
|
SCODE in varchar2, -- Мнемокод
|
||||||
|
SNAME in varchar2 -- Наименование
|
||||||
|
)
|
||||||
|
is
|
||||||
|
begin
|
||||||
|
/* Провим права доступа */
|
||||||
|
PKG_P8PANELS_QE_BASE.QUERY_ACCESS_MODIFY(NRN => NRN, SUSER => UTILIZER());
|
||||||
|
/* Базовое исправление */
|
||||||
|
PKG_P8PANELS_QE_BASE.QUERY_UPDATE(NRN => NRN, SCODE => SCODE, SNAME => SNAME);
|
||||||
|
end QUERY_UPDATE;
|
||||||
|
|
||||||
|
/* Удаление запроса */
|
||||||
|
procedure QUERY_DELETE
|
||||||
|
(
|
||||||
|
NRN in number -- Рег. номер запроса
|
||||||
|
)
|
||||||
|
is
|
||||||
|
begin
|
||||||
|
/* Провим права доступа */
|
||||||
|
PKG_P8PANELS_QE_BASE.QUERY_ACCESS_MODIFY(NRN => NRN, SUSER => UTILIZER());
|
||||||
|
/* Базовое удаление */
|
||||||
|
PKG_P8PANELS_QE_BASE.QUERY_DELETE(NRN => NRN);
|
||||||
|
end QUERY_DELETE;
|
||||||
|
|
||||||
|
/* Получение описания запроса */
|
||||||
|
procedure QUERY_DESC
|
||||||
|
(
|
||||||
|
NRN in number, -- Рег. номер запроса
|
||||||
|
COUT out clob -- Сериализованное описание запроса
|
||||||
|
)
|
||||||
|
is
|
||||||
|
begin
|
||||||
|
/* Проверим права доступа */
|
||||||
|
PKG_P8PANELS_QE_BASE.QUERY_ACCESS_VIEW(NRN => NRN, SUSER => UTILIZER());
|
||||||
|
/* Получим описание запроса */
|
||||||
|
COUT := PKG_P8PANELS_QE_BASE.QUERY_DESC_GET(NRN => NRN);
|
||||||
|
end QUERY_DESC;
|
||||||
|
|
||||||
|
/* Добавление сущности в запрос */
|
||||||
|
procedure QUERY_ENT_ADD
|
||||||
|
(
|
||||||
|
NRN in number, -- Рег. номер запроса
|
||||||
|
SNAME in varchar2, -- Имя
|
||||||
|
STYPE in varchar2 -- Тип (см. константы SENT_TYPE_*)
|
||||||
|
)
|
||||||
|
is
|
||||||
|
RQ P8PNL_QE_QUERY%rowtype; -- Запись запроса
|
||||||
|
RENTS PKG_P8PANELS_QE_BASE.TENTS; -- Коллекция существующих сущностей
|
||||||
|
begin
|
||||||
|
/* Провим права доступа */
|
||||||
|
PKG_P8PANELS_QE_BASE.QUERY_ACCESS_MODIFY(NRN => NRN, SUSER => UTILIZER());
|
||||||
|
/* Читаем запись запроса */
|
||||||
|
RQ := PKG_P8PANELS_QE_BASE.QUERY_GET(NRN => NRN);
|
||||||
|
/* Читаем существующие сущности */
|
||||||
|
RENTS := PKG_P8PANELS_QE_BASE.QUERY_ENTS_GET(CENTS => RQ.ENTS);
|
||||||
|
/* Формируем описание новой сущности и добавляем её в коллекцию */
|
||||||
|
PKG_P8PANELS_QE_BASE.TENTS_APPEND(RENTS => RENTS, SNAME => SNAME, STYPE => STYPE);
|
||||||
|
/* Сохраняем обновленный набор сущностей */
|
||||||
|
PKG_P8PANELS_QE_BASE.QUERY_ENTS_SET(NRN => RQ.RN, RENTS => RENTS);
|
||||||
|
end QUERY_ENT_ADD;
|
||||||
|
|
||||||
|
/* Удаление сущности из запроса */
|
||||||
|
procedure QUERY_ENT_REMOVE
|
||||||
|
(
|
||||||
|
NRN in number, -- Рег. номер запроса
|
||||||
|
SID in varchar2 -- Идентификатор сущности
|
||||||
|
)
|
||||||
|
is
|
||||||
|
RQ P8PNL_QE_QUERY%rowtype; -- Запись запроса
|
||||||
|
RENTS PKG_P8PANELS_QE_BASE.TENTS; -- Коллекция существующих сущностей
|
||||||
|
RENT PKG_P8PANELS_QE_BASE.TENT; -- Удаляемая сущность
|
||||||
|
RRLS PKG_P8PANELS_QE_BASE.TRLS; -- Коллекция существующих связей
|
||||||
|
RRLS_TMP PKG_P8PANELS_QE_BASE.TRLS; -- Буфер для коллекции удаляемых связей
|
||||||
|
begin
|
||||||
|
/* Провим права доступа */
|
||||||
|
PKG_P8PANELS_QE_BASE.QUERY_ACCESS_MODIFY(NRN => NRN, SUSER => UTILIZER());
|
||||||
|
/* Читаем запись запроса */
|
||||||
|
RQ := PKG_P8PANELS_QE_BASE.QUERY_GET(NRN => NRN);
|
||||||
|
/* Читаем существующие сущности */
|
||||||
|
RENTS := PKG_P8PANELS_QE_BASE.QUERY_ENTS_GET(CENTS => RQ.ENTS);
|
||||||
|
/* Читаем свзяи */
|
||||||
|
RRLS := PKG_P8PANELS_QE_BASE.QUERY_RLS_GET(CRLS => RQ.RLS);
|
||||||
|
/* Находим удаляемую сущность */
|
||||||
|
begin
|
||||||
|
RENT := RENTS(PKG_P8PANELS_QE_BASE.TENTS_INDEX_BY_ID(RENTS => RENTS, SID => SID));
|
||||||
|
exception
|
||||||
|
when VALUE_ERROR then
|
||||||
|
P_EXCEPTION(0,
|
||||||
|
'Сущность с идентификатором "%s" в запросе "%s" не определена.',
|
||||||
|
COALESCE(SID, '<НЕ УКАЗАН>'),
|
||||||
|
TO_CHAR(NRN));
|
||||||
|
end;
|
||||||
|
/* Удаляем сущность из коллекции */
|
||||||
|
PKG_P8PANELS_QE_BASE.TENTS_REMOVE(RENTS => RENTS, SID => SID);
|
||||||
|
/* Обходим атрибуты сущности */
|
||||||
|
if ((RENT.RATTRS is not null) and (RENT.RATTRS.COUNT > 0)) then
|
||||||
|
for I in RENT.RATTRS.FIRST .. RENT.RATTRS.LAST
|
||||||
|
loop
|
||||||
|
/* Если атрибут есть в связях (как источник или как приёмник) */
|
||||||
|
for J in 0 .. 1
|
||||||
|
loop
|
||||||
|
RRLS_TMP := PKG_P8PANELS_QE_BASE.TRLS_LIST_BY_ST(RRLS => RRLS,
|
||||||
|
SSOURCE_TARGET => RENT.RATTRS(I).SID,
|
||||||
|
NLIST_TYPE => J);
|
||||||
|
/* То связь должна быть удалена */
|
||||||
|
if ((RRLS_TMP is not null) and (RRLS_TMP.COUNT > 0)) then
|
||||||
|
for K in RRLS_TMP.FIRST .. RRLS_TMP.LAST
|
||||||
|
loop
|
||||||
|
PKG_P8PANELS_QE_BASE.TRLS_REMOVE(RRLS => RRLS, SID => RRLS_TMP(K).SID);
|
||||||
|
end loop;
|
||||||
|
end if;
|
||||||
|
end loop;
|
||||||
|
end loop;
|
||||||
|
end if;
|
||||||
|
/* Сохраняем обновленный набор сущностей */
|
||||||
|
PKG_P8PANELS_QE_BASE.QUERY_ENTS_SET(NRN => RQ.RN, RENTS => RENTS);
|
||||||
|
/* Сохраняем обновленный набор связей */
|
||||||
|
PKG_P8PANELS_QE_BASE.QUERY_RLS_SET(NRN => RQ.RN, RRLS => RRLS);
|
||||||
|
end QUERY_ENT_REMOVE;
|
||||||
|
|
||||||
|
/* Установка координат сущности в редакторе диаграммы запроса */
|
||||||
|
procedure QUERY_ENT_POSITION_SET
|
||||||
|
(
|
||||||
|
NRN in number, -- Рег. номер запроса
|
||||||
|
SID in varchar2, -- Идентификатор сущности
|
||||||
|
NX in number, -- Координата по оси абсцисс
|
||||||
|
NY in number -- Координата по оси ординат
|
||||||
|
)
|
||||||
|
is
|
||||||
|
RQ P8PNL_QE_QUERY%rowtype; -- Запись запроса
|
||||||
|
RENTS PKG_P8PANELS_QE_BASE.TENTS; -- Коллекция существующих сущностей
|
||||||
|
begin
|
||||||
|
/* Читаем запись запроса */
|
||||||
|
RQ := PKG_P8PANELS_QE_BASE.QUERY_GET(NRN => NRN);
|
||||||
|
/* Сохранять расположение будем только если это автор - так остальные могут перемещать на экране диаграмму, но не запоминать расположение сущностей */
|
||||||
|
if (RQ.AUTHOR = UTILIZER()) then
|
||||||
|
/* Читаем существующие сущности */
|
||||||
|
RENTS := PKG_P8PANELS_QE_BASE.QUERY_ENTS_GET(CENTS => RQ.ENTS);
|
||||||
|
/* Меняем координаты сущности в коллекции */
|
||||||
|
PKG_P8PANELS_QE_BASE.TENTS_POSITION_SET(RENTS => RENTS, SID => SID, NX => NX, NY => NY);
|
||||||
|
/* Сохраняем обновленный набор сущностей */
|
||||||
|
PKG_P8PANELS_QE_BASE.QUERY_ENTS_SET(NRN => RQ.RN, RENTS => RENTS);
|
||||||
|
end if;
|
||||||
|
end QUERY_ENT_POSITION_SET;
|
||||||
|
|
||||||
|
/* Добавление связи в запрос */
|
||||||
|
procedure QUERY_RL_ADD
|
||||||
|
(
|
||||||
|
NRN in number, -- Рег. номер запроса
|
||||||
|
SSOURCE in varchar2, -- Идентификатор атрибута-источника
|
||||||
|
STARGET in varchar2 -- Идентификатор атрибута-приёмника
|
||||||
|
)
|
||||||
|
is
|
||||||
|
RQ P8PNL_QE_QUERY%rowtype; -- Запись запроса
|
||||||
|
RRLS PKG_P8PANELS_QE_BASE.TRLS; -- Коллекция существующих связей
|
||||||
|
begin
|
||||||
|
/* Провим права доступа */
|
||||||
|
PKG_P8PANELS_QE_BASE.QUERY_ACCESS_MODIFY(NRN => NRN, SUSER => UTILIZER());
|
||||||
|
/* Читаем запись запроса */
|
||||||
|
RQ := PKG_P8PANELS_QE_BASE.QUERY_GET(NRN => NRN);
|
||||||
|
/* Читаем существующие связи */
|
||||||
|
RRLS := PKG_P8PANELS_QE_BASE.QUERY_RLS_GET(CRLS => RQ.RLS);
|
||||||
|
/* Формируем описание новой связи и добавляем её в коллекцию */
|
||||||
|
PKG_P8PANELS_QE_BASE.TRLS_APPEND(RRLS => RRLS, SSOURCE => SSOURCE, STARGET => STARGET);
|
||||||
|
/* Сохраняем обновленный набор связей */
|
||||||
|
PKG_P8PANELS_QE_BASE.QUERY_RLS_SET(NRN => RQ.RN, RRLS => RRLS);
|
||||||
|
end QUERY_RL_ADD;
|
||||||
|
|
||||||
|
/* Удаление связи из запроса */
|
||||||
|
procedure QUERY_RL_REMOVE
|
||||||
|
(
|
||||||
|
NRN in number, -- Рег. номер запроса
|
||||||
|
SID in varchar2 -- Идентификатор связи
|
||||||
|
)
|
||||||
|
is
|
||||||
|
RQ P8PNL_QE_QUERY%rowtype; -- Запись запроса
|
||||||
|
RRLS PKG_P8PANELS_QE_BASE.TRLS; -- Коллекция существующих связей
|
||||||
|
begin
|
||||||
|
/* Провим права доступа */
|
||||||
|
PKG_P8PANELS_QE_BASE.QUERY_ACCESS_MODIFY(NRN => NRN, SUSER => UTILIZER());
|
||||||
|
/* Читаем запись запроса */
|
||||||
|
RQ := PKG_P8PANELS_QE_BASE.QUERY_GET(NRN => NRN);
|
||||||
|
/* Читаем существующие связи */
|
||||||
|
RRLS := PKG_P8PANELS_QE_BASE.QUERY_RLS_GET(CRLS => RQ.RLS);
|
||||||
|
/* Удаляем связи из коллекции */
|
||||||
|
PKG_P8PANELS_QE_BASE.TRLS_REMOVE(RRLS => RRLS, SID => SID);
|
||||||
|
/* Сохраняем обновленный набор связей */
|
||||||
|
PKG_P8PANELS_QE_BASE.QUERY_RLS_SET(NRN => RQ.RN, RRLS => RRLS);
|
||||||
|
end QUERY_RL_REMOVE;
|
||||||
|
|
||||||
|
/* Установка признака "готовности" запроса */
|
||||||
|
procedure QUERY_READY_SET
|
||||||
|
(
|
||||||
|
NRN in number, -- Рег. номер запроса
|
||||||
|
NREADY in number -- Флаг готовности к использованию (0 - нет, 1 - да)
|
||||||
|
)
|
||||||
|
is
|
||||||
|
begin
|
||||||
|
/* Провим права доступа */
|
||||||
|
PKG_P8PANELS_QE_BASE.QUERY_ACCESS_MODIFY(NRN => NRN, SUSER => UTILIZER());
|
||||||
|
/* Базовая установка признака готовности */
|
||||||
|
PKG_P8PANELS_QE_BASE.QUERY_READY_SET(NRN => NRN, NREADY => NREADY);
|
||||||
|
end QUERY_READY_SET;
|
||||||
|
|
||||||
|
/* Установка признака "публичности" запроса */
|
||||||
|
procedure QUERY_PBL_SET
|
||||||
|
(
|
||||||
|
NRN in number, -- Рег. номер запроса
|
||||||
|
NPBL in number -- Флаг публичности (0 - приватный, 1 - публичный)
|
||||||
|
)
|
||||||
|
is
|
||||||
|
begin
|
||||||
|
/* Провим права доступа */
|
||||||
|
PKG_P8PANELS_QE_BASE.QUERY_ACCESS_MODIFY(NRN => NRN, SUSER => UTILIZER());
|
||||||
|
/* Базовая установка признака публичности */
|
||||||
|
PKG_P8PANELS_QE_BASE.QUERY_PBL_SET(NRN => NRN, NPBL => NPBL);
|
||||||
|
end QUERY_PBL_SET;
|
||||||
|
|
||||||
|
end PKG_P8PANELS_QE;
|
||||||
|
/
|
1573
db/PKG_P8PANELS_QE_BASE.pck
Normal file
1573
db/PKG_P8PANELS_QE_BASE.pck
Normal file
File diff suppressed because it is too large
Load Diff
@ -3,3 +3,5 @@ grant execute on PKG_P8PANELS_PROJECTS to public;
|
|||||||
grant execute on PKG_P8PANELS_SAMPLES to public;
|
grant execute on PKG_P8PANELS_SAMPLES to public;
|
||||||
grant execute on PKG_P8PANELS_EQUIPSRV to public;
|
grant execute on PKG_P8PANELS_EQUIPSRV to public;
|
||||||
grant execute on PKG_P8PANELS_RRPCONFED to public;
|
grant execute on PKG_P8PANELS_RRPCONFED to public;
|
||||||
|
grant execute on PKG_P8PANELS_PE to public;
|
||||||
|
grant execute on PKG_P8PANELS_QE to public;
|
||||||
|
1682
dist/p8-panels.js
vendored
1682
dist/p8-panels.js
vendored
File diff suppressed because one or more lines are too long
648
package-lock.json
generated
648
package-lock.json
generated
@ -30,6 +30,7 @@
|
|||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-grid-layout": "^1.5.1",
|
"react-grid-layout": "^1.5.1",
|
||||||
"react-router-dom": "^6.15.0",
|
"react-router-dom": "^6.15.0",
|
||||||
|
"reactflow": "^11.11.4",
|
||||||
"style-loader": "^4.0.0",
|
"style-loader": "^4.0.0",
|
||||||
"webpack": "^5.88.2",
|
"webpack": "^5.88.2",
|
||||||
"webpack-cli": "^5.1.4"
|
"webpack-cli": "^5.1.4"
|
||||||
@ -48,12 +49,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/code-frame": {
|
"node_modules/@babel/code-frame": {
|
||||||
"version": "7.24.7",
|
"version": "7.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
|
||||||
"integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==",
|
"integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/highlight": "^7.24.7",
|
"@babel/helper-validator-identifier": "^7.27.1",
|
||||||
"picocolors": "^1.0.0"
|
"js-tokens": "^4.0.0",
|
||||||
|
"picocolors": "^1.1.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
@ -232,17 +234,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-string-parser": {
|
"node_modules/@babel/helper-string-parser": {
|
||||||
"version": "7.24.8",
|
"version": "7.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
|
||||||
"integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==",
|
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-validator-identifier": {
|
"node_modules/@babel/helper-validator-identifier": {
|
||||||
"version": "7.24.7",
|
"version": "7.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
|
||||||
"integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==",
|
"integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
@ -256,35 +258,24 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helpers": {
|
"node_modules/@babel/helpers": {
|
||||||
"version": "7.24.8",
|
"version": "7.27.6",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.8.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz",
|
||||||
"integrity": "sha512-gV2265Nkcz7weJJfvDoAEVzC1e2OTDpkGbEsebse8koXUJUXPsCMi7sRo/+SPMuMZ9MtUPnGwITTnQnU5YjyaQ==",
|
"integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/template": "^7.24.7",
|
"@babel/template": "^7.27.2",
|
||||||
"@babel/types": "^7.24.8"
|
"@babel/types": "^7.27.6"
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@babel/highlight": {
|
|
||||||
"version": "7.24.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz",
|
|
||||||
"integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/helper-validator-identifier": "^7.24.7",
|
|
||||||
"chalk": "^2.4.2",
|
|
||||||
"js-tokens": "^4.0.0",
|
|
||||||
"picocolors": "^1.0.0"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/parser": {
|
"node_modules/@babel/parser": {
|
||||||
"version": "7.24.8",
|
"version": "7.28.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.8.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz",
|
||||||
"integrity": "sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w==",
|
"integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/types": "^7.28.0"
|
||||||
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"parser": "bin/babel-parser.js"
|
"parser": "bin/babel-parser.js"
|
||||||
},
|
},
|
||||||
@ -387,24 +378,21 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/runtime": {
|
"node_modules/@babel/runtime": {
|
||||||
"version": "7.24.8",
|
"version": "7.27.6",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz",
|
||||||
"integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==",
|
"integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==",
|
||||||
"dependencies": {
|
|
||||||
"regenerator-runtime": "^0.14.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/template": {
|
"node_modules/@babel/template": {
|
||||||
"version": "7.24.7",
|
"version": "7.27.2",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
|
||||||
"integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==",
|
"integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.24.7",
|
"@babel/code-frame": "^7.27.1",
|
||||||
"@babel/parser": "^7.24.7",
|
"@babel/parser": "^7.27.2",
|
||||||
"@babel/types": "^7.24.7"
|
"@babel/types": "^7.27.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
@ -431,13 +419,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/types": {
|
"node_modules/@babel/types": {
|
||||||
"version": "7.24.9",
|
"version": "7.28.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.9.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.0.tgz",
|
||||||
"integrity": "sha512-xm8XrMKz0IlUdocVbYJe0Z9xEgidU7msskG8BbhnTPK/HZ2z/7FP7ykqPgrUH+C+r414mNfNWam1f2vqOjqjYQ==",
|
"integrity": "sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-string-parser": "^7.24.8",
|
"@babel/helper-string-parser": "^7.27.1",
|
||||||
"@babel/helper-validator-identifier": "^7.24.7",
|
"@babel/helper-validator-identifier": "^7.27.1"
|
||||||
"to-fast-properties": "^2.0.0"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
@ -1090,6 +1077,102 @@
|
|||||||
"url": "https://opencollective.com/popperjs"
|
"url": "https://opencollective.com/popperjs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@reactflow/background": {
|
||||||
|
"version": "11.3.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@reactflow/background/-/background-11.3.14.tgz",
|
||||||
|
"integrity": "sha512-Gewd7blEVT5Lh6jqrvOgd4G6Qk17eGKQfsDXgyRSqM+CTwDqRldG2LsWN4sNeno6sbqVIC2fZ+rAUBFA9ZEUDA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@reactflow/core": "11.11.4",
|
||||||
|
"classcat": "^5.0.3",
|
||||||
|
"zustand": "^4.4.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=17",
|
||||||
|
"react-dom": ">=17"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@reactflow/controls": {
|
||||||
|
"version": "11.2.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@reactflow/controls/-/controls-11.2.14.tgz",
|
||||||
|
"integrity": "sha512-MiJp5VldFD7FrqaBNIrQ85dxChrG6ivuZ+dcFhPQUwOK3HfYgX2RHdBua+gx+40p5Vw5It3dVNp/my4Z3jF0dw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@reactflow/core": "11.11.4",
|
||||||
|
"classcat": "^5.0.3",
|
||||||
|
"zustand": "^4.4.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=17",
|
||||||
|
"react-dom": ">=17"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@reactflow/core": {
|
||||||
|
"version": "11.11.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@reactflow/core/-/core-11.11.4.tgz",
|
||||||
|
"integrity": "sha512-H4vODklsjAq3AMq6Np4LE12i1I4Ta9PrDHuBR9GmL8uzTt2l2jh4CiQbEMpvMDcp7xi4be0hgXj+Ysodde/i7Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3": "^7.4.0",
|
||||||
|
"@types/d3-drag": "^3.0.1",
|
||||||
|
"@types/d3-selection": "^3.0.3",
|
||||||
|
"@types/d3-zoom": "^3.0.1",
|
||||||
|
"classcat": "^5.0.3",
|
||||||
|
"d3-drag": "^3.0.0",
|
||||||
|
"d3-selection": "^3.0.0",
|
||||||
|
"d3-zoom": "^3.0.0",
|
||||||
|
"zustand": "^4.4.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=17",
|
||||||
|
"react-dom": ">=17"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@reactflow/minimap": {
|
||||||
|
"version": "11.7.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@reactflow/minimap/-/minimap-11.7.14.tgz",
|
||||||
|
"integrity": "sha512-mpwLKKrEAofgFJdkhwR5UQ1JYWlcAAL/ZU/bctBkuNTT1yqV+y0buoNVImsRehVYhJwffSWeSHaBR5/GJjlCSQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@reactflow/core": "11.11.4",
|
||||||
|
"@types/d3-selection": "^3.0.3",
|
||||||
|
"@types/d3-zoom": "^3.0.1",
|
||||||
|
"classcat": "^5.0.3",
|
||||||
|
"d3-selection": "^3.0.0",
|
||||||
|
"d3-zoom": "^3.0.0",
|
||||||
|
"zustand": "^4.4.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=17",
|
||||||
|
"react-dom": ">=17"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@reactflow/node-resizer": {
|
||||||
|
"version": "2.2.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@reactflow/node-resizer/-/node-resizer-2.2.14.tgz",
|
||||||
|
"integrity": "sha512-fwqnks83jUlYr6OHcdFEedumWKChTHRGw/kbCxj0oqBd+ekfs+SIp4ddyNU0pdx96JIm5iNFS0oNrmEiJbbSaA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@reactflow/core": "11.11.4",
|
||||||
|
"classcat": "^5.0.4",
|
||||||
|
"d3-drag": "^3.0.0",
|
||||||
|
"d3-selection": "^3.0.0",
|
||||||
|
"zustand": "^4.4.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=17",
|
||||||
|
"react-dom": ">=17"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@reactflow/node-toolbar": {
|
||||||
|
"version": "1.3.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@reactflow/node-toolbar/-/node-toolbar-1.3.14.tgz",
|
||||||
|
"integrity": "sha512-rbynXQnH/xFNu4P9H+hVqlEUafDCkEoCy0Dg9mG22Sg+rY/0ck6KkrAQrYrTgXusd+cEJOMK0uOOFCK2/5rSGQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@reactflow/core": "11.11.4",
|
||||||
|
"classcat": "^5.0.3",
|
||||||
|
"zustand": "^4.4.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=17",
|
||||||
|
"react-dom": ">=17"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@remix-run/router": {
|
"node_modules/@remix-run/router": {
|
||||||
"version": "1.18.0",
|
"version": "1.18.0",
|
||||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.18.0.tgz",
|
||||||
@ -1098,6 +1181,228 @@
|
|||||||
"node": ">=14.0.0"
|
"node": ">=14.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/d3": {
|
||||||
|
"version": "7.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz",
|
||||||
|
"integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-array": "*",
|
||||||
|
"@types/d3-axis": "*",
|
||||||
|
"@types/d3-brush": "*",
|
||||||
|
"@types/d3-chord": "*",
|
||||||
|
"@types/d3-color": "*",
|
||||||
|
"@types/d3-contour": "*",
|
||||||
|
"@types/d3-delaunay": "*",
|
||||||
|
"@types/d3-dispatch": "*",
|
||||||
|
"@types/d3-drag": "*",
|
||||||
|
"@types/d3-dsv": "*",
|
||||||
|
"@types/d3-ease": "*",
|
||||||
|
"@types/d3-fetch": "*",
|
||||||
|
"@types/d3-force": "*",
|
||||||
|
"@types/d3-format": "*",
|
||||||
|
"@types/d3-geo": "*",
|
||||||
|
"@types/d3-hierarchy": "*",
|
||||||
|
"@types/d3-interpolate": "*",
|
||||||
|
"@types/d3-path": "*",
|
||||||
|
"@types/d3-polygon": "*",
|
||||||
|
"@types/d3-quadtree": "*",
|
||||||
|
"@types/d3-random": "*",
|
||||||
|
"@types/d3-scale": "*",
|
||||||
|
"@types/d3-scale-chromatic": "*",
|
||||||
|
"@types/d3-selection": "*",
|
||||||
|
"@types/d3-shape": "*",
|
||||||
|
"@types/d3-time": "*",
|
||||||
|
"@types/d3-time-format": "*",
|
||||||
|
"@types/d3-timer": "*",
|
||||||
|
"@types/d3-transition": "*",
|
||||||
|
"@types/d3-zoom": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-array": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-axis": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz",
|
||||||
|
"integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-selection": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-brush": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz",
|
||||||
|
"integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-selection": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-chord": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz",
|
||||||
|
"integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-color": {
|
||||||
|
"version": "3.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
|
||||||
|
"integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-contour": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz",
|
||||||
|
"integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-array": "*",
|
||||||
|
"@types/geojson": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-delaunay": {
|
||||||
|
"version": "6.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
|
||||||
|
"integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-dispatch": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz",
|
||||||
|
"integrity": "sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-drag": {
|
||||||
|
"version": "3.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz",
|
||||||
|
"integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-selection": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-dsv": {
|
||||||
|
"version": "3.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz",
|
||||||
|
"integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-ease": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-fetch": {
|
||||||
|
"version": "3.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz",
|
||||||
|
"integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-dsv": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-force": {
|
||||||
|
"version": "3.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz",
|
||||||
|
"integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-format": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-geo": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/geojson": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-hierarchy": {
|
||||||
|
"version": "3.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz",
|
||||||
|
"integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-interpolate": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-color": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-path": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-polygon": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-quadtree": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz",
|
||||||
|
"integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-random": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-scale": {
|
||||||
|
"version": "4.0.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
|
||||||
|
"integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-time": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-scale-chromatic": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-selection": {
|
||||||
|
"version": "3.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz",
|
||||||
|
"integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-shape": {
|
||||||
|
"version": "3.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz",
|
||||||
|
"integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-path": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-time": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-time-format": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-timer": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-transition": {
|
||||||
|
"version": "3.0.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz",
|
||||||
|
"integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-selection": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-zoom": {
|
||||||
|
"version": "3.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz",
|
||||||
|
"integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-interpolate": "*",
|
||||||
|
"@types/d3-selection": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/eslint": {
|
"node_modules/@types/eslint": {
|
||||||
"version": "9.6.0",
|
"version": "9.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.0.tgz",
|
||||||
@ -1121,6 +1426,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
|
||||||
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="
|
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/geojson": {
|
||||||
|
"version": "7946.0.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
|
||||||
|
"integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg=="
|
||||||
|
},
|
||||||
"node_modules/@types/hoist-non-react-statics": {
|
"node_modules/@types/hoist-non-react-statics": {
|
||||||
"version": "3.3.6",
|
"version": "3.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.6.tgz",
|
||||||
@ -1454,17 +1764,6 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ansi-styles": {
|
|
||||||
"version": "3.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
|
||||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
|
||||||
"dependencies": {
|
|
||||||
"color-convert": "^1.9.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/argparse": {
|
"node_modules/argparse": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||||
@ -1651,9 +1950,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.12",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"balanced-match": "^1.0.0",
|
"balanced-match": "^1.0.0",
|
||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
@ -1740,27 +2039,6 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/chalk": {
|
|
||||||
"version": "2.4.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
|
||||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"ansi-styles": "^3.2.1",
|
|
||||||
"escape-string-regexp": "^1.0.5",
|
|
||||||
"supports-color": "^5.3.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/chalk/node_modules/escape-string-regexp": {
|
|
||||||
"version": "1.0.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
|
||||||
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/chart.js": {
|
"node_modules/chart.js": {
|
||||||
"version": "4.4.3",
|
"version": "4.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.3.tgz",
|
||||||
@ -1780,6 +2058,11 @@
|
|||||||
"node": ">=6.0"
|
"node": ">=6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/classcat": {
|
||||||
|
"version": "5.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz",
|
||||||
|
"integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w=="
|
||||||
|
},
|
||||||
"node_modules/clone-deep": {
|
"node_modules/clone-deep": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
|
||||||
@ -1801,19 +2084,6 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/color-convert": {
|
|
||||||
"version": "1.9.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
|
||||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
|
||||||
"dependencies": {
|
|
||||||
"color-name": "1.1.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/color-name": {
|
|
||||||
"version": "1.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
|
||||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
|
|
||||||
},
|
|
||||||
"node_modules/colorette": {
|
"node_modules/colorette": {
|
||||||
"version": "2.0.20",
|
"version": "2.0.20",
|
||||||
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
|
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
|
||||||
@ -1936,6 +2206,102 @@
|
|||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/d3-color": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-dispatch": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-drag": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-dispatch": "1 - 3",
|
||||||
|
"d3-selection": "3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-ease": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-interpolate": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-color": "1 - 3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-selection": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-timer": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-transition": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-color": "1 - 3",
|
||||||
|
"d3-dispatch": "1 - 3",
|
||||||
|
"d3-ease": "1 - 3",
|
||||||
|
"d3-interpolate": "1 - 3",
|
||||||
|
"d3-timer": "1 - 3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"d3-selection": "2 - 3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-zoom": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-dispatch": "1 - 3",
|
||||||
|
"d3-drag": "2 - 3",
|
||||||
|
"d3-interpolate": "1 - 3",
|
||||||
|
"d3-selection": "2 - 3",
|
||||||
|
"d3-transition": "2 - 3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/data-view-buffer": {
|
"node_modules/data-view-buffer": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz",
|
||||||
@ -2930,14 +3296,6 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/has-flag": {
|
|
||||||
"version": "3.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
|
||||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/has-property-descriptors": {
|
"node_modules/has-property-descriptors": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
|
||||||
@ -4468,6 +4826,23 @@
|
|||||||
"react-dom": ">=16.6.0"
|
"react-dom": ">=16.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/reactflow": {
|
||||||
|
"version": "11.11.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/reactflow/-/reactflow-11.11.4.tgz",
|
||||||
|
"integrity": "sha512-70FOtJkUWH3BAOsN+LU9lCrKoKbtOPnz2uq0CV2PLdNSwxTXOhCbsZr50GmZ+Rtw3jx8Uv7/vBFtCGixLfd4Og==",
|
||||||
|
"dependencies": {
|
||||||
|
"@reactflow/background": "11.3.14",
|
||||||
|
"@reactflow/controls": "11.2.14",
|
||||||
|
"@reactflow/core": "11.11.4",
|
||||||
|
"@reactflow/minimap": "11.7.14",
|
||||||
|
"@reactflow/node-resizer": "2.2.14",
|
||||||
|
"@reactflow/node-toolbar": "1.3.14"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=17",
|
||||||
|
"react-dom": ">=17"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/rechoir": {
|
"node_modules/rechoir": {
|
||||||
"version": "0.8.0",
|
"version": "0.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz",
|
||||||
@ -4507,11 +4882,6 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/regenerator-runtime": {
|
|
||||||
"version": "0.14.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
|
||||||
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
|
|
||||||
},
|
|
||||||
"node_modules/regexp.prototype.flags": {
|
"node_modules/regexp.prototype.flags": {
|
||||||
"version": "1.5.2",
|
"version": "1.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz",
|
||||||
@ -5010,17 +5380,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
|
||||||
"integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="
|
"integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="
|
||||||
},
|
},
|
||||||
"node_modules/supports-color": {
|
|
||||||
"version": "5.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
|
||||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
|
||||||
"dependencies": {
|
|
||||||
"has-flag": "^3.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/supports-preserve-symlinks-flag": {
|
"node_modules/supports-preserve-symlinks-flag": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||||
@ -5100,14 +5459,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
|
||||||
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="
|
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="
|
||||||
},
|
},
|
||||||
"node_modules/to-fast-properties": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/type-check": {
|
"node_modules/type-check": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||||
@ -5263,6 +5614,14 @@
|
|||||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/use-sync-external-store": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz",
|
||||||
|
"integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/util-deprecate": {
|
"node_modules/util-deprecate": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
@ -5548,6 +5907,33 @@
|
|||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/zustand": {
|
||||||
|
"version": "4.5.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz",
|
||||||
|
"integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==",
|
||||||
|
"dependencies": {
|
||||||
|
"use-sync-external-store": "^1.2.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.7.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": ">=16.8",
|
||||||
|
"immer": ">=9.0.6",
|
||||||
|
"react": ">=16.8"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"immer": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-grid-layout": "^1.5.1",
|
"react-grid-layout": "^1.5.1",
|
||||||
"react-router-dom": "^6.15.0",
|
"react-router-dom": "^6.15.0",
|
||||||
|
"reactflow": "^11.11.4",
|
||||||
"style-loader": "^4.0.0",
|
"style-loader": "^4.0.0",
|
||||||
"webpack": "^5.88.2",
|
"webpack": "^5.88.2",
|
||||||
"webpack-cli": "^5.1.4"
|
"webpack-cli": "^5.1.4"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user