ЦИТК-784 - Добавление панели "Производственная программа" #1
@ -1,318 +0,0 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга - ПУП - Производственная программа
|
||||
Панель мониторинга: Корневая панель производственной программы
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import React, { useContext, useState, useCallback, useEffect } from "react"; //Классы React
|
||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||
import { Drawer, Fab, Box, List, ListItemButton, ListItemText, Typography, Grid, TextField, Select, MenuItem, InputLabel } from "@mui/material"; //Интерфейсные элементы
|
||||
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
||||
import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений
|
||||
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
||||
import { P8P_GANTT_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
||||
import { P8PGantt } from "../../components/p8p_gantt"; //Диаграмма Ганта
|
||||
import { formatDateJSONDateOnly } from "../../core/utils"; //Вспомогательные функции
|
||||
import { useFilteredPlans } from "./hooks"; //Вспомогательные хуки
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Высота диаграммы Ганта
|
||||
const GANTT_HEIGHT = "650px";
|
||||
|
||||
//Ширина диаграммы Ганта
|
||||
const GANTT_WIDTH = "98vw";
|
||||
|
||||
//Стили
|
||||
const STYLES = {
|
||||
PROJECTS_FINDER: { marginTop: "10px", marginLeft: "10px", width: "93%" },
|
||||
PROJECTS_LIST_ITEM_PRIMARY: { wordWrap: "break-word" },
|
||||
PROJECTS_LIST_ITEM_SECONDARY: { wordWrap: "break-word", fontSize: "0.5rem", textTransform: "uppercase" },
|
||||
PROJECTS_LIST_ITEM_SECONDARY_NOJOBS: { color: "red" },
|
||||
PROJECTS_LIST_ITEM_SECONDARY_NOEDIT: { color: "gray" },
|
||||
PROJECTS_LIST_ITEM_SECONDARY_CHANGED: { color: "green" },
|
||||
PROJECTS_BUTTON: { position: "absolute" },
|
||||
PROJECTS_DRAWER: {
|
||||
minWidth: "250px",
|
||||
display: "inline-block",
|
||||
flexShrink: 0,
|
||||
[`& .MuiDrawer-paper`]: { minWidth: "250px", display: "inline-block", boxSizing: "border-box" }
|
||||
},
|
||||
GANTT_CONTAINER: { height: GANTT_HEIGHT, width: GANTT_WIDTH },
|
||||
GANTT_TITLE: { paddingLeft: "100px", paddingRight: "120px" },
|
||||
PERIODS_BUTTON: { position: "absolute", right: "20px" },
|
||||
PERIODS_DRAWER: { width: "1000px", flexShrink: 0, [`& .MuiDrawer-paper`]: { width: "1000px", boxSizing: "border-box" } }
|
||||
};
|
||||
|
||||
//Список проектов
|
||||
const ProjectsList = ({ plans = [], selectedPlan, filter, setFilter, onClick } = {}) => {
|
||||
//Генерация содержимого
|
||||
return (
|
||||
<div>
|
||||
<TextField
|
||||
sx={STYLES.PROJECTS_FINDER}
|
||||
name="planFilter"
|
||||
label="План"
|
||||
value={filter}
|
||||
variant="standard"
|
||||
fullWidth
|
||||
onChange={event => {
|
||||
setFilter(event.target.value);
|
||||
}}
|
||||
></TextField>
|
||||
<List>
|
||||
{plans.map(p => (
|
||||
<ListItemButton key={p.NRN} selected={p.NRN === selectedPlan} onClick={() => (onClick ? onClick(p) : null)}>
|
||||
<ListItemText
|
||||
primary={<Typography sx={STYLES.PROJECTS_LIST_ITEM_PRIMARY}>{p.SDOC_INFO}</Typography>}
|
||||
secondary={
|
||||
<Typography
|
||||
sx={{
|
||||
...STYLES.PROJECTS_LIST_ITEM_SECONDARY,
|
||||
...(p.NJOBS == 0
|
||||
? STYLES.PROJECTS_LIST_ITEM_SECONDARY_NOJOBS
|
||||
: p.NCHANGED == 1
|
||||
? STYLES.PROJECTS_LIST_ITEM_SECONDARY_CHANGED
|
||||
: STYLES.PROJECTS_LIST_ITEM_SECONDARY_NOEDIT)
|
||||
}}
|
||||
></Typography>
|
||||
}
|
||||
/>
|
||||
</ListItemButton>
|
||||
))}
|
||||
</List>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Список проектов
|
||||
ProjectsList.propTypes = {
|
||||
plans: PropTypes.array,
|
||||
selectedPlan: PropTypes.number,
|
||||
onClick: PropTypes.func,
|
||||
filter: PropTypes.string,
|
||||
setFilter: PropTypes.func
|
||||
};
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Корневая панель производственной программы
|
||||
const MechRecCostProdPlans = () => {
|
||||
//Собственное состояние
|
||||
let [state, setState] = useState({
|
||||
init: false,
|
||||
showPlanList: false,
|
||||
plans: [],
|
||||
plansLoaded: false,
|
||||
selectedPlanSpecsLoaded: false,
|
||||
selectedPlan: null,
|
||||
selectedPlanMaxLevel: null,
|
||||
selectedPlanCurLevel: null,
|
||||
selectedPlanGanttDef: {},
|
||||
selectedPlanSpecs: []
|
||||
});
|
||||
|
||||
const [filter, setFilter] = useState("");
|
||||
|
||||
const filteredPlans = useFilteredPlans(state.plans, filter);
|
||||
|
||||
//Подключение к контексту приложения
|
||||
const { pOnlineShowDocument } = useContext(ApplicationСtx);
|
||||
|
||||
//Подключение к контексту сообщений
|
||||
const { InlineMsgInfo } = useContext(MessagingСtx);
|
||||
|
||||
//Подключение к контексту взаимодействия с сервером
|
||||
const { executeStored } = useContext(BackEndСtx);
|
||||
|
||||
// Инициализация планов
|
||||
const initPlans = useCallback(async () => {
|
||||
if (!state.init) {
|
||||
const data = await executeStored({
|
||||
stored: "PKG_P8PANELS_MECHREC.PRODPLAN_INIT",
|
||||
args: {},
|
||||
respArg: "COUT"
|
||||
});
|
||||
setState(pv => ({
|
||||
...pv,
|
||||
init: true,
|
||||
plans: [...(data?.XFCPRODPLANS || [])],
|
||||
plansLoaded: true
|
||||
}));
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [state.init, executeStored]);
|
||||
|
||||
//Выбор плана
|
||||
const selectPlan = project => {
|
||||
setState(pv => ({
|
||||
...pv,
|
||||
selectedPlan: project,
|
||||
selectedPlanSpecsLoaded: false,
|
||||
selectedPlanMaxLevel: null,
|
||||
selectedPlanCurLevel: null,
|
||||
selectedPlanSpecs: [],
|
||||
selectedPlanGanttDef: {},
|
||||
showPlanList: false
|
||||
}));
|
||||
};
|
||||
|
||||
//Сброс выбора плана
|
||||
const unselectPlan = () =>
|
||||
setState(pv => ({
|
||||
...pv,
|
||||
selectedPlanSpecsLoaded: false,
|
||||
selectedPlan: null,
|
||||
selectedPlanMaxLevel: null,
|
||||
selectedPlanCurLevel: null,
|
||||
selectedPlanSpecs: [],
|
||||
selectedPlanGanttDef: {},
|
||||
showPlanList: false
|
||||
}));
|
||||
|
||||
//Загрузка списка спецификаций плана
|
||||
const loadPlanSpecs = useCallback(
|
||||
async (curMaxLevel = null, level = null) => {
|
||||
const data = await executeStored({
|
||||
stored: "PKG_P8PANELS_MECHREC.FCPRODPLANSP_GET",
|
||||
args: { NFCPRODPLAN: state.selectedPlan, NLEVEL: level },
|
||||
attributeValueProcessor: (name, val) =>
|
||||
name == "numb" ? undefined : ["start", "end"].includes(name) ? formatDateJSONDateOnly(val) : val,
|
||||
respArg: "COUT"
|
||||
});
|
||||
let maxLevel = curMaxLevel ? curMaxLevel : 0;
|
||||
//Если есть данные
|
||||
if (data.XGANTT_TASKS) {
|
||||
//Обходим данные
|
||||
data.XGANTT_TASKS.forEach(el => {
|
||||
// Если есть зависимости
|
||||
if (el.dependencies) {
|
||||
//Разбиваем их в array
|
||||
el.dependencies = el.dependencies[0].split(",");
|
||||
}
|
||||
//Если уровень больше рассчитанного максимального
|
||||
maxLevel = maxLevel < el.level ? el.level : maxLevel;
|
||||
});
|
||||
}
|
||||
setState(pv => ({
|
||||
...pv,
|
||||
selectedPlanMaxLevel: maxLevel,
|
||||
selectedPlanCurLevel: level || level === 0 ? level : maxLevel,
|
||||
selectedPlanSpecsLoaded: true,
|
||||
selectedPlanGanttDef: data.XGANTT_DEF ? { ...data.XGANTT_DEF } : {},
|
||||
selectedPlanSpecs: [...(data?.XGANTT_TASKS || [])]
|
||||
}));
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[executeStored, state.ident, state.selectedPlan]
|
||||
);
|
||||
|
||||
//Обработка нажатия на элемент в списке планов
|
||||
const handleProjectClick = project => {
|
||||
if (state.selectedPlan != project.NRN) selectPlan(project.NRN);
|
||||
else unselectPlan();
|
||||
};
|
||||
|
||||
//Отработка нажатия на заголовок плана
|
||||
const handleTitleClick = () => {
|
||||
state.selectedPlan ? pOnlineShowDocument({ unitCode: "CostProductPlans", document: state.selectedPlan }) : null;
|
||||
};
|
||||
|
||||
//При подключении компонента к странице
|
||||
useEffect(() => {
|
||||
initPlans();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
//При смене выбранного плана
|
||||
useEffect(() => {
|
||||
if (state.selectedPlan) loadPlanSpecs();
|
||||
}, [state.selectedPlan, loadPlanSpecs]);
|
||||
|
||||
//Выбор уровня
|
||||
const handleChangeSelectList = selectedLevel => {
|
||||
loadPlanSpecs(state.selectedPlanMaxLevel, selectedLevel);
|
||||
setState(pv => ({ ...pv, selectedPlanCurLevel: selectedLevel }));
|
||||
};
|
||||
|
||||
//Генерация содержимого
|
||||
return (
|
||||
<Box p={2}>
|
||||
<Fab variant="extended" sx={STYLES.PROJECTS_BUTTON} onClick={() => setState(pv => ({ ...pv, showPlanList: !pv.showPlanList }))}>
|
||||
Планы
|
||||
</Fab>
|
||||
<Drawer
|
||||
anchor={"left"}
|
||||
open={state.showPlanList}
|
||||
onClose={() => setState(pv => ({ ...pv, showPlanList: false }))}
|
||||
sx={STYLES.PROJECTS_DRAWER}
|
||||
>
|
||||
<ProjectsList
|
||||
plans={filteredPlans}
|
||||
selectedPlan={state.selectedPlan}
|
||||
filter={filter}
|
||||
setFilter={setFilter}
|
||||
onClick={handleProjectClick}
|
||||
/>
|
||||
</Drawer>
|
||||
{state.init == true ? (
|
||||
<Grid container spacing={1}>
|
||||
<Grid item xs={12}>
|
||||
{state.selectedPlanSpecsLoaded ? (
|
||||
state.selectedPlanSpecs.length === 0 ? (
|
||||
<InlineMsgInfo okBtn={false} text={"В плане отсутствуют записи спецификации"} />
|
||||
) : (
|
||||
<Box sx={STYLES.GANTT_CONTAINER} p={1}>
|
||||
{state.selectedPlanMaxLevel ? (
|
||||
<Box sx={{ float: "right" }}>
|
||||
<InputLabel id="demo-simple-select-label">Уровень</InputLabel>
|
||||
<Select
|
||||
labelId="demo-simple-select-label"
|
||||
id="demo-simple-select"
|
||||
value={state.selectedPlanCurLevel}
|
||||
label="Уровень"
|
||||
onChange={event => {
|
||||
handleChangeSelectList(event.target.value);
|
||||
}}
|
||||
defaultValue={state.selectedPlanCurLevel}
|
||||
>
|
||||
{[...Array(state.selectedPlanMaxLevel + 1).keys()].map(el => (
|
||||
<MenuItem value={el} key={el}>
|
||||
{el}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</Box>
|
||||
) : null}
|
||||
<P8PGantt
|
||||
{...P8P_GANTT_CONFIG_PROPS}
|
||||
{...state.selectedPlanGanttDef}
|
||||
height={GANTT_HEIGHT}
|
||||
onTitleClick={handleTitleClick}
|
||||
titleStyle={STYLES.GANTT_TITLE}
|
||||
tasks={state.selectedPlanSpecs}
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
) : !state.selectedPlan ? (
|
||||
<InlineMsgInfo okBtn={false} text={"Укажите план для отображения его спецификации"} />
|
||||
) : null}
|
||||
</Grid>
|
||||
</Grid>
|
||||
) : null}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { MechRecCostProdPlans };
|
Loading…
x
Reference in New Issue
Block a user