ЦИТК-979 - Добавление сущности в запрос из списка с поиском по БД

This commit is contained in:
Mikhail Chechnev 2025-09-04 15:48:16 +03:00
parent f698bc1789
commit 538643591f
9 changed files with 474 additions and 77 deletions

View File

@ -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 };

View File

@ -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;
}

View File

@ -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 };

View File

@ -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
}; };
//---------------- //----------------

View File

@ -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 };

View File

@ -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 };

View File

@ -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} />
)} )}

View File

@ -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
( (

View File

@ -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
( (