ЦИТК-979 - Добавление сущности в запрос из списка с поиском по БД
This commit is contained in:
parent
f698bc1789
commit
538643591f
@ -23,8 +23,20 @@ const NODE_TYPE = {
|
|||||||
ATTRIBUTE: "attribute"
|
ATTRIBUTE: "attribute"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//Типы сущностей
|
||||||
|
const ENTITY_TYPE = {
|
||||||
|
VIEW: "VIEW",
|
||||||
|
TABLE: "TABLE"
|
||||||
|
};
|
||||||
|
|
||||||
|
//Иконки типов сущностей
|
||||||
|
const ENTITY_TYPE_ICON = {
|
||||||
|
[ENTITY_TYPE.VIEW]: "table_view",
|
||||||
|
[ENTITY_TYPE.TABLE]: "table_rows"
|
||||||
|
};
|
||||||
|
|
||||||
//----------------
|
//----------------
|
||||||
//Интерфейс модуля
|
//Интерфейс модуля
|
||||||
//----------------
|
//----------------
|
||||||
|
|
||||||
export { DATA_TYPE, DATA_TYPE_ICON, NODE_TYPE };
|
export { DATA_TYPE, DATA_TYPE_ICON, NODE_TYPE, ENTITY_TYPE, ENTITY_TYPE_ICON };
|
||||||
|
|||||||
@ -1,33 +0,0 @@
|
|||||||
.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;
|
|
||||||
}
|
|
||||||
@ -9,47 +9,136 @@
|
|||||||
|
|
||||||
import React from "react"; //Классы React
|
import React from "react"; //Классы React
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import "./entity.css"; //Стили компомнента
|
import { Box, Stack, ListItem, Icon, ListItemButton, Typography } from "@mui/material"; //Компоненты UI
|
||||||
|
import { ENTITY_TYPE, ENTITY_TYPE_ICON } from "../../common"; //Общие ресурсы и константы редактора запросов
|
||||||
import { ATTRIBUTE_SHAPE } from "../attribute/attribute"; //Описание атрибута сущности
|
import { ATTRIBUTE_SHAPE } from "../attribute/attribute"; //Описание атрибута сущности
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
//Константы
|
//Константы
|
||||||
//---------
|
//---------
|
||||||
|
|
||||||
|
//Варианты представления сущности
|
||||||
|
const ENTITY_VARIANT = {
|
||||||
|
LIST_ITEM: "LIST_ITEM",
|
||||||
|
DIAGRAM: "DIAGRAM"
|
||||||
|
};
|
||||||
|
|
||||||
|
//Иконки
|
||||||
|
const ICONS = { ...ENTITY_TYPE_ICON, DEFAULT: "border_clear" };
|
||||||
|
|
||||||
//Структура данных о сущности запроса
|
//Структура данных о сущности запроса
|
||||||
const ENTITY_SHAPE = PropTypes.shape({
|
const ENTITY_SHAPE = PropTypes.shape({
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.string.isRequired,
|
||||||
|
type: PropTypes.oneOf(Object.values(ENTITY_TYPE)).isRequired,
|
||||||
x: PropTypes.number.isRequired,
|
x: PropTypes.number.isRequired,
|
||||||
y: PropTypes.number.isRequired,
|
y: PropTypes.number.isRequired,
|
||||||
attrs: PropTypes.arrayOf(ATTRIBUTE_SHAPE).isRequired
|
attrs: PropTypes.arrayOf(ATTRIBUTE_SHAPE).isRequired
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//Структура данных о сущности запроса (краткая)
|
||||||
|
const ENTITY_BRIEF_SHAPE = PropTypes.shape({
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
title: PropTypes.string.isRequired,
|
||||||
|
type: PropTypes.oneOf(Object.values(ENTITY_TYPE)).isRequired
|
||||||
|
});
|
||||||
|
|
||||||
|
//Стили
|
||||||
|
const STYLES = {
|
||||||
|
CONTAINER: selected => ({
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
border: "1px solid var(--border-color-dark)",
|
||||||
|
borderRadius: "6px",
|
||||||
|
boxShadow: "var(--shadow-entity)",
|
||||||
|
overflow: "hidden",
|
||||||
|
backgroundColor: "white",
|
||||||
|
cursor: "move",
|
||||||
|
...(selected
|
||||||
|
? {
|
||||||
|
outline: "1px solid var(--outline-color)",
|
||||||
|
borderColor: "var(--outline-color)"
|
||||||
|
}
|
||||||
|
: {})
|
||||||
|
}),
|
||||||
|
CONTENT_STACK: variant => ({
|
||||||
|
width: "100%",
|
||||||
|
backgroundColor: "var(--entity-title-bg)",
|
||||||
|
...(variant === ENTITY_VARIANT.DIAGRAM ? { height: "50px" } : {})
|
||||||
|
}),
|
||||||
|
CAPTIONS_TYPOGRAPHY: variant => ({ ...(variant === ENTITY_VARIANT.DIAGRAM ? { maxWidth: "100px", overflow: "hidden" } : {}) })
|
||||||
|
};
|
||||||
|
|
||||||
//-----------
|
//-----------
|
||||||
//Тело модуля
|
//Тело модуля
|
||||||
//-----------
|
//-----------
|
||||||
|
|
||||||
//Сущность запроса
|
//Сущность запроса
|
||||||
const Entity = ({ data, selected = false }) => {
|
const Entity = ({ data, variant = ENTITY_VARIANT.DIAGRAM, selected = false, onClick = null }) => {
|
||||||
return (
|
//Иконка
|
||||||
<div className="entity__wrapper" data-selected={selected}>
|
const icon = ICONS[data.type] || ICONS.DEFAULT;
|
||||||
<div className="entity__title">
|
|
||||||
<span>{data.title}</span>
|
//Всплывающая подсказка
|
||||||
<div className="entity__name">{data.name}</div>
|
const iconTitle = data.type === ENTITY_TYPE.VIEW ? "Представление" : "Таблица";
|
||||||
</div>
|
|
||||||
</div>
|
//Содержимое самой сущности
|
||||||
|
const entContent = (
|
||||||
|
<Stack
|
||||||
|
direction={"row"}
|
||||||
|
alignItems={"center"}
|
||||||
|
justifyContent={variant === ENTITY_VARIANT.DIAGRAM ? "center" : "left"}
|
||||||
|
p={1}
|
||||||
|
sx={STYLES.CONTENT_STACK(variant)}
|
||||||
|
>
|
||||||
|
<Icon color={"action"} title={iconTitle}>
|
||||||
|
{icon}
|
||||||
|
</Icon>
|
||||||
|
<Stack direction={"column"} alignItems={"left"} pl={1}>
|
||||||
|
<Typography
|
||||||
|
variant={variant === ENTITY_VARIANT.DIAGRAM ? "subtitle2" : "body2"}
|
||||||
|
noWrap={variant === ENTITY_VARIANT.DIAGRAM ? true : false}
|
||||||
|
title={data.title}
|
||||||
|
sx={STYLES.CAPTIONS_TYPOGRAPHY(variant)}
|
||||||
|
>
|
||||||
|
{data.title}
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
component={"div"}
|
||||||
|
variant={"caption"}
|
||||||
|
color={"text.secondary"}
|
||||||
|
noWrap={variant === ENTITY_VARIANT.DIAGRAM ? true : false}
|
||||||
|
title={data.name}
|
||||||
|
sx={STYLES.CAPTIONS_TYPOGRAPHY(variant)}
|
||||||
|
>
|
||||||
|
{data.name}
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
//Формирование представления
|
||||||
|
return variant == ENTITY_VARIANT.LIST_ITEM ? (
|
||||||
|
<ListItem disablePadding>
|
||||||
|
<ListItemButton onClick={() => onClick && onClick(data)} dense>
|
||||||
|
{entContent}
|
||||||
|
</ListItemButton>
|
||||||
|
</ListItem>
|
||||||
|
) : variant == ENTITY_VARIANT.DIAGRAM ? (
|
||||||
|
<Box sx={STYLES.CONTAINER(selected)}>{entContent}</Box>
|
||||||
|
) : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
//Контроль свойств компонента - Сущность запроса
|
//Контроль свойств компонента - Сущность запроса
|
||||||
Entity.propTypes = {
|
Entity.propTypes = {
|
||||||
data: ENTITY_SHAPE.isRequired,
|
data: PropTypes.oneOfType([ENTITY_SHAPE, ENTITY_BRIEF_SHAPE]).isRequired,
|
||||||
selected: PropTypes.bool
|
variant: PropTypes.string,
|
||||||
|
selected: PropTypes.bool,
|
||||||
|
onClick: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
//----------------
|
//----------------
|
||||||
//Интерфейс модуля
|
//Интерфейс модуля
|
||||||
//----------------
|
//----------------
|
||||||
|
|
||||||
export { Entity, ENTITY_SHAPE };
|
export { Entity, ENTITY_VARIANT, ENTITY_SHAPE, ENTITY_BRIEF_SHAPE };
|
||||||
|
|||||||
@ -7,33 +7,103 @@
|
|||||||
//Подключение библиотек
|
//Подключение библиотек
|
||||||
//---------------------
|
//---------------------
|
||||||
|
|
||||||
import React from "react"; //Классы React
|
import React, { useState } from "react"; //Классы React
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import { P8PDialog } from "../../../../components/p8p_dialog"; //Типовой диалог
|
import { Box, TextField, InputAdornment, Icon, IconButton } from "@mui/material"; //Интерфейсные элементы MUI
|
||||||
|
import { P8PDialog, P8P_DIALOG_WIDTH } from "../../../../components/p8p_dialog"; //Типовой диалог
|
||||||
|
import { P8PComponentInlineMessage } from "../../../../components/editors/p8p_component_inline_message"; //Типовое встраиваемое сообщение
|
||||||
import { TITLES } from "../../../../../app.text"; //Общие текстовые ресурсы приложения
|
import { TITLES } from "../../../../../app.text"; //Общие текстовые ресурсы приложения
|
||||||
|
import { EntsList } from "./ents_list"; //Список сущостей
|
||||||
|
import { useEntities, useDebounce } from "./hooks"; //Хуки для работы с сущностями
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Максимальная длина поисковой фразы для начала поиска (символов)
|
||||||
|
const FILTER_MIN_LENGTH = 3;
|
||||||
|
|
||||||
|
//Таймаут ожидания ввода поисковой фразы до начала поиска на сервере (мс)
|
||||||
|
const FILTER_TIMEOUT = 1000;
|
||||||
|
|
||||||
|
//Стили
|
||||||
|
const STYLES = {
|
||||||
|
LIST_CONTAINER: { width: "100%", height: "500px" }
|
||||||
|
};
|
||||||
|
|
||||||
//-----------
|
//-----------
|
||||||
//Тело модуля
|
//Тело модуля
|
||||||
//-----------
|
//-----------
|
||||||
|
|
||||||
//Диалог добавления сущности запроса
|
//Диалог добавления сущности запроса
|
||||||
const EntityAddDialog = ({ onOk, onCancel }) => {
|
const EntityAddDialog = ({ onSelect, onClose }) => {
|
||||||
//Нажатие на кнопку "Ok"
|
//Собственное состояние - фильтр сущностей
|
||||||
const handleOk = values => onOk && onOk({ ...values });
|
const [filter, setFilter] = useState("");
|
||||||
|
|
||||||
//Нажатие на кнопку "Отмена"
|
//Значение для отложенного поиска
|
||||||
const handleCancel = () => onCancel && onCancel();
|
const debouncedFilter = useDebounce(filter, FILTER_TIMEOUT);
|
||||||
|
|
||||||
|
//Доступные сущности
|
||||||
|
const [ents] = useEntities(debouncedFilter, FILTER_MIN_LENGTH);
|
||||||
|
|
||||||
|
//При выборе сущности в списке
|
||||||
|
const handleSelect = ent => onSelect && onSelect({ ...ent });
|
||||||
|
|
||||||
|
//Нажатие на кнопку "Закыть"
|
||||||
|
const handleClose = () => onClose && onClose();
|
||||||
|
|
||||||
|
//При изменении значения фильтра
|
||||||
|
const handleFilterChange = e => setFilter(e.target.value);
|
||||||
|
|
||||||
|
//При очистке фильтра
|
||||||
|
const handleFilterClear = () => setFilter("");
|
||||||
|
|
||||||
//Генерация содержимого
|
//Генерация содержимого
|
||||||
return (
|
return (
|
||||||
<P8PDialog title={`${TITLES.INSERT} сущности`} inputs={[{ name: "name", value: "", label: "Имя" }]} onOk={handleOk} onCancel={handleCancel} />
|
<P8PDialog title={`${TITLES.INSERT} сущности`} width={P8P_DIALOG_WIDTH.SM} fullWidth onClose={handleClose}>
|
||||||
|
<TextField
|
||||||
|
margin={"normal"}
|
||||||
|
variant={"standard"}
|
||||||
|
fullWidth
|
||||||
|
placeholder={"Поиск сущности..."}
|
||||||
|
value={filter}
|
||||||
|
onChange={handleFilterChange}
|
||||||
|
InputProps={{
|
||||||
|
startAdornment: (
|
||||||
|
<InputAdornment position={"start"}>
|
||||||
|
<Icon>search</Icon>
|
||||||
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
endAdornment: (
|
||||||
|
<InputAdornment position={"end"}>
|
||||||
|
<IconButton onClick={handleFilterClear}>
|
||||||
|
<Icon>clear</Icon>
|
||||||
|
</IconButton>
|
||||||
|
</InputAdornment>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Box sx={STYLES.LIST_CONTAINER} justifyContent={"center"} alignItems={"center"} display={"flex"}>
|
||||||
|
{ents && <EntsList ents={ents} onSelect={handleSelect} />}
|
||||||
|
{!ents && (
|
||||||
|
<P8PComponentInlineMessage
|
||||||
|
name={"Нет данных, соответствующих фильтру"}
|
||||||
|
message={
|
||||||
|
filter || String(filter).length > FILTER_MIN_LENGTH
|
||||||
|
? "Измените значение фильтра для повторного поиска"
|
||||||
|
: `Начните ввод в строку поиска (не менее ${FILTER_MIN_LENGTH} символов)`
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</P8PDialog>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
//Контроль свойств - Диалог добавления сущности запроса
|
//Контроль свойств - Диалог добавления сущности запроса
|
||||||
EntityAddDialog.propTypes = {
|
EntityAddDialog.propTypes = {
|
||||||
onOk: PropTypes.func,
|
onSelect: PropTypes.func,
|
||||||
onCancel: PropTypes.func
|
onClose: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
//----------------
|
//----------------
|
||||||
|
|||||||
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - Редактор запросов
|
||||||
|
Компонент: Список сущностей
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React from "react"; //Классы React
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
import { List } from "@mui/material"; //Интерфейсные компоненты MUI
|
||||||
|
import { APP_STYLES } from "../../../../../app.styles"; //Общие стили приложения
|
||||||
|
import { Entity, ENTITY_VARIANT, ENTITY_BRIEF_SHAPE } from "../entity/entity"; //Сущности
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Стили
|
||||||
|
const STYLES = {
|
||||||
|
LIST: { height: "100%", width: "100%", bgcolor: "background.paper", overflowY: "auto", ...APP_STYLES.SCROLL }
|
||||||
|
};
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Список сущностей
|
||||||
|
const EntsList = ({ ents = [], onSelect = null } = {}) => {
|
||||||
|
//При нажатии на элемент списка
|
||||||
|
const handleItemClick = ent => onSelect && onSelect(ent);
|
||||||
|
|
||||||
|
//Формирование представления
|
||||||
|
return (
|
||||||
|
<List sx={STYLES.LIST}>
|
||||||
|
{ents && ents.map((ent, i) => <Entity key={i} data={ent} variant={ENTITY_VARIANT.LIST_ITEM} onClick={handleItemClick} />)}
|
||||||
|
</List>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств компонента - Список сущностей
|
||||||
|
EntsList.propTypes = {
|
||||||
|
ents: PropTypes.arrayOf(ENTITY_BRIEF_SHAPE),
|
||||||
|
onSelect: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { EntsList };
|
||||||
@ -7,7 +7,7 @@
|
|||||||
//Подключение библиотек
|
//Подключение библиотек
|
||||||
//---------------------
|
//---------------------
|
||||||
|
|
||||||
import { useContext, useCallback, useEffect, useState } from "react"; //Классы React
|
import { useRef, useContext, useCallback, useEffect, useState } from "react"; //Классы React
|
||||||
import { BackEndСtx } from "../../../../context/backend"; //Контекст взаимодействия с сервером
|
import { BackEndСtx } from "../../../../context/backend"; //Контекст взаимодействия с сервером
|
||||||
import { object2Base64XML } from "../../../../core/utils"; //Вспомогательные функции
|
import { object2Base64XML } from "../../../../core/utils"; //Вспомогательные функции
|
||||||
|
|
||||||
@ -15,6 +15,69 @@ import { object2Base64XML } from "../../../../core/utils"; //Вспомогат
|
|||||||
//Тело модуля
|
//Тело модуля
|
||||||
//-----------
|
//-----------
|
||||||
|
|
||||||
|
//Работа с сущностями системы
|
||||||
|
const useEntities = (filter, minFilterLen) => {
|
||||||
|
//Контроллер для прерывания запросов
|
||||||
|
const abortController = useRef(null);
|
||||||
|
|
||||||
|
//Собственное состояние - флаг загрузки
|
||||||
|
const [isLoading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
//Собственное состояние - флаг необходимости обновления
|
||||||
|
const [refresh, setRefresh] = useState(true);
|
||||||
|
|
||||||
|
//Собственное состояние - данные
|
||||||
|
const [data, setData] = useState(null);
|
||||||
|
|
||||||
|
//Подключение к контексту взаимодействия с сервером
|
||||||
|
const { executeStored } = useContext(BackEndСtx);
|
||||||
|
|
||||||
|
//Обновление данных
|
||||||
|
const doRefresh = () => setRefresh(true);
|
||||||
|
|
||||||
|
//При необходимости получить/обновить данные
|
||||||
|
useEffect(() => {
|
||||||
|
//Загрузка данных с сервера
|
||||||
|
const loadData = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
abortController.current?.abort?.();
|
||||||
|
abortController.current = new AbortController();
|
||||||
|
const data = await executeStored({
|
||||||
|
stored: "PKG_P8PANELS_QE.ENTITY_LIST",
|
||||||
|
args: { SSEARCH: filter },
|
||||||
|
respArg: "COUT",
|
||||||
|
isArray: name => ["XENT"].includes(name),
|
||||||
|
attributeValueProcessor: (name, val) => (["name", "title"].includes(name) ? undefined : val),
|
||||||
|
loader: false,
|
||||||
|
signal: abortController.current.signal
|
||||||
|
});
|
||||||
|
setData(data?.XENTS?.XENT || null);
|
||||||
|
} finally {
|
||||||
|
setRefresh(false);
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//Если надо обновить
|
||||||
|
if (refresh) {
|
||||||
|
//Если фильтр задан и он нас удовлетворяет - получим данные
|
||||||
|
if (filter && String(filter).length >= minFilterLen) loadData();
|
||||||
|
//Нет фильтра - нет данных
|
||||||
|
else {
|
||||||
|
setData(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Сбрасываем запрос при смене зависимостей или отмонтировании
|
||||||
|
return () => abortController.current?.abort?.();
|
||||||
|
}, [refresh, filter, minFilterLen, executeStored]);
|
||||||
|
|
||||||
|
//При изменении входных свойств - поднимаем флаг обновления
|
||||||
|
useEffect(() => setRefresh(true), [filter, minFilterLen]);
|
||||||
|
|
||||||
|
//Возвращаем интерфейс хука
|
||||||
|
return [data, doRefresh, isLoading];
|
||||||
|
};
|
||||||
|
|
||||||
//Работа с сущностями запроса
|
//Работа с сущностями запроса
|
||||||
const useQueryEntities = query => {
|
const useQueryEntities = query => {
|
||||||
//Подключение к контексту взаимодействия с сервером
|
//Подключение к контексту взаимодействия с сервером
|
||||||
@ -114,8 +177,27 @@ const useEntityAttrs = (query, entity) => {
|
|||||||
return [data, setAttrs, doRefresh, isLoading];
|
return [data, setAttrs, doRefresh, isLoading];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//Отложенное значение
|
||||||
|
const useDebounce = (value, delay) => {
|
||||||
|
//Собственное состояние
|
||||||
|
const [debouncedValue, setDebouncedValue] = useState(value);
|
||||||
|
|
||||||
|
//При изменении интервала или значения
|
||||||
|
useEffect(() => {
|
||||||
|
//Взводим таймер
|
||||||
|
const handler = setTimeout(() => {
|
||||||
|
setDebouncedValue(value);
|
||||||
|
}, delay);
|
||||||
|
//Сбрасываем таймер при отмонтировании или изменении зависимостей
|
||||||
|
return () => clearTimeout(handler);
|
||||||
|
}, [value, delay]);
|
||||||
|
|
||||||
|
//Возвращаем отложенное значение
|
||||||
|
return debouncedValue;
|
||||||
|
};
|
||||||
|
|
||||||
//----------------
|
//----------------
|
||||||
//Интерфейс модуля
|
//Интерфейс модуля
|
||||||
//----------------
|
//----------------
|
||||||
|
|
||||||
export { useQueryEntities, useEntityAttrs };
|
export { useEntities, useQueryEntities, useEntityAttrs, useDebounce };
|
||||||
|
|||||||
@ -54,12 +54,12 @@ const InspectorQueryEntities = ({ query, entity, onOptionsChanged }) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//Закрытие диалога добавления сущности по "Отмена"
|
//Закрытие диалога добавления сущности по "Закрыть"
|
||||||
const handleEntityAddDialogCancel = () => setOpenEntityAddDialog(false);
|
const handleEntityAddDialogClose = () => setOpenEntityAddDialog(false);
|
||||||
|
|
||||||
//Закрытие диалога добавления сущности по "ОК"
|
//Закрытие диалога добавления сущности по выбору сущности из списка
|
||||||
const handleEntityAddDialogOk = async values => {
|
const handleEntityAddDialogSelect = async ent => {
|
||||||
await addEnt(values.name, "VIEW");
|
await addEnt(ent.name, ent.type);
|
||||||
setOpenEntityAddDialog(false);
|
setOpenEntityAddDialog(false);
|
||||||
notifyOptionsChanged();
|
notifyOptionsChanged();
|
||||||
};
|
};
|
||||||
@ -76,7 +76,7 @@ const InspectorQueryEntities = ({ query, entity, onOptionsChanged }) => {
|
|||||||
//Формирование представления
|
//Формирование представления
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{openEntityAddDialog && <EntityAddDialog onOk={handleEntityAddDialogOk} onCancel={handleEntityAddDialogCancel} />}
|
{openEntityAddDialog && <EntityAddDialog onSelect={handleEntityAddDialogSelect} onClose={handleEntityAddDialogClose} />}
|
||||||
{openEntityAttrsDialog && (
|
{openEntityAttrsDialog && (
|
||||||
<EntityAttrsDialog query={query} {...entity} onOk={handleEntityAttrsDialogOk} onCancel={handleEntityAttrsDialogCancel} />
|
<EntityAttrsDialog query={query} {...entity} onOk={handleEntityAttrsDialogOk} onCancel={handleEntityAttrsDialogCancel} />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
create or replace package PKG_P8PANELS_QE as
|
create or replace package PKG_P8PANELS_QE as
|
||||||
|
|
||||||
|
/* Получение списка доступных сущностей */
|
||||||
|
procedure ENTITY_LIST
|
||||||
|
(
|
||||||
|
SSEARCH in varchar2, -- Поисковый запрос
|
||||||
|
COUT out clob -- Сериализованный список доступных сущностей
|
||||||
|
);
|
||||||
|
|
||||||
/* Получение данных о запросе */
|
/* Получение данных о запросе */
|
||||||
procedure QUERY
|
procedure QUERY
|
||||||
(
|
(
|
||||||
@ -142,6 +149,24 @@ end PKG_P8PANELS_QE;
|
|||||||
/
|
/
|
||||||
create or replace package body PKG_P8PANELS_QE as
|
create or replace package body PKG_P8PANELS_QE as
|
||||||
|
|
||||||
|
/* Константы - Минимальная длина поисковой фразы для сущностей */
|
||||||
|
NENT_MIN_SEARCH_LEN constant PKG_STD.TNUMBER := 3; -- Количество символов
|
||||||
|
|
||||||
|
/* Получение списка доступных сущностей */
|
||||||
|
procedure ENTITY_LIST
|
||||||
|
(
|
||||||
|
SSEARCH in varchar2, -- Поисковый запрос
|
||||||
|
COUT out clob -- Сериализованный список доступных сущностей
|
||||||
|
)
|
||||||
|
is
|
||||||
|
begin
|
||||||
|
/* Если фильтр задан и он нас утраивает */
|
||||||
|
if ((SSEARCH is not null) and (LENGTH(SSEARCH) >= NENT_MIN_SEARCH_LEN)) then
|
||||||
|
/* Получим список доступных сущностей */
|
||||||
|
COUT := PKG_P8PANELS_QE_BASE.ENTITY_LIST(SSEARCH => SSEARCH, NBRIEF => 1, NINCLUDE_ATTRS => 0);
|
||||||
|
end if;
|
||||||
|
end ENTITY_LIST;
|
||||||
|
|
||||||
/* Получение описания запроса */
|
/* Получение описания запроса */
|
||||||
procedure QUERY
|
procedure QUERY
|
||||||
(
|
(
|
||||||
|
|||||||
@ -62,6 +62,12 @@ create or replace package PKG_P8PANELS_QE_BASE as
|
|||||||
/* Типы данных - Коллекция отношений */
|
/* Типы данных - Коллекция отношений */
|
||||||
type TRLS is table of TRL;
|
type TRLS is table of TRL;
|
||||||
|
|
||||||
|
/* Получение заголовка представления из метаданных */
|
||||||
|
function DMSCLVIEWS_TITLE_GET
|
||||||
|
(
|
||||||
|
SVIEW_NAME in varchar2 -- Имя представления
|
||||||
|
) return varchar2; -- Заголовок представления из метаданных
|
||||||
|
|
||||||
/* Десериализация коллекции атрибутов сущности (из BASE64) */
|
/* Десериализация коллекции атрибутов сущности (из BASE64) */
|
||||||
function TATTRS_FROM_XML_BASE64
|
function TATTRS_FROM_XML_BASE64
|
||||||
(
|
(
|
||||||
@ -117,7 +123,8 @@ create or replace package PKG_P8PANELS_QE_BASE as
|
|||||||
(
|
(
|
||||||
RENTS in out nocopy TENTS, -- Изменяемая коллекция
|
RENTS in out nocopy TENTS, -- Изменяемая коллекция
|
||||||
SNAME in varchar2, -- Имя
|
SNAME in varchar2, -- Имя
|
||||||
STYPE in varchar2 -- Тип (см. константы SENT_TYPE_*)
|
STYPE in varchar2, -- Тип (см. константы SENT_TYPE_*)
|
||||||
|
NINIT_ATTRS in number := 1 -- Флаг инициализации атрибутов сущности (0 - нет, 1 - да)
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Удаление сущности из коллекции */
|
/* Удаление сущности из коллекции */
|
||||||
@ -158,6 +165,14 @@ create or replace package PKG_P8PANELS_QE_BASE as
|
|||||||
SATTR_ID in varchar2 -- Идентификатор атрибута
|
SATTR_ID in varchar2 -- Идентификатор атрибута
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/* Формирование списка доступных сущностей */
|
||||||
|
function ENTITY_LIST
|
||||||
|
(
|
||||||
|
SSEARCH in varchar2, -- Поисковый запрос
|
||||||
|
NBRIEF in number, -- Флаг формирования описания сущности в кратком формате (0 - нет, 1 - да)
|
||||||
|
NINCLUDE_ATTRS in number -- Флаг включения в ответ атрибутов сущности (0 - нет, 1 - да)
|
||||||
|
) return clob; -- Список сущностей
|
||||||
|
|
||||||
/* Считывание записи запроса */
|
/* Считывание записи запроса */
|
||||||
function QUERY_GET
|
function QUERY_GET
|
||||||
(
|
(
|
||||||
@ -852,7 +867,7 @@ create or replace package body PKG_P8PANELS_QE_BASE as
|
|||||||
return null;
|
return null;
|
||||||
end TATTRS_INDEX_BY_NAME;
|
end TATTRS_INDEX_BY_NAME;
|
||||||
|
|
||||||
/* Формирование списка атрибутов сущности по типу и наименованию */
|
/* Формирование списка атрибутов сущности */
|
||||||
function TATTRS_MAKE
|
function TATTRS_MAKE
|
||||||
(
|
(
|
||||||
RENT in TENT, -- Родительская сущность
|
RENT in TENT, -- Родительская сущность
|
||||||
@ -1055,7 +1070,8 @@ create or replace package body PKG_P8PANELS_QE_BASE as
|
|||||||
(
|
(
|
||||||
SNAME in varchar2, -- Имя
|
SNAME in varchar2, -- Имя
|
||||||
STYPE in varchar2, -- Тип (см. константы SENT_TYPE_*)
|
STYPE in varchar2, -- Тип (см. константы SENT_TYPE_*)
|
||||||
NNUMB in number -- Номер сущности
|
NNUMB in number, -- Номер сущности
|
||||||
|
NINIT_ATTRS in number := 1 -- Флаг инициализации атрибутов сущности (0 - нет, 1 - да)
|
||||||
) return TENT -- Описание сущности
|
) return TENT -- Описание сущности
|
||||||
is
|
is
|
||||||
RENT TENT; -- Буфер для результата
|
RENT TENT; -- Буфер для результата
|
||||||
@ -1080,7 +1096,11 @@ create or replace package body PKG_P8PANELS_QE_BASE as
|
|||||||
RENT.STITLE := DMSCLVIEWS_TITLE_GET(SVIEW_NAME => RENT.SNAME);
|
RENT.STITLE := DMSCLVIEWS_TITLE_GET(SVIEW_NAME => RENT.SNAME);
|
||||||
RENT.STYPE := SENT_TYPE_VIEW;
|
RENT.STYPE := SENT_TYPE_VIEW;
|
||||||
/* Формируем набор атрибутов */
|
/* Формируем набор атрибутов */
|
||||||
RENT.RATTRS := TATTRS_MAKE(RENT => RENT, NCOUNT => 10);
|
if (NINIT_ATTRS = 1) then
|
||||||
|
RENT.RATTRS := TATTRS_MAKE(RENT => RENT, NCOUNT => 10);
|
||||||
|
else
|
||||||
|
RENT.RATTRS := TATTRS();
|
||||||
|
end if;
|
||||||
end if;
|
end if;
|
||||||
/* Если сущность это таблица */
|
/* Если сущность это таблица */
|
||||||
if (STYPE = SENT_TYPE_TABLE) then
|
if (STYPE = SENT_TYPE_TABLE) then
|
||||||
@ -1094,21 +1114,27 @@ create or replace package body PKG_P8PANELS_QE_BASE as
|
|||||||
/* Сериализация сущности */
|
/* Сериализация сущности */
|
||||||
procedure TENT_TO_XML
|
procedure TENT_TO_XML
|
||||||
(
|
(
|
||||||
RENT in TENT -- Сущность
|
RENT in TENT, -- Сущность
|
||||||
|
NBRIEF in number := 0, -- Флаг формирования описания сущности в кратком формате (0 - нет, 1 - да)
|
||||||
|
NINCLUDE_ATTRS in number := 1 -- Флаг включения атрибутов в описание (0 - нет, 1 - да)
|
||||||
)
|
)
|
||||||
is
|
is
|
||||||
begin
|
begin
|
||||||
/* Открываем описание сущности */
|
/* Открываем описание сущности */
|
||||||
PKG_XFAST.DOWN_NODE(SNAME => STAG_ENT);
|
PKG_XFAST.DOWN_NODE(SNAME => STAG_ENT);
|
||||||
/* Cущность */
|
/* Cущность */
|
||||||
PKG_XFAST.ATTR(SNAME => SATTR_ID, SVALUE => RENT.SID);
|
if (NBRIEF = 0) then
|
||||||
|
PKG_XFAST.ATTR(SNAME => SATTR_ID, SVALUE => RENT.SID);
|
||||||
|
PKG_XFAST.ATTR(SNAME => SATTR_X, NVALUE => RENT.NX);
|
||||||
|
PKG_XFAST.ATTR(SNAME => SATTR_Y, NVALUE => RENT.NY);
|
||||||
|
end if;
|
||||||
PKG_XFAST.ATTR(SNAME => SATTR_NAME, SVALUE => RENT.SNAME);
|
PKG_XFAST.ATTR(SNAME => SATTR_NAME, SVALUE => RENT.SNAME);
|
||||||
PKG_XFAST.ATTR(SNAME => SATTR_TITLE, SVALUE => RENT.STITLE);
|
PKG_XFAST.ATTR(SNAME => SATTR_TITLE, SVALUE => RENT.STITLE);
|
||||||
PKG_XFAST.ATTR(SNAME => SATTR_TYPE, SVALUE => RENT.STYPE);
|
PKG_XFAST.ATTR(SNAME => SATTR_TYPE, SVALUE => RENT.STYPE);
|
||||||
PKG_XFAST.ATTR(SNAME => SATTR_X, NVALUE => RENT.NX);
|
|
||||||
PKG_XFAST.ATTR(SNAME => SATTR_Y, NVALUE => RENT.NY);
|
|
||||||
/* Атрибуты */
|
/* Атрибуты */
|
||||||
TATTRS_TO_XML(RATTRS => RENT.RATTRS);
|
if (NINCLUDE_ATTRS = 1) then
|
||||||
|
TATTRS_TO_XML(RATTRS => RENT.RATTRS);
|
||||||
|
end if;
|
||||||
/* Закрываем описание сущности */
|
/* Закрываем описание сущности */
|
||||||
PKG_XFAST.UP();
|
PKG_XFAST.UP();
|
||||||
end TENT_TO_XML;
|
end TENT_TO_XML;
|
||||||
@ -1209,7 +1235,8 @@ create or replace package body PKG_P8PANELS_QE_BASE as
|
|||||||
(
|
(
|
||||||
RENTS in out nocopy TENTS, -- Изменяемая коллекция
|
RENTS in out nocopy TENTS, -- Изменяемая коллекция
|
||||||
SNAME in varchar2, -- Имя
|
SNAME in varchar2, -- Имя
|
||||||
STYPE in varchar2 -- Тип (см. константы SENT_TYPE_*)
|
STYPE in varchar2, -- Тип (см. константы SENT_TYPE_*)
|
||||||
|
NINIT_ATTRS in number := 1 -- Флаг инициализации атрибутов сущности (0 - нет, 1 - да)
|
||||||
)
|
)
|
||||||
is
|
is
|
||||||
RENT TENT; -- Добавляемая сущность
|
RENT TENT; -- Добавляемая сущность
|
||||||
@ -1219,7 +1246,10 @@ create or replace package body PKG_P8PANELS_QE_BASE as
|
|||||||
RENTS := TENTS();
|
RENTS := TENTS();
|
||||||
end if;
|
end if;
|
||||||
/* Формируем пописание сущности */
|
/* Формируем пописание сущности */
|
||||||
RENT := TENT_MAKE(SNAME => SNAME, STYPE => STYPE, NNUMB => TENTS_NEXT_NUMB(RENTS => RENTS, SNAME => SNAME));
|
RENT := TENT_MAKE(SNAME => SNAME,
|
||||||
|
STYPE => STYPE,
|
||||||
|
NNUMB => TENTS_NEXT_NUMB(RENTS => RENTS, SNAME => SNAME),
|
||||||
|
NINIT_ATTRS => NINIT_ATTRS);
|
||||||
/* Добавляем её в коллекцию */
|
/* Добавляем её в коллекцию */
|
||||||
RENTS.EXTEND();
|
RENTS.EXTEND();
|
||||||
RENTS(RENTS.LAST) := RENT;
|
RENTS(RENTS.LAST) := RENT;
|
||||||
@ -1260,10 +1290,45 @@ create or replace package body PKG_P8PANELS_QE_BASE as
|
|||||||
end if;
|
end if;
|
||||||
end TENTS_POSITION_SET;
|
end TENTS_POSITION_SET;
|
||||||
|
|
||||||
|
/* Формирование списка сущностей */
|
||||||
|
function TENTS_MAKE
|
||||||
|
(
|
||||||
|
SSEARCH in varchar2, -- Поисковый запрос
|
||||||
|
NINIT_ATTRS in number -- Флаг инициализации атрибутов сущности (0 - нет, 1 - да)
|
||||||
|
) return TENTS -- Коллекция сущностей
|
||||||
|
is
|
||||||
|
RRES TENTS; -- Буфер для результата
|
||||||
|
SSEARCH_ PKG_STD.TSTRING; -- Строка поиска, предобразованная для применения в запросе
|
||||||
|
begin
|
||||||
|
/* Инициализируем результат */
|
||||||
|
RRES := TENTS();
|
||||||
|
/* Формируем список представлений */
|
||||||
|
PKG_OBJECT_DESC.SNAP_VIEWS(NACCESS_TYPE => 1);
|
||||||
|
/* Подготовим поисковую фразу */
|
||||||
|
if (SSEARCH is not null) then
|
||||||
|
SSEARCH_ := '%' || LOWER(replace(SSEARCH, ' ', '%')) || '%';
|
||||||
|
else
|
||||||
|
SSEARCH_ := null;
|
||||||
|
end if;
|
||||||
|
/* Обходим полученный список */
|
||||||
|
for C in (select T.VIEW_NAME
|
||||||
|
from SNAP_VIEWS T
|
||||||
|
where ((SSEARCH_ is null) or ((SSEARCH_ is not null) and ((LOWER(T.VIEW_NAME) like SSEARCH_) or (LOWER(DMSCLVIEWS_TITLE_GET(T.VIEW_NAME)) like
|
||||||
|
SSEARCH_)))))
|
||||||
|
loop
|
||||||
|
/* Добавляем представления в коллекцию */
|
||||||
|
TENTS_ADD(RENTS => RRES, SNAME => C.VIEW_NAME, STYPE => SENT_TYPE_VIEW, NINIT_ATTRS => NINIT_ATTRS);
|
||||||
|
end loop;
|
||||||
|
/* Вернём полученный список */
|
||||||
|
return RRES;
|
||||||
|
end TENTS_MAKE;
|
||||||
|
|
||||||
/* Сериализация коллекции сущностей */
|
/* Сериализация коллекции сущностей */
|
||||||
procedure TENTS_TO_XML
|
procedure TENTS_TO_XML
|
||||||
(
|
(
|
||||||
RENTS in TENTS -- Коллекция сущностей
|
RENTS in TENTS, -- Коллекция сущностей
|
||||||
|
NBRIEF in number := 0, -- Флаг формирования описания сущности в кратком формате (0 - нет, 1 - да)
|
||||||
|
NINCLUDE_ATTRS in number := 1 -- Флаг включения атрибутов в описание (0 - нет, 1 - да)
|
||||||
)
|
)
|
||||||
is
|
is
|
||||||
begin
|
begin
|
||||||
@ -1275,7 +1340,7 @@ create or replace package body PKG_P8PANELS_QE_BASE as
|
|||||||
loop
|
loop
|
||||||
begin
|
begin
|
||||||
/* Добавляем описание сущности */
|
/* Добавляем описание сущности */
|
||||||
TENT_TO_XML(RENT => RENTS(I));
|
TENT_TO_XML(RENT => RENTS(I), NBRIEF => NBRIEF, NINCLUDE_ATTRS => NINCLUDE_ATTRS);
|
||||||
exception
|
exception
|
||||||
when NO_DATA_FOUND then
|
when NO_DATA_FOUND then
|
||||||
null;
|
null;
|
||||||
@ -1620,6 +1685,41 @@ create or replace package body PKG_P8PANELS_QE_BASE as
|
|||||||
return RRES;
|
return RRES;
|
||||||
end TRLS_FROM_XML;
|
end TRLS_FROM_XML;
|
||||||
|
|
||||||
|
/* Формирование списка доступных сущностей */
|
||||||
|
function ENTITY_LIST
|
||||||
|
(
|
||||||
|
SSEARCH in varchar2, -- Поисковый запрос
|
||||||
|
NBRIEF in number, -- Флаг формирования описания сущности в кратком формате (0 - нет, 1 - да)
|
||||||
|
NINCLUDE_ATTRS in number -- Флаг включения в ответ атрибутов сущности (0 - нет, 1 - да)
|
||||||
|
) return clob -- Список сущностей
|
||||||
|
is
|
||||||
|
CRES clob; -- Буфер для сериализации
|
||||||
|
begin
|
||||||
|
/* Начинаем формирование XML */
|
||||||
|
PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_, BALINE => true, BINDENT => true);
|
||||||
|
/* Открываем корень */
|
||||||
|
PKG_XFAST.DOWN_NODE(SNAME => STAG_DATA);
|
||||||
|
/* Строим и сериализуем список доступных сущностей */
|
||||||
|
TENTS_TO_XML(RENTS => TENTS_MAKE(SSEARCH => SSEARCH, NINIT_ATTRS => NINCLUDE_ATTRS),
|
||||||
|
NBRIEF => NBRIEF,
|
||||||
|
NINCLUDE_ATTRS => NINCLUDE_ATTRS);
|
||||||
|
/* Закрываем корень */
|
||||||
|
PKG_XFAST.UP();
|
||||||
|
/* Сериализуем */
|
||||||
|
CRES := PKG_XFAST.SERIALIZE_TO_CLOB();
|
||||||
|
/* Завершаем формирование XML */
|
||||||
|
PKG_XFAST.EPILOGUE();
|
||||||
|
/* Возвращаем результат */
|
||||||
|
return CRES;
|
||||||
|
exception
|
||||||
|
when others then
|
||||||
|
/* Завершаем формирование XML */
|
||||||
|
PKG_XFAST.EPILOGUE();
|
||||||
|
/* Вернем ошибку */
|
||||||
|
PKG_STATE.DIAGNOSTICS_STACKED();
|
||||||
|
P_EXCEPTION(0, PKG_STATE.SQL_ERRM());
|
||||||
|
end ENTITY_LIST;
|
||||||
|
|
||||||
/* Считывание записи запроса */
|
/* Считывание записи запроса */
|
||||||
function QUERY_GET
|
function QUERY_GET
|
||||||
(
|
(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user