forked from CITKParus/P8-Panels
200 lines
7.9 KiB
JavaScript
200 lines
7.9 KiB
JavaScript
/*
|
||
Парус 8 - Панели мониторинга - Примеры для разработчиков
|
||
Пример: Диаграмма Ганта "P8PGantt"
|
||
*/
|
||
|
||
//---------------------
|
||
//Подключение библиотек
|
||
//---------------------
|
||
|
||
import React, { useState, useContext, useCallback, useEffect } from "react"; //Классы React
|
||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||
import { Typography, Grid, Stack, Icon, FormControlLabel, Checkbox, Card, CardHeader, CardActions, Avatar, CardContent, Button } from "@mui/material"; //Интерфейсные элементы
|
||
import { formatDateJSONDateOnly, formatDateRF } from "../../core/utils"; //Вспомогательные функции
|
||
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
|
||
import { P8PGantt } from "../../components/p8p_gantt"; //Диаграмма Ганта
|
||
import { P8P_GANTT_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
||
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
||
|
||
//---------
|
||
//Константы
|
||
//---------
|
||
|
||
//Отступ контейнера страницы от заголовка
|
||
const CONTAINER_PADDING_TOP = "20px";
|
||
|
||
//Высота заголовка страницы
|
||
const TITLE_HEIGHT = "47px";
|
||
|
||
//Высота элементов управления
|
||
const CONTROL_HEIGHT = "42px";
|
||
|
||
//Стили
|
||
const STYLES = {
|
||
CONTAINER: { textAlign: "center", paddingTop: CONTAINER_PADDING_TOP },
|
||
TITLE: { paddingBottom: "15px", height: TITLE_HEIGHT },
|
||
CONTROL: { height: CONTROL_HEIGHT },
|
||
GANTT_CONTAINER: {
|
||
height: `calc(100vh - ${APP_BAR_HEIGHT} - ${TITLE_HEIGHT} - ${CONTROL_HEIGHT} - ${CONTAINER_PADDING_TOP})`,
|
||
width: "100vw",
|
||
paddingTop: "5px"
|
||
}
|
||
};
|
||
|
||
//---------------------------------------------
|
||
//Вспомогательные функции форматирования данных
|
||
//---------------------------------------------
|
||
|
||
//Формирование значения для колонки "Тип задачи"
|
||
const formatTaskTypeValue = value => {
|
||
const [text, icon] = value == 0 ? ["Этап проекта", "check"] : ["Работа проекта", "work_outline"];
|
||
return (
|
||
<Stack direction="row" gap={0.5}>
|
||
<Icon title={text}>{icon}</Icon>
|
||
{text}
|
||
</Stack>
|
||
);
|
||
};
|
||
|
||
//Генерация кастомных представлений атрибутов задачи в редакторе
|
||
const taskAttributeRenderer = ({ task, attribute }) => {
|
||
switch (attribute.name) {
|
||
case "type":
|
||
return formatTaskTypeValue(task.type);
|
||
default:
|
||
return null;
|
||
}
|
||
};
|
||
|
||
//Генерация кастомного диалога задачи
|
||
const taskDialogRenderer = ({ task, close }) => {
|
||
return (
|
||
<Card>
|
||
<CardHeader
|
||
avatar={<Avatar sx={{ bgcolor: task.bgColor }}>{task.type == 0 ? "Эт" : "Ра"}</Avatar>}
|
||
title={task.name}
|
||
subheader={`с ${formatDateRF(task.start)} по ${formatDateRF(task.end)}`}
|
||
/>
|
||
<CardContent>
|
||
<Typography variant="body2" color="text.secondary">
|
||
Это пользовательский диалог с данными о задаче. Вы можете формировать такие указав свой функциональный компонент в качестве
|
||
свойства "taskDialogRenderer" компонента "P8PGantt".
|
||
</Typography>
|
||
</CardContent>
|
||
<CardActions disableSpacing>
|
||
<Button size="small" onClick={close}>
|
||
Закрыть
|
||
</Button>
|
||
</CardActions>
|
||
</Card>
|
||
);
|
||
};
|
||
|
||
//-----------
|
||
//Тело модуля
|
||
//-----------
|
||
|
||
//Пример: Диаграмма Ганта "P8Gantt"
|
||
const Gantt = ({ title }) => {
|
||
//Собственное состояние
|
||
const [gantt, setGantt] = useState({
|
||
init: false,
|
||
dataLoaded: false,
|
||
ident: null,
|
||
useCustomTaskDialog: false
|
||
});
|
||
|
||
//Подключение к контексту взаимодействия с сервером
|
||
const { executeStored } = useContext(BackEndСtx);
|
||
|
||
//Загрузка данных диаграммы с сервера
|
||
const loadData = useCallback(async () => {
|
||
const data = await executeStored({
|
||
stored: "PKG_P8PANELS_SAMPLES.GANTT",
|
||
args: { NIDENT: gantt.ident },
|
||
attributeValueProcessor: (name, val) =>
|
||
name == "numb" ? undefined : ["start", "end"].includes(name) ? formatDateJSONDateOnly(val) : val,
|
||
respArg: "COUT"
|
||
});
|
||
setGantt(pv => ({ ...pv, dataLoaded: true, ...data.XGANTT }));
|
||
}, [gantt.ident, executeStored]);
|
||
|
||
//Инициализация данных диаграммы
|
||
const initData = useCallback(async () => {
|
||
if (!gantt.init) {
|
||
const data = await executeStored({ stored: "PKG_P8PANELS_SAMPLES.GANTT_INIT", args: { NIDENT: gantt.ident } });
|
||
setGantt(pv => ({ ...pv, init: true, ident: data.NIDENT }));
|
||
}
|
||
}, [gantt.init, gantt.ident, executeStored]);
|
||
|
||
//Изменение данных диаграммы
|
||
const modifyData = useCallback(
|
||
async ({ rn, start, end }) => {
|
||
try {
|
||
await executeStored({
|
||
stored: "PKG_P8PANELS_SAMPLES.GANTT_MODIFY",
|
||
args: { NIDENT: gantt.ident, NRN: rn, DDATE_FROM: new Date(start), DDATE_TO: new Date(end) }
|
||
});
|
||
} finally {
|
||
loadData();
|
||
}
|
||
},
|
||
[gantt.ident, executeStored, loadData]
|
||
);
|
||
|
||
//Обработка измненения сроков задачи в диаграмме Гантта
|
||
const handleTaskDatesChange = ({ task, start, end, isMain }) => {
|
||
if (isMain) modifyData({ rn: task.rn, start, end });
|
||
};
|
||
|
||
//При необходимости обновить данные таблицы
|
||
useEffect(() => {
|
||
if (gantt.ident) loadData();
|
||
}, [gantt.ident, loadData]);
|
||
|
||
//При подключении компонента к странице
|
||
useEffect(() => {
|
||
initData();
|
||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||
}, []);
|
||
|
||
//Генерация содержимого
|
||
return (
|
||
<div style={STYLES.CONTAINER}>
|
||
<Typography sx={STYLES.TITLE} variant={"h6"}>
|
||
{title}
|
||
</Typography>
|
||
<FormControlLabel
|
||
sx={STYLES.CONTROL}
|
||
control={<Checkbox onChange={() => setGantt(pv => ({ ...pv, useCustomTaskDialog: !pv.useCustomTaskDialog }))} />}
|
||
label="Отображать пользовательский диалог задачи"
|
||
/>
|
||
<Grid container direction="column" alignItems="center">
|
||
<Grid item xs={12}>
|
||
{gantt.dataLoaded ? (
|
||
<P8PGantt
|
||
{...P8P_GANTT_CONFIG_PROPS}
|
||
{...gantt}
|
||
containerStyle={STYLES.GANTT_CONTAINER}
|
||
onTaskDatesChange={handleTaskDatesChange}
|
||
taskAttributeRenderer={taskAttributeRenderer}
|
||
taskDialogRenderer={gantt.useCustomTaskDialog ? taskDialogRenderer : null}
|
||
/>
|
||
) : null}
|
||
</Grid>
|
||
</Grid>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
//Контроль свойств - Пример: Диаграмма Ганта "P8Gantt"
|
||
Gantt.propTypes = {
|
||
title: PropTypes.string.isRequired
|
||
};
|
||
|
||
//----------------
|
||
//Интерфейс модуля
|
||
//----------------
|
||
|
||
export { Gantt };
|