diff --git a/app/panels/mech_rec_assembly_mon/backend.js b/app/panels/mech_rec_assembly_mon/backend.js
index 59f1cda..7264b6b 100644
--- a/app/panels/mech_rec_assembly_mon/backend.js
+++ b/app/panels/mech_rec_assembly_mon/backend.js
@@ -11,7 +11,7 @@ import { object2Base64XML } from "../../core/utils"; //Вспомогатель
//---------
//Размер страницы данных
-const DATA_GRID_PAGE_SIZE = 10;
+const DATA_GRID_PAGE_SIZE = 0;
//-----------
//Тело модуля
@@ -25,10 +25,10 @@ const useMechRecAssemblyMon = () => {
showPlanList: false,
planCtlgs: [],
planCtlgsLoaded: false,
- selectedPlanCtlg: { NRN: null, SNAME: null, NMIN_YEAR: null, NMAX_YEAR: null },
+ selectedPlanCtlg: {},
plans: [],
plansLoaded: false,
- selectedPlan: { NRN: null, SNUMB: null, NPROGRESS: null, SDETAIL: null, NYEAR: null }
+ selectedPlan: {}
});
//Подключение к контексту взаимодействия с сервером
@@ -66,19 +66,21 @@ const useMechRecAssemblyMon = () => {
);
//Выбор каталога планов
- const selectPlan = project => {
+ const selectPlanCtlg = planCtlg => {
setState(pv => ({
...pv,
- selectedPlanCtlg: project,
+ selectedPlanCtlg: { ...planCtlg },
+ selectedPlan: {},
showPlanList: false
}));
};
//Сброс выбора каталога планов
- const unselectPlan = () =>
+ const unselectPlanCtlg = () =>
setState(pv => ({
...pv,
- selectedPlanCtlg: { NRN: null, SNAME: null, NMIN_YEAR: null, NMAX_YEAR: null },
+ selectedPlanCtlg: {},
+ selectedPlan: {},
showPlanList: false
}));
@@ -99,7 +101,7 @@ const useMechRecAssemblyMon = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [state.selectedPlanCtlg]);
- return [state, setState, selectPlan, unselectPlan];
+ return [state, setState, selectPlanCtlg, unselectPlanCtlg];
};
//Хук для информации по производственным составам
@@ -109,6 +111,8 @@ const useCostProductComposition = nProdPlan => {
init: false,
showPlanList: false,
products: [],
+ productsLoaded: false,
+ model: null,
selectedProduct: null
});
@@ -124,7 +128,13 @@ const useCostProductComposition = nProdPlan => {
respArg: "COUT",
isArray: name => name === "XFCPRODCMP"
});
- setCostProductComposition(pv => ({ ...pv, init: true, products: [...(data?.XFCPRODCMP || [])], productsLoaded: true }));
+ setCostProductComposition(pv => ({
+ ...pv,
+ init: true,
+ products: [...(data?.XFCPRODCMP || [])],
+ productsLoaded: true,
+ model: data?.BMODEL
+ }));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [costProductComposition.init, executeStored]);
@@ -138,204 +148,53 @@ const useCostProductComposition = nProdPlan => {
return [costProductComposition, setCostProductComposition];
};
-//Хук для таблицы маршрутных листов
-const useCostRouteLists = (plan, product) => {
+//Хук для таблицы детализации изделия
+const useProductDetailsTable = (plan, product, orders, pageNumber, stored) => {
+ //Собственное состояние - флаг загрузки
+ const [isLoading, setLoading] = useState(true);
+
//Собственное состояние - таблица данных
- const [costRouteLists, setCostRouteLists] = useState({
- dataLoaded: false,
+ const [data, setData] = useState({
columnsDef: [],
- orders: null,
rows: [],
- reload: true,
- pageNumber: 1,
- morePages: true,
- selectedProduct: null
+ morePages: true
});
//Подключение к контексту взаимодействия с сервером
const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
- //Загрузка данных таблицы с сервера
- const loadData = useCallback(
- async () => {
- if (costRouteLists.reload) {
+ //Загрузка данных при изменении зависимостей
+ useEffect(() => {
+ const loadData = async () => {
+ try {
+ setLoading(true);
const data = await executeStored({
- stored: "PKG_P8PANELS_MECHREC.FCROUTLST_MON_DG_GET",
+ stored,
args: {
NPRODCMPSP: product,
NFCPRODPLAN: plan,
- CORDERS: { VALUE: object2Base64XML(costRouteLists.orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
- NPAGE_NUMBER: costRouteLists.pageNumber,
+ CORDERS: { VALUE: object2Base64XML(orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
+ NPAGE_NUMBER: pageNumber,
NPAGE_SIZE: DATA_GRID_PAGE_SIZE,
- NINCLUDE_DEF: costRouteLists.dataLoaded ? 0 : 1
+ NINCLUDE_DEF: 1
},
respArg: "COUT"
});
- setCostRouteLists(pv => ({
+ setData(pv => ({
...pv,
columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
- rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
- dataLoaded: true,
- reload: false,
- morePages: (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE
+ rows: pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
+ morePages: DATA_GRID_PAGE_SIZE == 0 ? false : (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE
}));
+ } finally {
+ setLoading(false);
}
- },
- // eslint-disable-next-line react-hooks/exhaustive-deps
- [costRouteLists.reload, costRouteLists.orders, costRouteLists.dataLoaded, costRouteLists.pageNumber, executeStored, SERV_DATA_TYPE_CLOB]
- );
+ };
+ if (plan && product) loadData();
+ }, [plan, product, orders, pageNumber, stored, executeStored, SERV_DATA_TYPE_CLOB]);
- //При изменении продукта
- useEffect(() => {
- //Если продукт указан
- if (product) {
- //Принудительно обновляем состояние
- setCostRouteLists(pv => ({
- ...pv,
- dataLoaded: false,
- columnsDef: [],
- orders: null,
- rows: [],
- reload: true,
- pageNumber: 1,
- morePages: true,
- selectedProduct: null
- }));
- //Загружаем данные с учетом выбранного продукта
- loadData();
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [product]);
-
- //При необходимости обновить данные таблицы
- useEffect(() => {
- //Если продукт указан и необходимо стандартное обновление
- if (product) {
- loadData();
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [costRouteLists.reload, loadData]);
-
- //При изменении плана
- useEffect(() => {
- setCostRouteLists(pv => ({
- ...pv,
- dataLoaded: false,
- columnsDef: [],
- orders: null,
- rows: [],
- reload: true,
- pageNumber: 1,
- morePages: true,
- selectedProduct: null
- }));
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [plan]);
-
- return [costRouteLists, setCostRouteLists];
+ //Вернём данные
+ return { data, isLoading };
};
-//Хук для таблицы комплектовочных ведомостей
-const useCostDeliverySheets = (plan, product) => {
- //Собственное состояние - таблица данных
- const [costDeliverySheets, setCostDeliverySheets] = useState({
- dataLoaded: false,
- columnsDef: [],
- orders: null,
- rows: [],
- reload: true,
- pageNumber: 1,
- morePages: true,
- selectedProduct: null
- });
-
- //Подключение к контексту взаимодействия с сервером
- const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
-
- //Загрузка данных таблицы с сервера
- const loadData = useCallback(
- async () => {
- if (costDeliverySheets.reload) {
- const data = await executeStored({
- stored: "PKG_P8PANELS_MECHREC.FCDELIVSH_DG_GET",
- args: {
- NPRODCMPSP: product,
- NFCPRODPLAN: plan,
- CORDERS: { VALUE: object2Base64XML(costDeliverySheets.orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
- NPAGE_NUMBER: costDeliverySheets.pageNumber,
- NPAGE_SIZE: DATA_GRID_PAGE_SIZE,
- NINCLUDE_DEF: costDeliverySheets.dataLoaded ? 0 : 1
- },
- respArg: "COUT"
- });
- setCostDeliverySheets(pv => ({
- ...pv,
- columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
- rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
- dataLoaded: true,
- reload: false,
- morePages: (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE
- }));
- }
- },
- // eslint-disable-next-line react-hooks/exhaustive-deps
- [
- costDeliverySheets.reload,
- costDeliverySheets.orders,
- costDeliverySheets.dataLoaded,
- costDeliverySheets.pageNumber,
- executeStored,
- SERV_DATA_TYPE_CLOB
- ]
- );
-
- //При изменении продукта
- useEffect(() => {
- //Если продукт указан
- if (product) {
- //Принудительно обновляем состояние
- setCostDeliverySheets(pv => ({
- ...pv,
- dataLoaded: false,
- columnsDef: [],
- orders: null,
- rows: [],
- reload: true,
- pageNumber: 1,
- morePages: true
- }));
- //Загружаем данные с учетом выбранного продукта
- loadData();
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [product]);
-
- //При необходимости обновить данные таблицы
- useEffect(() => {
- //Если продукт указан и необходимо стандартное обновление
- if (product) {
- loadData();
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [costDeliverySheets.reload, loadData]);
-
- //При изменении плана
- useEffect(() => {
- setCostDeliverySheets(pv => ({
- ...pv,
- dataLoaded: false,
- columnsDef: [],
- orders: null,
- rows: [],
- reload: true,
- pageNumber: 1,
- morePages: true,
- selectedProduct: null
- }));
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [plan]);
-
- return [costDeliverySheets, setCostDeliverySheets];
-};
-
-export { useMechRecAssemblyMon, useCostProductComposition, useCostRouteLists, useCostDeliverySheets };
+export { useMechRecAssemblyMon, useCostProductComposition, useProductDetailsTable };
diff --git a/app/panels/mech_rec_assembly_mon/blocks/cardBlock.js b/app/panels/mech_rec_assembly_mon/blocks/cardBlock.js
deleted file mode 100644
index fc1d05d..0000000
--- a/app/panels/mech_rec_assembly_mon/blocks/cardBlock.js
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- Парус 8 - Панели мониторинга - ПУП - Мониторинг сборки изделий
- Панель мониторинга: Информация об объекте
-*/
-
-//---------------------
-//Подключение библиотек
-//---------------------
-
-import React from "react"; //Классы React
-import PropTypes from "prop-types"; //Контроль свойств компонента
-import { Typography, Box, ImageList, ImageListItem } from "@mui/material"; //Интерфейсные элементы
-import { ProgressBox } from "../elements/progressBox"; //Блок информации по прогрессу объекта
-
-//---------
-//Константы
-//---------
-
-//Стили
-const STYLES = {
- PLAN_INFO: {
- display: "flex",
- justifyContent: "center",
- alignItems: "center",
- flexDirection: "column",
- gap: "24px",
- border: "1px solid",
- borderRadius: "25px"
- }
-};
-
-//------------------------------------
-//Вспомогательные функции и компоненты
-//------------------------------------
-
-//Картинка объекта
-const CardImage = ({ card }) => {
- return (
-
-
-
-
- {/*
*/}
-
-
-
- );
-};
-
-//Контроль свойств - Заголовок первого уровня
-CardImage.propTypes = {
- card: PropTypes.object
-};
-
-//-----------
-//Тело модуля
-//-----------
-
-//Информация об объекте
-const CardBlock = ({ card, handleCardClick }) => {
- return (
- handleCardClick(card)}>
-
-
-
- Номер борта
-
- {card.SNUMB}
-
-
-
-
- Год выпуска:
-
-
- {card.NYEAR}
-
-
-
- );
-};
-
-//Контроль свойств - Заголовок первого уровня
-CardBlock.propTypes = {
- card: PropTypes.object,
- handleCardClick: PropTypes.func
-};
-
-//----------------
-//Интерфейс модуля
-//----------------
-
-export { CardBlock };
diff --git a/app/panels/mech_rec_assembly_mon/blocks/cardDetail.js b/app/panels/mech_rec_assembly_mon/blocks/cardDetail.js
deleted file mode 100644
index 3304045..0000000
--- a/app/panels/mech_rec_assembly_mon/blocks/cardDetail.js
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- Парус 8 - Панели мониторинга - ПУП - Мониторинг сборки изделий
- Панель мониторинга: Детализация по объекту
-*/
-
-//---------------------
-//Подключение библиотек
-//---------------------
-
-import React from "react"; //Классы React
-import PropTypes from "prop-types"; //Контроль свойств компонента
-import { Box, Grid, Container, Button, Typography } from "@mui/material"; //Интерфейсные элементы
-import { ProgressBox } from "../elements/progressBox"; //Блок информации по прогрессу объекта
-import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../../components/p8p_data_grid"; //Таблица данных
-import { P8P_DATA_GRID_CONFIG_PROPS } from "../../../config_wrapper"; //Подключение компонентов к настройкам приложения
-import { useCostProductComposition, useCostRouteLists, useCostDeliverySheets } from "../backend"; //Компоненты панели
-
-//---------
-//Константы
-//---------
-
-//Стили
-const STYLES = {
- TABLE_INFO_MAIN: {
- display: "flex",
- justifyContent: "center",
- alignItems: "center",
- flexDirection: "column",
- border: "1px solid",
- borderRadius: "25px",
- height: "35vh"
- },
- TABLE_INFO_SUB: {
- margin: "21.6px 0px",
- maxHeight: "100%",
- overflow: "auto",
- textAlign: "center",
- width: "100%"
- },
- DETAIL_INFO: {
- display: "flex",
- justifyContent: "space-around",
- alignItems: "center",
- border: "1px solid",
- borderRadius: "25px",
- height: "17vh"
- },
- PRODUCT_SELECTOR: {
- display: "flex",
- justifyContent: "center",
- alignItems: "center",
- flexDirection: "column",
- border: "1px solid",
- borderRadius: "25px",
- height: "53vh",
- marginTop: "16px"
- },
- PLAN_INFO_MAIN: {
- display: "flex",
- flexDirection: "column",
- gap: "16px"
- },
- PLAN_INFO_SUB: {
- display: "flex",
- justifyContent: "space-between",
- width: "280px",
- borderBottom: "1px solid"
- }
-};
-
-//------------------------------------
-//Вспомогательные функции и компоненты
-//------------------------------------
-
-//Информация об объекте
-const CardDetailInfo = ({ cardInfo }) => {
- return (
- <>
-
-
-
- Номер борта:
-
- {cardInfo.SNUMB}
-
-
-
- Год выпуска:
-
- {cardInfo.NYEAR}
-
-
-
- >
- );
-};
-
-//Контроль свойств - Информация об объекте
-CardDetailInfo.propTypes = {
- cardInfo: PropTypes.object
-};
-
-//Детали объекта
-const CardSelector = ({ products, setCostProductComposition }) => {
- //При выборе детали в SVG
- const handleProductClick = product => {
- setCostProductComposition(pv => ({ ...pv, selectedProduct: product }));
- };
-
- return (
- <>
-
- {products.map(el => (
-
- ))}
-
- >
- );
-};
-
-//Контроль свойств - Детали объекта
-CardSelector.propTypes = {
- products: PropTypes.array,
- setCostProductComposition: PropTypes.func
-};
-
-//Генерация представления ячейки заголовка
-const headCellRender = ({ columnDef }) => {
- //Описываем общий стиль
- let cellStyle = { padding: "2px 5px", fontSize: "12px", textAlign: "center", lineHeight: "1rem" };
- let stackProps = { justifyContent: "center" };
- //Дополнительные свойства
- switch (columnDef.name) {
- case "NREMN_LABOUR":
- //Добавляем максимальную ширину
- cellStyle = { ...cellStyle, maxWidth: "90px" };
- break;
- case "NDEFICIT":
- //Добавляем максимальную ширину
- cellStyle = { ...cellStyle, maxWidth: "55px" };
- break;
- case "NAPPLICABILITY":
- //Добавляем максимальную ширину
- cellStyle = { ...cellStyle, maxWidth: "90px" };
- break;
- default:
- break;
- }
- return {
- stackProps,
- cellStyle
- };
-};
-
-//Генерация заливки строки исходя от значений
-const dataCellRender = ({ row, columnDef }) => {
- //Описываем общий стиль
- let cellStyle = { padding: "2px 5px", fontSize: "12px" };
- //Для всех кроме содержания и номенклатуры добавляем выравнивание
- switch (columnDef.name) {
- case "SOPERATION":
- break;
- case "SNOMEN":
- break;
- default:
- //Добавляем выравнивание
- cellStyle = { ...cellStyle, textAlign: "center" };
- break;
- }
- return {
- cellStyle,
- data: row[columnDef]
- };
-};
-
-//-----------
-//Тело модуля
-//-----------
-
-//Детализация по объекту
-const CardDetail = ({ card, handleBackClick }) => {
- //Собственное состояние - данные производственных составов SVG
- const [costProductComposition, setCostProductComposition] = useCostProductComposition(card.NRN);
- //Собственное состояние - таблица данных маршрутных листов
- const [costRouteLists, setCostRouteLists] = useCostRouteLists(card.NRN, costProductComposition.selectedProduct);
- //Собственное состояние - таблица данных комплектовочных ведомостей
- const [сostDeliverySheets, setСostDeliverySheets] = useCostDeliverySheets(card.NRN, costProductComposition.selectedProduct);
-
- //При изменении состояния сортировки маршрутных листов
- const costRouteListsOrderChanged = ({ orders }) => setCostRouteLists(pv => ({ ...pv, orders: [...orders], pageNumber: 1, reload: true }));
-
- //При изменении количества отображаемых страниц маршрутных листов
- const costRouteListsPagesCountChanged = () => setCostRouteLists(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reload: true }));
-
- //При изменении состояния сортировки комплектовочных ведомостей
- const СostDeliverySheetsOrderChanged = ({ orders }) => setСostDeliverySheets(pv => ({ ...pv, orders: [...orders], pageNumber: 1, reload: true }));
-
- //При изменении количества отображаемых страниц комплектовочных ведомостей
- const СostDeliverySheetsPagesCountChanged = () => setСostDeliverySheets(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reload: true }));
-
- return (
-
-
-
-
-
-
- {!costRouteLists.dataLoaded ? (
- Выберите агрегат самолёта, чтобы увидеть информацию
- ) : costRouteLists.rows.length === 0 ? (
- Нет данных по МК
- ) : (
- <>
- Маршрутная карта
-
- >
- )}
-
-
-
-
- {!сostDeliverySheets.dataLoaded ? (
- Выберите агрегат самолёта, чтобы увидеть информацию
- ) : сostDeliverySheets.rows.length === 0 ? (
- Нет данных по КВ
- ) : (
- <>
- Дефицит по КВ
-
- >
- )}
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-//Контроль свойств - Детализация по объекту
-CardDetail.propTypes = {
- card: PropTypes.object,
- handleBackClick: PropTypes.func
-};
-
-//----------------
-//Интерфейс модуля
-//----------------
-
-export { CardDetail };
diff --git a/app/panels/mech_rec_assembly_mon/components/plan_detail.js b/app/panels/mech_rec_assembly_mon/components/plan_detail.js
new file mode 100644
index 0000000..7068b2a
--- /dev/null
+++ b/app/panels/mech_rec_assembly_mon/components/plan_detail.js
@@ -0,0 +1,324 @@
+/*
+ Парус 8 - Панели мониторинга - ПУП - Мониторинг сборки изделий
+ Панель мониторинга: Детализация по объекту
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React, { useEffect, useState } from "react"; //Классы React
+import PropTypes from "prop-types"; //Контроль свойств компонента
+import { Box, Grid, Container, Button, Typography, Icon, Stack, IconButton } from "@mui/material"; //Интерфейсные элементы
+import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../../components/p8p_data_grid"; //Таблица данных
+import { P8PSVG } from "../../../components/p8p_svg"; //Интерактивные изображения
+import { P8P_DATA_GRID_CONFIG_PROPS } from "../../../config_wrapper"; //Подключение компонентов к настройкам приложения
+import { useCostProductComposition, useProductDetailsTable } from "../backend"; //Взаимодействие с сервером
+import { ProgressBox } from "./progress_box"; //Информация по прогрессу объекта
+
+//---------
+//Константы
+//---------
+
+//Стили
+const STYLES = {
+ BOX_INFO_MAIN: {
+ border: "1px solid",
+ borderRadius: "25px",
+ height: "35vh"
+ },
+ BOX_INFO_SUB: isMessage => ({
+ overflow: "hidden",
+ textAlign: "center",
+ width: "100%",
+ height: "100%",
+ display: "flex",
+ flexDirection: "column",
+ justifyContent: isMessage ? "center" : "flex-start",
+ paddingLeft: "5px",
+ paddingRight: "5px",
+ ...(isMessage ? { padding: "5px" } : { paddingTop: "10px" })
+ }),
+ DETAIL_INFO: {
+ display: "flex",
+ justifyContent: "space-around",
+ alignItems: "center",
+ border: "1px solid",
+ borderRadius: "25px",
+ height: "17vh"
+ },
+ PRODUCT_SELECTOR_CONTAINER: {
+ display: "flex",
+ justifyContent: "center",
+ alignItems: "center",
+ flexDirection: "column",
+ border: "1px solid",
+ borderRadius: "25px",
+ height: "53vh",
+ marginTop: "16px"
+ },
+ PRODUCT_SELECTOR_MODEL: { width: "70%" },
+ PLAN_INFO_MAIN: {
+ display: "flex",
+ flexDirection: "column",
+ gap: "16px"
+ },
+ PLAN_INFO_SUB: {
+ display: "flex",
+ justifyContent: "space-between",
+ width: "280px",
+ borderBottom: "1px solid"
+ },
+ TABLE_DETAILS: { height: "260px" },
+ TABLE_DETAILS_HEADER_CELL: maxWidth => ({
+ padding: "2px 2px",
+ fontSize: "11px",
+ textAlign: "center",
+ lineHeight: "1rem",
+ ...(maxWidth ? { maxWidth } : {})
+ }),
+ TABLE_DETAILS_DATA_CELL: textAlign => ({ padding: "2px 2px", fontSize: "11px", ...(textAlign ? { textAlign } : {}) }),
+ CARD_DETAILS_CONTAINER: { minWidth: "1200px", maxWidth: "1400px" },
+ CARD_DETAILS_NAVIGATION_STACK: { width: "100%" }
+};
+
+//------------------------------------
+//Вспомогательные функции и компоненты
+//------------------------------------
+
+//Информация о плане
+const PlanInfo = ({ plan }) => {
+ return (
+ <>
+
+
+
+ Номер борта:
+
+ {plan.SNUMB}
+
+
+
+ Год выпуска:
+
+ {plan.NYEAR}
+
+
+
+ >
+ );
+};
+
+//Контроль свойств - Информация о плане
+PlanInfo.propTypes = {
+ plan: PropTypes.object
+};
+
+//Модель выпуска плана
+const PlanProductCompositionModel = ({ model, products, setCostProductComposition }) => {
+ //При выборе детали на модели
+ const handleProductClick = ({ item }) => {
+ const product = products.find(p => p.SMODEL_ID == item.id);
+ if (product) setCostProductComposition(pv => ({ ...pv, selectedProduct: { ...product } }));
+ };
+
+ //Генерация содержимого
+ return (
+ <>
+
+ {model ? (
+ ({ id: p.SMODEL_ID, backgroundColor: p.SMODEL_BG_COLOR || "red", desc: p.SNAME, title: p.SNAME }))}
+ fillOpacity={"0.3"}
+ onItemClick={handleProductClick}
+ />
+ ) : (
+ Модель изделия не загружена
+ )}
+
+ >
+ );
+};
+
+//Контроль свойств - Модель выпуска плана
+PlanProductCompositionModel.propTypes = {
+ model: PropTypes.any,
+ products: PropTypes.array,
+ setCostProductComposition: PropTypes.func
+};
+
+//Генерация представления ячейки заголовка
+const headCellRender = ({ columnDef }) => ({
+ stackProps: { justifyContent: "center" },
+ cellStyle: STYLES.TABLE_DETAILS_HEADER_CELL(
+ ["NREMN_LABOUR", "NAPPLICABILITY"].includes(columnDef.name) ? "90px" : ["NDEFICIT"].includes(columnDef.name) ? "55px" : null
+ )
+});
+
+//Генерация заливки строки исходя от значений
+const dataCellRender = ({ row, columnDef }) => ({
+ cellStyle: STYLES.TABLE_DETAILS_DATA_CELL(["SOPERATION", "SNOMEN"].includes(columnDef.name) ? null : "center"),
+ data: row[columnDef]
+});
+
+//Таблица детализации изделия
+const ProductDetailsTable = ({ plan, product, stored, noProductMessage, noDataFoundMessage, title }) => {
+ //Собственное состояние
+ const [state, setState] = useState({ plan: null, product: null, orders: null, pageNumber: 1 });
+
+ //Собственное состояние - данные таблицы
+ const { data, isLoading } = useProductDetailsTable(state.plan, state.product, state.orders, state.pageNumber, stored);
+
+ //При изменении состояния сортировки
+ const handleOrderChanged = ({ orders }) => setState(pv => ({ ...pv, orders: [...orders], pageNumber: 1 }));
+
+ //При изменении количества отображаемых страниц
+ const handlePagesCountChanged = () => setState(pv => ({ ...pv, pageNumber: pv.pageNumber + 1 }));
+
+ //При изменении изделия
+ useEffect(() => {
+ setState(pv => ({ ...pv, plan, product, orders: null, pageNumber: 1 }));
+ }, [product, plan]);
+
+ //Генерация содержимого
+ return (
+
+ {!product ? (
+ {noProductMessage}
+ ) : isLoading ? null : data.rows.length === 0 ? (
+ {noDataFoundMessage}
+ ) : (
+ <>
+
+ {title}
+
+
+ >
+ )}
+
+ );
+};
+
+//Контроль свойств - Таблица детализации изделия
+ProductDetailsTable.propTypes = {
+ plan: PropTypes.number.isRequired,
+ product: PropTypes.number,
+ stored: PropTypes.string.isRequired,
+ noProductMessage: PropTypes.string.isRequired,
+ noDataFoundMessage: PropTypes.string.isRequired,
+ title: PropTypes.string.isRequired
+};
+
+//-----------
+//Тело модуля
+//-----------
+
+//Детализация по объекту
+const PlanDetail = ({ plan, disableNavigatePrev = false, disableNavigateNext = false, onNavigate, onBack }) => {
+ //Собственное состояние - данные производственных составов SVG
+ const [costProductComposition, setCostProductComposition] = useCostProductComposition(plan.NRN);
+
+ //Формируем представление
+ return (
+
+
+
+
+ (onNavigate ? onNavigate(-1) : null)}>
+ navigate_before
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ (onNavigate ? onNavigate(1) : null)}>
+ navigate_next
+
+
+
+
+
+ );
+};
+
+//Контроль свойств - Детализация по объекту
+PlanDetail.propTypes = {
+ plan: PropTypes.object,
+ disableNavigatePrev: PropTypes.bool,
+ disableNavigateNext: PropTypes.bool,
+ onNavigate: PropTypes.func,
+ onBack: PropTypes.func
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { PlanDetail };
diff --git a/app/panels/mech_rec_assembly_mon/components/plans_list.js b/app/panels/mech_rec_assembly_mon/components/plans_list.js
new file mode 100644
index 0000000..7debec8
--- /dev/null
+++ b/app/panels/mech_rec_assembly_mon/components/plans_list.js
@@ -0,0 +1,83 @@
+/*
+ Парус 8 - Панели мониторинга - ПУП - Мониторинг сборки изделий
+ Компонент: Список планов
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React, { useState } from "react"; //Классы React
+import { Container, Grid, IconButton, Icon } from "@mui/material"; //Интерфейсные элементы
+import PropTypes from "prop-types"; //Контроль свойств компонента
+import { PlansListItem } from "./plans_list_item"; //Элемент списка планов
+
+//---------
+//Константы
+//---------
+
+//Количество одновременно отображаемых элементов списка по умолчанию
+const DEFAULT_PAGE_SIZE = 5;
+
+//Стили
+const STYLES = {
+ PLAN_DOCUMENTS_LIST: { minWidth: "1024px" }
+};
+
+//-----------
+//Тело модуля
+//-----------
+
+//Список планов
+const PlansList = ({ plans, pageSize = DEFAULT_PAGE_SIZE, onItemClick }) => {
+ //Состояние прокрутки списка отображаемых планов
+ const [scroll, setScroll] = useState(0);
+
+ //Отработка нажатия на прокрутку списка планов влево
+ const handleScrollLeft = () => setScroll(pv => (pv <= 1 ? 0 : pv - 1));
+
+ //Отработка нажатия на прокрутку списка планов вправо
+ const handleScrollRight = () => setScroll(pv => (pv + pageSize >= plans.length ? pv : pv + 1));
+
+ //Сборка представления
+ return (
+
+
+
+
+ navigate_before
+
+
+ {plans.map((el, i) =>
+ i >= scroll && i < scroll + pageSize ? (
+
+ (onItemClick ? onItemClick(card, cardIndex) : null)}
+ />
+
+ ) : null
+ )}
+
+ = plans.length}>
+ navigate_next
+
+
+
+
+ );
+};
+
+//Контроль свойств - Список планов
+PlansList.propTypes = {
+ plans: PropTypes.arrayOf(PropTypes.object),
+ pageSize: PropTypes.number,
+ onItemClick: PropTypes.func
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { PlansList };
diff --git a/app/panels/mech_rec_assembly_mon/components/plans_list_item.js b/app/panels/mech_rec_assembly_mon/components/plans_list_item.js
new file mode 100644
index 0000000..d01e0be
--- /dev/null
+++ b/app/panels/mech_rec_assembly_mon/components/plans_list_item.js
@@ -0,0 +1,108 @@
+/*
+ Парус 8 - Панели мониторинга - ПУП - Мониторинг сборки изделий
+ Компонент: Элемент списка планов
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React from "react"; //Классы React
+import PropTypes from "prop-types"; //Контроль свойств компонента
+import { Typography, Box, ImageList, ImageListItem, Icon } from "@mui/material"; //Интерфейсные элементы
+import { ProgressBox } from "./progress_box"; //Информация по прогрессу объекта
+
+//---------
+//Константы
+//---------
+
+//Стили
+const STYLES = {
+ CONTAINER: {
+ display: "flex",
+ justifyContent: "center",
+ alignItems: "center",
+ flexDirection: "column",
+ gap: "24px",
+ border: "1px solid",
+ borderRadius: "25px",
+ cursor: "pointer"
+ },
+ IMAGE_BOX: { width: "180px", height: "180px", alignItems: "center", justifyContent: "center", display: "flex" },
+ IMAGE_LIST_ITEM: { textAlign: "center" },
+ IMAGE_IMG: { width: "160px" }
+};
+
+//------------------------------------
+//Вспомогательные функции и компоненты
+//------------------------------------
+
+//Изображение для элемента
+const PlansListItemImage = ({ card }) => {
+ return (
+
+
+
+ {card["BIMAGE"] ? (
+
+ ) : (
+ construction
+ )}
+
+
+
+ );
+};
+
+//Контроль свойств - Изображение для элемента
+PlansListItemImage.propTypes = {
+ card: PropTypes.object
+};
+
+//-----------
+//Тело модуля
+//-----------
+
+//Элемент списка планов
+const PlansListItem = ({ card, cardIndex, onClick }) => {
+ return (
+ (onClick ? onClick(card, cardIndex) : null)}>
+
+
+
+ Номер борта
+
+ {card.SNUMB}
+
+
+
+
+ Год выпуска:
+
+
+ {card.NYEAR}
+
+
+
+ );
+};
+
+//Контроль свойств - Элемент списка планов
+PlansListItem.propTypes = {
+ card: PropTypes.object,
+ cardIndex: PropTypes.number,
+ onClick: PropTypes.func
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { PlansListItem };
diff --git a/app/panels/mech_rec_assembly_mon/components/progress_box.js b/app/panels/mech_rec_assembly_mon/components/progress_box.js
new file mode 100644
index 0000000..169ff90
--- /dev/null
+++ b/app/panels/mech_rec_assembly_mon/components/progress_box.js
@@ -0,0 +1,75 @@
+/*
+ Парус 8 - Панели мониторинга - ПУП - Мониторинг сборки изделий
+ Компонент: Информация по прогрессу объекта
+*/
+
+//---------------------
+//Подключение библиотек
+//---------------------
+
+import React from "react"; //Классы React
+import PropTypes from "prop-types"; //Контроль свойств компонента
+import { Typography, Box } from "@mui/material"; //Интерфейсные элементы
+
+//---------
+//Константы
+//---------
+
+//Стили
+const STYLES = {
+ PROGRESS_BOX: (width, height) => ({
+ display: "flex",
+ justifyContent: "center",
+ alignItems: "center",
+ flexDirection: "column",
+ margin: "0px 32px",
+ borderRadius: "50%",
+ ...(width ? { width } : {}),
+ ...(height ? { height } : {})
+ })
+};
+
+//-----------
+//Тело модуля
+//-----------
+
+//Информация по прогрессу объекта
+const ProgressBox = ({ progress, detail, width, height, progressVariant, detailVariant }) => {
+ //Определяем цвет тени
+ let boxShadow = "0 0 30px #d3d3d3";
+ switch (true) {
+ case progress >= 70:
+ boxShadow = "0 0 30px #21d21e66";
+ break;
+ case progress >= 40:
+ boxShadow = "0 0 30px #fddd3566";
+ break;
+ case progress >= 10:
+ boxShadow = "0 0 30px #ea5c4966";
+ break;
+ }
+
+ //Возвращаем содержимое
+ return (
+
+ {`${progress}%`}
+ {detail}
+
+ );
+};
+
+//Контроль свойств - Информация по прогрессу объекта
+ProgressBox.propTypes = {
+ progress: PropTypes.number,
+ detail: PropTypes.string,
+ width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ progressVariant: PropTypes.string,
+ detailVariant: PropTypes.string
+};
+
+//----------------
+//Интерфейс модуля
+//----------------
+
+export { ProgressBox };
diff --git a/app/panels/mech_rec_assembly_mon/elements/progressBox.js b/app/panels/mech_rec_assembly_mon/elements/progressBox.js
deleted file mode 100644
index ea8b511..0000000
--- a/app/panels/mech_rec_assembly_mon/elements/progressBox.js
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- Парус 8 - Панели мониторинга - ПУП - Мониторинг сборки изделий
- Панель мониторинга: Блок информации по прогрессу объекта
-*/
-
-//---------------------
-//Подключение библиотек
-//---------------------
-
-import React from "react"; //Классы React
-import PropTypes from "prop-types"; //Контроль свойств компонента
-import { Typography, Box } from "@mui/material"; //Интерфейсные элементы
-
-//---------
-//Константы
-//---------
-
-//Стили
-const STYLES = {
- PROGRESS_INFO: {
- display: "flex",
- justifyContent: "center",
- alignItems: "center",
- flexDirection: "column",
- margin: "0px 32px",
- borderRadius: "50%"
- }
-};
-
-//------------------------------------
-//Вспомогательные функции и компоненты
-//------------------------------------
-
-//-----------
-//Тело модуля
-//-----------
-
-//Детализация по объекту
-
-//Блок информации по прогрессу объекта
-const ProgressBox = ({ prms }) => {
- //Инициализируем цвет тени
- let boxShadow = null;
- //Определяем цвет тени
- switch (true) {
- case prms.NPROGRESS >= 70:
- boxShadow = "0 0 30px #21d21e66";
- break;
- case prms.NPROGRESS >= 40:
- boxShadow = "0 0 30px #fddd3566";
- break;
- case prms.NPROGRESS >= 10:
- boxShadow = "0 0 30px #ea5c4966";
- break;
- default:
- boxShadow = "0 0 30px #d3d3d3";
- }
- //Возвращаем блок
- return (
-
- {`${prms.NPROGRESS}%`}
- {prms.SDETAIL}
-
- );
-};
-
-//Контроль свойств - Блок информации по прогрессу объекта
-ProgressBox.propTypes = {
- prms: PropTypes.object
-};
-
-//----------------
-//Интерфейс модуля
-//----------------
-
-export { ProgressBox };
diff --git a/app/panels/mech_rec_assembly_mon/mech_rec_assembly_mon.js b/app/panels/mech_rec_assembly_mon/mech_rec_assembly_mon.js
index 2d51675..18e560e 100644
--- a/app/panels/mech_rec_assembly_mon/mech_rec_assembly_mon.js
+++ b/app/panels/mech_rec_assembly_mon/mech_rec_assembly_mon.js
@@ -9,26 +9,12 @@
import React, { useState, useContext } from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента
-import {
- Drawer,
- Fab,
- Box,
- List,
- ListItemButton,
- ListItemText,
- Typography,
- Grid,
- TextField,
- FormGroup,
- FormControlLabel,
- Checkbox,
- Container
-} from "@mui/material"; //Интерфейсные элементы
+import { Drawer, Fab, Box, List, ListItemButton, ListItemText, Typography, TextField, FormGroup, FormControlLabel, Checkbox } from "@mui/material"; //Интерфейсные элементы
import { ThemeProvider } from "@mui/material/styles"; //Подключение темы
import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений
-import { CardBlock } from "./blocks/cardBlock"; //Информация об объекте
-import { CardDetail } from "./blocks/cardDetail"; //Детализация по объекту
-import { theme } from "./styles/themes.js"; //Стиль темы
+import { PlansList } from "./components/plans_list"; //Список планов
+import { PlanDetail } from "./components/plan_detail"; //Детали плана
+import { theme } from "./styles/themes"; //Стиль темы
import { useFilteredPlanCtlgs } from "./hooks"; //Вспомогательные хуки
import { useMechRecAssemblyMon } from "./backend"; //Хук корневой панели мониторинга сборки изделий
@@ -49,7 +35,8 @@ const STYLES = {
display: "inline-block",
flexShrink: 0,
[`& .MuiDrawer-paper`]: { width: "350px", display: "inline-block", boxSizing: "border-box" }
- }
+ },
+ PLANS_LIST_BOX: { paddingTop: "20px" }
};
//------------------------------------
@@ -131,11 +118,18 @@ PlanCtlgsList.propTypes = {
//Корневая панель мониторинга сборки изделий
const MechRecAssemblyMon = () => {
//Собственное состояние
- const [state, setState, selectPlan, unselectPlan] = useMechRecAssemblyMon();
+ const [state, setState, selectPlanCtlg, unselectPlanCtlg] = useMechRecAssemblyMon();
- //Состояние для фильтра каталогов
+ //Состояние фильтра каталогов
const [filter, setFilter] = useState({ ctlgName: "", haveDocs: false });
+ //Состояние навигации по карточкам детализации
+ const [planDetailNavigation, setPlanDetailNavigation] = useState({
+ disableNavigatePrev: false,
+ disableNavigateNext: false,
+ currentPlanIndex: 0
+ });
+
//Массив отфильтрованных каталогов
const filteredPlanCtgls = useFilteredPlanCtlgs(state.planCtlgs, filter);
@@ -143,24 +137,38 @@ const MechRecAssemblyMon = () => {
const { InlineMsgInfo } = useContext(MessagingСtx);
//Обработка нажатия на элемент в списке каталогов планов
- const handleProjectClick = project => {
- if (state.selectedPlanCtlg.NRN != project.NRN) selectPlan(project);
- else unselectPlan();
+ const handlePlanCtlgClick = planCtlg => {
+ if (state.selectedPlanCtlg.NRN != planCtlg.NRN) selectPlanCtlg(planCtlg);
+ else unselectPlanCtlg();
};
- //Обработка нажатия на карточку объекта
- const handleCardClick = plan => {
+ //Перемещение к нужному плану
+ const navigateToPlan = planIndex => {
+ if (planIndex < 0) planIndex = 0;
+ if (planIndex > state.plans.length - 1) planIndex = state.plans.length - 1;
setState(pv => ({
...pv,
- selectedPlan: { NRN: plan.NRN, SNUMB: plan.SNUMB, NPROGRESS: plan.NPROGRESS, SDETAIL: plan.SDETAIL, NYEAR: plan.NYEAR }
+ selectedPlan: { ...state.plans[planIndex] }
+ }));
+ setPlanDetailNavigation(pv => ({
+ ...pv,
+ disableNavigatePrev: planIndex == 0 ? true : false,
+ disableNavigateNext: planIndex == state.plans.length - 1 ? true : false,
+ currentPlanIndex: planIndex
}));
};
+ //Обработка нажатия на документ плана
+ const handlePlanClick = (plan, planIndex) => navigateToPlan(planIndex);
+
//Обработка нажатия на кнопку "Назад"
- const handleBackClick = () => {
- setState(pv => ({ ...pv, selectedPlan: { NRN: null, SNUMB: null, NPROGRESS: null, SDETAIL: null, NYEAR: null } }));
+ const handlePlanDetailBackClick = () => {
+ setState(pv => ({ ...pv, selectedPlan: {} }));
};
+ //Обработка навигации из карточки с деталями плана
+ const handlePlanDetailNavigateClick = direction => navigateToPlan(planDetailNavigation.currentPlanIndex + direction);
+
//Генерация содержимого
return (
@@ -179,34 +187,30 @@ const MechRecAssemblyMon = () => {
selectedPlanCtlg={state.selectedPlanCtlg.NRN}
filter={filter}
setFilter={setFilter}
- onClick={handleProjectClick}
+ onClick={handlePlanCtlgClick}
/>
{state.init == true ? (
state.selectedPlanCtlg.NRN ? (
<>
-
- {`${state.selectedPlanCtlg.SNAME} на ${state.selectedPlanCtlg.NMIN_YEAR}г. - ${state.selectedPlanCtlg.NMAX_YEAR}г.`}
+
+ {`${state.selectedPlanCtlg.SNAME} ${
+ state.selectedPlanCtlg.NMIN_YEAR ? `с ${state.selectedPlanCtlg.NMIN_YEAR} г` : ""
+ } ${state.selectedPlanCtlg.NMAX_YEAR ? `по ${state.selectedPlanCtlg.NMAX_YEAR}` : ""}`}
{state.plansLoaded == true ? (
state.selectedPlan.NRN ? (
-
+
) : (
-
-
- {state.plans.map(el => (
- = 5 ? 2.4 : 12 / state.plans.length}
- key={el.NRN}
- display="flex"
- justifyContent="center"
- >
-
-
- ))}
-
-
+
+
+
)
) : null}
>
diff --git a/app/panels/mech_rec_assembly_mon/styles/themes.js b/app/panels/mech_rec_assembly_mon/styles/themes.js
index 37818ee..277aeae 100644
--- a/app/panels/mech_rec_assembly_mon/styles/themes.js
+++ b/app/panels/mech_rec_assembly_mon/styles/themes.js
@@ -4,6 +4,7 @@ import { createTheme } from "@mui/material/styles"; //Интерфейсные
const theme = createTheme({
palette: {
text: {
+ title: { fontColor: "rgba(0, 0, 0, 0.65)" },
secondary: { fontColor: "rgba(0, 0, 0, 0.298)" }
}
},