ЦИТК-979 - Рефакторинг понятий Сущность, Атрибут, Отношение, Элемент диаграммы, Связь диаграммы

This commit is contained in:
Mikhail Chechnev 2025-09-03 15:16:12 +03:00
parent 86aa639ca0
commit 7515a9cce3
11 changed files with 159 additions and 130 deletions

View File

@ -51,9 +51,9 @@ const STYLES = {
const ICONS = { ...DATA_TYPE_ICON, DEFAULT: "category" };
//Структура данных об атрибуте сущности
const ATTRIBUTE_DATA_SHAPE = PropTypes.shape({
const ATTRIBUTE_SHAPE = PropTypes.shape({
id: PropTypes.string.isRequired,
parentEntity: PropTypes.string,
parentEntity: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
dataType: PropTypes.oneOf(Object.values(DATA_TYPE)),
@ -136,11 +136,11 @@ const Attribute = ({ data }) => {
//Контроль свойств компонента - Атрибут сущности
Attribute.propTypes = {
data: ATTRIBUTE_DATA_SHAPE
data: ATTRIBUTE_SHAPE
};
//----------------
//Интерфейс модуля
//----------------
export { Attribute, ATTRIBUTE_DATA_SHAPE, attrGetUse, attrGetShow };
export { Attribute, ATTRIBUTE_SHAPE, attrGetUse, attrGetShow };

View File

@ -10,16 +10,20 @@
import React from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента
import "./entity.css"; //Стили компомнента
import { ATTRIBUTE_SHAPE } from "../attribute/attribute"; //Описание атрибута сущности
//---------
//Константы
//---------
//Структура данных о сущности запроса
const ENTITY_DATA_SHAPE = PropTypes.shape({
const ENTITY_SHAPE = PropTypes.shape({
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
title: PropTypes.string.isRequired
title: PropTypes.string.isRequired,
x: PropTypes.number.isRequired,
y: PropTypes.number.isRequired,
attrs: PropTypes.arrayOf(ATTRIBUTE_SHAPE).isRequired
});
//-----------
@ -40,12 +44,12 @@ const Entity = ({ data, selected = false }) => {
//Контроль свойств компонента - Сущность запроса
Entity.propTypes = {
data: ENTITY_DATA_SHAPE,
selected: PropTypes.bool.isRequired
data: ENTITY_SHAPE.isRequired,
selected: PropTypes.bool
};
//----------------
//Интерфейс модуля
//----------------
export { Entity, ENTITY_DATA_SHAPE };
export { Entity, ENTITY_SHAPE };

View File

@ -11,8 +11,8 @@ import React from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента
import { P8PEditorBox } from "../../../../components/editors/p8p_editor_box"; //Контейнер параметров редактора
import { P8PEditorSubHeader } from "../../../../components/editors/p8p_editor_sub_header"; //Подзаголовок группы параметров редактора
import { ENTITY_DATA_SHAPE } from "../entity/entity"; //Описание сущности
import { RELATION_DATA_SHAPE } from "../relation/relation"; //Описание связи
import { ENTITY_SHAPE } from "../entity/entity"; //Описание сущности
import { RELATION_SHAPE } from "../relation/relation"; //Описание связи
import { ARGUMENT_SHAPE } from "../argument/argument"; //Описание аргумента запроса
import { InspectorQueryArguments } from "../inspector_query_args/inspector_query_args"; //Управление аргументами запроса
import { InspectorQueryConditions } from "../inspector_query_cond/inspector_query_cond"; //Управление условиями отбора запроса
@ -24,7 +24,7 @@ import { InspectorQueryRelations } from "../inspector_query_rls/inspector_query_
//-----------
//Инспектор свойств
const Inspector = ({ query, entity, relation, args = [], cond = null, onOptionsChanged = null }) => {
const Inspector = ({ query, entity, relation, entities = [], args = [], cond = null, onOptionsChanged = null }) => {
//При изменении настроек запроса
const handleOptionsChanged = () => onOptionsChanged && onOptionsChanged();
@ -34,7 +34,7 @@ const Inspector = ({ query, entity, relation, args = [], cond = null, onOptionsC
<P8PEditorSubHeader title={"Аргументы"} />
<InspectorQueryArguments query={query} args={args} onOptionsChanged={handleOptionsChanged} />
<P8PEditorSubHeader title={"Условия отбора"} />
<InspectorQueryConditions query={query} cond={cond} onOptionsChanged={handleOptionsChanged} />
<InspectorQueryConditions query={query} cond={cond} entities={entities} args={args} onOptionsChanged={handleOptionsChanged} />
<P8PEditorSubHeader title={"Сущности"} />
<InspectorQueryEntities query={query} entity={entity} onOptionsChanged={handleOptionsChanged} />
{relation && (
@ -50,8 +50,9 @@ const Inspector = ({ query, entity, relation, args = [], cond = null, onOptionsC
//Контроль свойств компонента - Инспектор свойств
Inspector.propTypes = {
query: PropTypes.number.isRequired,
entity: ENTITY_DATA_SHAPE,
relation: RELATION_DATA_SHAPE,
entity: ENTITY_SHAPE,
relation: RELATION_SHAPE,
entities: PropTypes.arrayOf(ENTITY_SHAPE),
args: PropTypes.arrayOf(ARGUMENT_SHAPE),
cond: PropTypes.string,
onOptionsChanged: PropTypes.func

View File

@ -10,7 +10,7 @@
import React from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента
import { Stack, List, ListItem, IconButton, Icon, ListItemButton, ListItemText, ListItemIcon, Checkbox, Typography } from "@mui/material"; //Интерфейсные компоненты MUI
import { ATTRIBUTE_DATA_SHAPE, attrGetUse, attrGetShow } from "../attribute/attribute"; //Атрибут сущности
import { ATTRIBUTE_SHAPE, attrGetUse, attrGetShow } from "../attribute/attribute"; //Атрибут сущности
import { APP_STYLES } from "../../../../../app.styles"; //Общие стили приложения
//---------
@ -84,7 +84,7 @@ const AttrsList = ({ attrs = [], filter, onSelect = null, onShow = null } = {})
//Контроль свойств компонента - Список атрибутов сущности
AttrsList.propTypes = {
attrs: PropTypes.arrayOf(ATTRIBUTE_DATA_SHAPE),
attrs: PropTypes.arrayOf(ATTRIBUTE_SHAPE),
filter: PropTypes.string,
onSelect: PropTypes.func,
onShow: PropTypes.func

View File

@ -13,7 +13,7 @@ import { Icon, Button } from "@mui/material"; //Интерфейсные эле
import { MessagingСtx } from "../../../../context/messaging"; //Контекст сообщений приложения
import { P8PEditorSubHeader } from "../../../../components/editors/p8p_editor_sub_header"; //Подзаголовок группы параметров редактора
import { BUTTONS } from "../../../../../app.text"; //Общие текстовые ресурсы
import { ENTITY_DATA_SHAPE } from "../entity/entity"; //Описание сущности
import { ENTITY_SHAPE } from "../entity/entity"; //Описание сущности
import { EntityAddDialog } from "./entity_add_dialog"; //Диалог добавления сущности
import { EntityAttrsDialog } from "./entity_attrs_dialog"; //Диалог настройки атрибутов сущности
import { useQueryEntities } from "./hooks"; //Хуки для работы с сущностями на сервере
@ -101,7 +101,7 @@ const InspectorQueryEntities = ({ query, entity, onOptionsChanged }) => {
//Контроль свойств компонента - Компонент инспектора - Сущности запроса
InspectorQueryEntities.propTypes = {
query: PropTypes.number.isRequired,
entity: ENTITY_DATA_SHAPE,
entity: ENTITY_SHAPE,
onOptionsChanged: PropTypes.func
};

View File

@ -12,7 +12,7 @@ import PropTypes from "prop-types"; //Контроль свойств компо
import { Icon, Button } from "@mui/material"; //Интерфейсные элементы
import { MessagingСtx } from "../../../../context/messaging"; //Контекст сообщений приложения
import { BUTTONS } from "../../../../../app.text"; //Общие текстовые ресурсы
import { RELATION_DATA_SHAPE } from "../relation/relation"; //Описание связи
import { RELATION_SHAPE } from "../relation/relation"; //Описание связи
import { useQueryRelations } from "./hooks"; //Хуки для работы со связями
//-----------
@ -50,7 +50,7 @@ const InspectorQueryRelations = ({ query, relation, onOptionsChanged }) => {
//Контроль свойств компонента - Компонент инспектора - Связи запроса
InspectorQueryRelations.propTypes = {
query: PropTypes.number.isRequired,
relation: RELATION_DATA_SHAPE.isRequired,
relation: RELATION_SHAPE.isRequired,
onOptionsChanged: PropTypes.func
};

View File

@ -11,8 +11,8 @@ import React, { useState, useCallback, useEffect } from "react"; //Классы
import PropTypes from "prop-types"; //Контроль свойств компонента
import ReactFlow, { addEdge, Controls, getOutgoers, applyNodeChanges, applyEdgeChanges } from "reactflow"; //Библиотека редактора диаграмм
import { NODE_TYPE } from "../../common"; //Общие ресурсы и константы редактора
import { Entity, ENTITY_DATA_SHAPE } from "../entity/entity"; //Сущность запроса
import { Attribute, ATTRIBUTE_DATA_SHAPE } from "../attribute/attribute"; //Атрибут сущности
import { Entity, ENTITY_SHAPE } from "../entity/entity"; //Сущность запроса
import { Attribute, ATTRIBUTE_SHAPE } from "../attribute/attribute"; //Атрибут сущности
import "reactflow/dist/style.css"; //Типовые стили библиотеки редактора диаграмм
import "./query_diagram.css"; //Стили компонента
@ -37,18 +37,18 @@ const NODE_TYPES_COMPONENTS = {
[NODE_TYPE.ATTRIBUTE]: Attribute
};
//Структура сущности запроса
const ENTITY_SHAPE = PropTypes.shape({
//Структура элемента диаграммы
const NODE_SHAPE = PropTypes.shape({
id: PropTypes.string.isRequired,
type: PropTypes.oneOf([NODE_TYPE.ENTITY, NODE_TYPE.ATTRIBUTE]).isRequired,
style: PropTypes.object,
position: PropTypes.shape({ x: PropTypes.number.isRequired, y: PropTypes.number.isRequired }),
draggable: PropTypes.bool.isRequired,
data: PropTypes.oneOfType([ENTITY_DATA_SHAPE, ATTRIBUTE_DATA_SHAPE])
data: PropTypes.oneOfType([ENTITY_SHAPE, ATTRIBUTE_SHAPE])
});
//Структура связи запроса
const RELATION_SHAPE = PropTypes.shape({ id: PropTypes.string.isRequired, source: PropTypes.string.isRequired, target: PropTypes.string.isRequired });
//Структура связи диаграммы
const EDGE_SHAPE = PropTypes.shape({ id: PropTypes.string.isRequired, source: PropTypes.string.isRequired, target: PropTypes.string.isRequired });
//------------------------------------
//Вспомогательные функции и компоненты
@ -88,8 +88,8 @@ const isValidConnection = (connection, nodes, edges) => {
//Диаграмма запроса
const QueryDiagram = ({
entities = [],
relations = [],
nodes = [],
edges = [],
onEntityClick,
onEntityAttrClick,
onEntityPositionChange,
@ -100,10 +100,10 @@ const QueryDiagram = ({
onPaneClick
}) => {
//Собственное состояние - элементы
const [nodes, setNodes] = useState(entities);
const [nodesCurrent, setNodesCurrent] = useState(nodes);
//Собственное состояние - связи
const [edges, setEdges] = useState(relations);
const [edgesCurrent, setEdgesCurrent] = useState(edges);
//Собственное состояние - перемещённый элемент
const [movedNode, setMovedNode] = useState(null);
@ -115,7 +115,7 @@ const QueryDiagram = ({
const tmpChanges = changes.reduce((prevChanges, curChanges) => {
const tmp = { ...curChanges };
if (tmp.type == "select") {
const chEnt = entities.find(e => e.id === tmp.id);
const chEnt = nodes.find(n => n.id === tmp.id);
if (chEnt && chEnt?.data?.parentEntity) {
prevChanges.push({ ...curChanges, id: chEnt.data.parentEntity });
tmp.selected = false;
@ -125,7 +125,7 @@ const QueryDiagram = ({
return prevChanges;
}, []);
//Применим изменения в диаграмме
setNodes(nodesSnapshot => applyNodeChanges(tmpChanges, nodesSnapshot));
setNodesCurrent(nodesSnapshot => applyNodeChanges(tmpChanges, nodesSnapshot));
//Если двигали сущность - запомним начало движения
if (changes.length == 1 && changes[0].type == "position" && changes[0].dragging)
setMovedNode({ id: changes[0].id, position: { ...changes[0].position } });
@ -136,10 +136,10 @@ const QueryDiagram = ({
setMovedNode(null);
}
//Если удалили сущность - вызываем колбэк для удаления
if (changes[0].type == "remove" && entities.find(e => e.id == changes[0].id && e.type == NODE_TYPE.ENTITY) && onEntityRemove)
if (changes[0].type == "remove" && nodes.find(n => n.id == changes[0].id && n.type == NODE_TYPE.ENTITY) && onEntityRemove)
onEntityRemove(changes[0].id);
},
[movedNode, entities, onEntityClick, onEntityPositionChange, onEntityRemove]
[movedNode, nodes, onEntityClick, onEntityPositionChange, onEntityRemove]
);
//При выборе элемента диаграммы
@ -153,7 +153,7 @@ const QueryDiagram = ({
//При связывании элементов на диаграмме
const handleConnect = connection => {
setEdges(state => addEdge({ ...connection, id: `${connection.source}-${connection.target}` }, state));
setEdgesCurrent(state => addEdge({ ...connection, id: `${connection.source}-${connection.target}` }, state));
onRelationAdd && onRelationAdd(connection.source, connection.target);
};
@ -163,7 +163,7 @@ const QueryDiagram = ({
//При изменении связей на диаграмме
const handleEdgesChange = useCallback(
changes => {
setEdges(edgesSnapshot => applyEdgeChanges(changes, edgesSnapshot));
setEdgesCurrent(edgesSnapshot => applyEdgeChanges(changes, edgesSnapshot));
if (changes.length == 1 && changes[0].type == "remove" && onRelationRemove) onRelationRemove(changes[0].id);
},
[onRelationRemove]
@ -173,22 +173,22 @@ const QueryDiagram = ({
const handlePaneClick = () => onPaneClick && onPaneClick();
//Валидация связи
const validateConnection = connection => isValidConnection(connection, nodes, edges);
const validateConnection = connection => isValidConnection(connection, nodesCurrent, edgesCurrent);
//Подсветка выбранной сущности
//При изменении состава сущностей
useEffect(() => setNodes(entities), [entities]);
useEffect(() => setNodesCurrent(nodes), [nodes]);
//При изменении состава связей
useEffect(() => setEdges(relations), [relations]);
useEffect(() => setEdgesCurrent(edges), [edges]);
//Генерация содержимого
return (
<ReactFlow
nodes={nodes}
nodes={nodesCurrent}
nodeTypes={NODE_TYPES_COMPONENTS}
edges={edges}
edges={edgesCurrent}
onNodeClick={handleNodeClick}
onNodesChange={handleNodesChange}
onEdgeClick={handleEdgeClick}
@ -210,8 +210,8 @@ const QueryDiagram = ({
//Контроль свойств компонента - Диаграмма запроса
QueryDiagram.propTypes = {
entities: PropTypes.arrayOf(ENTITY_SHAPE),
relations: PropTypes.arrayOf(RELATION_SHAPE),
nodes: PropTypes.arrayOf(NODE_SHAPE),
edges: PropTypes.arrayOf(EDGE_SHAPE),
onEntityClick: PropTypes.func,
onEntityAttrClick: PropTypes.func,
onEntityPositionChange: PropTypes.func,

View File

@ -14,7 +14,7 @@ import PropTypes from "prop-types"; //Контроль свойств компо
//---------
//Структура данных о связи сущностей запроса
const RELATION_DATA_SHAPE = PropTypes.shape({
const RELATION_SHAPE = PropTypes.shape({
id: PropTypes.string.isRequired,
source: PropTypes.string.isRequired,
target: PropTypes.string.isRequired
@ -24,4 +24,4 @@ const RELATION_DATA_SHAPE = PropTypes.shape({
//Интерфейс модуля
//----------------
export { RELATION_DATA_SHAPE };
export { RELATION_SHAPE };

View File

@ -11,76 +11,86 @@ import { useState, useContext, useEffect } from "react"; //Классы React
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
import { NODE_TYPE } from "./common"; //Общие ресурсы и константы редактора
//---------
//Константы
//---------
//Ширина элемента диаграммы
const NODE_WIDTH = 250;
//Высота единицы состава группового элемента диаграммы
const GROUP_NODE_ITEM_HEIGHT = 50;
//Стили
const STYLES = {
ATTRIBUTE: isLast => ({ borderBottom: isLast ? "none" : "1px solid #dee2e6" })
};
//------------------------------------
//Вспомогательные функции и компоненты
//------------------------------------
//Конвертация серверного описания сущностей запроса в элементы диаграммы
const serverEntity2QueryDiagramNodes = entity => {
const groupWidth = 250;
const nameColumnHeight = 50;
const columns = entity?.XATTRS?.XATTR || [];
const columnsCount = columns.length;
const groupHeight = nameColumnHeight + columnsCount * 50;
const groupNode = {
//Ссылка на атрибуты
const attrs = entity.attrs || [];
//Количество атрибутов
const attrsCount = attrs.length;
//Высота группового элемента диаграммы
const entityNodeHeight = GROUP_NODE_ITEM_HEIGHT + attrsCount * GROUP_NODE_ITEM_HEIGHT;
//Элемент диаграммы для сущности (групповой элемент)
const entityNode = {
id: entity.id,
type: NODE_TYPE.ENTITY,
data: { ...entity },
position: { x: entity.x, y: entity.y },
style: {
width: groupWidth,
height: groupHeight
width: NODE_WIDTH,
height: entityNodeHeight
},
draggable: true
};
const columnNodes = columns.map((column, index, columns) => {
const x = 1;
const y = 50 * (index + 1);
const width = groupWidth - 2;
const height = 50;
const isLast = index === columns.length - 1;
const defaultColumnStyles = {
borderBottom: "1px solid #dee2e6"
};
const lastColumnStyles = {
borderBottom: "none"
};
const otherStyles = isLast ? lastColumnStyles : defaultColumnStyles;
return {
id: column.id,
type: NODE_TYPE.ATTRIBUTE,
data: {
...column,
included: false,
parentEntity: entity.id
},
position: { x, y },
parentId: entity.id,
extent: "parent",
style: {
width,
height,
...otherStyles
},
draggable: false,
selectable: true
};
});
return [groupNode, ...columnNodes];
//Элементы диаграммы для атрибутов сущности (состав группового элемента)
const attrsNodes = attrs.map((attr, index, attrs) => ({
id: attr.id,
type: NODE_TYPE.ATTRIBUTE,
data: { ...attr },
position: { x: 1, y: GROUP_NODE_ITEM_HEIGHT * (index + 1) },
parentId: entity.id,
extent: "parent",
style: {
width: NODE_WIDTH - 2,
height: GROUP_NODE_ITEM_HEIGHT,
...STYLES.ATTRIBUTE(index === attrs.length - 1)
},
draggable: false,
selectable: true
}));
//Возвращаем элемент для сущности (групповой) и элементы для атрибутов (состав группового)
return [entityNode, ...attrsNodes];
};
//Конвертация серверного описания запроса в данные для редактора диаграмм
const serverQueryData2QueryDiagram = (entities, relations) => {
const result = { entities: [], relations: [...relations] };
entities.forEach(entity => {
const nodes = serverEntity2QueryDiagramNodes(entity);
result.entities = [...result.entities, ...nodes];
//Инициализация результата
const result = { entities: [], relations: [], nodes: [], edges: [] };
//Сущности (почти как есть на сервере, только массив XATTRS.XATTR перемещается в attrs)
result.entities = entities.map(e => {
const tmp = { ...e };
tmp.attrs = tmp?.XATTRS?.XATTR?.map(a => ({ ...a })) || [];
delete tmp.XATTRS;
return tmp;
});
//Связи сущностей
result.relations = relations.map(r => ({ ...r }));
//Элементы для диаграммы
result.entities.forEach(entity => {
const nodes = serverEntity2QueryDiagramNodes(entity);
result.nodes = [...result.nodes, ...nodes];
});
//Грани для диаграммы
result.edges = relations.map(r => ({ ...r }));
//Вернем итоговый результат
return result;
};

View File

@ -67,7 +67,7 @@ const QueryEditor = () => {
const selectEntity = ent => {
setRelation(null);
const queryEnt = queryDiagram.entities.find(e => e.id === ent);
if (queryEnt) setEntity({ ...queryEnt.data });
if (queryEnt) setEntity({ ...queryEnt });
};
//Выбор связи
@ -137,6 +137,7 @@ const QueryEditor = () => {
setAppBarTitle(`Запрос [${name}]`);
setQuery(rn);
setOpenQueriesManager(false);
cleanupEnRlSelection();
};
//При изменении свойств запроса
@ -163,7 +164,8 @@ const QueryEditor = () => {
<Grid item xs={20}>
{queryDiagram && (
<QueryDiagram
{...queryDiagram}
nodes={queryDiagram?.nodes}
edges={queryDiagram?.edges}
onEntityClick={handleEntityClick}
onEntityAttrClick={handleEntityAttrClick}
onEntityPositionChange={handleEntityPositionChange}
@ -178,7 +180,14 @@ const QueryEditor = () => {
<Grid item xs={5} sx={STYLES.GRID_ITEM_INSPECTOR}>
{toolBar}
{query && (
<Inspector {...queryOption} query={query} entity={entity} relation={relation} onOptionsChanged={handleQueryOptionsChanged} />
<Inspector
{...queryOption}
query={query}
entity={entity}
relation={relation}
entities={queryDiagram?.entities}
onOptionsChanged={handleQueryOptionsChanged}
/>
)}
</Grid>
</Grid>

View File

@ -23,6 +23,7 @@ create or replace package PKG_P8PANELS_QE_BASE as
type TATTR is record
(
SID PKG_STD.TSTRING, -- Уникальный идентификатор в запросе
SPARENT_ENTITY PKG_STD.TSTRING, -- Идентификатор родительской сущности
SNAME PKG_STD.TSTRING, -- Имя
STITLE PKG_STD.TSTRING, -- Заголовок
NDATA_TYPE PKG_STD.TNUMBER, -- Тип данных (см. константы PKG_STD.DATA_TYPE_*)
@ -310,27 +311,28 @@ create or replace package body PKG_P8PANELS_QE_BASE as
STAG_COND constant PKG_STD.TSTRING := 'XCOND'; -- Условия запроса
/* Константы - Атрибуты для сериализации */
SATTR_ID constant PKG_STD.TSTRING := 'id'; -- Идентификатор
SATTR_RN constant PKG_STD.TSTRING := 'rn'; -- Регистрационный номер
SATTR_CODE constant PKG_STD.TSTRING := 'code'; -- Код
SATTR_NAME constant PKG_STD.TSTRING := 'name'; -- Имя
SATTR_AUTHOR constant PKG_STD.TSTRING := 'author'; -- Автор
SATTR_CH_DATE constant PKG_STD.TSTRING := 'chDate'; -- Дата изменения
SATTR_READY constant PKG_STD.TSTRING := 'ready'; -- Готовность к использованию
SATTR_PBL constant PKG_STD.TSTRING := 'pbl'; -- Публичность
SATTR_MODIFY constant PKG_STD.TSTRING := 'modify'; -- Изменяемость
SATTR_TITLE constant PKG_STD.TSTRING := 'title'; -- Заголовок
SATTR_TYPE constant PKG_STD.TSTRING := 'type'; -- Тип
SATTR_DATA_TYPE constant PKG_STD.TSTRING := 'dataType'; -- Тип данных
SATTR_MANDATORY constant PKG_STD.TSTRING := 'mandatory'; -- Обязательность
SATTR_X constant PKG_STD.TSTRING := 'x'; -- Координата по X
SATTR_Y constant PKG_STD.TSTRING := 'y'; -- Координата по Y
SATTR_SOURCE constant PKG_STD.TSTRING := 'source'; -- Источник
SATTR_TARGET constant PKG_STD.TSTRING := 'target'; -- Приёмник
SATTR_AGG constant PKG_STD.TSTRING := 'agg'; -- Агрегатная функция
SATTR_ALIAS constant PKG_STD.TSTRING := 'alias'; -- Псевдоним
SATTR_USE constant PKG_STD.TSTRING := 'use'; -- Применение в запросе
SATTR_SHOW constant PKG_STD.TSTRING := 'show'; -- Отображение в запросе
SATTR_ID constant PKG_STD.TSTRING := 'id'; -- Идентификатор
SATTR_RN constant PKG_STD.TSTRING := 'rn'; -- Регистрационный номер
SATTR_CODE constant PKG_STD.TSTRING := 'code'; -- Код
SATTR_NAME constant PKG_STD.TSTRING := 'name'; -- Имя
SATTR_AUTHOR constant PKG_STD.TSTRING := 'author'; -- Автор
SATTR_CH_DATE constant PKG_STD.TSTRING := 'chDate'; -- Дата изменения
SATTR_READY constant PKG_STD.TSTRING := 'ready'; -- Готовность к использованию
SATTR_PBL constant PKG_STD.TSTRING := 'pbl'; -- Публичность
SATTR_MODIFY constant PKG_STD.TSTRING := 'modify'; -- Изменяемость
SATTR_TITLE constant PKG_STD.TSTRING := 'title'; -- Заголовок
SATTR_TYPE constant PKG_STD.TSTRING := 'type'; -- Тип
SATTR_DATA_TYPE constant PKG_STD.TSTRING := 'dataType'; -- Тип данных
SATTR_MANDATORY constant PKG_STD.TSTRING := 'mandatory'; -- Обязательность
SATTR_X constant PKG_STD.TSTRING := 'x'; -- Координата по X
SATTR_Y constant PKG_STD.TSTRING := 'y'; -- Координата по Y
SATTR_SOURCE constant PKG_STD.TSTRING := 'source'; -- Источник
SATTR_TARGET constant PKG_STD.TSTRING := 'target'; -- Приёмник
SATTR_AGG constant PKG_STD.TSTRING := 'agg'; -- Агрегатная функция
SATTR_ALIAS constant PKG_STD.TSTRING := 'alias'; -- Псевдоним
SATTR_USE constant PKG_STD.TSTRING := 'use'; -- Применение в запросе
SATTR_SHOW constant PKG_STD.TSTRING := 'show'; -- Отображение в запросе
SATTR_PARENT_ENTITY constant PKG_STD.TSTRING := 'parentEntity'; -- Идентификатор родительской сущности
/* Константы - зарезервированные имена рагументов */
SARG_NAME_PAGE constant PKG_STD.TSTRING := 'NPAGE'; -- Номер страницы
@ -767,6 +769,7 @@ create or replace package body PKG_P8PANELS_QE_BASE as
PKG_XFAST.DOWN_NODE(SNAME => STAG_ATTR);
/* Атрибут */
PKG_XFAST.ATTR(SNAME => SATTR_ID, SVALUE => RATTR.SID);
PKG_XFAST.ATTR(SNAME => SATTR_PARENT_ENTITY, SVALUE => RATTR.SPARENT_ENTITY);
PKG_XFAST.ATTR(SNAME => SATTR_NAME, SVALUE => RATTR.SNAME);
PKG_XFAST.ATTR(SNAME => SATTR_TITLE, SVALUE => RATTR.STITLE);
PKG_XFAST.ATTR(SNAME => SATTR_DATA_TYPE, NVALUE => RATTR.NDATA_TYPE);
@ -798,14 +801,15 @@ create or replace package body PKG_P8PANELS_QE_BASE as
XROOT := PKG_XPATH.ROOT_NODE(RDOCUMENT => XDOC);
XNODE := PKG_XPATH.SINGLE_NODE(RPARENT_NODE => XROOT, SPATTERN => '/' || STAG_ATTR);
/* Получаем значения */
RRES.SID := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_ID);
RRES.SNAME := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_NAME);
RRES.STITLE := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_TITLE);
RRES.NDATA_TYPE := PKG_XPATH.ATTRIBUTE_NUM(RNODE => XNODE, SNAME => SATTR_DATA_TYPE);
RRES.SAGG := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_AGG);
RRES.SALIAS := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_ALIAS);
RRES.NUSE := PKG_XPATH.ATTRIBUTE_NUM(RNODE => XNODE, SNAME => SATTR_USE);
RRES.NSHOW := PKG_XPATH.ATTRIBUTE_NUM(RNODE => XNODE, SNAME => SATTR_SHOW);
RRES.SID := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_ID);
RRES.SPARENT_ENTITY := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_PARENT_ENTITY);
RRES.SNAME := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_NAME);
RRES.STITLE := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_TITLE);
RRES.NDATA_TYPE := PKG_XPATH.ATTRIBUTE_NUM(RNODE => XNODE, SNAME => SATTR_DATA_TYPE);
RRES.SAGG := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_AGG);
RRES.SALIAS := PKG_XPATH.ATTRIBUTE(RNODE => XNODE, SNAME => SATTR_ALIAS);
RRES.NUSE := PKG_XPATH.ATTRIBUTE_NUM(RNODE => XNODE, SNAME => SATTR_USE);
RRES.NSHOW := PKG_XPATH.ATTRIBUTE_NUM(RNODE => XNODE, SNAME => SATTR_SHOW);
/* Освободим документ */
PKG_XPATH.FREE(RDOCUMENT => XDOC);
exception
@ -908,6 +912,7 @@ create or replace package body PKG_P8PANELS_QE_BASE as
else
/* Такого элемента нет - берем из представления */
RRES(RRES.LAST).SID := TATTR_ID_MAKE(SENT_ID => RENT.SID, SNAME => RVIEW_FIELD.COLUMN_NAME);
RRES(RRES.LAST).SPARENT_ENTITY := RENT.SID;
RRES(RRES.LAST).SNAME := RVIEW_FIELD.COLUMN_NAME;
RRES(RRES.LAST).STITLE := DMSCLVIEWSATTRS_TITLE_GET(SVIEW_NAME => RENT.SNAME,
SATTR_NAME => RRES(RRES.LAST).SNAME);