diff --git a/app/panels/query_editor/components/inspector_query_cond/cond_component_select_button.js b/app/panels/query_editor/components/inspector_query_cond/cond_component_select_button.js
new file mode 100644
index 0000000..e6f6822
--- /dev/null
+++ b/app/panels/query_editor/components/inspector_query_cond/cond_component_select_button.js
@@ -0,0 +1,138 @@
+/*
+ Парус 8 - Панели мониторинга - Редактор запросов
+ Компонент: Кнопка выбора компонента условия
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React, { useState } from "react"; //Классы React
+import PropTypes from "prop-types"; //Контроль свойств компонента
+import { Button, Menu, MenuItem, Icon, ListItemIcon, Stack, Typography } from "@mui/material"; //Интерфейсные компоненты MUI
+import { P8PEditorSubHeader } from "../../../../components/editors/p8p_editor_sub_header"; //Подзаголовок
+
+//---------
+//Константы
+//---------
+
+//Типы компонентов
+const COMPONENT_TYPE = {
+ DIVIDER: "divider",
+ ITEM: "item"
+};
+
+//Высота элемента меню для компонента
+const ITEM_HEIGHT = 48;
+
+//Количество одновременно отображаемых элементов (остальные - прокруткой)
+const ITEM_DSPL_COUNT = 10;
+
+//Стили
+const STYLES = {
+ MENU_PAPER: {
+ maxHeight: ITEM_HEIGHT * ITEM_DSPL_COUNT,
+ maxWidth: "400px",
+ overflow: "auto"
+ }
+};
+
+//-----------------------
+//Вспомогательные функции
+//-----------------------
+
+//Проверка строкового свойства, обязательного для компонента типа "ITEM"
+const checkReqStringPropForItem = (props, propName, componentName) => {
+ if (props.type === COMPONENT_TYPE.ITEM && !props[propName]) {
+ return new Error(`Prop \`${propName}\` is required when \`type\` is \`${COMPONENT_TYPE.ITEM}\` in \`${componentName}\`.`);
+ }
+ if (props[propName] && typeof props[propName] !== "string" && !(props[propName] instanceof String)) {
+ return new Error(`Prop \`${propName}\` must be a string`);
+ }
+ return null;
+};
+
+//-----------
+//Тело модуля
+//-----------
+
+//Кнопка выбора компонента условия
+const CondComponentSelectButton = ({ caption, components = [], onSelect }) => {
+ //Собственное состояние - элемент привязки меню
+ const [anchorEl, setAnchorEl] = useState(null);
+
+ //При нажатии на кнопку
+ const handleButtonClick = event => setAnchorEl(event.currentTarget);
+
+ //При закрытии меню
+ const handleClose = () => setAnchorEl(null);
+
+ //При нажатии на элемент меню
+ const handleComponentClick = component => {
+ handleClose();
+ onSelect && onSelect(component.value);
+ };
+
+ //Расчет флага открытия меню
+ const open = Boolean(anchorEl);
+
+ //Формирование представления
+ return (
+ <>
+
+
+ >
+ );
+};
+
+//Контроль свойств - Кнопка выбора компонента условия
+CondComponentSelectButton.propTypes = {
+ caption: PropTypes.string.isRequired,
+ components: PropTypes.arrayOf(
+ PropTypes.shape({
+ type: PropTypes.oneOf(Object.values(COMPONENT_TYPE)).isRequired,
+ title: PropTypes.string.isRequired,
+ name: checkReqStringPropForItem,
+ icon: checkReqStringPropForItem,
+ value: PropTypes.any
+ })
+ ),
+ onSelect: PropTypes.func.isRequired
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { CondComponentSelectButton, COMPONENT_TYPE };
diff --git a/app/panels/query_editor/components/inspector_query_cond/cond_operation_buttons.js b/app/panels/query_editor/components/inspector_query_cond/cond_operation_buttons.js
index 4008995..6fb088e 100644
--- a/app/panels/query_editor/components/inspector_query_cond/cond_operation_buttons.js
+++ b/app/panels/query_editor/components/inspector_query_cond/cond_operation_buttons.js
@@ -17,6 +17,7 @@ import { ButtonGroup, Button } from "@mui/material"; //Интерфейсные
//Кнопки операций условий отбора
const CondOperationButtons = ({ buttons = [], onClick }) => {
+ //Формирование представления
return (
{buttons.map((button, i) => (
diff --git a/app/panels/query_editor/components/inspector_query_cond/inspector_query_cond.js b/app/panels/query_editor/components/inspector_query_cond/inspector_query_cond.js
index acee7e8..6e35224 100644
--- a/app/panels/query_editor/components/inspector_query_cond/inspector_query_cond.js
+++ b/app/panels/query_editor/components/inspector_query_cond/inspector_query_cond.js
@@ -14,6 +14,8 @@ import { BUTTONS } from "../../../../../app.text"; //Общие текстовы
import { STYLES as COMMON_STYLES } from "../../../../components/editors/p8p_editors_common"; //Общие ресурсы редаторов
import { useQueryConditions } from "./hooks"; //Хуки для работы с условиями отбора
import { QueryCondDialog } from "./query_cond_dialog"; //Диалог настройки условий отбора
+import { ENTITY_SHAPE } from "../entity/entity"; //Описание сущности
+import { ARGUMENT_SHAPE } from "../argument/argument"; //Описание аргумента запроса
//---------
//Константы
@@ -39,7 +41,7 @@ const STYLES = {
//-----------
//Компонент инспектора - Условия отбора запроса
-const InspectorQueryConditions = ({ query, cond, onOptionsChanged }) => {
+const InspectorQueryConditions = ({ query, cond, entities = [], args = [], onOptionsChanged }) => {
//Собственное состояние - отображение диалога настройки условий отбора
const [openQueryCondDialog, setOpenQueryCondDialog] = useState(false);
@@ -68,7 +70,9 @@ const InspectorQueryConditions = ({ query, cond, onOptionsChanged }) => {
//Формирование представления
return (
<>
- {openQueryCondDialog && }
+ {openQueryCondDialog && (
+
+ )}
{configured && (
@@ -93,6 +97,8 @@ const InspectorQueryConditions = ({ query, cond, onOptionsChanged }) => {
InspectorQueryConditions.propTypes = {
query: PropTypes.number.isRequired,
cond: PropTypes.string,
+ entities: PropTypes.arrayOf(ENTITY_SHAPE),
+ args: PropTypes.arrayOf(ARGUMENT_SHAPE),
onOptionsChanged: PropTypes.func
};
diff --git a/app/panels/query_editor/components/inspector_query_cond/query_cond_dialog.js b/app/panels/query_editor/components/inspector_query_cond/query_cond_dialog.js
index 8874058..fab290e 100644
--- a/app/panels/query_editor/components/inspector_query_cond/query_cond_dialog.js
+++ b/app/panels/query_editor/components/inspector_query_cond/query_cond_dialog.js
@@ -7,30 +7,46 @@
//Подключение библиотек
//---------------------
-import React, { useState, useRef } from "react"; //Классы React
+import React, { useState, useRef, useEffect } from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента
-import { Stack, TextField } from "@mui/material"; //Интерфейсные компоненты MUI
+import { Button, Stack, TextField } from "@mui/material"; //Интерфейсные компоненты MUI
import { P8PDialog } from "../../../../components/p8p_dialog"; //Типовой диалог
+import { DATA_TYPE_ICON } from "../../common"; //Общие ресурсы и константы редактора запросов
+import { ENTITY_SHAPE } from "../entity/entity"; //Описание сущности
+import { ARGUMENT_SHAPE } from "../argument/argument"; //Описание аргумента запроса
import { CondOperationButtons } from "./cond_operation_buttons"; //Кнопки операций условия отбора
+import { CondComponentSelectButton, COMPONENT_TYPE } from "./cond_component_select_button"; //Кнопка выбора компонента условия
+
+//---------
+//Константы
+//---------
+
+//Иконки
+const ICONS = { ...DATA_TYPE_ICON, DEFAULT: "category" };
+
+//Стили
+const STYLE = {
+ BUTTONS_STACK: { width: "550px" }
+};
//-----------
//Тело модуля
//-----------
//Диалог настройки условий отбора запроса
-const QueryCondDialog = ({ cond, onOk, onCancel }) => {
+const QueryCondDialog = ({ cond, entities, args, onOk, onCancel }) => {
//Собственное состояние - условия отбора
const [conditions, setConditions] = useState(cond || "");
//Ссылка на элемент ввода условия
- const condInputRef = useRef(null);
+ const coditionInputRef = useRef(null);
//Перемещение курсора в конец поля ввода условия
const moveCondCursorToEnd = () => {
- if (condInputRef.current) {
- const length = condInputRef.current.value.length;
- condInputRef.current.setSelectionRange(length, length);
- condInputRef.current.focus();
+ if (coditionInputRef.current) {
+ const length = coditionInputRef.current.value.length;
+ coditionInputRef.current.setSelectionRange(length, length);
+ coditionInputRef.current.focus();
}
};
@@ -43,16 +59,79 @@ const QueryCondDialog = ({ cond, onOk, onCancel }) => {
//При изменении условия через компонент
const handleChange = e => setConditions(e.target.value);
+ //При выборе компонента условия
+ const handleComponentSelected = value => {
+ setConditions(pv => pv + value);
+ moveCondCursorToEnd();
+ };
+
//При нажатии на кнопку операции
const handleOperationButtonClick = value => {
setConditions(pv => pv + value);
moveCondCursorToEnd();
};
+ //При нажатии на кнопку очистки условий
+ const handleClearClick = () => setConditions("");
+
+ //При подмонтировании компонента
+ useEffect(() => {
+ //Перевод курсора в конец обёрнут в setTimeout по тому, что Input подмонтируется позднее диалога (он внутри children)
+ setTimeout(moveCondCursorToEnd, 100);
+ }, []);
+
+ //Доступные атрибуты сущностей запроса
+ const entsAttributes =
+ entities && entities?.length > 0
+ ? entities.reduce(
+ (components, e) => [
+ ...components,
+ { type: COMPONENT_TYPE.DIVIDER, title: e.title },
+ ...e.attrs.map(a => ({
+ type: COMPONENT_TYPE.ITEM,
+ title: a.title,
+ name: a.name,
+ value: a.id,
+ icon: ICONS[a.dataType] || ICONS.DEFAULT
+ }))
+ ],
+ []
+ )
+ : [];
+ const entsAttributesExists = entsAttributes.find(a => a.type === COMPONENT_TYPE.ITEM) ? true : false;
+
+ //Доступные аргументы запроса
+ const queryArguments =
+ args && args?.length > 0
+ ? args.reduce(
+ (components, a) => [
+ ...components,
+ {
+ type: COMPONENT_TYPE.ITEM,
+ title: a.title,
+ name: a.name,
+ value: `:${a.name}`,
+ icon: ICONS[a.dataType] || ICONS.DEFAULT
+ }
+ ],
+ []
+ )
+ : [];
+ const queryArgumentsExists = queryArguments.length > 0 ? true : false;
+
//Генерация содержимого
return (
-
+
+ {entsAttributesExists && (
+
+ )}
+ {queryArgumentsExists && (
+
+ )}
+
+
+
{
/>
{
QueryCondDialog.propTypes = {
cond: PropTypes.string,
onOk: PropTypes.func,
+ entities: PropTypes.arrayOf(ENTITY_SHAPE),
+ args: PropTypes.arrayOf(ARGUMENT_SHAPE),
onCancel: PropTypes.func
};