forked from CITKParus/P8-Panels
ЦИТК-979 - Добавление сущности в запрос из списка с поиском по БД
This commit is contained in:
parent
f698bc1789
commit
538643591f
@ -23,8 +23,20 @@ const NODE_TYPE = {
|
||||
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 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"; //Описание атрибута сущности
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Варианты представления сущности
|
||||
const ENTITY_VARIANT = {
|
||||
LIST_ITEM: "LIST_ITEM",
|
||||
DIAGRAM: "DIAGRAM"
|
||||
};
|
||||
|
||||
//Иконки
|
||||
const ICONS = { ...ENTITY_TYPE_ICON, DEFAULT: "border_clear" };
|
||||
|
||||
//Структура данных о сущности запроса
|
||||
const ENTITY_SHAPE = PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
type: PropTypes.oneOf(Object.values(ENTITY_TYPE)).isRequired,
|
||||
x: PropTypes.number.isRequired,
|
||||
y: PropTypes.number.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 }) => {
|
||||
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>
|
||||
const Entity = ({ data, variant = ENTITY_VARIANT.DIAGRAM, selected = false, onClick = null }) => {
|
||||
//Иконка
|
||||
const icon = ICONS[data.type] || ICONS.DEFAULT;
|
||||
|
||||
//Всплывающая подсказка
|
||||
const iconTitle = data.type === ENTITY_TYPE.VIEW ? "Представление" : "Таблица";
|
||||
|
||||
//Содержимое самой сущности
|
||||
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 = {
|
||||
data: ENTITY_SHAPE.isRequired,
|
||||
selected: PropTypes.bool
|
||||
data: PropTypes.oneOfType([ENTITY_SHAPE, ENTITY_BRIEF_SHAPE]).isRequired,
|
||||
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 { 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 { 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 }) => {
|
||||
//Нажатие на кнопку "Ok"
|
||||
const handleOk = values => onOk && onOk({ ...values });
|
||||
const EntityAddDialog = ({ onSelect, onClose }) => {
|
||||
//Собственное состояние - фильтр сущностей
|
||||
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 (
|
||||
<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 = {
|
||||
onOk: PropTypes.func,
|
||||
onCancel: PropTypes.func
|
||||
onSelect: 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 { 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 => {
|
||||
//Подключение к контексту взаимодействия с сервером
|
||||
@ -114,8 +177,27 @@ const useEntityAttrs = (query, entity) => {
|
||||
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 => {
|
||||
await addEnt(values.name, "VIEW");
|
||||
//Закрытие диалога добавления сущности по выбору сущности из списка
|
||||
const handleEntityAddDialogSelect = async ent => {
|
||||
await addEnt(ent.name, ent.type);
|
||||
setOpenEntityAddDialog(false);
|
||||
notifyOptionsChanged();
|
||||
};
|
||||
@ -76,7 +76,7 @@ const InspectorQueryEntities = ({ query, entity, onOptionsChanged }) => {
|
||||
//Формирование представления
|
||||
return (
|
||||
<>
|
||||
{openEntityAddDialog && <EntityAddDialog onOk={handleEntityAddDialogOk} onCancel={handleEntityAddDialogCancel} />}
|
||||
{openEntityAddDialog && <EntityAddDialog onSelect={handleEntityAddDialogSelect} onClose={handleEntityAddDialogClose} />}
|
||||
{openEntityAttrsDialog && (
|
||||
<EntityAttrsDialog query={query} {...entity} onOk={handleEntityAttrsDialogOk} onCancel={handleEntityAttrsDialogCancel} />
|
||||
)}
|
||||
|
||||
@ -1,5 +1,12 @@
|
||||
create or replace package PKG_P8PANELS_QE as
|
||||
|
||||
/* Получение списка доступных сущностей */
|
||||
procedure ENTITY_LIST
|
||||
(
|
||||
SSEARCH in varchar2, -- Поисковый запрос
|
||||
COUT out clob -- Сериализованный список доступных сущностей
|
||||
);
|
||||
|
||||
/* Получение данных о запросе */
|
||||
procedure QUERY
|
||||
(
|
||||
@ -142,6 +149,24 @@ end PKG_P8PANELS_QE;
|
||||
/
|
||||
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
|
||||
(
|
||||
|
||||
@ -62,6 +62,12 @@ create or replace package PKG_P8PANELS_QE_BASE as
|
||||
/* Типы данных - Коллекция отношений */
|
||||
type TRLS is table of TRL;
|
||||
|
||||
/* Получение заголовка представления из метаданных */
|
||||
function DMSCLVIEWS_TITLE_GET
|
||||
(
|
||||
SVIEW_NAME in varchar2 -- Имя представления
|
||||
) return varchar2; -- Заголовок представления из метаданных
|
||||
|
||||
/* Десериализация коллекции атрибутов сущности (из BASE64) */
|
||||
function TATTRS_FROM_XML_BASE64
|
||||
(
|
||||
@ -117,7 +123,8 @@ create or replace package PKG_P8PANELS_QE_BASE as
|
||||
(
|
||||
RENTS in out nocopy TENTS, -- Изменяемая коллекция
|
||||
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 -- Идентификатор атрибута
|
||||
);
|
||||
|
||||
/* Формирование списка доступных сущностей */
|
||||
function ENTITY_LIST
|
||||
(
|
||||
SSEARCH in varchar2, -- Поисковый запрос
|
||||
NBRIEF in number, -- Флаг формирования описания сущности в кратком формате (0 - нет, 1 - да)
|
||||
NINCLUDE_ATTRS in number -- Флаг включения в ответ атрибутов сущности (0 - нет, 1 - да)
|
||||
) return clob; -- Список сущностей
|
||||
|
||||
/* Считывание записи запроса */
|
||||
function QUERY_GET
|
||||
(
|
||||
@ -852,7 +867,7 @@ create or replace package body PKG_P8PANELS_QE_BASE as
|
||||
return null;
|
||||
end TATTRS_INDEX_BY_NAME;
|
||||
|
||||
/* Формирование списка атрибутов сущности по типу и наименованию */
|
||||
/* Формирование списка атрибутов сущности */
|
||||
function TATTRS_MAKE
|
||||
(
|
||||
RENT in TENT, -- Родительская сущность
|
||||
@ -1055,7 +1070,8 @@ create or replace package body PKG_P8PANELS_QE_BASE as
|
||||
(
|
||||
SNAME in varchar2, -- Имя
|
||||
STYPE in varchar2, -- Тип (см. константы SENT_TYPE_*)
|
||||
NNUMB in number -- Номер сущности
|
||||
NNUMB in number, -- Номер сущности
|
||||
NINIT_ATTRS in number := 1 -- Флаг инициализации атрибутов сущности (0 - нет, 1 - да)
|
||||
) return TENT -- Описание сущности
|
||||
is
|
||||
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.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;
|
||||
/* Если сущность это таблица */
|
||||
if (STYPE = SENT_TYPE_TABLE) then
|
||||
@ -1094,21 +1114,27 @@ create or replace package body PKG_P8PANELS_QE_BASE as
|
||||
/* Сериализация сущности */
|
||||
procedure TENT_TO_XML
|
||||
(
|
||||
RENT in TENT -- Сущность
|
||||
RENT in TENT, -- Сущность
|
||||
NBRIEF in number := 0, -- Флаг формирования описания сущности в кратком формате (0 - нет, 1 - да)
|
||||
NINCLUDE_ATTRS in number := 1 -- Флаг включения атрибутов в описание (0 - нет, 1 - да)
|
||||
)
|
||||
is
|
||||
begin
|
||||
/* Открываем описание сущности */
|
||||
PKG_XFAST.DOWN_NODE(SNAME => STAG_ENT);
|
||||
/* 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_TITLE, SVALUE => RENT.STITLE);
|
||||
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();
|
||||
end TENT_TO_XML;
|
||||
@ -1209,7 +1235,8 @@ create or replace package body PKG_P8PANELS_QE_BASE as
|
||||
(
|
||||
RENTS in out nocopy TENTS, -- Изменяемая коллекция
|
||||
SNAME in varchar2, -- Имя
|
||||
STYPE in varchar2 -- Тип (см. константы SENT_TYPE_*)
|
||||
STYPE in varchar2, -- Тип (см. константы SENT_TYPE_*)
|
||||
NINIT_ATTRS in number := 1 -- Флаг инициализации атрибутов сущности (0 - нет, 1 - да)
|
||||
)
|
||||
is
|
||||
RENT TENT; -- Добавляемая сущность
|
||||
@ -1219,7 +1246,10 @@ create or replace package body PKG_P8PANELS_QE_BASE as
|
||||
RENTS := TENTS();
|
||||
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(RENTS.LAST) := RENT;
|
||||
@ -1260,10 +1290,45 @@ create or replace package body PKG_P8PANELS_QE_BASE as
|
||||
end if;
|
||||
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
|
||||
(
|
||||
RENTS in TENTS -- Коллекция сущностей
|
||||
RENTS in TENTS, -- Коллекция сущностей
|
||||
NBRIEF in number := 0, -- Флаг формирования описания сущности в кратком формате (0 - нет, 1 - да)
|
||||
NINCLUDE_ATTRS in number := 1 -- Флаг включения атрибутов в описание (0 - нет, 1 - да)
|
||||
)
|
||||
is
|
||||
begin
|
||||
@ -1275,7 +1340,7 @@ create or replace package body PKG_P8PANELS_QE_BASE as
|
||||
loop
|
||||
begin
|
||||
/* Добавляем описание сущности */
|
||||
TENT_TO_XML(RENT => RENTS(I));
|
||||
TENT_TO_XML(RENT => RENTS(I), NBRIEF => NBRIEF, NINCLUDE_ATTRS => NINCLUDE_ATTRS);
|
||||
exception
|
||||
when NO_DATA_FOUND then
|
||||
null;
|
||||
@ -1620,6 +1685,41 @@ create or replace package body PKG_P8PANELS_QE_BASE as
|
||||
return RRES;
|
||||
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
|
||||
(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user