ЦИТК-979 - Настройка запроса - условия отбора (сервер и клиент)

This commit is contained in:
Mikhail Chechnev 2025-08-22 18:08:45 +03:00
parent eef77cbd2d
commit b09ae4d83b
6 changed files with 321 additions and 1 deletions

View File

@ -13,10 +13,11 @@ import { P8PEditorBox } from "../../../../components/editors/p8p_editor_box"; //
import { P8PEditorSubHeader } from "../../../../components/editors/p8p_editor_sub_header"; //Подзаголовок группы параметров редактора import { P8PEditorSubHeader } from "../../../../components/editors/p8p_editor_sub_header"; //Подзаголовок группы параметров редактора
import { ENTITY_DATA_SHAPE } from "../entity/entity"; //Описание сущности import { ENTITY_DATA_SHAPE } from "../entity/entity"; //Описание сущности
import { RELATION_DATA_SHAPE } from "../relation/relation"; //Описание связи import { RELATION_DATA_SHAPE } from "../relation/relation"; //Описание связи
import { ARGUMENT_SHAPE } from "../argument/argument"; //Описание аргумента запроса
import { InspectorQueryArguments } from "../inspector_query_args/inspector_query_args"; //Управление аргументами запроса import { InspectorQueryArguments } from "../inspector_query_args/inspector_query_args"; //Управление аргументами запроса
import { InspectorQueryConditions } from "../inspector_query_cond/inspector_query_cond"; //Управление условиями отбора запроса
import { InspectorQueryEntities } from "../inspector_query_ents/inspector_query_ents"; //Управление сущностями запроса import { InspectorQueryEntities } from "../inspector_query_ents/inspector_query_ents"; //Управление сущностями запроса
import { InspectorQueryRelations } from "../inspector_query_rls/inspector_query_rls"; //Управление связями запроса import { InspectorQueryRelations } from "../inspector_query_rls/inspector_query_rls"; //Управление связями запроса
import { ARGUMENT_SHAPE } from "../argument/argument"; //Аргументы запроса
//----------- //-----------
//Тело модуля //Тело модуля
@ -33,6 +34,7 @@ const Inspector = ({ query, entity, relation, args = [], cond = null, onOptionsC
<P8PEditorSubHeader title={"Аргументы"} /> <P8PEditorSubHeader title={"Аргументы"} />
<InspectorQueryArguments query={query} args={args} onOptionsChanged={handleOptionsChanged} /> <InspectorQueryArguments query={query} args={args} onOptionsChanged={handleOptionsChanged} />
<P8PEditorSubHeader title={"Условия отбора"} /> <P8PEditorSubHeader title={"Условия отбора"} />
<InspectorQueryConditions query={query} cond={cond} onOptionsChanged={handleOptionsChanged} />
<P8PEditorSubHeader title={"Сущности"} /> <P8PEditorSubHeader title={"Сущности"} />
<InspectorQueryEntities query={query} entity={entity} onOptionsChanged={handleOptionsChanged} /> <InspectorQueryEntities query={query} entity={entity} onOptionsChanged={handleOptionsChanged} />
{relation && ( {relation && (

View File

@ -0,0 +1,43 @@
/*
Парус 8 - Панели мониторинга - Редактор запросов
Компонент: Кнопки операций условий отбора
*/
//---------------------
//Подключение библиотек
//---------------------
import React from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента
import { ButtonGroup, Button } from "@mui/material"; //Интерфейсные компоненты MUI
//-----------
//Тело модуля
//-----------
//Кнопки операций условий отбора
const CondOperationButtons = ({ buttons = [], onClick }) => {
return (
<ButtonGroup size={"small"} variant={"outlined"}>
{buttons.map((button, i) => (
<Button key={i} onClick={() => onClick(button.value)} title={button.title}>
{button.caption}
</Button>
))}
</ButtonGroup>
);
};
//Контроль свойств - Кнопки операций условий отбора
CondOperationButtons.propTypes = {
buttons: PropTypes.arrayOf(
PropTypes.shape({ caption: PropTypes.string.isRequired, title: PropTypes.string.isRequired, value: PropTypes.string.isRequired })
),
onClick: PropTypes.func.isRequired
};
//----------------
//Интерфейс модуля
//----------------
export { CondOperationButtons };

View File

@ -0,0 +1,38 @@
/*
Парус 8 - Панели мониторинга - Редактор запросов
Пользовательские хуки для работы с условиями отбора запроса
*/
//---------------------
//Подключение библиотек
//---------------------
import { useContext, useCallback } from "react"; //Классы React
import { BackEndСtx } from "../../../../context/backend"; //Контекст взаимодействия с сервером
//-----------
//Тело модуля
//-----------
//Работа с условиями отбора запроса
const useQueryConditions = query => {
//Подключение к контексту взаимодействия с сервером
const { executeStored } = useContext(BackEndСtx);
//Установка условий отбора в запросе
const setCond = useCallback(
async cond => {
await executeStored({ stored: "PKG_P8PANELS_QE.QUERY_OPT_COND_SET", args: { NRN: query, SCOND: cond }, loader: false });
},
[query, executeStored]
);
//Возвращаем интерфейс хука
return { setCond };
};
//----------------
//Интерфейс модуля
//----------------
export { useQueryConditions };

View File

@ -0,0 +1,103 @@
/*
Парус 8 - Панели мониторинга - Редактор запросов
Компонент инспектора - Условия отбора запроса
*/
//---------------------
//Подключение библиотек
//---------------------
import React, { useState } from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента
import { Card, CardActionArea, CardContent, Stack, Chip, Icon, Button } from "@mui/material"; //Интерфейсные элементы
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"; //Диалог настройки условий отбора
//---------
//Константы
//---------
//Общий стиль многострочного компонента "Chip"
const COMMON_MULTILINE_CHIP = COMMON_STYLES.CHIP(true, true);
//Стили
const STYLES = {
COND_CHIP: {
...COMMON_MULTILINE_CHIP,
"& .MuiChip-label": {
...COMMON_MULTILINE_CHIP["& .MuiChip-label"],
paddingTop: "5px",
paddingBottom: "5px"
}
}
};
//-----------
//Тело модуля
//-----------
//Компонент инспектора - Условия отбора запроса
const InspectorQueryConditions = ({ query, cond, onOptionsChanged }) => {
//Собственное состояние - отображение диалога настройки условий отбора
const [openQueryCondDialog, setOpenQueryCondDialog] = useState(false);
//Работа со связями на сервере
const { setCond } = useQueryConditions(query);
//Уведомление родителя об изменении свойств
const notifyOptionsChanged = () => onOptionsChanged && onOptionsChanged();
//При нажатии на кнопку настройки условий отбора
const handleSetup = () => setOpenQueryCondDialog(true);
//При закрытии диалога изменений условий отбора по "ОК"
const handleQueryCondDialogOk = async cond => {
await setCond(cond);
notifyOptionsChanged();
setOpenQueryCondDialog(false);
};
//При закрытии диалога изменений условий отбора по "Отмена"
const handleQueryCondDialogCancel = () => setOpenQueryCondDialog(false);
//Расчет флага сконфигурированности
const configured = cond ? true : false;
//Формирование представления
return (
<>
{openQueryCondDialog && <QueryCondDialog cond={cond} onOk={handleQueryCondDialogOk} onCancel={handleQueryCondDialogCancel} />}
{configured && (
<Card variant={"outlined"}>
<CardActionArea onClick={handleSetup}>
<CardContent>
<Stack direction={"column"} spacing={1}>
<Chip sx={STYLES.COND_CHIP} label={cond} />
</Stack>
</CardContent>
</CardActionArea>
</Card>
)}
{!configured && (
<Button startIcon={<Icon>build</Icon>} onClick={handleSetup}>
{BUTTONS.CONFIG}
</Button>
)}
</>
);
};
//Контроль свойств компонента - Компонент инспектора - Условия отбора запроса
InspectorQueryConditions.propTypes = {
query: PropTypes.number.isRequired,
cond: PropTypes.string,
onOptionsChanged: PropTypes.func
};
//----------------
//Интерфейс модуля
//----------------
export { InspectorQueryConditions };

View File

@ -0,0 +1,105 @@
/*
Парус 8 - Панели мониторинга - Редактор запросов
Компонент: Диалог настройки условий отбора запроса
*/
//---------------------
//Подключение библиотек
//---------------------
import React, { useState, useRef } from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента
import { Stack, TextField } from "@mui/material"; //Интерфейсные компоненты MUI
import { P8PDialog } from "../../../../components/p8p_dialog"; //Типовой диалог
import { CondOperationButtons } from "./cond_operation_buttons"; //Кнопки операций условия отбора
//-----------
//Тело модуля
//-----------
//Диалог настройки условий отбора запроса
const QueryCondDialog = ({ cond, onOk, onCancel }) => {
//Собственное состояние - условия отбора
const [conditions, setConditions] = useState(cond || "");
//Ссылка на элемент ввода условия
const condInputRef = useRef(null);
//Перемещение курсора в конец поля ввода условия
const moveCondCursorToEnd = () => {
if (condInputRef.current) {
const length = condInputRef.current.value.length;
condInputRef.current.setSelectionRange(length, length);
condInputRef.current.focus();
}
};
//Нажатие на кнопку "ОК"
const handleOk = () => onOk && onOk(conditions);
//Нажатие на кнопку "Отмена"
const handleCancel = () => onCancel && onCancel();
//При изменении условия через компонент
const handleChange = e => setConditions(e.target.value);
//При нажатии на кнопку операции
const handleOperationButtonClick = value => {
setConditions(pv => pv + value);
moveCondCursorToEnd();
};
//Генерация содержимого
return (
<P8PDialog title={`Условия отбора запроса`} onOk={handleOk} onCancel={handleCancel}>
<Stack sx={{ width: "550px" }} direction={"row"} spacing={1} pb={1}>
<CondOperationButtons
buttons={[
{ caption: "=", title: "Равно", value: " = " },
{ caption: "<>", title: "Не равно", value: " <> " },
{ caption: ">", title: "Больше", value: " > " },
{ caption: ">=", title: "Больше или равно", value: " >= " },
{ caption: "<", title: "Меньше", value: " < " },
{ caption: "<=", title: "Меньше или равно", value: " <= " },
{ caption: "(", title: "Открывающая скобка", value: "(" },
{ caption: ")", title: "Закрывающая скобка", value: ")" }
]}
onClick={handleOperationButtonClick}
/>
<CondOperationButtons
buttons={[
{ caption: "И", title: "Логическое умножение (конъюнкция)", value: " and " },
{ caption: "ИЛИ", title: "Логическое сложение (дизъюнкция)", value: " or " },
{ caption: "НЕ", title: "Отрицание (инверсия)", value: " not " },
{ caption: "B", title: "Входит в диапазон", value: " in (0, 1, ...) " }
]}
onClick={handleOperationButtonClick}
/>
</Stack>
<TextField
inputRef={condInputRef}
focused={true}
autoFocus
value={conditions}
placeholder={"Настройте условия отбора..."}
multiline
minRows={10}
fullWidth
onChange={handleChange}
/>
</P8PDialog>
);
};
//Контроль свойств - Диалог настройки условий отбора запроса
QueryCondDialog.propTypes = {
cond: PropTypes.string,
onOk: PropTypes.func,
onCancel: PropTypes.func
};
//----------------
//Интерфейс модуля
//----------------
export { QueryCondDialog };

View File

@ -130,6 +130,13 @@ create or replace package PKG_P8PANELS_QE as
NRN in number, -- Рег. номер запроса NRN in number, -- Рег. номер запроса
SNAME in varchar2 -- Имя SNAME in varchar2 -- Имя
); );
/* Установка условий отбора запроса */
procedure QUERY_OPT_COND_SET
(
NRN in number, -- Рег. номер запроса
SCOND in varchar2 -- Условия отбора
);
end PKG_P8PANELS_QE; end PKG_P8PANELS_QE;
/ /
@ -536,5 +543,27 @@ create or replace package body PKG_P8PANELS_QE as
PKG_P8PANELS_QE_BASE.QUERY_OPT_SET(NRN => RQ.RN, ROPT => ROPT); PKG_P8PANELS_QE_BASE.QUERY_OPT_SET(NRN => RQ.RN, ROPT => ROPT);
end QUERY_OPT_ARG_REMOVE; end QUERY_OPT_ARG_REMOVE;
/* Установка условий отбора запроса */
procedure QUERY_OPT_COND_SET
(
NRN in number, -- Рег. номер запроса
SCOND in varchar2 -- Условия отбора
)
is
RQ P8PNL_QE_QUERY%rowtype; -- Запись запроса
ROPT PKG_P8PANELS_QE_BASE.TOPT; -- Настройка запроса
begin
/* Провим права доступа */
PKG_P8PANELS_QE_BASE.QUERY_ACCESS_MODIFY(NRN => NRN, SUSER => UTILIZER());
/* Читаем запись запроса */
RQ := PKG_P8PANELS_QE_BASE.QUERY_GET(NRN => NRN);
/* Читаем текущую настройку */
ROPT := PKG_P8PANELS_QE_BASE.QUERY_OPT_GET(COPT => RQ.OPT);
/* Установим новые условия отбора */
ROPT.SCOND := SCOND;
/* Сохраняем обновленную настройку */
PKG_P8PANELS_QE_BASE.QUERY_OPT_SET(NRN => RQ.RN, ROPT => ROPT);
end QUERY_OPT_COND_SET;
end PKG_P8PANELS_QE; end PKG_P8PANELS_QE;
/ /