forked from CITKParus/P8-Panels
WEB APP: ЦИТК-841 панель "Мониторинг сборки изделий" - добавлена SVG-модель и проведён первый этап рефакторинга
This commit is contained in:
parent
46d078219f
commit
dea0f3643d
@ -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
|
||||
});
|
||||
|
||||
//Подключение к контексту взаимодействия с сервером
|
||||
const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
|
||||
|
||||
//Загрузка данных таблицы с сервера
|
||||
const loadData = useCallback(
|
||||
async () => {
|
||||
if (costRouteLists.reload) {
|
||||
const data = await executeStored({
|
||||
stored: "PKG_P8PANELS_MECHREC.FCROUTLST_MON_DG_GET",
|
||||
args: {
|
||||
NPRODCMPSP: product,
|
||||
NFCPRODPLAN: plan,
|
||||
CORDERS: { VALUE: object2Base64XML(costRouteLists.orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
|
||||
NPAGE_NUMBER: costRouteLists.pageNumber,
|
||||
NPAGE_SIZE: DATA_GRID_PAGE_SIZE,
|
||||
NINCLUDE_DEF: costRouteLists.dataLoaded ? 0 : 1
|
||||
},
|
||||
respArg: "COUT"
|
||||
});
|
||||
setCostRouteLists(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
|
||||
[costRouteLists.reload, costRouteLists.orders, costRouteLists.dataLoaded, costRouteLists.pageNumber, 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];
|
||||
};
|
||||
|
||||
//Хук для таблицы комплектовочных ведомостей
|
||||
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]);
|
||||
//Подключение к контексту взаимодействия с сервером
|
||||
const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
|
||||
|
||||
//При изменении плана
|
||||
//Загрузка данных при изменении зависимостей
|
||||
useEffect(() => {
|
||||
setCostDeliverySheets(pv => ({
|
||||
const loadData = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const data = await executeStored({
|
||||
stored,
|
||||
args: {
|
||||
NPRODCMPSP: product,
|
||||
NFCPRODPLAN: plan,
|
||||
CORDERS: { VALUE: object2Base64XML(orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
|
||||
NPAGE_NUMBER: pageNumber,
|
||||
NPAGE_SIZE: DATA_GRID_PAGE_SIZE,
|
||||
NINCLUDE_DEF: 1
|
||||
},
|
||||
respArg: "COUT"
|
||||
});
|
||||
setData(pv => ({
|
||||
...pv,
|
||||
dataLoaded: false,
|
||||
columnsDef: [],
|
||||
orders: null,
|
||||
rows: [],
|
||||
reload: true,
|
||||
pageNumber: 1,
|
||||
morePages: true,
|
||||
selectedProduct: null
|
||||
columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
|
||||
rows: pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
|
||||
morePages: DATA_GRID_PAGE_SIZE == 0 ? false : (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE
|
||||
}));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [plan]);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
if (plan && product) loadData();
|
||||
}, [plan, product, orders, pageNumber, stored, executeStored, SERV_DATA_TYPE_CLOB]);
|
||||
|
||||
return [costDeliverySheets, setCostDeliverySheets];
|
||||
//Вернём данные
|
||||
return { data, isLoading };
|
||||
};
|
||||
|
||||
export { useMechRecAssemblyMon, useCostProductComposition, useCostRouteLists, useCostDeliverySheets };
|
||||
export { useMechRecAssemblyMon, useCostProductComposition, useProductDetailsTable };
|
||||
|
@ -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 (
|
||||
<Box width={180} height={180}>
|
||||
<ImageList variant="masonry" cols={1} gap={8}>
|
||||
<ImageListItem key={1}>
|
||||
<img src={`data:image/png;base64,${card["#text"]}`} alt={"Image not loaded."} loading="lazy" width={180} />
|
||||
{/* <img src={`./${airplaneImg}`} alt={"Image not loaded."} loading="lazy" width={180} /> */}
|
||||
</ImageListItem>
|
||||
</ImageList>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Заголовок первого уровня
|
||||
CardImage.propTypes = {
|
||||
card: PropTypes.object
|
||||
};
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Информация об объекте
|
||||
const CardBlock = ({ card, handleCardClick }) => {
|
||||
return (
|
||||
<Box sx={STYLES.PLAN_INFO} onClick={() => handleCardClick(card)}>
|
||||
<CardImage card={card} />
|
||||
<Box textAlign="center">
|
||||
<Typography variant="UDO_body1" color="text.secondary.fontColor">
|
||||
Номер борта
|
||||
</Typography>
|
||||
<Typography variant="h2">{card.SNUMB}</Typography>
|
||||
</Box>
|
||||
<ProgressBox
|
||||
prms={{
|
||||
NPROGRESS: card.NPROGRESS,
|
||||
SDETAIL: card.SDETAIL,
|
||||
width: "155px",
|
||||
height: "155px",
|
||||
progressVariant: "h3",
|
||||
detailVariant: "UDO_body2"
|
||||
}}
|
||||
/>
|
||||
<Box>
|
||||
<Typography variant="UDO_body1" color="text.secondary.fontColor">
|
||||
Год выпуска:
|
||||
</Typography>
|
||||
<Typography variant="subtitle1" mt={-1}>
|
||||
{card.NYEAR}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Заголовок первого уровня
|
||||
CardBlock.propTypes = {
|
||||
card: PropTypes.object,
|
||||
handleCardClick: PropTypes.func
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { CardBlock };
|
@ -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 (
|
||||
<>
|
||||
<Box sx={STYLES.PLAN_INFO_MAIN}>
|
||||
<Box sx={STYLES.PLAN_INFO_SUB}>
|
||||
<Typography variant="UDO_body1" mt={1}>
|
||||
Номер борта:
|
||||
</Typography>
|
||||
<Typography variant="subtitle2">{cardInfo.SNUMB}</Typography>
|
||||
</Box>
|
||||
<Box sx={STYLES.PLAN_INFO_SUB}>
|
||||
<Typography variant="UDO_body1" mt={1}>
|
||||
Год выпуска:
|
||||
</Typography>
|
||||
<Typography variant="subtitle2">{cardInfo.NYEAR}</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
<ProgressBox
|
||||
prms={{
|
||||
NPROGRESS: cardInfo.NPROGRESS,
|
||||
SDETAIL: cardInfo.SDETAIL,
|
||||
width: "110px",
|
||||
height: "110px",
|
||||
progressVariant: "subtitle2",
|
||||
detailVariant: "body3"
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Информация об объекте
|
||||
CardDetailInfo.propTypes = {
|
||||
cardInfo: PropTypes.object
|
||||
};
|
||||
|
||||
//Детали объекта
|
||||
const CardSelector = ({ products, setCostProductComposition }) => {
|
||||
//При выборе детали в SVG
|
||||
const handleProductClick = product => {
|
||||
setCostProductComposition(pv => ({ ...pv, selectedProduct: product }));
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box sx={STYLES.PLAN_INFO_MAIN}>
|
||||
{products.map(el => (
|
||||
<Button key={el.NRN} onClick={() => handleProductClick(el.NRN)}>{`${el.SNAME}`}</Button>
|
||||
))}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Детали объекта
|
||||
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 (
|
||||
<Container>
|
||||
<Button onClick={() => handleBackClick()}>Назад</Button>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={5}>
|
||||
<Box sx={STYLES.TABLE_INFO_MAIN}>
|
||||
<Box sx={STYLES.TABLE_INFO_SUB}>
|
||||
{!costRouteLists.dataLoaded ? (
|
||||
<Typography variant="UDO_body2">Выберите агрегат самолёта, чтобы увидеть информацию</Typography>
|
||||
) : costRouteLists.rows.length === 0 ? (
|
||||
<Typography variant="subtitle2">Нет данных по МК</Typography>
|
||||
) : (
|
||||
<>
|
||||
<Typography variant="h4">Маршрутная карта</Typography>
|
||||
<P8PDataGrid
|
||||
{...P8P_DATA_GRID_CONFIG_PROPS}
|
||||
columnsDef={costRouteLists.columnsDef}
|
||||
rows={costRouteLists.rows}
|
||||
size={P8P_DATA_GRID_SIZE.SMALL}
|
||||
morePages={costRouteLists.morePages}
|
||||
reloading={costRouteLists.reload}
|
||||
dataCellRender={dataCellRender}
|
||||
headCellRender={headCellRender}
|
||||
onOrderChanged={costRouteListsOrderChanged}
|
||||
onPagesCountChanged={costRouteListsPagesCountChanged}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
<Box sx={STYLES.TABLE_INFO_MAIN} mt={2}>
|
||||
<Box sx={STYLES.TABLE_INFO_SUB}>
|
||||
{!сostDeliverySheets.dataLoaded ? (
|
||||
<Typography variant="UDO_body2">Выберите агрегат самолёта, чтобы увидеть информацию</Typography>
|
||||
) : сostDeliverySheets.rows.length === 0 ? (
|
||||
<Typography variant="subtitle2">Нет данных по КВ</Typography>
|
||||
) : (
|
||||
<>
|
||||
<Typography variant="h4">Дефицит по КВ</Typography>
|
||||
<P8PDataGrid
|
||||
{...P8P_DATA_GRID_CONFIG_PROPS}
|
||||
columnsDef={сostDeliverySheets.columnsDef}
|
||||
rows={сostDeliverySheets.rows}
|
||||
size={P8P_DATA_GRID_SIZE.SMALL}
|
||||
morePages={сostDeliverySheets.morePages}
|
||||
reloading={сostDeliverySheets.reload}
|
||||
dataCellRender={dataCellRender}
|
||||
headCellRender={headCellRender}
|
||||
onOrderChanged={СostDeliverySheetsOrderChanged}
|
||||
onPagesCountChanged={СostDeliverySheetsPagesCountChanged}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item xs={7}>
|
||||
<Box sx={STYLES.DETAIL_INFO}>
|
||||
<CardDetailInfo cardInfo={card} />
|
||||
</Box>
|
||||
<Box sx={STYLES.PRODUCT_SELECTOR}>
|
||||
<CardSelector products={costProductComposition.products} setCostProductComposition={setCostProductComposition} />
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Детализация по объекту
|
||||
CardDetail.propTypes = {
|
||||
card: PropTypes.object,
|
||||
handleBackClick: PropTypes.func
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { CardDetail };
|
324
app/panels/mech_rec_assembly_mon/components/plan_detail.js
Normal file
324
app/panels/mech_rec_assembly_mon/components/plan_detail.js
Normal file
@ -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 (
|
||||
<>
|
||||
<Box sx={STYLES.PLAN_INFO_MAIN}>
|
||||
<Box sx={STYLES.PLAN_INFO_SUB}>
|
||||
<Typography variant="UDO_body1" mt={1}>
|
||||
Номер борта:
|
||||
</Typography>
|
||||
<Typography variant="subtitle2">{plan.SNUMB}</Typography>
|
||||
</Box>
|
||||
<Box sx={STYLES.PLAN_INFO_SUB}>
|
||||
<Typography variant="UDO_body1" mt={1}>
|
||||
Год выпуска:
|
||||
</Typography>
|
||||
<Typography variant="subtitle2">{plan.NYEAR}</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
<ProgressBox
|
||||
progress={plan.NPROGRESS}
|
||||
detail={plan.SDETAIL}
|
||||
width={"110px"}
|
||||
height={"110px"}
|
||||
progressVariant={"subtitle2"}
|
||||
detailVariant={"body3"}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Информация о плане
|
||||
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 (
|
||||
<>
|
||||
<Box sx={STYLES.PRODUCT_SELECTOR_MODEL}>
|
||||
{model ? (
|
||||
<P8PSVG
|
||||
data={atob(model)}
|
||||
items={products.map(p => ({ id: p.SMODEL_ID, backgroundColor: p.SMODEL_BG_COLOR || "red", desc: p.SNAME, title: p.SNAME }))}
|
||||
fillOpacity={"0.3"}
|
||||
onItemClick={handleProductClick}
|
||||
/>
|
||||
) : (
|
||||
<Typography variant="subtitle2">Модель изделия не загружена</Typography>
|
||||
)}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Модель выпуска плана
|
||||
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 (
|
||||
<Box sx={STYLES.BOX_INFO_SUB(!product || data.rows.length === 0)}>
|
||||
{!product ? (
|
||||
<Typography variant="UDO_body2">{noProductMessage}</Typography>
|
||||
) : isLoading ? null : data.rows.length === 0 ? (
|
||||
<Typography variant="subtitle2">{noDataFoundMessage}</Typography>
|
||||
) : (
|
||||
<>
|
||||
<Typography variant="h4">
|
||||
<b>{title}</b>
|
||||
</Typography>
|
||||
<P8PDataGrid
|
||||
{...P8P_DATA_GRID_CONFIG_PROPS}
|
||||
containerComponentProps={{ sx: STYLES.TABLE_DETAILS, elevation: 0 }}
|
||||
columnsDef={data.columnsDef}
|
||||
rows={data.rows}
|
||||
size={P8P_DATA_GRID_SIZE.SMALL}
|
||||
morePages={data.morePages}
|
||||
fixedHeader={true}
|
||||
reloading={false}
|
||||
dataCellRender={dataCellRender}
|
||||
headCellRender={headCellRender}
|
||||
onOrderChanged={handleOrderChanged}
|
||||
onPagesCountChanged={handlePagesCountChanged}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Таблица детализации изделия
|
||||
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 (
|
||||
<Container maxWidth={false} sx={STYLES.CARD_DETAILS_CONTAINER}>
|
||||
<Grid container direction="row" justifyContent="center" alignItems="center" spacing={0}>
|
||||
<Grid item display="flex" justifyContent="center" xs={1}>
|
||||
<Stack display="flex" direction="row" justifyContent="flex-end" alignItems="center" sx={STYLES.CARD_DETAILS_NAVIGATION_STACK}>
|
||||
<IconButton disabled={disableNavigatePrev} onClick={() => (onNavigate ? onNavigate(-1) : null)}>
|
||||
<Icon>navigate_before</Icon>
|
||||
</IconButton>
|
||||
</Stack>
|
||||
</Grid>
|
||||
<Grid item xs={10}>
|
||||
<Container maxWidth={false}>
|
||||
<Button onClick={() => (onBack ? onBack() : null)}>
|
||||
<Stack direction="row">
|
||||
<Icon>chevron_left</Icon>Назад
|
||||
</Stack>
|
||||
</Button>
|
||||
|
||||
<Grid container spacing={2} sx={{ paddingTop: "5px" }}>
|
||||
<Grid item xs={5}>
|
||||
<Box sx={STYLES.BOX_INFO_MAIN}>
|
||||
<ProductDetailsTable
|
||||
plan={plan.NRN}
|
||||
product={costProductComposition.selectedProduct?.NRN}
|
||||
stored={"PKG_P8PANELS_MECHREC.FCROUTLST_DG_BY_PRDCMPSP_GET"}
|
||||
noProductMessage={"Укажите элемент модели, чтобы увидеть информацию о маршрутных картах"}
|
||||
noDataFoundMessage={"Маршрутные карты не найдены"}
|
||||
title={"Маршрутные карты"}
|
||||
/>
|
||||
</Box>
|
||||
<Box sx={STYLES.BOX_INFO_MAIN} mt={2}>
|
||||
<ProductDetailsTable
|
||||
plan={plan.NRN}
|
||||
product={costProductComposition.selectedProduct?.NRN}
|
||||
stored={"PKG_P8PANELS_MECHREC.FCDELIVSH_DG_BY_PRDCMPSP_GET"}
|
||||
noProductMessage={"Укажите элемент модели, чтобы увидеть информацию о комплектовочных ведомостях"}
|
||||
noDataFoundMessage={"Комплектовочные ведомости не найдены"}
|
||||
title={"Дефицит комплектации"}
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item xs={7}>
|
||||
<Box sx={STYLES.DETAIL_INFO}>
|
||||
<PlanInfo plan={plan} />
|
||||
</Box>
|
||||
<Box sx={STYLES.PRODUCT_SELECTOR_CONTAINER}>
|
||||
<PlanProductCompositionModel
|
||||
model={costProductComposition.model}
|
||||
products={costProductComposition.products}
|
||||
setCostProductComposition={setCostProductComposition}
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Container>
|
||||
</Grid>
|
||||
<Grid item display="flex" justifyContent="center" xs={1}>
|
||||
<Stack display="flex" direction="row" justifyContent="flex-start" alignItems="center" sx={STYLES.CARD_DETAILS_NAVIGATION_STACK}>
|
||||
<IconButton disabled={disableNavigateNext} onClick={() => (onNavigate ? onNavigate(1) : null)}>
|
||||
<Icon>navigate_next</Icon>
|
||||
</IconButton>
|
||||
</Stack>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Детализация по объекту
|
||||
PlanDetail.propTypes = {
|
||||
plan: PropTypes.object,
|
||||
disableNavigatePrev: PropTypes.bool,
|
||||
disableNavigateNext: PropTypes.bool,
|
||||
onNavigate: PropTypes.func,
|
||||
onBack: PropTypes.func
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { PlanDetail };
|
83
app/panels/mech_rec_assembly_mon/components/plans_list.js
Normal file
83
app/panels/mech_rec_assembly_mon/components/plans_list.js
Normal file
@ -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 (
|
||||
<Container>
|
||||
<Grid container direction="row" justifyContent="center" alignItems="center" spacing={2} sx={STYLES.PLAN_DOCUMENTS_LIST}>
|
||||
<Grid item display="flex" justifyContent="center" xs={1}>
|
||||
<IconButton onClick={handleScrollLeft} disabled={scroll <= 0}>
|
||||
<Icon>navigate_before</Icon>
|
||||
</IconButton>
|
||||
</Grid>
|
||||
{plans.map((el, i) =>
|
||||
i >= scroll && i < scroll + pageSize ? (
|
||||
<Grid item key={`${el.NRN}_${i}`} xs={2}>
|
||||
<PlansListItem
|
||||
card={el}
|
||||
cardIndex={i}
|
||||
onClick={(card, cardIndex) => (onItemClick ? onItemClick(card, cardIndex) : null)}
|
||||
/>
|
||||
</Grid>
|
||||
) : null
|
||||
)}
|
||||
<Grid item display="flex" justifyContent="center" xs={1}>
|
||||
<IconButton onClick={handleScrollRight} disabled={scroll + pageSize >= plans.length}>
|
||||
<Icon>navigate_next</Icon>
|
||||
</IconButton>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Список планов
|
||||
PlansList.propTypes = {
|
||||
plans: PropTypes.arrayOf(PropTypes.object),
|
||||
pageSize: PropTypes.number,
|
||||
onItemClick: PropTypes.func
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { PlansList };
|
108
app/panels/mech_rec_assembly_mon/components/plans_list_item.js
Normal file
108
app/panels/mech_rec_assembly_mon/components/plans_list_item.js
Normal file
@ -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 (
|
||||
<Box sx={STYLES.IMAGE_BOX}>
|
||||
<ImageList variant="masonry" cols={1} gap={8}>
|
||||
<ImageListItem key={1} sx={STYLES.IMAGE_LIST_ITEM}>
|
||||
{card["BIMAGE"] ? (
|
||||
<img src={`data:image/png;base64,${card["BIMAGE"]}`} loading="lazy" style={STYLES.IMAGE_IMG} />
|
||||
) : (
|
||||
<Icon sx={{ fontSize: "5rem" }}>construction</Icon>
|
||||
)}
|
||||
</ImageListItem>
|
||||
</ImageList>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Изображение для элемента
|
||||
PlansListItemImage.propTypes = {
|
||||
card: PropTypes.object
|
||||
};
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Элемент списка планов
|
||||
const PlansListItem = ({ card, cardIndex, onClick }) => {
|
||||
return (
|
||||
<Box sx={STYLES.CONTAINER} onClick={() => (onClick ? onClick(card, cardIndex) : null)}>
|
||||
<PlansListItemImage card={card} />
|
||||
<Box textAlign="center">
|
||||
<Typography variant="UDO_body1" color="text.secondary.fontColor">
|
||||
Номер борта
|
||||
</Typography>
|
||||
<Typography variant="h2">{card.SNUMB}</Typography>
|
||||
</Box>
|
||||
<ProgressBox
|
||||
progress={card.NPROGRESS}
|
||||
detail={card.SDETAIL}
|
||||
width={"155px"}
|
||||
height={"155px"}
|
||||
progressVariant={"h3"}
|
||||
detailVariant={"UDO_body2"}
|
||||
/>
|
||||
<Box>
|
||||
<Typography variant="UDO_body1" color="text.secondary.fontColor">
|
||||
Год выпуска:
|
||||
</Typography>
|
||||
<Typography variant="subtitle1" mt={-1}>
|
||||
{card.NYEAR}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Элемент списка планов
|
||||
PlansListItem.propTypes = {
|
||||
card: PropTypes.object,
|
||||
cardIndex: PropTypes.number,
|
||||
onClick: PropTypes.func
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { PlansListItem };
|
75
app/panels/mech_rec_assembly_mon/components/progress_box.js
Normal file
75
app/panels/mech_rec_assembly_mon/components/progress_box.js
Normal file
@ -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 (
|
||||
<Box sx={STYLES.PROGRESS_BOX(width, height)} boxShadow={boxShadow}>
|
||||
<Typography variant={progressVariant}>{`${progress}%`}</Typography>
|
||||
<Typography variant={detailVariant}>{detail}</Typography>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Информация по прогрессу объекта
|
||||
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 };
|
@ -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 (
|
||||
<Box sx={{ ...STYLES.PROGRESS_INFO, width: prms.width, height: prms.height }} boxShadow={boxShadow}>
|
||||
<Typography variant={prms.progressVariant}>{`${prms.NPROGRESS}%`}</Typography>
|
||||
<Typography variant={prms.detailVariant}>{prms.SDETAIL}</Typography>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Блок информации по прогрессу объекта
|
||||
ProgressBox.propTypes = {
|
||||
prms: PropTypes.object
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { ProgressBox };
|
@ -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 (
|
||||
<Box p={2}>
|
||||
@ -179,34 +187,30 @@ const MechRecAssemblyMon = () => {
|
||||
selectedPlanCtlg={state.selectedPlanCtlg.NRN}
|
||||
filter={filter}
|
||||
setFilter={setFilter}
|
||||
onClick={handleProjectClick}
|
||||
onClick={handlePlanCtlgClick}
|
||||
/>
|
||||
</Drawer>
|
||||
{state.init == true ? (
|
||||
state.selectedPlanCtlg.NRN ? (
|
||||
<>
|
||||
<Typography variant="h1" align="center" py={3}>
|
||||
{`${state.selectedPlanCtlg.SNAME} на ${state.selectedPlanCtlg.NMIN_YEAR}г. - ${state.selectedPlanCtlg.NMAX_YEAR}г.`}
|
||||
<Typography variant="h3" align="center" color="text.title.fontColor" py={2}>
|
||||
{`${state.selectedPlanCtlg.SNAME} ${
|
||||
state.selectedPlanCtlg.NMIN_YEAR ? `с ${state.selectedPlanCtlg.NMIN_YEAR} г` : ""
|
||||
} ${state.selectedPlanCtlg.NMAX_YEAR ? `по ${state.selectedPlanCtlg.NMAX_YEAR}` : ""}`}
|
||||
</Typography>
|
||||
{state.plansLoaded == true ? (
|
||||
state.selectedPlan.NRN ? (
|
||||
<CardDetail card={state.selectedPlan} handleBackClick={handleBackClick} />
|
||||
<PlanDetail
|
||||
plan={state.selectedPlan}
|
||||
disableNavigatePrev={planDetailNavigation.disableNavigatePrev}
|
||||
disableNavigateNext={planDetailNavigation.disableNavigateNext}
|
||||
onNavigate={handlePlanDetailNavigateClick}
|
||||
onBack={handlePlanDetailBackClick}
|
||||
/>
|
||||
) : (
|
||||
<Container>
|
||||
<Grid container spacing={5}>
|
||||
{state.plans.map(el => (
|
||||
<Grid
|
||||
item
|
||||
xs={state.plans.length >= 5 ? 2.4 : 12 / state.plans.length}
|
||||
key={el.NRN}
|
||||
display="flex"
|
||||
justifyContent="center"
|
||||
>
|
||||
<CardBlock card={el} handleCardClick={handleCardClick} />
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Container>
|
||||
<Box sx={STYLES.PLANS_LIST_BOX}>
|
||||
<PlansList plans={state.plans} onItemClick={handlePlanClick} />
|
||||
</Box>
|
||||
)
|
||||
) : null}
|
||||
</>
|
||||
|
@ -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)" }
|
||||
}
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user