ЦИТК-979 - Установка атрибутов сущности (клиент)
This commit is contained in:
parent
fa00940776
commit
a20de79a40
@ -0,0 +1,97 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга - Редактор запросов
|
||||
Компонент: Список атрибутов сущности
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
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 { APP_STYLES } from "../../../../../app.styles"; //Общие стили приложения
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Стили
|
||||
const STYLES = {
|
||||
SMALL_TOOL_ICON: {
|
||||
fontSize: 20
|
||||
},
|
||||
LIST: { height: "500px", width: "360px", bgcolor: "background.paper", overflowY: "auto", ...APP_STYLES.SCROLL }
|
||||
};
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Список атрибутов сущности
|
||||
const AttrsList = ({ attrs = [], filter, onSelect = null, onShow = null } = {}) => {
|
||||
//При выборе элемента списка
|
||||
const handleSelectClick = attr => {
|
||||
onSelect && onSelect(attr);
|
||||
};
|
||||
|
||||
//При нажатии на исправлении
|
||||
const handleShowClick = (e, attr) => {
|
||||
e.stopPropagation();
|
||||
onShow && onShow(attr);
|
||||
};
|
||||
|
||||
//Рег. выражение для фильтра
|
||||
const filterRegExp = filter ? new RegExp(filter, "i") : null;
|
||||
|
||||
//Формирование представления
|
||||
return (
|
||||
<List sx={STYLES.LIST}>
|
||||
{attrs &&
|
||||
attrs
|
||||
.filter(attr => (filterRegExp ? filterRegExp.test(attr.name) || filterRegExp.test(attr.title) : true))
|
||||
.map((attr, i) => {
|
||||
const [selected, selectedTitle] = attrGetUse(attr, true);
|
||||
const [showTitle, showIcon] = attrGetShow(attr, true);
|
||||
return (
|
||||
<ListItem key={i} disablePadding>
|
||||
<ListItemButton onClick={() => handleSelectClick(attr)} selected={selected} dense>
|
||||
<ListItemIcon>
|
||||
<Checkbox edge="start" checked={selected} tabIndex={-1} disableRipple title={selectedTitle} />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={attr.title}
|
||||
secondaryTypographyProps={{ component: "div" }}
|
||||
secondary={
|
||||
<Stack direction={"column"}>
|
||||
<Typography variant={"caption"}>{`${attr.alias || attr.name}`}</Typography>
|
||||
</Stack>
|
||||
}
|
||||
/>
|
||||
<Stack direction={"row"}>
|
||||
<IconButton onClick={e => handleShowClick(e, attr)} title={showTitle}>
|
||||
<Icon>{showIcon}</Icon>
|
||||
</IconButton>
|
||||
</Stack>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств компонента - Список атрибутов сущности
|
||||
AttrsList.propTypes = {
|
||||
attrs: PropTypes.arrayOf(ATTRIBUTE_DATA_SHAPE),
|
||||
filter: PropTypes.string,
|
||||
onSelect: PropTypes.func,
|
||||
onShow: PropTypes.func
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { AttrsList };
|
@ -7,28 +7,97 @@
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import React from "react"; //Классы React
|
||||
import React, { useState, useEffect } from "react"; //Классы React
|
||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||
import { TextField, InputAdornment, Icon, IconButton } from "@mui/material"; //Интерфейсные элементы MUI
|
||||
import { P8PDialog } from "../../../../components/p8p_dialog"; //Типовой диалог
|
||||
import { AttrsList } from "./attrs_list"; //Список атрибутов сущности
|
||||
import { useEntityAttrs } from "./hooks"; //Хуки диалога настройки атрибутов сущности
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Диалог настройки атрибутов сущности
|
||||
const EntityAttrsDialog = ({ id, title, onOk, onCancel }) => {
|
||||
const EntityAttrsDialog = ({ query, id, title, onOk, onCancel }) => {
|
||||
//Собственное состояние - фильтр атрибутов
|
||||
const [filter, setFilter] = useState("");
|
||||
|
||||
//Собственное состояние - список атрибутов
|
||||
const [attrs, setAttrs] = useState([]);
|
||||
|
||||
//Хук для взаимодействия с сервером
|
||||
const [srvAttrs, saveAttrs] = useEntityAttrs(query, id);
|
||||
|
||||
//Нажатие на кнопку "Ok"
|
||||
const handleOk = values => onOk && onOk({ ...values });
|
||||
const handleOk = async () => {
|
||||
await saveAttrs(attrs);
|
||||
onOk && onOk();
|
||||
};
|
||||
|
||||
//Нажатие на кнопку "Отмена"
|
||||
const handleCancel = () => onCancel && onCancel();
|
||||
|
||||
//Выбор/исключение атрибута из запроса
|
||||
const handleAttrSelect = attr =>
|
||||
setAttrs(
|
||||
attrs.map(a => ({
|
||||
...(a.id === attr.id ? { ...a, use: a.use === 1 ? 0 : 1, show: a.use === 1 ? 0 : a.show } : a)
|
||||
}))
|
||||
);
|
||||
|
||||
//Отображение/сокрытие атрибута в запросе
|
||||
const handleAttrShow = attr =>
|
||||
setAttrs(
|
||||
attrs.map(a => ({
|
||||
...(a.id === attr.id ? { ...a, show: a.show === 1 ? 0 : 1, use: a.show === 0 ? 1 : a.use } : a)
|
||||
}))
|
||||
);
|
||||
|
||||
//При изменении значения фильтра
|
||||
const handleFilterChange = e => setFilter(e.target.value);
|
||||
|
||||
//При очистке фильтра
|
||||
const handleFilterClear = () => setFilter("");
|
||||
|
||||
//При загрузке данных с сервера
|
||||
useEffect(() => {
|
||||
if (srvAttrs) setAttrs(srvAttrs.map(srvAttr => ({ ...srvAttr })));
|
||||
}, [srvAttrs]);
|
||||
|
||||
//Генерация содержимого
|
||||
return <P8PDialog title={`Атрибуты сущности "${title}"`} onOk={handleOk} onCancel={handleCancel} />;
|
||||
return (
|
||||
<P8PDialog title={`Атрибуты сущности "${title}"`} onOk={handleOk} onCancel={handleCancel}>
|
||||
<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>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<AttrsList attrs={attrs} filter={filter} onSelect={handleAttrSelect} onShow={handleAttrShow} />
|
||||
</P8PDialog>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Диалог настройки атрибутов сущности
|
||||
EntityAttrsDialog.propTypes = {
|
||||
query: PropTypes.number.isRequired,
|
||||
id: PropTypes.string.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
onOk: PropTypes.func,
|
||||
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга - Редактор запросов
|
||||
Пользовательские хуки диалога настройки атрибутов сущности
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import { useState, useContext, useEffect, useCallback } from "react"; //Классы React
|
||||
import { BackEndСtx } from "../../../../context/backend"; //Контекст взаимодействия с сервером
|
||||
import { object2Base64XML } from "../../../../core/utils"; //Вспомогательные функции
|
||||
|
||||
//------------------------------------
|
||||
//Вспомогательные функции и компоненты
|
||||
//------------------------------------
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Работа с атрибутами сущности
|
||||
const useEntityAttrs = (query, entity) => {
|
||||
//Собственное состояние - флаг загрузки
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
|
||||
//Собственное состояние - флаг необходимости обновления
|
||||
const [refresh, setRefresh] = useState(true);
|
||||
|
||||
//Собственное состояние - данные
|
||||
const [data, setData] = useState(null);
|
||||
|
||||
//Подключение к контексту взаимодействия с сервером
|
||||
const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
|
||||
|
||||
//Обновление данных
|
||||
const doRefresh = () => setRefresh(true);
|
||||
|
||||
//Установка атрибутов сущности
|
||||
const setAttrs = useCallback(
|
||||
async attrs => {
|
||||
await executeStored({
|
||||
stored: "PKG_P8PANELS_QE.QUERY_ENT_ATTRS_SET",
|
||||
args: {
|
||||
NRN: query,
|
||||
SID: entity,
|
||||
CATTRS: {
|
||||
VALUE: object2Base64XML(attrs, { arrayNodeName: "XATTR", ignoreAttributes: false, attributeNamePrefix: "" }),
|
||||
SDATA_TYPE: SERV_DATA_TYPE_CLOB
|
||||
}
|
||||
},
|
||||
loader: false
|
||||
});
|
||||
},
|
||||
[query, entity, executeStored, SERV_DATA_TYPE_CLOB]
|
||||
);
|
||||
|
||||
//При необходимости получить/обновить данные
|
||||
useEffect(() => {
|
||||
//Загрузка данных с сервера
|
||||
const loadData = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const data = await executeStored({
|
||||
stored: "PKG_P8PANELS_QE.QUERY_ENT_ATTRS_GET",
|
||||
args: { NRN: query, SID: entity },
|
||||
respArg: "COUT",
|
||||
isArray: name => ["XATTR"].includes(name),
|
||||
attributeValueProcessor: (name, val) => (["name", "title", "agg"].includes(name) ? undefined : val),
|
||||
loader: true
|
||||
});
|
||||
setData(data?.XATTRS?.XATTR || []);
|
||||
} finally {
|
||||
setRefresh(false);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
//Если надо обновить
|
||||
if (refresh)
|
||||
//Получим данные
|
||||
loadData();
|
||||
}, [refresh, query, entity, executeStored]);
|
||||
|
||||
//Возвращаем интерфейс хука
|
||||
return [data, setAttrs, doRefresh, isLoading];
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { useEntityAttrs };
|
@ -24,7 +24,7 @@ import { RELATION_DATA_SHAPE } from "../relation/relation"; //Описание
|
||||
//-----------
|
||||
|
||||
//Свойства запроса
|
||||
const QueryOptions = ({ onEntityAdd, onEntityRemove, onRelationRemove, onQueryOptionsChanged, entity, relation }) => {
|
||||
const QueryOptions = ({ query, entity, relation, onEntityAdd, onEntityRemove, onRelationRemove, onQueryOptionsChanged }) => {
|
||||
//Отображение диалога добавления сущности
|
||||
const [openEntityAddDialog, setOpenEntityAddDialog] = useState(false);
|
||||
|
||||
@ -70,8 +70,12 @@ const QueryOptions = ({ onEntityAdd, onEntityRemove, onRelationRemove, onQueryOp
|
||||
return (
|
||||
<>
|
||||
{openEntityAddDialog && <EntityAddDialog onOk={handleEntityAddDialogOk} onCancel={handleEntityAddDialogCancel} />}
|
||||
{openEntityAttrsDialog && <EntityAttrsDialog {...entity} onOk={handleEntityAttrsDialogOk} onCancel={handleEntityAttrsDialogCancel} />}
|
||||
<P8PEditorBox title={"Параметры запроса"}>
|
||||
{openEntityAttrsDialog && (
|
||||
<EntityAttrsDialog query={query} {...entity} onOk={handleEntityAttrsDialogOk} onCancel={handleEntityAttrsDialogCancel} />
|
||||
)}
|
||||
<P8PEditorBox title={"Настройки запроса"}>
|
||||
<P8PEditorSubHeader title={"Параметры"} />
|
||||
<P8PEditorSubHeader title={"Условия отбора"} />
|
||||
<P8PEditorSubHeader title={"Сущности"} />
|
||||
<Button startIcon={<Icon>add</Icon>} onClick={handleEntityAddClick}>
|
||||
{BUTTONS.INSERT}
|
||||
@ -102,12 +106,13 @@ const QueryOptions = ({ onEntityAdd, onEntityRemove, onRelationRemove, onQueryOp
|
||||
|
||||
//Контроль свойств компонента - Свойства запроса
|
||||
QueryOptions.propTypes = {
|
||||
query: PropTypes.number.isRequired,
|
||||
entity: ENTITY_DATA_SHAPE,
|
||||
relation: RELATION_DATA_SHAPE,
|
||||
onEntityAdd: PropTypes.func,
|
||||
onEntityRemove: PropTypes.func,
|
||||
onRelationRemove: PropTypes.func,
|
||||
onQueryOptionsChanged: PropTypes.func,
|
||||
entity: ENTITY_DATA_SHAPE,
|
||||
relation: RELATION_DATA_SHAPE
|
||||
onQueryOptionsChanged: PropTypes.func
|
||||
};
|
||||
|
||||
//----------------
|
||||
|
@ -55,6 +55,26 @@ const QueryEditor = () => {
|
||||
//Подключение к контексту приложения
|
||||
const { setAppBarTitle } = useContext(ApplicationСtx);
|
||||
|
||||
//Выбор сущности
|
||||
const selectEntity = ent => {
|
||||
setRelation(null);
|
||||
const queryEnt = queryDiagram.entities.find(e => e.id === ent);
|
||||
if (queryEnt) setEntity({ ...queryEnt.data });
|
||||
};
|
||||
|
||||
//Выбор связи
|
||||
const selectRelation = rl => {
|
||||
setEntity(null);
|
||||
const queryRl = queryDiagram.relations.find(r => r.id === rl);
|
||||
if (queryRl) setRelation({ ...queryRl });
|
||||
};
|
||||
|
||||
//Сброс выбора связи и сущности
|
||||
const cleanupEnRlSelection = () => {
|
||||
setRelation(null);
|
||||
setEntity(null);
|
||||
};
|
||||
|
||||
//Обработка изменения положения сущности на диаграмме
|
||||
const handleEntityPositionChange = (ent, position) => setEntPosition(ent, position.x, position.y);
|
||||
|
||||
@ -67,36 +87,33 @@ const QueryEditor = () => {
|
||||
//Обработка удаления сущности из запроса
|
||||
const handleEntityRemove = async ent => {
|
||||
await removeEnt(ent);
|
||||
if (entity && entity?.id === ent) setEntity(null);
|
||||
if (entity && entity?.id === ent) cleanupEnRlSelection();
|
||||
};
|
||||
|
||||
//Обработка выделения сущности
|
||||
const handleEntityClick = ent => {
|
||||
setRelation(null);
|
||||
const queryEnt = queryDiagram.entities.find(e => e.id === ent);
|
||||
if (queryEnt)
|
||||
if (entity?.id == queryEnt.id) setEntity(null);
|
||||
else setEntity({ ...queryEnt.data });
|
||||
};
|
||||
const handleEntityClick = ent => selectEntity(ent);
|
||||
|
||||
//Обработка выделения тарибута сущности
|
||||
const handleEntityAttrClick = ent => selectEntity(ent);
|
||||
|
||||
//Обработка выделения связи
|
||||
const handleRelationClick = rl => {
|
||||
setEntity(null);
|
||||
const queryRl = queryDiagram.relations.find(r => r.id === rl);
|
||||
if (queryRl)
|
||||
if (relation?.id == queryRl.id) setRelation(null);
|
||||
else setRelation({ ...queryRl });
|
||||
};
|
||||
const handleRelationClick = rl => selectRelation(rl);
|
||||
|
||||
//Обработка добавления отношения cущностей
|
||||
const handleRelationAdd = (source, target) => addRl(source, target);
|
||||
const handleRelationAdd = (source, target) => {
|
||||
cleanupEnRlSelection();
|
||||
addRl(source, target);
|
||||
};
|
||||
|
||||
//Обработка удаления отношения cущностей
|
||||
const handleRelationRemove = async rl => {
|
||||
await removeRl(rl);
|
||||
if (relation && relation?.id === rl) setRelation(null);
|
||||
if (relation && relation?.id === rl) cleanupEnRlSelection();
|
||||
};
|
||||
|
||||
//При нажатии на панели (пустом месте) диаграммы запроса
|
||||
const handlePaneClick = () => cleanupEnRlSelection();
|
||||
|
||||
//Открытие менеджера запросов
|
||||
const handleOpenQueriesManager = () => setOpenQueriesManager(true);
|
||||
|
||||
@ -106,8 +123,7 @@ const QueryEditor = () => {
|
||||
//Закрытие запроса
|
||||
const handleQueryClose = () => {
|
||||
setAppBarTitle(APP_BAR_TITLE_DEFAULT);
|
||||
setEntity(null);
|
||||
setRelation(null);
|
||||
cleanupEnRlSelection();
|
||||
setQuery(null);
|
||||
};
|
||||
|
||||
@ -119,7 +135,10 @@ const QueryEditor = () => {
|
||||
};
|
||||
|
||||
//При изменении свойств запроса
|
||||
const handleQueryOptionsChanged = () => doRefresh();
|
||||
const handleQueryOptionsChanged = () => {
|
||||
cleanupEnRlSelection();
|
||||
doRefresh();
|
||||
};
|
||||
|
||||
//Панель инструмментов
|
||||
const toolBar = (
|
||||
@ -141,11 +160,13 @@ const QueryEditor = () => {
|
||||
<QueryDiagram
|
||||
{...queryDiagram}
|
||||
onEntityClick={handleEntityClick}
|
||||
onEntityAttrClick={handleEntityAttrClick}
|
||||
onEntityPositionChange={handleEntityPositionChange}
|
||||
onEntityRemove={handleEntityRemove}
|
||||
onRelactionClick={handleRelationClick}
|
||||
onRelationAdd={handleRelationAdd}
|
||||
onRelationRemove={handleRelationRemove}
|
||||
onPaneClick={handlePaneClick}
|
||||
/>
|
||||
)}
|
||||
</Grid>
|
||||
@ -153,12 +174,13 @@ const QueryEditor = () => {
|
||||
{toolBar}
|
||||
{query && (
|
||||
<QueryOptions
|
||||
query={query}
|
||||
entity={entity}
|
||||
relation={relation}
|
||||
onEntityAdd={handleEntityAdd}
|
||||
onEntityRemove={handleEntityRemove}
|
||||
onRelationRemove={handleRelationRemove}
|
||||
onQueryOptionsChanged={handleQueryOptionsChanged}
|
||||
entity={entity}
|
||||
relation={relation}
|
||||
/>
|
||||
)}
|
||||
</Grid>
|
||||
|
Loading…
x
Reference in New Issue
Block a user