forked from CITKParus/P8-Panels
ЦИТК-979 - Компонент для настройки свойств запроса, настройка атрибутов сущностей (начало, клиент)
This commit is contained in:
parent
836a3db5e2
commit
f9b2f1ae34
@ -101,4 +101,4 @@ Attribute.propTypes = {
|
|||||||
//Интерфейс модуля
|
//Интерфейс модуля
|
||||||
//----------------
|
//----------------
|
||||||
|
|
||||||
export { Attribute };
|
export { Attribute, ATTRIBUTE_DATA_SHAPE };
|
||||||
|
@ -17,6 +17,7 @@ import "./entity.css"; //Стили компомнента
|
|||||||
|
|
||||||
//Структура данных о сущности запроса
|
//Структура данных о сущности запроса
|
||||||
const ENTITY_DATA_SHAPE = PropTypes.shape({
|
const ENTITY_DATA_SHAPE = PropTypes.shape({
|
||||||
|
id: PropTypes.string.isRequired,
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
title: PropTypes.string.isRequired
|
title: PropTypes.string.isRequired
|
||||||
});
|
});
|
||||||
@ -26,7 +27,7 @@ const ENTITY_DATA_SHAPE = PropTypes.shape({
|
|||||||
//-----------
|
//-----------
|
||||||
|
|
||||||
//Сущность запроса
|
//Сущность запроса
|
||||||
const Entity = ({ data, selected }) => {
|
const Entity = ({ data, selected = false }) => {
|
||||||
return (
|
return (
|
||||||
<div className="entity__wrapper" data-selected={selected}>
|
<div className="entity__wrapper" data-selected={selected}>
|
||||||
<div className="entity__title">
|
<div className="entity__title">
|
||||||
@ -47,4 +48,4 @@ Entity.propTypes = {
|
|||||||
//Интерфейс модуля
|
//Интерфейс модуля
|
||||||
//----------------
|
//----------------
|
||||||
|
|
||||||
export { Entity };
|
export { Entity, ENTITY_DATA_SHAPE };
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - Редактор запросов
|
||||||
|
Компонент: Диалог настройки атрибутов сущности
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React from "react"; //Классы React
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
import { P8PDialog } from "../../../../components/p8p_dialog"; //Типовой диалог
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Диалог настройки атрибутов сущности
|
||||||
|
const EntityAttrsDialog = ({ id, title, onOk, onCancel }) => {
|
||||||
|
//Нажатие на кнопку "Ok"
|
||||||
|
const handleOk = values => onOk && onOk({ ...values });
|
||||||
|
|
||||||
|
//Нажатие на кнопку "Отмена"
|
||||||
|
const handleCancel = () => onCancel && onCancel();
|
||||||
|
|
||||||
|
//Генерация содержимого
|
||||||
|
return <P8PDialog title={`Атрибуты сущности "${title}"`} onOk={handleOk} onCancel={handleCancel} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств - Диалог настройки атрибутов сущности
|
||||||
|
EntityAttrsDialog.propTypes = {
|
||||||
|
id: PropTypes.string.isRequired,
|
||||||
|
title: PropTypes.string.isRequired,
|
||||||
|
onOk: PropTypes.func,
|
||||||
|
onCancel: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { EntityAttrsDialog };
|
@ -10,6 +10,7 @@
|
|||||||
import React from "react"; //Классы React
|
import React from "react"; //Классы React
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import { Stack, List, ListItem, IconButton, Icon, ListItemButton, ListItemText, Typography } from "@mui/material"; //Интерфейсные компоненты MUI
|
import { Stack, List, ListItem, IconButton, Icon, ListItemButton, ListItemText, Typography } from "@mui/material"; //Интерфейсные компоненты MUI
|
||||||
|
import { APP_STYLES } from "../../../../../app.styles"; //Общие стили приложения
|
||||||
import { BUTTONS } from "../../../../../app.text"; //Общие текстовые ресурсы приложения
|
import { BUTTONS } from "../../../../../app.text"; //Общие текстовые ресурсы приложения
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
@ -20,7 +21,8 @@ import { BUTTONS } from "../../../../../app.text"; //Общие текстовы
|
|||||||
const STYLES = {
|
const STYLES = {
|
||||||
SMALL_TOOL_ICON: {
|
SMALL_TOOL_ICON: {
|
||||||
fontSize: 20
|
fontSize: 20
|
||||||
}
|
},
|
||||||
|
LIST: { height: "500px", width: "360px", bgcolor: "background.paper", overflowY: "auto", ...APP_STYLES.SCROLL }
|
||||||
};
|
};
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
@ -76,7 +78,7 @@ const QueriesList = ({ queries = [], current = null, onSelect = null, onPbl = nu
|
|||||||
|
|
||||||
//Формирование представления
|
//Формирование представления
|
||||||
return (
|
return (
|
||||||
<List sx={{ height: "500px", width: "360px", bgcolor: "background.paper", overflowY: "auto" }}>
|
<List sx={STYLES.LIST}>
|
||||||
{queries.map((query, i) => {
|
{queries.map((query, i) => {
|
||||||
const selected = query.rn === current;
|
const selected = query.rn === current;
|
||||||
const disabled = !query.modify;
|
const disabled = !query.modify;
|
||||||
|
@ -36,7 +36,7 @@ const QueriesManager = ({ current = null, onQuerySelect = null, onCancel = null
|
|||||||
const handleQueryAdd = () => setModQuery(true);
|
const handleQueryAdd = () => setModQuery(true);
|
||||||
|
|
||||||
//При выборе запроса
|
//При выборе запроса
|
||||||
const handleQuerySelect = query => onQuerySelect && onQuerySelect(query.rn);
|
const handleQuerySelect = query => onQuerySelect && onQuerySelect({ ...query });
|
||||||
|
|
||||||
//При установке признака публичности
|
//При установке признака публичности
|
||||||
const handleQueryPblSet = query => setQueryPbl(query.rn, query.pbl === 1 ? 0 : 1);
|
const handleQueryPblSet = query => setQueryPbl(query.rn, query.pbl === 1 ? 0 : 1);
|
||||||
|
@ -8,10 +8,11 @@
|
|||||||
//---------------------
|
//---------------------
|
||||||
|
|
||||||
import React, { useState, useCallback, useEffect } from "react"; //Классы React
|
import React, { useState, useCallback, useEffect } from "react"; //Классы React
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import ReactFlow, { addEdge, Controls, getOutgoers, applyNodeChanges, applyEdgeChanges } from "reactflow"; //Библиотека редактора диаграмм
|
import ReactFlow, { addEdge, Controls, getOutgoers, applyNodeChanges, applyEdgeChanges } from "reactflow"; //Библиотека редактора диаграмм
|
||||||
import { NODE_TYPE } from "../../common"; //Общие ресурсы и константы редактора
|
import { NODE_TYPE } from "../../common"; //Общие ресурсы и константы редактора
|
||||||
import { Entity } from "../entity/entity"; //Сущность запроса
|
import { Entity, ENTITY_DATA_SHAPE } from "../entity/entity"; //Сущность запроса
|
||||||
import { Attribute } from "../attribute/attribute"; //Атрибут сущности
|
import { Attribute, ATTRIBUTE_DATA_SHAPE } from "../attribute/attribute"; //Атрибут сущности
|
||||||
import "reactflow/dist/style.css"; //Типовые стили библиотеки редактора диаграмм
|
import "reactflow/dist/style.css"; //Типовые стили библиотеки редактора диаграмм
|
||||||
import "./query_diagram.css"; //Стили компонента
|
import "./query_diagram.css"; //Стили компонента
|
||||||
|
|
||||||
@ -36,42 +37,44 @@ const NODE_TYPES_COMPONENTS = {
|
|||||||
[NODE_TYPE.ATTRIBUTE]: Attribute
|
[NODE_TYPE.ATTRIBUTE]: Attribute
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//Структура сущности запроса
|
||||||
|
const ENTITY_SHAPE = PropTypes.shape({
|
||||||
|
id: PropTypes.string.isRequired,
|
||||||
|
type: PropTypes.oneOf([NODE_TYPE.ENTITY, NODE_TYPE.ATTRIBUTE]).isRequired,
|
||||||
|
style: PropTypes.object,
|
||||||
|
position: PropTypes.shape({ x: PropTypes.number.isRequired, y: PropTypes.number.isRequired }),
|
||||||
|
draggable: PropTypes.bool.isRequired,
|
||||||
|
data: PropTypes.oneOfType([ENTITY_DATA_SHAPE, ATTRIBUTE_DATA_SHAPE])
|
||||||
|
});
|
||||||
|
|
||||||
|
//Структура связи запроса
|
||||||
|
const RELATION_SHAPE = PropTypes.shape({ id: PropTypes.string.isRequired, source: PropTypes.string.isRequired, target: PropTypes.string.isRequired });
|
||||||
|
|
||||||
//------------------------------------
|
//------------------------------------
|
||||||
//Вспомогательные функции и компоненты
|
//Вспомогательные функции и компоненты
|
||||||
//------------------------------------
|
//------------------------------------
|
||||||
|
|
||||||
|
//Проверка зацикленности связи
|
||||||
const hasCycle = (connection, target, nodes, edges, visited = new Set()) => {
|
const hasCycle = (connection, target, nodes, edges, visited = new Set()) => {
|
||||||
if (visited.has(target.id)) {
|
if (visited.has(target.id)) return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
visited.add(target.id);
|
visited.add(target.id);
|
||||||
|
for (const outgoer of getOutgoers(target, nodes, edges))
|
||||||
for (const outgoer of getOutgoers(target, nodes, edges)) {
|
if (outgoer.id === connection.source || hasCycle(connection, outgoer, nodes, edges, visited)) return true;
|
||||||
if (outgoer.id === connection.source || hasCycle(connection, outgoer, nodes, edges, visited)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//Проверка корректности связи
|
||||||
const isValidConnection = (connection, nodes, edges) => {
|
const isValidConnection = (connection, nodes, edges) => {
|
||||||
if (!connection.source || !connection.target) {
|
//Должны быть заданы источник и приёмник
|
||||||
return false;
|
if (!connection.source || !connection.target) return false;
|
||||||
}
|
//Нельзя ссылаться на самого себя
|
||||||
|
const tableId = connection.source.split(".")[0];
|
||||||
const tableId = connection.source.split("-")[0];
|
|
||||||
const isSameTable = connection.target.startsWith(tableId);
|
const isSameTable = connection.target.startsWith(tableId);
|
||||||
if (isSameTable) {
|
if (isSameTable) return false;
|
||||||
return false;
|
//Приёмник должен быть среди элементов диаграммы и не должен быть источником
|
||||||
}
|
|
||||||
|
|
||||||
const target = nodes.find(node => node.id === connection.target);
|
const target = nodes.find(node => node.id === connection.target);
|
||||||
if (!target || target.id === connection.source) {
|
if (!target || target.id === connection.source) return false;
|
||||||
return false;
|
//Нельзя зацикливаться
|
||||||
}
|
|
||||||
|
|
||||||
return !hasCycle(connection, target, nodes, edges);
|
return !hasCycle(connection, target, nodes, edges);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -80,7 +83,16 @@ const isValidConnection = (connection, nodes, edges) => {
|
|||||||
//-----------
|
//-----------
|
||||||
|
|
||||||
//Диаграмма запроса
|
//Диаграмма запроса
|
||||||
const QueryDiagram = ({ entities, relations, onEntityPositionChange, onEntityRemove, onRelationAdd, onRelationRemove }) => {
|
const QueryDiagram = ({
|
||||||
|
entities = [],
|
||||||
|
relations = [],
|
||||||
|
onEntityClick,
|
||||||
|
onEntityPositionChange,
|
||||||
|
onEntityRemove,
|
||||||
|
onRelactionClick,
|
||||||
|
onRelationAdd,
|
||||||
|
onRelationRemove
|
||||||
|
}) => {
|
||||||
//Собственное состояние - элементы
|
//Собственное состояние - элементы
|
||||||
const [nodes, setNodes] = useState(entities);
|
const [nodes, setNodes] = useState(entities);
|
||||||
|
|
||||||
@ -98,20 +110,27 @@ const QueryDiagram = ({ entities, relations, onEntityPositionChange, onEntityRem
|
|||||||
setMovedNode({ id: changes[0].id, position: { ...changes[0].position } });
|
setMovedNode({ id: changes[0].id, position: { ...changes[0].position } });
|
||||||
if (changes.length == 1 && changes[0].type == "position" && !changes[0].dragging && movedNode) {
|
if (changes.length == 1 && changes[0].type == "position" && !changes[0].dragging && movedNode) {
|
||||||
if (onEntityPositionChange) onEntityPositionChange(movedNode.id, movedNode.position);
|
if (onEntityPositionChange) onEntityPositionChange(movedNode.id, movedNode.position);
|
||||||
|
if (onEntityClick) onEntityClick(movedNode.id);
|
||||||
setMovedNode(null);
|
setMovedNode(null);
|
||||||
}
|
}
|
||||||
if (changes[0].type == "remove" && entities.find(e => e.id == changes[0].id && e.type == NODE_TYPE.ENTITY) && onEntityRemove)
|
if (changes[0].type == "remove" && entities.find(e => e.id == changes[0].id && e.type == NODE_TYPE.ENTITY) && onEntityRemove)
|
||||||
onEntityRemove(changes[0].id);
|
onEntityRemove(changes[0].id);
|
||||||
},
|
},
|
||||||
[movedNode, entities, onEntityPositionChange, onEntityRemove]
|
[movedNode, entities, onEntityClick, onEntityPositionChange, onEntityRemove]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
//При выборе элемента диаграммы
|
||||||
|
const handleNodeClick = useCallback((e, node) => onEntityClick && onEntityClick(node.id), [onEntityClick]);
|
||||||
|
|
||||||
//При связывании элементов на диаграмме
|
//При связывании элементов на диаграмме
|
||||||
const handleConnect = connection => {
|
const handleConnect = connection => {
|
||||||
setEdges(state => addEdge({ ...connection, id: `${connection.source}-${connection.target}` }, state));
|
setEdges(state => addEdge({ ...connection, id: `${connection.source}-${connection.target}` }, state));
|
||||||
onRelationAdd && onRelationAdd(connection.source, connection.target);
|
onRelationAdd && onRelationAdd(connection.source, connection.target);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//При выборе связи диаграммы
|
||||||
|
const handleEdgeClick = useCallback((e, edge) => onRelactionClick && onRelactionClick(edge.id), [onRelactionClick]);
|
||||||
|
|
||||||
//При изменении связей на диаграмме
|
//При изменении связей на диаграмме
|
||||||
const handleEdgesChange = useCallback(
|
const handleEdgesChange = useCallback(
|
||||||
changes => {
|
changes => {
|
||||||
@ -121,9 +140,8 @@ const QueryDiagram = ({ entities, relations, onEntityPositionChange, onEntityRem
|
|||||||
[onRelationRemove]
|
[onRelationRemove]
|
||||||
);
|
);
|
||||||
|
|
||||||
const validateConnection = connection => {
|
//Валидация связи
|
||||||
return isValidConnection(connection, nodes, edges);
|
const validateConnection = connection => isValidConnection(connection, nodes, edges);
|
||||||
};
|
|
||||||
|
|
||||||
//При изменении состава сущностей
|
//При изменении состава сущностей
|
||||||
useEffect(() => setNodes(entities), [entities]);
|
useEffect(() => setNodes(entities), [entities]);
|
||||||
@ -137,7 +155,9 @@ const QueryDiagram = ({ entities, relations, onEntityPositionChange, onEntityRem
|
|||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
nodeTypes={NODE_TYPES_COMPONENTS}
|
nodeTypes={NODE_TYPES_COMPONENTS}
|
||||||
edges={edges}
|
edges={edges}
|
||||||
|
onNodeClick={handleNodeClick}
|
||||||
onNodesChange={handleNodesChange}
|
onNodesChange={handleNodesChange}
|
||||||
|
onEdgeClick={handleEdgeClick}
|
||||||
onEdgesChange={handleEdgesChange}
|
onEdgesChange={handleEdgesChange}
|
||||||
defaultEdgeOptions={{
|
defaultEdgeOptions={{
|
||||||
animated: true,
|
animated: true,
|
||||||
@ -153,6 +173,18 @@ const QueryDiagram = ({ entities, relations, onEntityPositionChange, onEntityRem
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//Контроль свойств компонента - Диаграмма запроса
|
||||||
|
QueryDiagram.propTypes = {
|
||||||
|
entities: PropTypes.arrayOf(ENTITY_SHAPE),
|
||||||
|
relations: PropTypes.arrayOf(RELATION_SHAPE),
|
||||||
|
onEntityClick: PropTypes.func,
|
||||||
|
onEntityPositionChange: PropTypes.func,
|
||||||
|
onEntityRemove: PropTypes.func,
|
||||||
|
onRelactionClick: PropTypes.func,
|
||||||
|
onRelationAdd: PropTypes.func,
|
||||||
|
onRelationRemove: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
//----------------
|
//----------------
|
||||||
//Интерфейс модуля
|
//Интерфейс модуля
|
||||||
//----------------
|
//----------------
|
||||||
|
@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
Парус 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 { EntityAddDialog } from "../entity_add_dialog/entity_add_dialog"; //Диалог добавления сущности
|
||||||
|
import { EntityAttrsDialog } from "../entity_attrs_dialog/entity_attrs_dialog"; //Диалог настройки атрибутов сущности
|
||||||
|
import { P8PEditorBox } from "../../../../components/editors/p8p_editor_box"; //Контейнер параметров редактора
|
||||||
|
import { P8PEditorSubHeader } from "../../../../components/editors/p8p_editor_sub_header"; //Подзаголовок группы параметров редактора
|
||||||
|
import { ENTITY_DATA_SHAPE } from "../entity/entity"; //Описание сущности
|
||||||
|
import { RELATION_DATA_SHAPE } from "../relation/relation"; //Описание связи
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Свойства запроса
|
||||||
|
const QueryOptions = ({ onEntityAdd, onEntityRemove, onRelationRemove, onQueryOptionsChanged, entity, relation }) => {
|
||||||
|
//Отображение диалога добавления сущности
|
||||||
|
const [openEntityAddDialog, setOpenEntityAddDialog] = useState(false);
|
||||||
|
|
||||||
|
//Отображение диалога настройки атрибутов сущности
|
||||||
|
const [openEntityAttrsDialog, setOpenEntityAttrsDialog] = useState(false);
|
||||||
|
|
||||||
|
//Подключение к контексту сообщений
|
||||||
|
const { showMsgWarn } = useContext(MessagingСtx);
|
||||||
|
|
||||||
|
//При нажатии на кнопку добавлении сущности в запрос
|
||||||
|
const handleEntityAddClick = () => setOpenEntityAddDialog(true);
|
||||||
|
|
||||||
|
//При нажатии на кнопку настройки атрибутов сущности
|
||||||
|
const handleEntityAttrsClick = () => setOpenEntityAttrsDialog(true);
|
||||||
|
|
||||||
|
//При нажатии на кнопку даления сущности из запроса
|
||||||
|
const handleEntityRemoveClick = () =>
|
||||||
|
showMsgWarn(`Удалить сущность "${entity.title}"?`, () => entity?.id && onEntityRemove && onEntityRemove(entity.id));
|
||||||
|
|
||||||
|
//При нажатии на кнопку даления связи из запроса
|
||||||
|
const handleRelationRemoveClick = () =>
|
||||||
|
showMsgWarn(
|
||||||
|
`Удалить связь "${relation.source}" - "${relation.target}"?`,
|
||||||
|
() => relation?.id && onRelationRemove && onRelationRemove(relation.id)
|
||||||
|
);
|
||||||
|
|
||||||
|
//Закрытие диалога добавления сущности по "Отмена"
|
||||||
|
const handleEntityAddDialogCancel = () => setOpenEntityAddDialog(false);
|
||||||
|
|
||||||
|
//Закрытие диалога добавления сущности по "ОК"
|
||||||
|
const handleEntityAddDialogOk = values => onEntityAdd && onEntityAdd(values.name, res => res && setOpenEntityAddDialog(false));
|
||||||
|
|
||||||
|
//Закрытие диалога настройки атрибутов сущности по "Отмена"
|
||||||
|
const handleEntityAttrsDialogCancel = () => setOpenEntityAttrsDialog(false);
|
||||||
|
|
||||||
|
//Закрытие диалога настройки атрибутов сущности по "ОК"
|
||||||
|
const handleEntityAttrsDialogOk = () => {
|
||||||
|
onQueryOptionsChanged && onQueryOptionsChanged();
|
||||||
|
setOpenEntityAttrsDialog();
|
||||||
|
};
|
||||||
|
|
||||||
|
//Генерация содержимого
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{openEntityAddDialog && <EntityAddDialog onOk={handleEntityAddDialogOk} onCancel={handleEntityAddDialogCancel} />}
|
||||||
|
{openEntityAttrsDialog && <EntityAttrsDialog {...entity} onOk={handleEntityAttrsDialogOk} onCancel={handleEntityAttrsDialogCancel} />}
|
||||||
|
<P8PEditorBox title={"Параметры запроса"}>
|
||||||
|
<P8PEditorSubHeader title={"Сущности"} />
|
||||||
|
<Button startIcon={<Icon>add</Icon>} onClick={handleEntityAddClick}>
|
||||||
|
{BUTTONS.INSERT}
|
||||||
|
</Button>
|
||||||
|
{entity && (
|
||||||
|
<>
|
||||||
|
<P8PEditorSubHeader title={entity.title} />
|
||||||
|
<Button startIcon={<Icon>edit_attributes</Icon>} onClick={handleEntityAttrsClick}>
|
||||||
|
Атрибуты
|
||||||
|
</Button>
|
||||||
|
<Button startIcon={<Icon>delete</Icon>} onClick={handleEntityRemoveClick}>
|
||||||
|
{BUTTONS.DELETE}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{relation && (
|
||||||
|
<>
|
||||||
|
<P8PEditorSubHeader title={"Связь"} />
|
||||||
|
<Button startIcon={<Icon>delete</Icon>} onClick={handleRelationRemoveClick}>
|
||||||
|
{BUTTONS.DELETE}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</P8PEditorBox>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств компонента - Свойства запроса
|
||||||
|
QueryOptions.propTypes = {
|
||||||
|
onEntityAdd: PropTypes.func,
|
||||||
|
onEntityRemove: PropTypes.func,
|
||||||
|
onRelationRemove: PropTypes.func,
|
||||||
|
onQueryOptionsChanged: PropTypes.func,
|
||||||
|
entity: ENTITY_DATA_SHAPE,
|
||||||
|
relation: RELATION_DATA_SHAPE
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { QueryOptions };
|
27
app/panels/query_editor/components/relation/relation.js
Normal file
27
app/panels/query_editor/components/relation/relation.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - Редактор запросов
|
||||||
|
Компоненты: Связь сущностей запроса
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Структура данных о связи сущностей запроса
|
||||||
|
const RELATION_DATA_SHAPE = PropTypes.shape({
|
||||||
|
id: PropTypes.string.isRequired,
|
||||||
|
source: PropTypes.string.isRequired,
|
||||||
|
target: PropTypes.string.isRequired
|
||||||
|
});
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { RELATION_DATA_SHAPE };
|
@ -7,22 +7,23 @@
|
|||||||
//Подключение библиотек
|
//Подключение библиотек
|
||||||
//---------------------
|
//---------------------
|
||||||
|
|
||||||
import React, { useState } from "react"; //Классы React
|
import React, { useState, useContext } from "react"; //Классы React
|
||||||
import { Box, Grid, Button, Icon } from "@mui/material"; //Интерфейсные компоненты MUI
|
import { Box, Grid } from "@mui/material"; //Интерфейсные компоненты MUI
|
||||||
|
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 { P8PEditorToolBar } from "../../components/editors/p8p_editor_toolbar"; //Панель инструментов редактора
|
||||||
import { BUTTONS } from "../../../app.text"; //Общие текстовые ресурсы приложения
|
|
||||||
import { QueryDiagram } from "./components/query_diagram/query_diagram"; //Диаграмма запроса
|
import { QueryDiagram } from "./components/query_diagram/query_diagram"; //Диаграмма запроса
|
||||||
|
import { QueryOptions } from "./components/query_options/query_options"; //Свойства запроса
|
||||||
import { QueriesManager } from "./components/queries_manager/queries_manager"; //Менеджер запросов
|
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"; //Пользовательские хуки
|
import { useQueryDesc } from "./hooks"; //Пользовательские хуки
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
//Константы
|
//Константы
|
||||||
//---------
|
//---------
|
||||||
|
|
||||||
|
//Заголовок панели по умолчанию
|
||||||
|
const APP_BAR_TITLE_DEFAULT = "Редактор запросов";
|
||||||
|
|
||||||
//Стили
|
//Стили
|
||||||
const STYLES = {
|
const STYLES = {
|
||||||
CONTAINER: { display: "flex" },
|
CONTAINER: { display: "flex" },
|
||||||
@ -39,26 +40,62 @@ const QueryEditor = () => {
|
|||||||
//Текущий запрос
|
//Текущий запрос
|
||||||
const [query, setQuery] = useState(null);
|
const [query, setQuery] = useState(null);
|
||||||
|
|
||||||
|
//Текущая сущность
|
||||||
|
const [entity, setEntity] = useState(null);
|
||||||
|
|
||||||
|
//Текущая связь
|
||||||
|
const [relation, setRelation] = useState(null);
|
||||||
|
|
||||||
//Отображения менеджера запросов
|
//Отображения менеджера запросов
|
||||||
const [openQueriesManager, setOpenQueriesManager] = useState(true);
|
const [openQueriesManager, setOpenQueriesManager] = useState(true);
|
||||||
|
|
||||||
//Отображение диалога добавления сущности
|
|
||||||
const [openEntityAddDialog, setOpenEntityAddDialog] = useState(false);
|
|
||||||
|
|
||||||
//Получение данных запроса
|
//Получение данных запроса
|
||||||
const [queryDiagram, addEnt, removeEnt, setEntPosition, addRl, removeRl] = useQueryDesc(query);
|
const [queryDiagram, addEnt, removeEnt, setEntPosition, addRl, removeRl, doRefresh] = useQueryDesc(query);
|
||||||
|
|
||||||
|
//Подключение к контексту приложения
|
||||||
|
const { setAppBarTitle } = useContext(ApplicationСtx);
|
||||||
|
|
||||||
//Обработка изменения положения сущности на диаграмме
|
//Обработка изменения положения сущности на диаграмме
|
||||||
const handleEntityPositionChange = (ent, position) => setEntPosition(ent, position.x, position.y);
|
const handleEntityPositionChange = (ent, position) => setEntPosition(ent, position.x, position.y);
|
||||||
|
|
||||||
|
//Обработка добавления сущности в запрос
|
||||||
|
const handleEntityAdd = async (entName, cb) => {
|
||||||
|
await addEnt(entName, "VIEW");
|
||||||
|
cb(true);
|
||||||
|
};
|
||||||
|
|
||||||
//Обработка удаления сущности из запроса
|
//Обработка удаления сущности из запроса
|
||||||
const handleEntityRemove = ent => removeEnt(ent);
|
const handleEntityRemove = async ent => {
|
||||||
|
await removeEnt(ent);
|
||||||
|
if (entity && entity?.id === ent) setEntity(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Обработка выделения сущности
|
||||||
|
const handleEntityClick = ent => {
|
||||||
|
setRelation(null);
|
||||||
|
const queryEnt = queryDiagram.entities.find(e => e.id === ent);
|
||||||
|
if (queryEnt)
|
||||||
|
if (entity?.id == queryEnt.id) setEntity(null);
|
||||||
|
else setEntity({ ...queryEnt.data });
|
||||||
|
};
|
||||||
|
|
||||||
|
//Обработка выделения связи
|
||||||
|
const handleRelationClick = rl => {
|
||||||
|
setEntity(null);
|
||||||
|
const queryRl = queryDiagram.relations.find(r => r.id === rl);
|
||||||
|
if (queryRl)
|
||||||
|
if (relation?.id == queryRl.id) setRelation(null);
|
||||||
|
else setRelation({ ...queryRl });
|
||||||
|
};
|
||||||
|
|
||||||
//Обработка добавления отношения cущностей
|
//Обработка добавления отношения cущностей
|
||||||
const handleRelationAdd = (source, target) => addRl(source, target);
|
const handleRelationAdd = (source, target) => addRl(source, target);
|
||||||
|
|
||||||
//Обработка удаления отношения cущностей
|
//Обработка удаления отношения cущностей
|
||||||
const handleRelationRemove = rl => removeRl(rl);
|
const handleRelationRemove = async rl => {
|
||||||
|
await removeRl(rl);
|
||||||
|
if (relation && relation?.id === rl) setRelation(null);
|
||||||
|
};
|
||||||
|
|
||||||
//Открытие менеджера запросов
|
//Открытие менеджера запросов
|
||||||
const handleOpenQueriesManager = () => setOpenQueriesManager(true);
|
const handleOpenQueriesManager = () => setOpenQueriesManager(true);
|
||||||
@ -67,25 +104,22 @@ const QueryEditor = () => {
|
|||||||
const handleCancelQueriesManager = () => setOpenQueriesManager(false);
|
const handleCancelQueriesManager = () => setOpenQueriesManager(false);
|
||||||
|
|
||||||
//Закрытие запроса
|
//Закрытие запроса
|
||||||
const handleQueryClose = () => setQuery(null);
|
const handleQueryClose = () => {
|
||||||
|
setAppBarTitle(APP_BAR_TITLE_DEFAULT);
|
||||||
|
setEntity(null);
|
||||||
|
setRelation(null);
|
||||||
|
setQuery(null);
|
||||||
|
};
|
||||||
|
|
||||||
//При выборе запроса
|
//При выборе запроса
|
||||||
const handleQuerySelect = query => {
|
const handleQuerySelect = ({ rn, name }) => {
|
||||||
setQuery(query);
|
setAppBarTitle(`Запрос [${name}]`);
|
||||||
|
setQuery(rn);
|
||||||
setOpenQueriesManager(false);
|
setOpenQueriesManager(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
//При добавлении сущности в запрос
|
//При изменении свойств запроса
|
||||||
const handleEntityAdd = () => setOpenEntityAddDialog(true);
|
const handleQueryOptionsChanged = () => doRefresh();
|
||||||
|
|
||||||
//Закрытие диалога добавления сущности по "ОК"
|
|
||||||
const handleEntityAddDialogOk = async values => {
|
|
||||||
await addEnt(values.name, "VIEW");
|
|
||||||
setOpenEntityAddDialog(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Закрытие диалога добавления сущности по "ОК"
|
|
||||||
const handleEntityAddDialogCancel = () => setOpenEntityAddDialog(false);
|
|
||||||
|
|
||||||
//Панель инструмментов
|
//Панель инструмментов
|
||||||
const toolBar = (
|
const toolBar = (
|
||||||
@ -101,14 +135,15 @@ const QueryEditor = () => {
|
|||||||
return (
|
return (
|
||||||
<Box sx={STYLES.CONTAINER}>
|
<Box sx={STYLES.CONTAINER}>
|
||||||
{openQueriesManager && <QueriesManager current={query} onQuerySelect={handleQuerySelect} onCancel={handleCancelQueriesManager} />}
|
{openQueriesManager && <QueriesManager current={query} onQuerySelect={handleQuerySelect} onCancel={handleCancelQueriesManager} />}
|
||||||
{openEntityAddDialog && <EntityAddDialog onOk={handleEntityAddDialogOk} onCancel={handleEntityAddDialogCancel} />}
|
|
||||||
<Grid container sx={STYLES.GRID_CONTAINER} columns={25}>
|
<Grid container sx={STYLES.GRID_CONTAINER} columns={25}>
|
||||||
<Grid item xs={20}>
|
<Grid item xs={20}>
|
||||||
{queryDiagram && (
|
{queryDiagram && (
|
||||||
<QueryDiagram
|
<QueryDiagram
|
||||||
{...queryDiagram}
|
{...queryDiagram}
|
||||||
|
onEntityClick={handleEntityClick}
|
||||||
onEntityPositionChange={handleEntityPositionChange}
|
onEntityPositionChange={handleEntityPositionChange}
|
||||||
onEntityRemove={handleEntityRemove}
|
onEntityRemove={handleEntityRemove}
|
||||||
|
onRelactionClick={handleRelationClick}
|
||||||
onRelationAdd={handleRelationAdd}
|
onRelationAdd={handleRelationAdd}
|
||||||
onRelationRemove={handleRelationRemove}
|
onRelationRemove={handleRelationRemove}
|
||||||
/>
|
/>
|
||||||
@ -117,12 +152,14 @@ const QueryEditor = () => {
|
|||||||
<Grid item xs={5} sx={STYLES.GRID_ITEM_INSPECTOR}>
|
<Grid item xs={5} sx={STYLES.GRID_ITEM_INSPECTOR}>
|
||||||
{toolBar}
|
{toolBar}
|
||||||
{query && (
|
{query && (
|
||||||
<P8PEditorBox title={"Параметры запроса"}>
|
<QueryOptions
|
||||||
<P8PEditorSubHeader title={"Сущности"} />
|
onEntityAdd={handleEntityAdd}
|
||||||
<Button startIcon={<Icon>add</Icon>} onClick={handleEntityAdd}>
|
onEntityRemove={handleEntityRemove}
|
||||||
{BUTTONS.INSERT}
|
onRelationRemove={handleRelationRemove}
|
||||||
</Button>
|
onQueryOptionsChanged={handleQueryOptionsChanged}
|
||||||
</P8PEditorBox>
|
entity={entity}
|
||||||
|
relation={relation}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user