WEB APP: P8PGantt - управление видимостью атрибута задачи, поддержка пользовательских карточек задач
This commit is contained in:
parent
30c75fb079
commit
69cb24ed6e
@ -79,6 +79,7 @@ const P8P_GANTT_TASK_COLOR_SHAPE = PropTypes.shape({
|
|||||||
|
|
||||||
//Стили
|
//Стили
|
||||||
const STYLES = {
|
const STYLES = {
|
||||||
|
TASK_EDITOR_CONTENT: { minWidth: 400, overflowX: "auto" },
|
||||||
TASK_EDITOR_LIST: { width: "100%", minWidth: 300, maxWidth: 700, bgcolor: "background.paper" }
|
TASK_EDITOR_LIST: { width: "100%", minWidth: 300, maxWidth: 700, bgcolor: "background.paper" }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -97,6 +98,7 @@ const P8PGanttTaskEditor = ({
|
|||||||
onOk,
|
onOk,
|
||||||
onCancel,
|
onCancel,
|
||||||
taskAttributeRenderer,
|
taskAttributeRenderer,
|
||||||
|
taskDialogRenderer,
|
||||||
numbCaption,
|
numbCaption,
|
||||||
nameCaption,
|
nameCaption,
|
||||||
startCaption,
|
startCaption,
|
||||||
@ -113,6 +115,10 @@ const P8PGanttTaskEditor = ({
|
|||||||
progress: task.progress
|
progress: task.progress
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//Отображаемые атрибуты
|
||||||
|
const dispTaskAttributes =
|
||||||
|
Array.isArray(taskAttributes) && taskAttributes.length > 0 ? taskAttributes.filter(attr => attr.visible && hasValue(task[attr.name])) : [];
|
||||||
|
|
||||||
//При сохранении
|
//При сохранении
|
||||||
const handleOk = () => (onOk && state.start && state.end ? onOk({ task, start: state.start, end: state.end, progress: state.progress }) : null);
|
const handleOk = () => (onOk && state.start && state.end ? onOk({ task, start: state.start, end: state.end, progress: state.progress }) : null);
|
||||||
|
|
||||||
@ -158,7 +164,11 @@ const P8PGanttTaskEditor = ({
|
|||||||
//Генерация содержимого
|
//Генерация содержимого
|
||||||
return (
|
return (
|
||||||
<Dialog open onClose={handleCancel}>
|
<Dialog open onClose={handleCancel}>
|
||||||
<DialogContent>
|
{taskDialogRenderer ? (
|
||||||
|
taskDialogRenderer({ task, taskAttributes, taskColors, close: handleCancel })
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<DialogContent sx={STYLES.TASK_EDITOR_CONTENT}>
|
||||||
<List sx={STYLES.TASK_EDITOR_LIST}>
|
<List sx={STYLES.TASK_EDITOR_LIST}>
|
||||||
<ListItem alignItems="flex-start">
|
<ListItem alignItems="flex-start">
|
||||||
<ListItemText primary={numbCaption} secondary={task.numb} />
|
<ListItemText primary={numbCaption} secondary={task.numb} />
|
||||||
@ -213,7 +223,7 @@ const P8PGanttTaskEditor = ({
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<Divider component="li" />
|
{hasValue(task.progress) || legend || dispTaskAttributes.length > 0 ? <Divider component="li" /> : null}
|
||||||
{hasValue(task.progress) ? (
|
{hasValue(task.progress) ? (
|
||||||
<>
|
<>
|
||||||
<ListItem alignItems="flex-start">
|
<ListItem alignItems="flex-start">
|
||||||
@ -232,19 +242,17 @@ const P8PGanttTaskEditor = ({
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<Divider component="li" />
|
{legend || dispTaskAttributes.length > 0 ? <Divider component="li" /> : null}
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
{legend ? (
|
{legend ? (
|
||||||
<>
|
<>
|
||||||
<ListItem alignItems="flex-start">{legend}</ListItem>
|
<ListItem alignItems="flex-start">{legend}</ListItem>
|
||||||
<Divider component="li" />
|
{dispTaskAttributes.length > 0 ? <Divider component="li" /> : null}
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
{Array.isArray(taskAttributes) && taskAttributes.length > 0
|
{dispTaskAttributes.length > 0
|
||||||
? taskAttributes
|
? dispTaskAttributes.map((attr, i) => {
|
||||||
.filter(attr => hasValue(task[attr.name]))
|
|
||||||
.map((attr, i) => {
|
|
||||||
const defaultView = task[attr.name];
|
const defaultView = task[attr.name];
|
||||||
const customView = taskAttributeRenderer ? taskAttributeRenderer({ task, attribute: attr }) : null;
|
const customView = taskAttributeRenderer ? taskAttributeRenderer({ task, attribute: attr }) : null;
|
||||||
return (
|
return (
|
||||||
@ -256,7 +264,7 @@ const P8PGanttTaskEditor = ({
|
|||||||
secondary={customView ? customView : defaultView}
|
secondary={customView ? customView : defaultView}
|
||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
{i < taskAttributes.length - 1 ? <Divider component="li" /> : null}
|
{i < dispTaskAttributes.length - 1 ? <Divider component="li" /> : null}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
@ -269,6 +277,8 @@ const P8PGanttTaskEditor = ({
|
|||||||
</Button>
|
</Button>
|
||||||
<Button onClick={handleCancel}>{cancelBtnCaption}</Button>
|
<Button onClick={handleCancel}>{cancelBtnCaption}</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -281,6 +291,7 @@ P8PGanttTaskEditor.propTypes = {
|
|||||||
onOk: PropTypes.func,
|
onOk: PropTypes.func,
|
||||||
onCancel: PropTypes.func,
|
onCancel: PropTypes.func,
|
||||||
taskAttributeRenderer: PropTypes.func,
|
taskAttributeRenderer: PropTypes.func,
|
||||||
|
taskDialogRenderer: PropTypes.func,
|
||||||
numbCaption: PropTypes.string.isRequired,
|
numbCaption: PropTypes.string.isRequired,
|
||||||
nameCaption: PropTypes.string.isRequired,
|
nameCaption: PropTypes.string.isRequired,
|
||||||
startCaption: PropTypes.string.isRequired,
|
startCaption: PropTypes.string.isRequired,
|
||||||
@ -312,6 +323,7 @@ const P8PGantt = ({
|
|||||||
onTaskDatesChange,
|
onTaskDatesChange,
|
||||||
onTaskProgressChange,
|
onTaskProgressChange,
|
||||||
taskAttributeRenderer,
|
taskAttributeRenderer,
|
||||||
|
taskDialogRenderer,
|
||||||
noDataFoundText,
|
noDataFoundText,
|
||||||
numbTaskEditorCaption,
|
numbTaskEditorCaption,
|
||||||
nameTaskEditorCaption,
|
nameTaskEditorCaption,
|
||||||
@ -417,6 +429,7 @@ const P8PGantt = ({
|
|||||||
onOk={handleTaskEditorSave}
|
onOk={handleTaskEditorSave}
|
||||||
onCancel={handleTaskEditorCancel}
|
onCancel={handleTaskEditorCancel}
|
||||||
taskAttributeRenderer={taskAttributeRenderer}
|
taskAttributeRenderer={taskAttributeRenderer}
|
||||||
|
taskDialogRenderer={taskDialogRenderer}
|
||||||
numbCaption={numbTaskEditorCaption}
|
numbCaption={numbTaskEditorCaption}
|
||||||
nameCaption={nameTaskEditorCaption}
|
nameCaption={nameTaskEditorCaption}
|
||||||
startCaption={startTaskEditorCaption}
|
startCaption={startTaskEditorCaption}
|
||||||
@ -451,6 +464,7 @@ P8PGantt.propTypes = {
|
|||||||
onTaskDatesChange: PropTypes.func,
|
onTaskDatesChange: PropTypes.func,
|
||||||
onTaskProgressChange: PropTypes.func,
|
onTaskProgressChange: PropTypes.func,
|
||||||
taskAttributeRenderer: PropTypes.func,
|
taskAttributeRenderer: PropTypes.func,
|
||||||
|
taskDialogRenderer: PropTypes.func,
|
||||||
noDataFoundText: PropTypes.string.isRequired,
|
noDataFoundText: PropTypes.string.isRequired,
|
||||||
numbTaskEditorCaption: PropTypes.string.isRequired,
|
numbTaskEditorCaption: PropTypes.string.isRequired,
|
||||||
nameTaskEditorCaption: PropTypes.string.isRequired,
|
nameTaskEditorCaption: PropTypes.string.isRequired,
|
||||||
|
@ -9,8 +9,22 @@
|
|||||||
|
|
||||||
import React, { useState, useContext, useCallback, useEffect } from "react"; //Классы React
|
import React, { useState, useContext, useCallback, useEffect } from "react"; //Классы React
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import { Typography, Grid, Stack, Icon, Box } from "@mui/material"; //Интерфейсные элементы
|
import {
|
||||||
import { formatDateJSONDateOnly } from "../../core/utils"; //Вспомогательные функции
|
Typography,
|
||||||
|
Grid,
|
||||||
|
Stack,
|
||||||
|
Icon,
|
||||||
|
Box,
|
||||||
|
FormControlLabel,
|
||||||
|
Checkbox,
|
||||||
|
Card,
|
||||||
|
CardHeader,
|
||||||
|
CardActions,
|
||||||
|
Avatar,
|
||||||
|
CardContent,
|
||||||
|
Button
|
||||||
|
} from "@mui/material"; //Интерфейсные элементы
|
||||||
|
import { formatDateJSONDateOnly, formatDateRF } from "../../core/utils"; //Вспомогательные функции
|
||||||
import { P8PGantt } from "../../components/p8p_gantt"; //Диаграмма Ганта
|
import { P8PGantt } from "../../components/p8p_gantt"; //Диаграмма Ганта
|
||||||
import { P8P_GANTT_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
import { P8P_GANTT_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
||||||
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
||||||
@ -57,6 +71,30 @@ const taskAttributeRenderer = ({ task, attribute }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//Генерация кастомного диалога задачи
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
//-----------
|
//-----------
|
||||||
//Тело модуля
|
//Тело модуля
|
||||||
//-----------
|
//-----------
|
||||||
@ -69,7 +107,8 @@ const Gantt = ({ title }) => {
|
|||||||
dataLoaded: false,
|
dataLoaded: false,
|
||||||
ident: null,
|
ident: null,
|
||||||
ganttDef: {},
|
ganttDef: {},
|
||||||
ganttTasks: []
|
ganttTasks: [],
|
||||||
|
useCustomTaskDialog: false
|
||||||
});
|
});
|
||||||
|
|
||||||
//Подключение к контексту взаимодействия с сервером
|
//Подключение к контексту взаимодействия с сервером
|
||||||
@ -132,6 +171,10 @@ const Gantt = ({ title }) => {
|
|||||||
<Typography sx={STYLES.TITLE} variant={"h6"}>
|
<Typography sx={STYLES.TITLE} variant={"h6"}>
|
||||||
{title}
|
{title}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
<FormControlLabel
|
||||||
|
control={<Checkbox onChange={() => setState(pv => ({ ...pv, useCustomTaskDialog: !pv.useCustomTaskDialog }))} />}
|
||||||
|
label="Отображать пользовательский диалог задачи"
|
||||||
|
/>
|
||||||
<Grid container spacing={0} direction="column" alignItems="center">
|
<Grid container spacing={0} direction="column" alignItems="center">
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
{state.dataLoaded ? (
|
{state.dataLoaded ? (
|
||||||
@ -143,6 +186,7 @@ const Gantt = ({ title }) => {
|
|||||||
tasks={state.ganttTasks}
|
tasks={state.ganttTasks}
|
||||||
onTaskDatesChange={handleTaskDatesChange}
|
onTaskDatesChange={handleTaskDatesChange}
|
||||||
taskAttributeRenderer={taskAttributeRenderer}
|
taskAttributeRenderer={taskAttributeRenderer}
|
||||||
|
taskDialogRenderer={state.useCustomTaskDialog ? taskDialogRenderer : null}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
) : null}
|
) : null}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user