Закладка "Обучение" - списки выборок и классов, карточка класса (начало)

This commit is contained in:
Mikhail Chechnev 2024-08-06 01:15:48 +03:00
parent ef520e0932
commit 2da1772489
9 changed files with 838 additions and 55 deletions

View File

@ -1,5 +1,32 @@
create or replace package UDO_PKG_EQUIPDS as
/* Список выборок данных */
procedure LIST
(
COUT out clob -- Сериализованная таблица данных
);
/* Список классов оборудования выборки данных */
procedure CM_LIST
(
NEQUIPDS in number, -- Рег. номер выборки данных
COUT out clob -- Сериализованная таблица данных
);
/* Список файлов данных класса оборудования */
procedure CMFL_LIST
(
NEQUIPDSCM in number, -- Рег. номер класса оборудования выборки данных
COUT out clob -- Сериализованная таблица данных
);
/* Список моделей класса оборудования */
procedure CMML_LIST
(
NEQUIPDSCM in number, -- Рег. номер класса оборудования выборки данных
COUT out clob -- Сериализованная таблица данных
);
/* Код доступного действия с моделью по единице оборудования */
function CMML_ACT_BY_EQCONFIG
(
@ -16,6 +43,222 @@ end UDO_PKG_EQUIPDS;
/
create or replace package body UDO_PKG_EQUIPDS as
/* Список выборок данных */
procedure LIST
(
COUT out clob -- Сериализованная таблица данных
)
is
NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Организация сеанса
NCUR integer; -- Курсор документа для результата
XDOC PKG_XMAKE.TNODE; -- Документ для результата
XDS PKG_XMAKE.TNODE; -- Элемент для выборки данных
begin
/* Открываем документ */
NCUR := PKG_XMAKE.OPEN_CURSOR();
/* Обходим выборки данных */
for C in (select T.RN NRN,
T.NAME SNAME
from UDO_T_EQUIPDS T
where T.COMPANY = NCOMPANY
order by T.NAME)
loop
XDS := PKG_XMAKE.CONCAT(ICURSOR => NCUR,
RNODE00 => XDS,
RNODE01 => PKG_XMAKE.ELEMENT(ICURSOR => NCUR,
SNAME => 'XDS',
RATTRIBUTES => PKG_XMAKE.ATTRIBUTES(ICURSOR => NCUR,
RATTRIBUTE00 => PKG_XMAKE.ATTRIBUTE(ICURSOR => NCUR,
SNAME => 'NRN',
SVALUE => C.NRN),
RATTRIBUTE01 => PKG_XMAKE.ATTRIBUTE(ICURSOR => NCUR,
SNAME => 'SNAME',
SVALUE => C.SNAME))));
end loop;
/* Формируем XML-представление ответа */
XDOC := PKG_XMAKE.ELEMENT(ICURSOR => NCUR, SNAME => 'XDATA', RNODE00 => XDS);
/* Конвертируем в CLOB */
COUT := PKG_XMAKE.SERIALIZE_TO_CLOB(ICURSOR => NCUR,
ITYPE => PKG_XMAKE.CONTENT_,
RNODE => XDOC,
RHEADER => PKG_XHEADER.WRAP_ALL(SVERSION => PKG_XHEADER.VERSION_1_0_,
SENCODING => PKG_XHEADER.ENCODING_UTF_,
SSTANDALONE => PKG_XHEADER.STANDALONE_YES_));
/* Закрываем документ */
PKG_XMAKE.CLOSE_CURSOR(ICURSOR => NCUR);
end LIST;
/* Список классов оборудования выборки данных */
procedure CM_LIST
(
NEQUIPDS in number, -- Рег. номер выборки данных
COUT out clob -- Сериализованная таблица данных
)
is
NCUR integer; -- Курсор документа для результата
XDOC PKG_XMAKE.TNODE; -- Документ для результата
XDSCM PKG_XMAKE.TNODE; -- Элемент для выборки данных
begin
/* Открываем документ */
NCUR := PKG_XMAKE.OPEN_CURSOR();
/* Обходим классы оборудования заданной выборки данных */
for C in (select T.RN NRN,
OK.CODE SCODE,
OK.NAME SNAME
from UDO_T_EQUIPDSCM T,
EQOBJKIND OK
where T.PRN = NEQUIPDS
and T.EQOBJKIND = OK.RN
order by OK.CODE)
loop
XDSCM := PKG_XMAKE.CONCAT(ICURSOR => NCUR,
RNODE00 => XDSCM,
RNODE01 => PKG_XMAKE.ELEMENT(ICURSOR => NCUR,
SNAME => 'XDSCM',
RATTRIBUTES => PKG_XMAKE.ATTRIBUTES(ICURSOR => NCUR,
RATTRIBUTE00 => PKG_XMAKE.ATTRIBUTE(ICURSOR => NCUR,
SNAME => 'NRN',
SVALUE => C.NRN),
RATTRIBUTE01 => PKG_XMAKE.ATTRIBUTE(ICURSOR => NCUR,
SNAME => 'SCODE',
SVALUE => C.SCODE),
RATTRIBUTE02 => PKG_XMAKE.ATTRIBUTE(ICURSOR => NCUR,
SNAME => 'SNAME',
SVALUE => C.SNAME))));
end loop;
/* Формируем XML-представление ответа */
XDOC := PKG_XMAKE.ELEMENT(ICURSOR => NCUR, SNAME => 'XDATA', RNODE00 => XDSCM);
/* Конвертируем в CLOB */
COUT := PKG_XMAKE.SERIALIZE_TO_CLOB(ICURSOR => NCUR,
ITYPE => PKG_XMAKE.CONTENT_,
RNODE => XDOC,
RHEADER => PKG_XHEADER.WRAP_ALL(SVERSION => PKG_XHEADER.VERSION_1_0_,
SENCODING => PKG_XHEADER.ENCODING_UTF_,
SSTANDALONE => PKG_XHEADER.STANDALONE_YES_));
/* Закрываем документ */
PKG_XMAKE.CLOSE_CURSOR(ICURSOR => NCUR);
end CM_LIST;
/* Список файлов данных класса оборудования */
procedure CMFL_LIST
(
NEQUIPDSCM in number, -- Рег. номер класса оборудования выборки данных
COUT out clob -- Сериализованная таблица данных
)
is
RDG PKG_P8PANELS_VISUAL.TDATA_GRID; -- Описание таблицы
RDG_ROW PKG_P8PANELS_VISUAL.TROW; -- Строка таблицы
begin
/* Инициализируем таблицу данных */
RDG := PKG_P8PANELS_VISUAL.TDATA_GRID_MAKE();
/* Добавляем в таблицу описание колонок */
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
SNAME => 'NRN',
SCAPTION => 'Рег. номер',
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
BVISIBLE => false);
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
SNAME => 'SFILE_NAME',
SCAPTION => 'Имя',
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR);
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
SNAME => 'SDESCR',
SCAPTION => 'Описание',
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR);
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
SNAME => 'NSTATUS',
SCAPTION => 'Состояние',
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB);
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
SNAME => 'SERR',
SCAPTION => 'Сообщение об ошибке',
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR);
/* Обходим данные */
for C in (select T.RN NRN,
T.FILE_NAME SFILE_NAME,
T.DESCR SDESCR,
T.STATUS NSTATUS,
T.ERR SERR
from UDO_T_EQUIPDSCMFL T
where T.PRN = NEQUIPDSCM
order by T.RN)
loop
/* Добавляем колонки с данными */
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NRN', NVALUE => C.NRN, BCLEAR => true);
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SFILE_NAME', SVALUE => C.SFILE_NAME);
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SDESCR', SVALUE => C.SDESCR);
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NSTATUS', NVALUE => C.NSTATUS);
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SERR', SVALUE => C.SERR);
/* Добавляем строку в таблицу */
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW);
end loop;
/* Сериализуем описание */
COUT := PKG_P8PANELS_VISUAL.TDATA_GRID_TO_XML(RDATA_GRID => RDG, NINCLUDE_DEF => 1);
end CMFL_LIST;
/* Список моделей класса оборудования */
procedure CMML_LIST
(
NEQUIPDSCM in number, -- Рег. номер класса оборудования выборки данных
COUT out clob -- Сериализованная таблица данных
)
is
RDG PKG_P8PANELS_VISUAL.TDATA_GRID; -- Описание таблицы
RDG_ROW PKG_P8PANELS_VISUAL.TROW; -- Строка таблицы
begin
/* Инициализируем таблицу данных */
RDG := PKG_P8PANELS_VISUAL.TDATA_GRID_MAKE();
/* Добавляем в таблицу описание колонок */
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
SNAME => 'NRN',
SCAPTION => 'Рег. номер',
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
BVISIBLE => false);
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
SNAME => 'STASK',
SCAPTION => 'Задача',
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR);
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
SNAME => 'NPRECISION_P',
SCAPTION => 'Точность (план)',
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB);
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
SNAME => 'NPRECISION_F',
SCAPTION => 'Точность (факт)',
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB);
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
SNAME => 'NSTATUS',
SCAPTION => 'Состояние',
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB);
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
SNAME => 'SERR',
SCAPTION => 'Сообщение об ошибке',
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR);
/* Обходим данные */
for C in (select T.RN NRN,
T.TASK STASK,
T.PRECISION_P NPRECISION_P,
T.PRECISION_F NPRECISION_F,
T.STATUS NSTATUS,
T.ERR SERR
from UDO_T_EQUIPDSCMML T
where T.PRN = NEQUIPDSCM
order by T.RN)
loop
/* Добавляем колонки с данными */
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NRN', NVALUE => C.NRN, BCLEAR => true);
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'STASK', SVALUE => C.STASK);
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NPRECISION_P', NVALUE => C.NPRECISION_P);
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NPRECISION_F', NVALUE => C.NPRECISION_F);
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NSTATUS', NVALUE => C.NSTATUS);
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SERR', SVALUE => C.SERR);
/* Добавляем строку в таблицу */
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW);
end loop;
/* Сериализуем описание */
COUT := PKG_P8PANELS_VISUAL.TDATA_GRID_TO_XML(RDATA_GRID => RDG, NINCLUDE_DEF => 1);
end CMML_LIST;
/* Код доступного действия с моделью по единице оборудования */
function CMML_ACT_BY_EQCONFIG
(

View File

@ -7,8 +7,26 @@
//Подключение библиотек
//---------------------
import React from "react"; //Классы React
import { Grid } from "@mui/material"; //Интерфейсные компоненты
import React, { useState, useEffect } from "react"; //Классы React
import { Box, Grid, Stack, Icon, Button } from "@mui/material"; //Интерфейсные компоненты
import { EquipDataSelectionList, EquipDataSelectionClassMachineList, EquipDataSelectionClassMachineCard } from "./admin_tab_layout"; //Вспомогательные компоненты и вёрстка
import {
DS_RN_DEFAULT,
useEquipDataSelectionList,
useEquipDataSelectionClassMachineList,
useEquipDataSelectionClassMachineFilesList,
useEquipDataSelectionClassMachineModelsList
} from "./admin_tab_hooks"; //Вспомогательные хуки
//---------
//Константы
//---------
//Стили
const STYLES = {
DATA_SELECTION_STACK: { alignItems: "center" },
CLASS_MACHINE_ADD_BUTTON: { margin: "5px" }
};
//-----------
//Тело модуля
@ -16,15 +34,70 @@ import { Grid } from "@mui/material"; //Интерфейсные компоне
//Закладка администрирования
const AdminTab = () => {
//Собственное состояние - выбранная выборка данных
const [equipDataSelection, setDataSelection] = useState(DS_RN_DEFAULT);
//Собственное состояние - выбранный класс оборудования
const [equipDataSelectionClassMachine, setDataSelectionClassMachine] = useState(null);
//Загрузка списка выборок данных
const { equipDataSelectionList } = useEquipDataSelectionList();
//Загрузка классов оборудования выбранной выборки данных
const { equipDataSelectionClassMachineList } = useEquipDataSelectionClassMachineList(equipDataSelection);
//Загрузка файлов класса оборудования
const { equipDataSelectionClassMachineFilesList } = useEquipDataSelectionClassMachineFilesList(equipDataSelectionClassMachine);
//Загрузка файлов класса оборудования
const { equipDataSelectionClassMachineModelsList } = useEquipDataSelectionClassMachineModelsList(equipDataSelectionClassMachine);
//При подключении компонента к странице
useEffect(() => {}, []);
//При смене выборки данных
const handleDataSelectionChange = value => {
setDataSelectionClassMachine(null);
setDataSelection(value);
};
//При нажатии на класс оборудования
const handleDataSelectionClassMachineClick = value => setDataSelectionClassMachine(value);
//Генерация содержимого
return (
<div>
<Box>
<Grid container>
<Grid item xs={12} sx={{ backgroundColor: "magenta" }}>
Здесь только администраторы могут работать
<Grid item xs={12}>
<Stack direction="row" spacing={2} p={2} sx={STYLES.DATA_SELECTION_STACK}>
<EquipDataSelectionList list={equipDataSelectionList} value={equipDataSelection} onChange={handleDataSelectionChange} />
<Button variant="contained" startIcon={<Icon>add</Icon>}>
Добавить выборку
</Button>
</Stack>
</Grid>
<Grid item xs={12}>
<Grid container>
<Grid item xs={2} hidden={equipDataSelection == DS_RN_DEFAULT}>
<Button fullWidth startIcon={<Icon>add</Icon>} sx={STYLES.CLASS_MACHINE_ADD_BUTTON}>
Добавить класс
</Button>
<EquipDataSelectionClassMachineList
list={equipDataSelectionClassMachineList}
value={equipDataSelectionClassMachine}
onClick={handleDataSelectionClassMachineClick}
/>
</Grid>
<Grid item xs={10} hidden={equipDataSelectionClassMachine == null}>
<EquipDataSelectionClassMachineCard
filesList={equipDataSelectionClassMachineFilesList}
modelsList={equipDataSelectionClassMachineModelsList}
/>
</Grid>
</Grid>
</div>
</Grid>
</Grid>
</Box>
);
};

View File

@ -0,0 +1,196 @@
/*
Парус 8 - Панели мониторинга - ТОиР - Прогнозирование технического состояния
Закладка администрирования: кастомные хуки
*/
//---------------------
//Подключение библиотек
//---------------------
import { useState, useEffect, useContext } from "react"; //Классы React
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
//---------
//Константы
//---------
//Рег. номер выборки данных по умолчанию
const DS_RN_DEFAULT = -1;
//Наименование выборки данных по умолчанию
const DS_NAME_DEFAULT = "Не указана";
//Выборка данных по умолчанию
const DS_DEFAULT = { NRN: DS_RN_DEFAULT, SNAME: DS_NAME_DEFAULT };
//-----------
//Тело модуля
//-----------
//Загрузка списка выборок данных
const useEquipDataSelectionList = () => {
//Собственное состояние - флаг загрузки
const [isLoading, setLoading] = useState(false);
//Собственное состояние - данные
const [data, setData] = useState([DS_DEFAULT]);
//Подключение к контексту взаимодействия с сервером
const { executeStored } = useContext(BackEndСtx);
//Загрузка данных при изменении зависимостей
useEffect(() => {
const loadData = async () => {
try {
setLoading(true);
const data = await executeStored({
stored: "UDO_PKG_EQUIPDS.LIST",
respArg: "COUT",
isArray: name => ["XDS"].includes(name),
loader: false
});
setData([DS_DEFAULT, ...(data?.XDS ? data.XDS : [])]);
} finally {
setLoading(false);
}
};
loadData();
}, [executeStored]);
//Вернём данные
return { equipDataSelectionList: data, equipDataSelectionListIsLoading: isLoading };
};
//Загрузка классов оборудования выбороки данных
const useEquipDataSelectionClassMachineList = dataSelection => {
//Собственное состояние - флаг загрузки
const [isLoading, setLoading] = useState(false);
//Собственное состояние - данные
const [data, setData] = useState([]);
//Подключение к контексту взаимодействия с сервером
const { executeStored } = useContext(BackEndСtx);
//Загрузка данных при изменении зависимостейuse
useEffect(() => {
const loadData = async () => {
try {
setLoading(true);
const data = await executeStored({
stored: "UDO_PKG_EQUIPDS.CM_LIST",
args: { NEQUIPDS: dataSelection },
respArg: "COUT",
isArray: name => ["XDSCM"].includes(name),
attributeValueProcessor: (name, val) => (["SCODE"].includes(name) ? undefined : val),
loader: false
});
setData(data?.XDSCM || []);
} finally {
setLoading(false);
}
};
if (dataSelection && dataSelection != DS_RN_DEFAULT) loadData();
}, [dataSelection, executeStored]);
//Вернём данные
return { equipDataSelectionClassMachineList: data, equipDataSelectionClassMachineListIsLoading: isLoading };
};
//Загрузка списка файлов класса оборудования
const useEquipDataSelectionClassMachineFilesList = classMachine => {
//Собственное состояние - флаг загрузки
const [isLoading, setLoading] = useState(false);
//Собственное состояние - таблица данных
const [data, setData] = useState({
init: false,
columnsDef: [],
rows: []
});
//Подключение к контексту взаимодействия с сервером
const { executeStored } = useContext(BackEndСtx);
//Загрузка данных при изменении зависимостей
useEffect(() => {
const loadData = async () => {
try {
setLoading(true);
const data = await executeStored({
stored: "UDO_PKG_EQUIPDS.CMFL_LIST",
args: { NEQUIPDSCM: classMachine },
respArg: "COUT",
loader: false
});
setData(pv => ({
...pv,
columnsDef: [...data.XCOLUMNS_DEF],
rows: [...(data.XROWS || [])],
init: true
}));
} finally {
setLoading(false);
}
};
if (classMachine) loadData();
}, [classMachine, executeStored]);
//Вернём данные
return { equipDataSelectionClassMachineFilesList: data, equipDataSelectionClassMachineFilesListIsLoading: isLoading };
};
//Загрузка списка моделей класса оборудования
const useEquipDataSelectionClassMachineModelsList = classMachine => {
//Собственное состояние - флаг загрузки
const [isLoading, setLoading] = useState(false);
//Собственное состояние - таблица данных
const [data, setData] = useState({
init: false,
columnsDef: [],
rows: []
});
//Подключение к контексту взаимодействия с сервером
const { executeStored } = useContext(BackEndСtx);
//Загрузка данных при изменении зависимостей
useEffect(() => {
const loadData = async () => {
try {
setLoading(true);
const data = await executeStored({
stored: "UDO_PKG_EQUIPDS.CMML_LIST",
args: { NEQUIPDSCM: classMachine },
respArg: "COUT",
loader: false
});
setData(pv => ({
...pv,
columnsDef: [...data.XCOLUMNS_DEF],
rows: [...(data.XROWS || [])],
init: true
}));
} finally {
setLoading(false);
}
};
if (classMachine) loadData();
}, [classMachine, executeStored]);
//Вернём данные
return { equipDataSelectionClassMachineModelsList: data, equipDataSelectionClassMachineModelsListIsLoading: isLoading };
};
//----------------
//Интерфейс модуля
//----------------
export {
DS_RN_DEFAULT,
useEquipDataSelectionList,
useEquipDataSelectionClassMachineList,
useEquipDataSelectionClassMachineFilesList,
useEquipDataSelectionClassMachineModelsList
};

View File

@ -0,0 +1,206 @@
/*
Парус 8 - Панели мониторинга - ТОиР - Прогнозирование технического состояния
Закладка администрирования: дополнительная разметка и вёрстка клиентских элементов
*/
//---------------------
//Подключение библиотек
//---------------------
import React from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента
import {
Box,
Stack,
FormControl,
InputLabel,
Select,
MenuItem,
List,
ListItem,
ListItemButton,
ListItemText,
Card,
CardContent,
CardActions,
Typography,
Button
} from "@mui/material"; //Интерфейсные компоненты
import { APP_BAR_HEIGHT, TABS_HEIGHT, SCROLL_STYLES } from "./eqs_tech_cond_forecast_lyaout"; //Общие вспомогательные компоненты и вёрстка
import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../components/p8p_data_grid"; //Таблица данных
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
//---------
//Константы
//---------
//Высота списка выборок данных
const DS_SELECTOR_HEIGHT = "80px";
//Высота кнопки добавления технического объекта
const CM_ADD_BTN_HEIGHT = "46.5px";
//Структура элемента списка выборок данных оборудования
const EQUIP_DATA_SELECTION_LIST = PropTypes.shape({
NRN: PropTypes.number.isRequired,
SNAME: PropTypes.string.isRequired
});
//Структура элемента списка классов оборудования выборки данных
const EQUIP_DATA_SELECTION_CLASS_MACHINE_LIST = PropTypes.shape({
NRN: PropTypes.number.isRequired,
SCODE: PropTypes.string.isRequired,
SNAME: PropTypes.string.isRequired
});
//Стили
const STYLES = {
EQUIP_DS_LIST_FORM_CONTROL: { minWidth: "300px" },
EQUIP_DSCM_LIST: {
minHeight: `calc(100vh - ${APP_BAR_HEIGHT} - ${TABS_HEIGHT} - ${DS_SELECTOR_HEIGHT} - ${CM_ADD_BTN_HEIGHT})`,
maxHeight: `calc(100vh - ${APP_BAR_HEIGHT} - ${TABS_HEIGHT} - ${DS_SELECTOR_HEIGHT} - ${CM_ADD_BTN_HEIGHT})`,
overflowX: "hidden",
overflowY: "auto",
...SCROLL_STYLES
},
EQUIP_DSCM_CARD: {
minHeight: `calc(100vh - ${APP_BAR_HEIGHT} - ${TABS_HEIGHT} - ${DS_SELECTOR_HEIGHT})`,
maxHeight: `calc(100vh - ${APP_BAR_HEIGHT} - ${TABS_HEIGHT} - ${DS_SELECTOR_HEIGHT})`,
overflowY: "auto",
...SCROLL_STYLES
},
FL_ML_TABLE: {
height: `200px`,
...SCROLL_STYLES
}
};
//-----------
//Тело модуля
//-----------
//Список выборок данных оборудования
const EquipDataSelectionList = ({ list, value, onChange }) => {
//При выборе элемента
const handleChange = e => (onChange ? onChange(e.target.value) : null);
//Генерация содержимого
return (
<FormControl variant="standard" sx={STYLES.EQUIP_DS_LIST_FORM_CONTROL}>
<InputLabel id="equipDSlabel">Выборка данных оборудования</InputLabel>
<Select labelId="equipDSlabel" id="equipDS" label="Выборка данных оборудования" value={value} onChange={handleChange}>
{list.map((item, i) => (
<MenuItem key={i} value={item.NRN}>
{item.SNAME}
</MenuItem>
))}
</Select>
</FormControl>
);
};
//Контроль свойств - Список выборок данных оборудования
EquipDataSelectionList.propTypes = {
list: PropTypes.arrayOf(EQUIP_DATA_SELECTION_LIST).isRequired,
value: PropTypes.number.isRequired,
onChange: PropTypes.func
};
//Список классов оборудования выборки данных
const EquipDataSelectionClassMachineList = ({ list, value, onClick }) => {
//При щелчке на элементе
const handleClick = id => (onClick ? onClick(id) : null);
//Генерация содержимого
return (
<List sx={STYLES.EQUIP_DSCM_LIST}>
{list.map((item, i) => (
<ListItem disablePadding key={i}>
<ListItemButton selected={value == item.NRN} onClick={() => handleClick(item.NRN)}>
<ListItemText primary={item.SCODE} secondary={item.SNAME} />
</ListItemButton>
</ListItem>
))}
</List>
);
};
//Контроль свойств - Список классов оборудования выборки данных
EquipDataSelectionClassMachineList.propTypes = {
list: PropTypes.arrayOf(EQUIP_DATA_SELECTION_CLASS_MACHINE_LIST).isRequired,
value: PropTypes.number,
onClick: PropTypes.func
};
//Карточка класса оборудования выборки данных
const EquipDataSelectionClassMachineCard = ({ filesList, modelsList }) => {
//Генерация содержимого
return (
<Box p={2} sx={STYLES.EQUIP_DSCM_CARD}>
<Stack spacing={2}>
<Card>
<CardContent>
<Typography variant="h5" component="div">
Наименование технического объекта
</Typography>
<Typography sx={{ mb: 1.5 }} color="text.secondary">
Состояние: готов к ...
</Typography>
<Typography variant="body2">фывафывафва</Typography>
</CardContent>
<CardActions>
<Button size="large">Сформировать</Button>
<Button size="large">Передать данные</Button>
<Button size="large">Обучить модель</Button>
</CardActions>
</Card>
<Card>
<CardContent>
<Typography variant="h6" component="div">
Файлы данных
</Typography>
<P8PDataGrid
{...{ ...P8P_DATA_GRID_CONFIG_PROPS }}
containerComponentProps={{
sx: STYLES.FL_ML_TABLE,
elevation: 0
}}
columnsDef={filesList.columnsDef}
rows={filesList.rows}
size={P8P_DATA_GRID_SIZE.SMALL}
morePages={false}
fixedHeader={true}
reloading={false}
/>
</CardContent>
</Card>
<Card>
<CardContent>
<Typography variant="h6" component="div">
Модели
</Typography>
<P8PDataGrid
{...{ ...P8P_DATA_GRID_CONFIG_PROPS }}
containerComponentProps={{
sx: STYLES.FL_ML_TABLE,
elevation: 0
}}
columnsDef={modelsList.columnsDef}
rows={modelsList.rows}
size={P8P_DATA_GRID_SIZE.SMALL}
morePages={false}
fixedHeader={true}
reloading={false}
/>
</CardContent>
</Card>
</Stack>
</Box>
);
};
//----------------
//Интерфейс модуля
//----------------
export { EquipDataSelectionList, EquipDataSelectionClassMachineList, EquipDataSelectionClassMachineCard };

View File

@ -7,8 +7,9 @@
//Подключение библиотек
//---------------------
import React, { useState, useEffect } from "react"; //Классы React
import React, { useState } from "react"; //Классы React
import { Box, Tabs, Tab } from "@mui/material"; //Интерфейсные компоненты
import { MODES, TabPanel } from "./eqs_tech_cond_forecast_lyaout"; //Вспомогательные компоненты и вёрстка
import { AdminTab } from "./admin_tab"; //Интерфейс администрирования
import { ForecastTab } from "./forecast_tab"; //Интерфейс прогнозирования
@ -16,23 +17,19 @@ import { ForecastTab } from "./forecast_tab"; //Интерфейс прогно
//Константы
//---------
//Режимы работы панели
const MODES = {
FORECAST: 1,
ADMIN: 2
//Стили
const STYLES = {
TABS_CONTAINER_BOX: { borderBottom: 1, borderColor: "divider" }
};
//-----------
//Тело модуля
//-----------
//Закладка
const TapPanel = ({ children }) => <Box>{children}</Box>;
//Корневая панель прогнозирования технического состояния
const EqsTechCondForecast = () => {
//Собственное состояние
const [mode, setMode] = useState(MODES.FORECAST);
const [mode, setMode] = useState(MODES.ADMIN);
//При переключении закладки
const handleTabChange = (e, newValue) => setMode(newValue);
@ -40,22 +37,18 @@ const EqsTechCondForecast = () => {
//Генерация содержимого
return (
<>
<Box sx={{ borderBottom: 1, borderColor: "divider" }}>
<Box sx={STYLES.TABS_CONTAINER_BOX}>
<Tabs value={mode} onChange={handleTabChange}>
<Tab label="Прогноз" value={MODES.FORECAST} />
<Tab label="Обучение" value={MODES.ADMIN} />
</Tabs>
</Box>
{mode == MODES.ADMIN ? (
<TapPanel>
<AdminTab />{" "}
</TapPanel>
) : null}
{mode == MODES.FORECAST ? (
<TapPanel>
<TabPanel mode={mode} value={MODES.FORECAST}>
<ForecastTab />
</TapPanel>
) : null}
</TabPanel>
<TabPanel mode={mode} value={MODES.ADMIN}>
<AdminTab />
</TabPanel>
</>
);
};

View File

@ -0,0 +1,73 @@
/*
Парус 8 - Панели мониторинга - ТОиР - Прогнозирование технического состояния
Корневая панель прогнозирования технического состояния: дополнительная разметка и вёрстка клиентских элементов
*/
//---------------------
//Подключение библиотек
//---------------------
import React from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента
import { Box } from "@mui/material"; //Интерфейсные компоненты
//---------
//Константы
//---------
//Режимы работы панели
const MODES = {
FORECAST: 1,
ADMIN: 2
};
//Высота главного меню
const APP_BAR_HEIGHT = "64px";
//Высота закладок
const TABS_HEIGHT = "49px";
//Высота кнопки "Ещё"
const TABLE_MORE_HEIGHT = "49px";
//Высота фильтров таблицы
const TABLE_FILTERS_HEIGHT = "48px";
//Стиль скролов
const SCROLL_STYLES = {
"&::-webkit-scrollbar": {
height: "8px",
width: "8px"
},
"&::-webkit-scrollbar-track": {
borderRadius: "8px",
backgroundColor: "#EBEBEB"
},
"&::-webkit-scrollbar-thumb": {
borderRadius: "8px",
backgroundColor: "#b4b4b4"
},
"&::-webkit-scrollbar-thumb:hover": {
backgroundColor: "#808080"
}
};
//-----------
//Тело модуля
//-----------
//Закладка
const TabPanel = ({ mode, value, children }) => <Box hidden={mode != value}>{children}</Box>;
//Контроль свойств - Закладка
TabPanel.propTypes = {
mode: PropTypes.number.isRequired,
value: PropTypes.number.isRequired,
children: PropTypes.element
};
//----------------
//Интерфейс модуля
//----------------
export { MODES, APP_BAR_HEIGHT, TABS_HEIGHT, TABLE_MORE_HEIGHT, TABLE_FILTERS_HEIGHT, SCROLL_STYLES, TabPanel };

View File

@ -13,25 +13,14 @@ import { RichTreeView } from "@mui/x-tree-view/RichTreeView"; //Дерево
import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений
import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../components/p8p_data_grid"; //Таблица данных
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
import { useEqConfigTree, useEqConfigTechObjTable, useEqConfigTechObjCard, findTreeItem, needLoadLevel } from "./forecast_tab_hooks"; //Вспомогательные хуки
import { APP_BAR_HEIGHT, TABS_HEIGHT, TABLE_FILTERS_HEIGHT, TABLE_MORE_HEIGHT, SCROLL_STYLES } from "./eqs_tech_cond_forecast_lyaout"; //Общие Вспомогательные компоненты и вёрстка
import { TechObjCard, eqConfigTechObjTableValueFormatter } from "./forecast_tab_layout"; //Вспомогательные компоненты и вёрстка
import { useEqConfigTree, useEqConfigTechObjTable, useEqConfigTechObjCard, findTreeItem, needLoadLevel } from "./forecast_tab_hooks"; //Вспомогательные хуки
//---------
//Константы
//---------
//Высота главного меню
const APP_BAR_HEIGHT = "64px";
//Высота закладок
const TABS_HEIGHT = "49px";
//Высота кнопки "Ещё"
const TABLE_MORE_HEIGHT = "49px";
//Высота фильтров таблицы
const TABLE_FILTERS_HEIGHT = "48px";
//Стили
const STYLES = {
LEFT_SIDE_GRID: {},
@ -40,15 +29,15 @@ const STYLES = {
minHeight: `calc(100vh - ${APP_BAR_HEIGHT} - ${TABS_HEIGHT})`,
maxHeight: `calc(100vh - ${APP_BAR_HEIGHT} - ${TABS_HEIGHT})`,
overflowX: "hidden",
overflowY: "scroll",
scrollbarWidth: "thin",
padding: "3px"
overflowY: "auto",
padding: "3px",
...SCROLL_STYLES
},
TECH_OBJ_TABLE: (morePages, filters) => ({
height: `calc(100vh - ${APP_BAR_HEIGHT} - ${TABS_HEIGHT} - ${morePages ? TABLE_MORE_HEIGHT : "0px"} - ${
filters ? TABLE_FILTERS_HEIGHT : "0px"
})`,
scrollbarWidth: "thin"
...SCROLL_STYLES
})
};
@ -74,7 +63,7 @@ const ForecastTab = () => {
const [techObjCardId, setTechObjCardId] = useState(null);
//Загрузчик веток дерева
const { tree } = useEqConfigTree(loadingTreeItem);
const { eQconfigTree } = useEqConfigTree(loadingTreeItem);
//Загрузчик технических объектов для выбранной ветки
const { techObjsDataGrid, techObjsDataGridIsLoading } = useEqConfigTechObjTable(
@ -89,11 +78,11 @@ const ForecastTab = () => {
//Обработка развёртывания/свёртывания уровня дерева
const handleTreeItemExpansionToggle = (event, itemId, isExpanded) =>
isExpanded && needLoadLevel(tree, itemId) ? setLoadingTreeItem(parseInt(itemId)) : null;
isExpanded && needLoadLevel(eQconfigTree, itemId) ? setLoadingTreeItem(parseInt(itemId)) : null;
//Обработка фокусировки на элементе дерева
const handleTreeItemFocus = (event, itemId) => {
const item = findTreeItem(tree, itemId);
const item = findTreeItem(eQconfigTree, itemId);
if (item && item?.showCard) setTechObjCardId(parseInt(itemId));
else {
setTechObjCardId(null);
@ -124,7 +113,7 @@ const ForecastTab = () => {
<Grid container>
<Grid item xs={3} sx={STYLES.LEFT_SIDE_GRID}>
<Box sx={STYLES.TREE_BOX}>
<RichTreeView items={tree} onItemExpansionToggle={handleTreeItemExpansionToggle} onItemFocus={handleTreeItemFocus} />
<RichTreeView items={eQconfigTree} onItemExpansionToggle={handleTreeItemExpansionToggle} onItemFocus={handleTreeItemFocus} />
</Box>
</Grid>
<Grid item xs={9} sx={STYLES.RIGHT_SIDE_GRID}>

View File

@ -1,6 +1,6 @@
/*
Парус 8 - Панели мониторинга - ТОиР - Прогнозирование технического состояния
Кастомные хуки закладки Прогнозирования
Закладка прогнозирования: кастомные хуки
*/
//---------------------
@ -91,7 +91,7 @@ const useEqConfigTree = parent => {
}, [parent, executeStored]);
//Вернём данные
return { tree, isLoading };
return { eQconfigTree: tree, eQconfigTreeIsLoading: isLoading };
};
//Загрузка списка технических объектов
@ -151,7 +151,7 @@ const useEqConfigTechObjCard = id => {
//Собственное состояние - флаг загрузки
const [isLoading, setLoading] = useState(false);
//Собственное состояние - данные дерева
//Собственное состояние - данные карточки
const [card, setCard] = useState({});
//Подключение к контексту взаимодействия с сервером

View File

@ -1,6 +1,6 @@
/*
Парус 8 - Панели мониторинга - ТОиР - Прогнозирование технического состояния
Дополнительная разметка и вёрстка клиентских элементов
Закладка прогнозирования: дополнительная разметка и вёрстка клиентских элементов
*/
//---------------------
@ -12,6 +12,16 @@ import PropTypes from "prop-types"; //Контроль свойств компо
import { Box, Card, CardContent, Typography, CardActions, Button } from "@mui/material"; //Интерфейсные компоненты
import { formatDateRF } from "../../core/utils"; //Вспомогательные функции
//---------
//Константы
//---------
//Стили
const STYLES = {
TECH_OBJ_CARD_TITLE: { fontSize: 14 },
TECH_OBJ_CARD_SUB_TITLE: { mb: 1.5 }
};
//-----------
//Тело модуля
//-----------
@ -20,16 +30,16 @@ import { formatDateRF } from "../../core/utils"; //Вспомогательны
const TechObjCard = ({ cardData }) => {
//Генерация содержимого
return (
<Box p={3}>
<Box p={2}>
<Card>
<CardContent>
<Typography sx={{ fontSize: 14 }} color="text.secondary" gutterBottom>
<Typography sx={STYLES.TECH_OBJ_CARD_TITLE} color="text.secondary" gutterBottom>
Технический объект
</Typography>
<Typography variant="h5" component="div">
{cardData.SCODE}
</Typography>
<Typography sx={{ mb: 1.5 }} color="text.secondary">
<Typography sx={STYLES.TECH_OBJ_CARD_SUB_TITLE} color="text.secondary">
{cardData.DOPER_DATE}
</Typography>
<Typography variant="body2">{cardData.SNAME}</Typography>