ЦИТК-1037 - Добавлены универсальные хуки для компонентов высокого уровня #43
351
README.md
351
README.md
@ -1459,6 +1459,55 @@ const MyPanel = () => {
|
|||||||
`PKG_P8PANELS_VISUAL.TDG_FILTERS_SET_QUERY` - процедура, вспомогательная утилита, производит вызов указанной серверной процедуры отбора с учётом переданных переменных окружения и значений в `RFILTERS`\
|
`PKG_P8PANELS_VISUAL.TDG_FILTERS_SET_QUERY` - процедура, вспомогательная утилита, производит вызов указанной серверной процедуры отбора с учётом переданных переменных окружения и значений в `RFILTERS`\
|
||||||
`PKG_P8PANELS_VISUAL.UTL_ROWS_LIMITS_CALC` - процедура, вспомогательная утилита, служит для конвертации номера страницы данных и размера страницы данных в границы диапазона строк выборки (как правило, клиентскому приложению удобнее прислать на сервер текущий номер страницы и её размер, в то время к в запросах, для выборки, удобнее применять границы диапазонов строк)
|
`PKG_P8PANELS_VISUAL.UTL_ROWS_LIMITS_CALC` - процедура, вспомогательная утилита, служит для конвертации номера страницы данных и размера страницы данных в границы диапазона строк выборки (как правило, клиентскому приложению удобнее прислать на сервер текущий номер страницы и её размер, в то время к в запросах, для выборки, удобнее применять границы диапазонов строк)
|
||||||
|
|
||||||
|
**Универсальный хук**
|
||||||
|
|
||||||
|
Для компонента существует универсальный хук `useP8PDataGrid`, который помогает автоматизировать и скрыть логику получения/обновления данных для таблицы. Использование универсального хука представлено в примере использования `P8PDataGrid` ниже.
|
||||||
|
|
||||||
|
Для использования универсального хука на панели его необходимо импортировать:
|
||||||
|
|
||||||
|
```
|
||||||
|
import {useP8PDataGrid} from "../../components/p8p_data_grid";
|
||||||
|
|
||||||
|
const MyPanel = () => {
|
||||||
|
const { dataGrid, isDataLoaded } = useP8PDataGrid({ stored: "ХРАНИМЫЙ_ОБЪЕКТ" });
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Входные параметры:**\
|
||||||
|
`stored` - обязательный, строка, имя исполняемого хранимого объекта (для пакетных - "ПАКЕТ.ОБЪЕКТ")\
|
||||||
|
`respArg` - необязательный, строка, имя выходного параметра исполняемого объекта, значение которого необходимо вернуть как данные ответа, по умолчанию - `COUT`\
|
||||||
|
`contentNodeName` - необязательный, строка, имя узла, содержащего информацию о таблице, по умолчанию `XDATA_GRID`\
|
||||||
|
`filtersNodeName` - необязательный, строка, имя узла, содержащего информацию о фильтрах, отправляемых на сервер, по умолчанию `filters`. При использовании в хранимом объекте стандартной функции `PKG_P8PANELS_VISUAL.TDG_FILTERS_FROM_XML` для десериализации данных важно, чтобы данный параметр имел значение `filters`\
|
||||||
|
`ordersNodeName` - необязательный, строка, имя узла, содержащего информацию о сортировках, отправляемых на сервер, по умолчанию `orders`. При использовании в хранимом объекте стандартной функции `PKG_P8PANELS_VISUAL.TDG_ORDERS_FROM_XML` для десериализации данных важно, чтобы данный параметр имел значение `orders`\
|
||||||
|
`pageSize` - необязательный, число, объем выборки данных на страницу или на догрузку, по умолчанию `10`. Передается в параметр `NPAGE_SIZE` хранимого объекта из переданного свойства `stored`\
|
||||||
|
`reloadDef` - необязательный, булево, определяет необходимость включения описания колонок таблицы в ответ, по умолчанию `false`. Передается в параметр `NINCLUDE_DEF` хранимого объекта из переданного свойства `stored`. При значении `true` соответствующий параметр `NINCLUDE_DEF` будет всегда равен `1`, в ином случае при первом обращении к хранимому объекту передается `1`, а в последующих `0`\
|
||||||
|
`initFilters` - необязательный, массив, начальное состояние фильтров таблицы, содержит объекты вида `{name: <НАИМЕНОВАНИЕ_КОЛОНКИ>, from: <НАЧАЛО_ДИАПАЗОНА_ЗНАЧЕНИЙ_ФИЛЬТРА>, to: <ОКОНЧАНИЕ_ДИАПАЗОНА_ЗНАЧЕНИЙ_ФИЛЬТРА>}`\
|
||||||
|
`initOrders` - необязательный, массив, начальное состояние сортировок таблицы, содержит объекты вида `{name: <НАИМЕНОВАНИЕ_КОЛОНКИ>, direction: <ASC|DESC>}`\
|
||||||
|
`storedArgs` - необязательный, объект, описание параметров исполняемого хранимого объекта вида `{"ПАРАМЕТР": "ЗНАЧЕНИЕ"|{VALUE: "ЗНАЧЕНИЕ", SDATA_TYPE: SERV_DATA_TYPE_*}}` (если тип данных параметров не указан явно - произойдёт попытка их автоматического определения, с CLOB-параметрами это не всегда может произойти корректно)\
|
||||||
|
`executeStoredArgs` - необязательный, объект, описание параметров функции `executeStored` (исполняющей хранимый объект) вида `{"ПАРАМЕТР": "ЗНАЧЕНИЕ"}`\
|
||||||
|
`allowDataLoad` - необязательный, функция, определяет игнорирование исполнения хранимой процедуры. Должна возвращать булево. Если возвращает `false`, то исполнение хранимой процедуры, независимо от необходимости, полностью игнорируется
|
||||||
|
|
||||||
|
**Результат:** объект для работы с компонентом `P8PDataGrid`. Возвращаемый объект имеет следующие свойства:\
|
||||||
|
`dataGrid` - объект, хранит информацию о таблице, загруженной с помощью хранимой процедуры\
|
||||||
|
`isDataLoaded` - булево, признак успешной загрузки и обработки данных из хранимой процедуры\
|
||||||
|
`isLoading` - булево, признак выполнения загрузки данных из хранимой процедуры\
|
||||||
|
`handleFilterChanged` - функция, изменение состояния фильтров таблицы. Сигнатура функции `f({filters})`, результат функции не интерпретируется\
|
||||||
|
`handleOrderChanged` - функция, изменение состояния сортировок таблицы. Сигнатура функции `f({orders})`, результат функции не интерпретируется\
|
||||||
|
`handlePagesCountChanged` - функция, изменение количества отображаемых страниц данных таблицы. Сигнатура функции `f()`, результат функции не интерпретируется\
|
||||||
|
`handlePageChange` - функция, изменение номера отображаемой страницы таблицы. Сигнатура функции `f({page})`, результат функции не интерпретируется\
|
||||||
|
`doReload` - функция, принудительное обновление данных таблицы. Сигнатура функции `f()`, результат функции не интерпретируется
|
||||||
|
|
||||||
|
**Особенности хранимых объектов**\
|
||||||
|
При использовании универсального хука для хранимой процедуры существуют параметры "по умолчанию", в которые отправляются соответствующие данные:\
|
||||||
|
`CFILTERS` - необязательный, `clob`, xml-представление фильтров таблицы\
|
||||||
|
`CORDERS` - необязательный, `clob`, xml-представление сортировок таблицы\
|
||||||
|
`NPAGE_NUMBER` - необязательный, `number`, номер страницы\
|
||||||
|
`NPAGE_SIZE` - необязательный, `number`, объем выборки данных на страницу или на догрузку, значение параметра `pageSize` универсального хука `useP8PDataGrid`\
|
||||||
|
`NINCLUDE_DEF` - необязательный, `number`, признак включения описания колонок таблицы в ответ, зависит от параметра `reloadDef` универсального хука `useP8PDataGrid`. При `reloadDef` = `true` всегда передается `1`, в ином случае при первом вызове `1`, в остальных `0`
|
||||||
|
|
||||||
|
Данные параметры не являются обязательными. При их отсутствии в хранимой процедуре они будут проигнорированы.
|
||||||
|
|
||||||
**Пример**
|
**Пример**
|
||||||
|
|
||||||
Код на стороне сервера БД (хранимая процедура в клиентском пакете `PKG_P8PANELS_SAMPLES`):
|
Код на стороне сервера БД (хранимая процедура в клиентском пакете `PKG_P8PANELS_SAMPLES`):
|
||||||
@ -1880,6 +1929,35 @@ const MyPanel = () => {
|
|||||||
`PKG_P8PANELS_VISUAL.TCHART_ADD_DATASET` - процедура, добавляет переданное объектное представление серии данных в указанный объект описания графика\
|
`PKG_P8PANELS_VISUAL.TCHART_ADD_DATASET` - процедура, добавляет переданное объектное представление серии данных в указанный объект описания графика\
|
||||||
`PKG_P8PANELS_VISUAL.TCHART_TO_XML` - функция, производит сериализацию объекта, описывающего график, в специальный XML-формат, корректно интерпретируемый клиентским компонентом `P8PChart` при передаче в WEB-приложение
|
`PKG_P8PANELS_VISUAL.TCHART_TO_XML` - функция, производит сериализацию объекта, описывающего график, в специальный XML-формат, корректно интерпретируемый клиентским компонентом `P8PChart` при передаче в WEB-приложение
|
||||||
|
|
||||||
|
**Универсальный хук**
|
||||||
|
|
||||||
|
Для компонента существует универсальный хук `useP8PChart`, который помогает автоматизировать и скрыть логику получения/обновления данных для графика. Использование универсального хука представлено в примере использования `P8PChart` ниже.
|
||||||
|
|
||||||
|
Для использования универсального хука на панели его необходимо импортировать:
|
||||||
|
|
||||||
|
```
|
||||||
|
import {useP8PChart} from "../../components/p8p_chart";
|
||||||
|
|
||||||
|
const MyPanel = () => {
|
||||||
|
const { chart, isDataLoaded } = useP8PChart({ stored: "ХРАНИМЫЙ_ОБЪЕКТ" });
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Входные параметры:**\
|
||||||
|
`stored` - обязательный, строка, имя исполняемого хранимого объекта (для пакетных - "ПАКЕТ.ОБЪЕКТ")\
|
||||||
|
`respArg` - необязательный, строка, имя выходного параметра исполняемого объекта, значение которого необходимо вернуть как данные ответа, по умолчанию - `COUT`\
|
||||||
|
`contentNodeName` - необязательный, строка, имя узла, содержащего информацию о графике, по умолчанию `XCHART`\
|
||||||
|
`storedArgs` - необязательный, объект, описание параметров исполняемого хранимого объекта вида `{"ПАРАМЕТР": "ЗНАЧЕНИЕ"|{VALUE: "ЗНАЧЕНИЕ", SDATA_TYPE: SERV_DATA_TYPE_*}}` (если тип данных параметров не указан явно - произойдёт попытка их автоматического определения, с CLOB-параметрами это не всегда может произойти корректно)\
|
||||||
|
`executeStoredArgs` - необязательный, объект, описание параметров функции `executeStored` (исполняющей хранимый объект) вида `{"ПАРАМЕТР": "ЗНАЧЕНИЕ"}`\
|
||||||
|
`allowDataLoad` - необязательный, функция, определяет игнорирование исполнения хранимой процедуры. Должна возвращать булево. Если возвращает `false`, то исполнение хранимой процедуры, независимо от необходимости, полностью игнорируется
|
||||||
|
|
||||||
|
**Результат:** объект для работы с компонентом `P8PChart`. Возвращаемый объект имеет следующие свойства:\
|
||||||
|
`chart` - объект, хранит информацию о графике, загруженном с помощью хранимой процедуры\
|
||||||
|
`isDataLoaded` - булево, признак успешной загрузки и обработки данных из хранимой процедуры\
|
||||||
|
`isLoading` - булево, признак выполнения загрузки данных из хранимой процедуры\
|
||||||
|
`doReload` - функция, принудительное обновление данных графика. Сигнатура функции `f()`, результат функции не интерпретируется
|
||||||
|
|
||||||
**Пример**
|
**Пример**
|
||||||
|
|
||||||
Код на стороне сервера БД (хранимая процедура в клиентском пакете `PKG_P8PANELS_SAMPLES`):
|
Код на стороне сервера БД (хранимая процедура в клиентском пакете `PKG_P8PANELS_SAMPLES`):
|
||||||
@ -1937,37 +2015,36 @@ const MyPanel = () => {
|
|||||||
Код панели на стороне клиента (WEB-приложения):
|
Код панели на стороне клиента (WEB-приложения):
|
||||||
|
|
||||||
```
|
```
|
||||||
import React, { useState, useContext, useCallback, useEffect } from "react"; //Классы React
|
import React, { useContext } from "react"; //Классы React
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import { Typography, Grid, Paper } from "@mui/material"; //Интерфейсные элементы
|
import { Typography, Grid, Paper } from "@mui/material"; //Интерфейсные элементы
|
||||||
import { P8PChart } from "../../components/p8p_chart"; //График
|
import { P8PChart, useP8PChart } from "../../components/p8p_chart"; //График
|
||||||
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
|
||||||
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
//Стили
|
//Стили
|
||||||
const STYLES = {
|
const STYLES = {
|
||||||
CONTAINER: { textAlign: "center", paddingTop: "20px" },
|
CONTAINER: { textAlign: "center", paddingTop: "20px" },
|
||||||
TITLE: { paddingBottom: "15px" },
|
TITLE: { paddingBottom: "15px" },
|
||||||
CHART: { minWidth: "80vw", maxHeight: "80vw", display: "flex", justifyContent: "center" },
|
CHART: { minWidth: "65vw", maxHeight: "65vw", display: "flex", justifyContent: "center" },
|
||||||
CHART_PAPER: { padding: "25px" }
|
CHART_PAPER: { padding: "25px" }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
//Пример: Графики "P8PChart"
|
//Пример: Графики "P8PChart"
|
||||||
const Chart = ({ title }) => {
|
const Chart = ({ title }) => {
|
||||||
//Собственное состояние - график
|
//Собственное состояние - график
|
||||||
const [chart, setChart] = useState({ loaded: false });
|
const { chart, isDataLoaded } = useP8PChart({ stored: "PKG_P8PANELS_SAMPLES.CHART" });
|
||||||
|
|
||||||
//Подключение к контексту взаимодействия с сервером
|
|
||||||
const { executeStored } = useContext(BackEndСtx);
|
|
||||||
|
|
||||||
//Подключение к контексту приложения
|
//Подключение к контексту приложения
|
||||||
const { pOnlineShowUnit } = useContext(ApplicationСtx);
|
const { pOnlineShowUnit } = useContext(ApplicationСtx);
|
||||||
|
|
||||||
//Загрузка данных графика с сервера
|
|
||||||
const loadChart = useCallback(async () => {
|
|
||||||
const chart = await executeStored({ stored: "PKG_P8PANELS_SAMPLES.CHART", respArg: "COUT" });
|
|
||||||
setChart(pv => ({ ...pv, loaded: true, ...chart.XCHART }));
|
|
||||||
}, [executeStored]);
|
|
||||||
|
|
||||||
//Отработка нажатия на график
|
//Отработка нажатия на график
|
||||||
const handleChartClick = ({ item }) => {
|
const handleChartClick = ({ item }) => {
|
||||||
pOnlineShowUnit({
|
pOnlineShowUnit({
|
||||||
@ -1976,12 +2053,6 @@ const Chart = ({ title }) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
//При подключении к странице
|
|
||||||
useEffect(() => {
|
|
||||||
loadChart();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
//Генерация содержимого
|
//Генерация содержимого
|
||||||
return (
|
return (
|
||||||
<div style={STYLES.CONTAINER}>
|
<div style={STYLES.CONTAINER}>
|
||||||
@ -1991,7 +2062,7 @@ const Chart = ({ title }) => {
|
|||||||
<Grid container spacing={0} pt={5} direction="column" alignItems="center">
|
<Grid container spacing={0} pt={5} direction="column" alignItems="center">
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<Paper elevation={6} sx={STYLES.CHART_PAPER}>
|
<Paper elevation={6} sx={STYLES.CHART_PAPER}>
|
||||||
{chart.loaded ? <P8PChart {...chart} onClick={handleChartClick} style={STYLES.CHART} /> : null}
|
{isDataLoaded ? <P8PChart {...chart} onClick={handleChartClick} style={STYLES.CHART} /> : null}
|
||||||
</Paper>
|
</Paper>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
@ -2093,6 +2164,35 @@ const MyPanel = () => {
|
|||||||
`PKG_P8PANELS_VISUAL.TGANTT_ADD_TASK` - процедура, добавляет, к указанному объекту описания диаграммы Ганта, новую задачу, ранее описанную через `TGANTT_TASK_MAKE`\
|
`PKG_P8PANELS_VISUAL.TGANTT_ADD_TASK` - процедура, добавляет, к указанному объекту описания диаграммы Ганта, новую задачу, ранее описанную через `TGANTT_TASK_MAKE`\
|
||||||
`PKG_P8PANELS_VISUAL.TGANTT_TO_XML` - функция, производит сериализацию объекта, описывающего диаграмму Ганта, в специальный XML-формат, корректно интерпретируемый клиентским компонентом `P8PGantt` при передаче в WEB-приложение
|
`PKG_P8PANELS_VISUAL.TGANTT_TO_XML` - функция, производит сериализацию объекта, описывающего диаграмму Ганта, в специальный XML-формат, корректно интерпретируемый клиентским компонентом `P8PGantt` при передаче в WEB-приложение
|
||||||
|
|
||||||
|
**Универсальный хук**
|
||||||
|
|
||||||
|
Для компонента существует универсальный хук `useP8PGantt`, который помогает автоматизировать и скрыть логику получения/обновления данных для диаграммы Ганта. Использование универсального хука представлено в примере использования `P8PGantt` ниже.
|
||||||
|
|
||||||
|
Для использования универсального хука на панели его необходимо импортировать:
|
||||||
|
|
||||||
|
```
|
||||||
|
import { useP8PGantt } from "../../components/p8p_gantt";
|
||||||
|
|
||||||
|
const MyPanel = () => {
|
||||||
|
const { gantt, isDataLoaded } = useP8PGantt({ stored: "ХРАНИМЫЙ_ОБЪЕКТ" });
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Входные параметры:**\
|
||||||
|
`stored` - обязательный, строка, имя исполняемого хранимого объекта (для пакетных - "ПАКЕТ.ОБЪЕКТ")\
|
||||||
|
`respArg` - необязательный, строка, имя выходного параметра исполняемого объекта, значение которого необходимо вернуть как данные ответа, по умолчанию - `COUT`\
|
||||||
|
`contentNodeName` - необязательный, строка, имя узла, содержащего информацию о диаграмме Ганта, по умолчанию `XGANTT`\
|
||||||
|
`storedArgs` - необязательный, объект, описание параметров исполняемого хранимого объекта вида `{"ПАРАМЕТР": "ЗНАЧЕНИЕ"|{VALUE: "ЗНАЧЕНИЕ", SDATA_TYPE: SERV_DATA_TYPE_*}}` (если тип данных параметров не указан явно - произойдёт попытка их автоматического определения, с CLOB-параметрами это не всегда может произойти корректно)\
|
||||||
|
`executeStoredArgs` - необязательный, объект, описание параметров функции `executeStored` (исполняющей хранимый объект) вида `{"ПАРАМЕТР": "ЗНАЧЕНИЕ"}`\
|
||||||
|
`allowDataLoad` - необязательный, функция, определяет игнорирование исполнения хранимой процедуры. Должна возвращать булево. Если возвращает `false`, то исполнение хранимой процедуры, независимо от необходимости, полностью игнорируется
|
||||||
|
|
||||||
|
**Результат:** объект для работы с компонентом `P8PGantt`. Возвращаемый объект имеет следующие свойства:\
|
||||||
|
`gantt` - объект, хранит информацию о диаграмме Ганта, загруженной с помощью хранимой процедуры\
|
||||||
|
`isDataLoaded` - булево, признак успешной загрузки и обработки данных из хранимой процедуры\
|
||||||
|
`isLoading` - булево, признак выполнения загрузки данных из хранимой процедуры\
|
||||||
|
`doReload` - функция, принудительное обновление данных диаграммы Ганта. Сигнатура функции `f()`, результат функции не интерпретируется
|
||||||
|
|
||||||
**Пример**
|
**Пример**
|
||||||
|
|
||||||
Код на стороне сервера БД (хранимая процедура в клиентском пакете `PKG_P8PANELS_SAMPLES`, требует наличия таблицы `P8PNL_SMPL_GANTT`, см. "db/P8PNL_SMPL_GANTT.sql"):
|
Код на стороне сервера БД (хранимая процедура в клиентском пакете `PKG_P8PANELS_SAMPLES`, требует наличия таблицы `P8PNL_SMPL_GANTT`, см. "db/P8PNL_SMPL_GANTT.sql"):
|
||||||
@ -2163,25 +2263,43 @@ const MyPanel = () => {
|
|||||||
|
|
||||||
```
|
```
|
||||||
import React, { useState, useContext, useCallback, useEffect } from "react"; //Классы React
|
import React, { useState, useContext, useCallback, useEffect } from "react"; //Классы React
|
||||||
import {Typography, Grid, Stack, Icon, Box, FormControlLabel, Checkbox, Card, CardHeader, CardActions, Avatar, CardContent, Button} from "@mui/material"; //Интерфейсные элементы
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import { formatDateJSONDateOnly, formatDateRF } from "../../core/utils"; //Вспомогательные функции
|
import { Typography, Grid, Stack, Icon, FormControlLabel, Checkbox, Card, CardHeader, CardActions, Avatar, CardContent, Button } from "@mui/material"; //Интерфейсные элементы
|
||||||
import { P8PGantt } from "../../components/p8p_gantt"; //Диаграмма Ганта
|
import { formatDateRF, hasValue } from "../../core/utils"; //Вспомогательные функции
|
||||||
|
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
|
||||||
|
import { P8PGantt, useP8PGantt } from "../../components/p8p_gantt"; //Диаграмма Ганта
|
||||||
import { P8P_GANTT_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
import { P8P_GANTT_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
||||||
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
||||||
|
|
||||||
//Высота диаграммы Ганта
|
//---------
|
||||||
const GANTT_HEIGHT = "70vh";
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
//Ширина диаграммы Ганта
|
//Отступ контейнера страницы от заголовка
|
||||||
const GANTT_WIDTH = "98vw";
|
const CONTAINER_PADDING_TOP = "20px";
|
||||||
|
|
||||||
|
//Высота заголовка страницы
|
||||||
|
const TITLE_HEIGHT = "47px";
|
||||||
|
|
||||||
|
//Высота элементов управления
|
||||||
|
const CONTROL_HEIGHT = "42px";
|
||||||
|
|
||||||
//Стили
|
//Стили
|
||||||
const STYLES = {
|
const STYLES = {
|
||||||
CONTAINER: { textAlign: "center", paddingTop: "20px" },
|
CONTAINER: { textAlign: "center", paddingTop: CONTAINER_PADDING_TOP },
|
||||||
TITLE: { paddingBottom: "15px" },
|
TITLE: { paddingBottom: "15px", height: TITLE_HEIGHT },
|
||||||
GANTT_CONTAINER: { height: GANTT_HEIGHT, width: GANTT_WIDTH }
|
CONTROL: { height: CONTROL_HEIGHT },
|
||||||
|
GANTT_CONTAINER: {
|
||||||
|
height: `calc(100vh - ${APP_BAR_HEIGHT} - ${TITLE_HEIGHT} - ${CONTROL_HEIGHT} - ${CONTAINER_PADDING_TOP})`,
|
||||||
|
width: "100vw",
|
||||||
|
paddingTop: "5px"
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//---------------------------------------------
|
||||||
|
//Вспомогательные функции форматирования данных
|
||||||
|
//---------------------------------------------
|
||||||
|
|
||||||
//Формирование значения для колонки "Тип задачи"
|
//Формирование значения для колонки "Тип задачи"
|
||||||
const formatTaskTypeValue = value => {
|
const formatTaskTypeValue = value => {
|
||||||
const [text, icon] = value == 0 ? ["Этап проекта", "check"] : ["Работа проекта", "work_outline"];
|
const [text, icon] = value == 0 ? ["Этап проекта", "check"] : ["Работа проекта", "work_outline"];
|
||||||
@ -2227,52 +2345,41 @@ const taskDialogRenderer = ({ task, close }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
//Пример: Диаграмма Ганта "P8Gantt"
|
//Пример: Диаграмма Ганта "P8Gantt"
|
||||||
const Gantt = ({ title }) => {
|
const Gantt = ({ title }) => {
|
||||||
//Собственное состояние
|
//Собственное состояние - идентификатор данных
|
||||||
const [gantt, setGantt] = useState({
|
const [dataIdent, setDataIdent] = useState(null);
|
||||||
init: false,
|
|
||||||
dataLoaded: false,
|
//Собственное состояние - использование собственного диалога задачи
|
||||||
ident: null,
|
const [isCustomTaskDialog, setIsCustomTaskDialog] = useState(false);
|
||||||
useCustomTaskDialog: false
|
|
||||||
|
//Собственное состояние - диаграмма Ганта
|
||||||
|
const { gantt, isDataLoaded, doReload } = useP8PGantt({
|
||||||
|
stored: "PKG_P8PANELS_SAMPLES.GANTT",
|
||||||
|
storedArgs: { NIDENT: dataIdent },
|
||||||
|
allowDataLoad: () => hasValue(dataIdent)
|
||||||
});
|
});
|
||||||
|
|
||||||
//Подключение к контексту взаимодействия с сервером
|
//Подключение к контексту взаимодействия с сервером
|
||||||
const { executeStored } = useContext(BackEndСtx);
|
const { executeStored } = useContext(BackEndСtx);
|
||||||
|
|
||||||
//Загрузка данных диаграммы с сервера
|
|
||||||
const loadData = useCallback(async () => {
|
|
||||||
const data = await executeStored({
|
|
||||||
stored: "PKG_P8PANELS_SAMPLES.GANTT",
|
|
||||||
args: { NIDENT: gantt.ident },
|
|
||||||
attributeValueProcessor: (name, val) =>
|
|
||||||
name == "numb" ? undefined : ["start", "end"].includes(name) ? formatDateJSONDateOnly(val) : val,
|
|
||||||
respArg: "COUT"
|
|
||||||
});
|
|
||||||
setGantt(pv => ({ ...pv, dataLoaded: true, ...data.XGANTT }));
|
|
||||||
}, [gantt.ident, executeStored]);
|
|
||||||
|
|
||||||
//Инициализация данных диаграммы
|
|
||||||
const initData = useCallback(async () => {
|
|
||||||
if (!gantt.init) {
|
|
||||||
const data = await executeStored({ stored: "PKG_P8PANELS_SAMPLES.GANTT_INIT", args: { NIDENT: gantt.ident } });
|
|
||||||
setGantt(pv => ({ ...pv, init: true, ident: data.NIDENT }));
|
|
||||||
}
|
|
||||||
}, [gantt.init, gantt.ident, executeStored]);
|
|
||||||
|
|
||||||
//Изменение данных диаграммы
|
//Изменение данных диаграммы
|
||||||
const modifyData = useCallback(
|
const modifyData = useCallback(
|
||||||
async ({ rn, start, end }) => {
|
async ({ rn, start, end }) => {
|
||||||
try {
|
try {
|
||||||
await executeStored({
|
await executeStored({
|
||||||
stored: "PKG_P8PANELS_SAMPLES.GANTT_MODIFY",
|
stored: "PKG_P8PANELS_SAMPLES.GANTT_MODIFY",
|
||||||
args: { NIDENT: gantt.ident, NRN: rn, DDATE_FROM: new Date(start), DDATE_TO: new Date(end) }
|
args: { NIDENT: dataIdent, NRN: rn, DDATE_FROM: new Date(start), DDATE_TO: new Date(end) }
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
loadData();
|
doReload();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[gantt.ident, executeStored, loadData]
|
[dataIdent, executeStored, doReload]
|
||||||
);
|
);
|
||||||
|
|
||||||
//Обработка измненения сроков задачи в диаграмме Гантта
|
//Обработка измненения сроков задачи в диаграмме Гантта
|
||||||
@ -2280,13 +2387,13 @@ const Gantt = ({ title }) => {
|
|||||||
if (isMain) modifyData({ rn: task.rn, start, end });
|
if (isMain) modifyData({ rn: task.rn, start, end });
|
||||||
};
|
};
|
||||||
|
|
||||||
//При необходимости обновить данные таблицы
|
|
||||||
useEffect(() => {
|
|
||||||
if (gantt.ident) loadData();
|
|
||||||
}, [gantt.ident, loadData]);
|
|
||||||
|
|
||||||
//При подключении компонента к странице
|
//При подключении компонента к странице
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
//Инициализация данных диаграммы Ганта
|
||||||
|
const initData = async () => {
|
||||||
|
const data = await executeStored({ stored: "PKG_P8PANELS_SAMPLES.GANTT_INIT", args: { NIDENT: null } });
|
||||||
|
setDataIdent(data.NIDENT);
|
||||||
|
};
|
||||||
initData();
|
initData();
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
@ -2299,19 +2406,19 @@ const Gantt = ({ title }) => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
sx={STYLES.CONTROL}
|
sx={STYLES.CONTROL}
|
||||||
control={<Checkbox onChange={() => setGantt(pv => ({ ...pv, useCustomTaskDialog: !pv.useCustomTaskDialog }))} />}
|
control={<Checkbox onChange={() => setIsCustomTaskDialog(!isCustomTaskDialog)} />}
|
||||||
label="Отображать пользовательский диалог задачи"
|
label="Отображать пользовательский диалог задачи"
|
||||||
/>
|
/>
|
||||||
<Grid container direction="column" alignItems="center">
|
<Grid container direction="column" alignItems="center">
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
{gantt.dataLoaded ? (
|
{isDataLoaded ? (
|
||||||
<P8PGantt
|
<P8PGantt
|
||||||
{...P8P_GANTT_CONFIG_PROPS}
|
{...P8P_GANTT_CONFIG_PROPS}
|
||||||
{...gantt}
|
{...gantt}
|
||||||
containerStyle={STYLES.GANTT_CONTAINER}
|
containerStyle={STYLES.GANTT_CONTAINER}
|
||||||
onTaskDatesChange={handleTaskDatesChange}
|
onTaskDatesChange={handleTaskDatesChange}
|
||||||
taskAttributeRenderer={taskAttributeRenderer}
|
taskAttributeRenderer={taskAttributeRenderer}
|
||||||
taskDialogRenderer={gantt.useCustomTaskDialog ? taskDialogRenderer : null}
|
taskDialogRenderer={isCustomTaskDialog ? taskDialogRenderer : null}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</Grid>
|
</Grid>
|
||||||
@ -2548,6 +2655,35 @@ const MyPanel = () => {
|
|||||||
`PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_TASK` - процедура, добавляет, к указанному объекту описания циклограммы, новую задачу, ранее описанную через `TCYCLOGRAM_TASK_MAKE`\
|
`PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_TASK` - процедура, добавляет, к указанному объекту описания циклограммы, новую задачу, ранее описанную через `TCYCLOGRAM_TASK_MAKE`\
|
||||||
`PKG_P8PANELS_VISUAL.TCYCLOGRAM_TO_XML` - функция, производит сериализацию объекта, описывающего циклограмму, в специальный XML-формат, корректно интерпретируемый клиентским компонентом `P8PCyclogram` при передаче в WEB-приложение
|
`PKG_P8PANELS_VISUAL.TCYCLOGRAM_TO_XML` - функция, производит сериализацию объекта, описывающего циклограмму, в специальный XML-формат, корректно интерпретируемый клиентским компонентом `P8PCyclogram` при передаче в WEB-приложение
|
||||||
|
|
||||||
|
**Универсальный хук**
|
||||||
|
|
||||||
|
Для компонента существует универсальный хук `useP8PCyclogram`, который помогает автоматизировать и скрыть логику получения/обновления данных для циклограммы. Использование универсального хука представлено в примере использования `P8PCyclogram` ниже.
|
||||||
|
|
||||||
|
Для использования универсального хука на панели его необходимо импортировать:
|
||||||
|
|
||||||
|
```
|
||||||
|
import { useP8PCyclogram } from "../../components/p8p_cyclogram";
|
||||||
|
|
||||||
|
const MyPanel = () => {
|
||||||
|
const { cyclogram, isDataLoaded } = useP8PCyclogram({ stored: "ХРАНИМЫЙ_ОБЪЕКТ" });
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Входные параметры:**\
|
||||||
|
`stored` - обязательный, строка, имя исполняемого хранимого объекта (для пакетных - "ПАКЕТ.ОБЪЕКТ")\
|
||||||
|
`respArg` - необязательный, строка, имя выходного параметра исполняемого объекта, значение которого необходимо вернуть как данные ответа, по умолчанию - `COUT`\
|
||||||
|
`contentNodeName` - необязательный, строка, имя узла, содержащего информацию о циклограмме, по умолчанию `XCYCLOGRAM`\
|
||||||
|
`storedArgs` - необязательный, объект, описание параметров исполняемого хранимого объекта вида `{"ПАРАМЕТР": "ЗНАЧЕНИЕ"|{VALUE: "ЗНАЧЕНИЕ", SDATA_TYPE: SERV_DATA_TYPE_*}}` (если тип данных параметров не указан явно - произойдёт попытка их автоматического определения, с CLOB-параметрами это не всегда может произойти корректно)\
|
||||||
|
`executeStoredArgs` - необязательный, объект, описание параметров функции `executeStored` (исполняющей хранимый объект) вида `{"ПАРАМЕТР": "ЗНАЧЕНИЕ"}`\
|
||||||
|
`allowDataLoad` - необязательный, функция, определяет игнорирование исполнения хранимой процедуры. Должна возвращать булево. Если возвращает `false`, то исполнение хранимой процедуры, независимо от необходимости, полностью игнорируется
|
||||||
|
|
||||||
|
**Результат:** объект для работы с компонентом `P8PCyclogram`. Возвращаемый объект имеет следующие свойства:\
|
||||||
|
`cyclogram` - объект, хранит информацию о циклограмме, загруженной с помощью хранимой процедуры\
|
||||||
|
`isDataLoaded` - булево, признак успешной загрузки и обработки данных из хранимой процедуры\
|
||||||
|
`isLoading` - булево, признак выполнения загрузки данных из хранимой процедуры\
|
||||||
|
`doReload` - функция, принудительное обновление данных циклограммы. Сигнатура функции `f()`, результат функции не интерпретируется
|
||||||
|
|
||||||
**Пример**
|
**Пример**
|
||||||
|
|
||||||
Код на стороне сервера БД (хранимая процедура в клиентском пакете `PKG_P8PANELS_SAMPLES`, требует наличия таблицы `P8PNL_SMPL_CYCLOGRAM`, см. "db/P8PNL_SMPL_CYCLOGRAM.sql"):
|
Код на стороне сервера БД (хранимая процедура в клиентском пакете `PKG_P8PANELS_SAMPLES`, требует наличия таблицы `P8PNL_SMPL_CYCLOGRAM`, см. "db/P8PNL_SMPL_CYCLOGRAM.sql"):
|
||||||
@ -2875,51 +3011,29 @@ const taskRenderer = ({ task }) => {
|
|||||||
|
|
||||||
//Пример: Циклограмма "P8PCyclogram"
|
//Пример: Циклограмма "P8PCyclogram"
|
||||||
const Cyclogram = ({ title }) => {
|
const Cyclogram = ({ title }) => {
|
||||||
|
//Собственное состояние - идентификатор данных
|
||||||
|
const [dataIdent, setDataIdent] = useState(null);
|
||||||
|
|
||||||
//Собственное состояние
|
//Собственное состояние
|
||||||
const [state, setState] = useState({
|
const { cyclogram, isDataLoaded, doReload } = useP8PCyclogram({
|
||||||
init: false,
|
stored: "PKG_P8PANELS_SAMPLES.CYCLOGRAM",
|
||||||
dataLoaded: false,
|
storedArgs: { NIDENT: dataIdent },
|
||||||
reload: true,
|
allowDataLoad: () => hasValue(dataIdent)
|
||||||
ident: null
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//Подключение к контексту взаимодействия с сервером
|
//Подключение к контексту взаимодействия с сервером
|
||||||
const { executeStored } = useContext(BackEndСtx);
|
const { executeStored } = useContext(BackEndСtx);
|
||||||
|
|
||||||
//При необходимости перезагрузки
|
|
||||||
const handleReload = () => {
|
|
||||||
setState(pv => ({ ...pv, reload: true }));
|
|
||||||
};
|
|
||||||
|
|
||||||
//При необходимости обновить данные таблицы
|
|
||||||
useEffect(() => {
|
|
||||||
//Загрузка данных циклограммы с сервера
|
|
||||||
const loadData = async () => {
|
|
||||||
const data = await executeStored({
|
|
||||||
stored: "PKG_P8PANELS_SAMPLES.CYCLOGRAM",
|
|
||||||
args: { NIDENT: state.ident },
|
|
||||||
attributeValueProcessor: (name, val) =>
|
|
||||||
name === "name" ? undefined : ["ddate_start", "ddate_end"].includes(name) ? formatDateJSONDateOnly(val) : val,
|
|
||||||
respArg: "COUT"
|
|
||||||
});
|
|
||||||
setState(pv => ({ ...pv, dataLoaded: true, ...data.XCYCLOGRAM, reload: false }));
|
|
||||||
};
|
|
||||||
//Если указан идентификатор и требуется перезагрузить
|
|
||||||
if (state.ident && state.reload) loadData();
|
|
||||||
}, [state.ident, state.reload, executeStored]);
|
|
||||||
|
|
||||||
//При подключении компонента к странице
|
//При подключении компонента к странице
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
//Инициализация данных циклограммы
|
//Инициализация данных циклограммы
|
||||||
const initData = async () => {
|
const initCyclogram = async () => {
|
||||||
const data = await executeStored({ stored: "PKG_P8PANELS_SAMPLES.CYCLOGRAM_INIT", args: { NIDENT: state.ident } });
|
const data = await executeStored({ stored: "PKG_P8PANELS_SAMPLES.CYCLOGRAM_INIT", args: { NIDENT: null } });
|
||||||
setState(pv => ({ ...pv, init: true, ident: data.NIDENT, reload: true }));
|
setDataIdent(data.NIDENT);
|
||||||
};
|
};
|
||||||
//Если требуется проинициализировать
|
//Инициализируем данные циклограммы
|
||||||
if (!state.init) {
|
initCyclogram();
|
||||||
initData();
|
}, [executeStored]);
|
||||||
}
|
|
||||||
}, [executeStored, state.ident, state.init]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
@ -2929,7 +3043,7 @@ const Cyclogram = ({ title }) => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
<Grid container direction="column" alignItems="center">
|
<Grid container direction="column" alignItems="center">
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
{state.dataLoaded ? (
|
{isDataLoaded ? (
|
||||||
<P8PCyclogram
|
<P8PCyclogram
|
||||||
{...P8P_CYCLOGRAM_CONFIG_PROPS}
|
{...P8P_CYCLOGRAM_CONFIG_PROPS}
|
||||||
{...state}
|
{...state}
|
||||||
@ -3014,6 +3128,35 @@ const MyPanel = () => {
|
|||||||
`PKG_P8PANELS_VISUAL.TINDICATOR_MAKE` - функция, инициализация индикатора, возвращает объект для хранения его описания\
|
`PKG_P8PANELS_VISUAL.TINDICATOR_MAKE` - функция, инициализация индикатора, возвращает объект для хранения его описания\
|
||||||
`PKG_P8PANELS_VISUAL.TINDICATOR_TO_XML` - функция, производит сериализацию объекта, описывающего индикатор, в специальный XML-формат, корректно интерпретируемый клиентским компонентом `P8PIndicator` при передаче в WEB-приложение
|
`PKG_P8PANELS_VISUAL.TINDICATOR_TO_XML` - функция, производит сериализацию объекта, описывающего индикатор, в специальный XML-формат, корректно интерпретируемый клиентским компонентом `P8PIndicator` при передаче в WEB-приложение
|
||||||
|
|
||||||
|
**Универсальный хук**
|
||||||
|
|
||||||
|
Для компонента существует универсальный хук `useP8PIndicator`, который помогает автоматизировать и скрыть логику получения/обновления данных для индикатора. Использование универсального хука представлено в примере использования `P8PIndicator` ниже.
|
||||||
|
|
||||||
|
Для использования универсального хука на панели его необходимо импортировать:
|
||||||
|
|
||||||
|
```
|
||||||
|
import { useP8PIndicator } from "../../components/p8p_indicator";
|
||||||
|
|
||||||
|
const MyPanel = () => {
|
||||||
|
const { indicator, isDataLoaded } = useP8PIndicator({ stored: "ХРАНИМЫЙ_ОБЪЕКТ" });
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Входные параметры:**\
|
||||||
|
`stored` - обязательный, строка, имя исполняемого хранимого объекта (для пакетных - "ПАКЕТ.ОБЪЕКТ")\
|
||||||
|
`respArg` - необязательный, строка, имя выходного параметра исполняемого объекта, значение которого необходимо вернуть как данные ответа, по умолчанию - `COUT`\
|
||||||
|
`contentNodeName` - необязательный, строка, имя узла, содержащего информацию об индикаторе, по умолчанию `XINDICATOR`\
|
||||||
|
`storedArgs` - необязательный, объект, описание параметров исполняемого хранимого объекта вида `{"ПАРАМЕТР": "ЗНАЧЕНИЕ"|{VALUE: "ЗНАЧЕНИЕ", SDATA_TYPE: SERV_DATA_TYPE_*}}` (если тип данных параметров не указан явно - произойдёт попытка их автоматического определения, с CLOB-параметрами это не всегда может произойти корректно)\
|
||||||
|
`executeStoredArgs` - необязательный, объект, описание параметров функции `executeStored` (исполняющей хранимый объект) вида `{"ПАРАМЕТР": "ЗНАЧЕНИЕ"}`\
|
||||||
|
`allowDataLoad` - необязательный, функция, определяет игнорирование исполнения хранимой процедуры. Должна возвращать булево. Если возвращает `false`, то исполнение хранимой процедуры, независимо от необходимости, полностью игнорируется
|
||||||
|
|
||||||
|
**Результат:** объект для работы с компонентом `P8PIndicator`. Возвращаемый объект имеет следующие свойства:\
|
||||||
|
`indicator` - объект, хранит информацию об индикаторе, загруженном с помощью хранимой процедуры\
|
||||||
|
`isDataLoaded` - булево, признак успешной загрузки и обработки данных из хранимой процедуры\
|
||||||
|
`isLoading` - булево, признак выполнения загрузки данных из хранимой процедуры\
|
||||||
|
`doReload` - функция, принудительное обновление данных индикатора. Сигнатура функции `f()`, результат функции не интерпретируется
|
||||||
|
|
||||||
**Пример**
|
**Пример**
|
||||||
|
|
||||||
Полный актуальный исходный код примера можно увидеть в "app/panels/samples/indicator.js" данного репозитория.
|
Полный актуальный исходный код примера можно увидеть в "app/panels/samples/indicator.js" данного репозитория.
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
import React, { useCallback, useEffect, useRef } from "react"; //Классы React
|
import React, { useCallback, useEffect, useRef } from "react"; //Классы React
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import Chart from "chart.js/auto"; //Диаграммы и графики
|
import Chart from "chart.js/auto"; //Диаграммы и графики
|
||||||
|
import { useP8PChart } from "./p8p_chart_hooks"; //Хук для графика
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
//Константы
|
//Константы
|
||||||
@ -121,4 +122,4 @@ P8PChart.propTypes = {
|
|||||||
//Интерфейс модуля
|
//Интерфейс модуля
|
||||||
//----------------
|
//----------------
|
||||||
|
|
||||||
export { P8P_CHART_TYPE, P8PChart };
|
export { P8P_CHART_TYPE, P8PChart, useP8PChart };
|
||||||
|
|||||||
136
app/components/p8p_chart_hooks.js
Normal file
136
app/components/p8p_chart_hooks.js
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга
|
||||||
|
Хуки для графиков
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import { useState, useCallback, useEffect, useContext, useRef, useMemo } from "react"; //Классы React
|
||||||
|
import { BackEndСtx } from "../context/backend"; //Контекст взаимодействия с сервером
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Константы - значения по умолчанию
|
||||||
|
const RESP_ARG_DEF = "COUT"; //Имя параметра, содержащего информацию о графике
|
||||||
|
const CHART_NODE_NAME_DEF = "XCHART"; //Наименование узла, содержащего информацию о графике
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Хук для P8PChart
|
||||||
|
const useP8PChart = ({
|
||||||
|
stored,
|
||||||
|
respArg = RESP_ARG_DEF,
|
||||||
|
contentNodeName = CHART_NODE_NAME_DEF,
|
||||||
|
storedArgs = {},
|
||||||
|
executeStoredArgs = {},
|
||||||
|
allowDataLoad = () => true
|
||||||
|
}) => {
|
||||||
|
//Собственное состояние - график
|
||||||
|
const [chart, setChart] = useState({
|
||||||
|
type: null,
|
||||||
|
title: null,
|
||||||
|
legendPosition: null,
|
||||||
|
labels: [],
|
||||||
|
datasets: []
|
||||||
|
});
|
||||||
|
|
||||||
|
//Собственное состояние - признак загрузки данных
|
||||||
|
const [isDataLoaded, setIsDataLoaded] = useState(false);
|
||||||
|
|
||||||
|
//Собственное состояние - флаг загрузки
|
||||||
|
const [isLoading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
//Собственное состояние - необходимость обновления данных
|
||||||
|
const [reload, setReload] = useState(true);
|
||||||
|
|
||||||
|
//Собственное состояние - дополнительные агрументы
|
||||||
|
const refStoredArgs = useRef(storedArgs);
|
||||||
|
|
||||||
|
//Собственное состояние - дополнительные параметры вызова процедуры
|
||||||
|
const refExecuteStoredArgs = useRef(executeStoredArgs);
|
||||||
|
|
||||||
|
//Признак допустимости обновления данных
|
||||||
|
const isAllowDataLoad = useMemo(() => {
|
||||||
|
return allowDataLoad();
|
||||||
|
}, [allowDataLoad]);
|
||||||
|
|
||||||
|
//Подключение к контексту взаимодействия с сервером
|
||||||
|
const { executeStored, isRespErr } = useContext(BackEndСtx);
|
||||||
|
|
||||||
|
//Загрузка данных графика с сервера
|
||||||
|
const loadData = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const data = await executeStored({ stored, respArg, args: { ...refStoredArgs.current }, ...refExecuteStoredArgs.current });
|
||||||
|
setChart(pv => ({ ...pv, ...data[contentNodeName] }));
|
||||||
|
//Устанавливаем признак загрузки данных с учетом возможных ошибок
|
||||||
|
setIsDataLoaded(!isRespErr(data));
|
||||||
|
} catch (e) {
|
||||||
|
//Если произошла ошибка - данные не загружены
|
||||||
|
setIsDataLoaded(false);
|
||||||
|
} finally {
|
||||||
|
//Сбрасываем признаки загрузки и перезагрузки данных
|
||||||
|
setLoading(false);
|
||||||
|
setReload(false);
|
||||||
|
}
|
||||||
|
}, [contentNodeName, executeStored, isRespErr, respArg, stored]);
|
||||||
|
|
||||||
|
//При необходимости обновления графика
|
||||||
|
const doReload = useCallback(() => {
|
||||||
|
setReload(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
//Проверка изменений параметров
|
||||||
|
const isArgsChanged = useCallback(
|
||||||
|
(currentArgs, args) => {
|
||||||
|
//Если дополнительные параметры изменились (и сейчас не происходит загрузка данных с сервера)
|
||||||
|
return !isLoading && JSON.stringify(currentArgs) != JSON.stringify(args);
|
||||||
|
},
|
||||||
|
[isLoading]
|
||||||
|
);
|
||||||
|
|
||||||
|
//При изменение дополнительных параметров процедуры
|
||||||
|
useEffect(() => {
|
||||||
|
//Если параметры изменились
|
||||||
|
if (isArgsChanged(refStoredArgs.current, storedArgs)) {
|
||||||
|
//Устанавливаем новые дополнительные параметры
|
||||||
|
refStoredArgs.current = storedArgs;
|
||||||
|
//При изменении дополнительных параметров необходимо перезагрузить данные
|
||||||
|
setReload(true);
|
||||||
|
}
|
||||||
|
}, [storedArgs, isArgsChanged]);
|
||||||
|
|
||||||
|
//При изменение дополнительных параметров вызова процедуры
|
||||||
|
useEffect(() => {
|
||||||
|
//Если параметры изменились
|
||||||
|
if (isArgsChanged(refExecuteStoredArgs.current, executeStoredArgs)) {
|
||||||
|
//Устанавливаем новые дополнительные параметры
|
||||||
|
refExecuteStoredArgs.current = executeStoredArgs;
|
||||||
|
//При изменении дополнительных параметров необходимо перезагрузить данные
|
||||||
|
setReload(true);
|
||||||
|
}
|
||||||
|
}, [executeStoredArgs, isArgsChanged]);
|
||||||
|
|
||||||
|
//При необходимости обновить данные графика
|
||||||
|
useEffect(() => {
|
||||||
|
//Если необходимо перезагрузить данные и это допустимо
|
||||||
|
if (isAllowDataLoad && reload) {
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
}, [isAllowDataLoad, reload, loadData]);
|
||||||
|
|
||||||
|
//Возвращаем данные графика
|
||||||
|
return { chart, isDataLoaded, isLoading, doReload };
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { useP8PChart };
|
||||||
@ -26,6 +26,7 @@ import {
|
|||||||
} from "@mui/material"; //Интерфейсные компоненты
|
} from "@mui/material"; //Интерфейсные компоненты
|
||||||
import { P8PAppInlineError } from "./p8p_app_message"; //Встраиваемое сообщение об ошибке
|
import { P8PAppInlineError } from "./p8p_app_message"; //Встраиваемое сообщение об ошибке
|
||||||
import { hasValue } from "../core/utils"; //Вспомогательный функции
|
import { hasValue } from "../core/utils"; //Вспомогательный функции
|
||||||
|
import { useP8PCyclogram } from "./p8p_cyclogram_hooks"; //Хук для циклограммы
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
//Константы
|
//Константы
|
||||||
@ -816,4 +817,4 @@ P8PCyclogram.propTypes = {
|
|||||||
//Интерфейс модуля
|
//Интерфейс модуля
|
||||||
//----------------
|
//----------------
|
||||||
|
|
||||||
export { P8PCyclogram };
|
export { P8PCyclogram, useP8PCyclogram };
|
||||||
|
|||||||
145
app/components/p8p_cyclogram_hooks.js
Normal file
145
app/components/p8p_cyclogram_hooks.js
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга
|
||||||
|
Хуки для циклограмм
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import { useState, useCallback, useEffect, useContext, useRef, useMemo } from "react"; //Классы React
|
||||||
|
import { BackEndСtx } from "../context/backend"; //Контекст взаимодействия с сервером
|
||||||
|
import { formatDateJSONDateOnly } from "../core/utils"; //Вспомогательные функции
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Константы - значения по умолчанию
|
||||||
|
const RESP_ARG_DEF = "COUT"; //Имя параметра, содержащего информацию о циклограмме
|
||||||
|
const CG_NODE_NAME_DEF = "XCYCLOGRAM"; //Наименование узла, содержащего информацию о циклограмме
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Хук для P8PCyclogram
|
||||||
|
const useP8PCyclogram = ({
|
||||||
|
stored,
|
||||||
|
respArg = RESP_ARG_DEF,
|
||||||
|
contentNodeName = CG_NODE_NAME_DEF,
|
||||||
|
storedArgs = {},
|
||||||
|
executeStoredArgs = {},
|
||||||
|
allowDataLoad = () => true
|
||||||
|
}) => {
|
||||||
|
//Собственное состояние - циклограмма
|
||||||
|
const [cyclogram, setCyclogram] = useState({
|
||||||
|
columns: [],
|
||||||
|
groups: [],
|
||||||
|
tasks: [],
|
||||||
|
taskAttributes: [],
|
||||||
|
title: null,
|
||||||
|
lineHeight: 0,
|
||||||
|
zoom: 1,
|
||||||
|
zoomBar: true
|
||||||
|
});
|
||||||
|
|
||||||
|
//Собственное состояние - признак загрузки данных
|
||||||
|
const [isDataLoaded, setIsDataLoaded] = useState(false);
|
||||||
|
|
||||||
|
//Собственное состояние - флаг загрузки
|
||||||
|
const [isLoading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
//Собственное состояние - необходимость обновления данных
|
||||||
|
const [reload, setReload] = useState(true);
|
||||||
|
|
||||||
|
//Собственное состояние - дополнительные агрументы
|
||||||
|
const refStoredArgs = useRef(storedArgs);
|
||||||
|
|
||||||
|
//Собственное состояние - дополнительные параметры вызова процедуры
|
||||||
|
const refExecuteStoredArgs = useRef(executeStoredArgs);
|
||||||
|
|
||||||
|
//Признак допустимости обновления данных
|
||||||
|
const isAllowDataLoad = useMemo(() => {
|
||||||
|
return allowDataLoad();
|
||||||
|
}, [allowDataLoad]);
|
||||||
|
|
||||||
|
//Подключение к контексту взаимодействия с сервером
|
||||||
|
const { executeStored, isRespErr } = useContext(BackEndСtx);
|
||||||
|
|
||||||
|
//Загрузка данных циклограммы с сервера
|
||||||
|
const loadData = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const data = await executeStored({
|
||||||
|
stored,
|
||||||
|
args: { ...refStoredArgs.current },
|
||||||
|
attributeValueProcessor: (name, val) => (["ddate_start", "ddate_end"].includes(name) ? formatDateJSONDateOnly(val) : val),
|
||||||
|
respArg,
|
||||||
|
...refExecuteStoredArgs.current
|
||||||
|
});
|
||||||
|
setCyclogram(pv => ({ ...pv, ...data[contentNodeName] }));
|
||||||
|
//Устанавливаем признак загрузки данных с учетом возможных ошибок
|
||||||
|
setIsDataLoaded(!isRespErr(data));
|
||||||
|
} catch (e) {
|
||||||
|
//Если произошла ошибка - данные не загружены
|
||||||
|
setIsDataLoaded(false);
|
||||||
|
} finally {
|
||||||
|
//Сбрасываем признаки загрузки и перезагрузки данных
|
||||||
|
setLoading(false);
|
||||||
|
setReload(false);
|
||||||
|
}
|
||||||
|
}, [contentNodeName, executeStored, isRespErr, respArg, stored]);
|
||||||
|
|
||||||
|
//При необходимости обновления циклограммы
|
||||||
|
const doReload = useCallback(() => {
|
||||||
|
setReload(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
//Проверка изменений параметров
|
||||||
|
const isArgsChanged = useCallback(
|
||||||
|
(currentArgs, args) => {
|
||||||
|
//Если дополнительные параметры изменились (и сейчас не происходит загрузка данных с сервера)
|
||||||
|
return !isLoading && JSON.stringify(currentArgs) != JSON.stringify(args);
|
||||||
|
},
|
||||||
|
[isLoading]
|
||||||
|
);
|
||||||
|
|
||||||
|
//При изменение дополнительных параметров процедуры
|
||||||
|
useEffect(() => {
|
||||||
|
//Если параметры изменились
|
||||||
|
if (isArgsChanged(refStoredArgs.current, storedArgs)) {
|
||||||
|
//Устанавливаем новые дополнительные параметры
|
||||||
|
refStoredArgs.current = storedArgs;
|
||||||
|
//При изменении дополнительных параметров необходимо перезагрузить данные
|
||||||
|
setReload(true);
|
||||||
|
}
|
||||||
|
}, [storedArgs, isArgsChanged]);
|
||||||
|
|
||||||
|
//При изменение дополнительных параметров вызова процедуры
|
||||||
|
useEffect(() => {
|
||||||
|
//Если параметры изменились
|
||||||
|
if (isArgsChanged(refExecuteStoredArgs.current, executeStoredArgs)) {
|
||||||
|
//Устанавливаем новые дополнительные параметры
|
||||||
|
refExecuteStoredArgs.current = executeStoredArgs;
|
||||||
|
//При изменении дополнительных параметров необходимо перезагрузить данные
|
||||||
|
setReload(true);
|
||||||
|
}
|
||||||
|
}, [executeStoredArgs, isArgsChanged]);
|
||||||
|
|
||||||
|
//При необходимости обновить данные циклограммы
|
||||||
|
useEffect(() => {
|
||||||
|
if (isAllowDataLoad && reload) {
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
}, [isAllowDataLoad, reload, loadData]);
|
||||||
|
|
||||||
|
//Возвращаем данные циклограммы
|
||||||
|
return { cyclogram, isDataLoaded, isLoading, doReload };
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { useP8PCyclogram };
|
||||||
@ -19,6 +19,7 @@ import {
|
|||||||
P8P_TABLE_PAGINATOR_ALIGN,
|
P8P_TABLE_PAGINATOR_ALIGN,
|
||||||
P8P_TABLE_PAGINATOR_POSITION
|
P8P_TABLE_PAGINATOR_POSITION
|
||||||
} from "./p8p_table"; //Таблица
|
} from "./p8p_table"; //Таблица
|
||||||
|
import { useP8PDataGrid } from "./p8p_data_grid_hooks"; //Хук для таблицы данных
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
//Константы
|
//Константы
|
||||||
@ -240,5 +241,6 @@ export {
|
|||||||
P8P_DATA_GRID_FILTERS_HEIGHT,
|
P8P_DATA_GRID_FILTERS_HEIGHT,
|
||||||
P8P_DATA_GRID_PAGINATOR_ALIGN,
|
P8P_DATA_GRID_PAGINATOR_ALIGN,
|
||||||
P8P_DATA_GRID_PAGINATOR_POSITION,
|
P8P_DATA_GRID_PAGINATOR_POSITION,
|
||||||
P8PDataGrid
|
P8PDataGrid,
|
||||||
|
useP8PDataGrid
|
||||||
};
|
};
|
||||||
|
|||||||
224
app/components/p8p_data_grid_hooks.js
Normal file
224
app/components/p8p_data_grid_hooks.js
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга
|
||||||
|
Хуки для таблиц данных
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import { useState, useCallback, useEffect, useContext, useRef, useMemo } from "react"; //Классы React
|
||||||
|
import { BackEndСtx } from "../context/backend"; //Контекст взаимодействия с сервером
|
||||||
|
import { object2Base64XML } from "../core/utils"; //Вспомогательные функции
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Константы - значения по умолчанию
|
||||||
|
const DG_PAGE_SIZE_DEF = 10; //Размер страницы
|
||||||
|
const DG_NODE_NAME_DEF = "XDATA_GRID"; //Наименование узла, содержащего информацию о таблице
|
||||||
|
const FILTERS_NODE_NAME_DEF = "filters"; //Наименование узла отборов
|
||||||
|
const ORDERS_NODE_NAME_DEF = "orders"; //Наименование узла сортировок
|
||||||
|
const RESP_ARG_DEF = "COUT"; //Имя параметра, содержащего информацию о таблице
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Хук для P8PDataGrid
|
||||||
|
const useP8PDataGrid = ({
|
||||||
|
stored,
|
||||||
|
respArg = RESP_ARG_DEF,
|
||||||
|
contentNodeName = DG_NODE_NAME_DEF,
|
||||||
|
filtersNodeName = FILTERS_NODE_NAME_DEF,
|
||||||
|
ordersNodeName = ORDERS_NODE_NAME_DEF,
|
||||||
|
pageSize = DG_PAGE_SIZE_DEF,
|
||||||
|
reloadDef = false,
|
||||||
|
initFilters = [],
|
||||||
|
initOrders = [],
|
||||||
|
storedArgs = {},
|
||||||
|
executeStoredArgs = {},
|
||||||
|
allowDataLoad = () => true
|
||||||
|
}) => {
|
||||||
|
//Собственное состояние - таблица данных
|
||||||
|
const [dataGrid, setDataGrid] = useState({
|
||||||
|
columnsDef: [],
|
||||||
|
groups: [],
|
||||||
|
rows: [],
|
||||||
|
filters: Array.isArray(initFilters) ? [...initFilters] : [],
|
||||||
|
orders: Array.isArray(initOrders) ? [...initOrders] : [],
|
||||||
|
pageNumber: 1,
|
||||||
|
pagesAlign: null,
|
||||||
|
pagesPosition: null,
|
||||||
|
pagesCount: 0,
|
||||||
|
fixedColumns: 0,
|
||||||
|
fixedHeader: false,
|
||||||
|
morePages: true
|
||||||
|
});
|
||||||
|
|
||||||
|
//Собственное состояние - признак загрузки данных
|
||||||
|
const [isDataLoaded, setIsDataLoaded] = useState(false);
|
||||||
|
|
||||||
|
//Собственное состояние - флаг загрузки
|
||||||
|
const [isLoading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
//Собственное состояние - необходимость обновления данных
|
||||||
|
const [reload, setReload] = useState(true);
|
||||||
|
|
||||||
|
//Собственное состояние - дополнительные параметры процедуры
|
||||||
|
const refStoredArgs = useRef(storedArgs);
|
||||||
|
|
||||||
|
//Собственное состояние - дополнительные параметры вызова процедуры
|
||||||
|
const refExecuteStoredArgs = useRef(executeStoredArgs);
|
||||||
|
|
||||||
|
//Признак допустимости обновления данных
|
||||||
|
const isAllowDataLoad = useMemo(() => {
|
||||||
|
return allowDataLoad();
|
||||||
|
}, [allowDataLoad]);
|
||||||
|
|
||||||
|
//Подключение к контексту взаимодействия с сервером
|
||||||
|
const { executeStored, SERV_DATA_TYPE_CLOB, isRespErr } = useContext(BackEndСtx);
|
||||||
|
|
||||||
|
//Загрузка данных таблицы с сервера
|
||||||
|
const loadData = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const data = await executeStored({
|
||||||
|
stored,
|
||||||
|
args: {
|
||||||
|
CFILTERS: { VALUE: object2Base64XML(dataGrid.filters, { arrayNodeName: filtersNodeName }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
|
||||||
|
CORDERS: { VALUE: object2Base64XML(dataGrid.orders, { arrayNodeName: ordersNodeName }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
|
||||||
|
NPAGE_NUMBER: dataGrid.pageNumber,
|
||||||
|
NPAGE_SIZE: pageSize,
|
||||||
|
NINCLUDE_DEF: reloadDef ? 1 : dataGrid.dataLoaded ? 0 : 1,
|
||||||
|
...refStoredArgs.current
|
||||||
|
},
|
||||||
|
respArg,
|
||||||
|
...refExecuteStoredArgs.current
|
||||||
|
});
|
||||||
|
setDataGrid(pv => ({
|
||||||
|
...pv,
|
||||||
|
...data[contentNodeName],
|
||||||
|
columnsDef: data[contentNodeName].columnsDef ? [...data[contentNodeName].columnsDef] : pv.columnsDef || [],
|
||||||
|
rows:
|
||||||
|
data[contentNodeName].pagesCount > 0 || pv.pageNumber == 1
|
||||||
|
? [...(data[contentNodeName].rows || [])]
|
||||||
|
: [...(pv.rows || []), ...(data[contentNodeName].rows || [])],
|
||||||
|
groups: data[contentNodeName].groups
|
||||||
|
? data[contentNodeName].pagesCount > 0 || pv.pageNumber == 1
|
||||||
|
? [...(data[contentNodeName].groups || [])]
|
||||||
|
: [...(pv.groups || []), ...data[contentNodeName].groups.filter(g => !pv.groups.find(pg => pg.name == g.name))]
|
||||||
|
: [...(pv.groups || [])],
|
||||||
|
morePages: data[contentNodeName].morePages && (data[contentNodeName].rows || []).length >= pageSize
|
||||||
|
}));
|
||||||
|
//Устанавливаем признак загрузки данных с учетом возможных ошибок
|
||||||
|
setIsDataLoaded(!isRespErr(data));
|
||||||
|
} catch (e) {
|
||||||
|
//Если произошла ошибка - данные не загружены
|
||||||
|
setIsDataLoaded(false);
|
||||||
|
} finally {
|
||||||
|
//Сбрасываем признаки загрузки и перезагрузки данных
|
||||||
|
setLoading(false);
|
||||||
|
setReload(false);
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
SERV_DATA_TYPE_CLOB,
|
||||||
|
contentNodeName,
|
||||||
|
dataGrid.dataLoaded,
|
||||||
|
dataGrid.filters,
|
||||||
|
dataGrid.orders,
|
||||||
|
dataGrid.pageNumber,
|
||||||
|
executeStored,
|
||||||
|
filtersNodeName,
|
||||||
|
isRespErr,
|
||||||
|
ordersNodeName,
|
||||||
|
pageSize,
|
||||||
|
reloadDef,
|
||||||
|
respArg,
|
||||||
|
stored
|
||||||
|
]);
|
||||||
|
|
||||||
|
//При изменении состояния фильтра
|
||||||
|
const handleFilterChanged = useCallback(({ filters }) => {
|
||||||
|
setDataGrid(pv => ({ ...pv, filters: [...filters], pageNumber: 1 }));
|
||||||
|
setReload(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
//При изменении состояния сортировки
|
||||||
|
const handleOrderChanged = useCallback(({ orders }) => {
|
||||||
|
setDataGrid(pv => ({ ...pv, orders: [...orders], pageNumber: 1 }));
|
||||||
|
setReload(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
//При изменении количества отображаемых страниц
|
||||||
|
const handlePagesCountChanged = useCallback(() => {
|
||||||
|
setDataGrid(pv => ({ ...pv, pageNumber: pv.pageNumber + 1 }));
|
||||||
|
setReload(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
//При изменении страницы отображения
|
||||||
|
const handlePageChange = useCallback(({ page }) => {
|
||||||
|
setDataGrid(pv => ({ ...pv, pageNumber: page }));
|
||||||
|
setReload(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
//При необходимости обновления таблицы
|
||||||
|
const doReload = useCallback(
|
||||||
|
({ returnOnFirstPage = false }) => {
|
||||||
|
//Если это не страничный вывод или установлен признак возврата на первую страницу
|
||||||
|
if (dataGrid.pagesCount <= 0 || returnOnFirstPage) {
|
||||||
|
setDataGrid(pv => ({ ...pv, pageNumber: 1 }));
|
||||||
|
}
|
||||||
|
setReload(true);
|
||||||
|
},
|
||||||
|
[dataGrid.pagesCount]
|
||||||
|
);
|
||||||
|
|
||||||
|
//Проверка изменений параметров
|
||||||
|
const isArgsChanged = useCallback(
|
||||||
|
(currentArgs, args) => {
|
||||||
|
//Если дополнительные параметры изменились (и сейчас не происходит загрузка данных с сервера)
|
||||||
|
return !isLoading && JSON.stringify(currentArgs) != JSON.stringify(args);
|
||||||
|
},
|
||||||
|
[isLoading]
|
||||||
|
);
|
||||||
|
|
||||||
|
//При изменение дополнительных параметров процедуры
|
||||||
|
useEffect(() => {
|
||||||
|
//Если параметры изменились
|
||||||
|
if (isArgsChanged(refStoredArgs.current, storedArgs)) {
|
||||||
|
//Устанавливаем новые дополнительные параметры
|
||||||
|
refStoredArgs.current = storedArgs;
|
||||||
|
//При изменении дополнительных параметров необходимо перезагрузить данные
|
||||||
|
setReload(true);
|
||||||
|
}
|
||||||
|
}, [storedArgs, isArgsChanged]);
|
||||||
|
|
||||||
|
//При изменение дополнительных параметров вызова процедуры
|
||||||
|
useEffect(() => {
|
||||||
|
//Если параметры изменились
|
||||||
|
if (isArgsChanged(refExecuteStoredArgs.current, executeStoredArgs)) {
|
||||||
|
//Устанавливаем новые дополнительные параметры
|
||||||
|
refExecuteStoredArgs.current = executeStoredArgs;
|
||||||
|
//При изменении дополнительных параметров необходимо перезагрузить данные
|
||||||
|
setReload(true);
|
||||||
|
}
|
||||||
|
}, [executeStoredArgs, isArgsChanged]);
|
||||||
|
|
||||||
|
//При необходимости обновить данные таблицы
|
||||||
|
useEffect(() => {
|
||||||
|
if (isAllowDataLoad && reload) {
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
}, [isAllowDataLoad, reload, loadData]);
|
||||||
|
|
||||||
|
//Возвращаем данные таблицы
|
||||||
|
return { dataGrid, isDataLoaded, isLoading, handleFilterChanged, handleOrderChanged, handlePagesCountChanged, handlePageChange, doReload };
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { useP8PDataGrid };
|
||||||
@ -27,6 +27,7 @@ import {
|
|||||||
Link
|
Link
|
||||||
} from "@mui/material"; //Интерфейсные компоненты
|
} from "@mui/material"; //Интерфейсные компоненты
|
||||||
import { P8PAppInlineError } from "./p8p_app_message"; //Встраиваемое сообщение об ошибке
|
import { P8PAppInlineError } from "./p8p_app_message"; //Встраиваемое сообщение об ошибке
|
||||||
|
import { useP8PGantt } from "./p8p_gantt_hooks"; //Хук для диаграммы Ганта
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
//Константы
|
//Константы
|
||||||
@ -522,4 +523,4 @@ P8PGantt.propTypes = {
|
|||||||
//Интерфейс модуля
|
//Интерфейс модуля
|
||||||
//----------------
|
//----------------
|
||||||
|
|
||||||
export { P8P_GANTT_TASK_SHAPE, P8P_GANTT_TASK_ATTRIBUTE_SHAPE, P8P_GANTT_TASK_COLOR_SHAPE, taskLegendDesc, P8PGantt };
|
export { P8P_GANTT_TASK_SHAPE, P8P_GANTT_TASK_ATTRIBUTE_SHAPE, P8P_GANTT_TASK_COLOR_SHAPE, taskLegendDesc, P8PGantt, useP8PGantt };
|
||||||
|
|||||||
148
app/components/p8p_gantt_hooks.js
Normal file
148
app/components/p8p_gantt_hooks.js
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга
|
||||||
|
Хуки для диаграмм Ганта
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import { useState, useCallback, useEffect, useContext, useRef, useMemo } from "react"; //Классы React
|
||||||
|
import { BackEndСtx } from "../context/backend"; //Контекст взаимодействия с сервером
|
||||||
|
import { formatDateJSONDateOnly } from "../core/utils"; //Вспомогательные функции
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Константы - значения по умолчанию
|
||||||
|
const RESP_ARG_DEF = "COUT"; //Имя параметра, содержащего информацию о диаграмме ганта
|
||||||
|
const GANTT_NODE_NAME_DEF = "XGANTT"; //Наименование узла, содержащего информацию о диаграмме ганта
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Хук для P8PGantt
|
||||||
|
const useP8PGantt = ({
|
||||||
|
stored,
|
||||||
|
respArg = RESP_ARG_DEF,
|
||||||
|
contentNodeName = GANTT_NODE_NAME_DEF,
|
||||||
|
storedArgs = {},
|
||||||
|
executeStoredArgs = {},
|
||||||
|
allowDataLoad = () => true
|
||||||
|
}) => {
|
||||||
|
//Собственное состояние - диаграмма ганта
|
||||||
|
const [gantt, setGantt] = useState({
|
||||||
|
title: null,
|
||||||
|
zoom: null,
|
||||||
|
zoomBar: null,
|
||||||
|
readOnly: false,
|
||||||
|
readOnlyDates: false,
|
||||||
|
readOnlyProgress: false,
|
||||||
|
taskAttributes: [],
|
||||||
|
tasks: [],
|
||||||
|
taskColors: []
|
||||||
|
});
|
||||||
|
|
||||||
|
//Собственное состояние - признак загрузки данных
|
||||||
|
const [isDataLoaded, setIsDataLoaded] = useState(false);
|
||||||
|
|
||||||
|
//Собственное состояние - флаг загрузки
|
||||||
|
const [isLoading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
//Собственное состояние - необходимость обновления данных
|
||||||
|
const [reload, setReload] = useState(true);
|
||||||
|
|
||||||
|
//Собственное состояние - дополнительные агрументы
|
||||||
|
const refStoredArgs = useRef(storedArgs);
|
||||||
|
|
||||||
|
//Собственное состояние - дополнительные параметры вызова процедуры
|
||||||
|
const refExecuteStoredArgs = useRef(executeStoredArgs);
|
||||||
|
|
||||||
|
//Признак допустимости обновления данных
|
||||||
|
const isAllowDataLoad = useMemo(() => {
|
||||||
|
return allowDataLoad();
|
||||||
|
}, [allowDataLoad]);
|
||||||
|
|
||||||
|
//Подключение к контексту взаимодействия с сервером
|
||||||
|
const { executeStored, isRespErr } = useContext(BackEndСtx);
|
||||||
|
|
||||||
|
//Загрузка данных диаграммы ганта с сервера
|
||||||
|
const loadData = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const data = await executeStored({
|
||||||
|
stored,
|
||||||
|
args: { ...refStoredArgs.current },
|
||||||
|
attributeValueProcessor: (name, val) =>
|
||||||
|
name == "numb" ? undefined : ["start", "end"].includes(name) ? formatDateJSONDateOnly(val) : val,
|
||||||
|
respArg,
|
||||||
|
...refExecuteStoredArgs.current
|
||||||
|
});
|
||||||
|
setGantt(pv => ({ ...pv, ...data[contentNodeName] }));
|
||||||
|
//Устанавливаем признак загрузки данных с учетом возможных ошибок
|
||||||
|
setIsDataLoaded(!isRespErr(data));
|
||||||
|
} catch (e) {
|
||||||
|
//Если произошла ошибка - данные не загружены
|
||||||
|
setIsDataLoaded(false);
|
||||||
|
} finally {
|
||||||
|
//Сбрасываем признаки загрузки и перезагрузки данных
|
||||||
|
setLoading(false);
|
||||||
|
setReload(false);
|
||||||
|
}
|
||||||
|
}, [contentNodeName, executeStored, isRespErr, respArg, stored]);
|
||||||
|
|
||||||
|
//При необходимости обновления диаграммы ганта
|
||||||
|
const doReload = useCallback(() => {
|
||||||
|
setReload(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
//Проверка изменений параметров
|
||||||
|
const isArgsChanged = useCallback(
|
||||||
|
(currentArgs, args) => {
|
||||||
|
//Если дополнительные параметры изменились (и сейчас не происходит загрузка данных с сервера)
|
||||||
|
return !isLoading && JSON.stringify(currentArgs) != JSON.stringify(args);
|
||||||
|
},
|
||||||
|
[isLoading]
|
||||||
|
);
|
||||||
|
|
||||||
|
//При изменение дополнительных параметров процедуры
|
||||||
|
useEffect(() => {
|
||||||
|
//Если параметры изменились
|
||||||
|
if (isArgsChanged(refStoredArgs.current, storedArgs)) {
|
||||||
|
//Устанавливаем новые дополнительные параметры
|
||||||
|
refStoredArgs.current = storedArgs;
|
||||||
|
//При изменении дополнительных параметров необходимо перезагрузить данные
|
||||||
|
setReload(true);
|
||||||
|
}
|
||||||
|
}, [storedArgs, isArgsChanged]);
|
||||||
|
|
||||||
|
//При изменение дополнительных параметров вызова процедуры
|
||||||
|
useEffect(() => {
|
||||||
|
//Если параметры изменились
|
||||||
|
if (isArgsChanged(refExecuteStoredArgs.current, executeStoredArgs)) {
|
||||||
|
//Устанавливаем новые дополнительные параметры
|
||||||
|
refExecuteStoredArgs.current = executeStoredArgs;
|
||||||
|
//При изменении дополнительных параметров необходимо перезагрузить данные
|
||||||
|
setReload(true);
|
||||||
|
}
|
||||||
|
}, [executeStoredArgs, isArgsChanged]);
|
||||||
|
|
||||||
|
//При необходимости обновить данные диаграммы ганта
|
||||||
|
useEffect(() => {
|
||||||
|
//Если необходимо перезагрузить данные и это допустимо
|
||||||
|
if (isAllowDataLoad && reload) {
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
}, [isAllowDataLoad, reload, loadData]);
|
||||||
|
|
||||||
|
//Возвращаем данные диаграммы ганта
|
||||||
|
return { gantt, isDataLoaded, isLoading, doReload };
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { useP8PGantt };
|
||||||
@ -13,6 +13,7 @@ import { IconButton, Icon, Typography, Paper, Stack } from "@mui/material"; //И
|
|||||||
import { P8PHintDialog } from "./p8p_app_message"; //Диалог подсказки
|
import { P8PHintDialog } from "./p8p_app_message"; //Диалог подсказки
|
||||||
import { TEXTS, STATE } from "../../app.text"; //Типовые текстовые ресурсы и константы
|
import { TEXTS, STATE } from "../../app.text"; //Типовые текстовые ресурсы и константы
|
||||||
import { APP_COLORS } from "../../app.styles"; //Типовые стили
|
import { APP_COLORS } from "../../app.styles"; //Типовые стили
|
||||||
|
import { useP8PIndicator } from "./p8p_indicator_hooks"; //Хук для индикатора
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
//Константы
|
//Константы
|
||||||
@ -226,4 +227,4 @@ P8PIndicator.propTypes = {
|
|||||||
//Интерфейс модуля
|
//Интерфейс модуля
|
||||||
//----------------
|
//----------------
|
||||||
|
|
||||||
export { P8P_INDICATOR_VARIANT, P8P_INDICATOR_STATE, P8PIndicator };
|
export { P8P_INDICATOR_VARIANT, P8P_INDICATOR_STATE, P8PIndicator, useP8PIndicator };
|
||||||
|
|||||||
143
app/components/p8p_indicator_hooks.js
Normal file
143
app/components/p8p_indicator_hooks.js
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга
|
||||||
|
Хуки для индикаторов
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import { useState, useCallback, useEffect, useContext, useRef, useMemo } from "react"; //Классы React
|
||||||
|
import { BackEndСtx } from "../context/backend"; //Контекст взаимодействия с сервером
|
||||||
|
import { P8P_INDICATOR_STATE, P8P_INDICATOR_VARIANT } from "./p8p_indicator";
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Константы - значения по умолчанию
|
||||||
|
const RESP_ARG_DEF = "COUT"; //Имя параметра, содержащего информацию об индикаторе
|
||||||
|
const INDICATOR_NODE_NAME_DEF = "XINDICATOR"; //Наименование узла, содержащего информацию об индикаторе
|
||||||
|
const ELEVATION_DEF = 3; //Высота парения
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Хук для P8PIndicator
|
||||||
|
const useP8PIndicator = ({
|
||||||
|
stored,
|
||||||
|
respArg = RESP_ARG_DEF,
|
||||||
|
contentNodeName = INDICATOR_NODE_NAME_DEF,
|
||||||
|
storedArgs = {},
|
||||||
|
executeStoredArgs = {},
|
||||||
|
allowDataLoad = () => true
|
||||||
|
}) => {
|
||||||
|
//Собственное состояние - индикатор
|
||||||
|
const [indicator, setIndicator] = useState({
|
||||||
|
caption: null,
|
||||||
|
value: null,
|
||||||
|
icon: null,
|
||||||
|
state: P8P_INDICATOR_STATE.UNDEFINED,
|
||||||
|
square: false,
|
||||||
|
elevation: ELEVATION_DEF,
|
||||||
|
variant: P8P_INDICATOR_VARIANT.ELEVATION,
|
||||||
|
hint: null,
|
||||||
|
backgroundColor: null,
|
||||||
|
color: null
|
||||||
|
});
|
||||||
|
|
||||||
|
//Собственное состояние - признак загрузки данных
|
||||||
|
const [isDataLoaded, setIsDataLoaded] = useState(false);
|
||||||
|
|
||||||
|
//Собственное состояние - флаг загрузки
|
||||||
|
const [isLoading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
//Собственное состояние - необходимость обновления данных
|
||||||
|
const [reload, setReload] = useState(true);
|
||||||
|
|
||||||
|
//Собственное состояние - дополнительные агрументы
|
||||||
|
const refStoredArgs = useRef(storedArgs);
|
||||||
|
|
||||||
|
//Собственное состояние - дополнительные параметры вызова процедуры
|
||||||
|
const refExecuteStoredArgs = useRef(executeStoredArgs);
|
||||||
|
|
||||||
|
//Признак допустимости обновления данных
|
||||||
|
const isAllowDataLoad = useMemo(() => {
|
||||||
|
return allowDataLoad();
|
||||||
|
}, [allowDataLoad]);
|
||||||
|
|
||||||
|
//Подключение к контексту взаимодействия с сервером
|
||||||
|
const { executeStored, isRespErr } = useContext(BackEndСtx);
|
||||||
|
|
||||||
|
//Загрузка данных индикатора с сервера
|
||||||
|
const loadData = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const data = await executeStored({ stored, respArg, args: { ...refStoredArgs.current }, ...refExecuteStoredArgs.current });
|
||||||
|
setIndicator(pv => ({ ...pv, ...data[contentNodeName] }));
|
||||||
|
//Устанавливаем признак загрузки данных с учетом возможных ошибок
|
||||||
|
setIsDataLoaded(!isRespErr(data));
|
||||||
|
} catch (e) {
|
||||||
|
//Если произошла ошибка - данные не загружены
|
||||||
|
setIsDataLoaded(false);
|
||||||
|
} finally {
|
||||||
|
//Сбрасываем признаки загрузки и перезагрузки данных
|
||||||
|
setLoading(false);
|
||||||
|
setReload(false);
|
||||||
|
}
|
||||||
|
}, [contentNodeName, executeStored, isRespErr, respArg, stored]);
|
||||||
|
|
||||||
|
//При необходимости обновления индикатора
|
||||||
|
const doReload = useCallback(() => {
|
||||||
|
setReload(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
//Проверка изменений параметров
|
||||||
|
const isArgsChanged = useCallback(
|
||||||
|
(currentArgs, args) => {
|
||||||
|
//Если дополнительные параметры изменились (и сейчас не происходит загрузка данных с сервера)
|
||||||
|
return !isLoading && JSON.stringify(currentArgs) != JSON.stringify(args);
|
||||||
|
},
|
||||||
|
[isLoading]
|
||||||
|
);
|
||||||
|
|
||||||
|
//При изменение дополнительных параметров процедуры
|
||||||
|
useEffect(() => {
|
||||||
|
//Если параметры изменились
|
||||||
|
if (isArgsChanged(refStoredArgs.current, storedArgs)) {
|
||||||
|
//Устанавливаем новые дополнительные параметры
|
||||||
|
refStoredArgs.current = storedArgs;
|
||||||
|
//При изменении дополнительных параметров необходимо перезагрузить данные
|
||||||
|
setReload(true);
|
||||||
|
}
|
||||||
|
}, [storedArgs, isArgsChanged]);
|
||||||
|
|
||||||
|
//При изменение дополнительных параметров вызова процедуры
|
||||||
|
useEffect(() => {
|
||||||
|
//Если параметры изменились
|
||||||
|
if (isArgsChanged(refExecuteStoredArgs.current, executeStoredArgs)) {
|
||||||
|
//Устанавливаем новые дополнительные параметры
|
||||||
|
refExecuteStoredArgs.current = executeStoredArgs;
|
||||||
|
//При изменении дополнительных параметров необходимо перезагрузить данные
|
||||||
|
setReload(true);
|
||||||
|
}
|
||||||
|
}, [executeStoredArgs, isArgsChanged]);
|
||||||
|
|
||||||
|
//При необходимости обновить данные индикатора
|
||||||
|
useEffect(() => {
|
||||||
|
//Если необходимо перезагрузить данные и это допустимо
|
||||||
|
if (isAllowDataLoad && reload) {
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
}, [isAllowDataLoad, reload, loadData]);
|
||||||
|
|
||||||
|
//Возвращаем данные индикатора
|
||||||
|
return { indicator, isDataLoaded, isLoading, doReload };
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { useP8PIndicator };
|
||||||
@ -7,15 +7,20 @@
|
|||||||
//Подключение библиотек
|
//Подключение библиотек
|
||||||
//---------------------
|
//---------------------
|
||||||
|
|
||||||
import React, { useState, useCallback, useEffect, useContext } from "react"; //Классы React
|
import React, { useState, useContext } from "react"; //Классы React
|
||||||
import { Box, Grid, Paper, Fab, Icon } from "@mui/material"; //Интерфейсные компоненты
|
import { Box, Grid, Paper, Fab, Icon } from "@mui/material"; //Интерфейсные компоненты
|
||||||
import { object2Base64XML } from "../../core/utils"; //Вспомогательные процедуры и функции
|
|
||||||
import { TEXTS } from "../../../app.text"; //Тектовые ресурсы и константы
|
import { TEXTS } from "../../../app.text"; //Тектовые ресурсы и константы
|
||||||
import { APP_STYLES } from "../../../app.styles"; //Типовые стили
|
import { APP_STYLES } from "../../../app.styles"; //Типовые стили
|
||||||
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
|
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
|
||||||
import { P8PDataGrid, P8P_DATA_GRID_SIZE, P8P_DATA_GRID_MORE_HEIGHT, P8P_DATA_GRID_FILTERS_HEIGHT } from "../../components/p8p_data_grid"; //Таблица данных
|
import {
|
||||||
|
P8PDataGrid,
|
||||||
|
P8P_DATA_GRID_SIZE,
|
||||||
|
P8P_DATA_GRID_MORE_HEIGHT,
|
||||||
|
P8P_DATA_GRID_FILTERS_HEIGHT,
|
||||||
|
useP8PDataGrid
|
||||||
|
} from "../../components/p8p_data_grid"; //Таблица данных
|
||||||
import { P8PFullScreenDialog } from "../../components/p8p_fullscreen_dialog"; //Полноэкранный диалог
|
import { P8PFullScreenDialog } from "../../components/p8p_fullscreen_dialog"; //Полноэкранный диалог
|
||||||
import { P8PChart } from "../../components/p8p_chart"; //График
|
import { P8PChart, useP8PChart } from "../../components/p8p_chart"; //График
|
||||||
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
||||||
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
||||||
import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений
|
import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений
|
||||||
@ -49,102 +54,40 @@ const STYLES = {
|
|||||||
|
|
||||||
//Список проектов
|
//Список проектов
|
||||||
const Projects = () => {
|
const Projects = () => {
|
||||||
//Собственное состояние
|
//Собственное состояние - выбранный проект
|
||||||
const [projectsDataGrid, setProjectsDataGrid] = useState({
|
const [selectedProject, setSelectedProject] = useState(null);
|
||||||
dataLoaded: false,
|
|
||||||
columnsDef: [],
|
|
||||||
filters: null,
|
|
||||||
orders: null,
|
|
||||||
rows: [],
|
|
||||||
reload: true,
|
|
||||||
pageNumber: 1,
|
|
||||||
morePages: true,
|
|
||||||
selectedProject: null,
|
|
||||||
stagesFilters: []
|
|
||||||
});
|
|
||||||
|
|
||||||
//Состояния графиков
|
//Собственное состояние - фильтры этапов
|
||||||
const [showCharts, setShowCharts] = useState(true);
|
const [stagesFilters, setStagesFilters] = useState([]);
|
||||||
const [problemsChart, setProblemsChart] = useState({ loaded: false, labels: [], datasets: [] });
|
|
||||||
const [customersChart, setCustomersChart] = useState({ loaded: false, labels: [], datasets: [] });
|
|
||||||
const [costNotesChart, setCostNotesChart] = useState({ loaded: false, labels: [], datasets: [] });
|
|
||||||
|
|
||||||
//Подключение к контексту взаимодействия с сервером
|
|
||||||
const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
|
|
||||||
|
|
||||||
//Подключение к контексту приложения
|
//Подключение к контексту приложения
|
||||||
const { pOnlineShowDocument, pOnlineShowUnit, configSystemPageSize } = useContext(ApplicationСtx);
|
const { pOnlineShowDocument, pOnlineShowUnit, configSystemPageSize } = useContext(ApplicationСtx);
|
||||||
|
|
||||||
|
//Собственное состояние
|
||||||
|
const {
|
||||||
|
dataGrid: projectsDataGrid,
|
||||||
|
isDataLoaded: projectsLoaded,
|
||||||
|
handleFilterChanged,
|
||||||
|
handleOrderChanged,
|
||||||
|
handlePagesCountChanged
|
||||||
|
} = useP8PDataGrid({
|
||||||
|
stored: "PKG_P8PANELS_PROJECTS.LIST",
|
||||||
|
pageSize: configSystemPageSize,
|
||||||
|
executeStoredArgs: { attributeValueProcessor: (name, val) => (name == "SGOVCNTRID" ? undefined : val) }
|
||||||
|
});
|
||||||
|
|
||||||
|
//Состояния графиков
|
||||||
|
const [showCharts, setShowCharts] = useState(true);
|
||||||
|
const { chart: problemsChart, isDataLoaded: isProblemsLoaded } = useP8PChart({ stored: "PKG_P8PANELS_PROJECTS.CHART_PROBLEMS" });
|
||||||
|
const { chart: customersChart, isDataLoaded: isCustomersLoaded } = useP8PChart({ stored: "PKG_P8PANELS_PROJECTS.CHART_CUSTOMERS" });
|
||||||
|
const { chart: costNotesChart, isDataLoaded: isCostNotesLoaded } = useP8PChart({ stored: "PKG_P8PANELS_PROJECTS.CHART_FCCOSTNOTES" });
|
||||||
|
|
||||||
|
//Подключение к контексту взаимодействия с сервером
|
||||||
|
const { executeStored } = useContext(BackEndСtx);
|
||||||
|
|
||||||
//Подключение к контексту сообщений
|
//Подключение к контексту сообщений
|
||||||
const { showMsgErr } = useContext(MessagingСtx);
|
const { showMsgErr } = useContext(MessagingСtx);
|
||||||
|
|
||||||
//Загрузка данных проектов с сервера
|
|
||||||
const loadProjects = useCallback(async () => {
|
|
||||||
if (projectsDataGrid.reload) {
|
|
||||||
const data = await executeStored({
|
|
||||||
stored: "PKG_P8PANELS_PROJECTS.LIST",
|
|
||||||
args: {
|
|
||||||
CFILTERS: { VALUE: object2Base64XML(projectsDataGrid.filters, { arrayNodeName: "filters" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
|
|
||||||
CORDERS: { VALUE: object2Base64XML(projectsDataGrid.orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
|
|
||||||
NPAGE_NUMBER: projectsDataGrid.pageNumber,
|
|
||||||
NPAGE_SIZE: configSystemPageSize,
|
|
||||||
NINCLUDE_DEF: projectsDataGrid.dataLoaded ? 0 : 1
|
|
||||||
},
|
|
||||||
attributeValueProcessor: (name, val) => (name == "SGOVCNTRID" ? undefined : val),
|
|
||||||
respArg: "COUT"
|
|
||||||
});
|
|
||||||
setProjectsDataGrid(pv => ({
|
|
||||||
...pv,
|
|
||||||
...data.XDATA_GRID,
|
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef,
|
|
||||||
rows: pv.pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...pv.rows, ...(data.XDATA_GRID.rows || [])],
|
|
||||||
dataLoaded: true,
|
|
||||||
reload: false,
|
|
||||||
morePages: (data.XDATA_GRID.rows || []).length >= configSystemPageSize
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
projectsDataGrid.reload,
|
|
||||||
projectsDataGrid.filters,
|
|
||||||
projectsDataGrid.orders,
|
|
||||||
projectsDataGrid.dataLoaded,
|
|
||||||
projectsDataGrid.pageNumber,
|
|
||||||
executeStored,
|
|
||||||
configSystemPageSize,
|
|
||||||
SERV_DATA_TYPE_CLOB
|
|
||||||
]);
|
|
||||||
|
|
||||||
//Получение данных графиков
|
|
||||||
const loadChartData = async () => {
|
|
||||||
const problemsChart = await executeStored({
|
|
||||||
stored: "PKG_P8PANELS_PROJECTS.CHART_PROBLEMS",
|
|
||||||
respArg: "COUT"
|
|
||||||
});
|
|
||||||
setProblemsChart(pv => ({
|
|
||||||
...pv,
|
|
||||||
loaded: true,
|
|
||||||
...problemsChart.XCHART
|
|
||||||
}));
|
|
||||||
const customersChart = await executeStored({
|
|
||||||
stored: "PKG_P8PANELS_PROJECTS.CHART_CUSTOMERS",
|
|
||||||
respArg: "COUT"
|
|
||||||
});
|
|
||||||
setCustomersChart(pv => ({
|
|
||||||
...pv,
|
|
||||||
loaded: true,
|
|
||||||
...customersChart.XCHART
|
|
||||||
}));
|
|
||||||
const costNotesChart = await executeStored({
|
|
||||||
stored: "PKG_P8PANELS_PROJECTS.CHART_FCCOSTNOTES",
|
|
||||||
respArg: "COUT"
|
|
||||||
});
|
|
||||||
setCostNotesChart(pv => ({
|
|
||||||
...pv,
|
|
||||||
loaded: true,
|
|
||||||
...costNotesChart.XCHART
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
//Отображение журнала платежей по этапу проекта
|
//Отображение журнала платежей по этапу проекта
|
||||||
const showPayNotes = async ({ sender, direction }) => {
|
const showPayNotes = async ({ sender, direction }) => {
|
||||||
const data = await executeStored({
|
const data = await executeStored({
|
||||||
@ -166,44 +109,23 @@ const Projects = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
//Отображение этапов проекта
|
//Отображение этапов проекта
|
||||||
const showStages = ({ sender, filters = [] } = {}) =>
|
const showStages = ({ sender, filters = [] } = {}) => {
|
||||||
setProjectsDataGrid(pv => ({ ...pv, selectedProject: { ...sender }, stagesFilters: [...filters] }));
|
setSelectedProject({ ...sender });
|
||||||
|
setStagesFilters([...filters]);
|
||||||
//При изменении состояния фильтра
|
};
|
||||||
const handleFilterChanged = ({ filters }) => setProjectsDataGrid(pv => ({ ...pv, filters: [...filters], pageNumber: 1, reload: true }));
|
|
||||||
|
|
||||||
//При изменении состояния сортировки
|
|
||||||
const handleOrderChanged = ({ orders }) => setProjectsDataGrid(pv => ({ ...pv, orders: [...orders], pageNumber: 1, reload: true }));
|
|
||||||
|
|
||||||
//При изменении количества отображаемых страниц
|
|
||||||
const handlePagesCountChanged = () => setProjectsDataGrid(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reload: true }));
|
|
||||||
|
|
||||||
//При закрытии списка этапов проекта
|
//При закрытии списка этапов проекта
|
||||||
const handleStagesClose = () => setProjectsDataGrid(pv => ({ ...pv, selectedProject: null, stagesFilters: [] }));
|
const handleStagesClose = () => {
|
||||||
|
setSelectedProject(null);
|
||||||
|
setStagesFilters([]);
|
||||||
|
};
|
||||||
|
|
||||||
//Отработка нажатия на график
|
//Отработка нажатия на график
|
||||||
const handleChartClick = ({ item }) => {
|
const handleChartClick = ({ item }) => {
|
||||||
if (item.SFILTER && item.SFILTER_VALUE)
|
if (item.SFILTER && item.SFILTER_VALUE) handleFilterChanged({ filters: [{ name: item.SFILTER, from: item.SFILTER_VALUE }] });
|
||||||
setProjectsDataGrid(pv => ({
|
|
||||||
...pv,
|
|
||||||
filters: [{ name: item.SFILTER, from: item.SFILTER_VALUE }],
|
|
||||||
pageNumber: 1,
|
|
||||||
reload: true
|
|
||||||
}));
|
|
||||||
if (item.SUNITCODE == "CostNotes" && item.NYEAR && item.NMONTH) showCostNotesChartDetail({ year: item.NYEAR, month: item.NMONTH });
|
if (item.SUNITCODE == "CostNotes" && item.NYEAR && item.NMONTH) showCostNotesChartDetail({ year: item.NYEAR, month: item.NMONTH });
|
||||||
};
|
};
|
||||||
|
|
||||||
//При необходимости обновить данные
|
|
||||||
useEffect(() => {
|
|
||||||
loadProjects();
|
|
||||||
}, [projectsDataGrid.reload, loadProjects]);
|
|
||||||
|
|
||||||
//При подключении к странице
|
|
||||||
useEffect(() => {
|
|
||||||
loadChartData();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
//Генерация содержимого
|
//Генерация содержимого
|
||||||
return (
|
return (
|
||||||
<Box p={1}>
|
<Box p={1}>
|
||||||
@ -212,23 +134,23 @@ const Projects = () => {
|
|||||||
<>
|
<>
|
||||||
<Grid item xs={4}>
|
<Grid item xs={4}>
|
||||||
<Paper elevation={3} sx={STYLES.CHART_PAPER}>
|
<Paper elevation={3} sx={STYLES.CHART_PAPER}>
|
||||||
{problemsChart.loaded ? <P8PChart {...problemsChart} onClick={handleChartClick} style={STYLES.CHART} /> : null}
|
{isProblemsLoaded ? <P8PChart {...problemsChart} onClick={handleChartClick} style={STYLES.CHART} /> : null}
|
||||||
</Paper>
|
</Paper>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={4}>
|
<Grid item xs={4}>
|
||||||
<Paper elevation={3} sx={STYLES.CHART_PAPER}>
|
<Paper elevation={3} sx={STYLES.CHART_PAPER}>
|
||||||
{customersChart.loaded ? <P8PChart {...customersChart} onClick={handleChartClick} style={STYLES.CHART} /> : null}
|
{isCustomersLoaded ? <P8PChart {...customersChart} onClick={handleChartClick} style={STYLES.CHART} /> : null}
|
||||||
</Paper>
|
</Paper>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={4}>
|
<Grid item xs={4}>
|
||||||
<Paper elevation={3} sx={STYLES.CHART_PAPER}>
|
<Paper elevation={3} sx={STYLES.CHART_PAPER}>
|
||||||
{costNotesChart.loaded ? <P8PChart {...costNotesChart} onClick={handleChartClick} style={STYLES.CHART} /> : null}
|
{isCostNotesLoaded ? <P8PChart {...costNotesChart} onClick={handleChartClick} style={STYLES.CHART} /> : null}
|
||||||
</Paper>
|
</Paper>
|
||||||
</Grid>
|
</Grid>
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
{projectsDataGrid.dataLoaded ? (
|
{projectsLoaded ? (
|
||||||
<P8PDataGrid
|
<P8PDataGrid
|
||||||
{...P8P_DATA_GRID_CONFIG_PROPS}
|
{...P8P_DATA_GRID_CONFIG_PROPS}
|
||||||
containerComponentProps={{
|
containerComponentProps={{
|
||||||
@ -259,22 +181,18 @@ const Projects = () => {
|
|||||||
onPagesCountChanged={handlePagesCountChanged}
|
onPagesCountChanged={handlePagesCountChanged}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{projectsDataGrid.selectedProject ? (
|
{selectedProject ? (
|
||||||
<P8PFullScreenDialog
|
<P8PFullScreenDialog
|
||||||
title={`Этапы проекта "${projectsDataGrid.selectedProject.SNAME_USL}"`}
|
title={`Этапы проекта "${selectedProject.SNAME_USL}"`}
|
||||||
onClose={handleStagesClose}
|
onClose={handleStagesClose}
|
||||||
contentProps={{ sx: COMMON_PROJECTS_STYLES.FULL_SCREEN_DIALOG_CONTENT }}
|
contentProps={{ sx: COMMON_PROJECTS_STYLES.FULL_SCREEN_DIALOG_CONTENT }}
|
||||||
>
|
>
|
||||||
<Stages
|
<Stages project={selectedProject.NRN} projectName={selectedProject.SNAME_USL} filters={stagesFilters} />
|
||||||
project={projectsDataGrid.selectedProject.NRN}
|
|
||||||
projectName={projectsDataGrid.selectedProject.SNAME_USL}
|
|
||||||
filters={projectsDataGrid.stagesFilters}
|
|
||||||
/>
|
|
||||||
</P8PFullScreenDialog>
|
</P8PFullScreenDialog>
|
||||||
) : null}
|
) : null}
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
{problemsChart.loaded || customersChart.loaded || costNotesChart.loaded ? (
|
{isProblemsLoaded || isCustomersLoaded || isCostNotesLoaded ? (
|
||||||
<Fab size="small" color="secondary" sx={STYLES.CHART_FAB} onClick={() => setShowCharts(!showCharts)}>
|
<Fab size="small" color="secondary" sx={STYLES.CHART_FAB} onClick={() => setShowCharts(!showCharts)}>
|
||||||
<Icon>{showCharts ? "expand_less" : "expand_more"}</Icon>
|
<Icon>{showCharts ? "expand_less" : "expand_more"}</Icon>
|
||||||
</Fab>
|
</Fab>
|
||||||
|
|||||||
@ -7,14 +7,19 @@
|
|||||||
//Подключение библиотек
|
//Подключение библиотек
|
||||||
//---------------------
|
//---------------------
|
||||||
|
|
||||||
import React, { useState, useCallback, useEffect, useContext } from "react"; //Классы React
|
import React, { useContext } from "react"; //Классы React
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import { Box } from "@mui/material"; //Интерфейсные компоненты
|
import { Box } from "@mui/material"; //Интерфейсные компоненты
|
||||||
import { object2Base64XML } from "../../core/utils"; //Вспомогательные процедуры и функции
|
|
||||||
import { TEXTS } from "../../../app.text"; //Тектовые ресурсы и константы
|
import { TEXTS } from "../../../app.text"; //Тектовые ресурсы и константы
|
||||||
import { APP_STYLES } from "../../../app.styles"; //Типовые стили
|
import { APP_STYLES } from "../../../app.styles"; //Типовые стили
|
||||||
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
|
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
|
||||||
import { P8PDataGrid, P8P_DATA_GRID_SIZE, P8P_DATA_GRID_FILTER_SHAPE, P8P_DATA_GRID_FILTERS_HEIGHT } from "../../components/p8p_data_grid"; //Таблица данных
|
import {
|
||||||
|
P8PDataGrid,
|
||||||
|
P8P_DATA_GRID_SIZE,
|
||||||
|
P8P_DATA_GRID_FILTER_SHAPE,
|
||||||
|
P8P_DATA_GRID_FILTERS_HEIGHT,
|
||||||
|
useP8PDataGrid
|
||||||
|
} from "../../components/p8p_data_grid"; //Таблица данных
|
||||||
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
||||||
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
||||||
import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений
|
import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений
|
||||||
@ -40,16 +45,18 @@ const STYLES = {
|
|||||||
//Калькуляция этапа проекта
|
//Калькуляция этапа проекта
|
||||||
const StageArts = ({ stage, filters }) => {
|
const StageArts = ({ stage, filters }) => {
|
||||||
//Собственное состояние
|
//Собственное состояние
|
||||||
const [stageArtsDataGrid, setStageArtsDataGrid] = useState({
|
const {
|
||||||
dataLoaded: false,
|
dataGrid: stageArtsDataGrid,
|
||||||
columnsDef: [],
|
isDataLoaded,
|
||||||
filters: [...filters],
|
handleFilterChanged
|
||||||
rows: [],
|
} = useP8PDataGrid({
|
||||||
reload: true
|
stored: "PKG_P8PANELS_PROJECTS.STAGE_ARTS_LIST",
|
||||||
|
initFilters: filters,
|
||||||
|
storedArgs: { NSTAGE: stage }
|
||||||
});
|
});
|
||||||
|
|
||||||
//Подключение к контексту взаимодействия с сервером
|
//Подключение к контексту взаимодействия с сервером
|
||||||
const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
|
const { executeStored } = useContext(BackEndСtx);
|
||||||
|
|
||||||
//Подключение к контексту приложения
|
//Подключение к контексту приложения
|
||||||
const { pOnlineShowUnit } = useContext(ApplicationСtx);
|
const { pOnlineShowUnit } = useContext(ApplicationСtx);
|
||||||
@ -57,29 +64,6 @@ const StageArts = ({ stage, filters }) => {
|
|||||||
//Подключение к контексту сообщений
|
//Подключение к контексту сообщений
|
||||||
const { showMsgErr } = useContext(MessagingСtx);
|
const { showMsgErr } = useContext(MessagingСtx);
|
||||||
|
|
||||||
//Загрузка данных калькуляции этапа с сервера
|
|
||||||
const loadStageArts = useCallback(async () => {
|
|
||||||
if (stageArtsDataGrid.reload) {
|
|
||||||
const data = await executeStored({
|
|
||||||
stored: "PKG_P8PANELS_PROJECTS.STAGE_ARTS_LIST",
|
|
||||||
args: {
|
|
||||||
NSTAGE: stage,
|
|
||||||
CFILTERS: { VALUE: object2Base64XML(stageArtsDataGrid.filters, { arrayNodeName: "filters" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
|
|
||||||
NINCLUDE_DEF: stageArtsDataGrid.dataLoaded ? 0 : 1
|
|
||||||
},
|
|
||||||
respArg: "COUT"
|
|
||||||
});
|
|
||||||
setStageArtsDataGrid(pv => ({
|
|
||||||
...pv,
|
|
||||||
...data.XDATA_GRID,
|
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef,
|
|
||||||
rows: [...(data.XDATA_GRID.rows || [])],
|
|
||||||
dataLoaded: true,
|
|
||||||
reload: false
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}, [stage, stageArtsDataGrid.reload, stageArtsDataGrid.filters, stageArtsDataGrid.dataLoaded, executeStored, SERV_DATA_TYPE_CLOB]);
|
|
||||||
|
|
||||||
//Отображение журнала затрат по статье калькуляции
|
//Отображение журнала затрат по статье калькуляции
|
||||||
const showCostNotes = async ({ sender }) => {
|
const showCostNotes = async ({ sender }) => {
|
||||||
const data = await executeStored({
|
const data = await executeStored({
|
||||||
@ -100,18 +84,10 @@ const StageArts = ({ stage, filters }) => {
|
|||||||
else showMsgErr(TEXTS.NO_DATA_FOUND);
|
else showMsgErr(TEXTS.NO_DATA_FOUND);
|
||||||
};
|
};
|
||||||
|
|
||||||
//При изменении состояния фильтра
|
|
||||||
const handleFilterChanged = ({ filters }) => setStageArtsDataGrid(pv => ({ ...pv, filters, reload: true }));
|
|
||||||
|
|
||||||
//При необходимости обновить данные
|
|
||||||
useEffect(() => {
|
|
||||||
loadStageArts();
|
|
||||||
}, [stageArtsDataGrid.reload, loadStageArts]);
|
|
||||||
|
|
||||||
//Генерация содержимого
|
//Генерация содержимого
|
||||||
return (
|
return (
|
||||||
<Box pt={2}>
|
<Box pt={2}>
|
||||||
{stageArtsDataGrid.dataLoaded ? (
|
{isDataLoaded ? (
|
||||||
<P8PDataGrid
|
<P8PDataGrid
|
||||||
{...P8P_DATA_GRID_CONFIG_PROPS}
|
{...P8P_DATA_GRID_CONFIG_PROPS}
|
||||||
containerComponentProps={{ sx: STYLES.TABLE_ARTS((stageArtsDataGrid.filters || []).length > 0), elevation: 0 }}
|
containerComponentProps={{ sx: STYLES.TABLE_ARTS((stageArtsDataGrid.filters || []).length > 0), elevation: 0 }}
|
||||||
|
|||||||
@ -7,10 +7,9 @@
|
|||||||
//Подключение библиотек
|
//Подключение библиотек
|
||||||
//---------------------
|
//---------------------
|
||||||
|
|
||||||
import React, { useState, useCallback, useEffect, useContext } from "react"; //Классы React
|
import React, { useContext } from "react"; //Классы React
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import { Box } from "@mui/material"; //Интерфейсные компоненты
|
import { Box } from "@mui/material"; //Интерфейсные компоненты
|
||||||
import { object2Base64XML } from "../../core/utils"; //Вспомогательные процедуры и функции
|
|
||||||
import { TEXTS } from "../../../app.text"; //Тектовые ресурсы и константы
|
import { TEXTS } from "../../../app.text"; //Тектовые ресурсы и константы
|
||||||
import { APP_STYLES } from "../../../app.styles"; //Типовые стили
|
import { APP_STYLES } from "../../../app.styles"; //Типовые стили
|
||||||
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
|
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
|
||||||
@ -19,7 +18,8 @@ import {
|
|||||||
P8P_DATA_GRID_SIZE,
|
P8P_DATA_GRID_SIZE,
|
||||||
P8P_DATA_GRID_FILTER_SHAPE,
|
P8P_DATA_GRID_FILTER_SHAPE,
|
||||||
P8P_DATA_GRID_MORE_HEIGHT,
|
P8P_DATA_GRID_MORE_HEIGHT,
|
||||||
P8P_DATA_GRID_FILTERS_HEIGHT
|
P8P_DATA_GRID_FILTERS_HEIGHT,
|
||||||
|
useP8PDataGrid
|
||||||
} from "../../components/p8p_data_grid"; //Таблица данных
|
} from "../../components/p8p_data_grid"; //Таблица данных
|
||||||
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
||||||
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
||||||
@ -47,68 +47,29 @@ const STYLES = {
|
|||||||
|
|
||||||
//Договоры с соисполнителями этапа проекта
|
//Договоры с соисполнителями этапа проекта
|
||||||
const StageContracts = ({ stage, filters }) => {
|
const StageContracts = ({ stage, filters }) => {
|
||||||
//Собственное состояние
|
|
||||||
const [stageContractsDataGrid, setStageContractsDataGrid] = useState({
|
|
||||||
dataLoaded: false,
|
|
||||||
columnsDef: [],
|
|
||||||
filters: [...filters],
|
|
||||||
orders: null,
|
|
||||||
rows: [],
|
|
||||||
reload: true,
|
|
||||||
pageNumber: 1,
|
|
||||||
morePages: true
|
|
||||||
});
|
|
||||||
|
|
||||||
//Подключение к контексту взаимодействия с сервером
|
|
||||||
const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
|
|
||||||
|
|
||||||
//Подключение к контексту приложения
|
//Подключение к контексту приложения
|
||||||
const { pOnlineShowDocument, pOnlineShowUnit, configSystemPageSize } = useContext(ApplicationСtx);
|
const { pOnlineShowDocument, pOnlineShowUnit, configSystemPageSize } = useContext(ApplicationСtx);
|
||||||
|
|
||||||
|
//Собственное состояние
|
||||||
|
const {
|
||||||
|
dataGrid: stageContractsDataGrid,
|
||||||
|
isDataLoaded,
|
||||||
|
handleFilterChanged,
|
||||||
|
handleOrderChanged,
|
||||||
|
handlePagesCountChanged
|
||||||
|
} = useP8PDataGrid({
|
||||||
|
stored: "PKG_P8PANELS_PROJECTS.STAGE_CONTRACTS_LIST",
|
||||||
|
pageSize: configSystemPageSize,
|
||||||
|
storedArgs: { NSTAGE: stage },
|
||||||
|
executeStoredArgs: { attributeValueProcessor: (name, val) => (name == "SGOVCNTRID" ? undefined : val) }
|
||||||
|
});
|
||||||
|
|
||||||
|
//Подключение к контексту взаимодействия с сервером
|
||||||
|
const { executeStored } = useContext(BackEndСtx);
|
||||||
|
|
||||||
//Подключение к контексту сообщений
|
//Подключение к контексту сообщений
|
||||||
const { showMsgErr } = useContext(MessagingСtx);
|
const { showMsgErr } = useContext(MessagingСtx);
|
||||||
|
|
||||||
//Загрузка данных этапов с сервера
|
|
||||||
const loadStageContracts = useCallback(async () => {
|
|
||||||
if (stageContractsDataGrid.reload) {
|
|
||||||
const data = await executeStored({
|
|
||||||
stored: "PKG_P8PANELS_PROJECTS.STAGE_CONTRACTS_LIST",
|
|
||||||
args: {
|
|
||||||
NSTAGE: stage,
|
|
||||||
CFILTERS: {
|
|
||||||
VALUE: object2Base64XML(stageContractsDataGrid.filters, { arrayNodeName: "filters" }),
|
|
||||||
SDATA_TYPE: SERV_DATA_TYPE_CLOB
|
|
||||||
},
|
|
||||||
CORDERS: { VALUE: object2Base64XML(stageContractsDataGrid.orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
|
|
||||||
NPAGE_NUMBER: stageContractsDataGrid.pageNumber,
|
|
||||||
NPAGE_SIZE: configSystemPageSize,
|
|
||||||
NINCLUDE_DEF: stageContractsDataGrid.dataLoaded ? 0 : 1
|
|
||||||
},
|
|
||||||
attributeValueProcessor: (name, val) => (name == "SGOVCNTRID" ? undefined : val),
|
|
||||||
respArg: "COUT"
|
|
||||||
});
|
|
||||||
setStageContractsDataGrid(pv => ({
|
|
||||||
...pv,
|
|
||||||
...data.XDATA_GRID,
|
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef,
|
|
||||||
rows: pv.pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...pv.rows, ...(data.XDATA_GRID.rows || [])],
|
|
||||||
dataLoaded: true,
|
|
||||||
reload: false,
|
|
||||||
morePages: (data.XDATA_GRID.rows || []).length >= configSystemPageSize
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
stage,
|
|
||||||
stageContractsDataGrid.reload,
|
|
||||||
stageContractsDataGrid.filters,
|
|
||||||
stageContractsDataGrid.orders,
|
|
||||||
stageContractsDataGrid.dataLoaded,
|
|
||||||
stageContractsDataGrid.pageNumber,
|
|
||||||
executeStored,
|
|
||||||
configSystemPageSize,
|
|
||||||
SERV_DATA_TYPE_CLOB
|
|
||||||
]);
|
|
||||||
|
|
||||||
//Отображение выходящих счетов на оплату от соисполнителя этапа
|
//Отображение выходящих счетов на оплату от соисполнителя этапа
|
||||||
const showPaymentAccountsIn = async ({ sender }) => {
|
const showPaymentAccountsIn = async ({ sender }) => {
|
||||||
const data = await executeStored({
|
const data = await executeStored({
|
||||||
@ -139,24 +100,10 @@ const StageContracts = ({ stage, filters }) => {
|
|||||||
else showMsgErr(TEXTS.NO_DATA_FOUND);
|
else showMsgErr(TEXTS.NO_DATA_FOUND);
|
||||||
};
|
};
|
||||||
|
|
||||||
//При изменении состояния фильтра
|
|
||||||
const handleFilterChanged = ({ filters }) => setStageContractsDataGrid(pv => ({ ...pv, filters, pageNumber: 1, reload: true }));
|
|
||||||
|
|
||||||
//При изменении состояния сортировки
|
|
||||||
const handleOrderChanged = ({ orders }) => setStageContractsDataGrid(pv => ({ ...pv, orders, pageNumber: 1, reload: true }));
|
|
||||||
|
|
||||||
//При изменении количества отображаемых страниц
|
|
||||||
const handlePagesCountChanged = () => setStageContractsDataGrid(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reload: true }));
|
|
||||||
|
|
||||||
//При необходимости обновить данные
|
|
||||||
useEffect(() => {
|
|
||||||
loadStageContracts();
|
|
||||||
}, [stageContractsDataGrid.reload, loadStageContracts]);
|
|
||||||
|
|
||||||
//Генерация содержимого
|
//Генерация содержимого
|
||||||
return (
|
return (
|
||||||
<Box pt={2}>
|
<Box pt={2}>
|
||||||
{stageContractsDataGrid.dataLoaded ? (
|
{isDataLoaded ? (
|
||||||
<P8PDataGrid
|
<P8PDataGrid
|
||||||
{...P8P_DATA_GRID_CONFIG_PROPS}
|
{...P8P_DATA_GRID_CONFIG_PROPS}
|
||||||
containerComponentProps={{
|
containerComponentProps={{
|
||||||
|
|||||||
@ -7,10 +7,9 @@
|
|||||||
//Подключение библиотек
|
//Подключение библиотек
|
||||||
//---------------------
|
//---------------------
|
||||||
|
|
||||||
import React, { useState, useCallback, useEffect, useContext } from "react"; //Классы React
|
import React, { useState, useContext } from "react"; //Классы React
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import { Box } from "@mui/material"; //Интерфейсные компоненты
|
import { Box } from "@mui/material"; //Интерфейсные компоненты
|
||||||
import { object2Base64XML } from "../../core/utils"; //Вспомогательные процедуры и функции
|
|
||||||
import { TEXTS } from "../../../app.text"; //Тектовые ресурсы и константы
|
import { TEXTS } from "../../../app.text"; //Тектовые ресурсы и константы
|
||||||
import { APP_STYLES } from "../../../app.styles"; //Типовые стили
|
import { APP_STYLES } from "../../../app.styles"; //Типовые стили
|
||||||
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
|
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
|
||||||
@ -19,7 +18,8 @@ import {
|
|||||||
P8P_DATA_GRID_SIZE,
|
P8P_DATA_GRID_SIZE,
|
||||||
P8P_DATA_GRID_FILTER_SHAPE,
|
P8P_DATA_GRID_FILTER_SHAPE,
|
||||||
P8P_DATA_GRID_MORE_HEIGHT,
|
P8P_DATA_GRID_MORE_HEIGHT,
|
||||||
P8P_DATA_GRID_FILTERS_HEIGHT
|
P8P_DATA_GRID_FILTERS_HEIGHT,
|
||||||
|
useP8PDataGrid
|
||||||
} from "../../components/p8p_data_grid"; //Таблица данных
|
} from "../../components/p8p_data_grid"; //Таблица данных
|
||||||
import { P8PFullScreenDialog } from "../../components/p8p_fullscreen_dialog"; //Полноэкранный диалог
|
import { P8PFullScreenDialog } from "../../components/p8p_fullscreen_dialog"; //Полноэкранный диалог
|
||||||
import { StageArts } from "./stage_arts"; //Калькуляция этапа проекта
|
import { StageArts } from "./stage_arts"; //Калькуляция этапа проекта
|
||||||
@ -50,69 +50,38 @@ const STYLES = {
|
|||||||
|
|
||||||
//Список этапов проекта
|
//Список этапов проекта
|
||||||
const Stages = ({ project, projectName, filters }) => {
|
const Stages = ({ project, projectName, filters }) => {
|
||||||
//Собственное состояние
|
//Собственное состояние - информация выбранного этапа
|
||||||
const [stagesDataGrid, setStagesDataGrid] = useState({
|
const [selectedStage, setSelectedStage] = useState({
|
||||||
dataLoaded: false,
|
stageNumb: null,
|
||||||
columnsDef: [],
|
|
||||||
filters: [...filters],
|
|
||||||
orders: null,
|
|
||||||
rows: [],
|
|
||||||
reload: true,
|
|
||||||
pageNumber: 1,
|
|
||||||
morePages: true,
|
|
||||||
selectedStageNumb: null,
|
|
||||||
showStageArts: null,
|
showStageArts: null,
|
||||||
stageArtsFilters: [],
|
stageArtsFilters: [],
|
||||||
showStageContracts: null,
|
showStageContracts: null,
|
||||||
stageContractsFilters: []
|
stageContractsFilters: []
|
||||||
});
|
});
|
||||||
|
|
||||||
//Подключение к контексту взаимодействия с сервером
|
|
||||||
const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
|
|
||||||
|
|
||||||
//Подключение к контексту приложения
|
//Подключение к контексту приложения
|
||||||
const { pOnlineShowDocument, pOnlineShowUnit, configSystemPageSize } = useContext(ApplicationСtx);
|
const { pOnlineShowDocument, pOnlineShowUnit, configSystemPageSize } = useContext(ApplicationСtx);
|
||||||
|
|
||||||
|
//Собственное состояние
|
||||||
|
const {
|
||||||
|
dataGrid: stagesDataGrid,
|
||||||
|
isDataLoaded: isStagesLoaded,
|
||||||
|
handleFilterChanged,
|
||||||
|
handleOrderChanged,
|
||||||
|
handlePagesCountChanged
|
||||||
|
} = useP8PDataGrid({
|
||||||
|
stored: "PKG_P8PANELS_PROJECTS.STAGES_LIST",
|
||||||
|
pageSize: configSystemPageSize,
|
||||||
|
initFilters: filters,
|
||||||
|
storedArgs: { NPRN: project }
|
||||||
|
});
|
||||||
|
|
||||||
|
//Подключение к контексту взаимодействия с сервером
|
||||||
|
const { executeStored } = useContext(BackEndСtx);
|
||||||
|
|
||||||
//Подключение к контексту сообщений
|
//Подключение к контексту сообщений
|
||||||
const { showMsgErr } = useContext(MessagingСtx);
|
const { showMsgErr } = useContext(MessagingСtx);
|
||||||
|
|
||||||
//Загрузка данных этапов с сервера
|
|
||||||
const loadStages = useCallback(async () => {
|
|
||||||
if (stagesDataGrid.reload) {
|
|
||||||
const data = await executeStored({
|
|
||||||
stored: "PKG_P8PANELS_PROJECTS.STAGES_LIST",
|
|
||||||
args: {
|
|
||||||
NPRN: project,
|
|
||||||
CFILTERS: { VALUE: object2Base64XML(stagesDataGrid.filters, { arrayNodeName: "filters" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
|
|
||||||
CORDERS: { VALUE: object2Base64XML(stagesDataGrid.orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
|
|
||||||
NPAGE_NUMBER: stagesDataGrid.pageNumber,
|
|
||||||
NPAGE_SIZE: configSystemPageSize,
|
|
||||||
NINCLUDE_DEF: stagesDataGrid.dataLoaded ? 0 : 1
|
|
||||||
},
|
|
||||||
respArg: "COUT"
|
|
||||||
});
|
|
||||||
setStagesDataGrid(pv => ({
|
|
||||||
...pv,
|
|
||||||
...data.XDATA_GRID,
|
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef,
|
|
||||||
rows: pv.pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...pv.rows, ...(data.XDATA_GRID.rows || [])],
|
|
||||||
dataLoaded: true,
|
|
||||||
reload: false,
|
|
||||||
morePages: (data.XDATA_GRID.rows || []).length >= configSystemPageSize
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
project,
|
|
||||||
stagesDataGrid.reload,
|
|
||||||
stagesDataGrid.filters,
|
|
||||||
stagesDataGrid.orders,
|
|
||||||
stagesDataGrid.dataLoaded,
|
|
||||||
stagesDataGrid.pageNumber,
|
|
||||||
executeStored,
|
|
||||||
configSystemPageSize,
|
|
||||||
SERV_DATA_TYPE_CLOB
|
|
||||||
]);
|
|
||||||
|
|
||||||
//Отображение журнала платежей по этапу проекта
|
//Отображение журнала платежей по этапу проекта
|
||||||
const showPayNotes = async ({ sender, direction }) => {
|
const showPayNotes = async ({ sender, direction }) => {
|
||||||
const data = await executeStored({
|
const data = await executeStored({
|
||||||
@ -145,36 +114,22 @@ const Stages = ({ project, projectName, filters }) => {
|
|||||||
|
|
||||||
//Отображение статей калькуляции по этапу проекта
|
//Отображение статей калькуляции по этапу проекта
|
||||||
const showStageArts = ({ sender, filters = [] } = {}) =>
|
const showStageArts = ({ sender, filters = [] } = {}) =>
|
||||||
setStagesDataGrid(pv => ({ ...pv, showStageArts: sender.NRN, selectedStageNumb: sender.SNUMB, stageArtsFilters: [...filters] }));
|
setSelectedStage(pv => ({ ...pv, showStageArts: sender.NRN, stageNumb: sender.SNUMB, stageArtsFilters: [...filters] }));
|
||||||
|
|
||||||
//Отображение договоров с соисполнителями по этапу проекта
|
//Отображение договоров с соисполнителями по этапу проекта
|
||||||
const showContracts = ({ sender, filters = [] } = {}) =>
|
const showContracts = ({ sender, filters = [] } = {}) =>
|
||||||
setStagesDataGrid(pv => ({ ...pv, showStageContracts: sender.NRN, selectedStageNumb: sender.SNUMB, stageContractsFilters: [...filters] }));
|
setSelectedStage(pv => ({ ...pv, showStageContracts: sender.NRN, stageNumb: sender.SNUMB, stageContractsFilters: [...filters] }));
|
||||||
|
|
||||||
//При изменении состояния фильтра
|
|
||||||
const handleFilterChanged = ({ filters }) => setStagesDataGrid(pv => ({ ...pv, filters, pageNumber: 1, reload: true }));
|
|
||||||
|
|
||||||
//При изменении состояния сортировки
|
|
||||||
const handleOrderChanged = ({ orders }) => setStagesDataGrid(pv => ({ ...pv, orders, pageNumber: 1, reload: true }));
|
|
||||||
|
|
||||||
//При изменении количества отображаемых страниц
|
|
||||||
const handlePagesCountChanged = () => setStagesDataGrid(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reload: true }));
|
|
||||||
|
|
||||||
//При закрытии списка договоров этапа
|
//При закрытии списка договоров этапа
|
||||||
const handleStageContractsClose = () => setStagesDataGrid(pv => ({ ...pv, showStageContracts: null, stageContractsFilters: [] }));
|
const handleStageContractsClose = () => setSelectedStage(pv => ({ ...pv, showStageContracts: null, stageContractsFilters: [] }));
|
||||||
|
|
||||||
//При закрытии калькуляции этапа
|
//При закрытии калькуляции этапа
|
||||||
const handleStageArtsClose = () => setStagesDataGrid(pv => ({ ...pv, showStageArts: null, stageArtsFilters: [] }));
|
const handleStageArtsClose = () => setSelectedStage(pv => ({ ...pv, showStageArts: null, stageArtsFilters: [] }));
|
||||||
|
|
||||||
//При необходимости обновить данные
|
|
||||||
useEffect(() => {
|
|
||||||
loadStages();
|
|
||||||
}, [stagesDataGrid.reload, loadStages]);
|
|
||||||
|
|
||||||
//Генерация содержимого
|
//Генерация содержимого
|
||||||
return (
|
return (
|
||||||
<Box pt={2}>
|
<Box pt={2}>
|
||||||
{stagesDataGrid.dataLoaded ? (
|
{isStagesLoaded ? (
|
||||||
<P8PDataGrid
|
<P8PDataGrid
|
||||||
{...P8P_DATA_GRID_CONFIG_PROPS}
|
{...P8P_DATA_GRID_CONFIG_PROPS}
|
||||||
containerComponentProps={{
|
containerComponentProps={{
|
||||||
@ -210,22 +165,22 @@ const Stages = ({ project, projectName, filters }) => {
|
|||||||
onPagesCountChanged={handlePagesCountChanged}
|
onPagesCountChanged={handlePagesCountChanged}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{stagesDataGrid.showStageContracts ? (
|
{selectedStage.showStageContracts ? (
|
||||||
<P8PFullScreenDialog
|
<P8PFullScreenDialog
|
||||||
title={`Договоры этапа "${stagesDataGrid.selectedStageNumb}" проекта "${projectName}"`}
|
title={`Договоры этапа "${selectedStage.stageNumb}" проекта "${projectName}"`}
|
||||||
onClose={handleStageContractsClose}
|
onClose={handleStageContractsClose}
|
||||||
contentProps={{ sx: COMMON_PROJECTS_STYLES.FULL_SCREEN_DIALOG_CONTENT }}
|
contentProps={{ sx: COMMON_PROJECTS_STYLES.FULL_SCREEN_DIALOG_CONTENT }}
|
||||||
>
|
>
|
||||||
<StageContracts stage={stagesDataGrid.showStageContracts} filters={stagesDataGrid.stageContractsFilters} />
|
<StageContracts stage={selectedStage.showStageContracts} filters={selectedStage.stageContractsFilters} />
|
||||||
</P8PFullScreenDialog>
|
</P8PFullScreenDialog>
|
||||||
) : null}
|
) : null}
|
||||||
{stagesDataGrid.showStageArts ? (
|
{selectedStage.showStageArts ? (
|
||||||
<P8PFullScreenDialog
|
<P8PFullScreenDialog
|
||||||
title={`Калькуляция этапа "${stagesDataGrid.selectedStageNumb}" проекта "${projectName}"`}
|
title={`Калькуляция этапа "${selectedStage.stageNumb}" проекта "${projectName}"`}
|
||||||
onClose={handleStageArtsClose}
|
onClose={handleStageArtsClose}
|
||||||
contentProps={{ sx: COMMON_PROJECTS_STYLES.FULL_SCREEN_DIALOG_CONTENT }}
|
contentProps={{ sx: COMMON_PROJECTS_STYLES.FULL_SCREEN_DIALOG_CONTENT }}
|
||||||
>
|
>
|
||||||
<StageArts stage={stagesDataGrid.showStageArts} filters={stagesDataGrid.stageArtsFilters} />
|
<StageArts stage={selectedStage.showStageArts} filters={selectedStage.stageArtsFilters} />
|
||||||
</P8PFullScreenDialog>
|
</P8PFullScreenDialog>
|
||||||
) : null}
|
) : null}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@ -7,11 +7,10 @@
|
|||||||
//Подключение библиотек
|
//Подключение библиотек
|
||||||
//---------------------
|
//---------------------
|
||||||
|
|
||||||
import React, { useState, useContext, useCallback, useEffect } from "react"; //Классы React
|
import React, { useContext } from "react"; //Классы React
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import { Typography, Grid, Paper } from "@mui/material"; //Интерфейсные элементы
|
import { Typography, Grid, Paper } from "@mui/material"; //Интерфейсные элементы
|
||||||
import { P8PChart } from "../../components/p8p_chart"; //График
|
import { P8PChart, useP8PChart } from "../../components/p8p_chart"; //График
|
||||||
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
|
||||||
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
@ -33,20 +32,11 @@ const STYLES = {
|
|||||||
//Пример: Графики "P8PChart"
|
//Пример: Графики "P8PChart"
|
||||||
const Chart = ({ title }) => {
|
const Chart = ({ title }) => {
|
||||||
//Собственное состояние - график
|
//Собственное состояние - график
|
||||||
const [chart, setChart] = useState({ loaded: false });
|
const { chart, isDataLoaded } = useP8PChart({ stored: "PKG_P8PANELS_SAMPLES.CHART" });
|
||||||
|
|
||||||
//Подключение к контексту взаимодействия с сервером
|
|
||||||
const { executeStored } = useContext(BackEndСtx);
|
|
||||||
|
|
||||||
//Подключение к контексту приложения
|
//Подключение к контексту приложения
|
||||||
const { pOnlineShowUnit } = useContext(ApplicationСtx);
|
const { pOnlineShowUnit } = useContext(ApplicationСtx);
|
||||||
|
|
||||||
//Загрузка данных графика с сервера
|
|
||||||
const loadChart = useCallback(async () => {
|
|
||||||
const chart = await executeStored({ stored: "PKG_P8PANELS_SAMPLES.CHART", respArg: "COUT" });
|
|
||||||
setChart(pv => ({ ...pv, loaded: true, ...chart.XCHART }));
|
|
||||||
}, [executeStored]);
|
|
||||||
|
|
||||||
//Отработка нажатия на график
|
//Отработка нажатия на график
|
||||||
const handleChartClick = ({ item }) => {
|
const handleChartClick = ({ item }) => {
|
||||||
pOnlineShowUnit({
|
pOnlineShowUnit({
|
||||||
@ -55,12 +45,6 @@ const Chart = ({ title }) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
//При подключении к странице
|
|
||||||
useEffect(() => {
|
|
||||||
loadChart();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
//Генерация содержимого
|
//Генерация содержимого
|
||||||
return (
|
return (
|
||||||
<div style={STYLES.CONTAINER}>
|
<div style={STYLES.CONTAINER}>
|
||||||
@ -70,7 +54,7 @@ const Chart = ({ title }) => {
|
|||||||
<Grid container spacing={0} pt={5} direction="column" alignItems="center">
|
<Grid container spacing={0} pt={5} direction="column" alignItems="center">
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<Paper elevation={6} sx={STYLES.CHART_PAPER}>
|
<Paper elevation={6} sx={STYLES.CHART_PAPER}>
|
||||||
{chart.loaded ? <P8PChart {...chart} onClick={handleChartClick} style={STYLES.CHART} /> : null}
|
{isDataLoaded ? <P8PChart {...chart} onClick={handleChartClick} style={STYLES.CHART} /> : null}
|
||||||
</Paper>
|
</Paper>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@ -24,9 +24,9 @@ import {
|
|||||||
Stack,
|
Stack,
|
||||||
Icon
|
Icon
|
||||||
} from "@mui/material"; //Интерфейсные элементы
|
} from "@mui/material"; //Интерфейсные элементы
|
||||||
import { formatDateJSONDateOnly, formatDateRF } from "../../core/utils"; //Вспомогательные функции
|
import { formatDateRF, hasValue } from "../../core/utils"; //Вспомогательные функции
|
||||||
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
|
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
|
||||||
import { P8PCyclogram } from "../../components/p8p_cyclogram"; //Циклограмма
|
import { P8PCyclogram, useP8PCyclogram } from "../../components/p8p_cyclogram"; //Циклограмма
|
||||||
import { P8P_CYCLOGRAM_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
import { P8P_CYCLOGRAM_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
||||||
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ const STYLES = {
|
|||||||
//---------------------------------------------
|
//---------------------------------------------
|
||||||
|
|
||||||
//Диалог открытия задачи
|
//Диалог открытия задачи
|
||||||
const CustomTaskDialog = ({ task, ident, handleReload, close }) => {
|
const CustomTaskDialog = ({ task, ident, onReload, close }) => {
|
||||||
//Собственное состояние
|
//Собственное состояние
|
||||||
const [taskDates, setTaskDates] = useState({ start: task.ddate_start, end: task.ddate_end });
|
const [taskDates, setTaskDates] = useState({ start: task.ddate_start, end: task.ddate_end });
|
||||||
|
|
||||||
@ -80,6 +80,9 @@ const CustomTaskDialog = ({ task, ident, handleReload, close }) => {
|
|||||||
//Подключение к контексту взаимодействия с сервером
|
//Подключение к контексту взаимодействия с сервером
|
||||||
const { executeStored } = useContext(BackEndСtx);
|
const { executeStored } = useContext(BackEndСtx);
|
||||||
|
|
||||||
|
//При необходимости перезагрузить данные
|
||||||
|
const handleReload = useCallback(() => onReload && onReload(), [onReload]);
|
||||||
|
|
||||||
//Изменение дат задачи
|
//Изменение дат задачи
|
||||||
const changeDates = useCallback(async () => {
|
const changeDates = useCallback(async () => {
|
||||||
//Изменяем даты задачи
|
//Изменяем даты задачи
|
||||||
@ -182,7 +185,7 @@ const CustomTaskDialog = ({ task, ident, handleReload, close }) => {
|
|||||||
CustomTaskDialog.propTypes = {
|
CustomTaskDialog.propTypes = {
|
||||||
task: PropTypes.object.isRequired,
|
task: PropTypes.object.isRequired,
|
||||||
ident: PropTypes.number.isRequired,
|
ident: PropTypes.number.isRequired,
|
||||||
handleReload: PropTypes.func.isRequired,
|
onReload: PropTypes.func.isRequired,
|
||||||
close: PropTypes.func.isRequired
|
close: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -216,50 +219,29 @@ const taskRenderer = ({ task }) => {
|
|||||||
|
|
||||||
//Пример: Циклограмма "P8PCyclogram"
|
//Пример: Циклограмма "P8PCyclogram"
|
||||||
const Cyclogram = ({ title }) => {
|
const Cyclogram = ({ title }) => {
|
||||||
|
//Собственное состояние - идентификатор данных
|
||||||
|
const [dataIdent, setDataIdent] = useState(null);
|
||||||
|
|
||||||
//Собственное состояние
|
//Собственное состояние
|
||||||
const [state, setState] = useState({
|
const { cyclogram, isDataLoaded, doReload } = useP8PCyclogram({
|
||||||
init: false,
|
stored: "PKG_P8PANELS_SAMPLES.CYCLOGRAM",
|
||||||
dataLoaded: false,
|
storedArgs: { NIDENT: dataIdent },
|
||||||
reload: true,
|
allowDataLoad: () => hasValue(dataIdent)
|
||||||
ident: null
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//Подключение к контексту взаимодействия с сервером
|
//Подключение к контексту взаимодействия с сервером
|
||||||
const { executeStored } = useContext(BackEndСtx);
|
const { executeStored } = useContext(BackEndСtx);
|
||||||
|
|
||||||
//При необходимости перезагрузки
|
|
||||||
const handleReload = () => {
|
|
||||||
setState(pv => ({ ...pv, reload: true }));
|
|
||||||
};
|
|
||||||
|
|
||||||
//При необходимости обновить данные таблицы
|
|
||||||
useEffect(() => {
|
|
||||||
//Загрузка данных циклограммы с сервера
|
|
||||||
const loadData = async () => {
|
|
||||||
const data = await executeStored({
|
|
||||||
stored: "PKG_P8PANELS_SAMPLES.CYCLOGRAM",
|
|
||||||
args: { NIDENT: state.ident },
|
|
||||||
attributeValueProcessor: (name, val) => (["ddate_start", "ddate_end"].includes(name) ? formatDateJSONDateOnly(val) : val),
|
|
||||||
respArg: "COUT"
|
|
||||||
});
|
|
||||||
setState(pv => ({ ...pv, dataLoaded: true, ...data.XCYCLOGRAM, reload: false }));
|
|
||||||
};
|
|
||||||
//Если указан идентификатор и требуется перезагрузить
|
|
||||||
if (state.ident && state.reload) loadData();
|
|
||||||
}, [state.ident, state.reload, executeStored]);
|
|
||||||
|
|
||||||
//При подключении компонента к странице
|
//При подключении компонента к странице
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
//Инициализация данных циклограммы
|
//Инициализация данных циклограммы
|
||||||
const initData = async () => {
|
const initCyclogram = async () => {
|
||||||
const data = await executeStored({ stored: "PKG_P8PANELS_SAMPLES.CYCLOGRAM_INIT", args: { NIDENT: state.ident } });
|
const data = await executeStored({ stored: "PKG_P8PANELS_SAMPLES.CYCLOGRAM_INIT", args: { NIDENT: null } });
|
||||||
setState(pv => ({ ...pv, init: true, ident: data.NIDENT, reload: true }));
|
setDataIdent(data.NIDENT);
|
||||||
};
|
};
|
||||||
//Если требуется проинициализировать
|
//Инициализируем данные циклограммы
|
||||||
if (!state.init) {
|
initCyclogram();
|
||||||
initData();
|
}, [executeStored]);
|
||||||
}
|
|
||||||
}, [executeStored, state.ident, state.init]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
@ -269,14 +251,14 @@ const Cyclogram = ({ title }) => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
<Grid container direction="column" alignItems="center">
|
<Grid container direction="column" alignItems="center">
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
{state.dataLoaded ? (
|
{isDataLoaded ? (
|
||||||
<P8PCyclogram
|
<P8PCyclogram
|
||||||
{...P8P_CYCLOGRAM_CONFIG_PROPS}
|
{...P8P_CYCLOGRAM_CONFIG_PROPS}
|
||||||
{...state}
|
{...cyclogram}
|
||||||
containerStyle={STYLES.CYCLOGRAM_CONTAINER}
|
containerStyle={STYLES.CYCLOGRAM_CONTAINER}
|
||||||
lineHeight={LINE_HEIGHT}
|
lineHeight={LINE_HEIGHT}
|
||||||
taskDialogRenderer={prms => (
|
taskDialogRenderer={prms => (
|
||||||
<CustomTaskDialog task={prms.task} ident={state.ident} handleReload={handleReload} close={prms.close} />
|
<CustomTaskDialog task={prms.task} ident={dataIdent} onReload={doReload} close={prms.close} />
|
||||||
)}
|
)}
|
||||||
taskRenderer={prms => taskRenderer(prms)}
|
taskRenderer={prms => taskRenderer(prms)}
|
||||||
groupHeaderRenderer={prms => <CustomGroupHeader group={prms.group} />}
|
groupHeaderRenderer={prms => <CustomGroupHeader group={prms.group} />}
|
||||||
|
|||||||
@ -7,13 +7,11 @@
|
|||||||
//Подключение библиотек
|
//Подключение библиотек
|
||||||
//---------------------
|
//---------------------
|
||||||
|
|
||||||
import React, { useState, useContext, useCallback, useEffect } from "react"; //Классы React
|
import React, { useContext } from "react"; //Классы React
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import { Typography, Grid, Stack, Icon, Box, Button } from "@mui/material"; //Интерфейсные элементы
|
import { Typography, Grid, Stack, Icon, Box, Button } from "@mui/material"; //Интерфейсные элементы
|
||||||
import { object2Base64XML } from "../../core/utils"; //Вспомогательные процедуры и функции
|
import { P8PDataGrid, P8P_DATA_GRID_SIZE, useP8PDataGrid } from "../../components/p8p_data_grid"; //Таблица данных
|
||||||
import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../components/p8p_data_grid"; //Таблица данных
|
|
||||||
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
||||||
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
|
||||||
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
||||||
import { APP_STYLES } from "../../../app.styles"; //Типовые стили
|
import { APP_STYLES } from "../../../app.styles"; //Типовые стили
|
||||||
|
|
||||||
@ -87,78 +85,17 @@ export const groupCellRender = () => ({ cellStyle: { padding: "2px" } });
|
|||||||
//Пример: Таблица данных "P8PDataGrid"
|
//Пример: Таблица данных "P8PDataGrid"
|
||||||
const DataGrid = ({ title }) => {
|
const DataGrid = ({ title }) => {
|
||||||
//Собственное состояние - таблица данных
|
//Собственное состояние - таблица данных
|
||||||
const [dataGrid, setDataGrid] = useState({
|
const { dataGrid, isDataLoaded, handleFilterChanged, handleOrderChanged, handlePageChange } = useP8PDataGrid({
|
||||||
dataLoaded: false,
|
stored: "PKG_P8PANELS_SAMPLES.DATA_GRID",
|
||||||
filters: null,
|
pageSize: DATA_GRID_PAGE_SIZE
|
||||||
orders: null,
|
|
||||||
pageNumber: 1,
|
|
||||||
morePages: true,
|
|
||||||
expandable: true,
|
|
||||||
reloading: true
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//Подключение к контексту взаимодействия с сервером
|
|
||||||
const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
|
|
||||||
|
|
||||||
//Подключение к контексту приложения
|
//Подключение к контексту приложения
|
||||||
const { pOnlineShowDocument } = useContext(ApplicationСtx);
|
const { pOnlineShowDocument } = useContext(ApplicationСtx);
|
||||||
|
|
||||||
//Загрузка данных таблицы с сервера
|
|
||||||
const loadData = useCallback(async () => {
|
|
||||||
if (dataGrid.reloading) {
|
|
||||||
const data = await executeStored({
|
|
||||||
stored: "PKG_P8PANELS_SAMPLES.DATA_GRID",
|
|
||||||
args: {
|
|
||||||
CFILTERS: { VALUE: object2Base64XML(dataGrid.filters, { arrayNodeName: "filters" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
|
|
||||||
CORDERS: { VALUE: object2Base64XML(dataGrid.orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
|
|
||||||
NPAGE_NUMBER: dataGrid.pageNumber,
|
|
||||||
NPAGE_SIZE: DATA_GRID_PAGE_SIZE,
|
|
||||||
NINCLUDE_DEF: dataGrid.dataLoaded ? 0 : 1
|
|
||||||
},
|
|
||||||
respArg: "COUT"
|
|
||||||
});
|
|
||||||
setDataGrid(pv => ({
|
|
||||||
...pv,
|
|
||||||
...data.XDATA_GRID,
|
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef || [],
|
|
||||||
rows:
|
|
||||||
data.XDATA_GRID.pagesCount > 0 || pv.pageNumber == 1
|
|
||||||
? [...(data.XDATA_GRID.rows || [])]
|
|
||||||
: [...(pv.rows || []), ...(data.XDATA_GRID.rows || [])],
|
|
||||||
groups: data.XDATA_GRID.groups
|
|
||||||
? data.XDATA_GRID.pagesCount > 0 || pv.pageNumber == 1
|
|
||||||
? [...data.XDATA_GRID.groups]
|
|
||||||
: [...(pv.groups || []), ...data.XDATA_GRID.groups.filter(g => !pv.groups.find(pg => pg.name == g.name))]
|
|
||||||
: [...(pv.groups || [])],
|
|
||||||
dataLoaded: true,
|
|
||||||
reloading: false,
|
|
||||||
morePages: data.XDATA_GRID.morePages && (data.XDATA_GRID.rows || []).length >= DATA_GRID_PAGE_SIZE
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}, [dataGrid.reloading, dataGrid.filters, dataGrid.orders, dataGrid.dataLoaded, dataGrid.pageNumber, executeStored, SERV_DATA_TYPE_CLOB]);
|
|
||||||
|
|
||||||
//При изменении состояния фильтра
|
|
||||||
const handleFilterChanged = ({ filters }) => setDataGrid(pv => ({ ...pv, filters: [...filters], pageNumber: 1, reloading: true }));
|
|
||||||
|
|
||||||
//При изменении состояния сортировки
|
|
||||||
const handleOrderChanged = ({ orders }) => setDataGrid(pv => ({ ...pv, orders: [...orders], pageNumber: 1, reloading: true }));
|
|
||||||
|
|
||||||
//При изменении количества отображаемых страниц
|
|
||||||
const handlePagesCountChanged = () => setDataGrid(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reloading: true }));
|
|
||||||
|
|
||||||
//При изменении страницы отображения
|
|
||||||
const handlePageChange = ({ page }) => {
|
|
||||||
setDataGrid(pv => ({ ...pv, pageNumber: page, reloading: true }));
|
|
||||||
};
|
|
||||||
|
|
||||||
//При нажатии на копку контрагента
|
//При нажатии на копку контрагента
|
||||||
const handleAgnButtonClicked = agnCode => pOnlineShowDocument({ unitCode: "AGNLIST", document: agnCode, inRnParameter: "in_AGNABBR" });
|
const handleAgnButtonClicked = agnCode => pOnlineShowDocument({ unitCode: "AGNLIST", document: agnCode, inRnParameter: "in_AGNABBR" });
|
||||||
|
|
||||||
//При необходимости обновить данные таблицы
|
|
||||||
useEffect(() => {
|
|
||||||
loadData();
|
|
||||||
}, [dataGrid.reloading, loadData]);
|
|
||||||
|
|
||||||
//Генерация содержимого
|
//Генерация содержимого
|
||||||
return (
|
return (
|
||||||
<div style={STYLES.CONTAINER}>
|
<div style={STYLES.CONTAINER}>
|
||||||
@ -168,7 +105,7 @@ const DataGrid = ({ title }) => {
|
|||||||
<Grid container spacing={1} pt={5}>
|
<Grid container spacing={1} pt={5}>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<Box p={5} display="flex" justifyContent="center" alignItems="center">
|
<Box p={5} display="flex" justifyContent="center" alignItems="center">
|
||||||
{dataGrid.dataLoaded ? (
|
{isDataLoaded ? (
|
||||||
<P8PDataGrid
|
<P8PDataGrid
|
||||||
{...P8P_DATA_GRID_CONFIG_PROPS}
|
{...P8P_DATA_GRID_CONFIG_PROPS}
|
||||||
{...dataGrid}
|
{...dataGrid}
|
||||||
@ -180,11 +117,10 @@ const DataGrid = ({ title }) => {
|
|||||||
groupCellRender={groupCellRender}
|
groupCellRender={groupCellRender}
|
||||||
onOrderChanged={handleOrderChanged}
|
onOrderChanged={handleOrderChanged}
|
||||||
onFilterChanged={handleFilterChanged}
|
onFilterChanged={handleFilterChanged}
|
||||||
onPagesCountChanged={handlePagesCountChanged}
|
|
||||||
onPageChanged={handlePageChange}
|
|
||||||
rowExpandRender={({ row }) => (
|
rowExpandRender={({ row }) => (
|
||||||
<Button onClick={() => handleAgnButtonClicked(row.SAGNABBR)}>Показать в разделе</Button>
|
<Button onClick={() => handleAgnButtonClicked(row.SAGNABBR)}>Показать в разделе</Button>
|
||||||
)}
|
)}
|
||||||
|
onPageChanged={handlePageChange}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@ -10,9 +10,9 @@
|
|||||||
import React, { useState, useContext, useCallback, useEffect } from "react"; //Классы React
|
import React, { useState, useContext, useCallback, useEffect } from "react"; //Классы React
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import { Typography, Grid, Stack, Icon, FormControlLabel, Checkbox, Card, CardHeader, CardActions, Avatar, CardContent, Button } from "@mui/material"; //Интерфейсные элементы
|
import { Typography, Grid, Stack, Icon, FormControlLabel, Checkbox, Card, CardHeader, CardActions, Avatar, CardContent, Button } from "@mui/material"; //Интерфейсные элементы
|
||||||
import { formatDateJSONDateOnly, formatDateRF } from "../../core/utils"; //Вспомогательные функции
|
import { formatDateRF, hasValue } from "../../core/utils"; //Вспомогательные функции
|
||||||
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
|
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
|
||||||
import { P8PGantt } from "../../components/p8p_gantt"; //Диаграмма Ганта
|
import { P8PGantt, useP8PGantt } from "../../components/p8p_gantt"; //Диаграмма Ганта
|
||||||
import { P8P_GANTT_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
import { P8P_GANTT_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
||||||
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
||||||
|
|
||||||
@ -96,50 +96,35 @@ const taskDialogRenderer = ({ task, close }) => {
|
|||||||
|
|
||||||
//Пример: Диаграмма Ганта "P8Gantt"
|
//Пример: Диаграмма Ганта "P8Gantt"
|
||||||
const Gantt = ({ title }) => {
|
const Gantt = ({ title }) => {
|
||||||
//Собственное состояние
|
//Собственное состояние - идентификатор данных
|
||||||
const [gantt, setGantt] = useState({
|
const [dataIdent, setDataIdent] = useState(null);
|
||||||
init: false,
|
|
||||||
dataLoaded: false,
|
//Собственное состояние - использование собственного диалога задачи
|
||||||
ident: null,
|
const [isCustomTaskDialog, setIsCustomTaskDialog] = useState(false);
|
||||||
useCustomTaskDialog: false
|
|
||||||
|
//Собственное состояние - диаграмма Ганта
|
||||||
|
const { gantt, isDataLoaded, doReload } = useP8PGantt({
|
||||||
|
stored: "PKG_P8PANELS_SAMPLES.GANTT",
|
||||||
|
storedArgs: { NIDENT: dataIdent },
|
||||||
|
allowDataLoad: () => hasValue(dataIdent)
|
||||||
});
|
});
|
||||||
|
|
||||||
//Подключение к контексту взаимодействия с сервером
|
//Подключение к контексту взаимодействия с сервером
|
||||||
const { executeStored } = useContext(BackEndСtx);
|
const { executeStored } = useContext(BackEndСtx);
|
||||||
|
|
||||||
//Загрузка данных диаграммы с сервера
|
|
||||||
const loadData = useCallback(async () => {
|
|
||||||
const data = await executeStored({
|
|
||||||
stored: "PKG_P8PANELS_SAMPLES.GANTT",
|
|
||||||
args: { NIDENT: gantt.ident },
|
|
||||||
attributeValueProcessor: (name, val) =>
|
|
||||||
name == "numb" ? undefined : ["start", "end"].includes(name) ? formatDateJSONDateOnly(val) : val,
|
|
||||||
respArg: "COUT"
|
|
||||||
});
|
|
||||||
setGantt(pv => ({ ...pv, dataLoaded: true, ...data.XGANTT }));
|
|
||||||
}, [gantt.ident, executeStored]);
|
|
||||||
|
|
||||||
//Инициализация данных диаграммы
|
|
||||||
const initData = useCallback(async () => {
|
|
||||||
if (!gantt.init) {
|
|
||||||
const data = await executeStored({ stored: "PKG_P8PANELS_SAMPLES.GANTT_INIT", args: { NIDENT: gantt.ident } });
|
|
||||||
setGantt(pv => ({ ...pv, init: true, ident: data.NIDENT }));
|
|
||||||
}
|
|
||||||
}, [gantt.init, gantt.ident, executeStored]);
|
|
||||||
|
|
||||||
//Изменение данных диаграммы
|
//Изменение данных диаграммы
|
||||||
const modifyData = useCallback(
|
const modifyData = useCallback(
|
||||||
async ({ rn, start, end }) => {
|
async ({ rn, start, end }) => {
|
||||||
try {
|
try {
|
||||||
await executeStored({
|
await executeStored({
|
||||||
stored: "PKG_P8PANELS_SAMPLES.GANTT_MODIFY",
|
stored: "PKG_P8PANELS_SAMPLES.GANTT_MODIFY",
|
||||||
args: { NIDENT: gantt.ident, NRN: rn, DDATE_FROM: new Date(start), DDATE_TO: new Date(end) }
|
args: { NIDENT: dataIdent, NRN: rn, DDATE_FROM: new Date(start), DDATE_TO: new Date(end) }
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
loadData();
|
doReload();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[gantt.ident, executeStored, loadData]
|
[dataIdent, executeStored, doReload]
|
||||||
);
|
);
|
||||||
|
|
||||||
//Обработка измненения сроков задачи в диаграмме Гантта
|
//Обработка измненения сроков задачи в диаграмме Гантта
|
||||||
@ -147,13 +132,13 @@ const Gantt = ({ title }) => {
|
|||||||
if (isMain) modifyData({ rn: task.rn, start, end });
|
if (isMain) modifyData({ rn: task.rn, start, end });
|
||||||
};
|
};
|
||||||
|
|
||||||
//При необходимости обновить данные таблицы
|
|
||||||
useEffect(() => {
|
|
||||||
if (gantt.ident) loadData();
|
|
||||||
}, [gantt.ident, loadData]);
|
|
||||||
|
|
||||||
//При подключении компонента к странице
|
//При подключении компонента к странице
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
//Инициализация данных диаграммы Ганта
|
||||||
|
const initData = async () => {
|
||||||
|
const data = await executeStored({ stored: "PKG_P8PANELS_SAMPLES.GANTT_INIT", args: { NIDENT: null } });
|
||||||
|
setDataIdent(data.NIDENT);
|
||||||
|
};
|
||||||
initData();
|
initData();
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
@ -166,19 +151,19 @@ const Gantt = ({ title }) => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
sx={STYLES.CONTROL}
|
sx={STYLES.CONTROL}
|
||||||
control={<Checkbox onChange={() => setGantt(pv => ({ ...pv, useCustomTaskDialog: !pv.useCustomTaskDialog }))} />}
|
control={<Checkbox onChange={() => setIsCustomTaskDialog(!isCustomTaskDialog)} />}
|
||||||
label="Отображать пользовательский диалог задачи"
|
label="Отображать пользовательский диалог задачи"
|
||||||
/>
|
/>
|
||||||
<Grid container direction="column" alignItems="center">
|
<Grid container direction="column" alignItems="center">
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
{gantt.dataLoaded ? (
|
{isDataLoaded ? (
|
||||||
<P8PGantt
|
<P8PGantt
|
||||||
{...P8P_GANTT_CONFIG_PROPS}
|
{...P8P_GANTT_CONFIG_PROPS}
|
||||||
{...gantt}
|
{...gantt}
|
||||||
containerStyle={STYLES.GANTT_CONTAINER}
|
containerStyle={STYLES.GANTT_CONTAINER}
|
||||||
onTaskDatesChange={handleTaskDatesChange}
|
onTaskDatesChange={handleTaskDatesChange}
|
||||||
taskAttributeRenderer={taskAttributeRenderer}
|
taskAttributeRenderer={taskAttributeRenderer}
|
||||||
taskDialogRenderer={gantt.useCustomTaskDialog ? taskDialogRenderer : null}
|
taskDialogRenderer={isCustomTaskDialog ? taskDialogRenderer : null}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import React, { useContext } from "react"; //Классы React
|
|||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import { Typography, Stack, Divider } from "@mui/material"; //Интерфейсные элементы
|
import { Typography, Stack, Divider } from "@mui/material"; //Интерфейсные элементы
|
||||||
import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений
|
import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений
|
||||||
import { P8P_INDICATOR_VARIANT, P8P_INDICATOR_STATE, P8PIndicator } from "../../components/p8p_indicator"; //Индикатор
|
import { P8P_INDICATOR_VARIANT, P8P_INDICATOR_STATE, P8PIndicator, useP8PIndicator } from "../../components/p8p_indicator"; //Индикатор
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
//Константы
|
//Константы
|
||||||
@ -30,6 +30,9 @@ const STYLES = {
|
|||||||
|
|
||||||
//Пример: Индикатор "P8PIndicator"
|
//Пример: Индикатор "P8PIndicator"
|
||||||
const Indicator = ({ title }) => {
|
const Indicator = ({ title }) => {
|
||||||
|
//Собственное состояние - индикатор данных с сервера
|
||||||
|
const { indicator, isDataLoaded } = useP8PIndicator({ stored: "PKG_P8PANELS_SAMPLES.INDICATOR" });
|
||||||
|
|
||||||
//Подключение к контексту сообщений
|
//Подключение к контексту сообщений
|
||||||
const { showMsgInfo } = useContext(MessagingСtx);
|
const { showMsgInfo } = useContext(MessagingСtx);
|
||||||
|
|
||||||
@ -140,6 +143,14 @@ const Indicator = ({ title }) => {
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
<Divider>Индикатор данных с сервера</Divider>
|
||||||
|
<Stack direction={"row"} spacing={2} p={5}>
|
||||||
|
{isDataLoaded ? (
|
||||||
|
<P8PIndicator {...indicator} />
|
||||||
|
) : (
|
||||||
|
<P8PIndicator caption={"Ошибка загрузки данных с сервера"} value={0} state={P8P_INDICATOR_STATE.WARN} />
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -81,6 +81,12 @@ create or replace package PKG_P8PANELS_SAMPLES as
|
|||||||
SDATE_TO in varchar2 -- Дата окончания (в строковом представлении)
|
SDATE_TO in varchar2 -- Дата окончания (в строковом представлении)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/* Индикатор */
|
||||||
|
procedure INDICATOR
|
||||||
|
(
|
||||||
|
COUT out clob -- Сериализованные данные для индикатора
|
||||||
|
);
|
||||||
|
|
||||||
end PKG_P8PANELS_SAMPLES;
|
end PKG_P8PANELS_SAMPLES;
|
||||||
/
|
/
|
||||||
create or replace package body PKG_P8PANELS_SAMPLES as
|
create or replace package body PKG_P8PANELS_SAMPLES as
|
||||||
@ -1313,6 +1319,26 @@ create or replace package body PKG_P8PANELS_SAMPLES as
|
|||||||
NTASK_GROUP => REC.TASK_GROUP);
|
NTASK_GROUP => REC.TASK_GROUP);
|
||||||
end loop;
|
end loop;
|
||||||
end CYCLOGRAM_TASK_MODIFY;
|
end CYCLOGRAM_TASK_MODIFY;
|
||||||
|
|
||||||
|
/* Индикатор */
|
||||||
|
procedure INDICATOR
|
||||||
|
(
|
||||||
|
COUT out clob -- Сериализованные данные для индикатора
|
||||||
|
)
|
||||||
|
is
|
||||||
|
RINDICATOR PKG_P8PANELS_VISUAL.TINDICATOR; -- Описание индикатора
|
||||||
|
begin
|
||||||
|
/* Формирование индикатора */
|
||||||
|
RINDICATOR := PKG_P8PANELS_VISUAL.TINDICATOR_MAKE(SCAPTION => 'Данные загружены с помощью серверной процедуры',
|
||||||
|
SVALUE => '1000',
|
||||||
|
SICON => 'downloading',
|
||||||
|
NELEVATION => 2,
|
||||||
|
SVARIANT => PKG_P8PANELS_VISUAL.SINDICATOR_VARIANT_ELEVATION,
|
||||||
|
SBACKGROUND_COLOR => '#63a1df',
|
||||||
|
SCOLOR => 'black');
|
||||||
|
/* Сериализуем собранные данные */
|
||||||
|
COUT := PKG_P8PANELS_VISUAL.TINDICATOR_TO_XML(RINDICATOR => RINDICATOR);
|
||||||
|
end INDICATOR;
|
||||||
|
|
||||||
end PKG_P8PANELS_SAMPLES;
|
end PKG_P8PANELS_SAMPLES;
|
||||||
/
|
/
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user