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 };
 |