From 538643591f003e51e4b6e607216af9d99d5bcbc6 Mon Sep 17 00:00:00 2001 From: Mikhail Chechnev Date: Thu, 4 Sep 2025 15:48:16 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A6=D0=98=D0=A2=D0=9A-979=20-=20=D0=94=D0=BE?= =?UTF-8?q?=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=81=D1=83?= =?UTF-8?q?=D1=89=D0=BD=D0=BE=D1=81=D1=82=D0=B8=20=D0=B2=20=D0=B7=D0=B0?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D1=81=20=D0=B8=D0=B7=20=D1=81=D0=BF=D0=B8?= =?UTF-8?q?=D1=81=D0=BA=D0=B0=20=D1=81=20=D0=BF=D0=BE=D0=B8=D1=81=D0=BA?= =?UTF-8?q?=D0=BE=D0=BC=20=D0=BF=D0=BE=20=D0=91=D0=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/panels/query_editor/common.js | 14 +- .../query_editor/components/entity/entity.css | 33 ----- .../query_editor/components/entity/entity.js | 113 ++++++++++++++-- .../inspector_query_ents/entity_add_dialog.js | 90 +++++++++++-- .../inspector_query_ents/ents_list.js | 52 ++++++++ .../components/inspector_query_ents/hooks.js | 86 +++++++++++- .../inspector_query_ents.js | 12 +- db/PKG_P8PANELS_QE.pck | 25 ++++ db/PKG_P8PANELS_QE_BASE.pck | 126 ++++++++++++++++-- 9 files changed, 474 insertions(+), 77 deletions(-) delete mode 100644 app/panels/query_editor/components/entity/entity.css create mode 100644 app/panels/query_editor/components/inspector_query_ents/ents_list.js diff --git a/app/panels/query_editor/common.js b/app/panels/query_editor/common.js index c198bb2..eb125f9 100644 --- a/app/panels/query_editor/common.js +++ b/app/panels/query_editor/common.js @@ -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 }; diff --git a/app/panels/query_editor/components/entity/entity.css b/app/panels/query_editor/components/entity/entity.css deleted file mode 100644 index 1f64803..0000000 --- a/app/panels/query_editor/components/entity/entity.css +++ /dev/null @@ -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; -} diff --git a/app/panels/query_editor/components/entity/entity.js b/app/panels/query_editor/components/entity/entity.js index b2503f5..6ccd2dc 100644 --- a/app/panels/query_editor/components/entity/entity.js +++ b/app/panels/query_editor/components/entity/entity.js @@ -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 ( -
-
- {data.title} -
{data.name}
-
-
+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 = ( + + + {icon} + + + + {data.title} + + + {data.name} + + + ); + + //Формирование представления + return variant == ENTITY_VARIANT.LIST_ITEM ? ( + + onClick && onClick(data)} dense> + {entContent} + + + ) : variant == ENTITY_VARIANT.DIAGRAM ? ( + {entContent} + ) : 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 }; diff --git a/app/panels/query_editor/components/inspector_query_ents/entity_add_dialog.js b/app/panels/query_editor/components/inspector_query_ents/entity_add_dialog.js index b67a589..54b05c4 100644 --- a/app/panels/query_editor/components/inspector_query_ents/entity_add_dialog.js +++ b/app/panels/query_editor/components/inspector_query_ents/entity_add_dialog.js @@ -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 ( - + + + search + + ), + endAdornment: ( + + + clear + + + ) + }} + /> + + {ents && } + {!ents && ( + FILTER_MIN_LENGTH + ? "Измените значение фильтра для повторного поиска" + : `Начните ввод в строку поиска (не менее ${FILTER_MIN_LENGTH} символов)` + } + /> + )} + + ); }; //Контроль свойств - Диалог добавления сущности запроса EntityAddDialog.propTypes = { - onOk: PropTypes.func, - onCancel: PropTypes.func + onSelect: PropTypes.func, + onClose: PropTypes.func }; //---------------- diff --git a/app/panels/query_editor/components/inspector_query_ents/ents_list.js b/app/panels/query_editor/components/inspector_query_ents/ents_list.js new file mode 100644 index 0000000..24dbe9d --- /dev/null +++ b/app/panels/query_editor/components/inspector_query_ents/ents_list.js @@ -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 ( + + {ents && ents.map((ent, i) => )} + + ); +}; + +//Контроль свойств компонента - Список сущностей +EntsList.propTypes = { + ents: PropTypes.arrayOf(ENTITY_BRIEF_SHAPE), + onSelect: PropTypes.func +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { EntsList }; diff --git a/app/panels/query_editor/components/inspector_query_ents/hooks.js b/app/panels/query_editor/components/inspector_query_ents/hooks.js index 3a8f03d..58d8a17 100644 --- a/app/panels/query_editor/components/inspector_query_ents/hooks.js +++ b/app/panels/query_editor/components/inspector_query_ents/hooks.js @@ -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 }; diff --git a/app/panels/query_editor/components/inspector_query_ents/inspector_query_ents.js b/app/panels/query_editor/components/inspector_query_ents/inspector_query_ents.js index 0e0cea4..ec8d23f 100644 --- a/app/panels/query_editor/components/inspector_query_ents/inspector_query_ents.js +++ b/app/panels/query_editor/components/inspector_query_ents/inspector_query_ents.js @@ -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 && } + {openEntityAddDialog && } {openEntityAttrsDialog && ( )} diff --git a/db/PKG_P8PANELS_QE.pck b/db/PKG_P8PANELS_QE.pck index f05edf2..d4a0fc3 100644 --- a/db/PKG_P8PANELS_QE.pck +++ b/db/PKG_P8PANELS_QE.pck @@ -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 ( diff --git a/db/PKG_P8PANELS_QE_BASE.pck b/db/PKG_P8PANELS_QE_BASE.pck index 7f2535c..514784e 100644 --- a/db/PKG_P8PANELS_QE_BASE.pck +++ b/db/PKG_P8PANELS_QE_BASE.pck @@ -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 (