ЦИТК-979 - Базовый серверный API, таблица, клиент - начало (менеджер запросов, базовая диаграмма, IUD сущностей и связей)
This commit is contained in:
parent
2830d8c5d9
commit
c990cb2246
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
@ -2,4 +2,6 @@ grant execute on PKG_P8PANELS to public;
|
|||||||
grant execute on PKG_P8PANELS_PROJECTS to public;
|
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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user