diff --git a/app/panels/query_editor/components/entity_attrs_dialog/attrs_list.js b/app/panels/query_editor/components/entity_attrs_dialog/attrs_list.js
new file mode 100644
index 0000000..705aac1
--- /dev/null
+++ b/app/panels/query_editor/components/entity_attrs_dialog/attrs_list.js
@@ -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 (
+
+ {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 (
+
+ handleSelectClick(attr)} selected={selected} dense>
+
+
+
+
+ {`${attr.alias || attr.name}`}
+
+ }
+ />
+
+ handleShowClick(e, attr)} title={showTitle}>
+ {showIcon}
+
+
+
+
+ );
+ })}
+
+ );
+};
+
+//Контроль свойств компонента - Список атрибутов сущности
+AttrsList.propTypes = {
+ attrs: PropTypes.arrayOf(ATTRIBUTE_DATA_SHAPE),
+ filter: PropTypes.string,
+ onSelect: PropTypes.func,
+ onShow: PropTypes.func
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { AttrsList };
diff --git a/app/panels/query_editor/components/entity_attrs_dialog/entity_attrs_dialog.js b/app/panels/query_editor/components/entity_attrs_dialog/entity_attrs_dialog.js
index 972d74a..d588f5b 100644
--- a/app/panels/query_editor/components/entity_attrs_dialog/entity_attrs_dialog.js
+++ b/app/panels/query_editor/components/entity_attrs_dialog/entity_attrs_dialog.js
@@ -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 ;
+ return (
+
+
+ search
+
+ ),
+ endAdornment: (
+
+
+ clear
+
+
+ )
+ }}
+ />
+
+
+ );
};
//Контроль свойств - Диалог настройки атрибутов сущности
EntityAttrsDialog.propTypes = {
+ query: PropTypes.number.isRequired,
id: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
onOk: PropTypes.func,
diff --git a/app/panels/query_editor/components/entity_attrs_dialog/hooks.js b/app/panels/query_editor/components/entity_attrs_dialog/hooks.js
new file mode 100644
index 0000000..5532430
--- /dev/null
+++ b/app/panels/query_editor/components/entity_attrs_dialog/hooks.js
@@ -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 };
diff --git a/app/panels/query_editor/components/query_options/query_options.js b/app/panels/query_editor/components/query_options/query_options.js
index d3c9f9d..0fbb2f0 100644
--- a/app/panels/query_editor/components/query_options/query_options.js
+++ b/app/panels/query_editor/components/query_options/query_options.js
@@ -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 && }
- {openEntityAttrsDialog && }
-
+ {openEntityAttrsDialog && (
+
+ )}
+
+
+