forked from CITKParus/P8-Panels
Compare commits
10 Commits
main
...
ClntTaskBo
| Author | SHA1 | Date | |
|---|---|---|---|
| dabe86957f | |||
| 4b2d589e63 | |||
| c64c9cd3a1 | |||
| 8507127c39 | |||
| 434504dda3 | |||
| c0b905fe18 | |||
| 29c8ecf4ae | |||
| c436700890 | |||
| 4d2c76eca8 | |||
| 4ad1024741 |
@ -53,7 +53,6 @@ export const CAPTIONS = {
|
|||||||
export const ERRORS = {
|
export const ERRORS = {
|
||||||
UNDER_CONSTRUCTION: "Панель в разработке",
|
UNDER_CONSTRUCTION: "Панель в разработке",
|
||||||
P8O_API_UNAVAILABLE: '"ПАРУС 8 Онлайн" недоступен',
|
P8O_API_UNAVAILABLE: '"ПАРУС 8 Онлайн" недоступен',
|
||||||
P8O_API_UNSUPPORTED: 'Функция "ПАРУС 8 Онлайн" не поддерживается',
|
|
||||||
DEFAULT: "Неожиданная ошибка"
|
DEFAULT: "Неожиданная ошибка"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -23,8 +23,7 @@ import {
|
|||||||
ListItemIcon,
|
ListItemIcon,
|
||||||
ListItemText
|
ListItemText
|
||||||
} from "@mui/material"; //Интерфейсные компоненты
|
} from "@mui/material"; //Интерфейсные компоненты
|
||||||
import { P8PPanelsMenuDrawer, P8P_PANELS_MENU_PANEL_SHAPE } from "./p8p_panels_menu"; //Меню
|
import { P8PPanelsMenuDrawer, P8P_PANELS_MENU_PANEL_SHAPE } from "./p8p_panels_menu";
|
||||||
import { APP_STYLES } from "../../app.styles"; //Типовые стили
|
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
//Константы
|
//Константы
|
||||||
@ -35,7 +34,6 @@ const APP_BAR_HEIGHT = "64px";
|
|||||||
|
|
||||||
//Стили
|
//Стили
|
||||||
const STYLES = {
|
const STYLES = {
|
||||||
DRAWER: { [`& .MuiDrawer-paper`]: { ...APP_STYLES.SCROLL } },
|
|
||||||
ROOT_BOX: { display: "flex" },
|
ROOT_BOX: { display: "flex" },
|
||||||
APP_BAR: { position: "fixed" },
|
APP_BAR: { position: "fixed" },
|
||||||
APP_BAR_BUTTON: { mr: 2 },
|
APP_BAR_BUTTON: { mr: 2 },
|
||||||
@ -90,7 +88,7 @@ const P8PAppWorkspace = ({ children, panels = [], selectedPanel, closeCaption, h
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
<Drawer anchor="left" open={open} onClose={handleDrawerClose} sx={STYLES.DRAWER}>
|
<Drawer anchor="left" open={open} onClose={handleDrawerClose}>
|
||||||
<List>
|
<List>
|
||||||
<ListItemButton onClick={handleDrawerClose}>
|
<ListItemButton onClick={handleDrawerClose}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
//Подключение библиотек
|
//Подключение библиотек
|
||||||
//---------------------
|
//---------------------
|
||||||
|
|
||||||
import React, { useCallback, useEffect, useRef } from "react"; //Классы React
|
import React, { useEffect, useRef } from "react"; //Классы React
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import Chart from "chart.js/auto"; //Диаграммы и графики
|
import Chart from "chart.js/auto"; //Диаграммы и графики
|
||||||
|
|
||||||
@ -37,26 +37,23 @@ const P8P_CHART_DATASET_SHAPE = PropTypes.shape({
|
|||||||
//-----------
|
//-----------
|
||||||
|
|
||||||
//График
|
//График
|
||||||
const P8PChart = ({ type, title, legendPosition, options = {}, labels = [], datasets = [], onClick, style }) => {
|
const P8PChart = ({ type, title, legendPosition, options, labels, datasets, onClick, style }) => {
|
||||||
//Ссылки на DOM
|
//Ссылки на DOM
|
||||||
const chartCanvasRef = useRef(null);
|
const chartCanvasRef = useRef(null);
|
||||||
const chartRef = useRef(null);
|
const chartRef = useRef(null);
|
||||||
|
|
||||||
//Обработка нажатия на элемент графика
|
//Обработка нажатия на элемент графика
|
||||||
const handleClick = useCallback(
|
const handleClick = e => {
|
||||||
e => {
|
const bar = chartRef.current.getElementsAtEventForMode(e, "nearest", { intersect: true }, true)[0];
|
||||||
const bar = chartRef.current.getElementsAtEventForMode(e, "nearest", { intersect: true }, true)[0];
|
if (onClick && bar)
|
||||||
if (onClick && bar)
|
onClick({
|
||||||
onClick({
|
datasetIndex: bar.datasetIndex,
|
||||||
datasetIndex: bar.datasetIndex,
|
itemIndex: bar.index,
|
||||||
itemIndex: bar.index,
|
item: chartRef.current.data.datasets[bar.datasetIndex].items
|
||||||
item: chartRef.current.data.datasets[bar.datasetIndex].items
|
? chartRef.current.data.datasets[bar.datasetIndex].items[bar.index]
|
||||||
? chartRef.current.data.datasets[bar.datasetIndex].items[bar.index]
|
: null
|
||||||
: null
|
});
|
||||||
});
|
};
|
||||||
},
|
|
||||||
[onClick]
|
|
||||||
);
|
|
||||||
|
|
||||||
//При подключении к старнице
|
//При подключении к старнице
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -92,10 +89,9 @@ const P8PChart = ({ type, title, legendPosition, options = {}, labels = [], data
|
|||||||
if (chartRef.current) {
|
if (chartRef.current) {
|
||||||
chartRef.current.data.labels = [...labels];
|
chartRef.current.data.labels = [...labels];
|
||||||
chartRef.current.data.datasets = [...datasets];
|
chartRef.current.data.datasets = [...datasets];
|
||||||
chartRef.current.options.onClick = handleClick;
|
|
||||||
chartRef.current.update();
|
chartRef.current.update();
|
||||||
}
|
}
|
||||||
}, [datasets, labels, handleClick]);
|
}, [datasets, labels]);
|
||||||
|
|
||||||
//Генерация содержимого
|
//Генерация содержимого
|
||||||
return (
|
return (
|
||||||
@ -111,7 +107,7 @@ P8PChart.propTypes = {
|
|||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
legendPosition: PropTypes.string,
|
legendPosition: PropTypes.string,
|
||||||
options: PropTypes.object,
|
options: PropTypes.object,
|
||||||
labels: PropTypes.arrayOf(PropTypes.string),
|
labels: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||||
datasets: PropTypes.arrayOf(P8P_CHART_DATASET_SHAPE),
|
datasets: PropTypes.arrayOf(P8P_CHART_DATASET_SHAPE),
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
style: PropTypes.object
|
style: PropTypes.object
|
||||||
|
|||||||
@ -1,819 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга
|
|
||||||
Компонент: Циклограмма
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React, { useEffect, useState, useRef } from "react"; //Классы React
|
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
Typography,
|
|
||||||
Dialog,
|
|
||||||
DialogActions,
|
|
||||||
DialogContent,
|
|
||||||
Button,
|
|
||||||
List,
|
|
||||||
ListItem,
|
|
||||||
ListItemText,
|
|
||||||
Link,
|
|
||||||
Divider,
|
|
||||||
IconButton,
|
|
||||||
Icon
|
|
||||||
} from "@mui/material"; //Интерфейсные компоненты
|
|
||||||
import { P8PAppInlineError } from "./p8p_app_message"; //Встраиваемое сообщение об ошибке
|
|
||||||
import { hasValue } from "../core/utils"; //Вспомогательный функции
|
|
||||||
|
|
||||||
//---------
|
|
||||||
//Константы
|
|
||||||
//---------
|
|
||||||
|
|
||||||
//Уровни масштаба
|
|
||||||
const P8P_CYCLOGRAM_ZOOM = [0.2, 0.4, 0.7, 1, 1.5, 2, 2.5];
|
|
||||||
|
|
||||||
//Параметры элементов циклограммы
|
|
||||||
const NDEFAULT_LINE_HEIGHT = 20;
|
|
||||||
const NDEFAULT_HEADER_HEIGHT = 35;
|
|
||||||
|
|
||||||
//Высота заголовка
|
|
||||||
const TITLE_HEIGHT = "44px";
|
|
||||||
|
|
||||||
//Высота панели масштабирования
|
|
||||||
const ZOOM_HEIGHT = "56px";
|
|
||||||
|
|
||||||
//Стили
|
|
||||||
const STYLES = {
|
|
||||||
CYCLOGRAM_TITLE: { height: TITLE_HEIGHT },
|
|
||||||
CYCLOGRAM_ZOOM: { height: ZOOM_HEIGHT },
|
|
||||||
HEADER_COLUMN: {
|
|
||||||
fontSize: "12px",
|
|
||||||
textOverflow: "ellipsis",
|
|
||||||
overflow: "hidden",
|
|
||||||
whiteSpace: "pre",
|
|
||||||
textAlign: "center",
|
|
||||||
lineHeight: "3",
|
|
||||||
padding: "0px 5px"
|
|
||||||
},
|
|
||||||
CYCLOGRAM_BOX: (noData, title, zoomBar) => ({
|
|
||||||
position: "relative",
|
|
||||||
overflow: "auto",
|
|
||||||
padding: "0px 8px",
|
|
||||||
height: `calc(100% - ${zoomBar ? ZOOM_HEIGHT : "0px"} - ${title ? TITLE_HEIGHT : "0px"})`,
|
|
||||||
display: noData ? "none" : ""
|
|
||||||
}),
|
|
||||||
GRID_ROW: index => (index % 2 === 0 ? { backgroundColor: "#ffffff" } : { backgroundColor: "#f5f5f5" }),
|
|
||||||
GROUP_HEADER_BOX: {
|
|
||||||
border: "1px solid",
|
|
||||||
backgroundColor: "#ebebeb",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center"
|
|
||||||
},
|
|
||||||
GROUP_HEADER: {
|
|
||||||
fontSize: "14px",
|
|
||||||
textAlign: "center",
|
|
||||||
wordWrap: "break-word"
|
|
||||||
},
|
|
||||||
TASK_EDITOR_CONTENT: { minWidth: 400, overflowX: "auto" },
|
|
||||||
TASK_EDITOR_LIST: { width: "100%", minWidth: 300, maxWidth: 700, bgcolor: "background.paper" },
|
|
||||||
TASK_BOX: (lineHeight, bgColor, textColor, highlightColor) => ({
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
backgroundColor: bgColor ? bgColor : "#b4b9bf",
|
|
||||||
...(textColor ? { color: textColor } : {}),
|
|
||||||
height: lineHeight,
|
|
||||||
"&:hover": {
|
|
||||||
...(highlightColor
|
|
||||||
? { backgroundColor: `${highlightColor} !important`, filter: "brightness(1) !important" }
|
|
||||||
: { filter: "brightness(1.25) !important" }),
|
|
||||||
cursor: "pointer !important"
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
TASK: lineHeight => {
|
|
||||||
const availableLines = Math.floor(lineHeight / 18);
|
|
||||||
return {
|
|
||||||
width: "100%",
|
|
||||||
fontSize: "12px",
|
|
||||||
overflowWrap: "break-word",
|
|
||||||
wordBreak: "break-all",
|
|
||||||
overflow: "hidden",
|
|
||||||
textOverflow: "ellipsis",
|
|
||||||
display: "-webkit-box",
|
|
||||||
lineHeight: "18px",
|
|
||||||
maxHeight: lineHeight,
|
|
||||||
WebkitLineClamp: availableLines < 1 ? 1 : availableLines,
|
|
||||||
WebkitBoxOrient: "vertical"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//Структура колонки
|
|
||||||
const P8P_CYCLOGRAM_COLUMN_SHAPE = PropTypes.shape({
|
|
||||||
name: PropTypes.string.isRequired,
|
|
||||||
start: PropTypes.number.isRequired,
|
|
||||||
end: PropTypes.number.isRequired
|
|
||||||
});
|
|
||||||
|
|
||||||
//Структура группы
|
|
||||||
const P8P_CYCLOGRAM_GROUP_SHAPE = PropTypes.shape({
|
|
||||||
name: PropTypes.string.isRequired,
|
|
||||||
height: PropTypes.number.isRequired,
|
|
||||||
width: PropTypes.number.isRequired,
|
|
||||||
visible: PropTypes.bool.isRequired
|
|
||||||
});
|
|
||||||
|
|
||||||
//Структура задачи
|
|
||||||
const P8P_CYCLOGRAM_TASK_SHAPE = PropTypes.shape({
|
|
||||||
id: PropTypes.string.isRequired,
|
|
||||||
rn: PropTypes.number.isRequired,
|
|
||||||
name: PropTypes.string.isRequired,
|
|
||||||
fullName: PropTypes.string.isRequired,
|
|
||||||
lineNumb: PropTypes.number.isRequired,
|
|
||||||
start: PropTypes.number.isRequired,
|
|
||||||
end: PropTypes.number.isRequired,
|
|
||||||
group: PropTypes.string,
|
|
||||||
bgColor: PropTypes.string,
|
|
||||||
textColor: PropTypes.string,
|
|
||||||
highlightColor: PropTypes.string
|
|
||||||
});
|
|
||||||
|
|
||||||
//Структура динамического атрибута задачи
|
|
||||||
const P8P_CYCLOGRAM_TASK_ATTRIBUTE_SHAPE = PropTypes.shape({
|
|
||||||
name: PropTypes.string.isRequired,
|
|
||||||
caption: PropTypes.string.isRequired,
|
|
||||||
visible: PropTypes.bool.isRequired
|
|
||||||
});
|
|
||||||
|
|
||||||
//--------------------------------
|
|
||||||
//Вспомогательные классы и функции
|
|
||||||
//--------------------------------
|
|
||||||
|
|
||||||
//Определение сдвига для максимальной ширины колонок
|
|
||||||
const getShift = (columns, currentColumnsMaxWidth, maxCyclogramWidth) => {
|
|
||||||
//Определяем доступное пространство для расширения
|
|
||||||
let maxWidthDiff = maxCyclogramWidth - currentColumnsMaxWidth;
|
|
||||||
//Инициализируем значение сдвига
|
|
||||||
let shift = 1;
|
|
||||||
//Если доступно больше ширины и есть пространство для расширения
|
|
||||||
if (maxCyclogramWidth > currentColumnsMaxWidth && maxCyclogramWidth - maxWidthDiff > columns.length) {
|
|
||||||
//Определяем доступный сдвиг колонок
|
|
||||||
shift = maxCyclogramWidth / currentColumnsMaxWidth;
|
|
||||||
}
|
|
||||||
//Возвращаем сдвиг
|
|
||||||
return shift;
|
|
||||||
};
|
|
||||||
|
|
||||||
//Формирование стилей для группы
|
|
||||||
const getGroupStyles = (indexGrp, highlightColor) => {
|
|
||||||
return `.main .TaskGrp${indexGrp}:hover .TaskGrp${indexGrp} {
|
|
||||||
${highlightColor ? `background: ${highlightColor};` : `filter: brightness(1.15);`}
|
|
||||||
}
|
|
||||||
.main:has(.TaskGrp${indexGrp}:hover) .TaskGrpHeader${indexGrp} {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
//cursor: pointer;
|
|
||||||
};
|
|
||||||
|
|
||||||
//Фон строк таблицы
|
|
||||||
const P8PCyclogramRowsGrid = ({ rows, maxWidth, lineHeight }) => {
|
|
||||||
return (
|
|
||||||
<g>
|
|
||||||
{rows.map((el, index) => (
|
|
||||||
<foreignObject x="0" y={NDEFAULT_HEADER_HEIGHT + index * lineHeight} width={maxWidth} height={lineHeight} key={index}>
|
|
||||||
<Box sx={STYLES.GRID_ROW(index)} height={lineHeight} />
|
|
||||||
</foreignObject>
|
|
||||||
))}
|
|
||||||
</g>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств - Фон строк таблицы
|
|
||||||
P8PCyclogramRowsGrid.propTypes = {
|
|
||||||
rows: PropTypes.array.isRequired,
|
|
||||||
maxWidth: PropTypes.number.isRequired,
|
|
||||||
lineHeight: PropTypes.number.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
//Линии строк таблицы
|
|
||||||
const P8PCyclogramRowsLines = ({ rows, maxWidth, lineHeight }) => {
|
|
||||||
return (
|
|
||||||
<g>
|
|
||||||
{rows.map((el, index) => (
|
|
||||||
<line
|
|
||||||
x1="0"
|
|
||||||
y1={NDEFAULT_HEADER_HEIGHT + lineHeight + index * lineHeight}
|
|
||||||
x2={maxWidth}
|
|
||||||
y2={NDEFAULT_HEADER_HEIGHT + lineHeight + index * lineHeight}
|
|
||||||
key={index}
|
|
||||||
></line>
|
|
||||||
))}
|
|
||||||
</g>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств - Линии строк таблицы
|
|
||||||
P8PCyclogramRowsLines.propTypes = {
|
|
||||||
rows: PropTypes.array.isRequired,
|
|
||||||
maxWidth: PropTypes.number.isRequired,
|
|
||||||
lineHeight: PropTypes.number.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
//Линии колонок таблицы
|
|
||||||
const P8PCyclogramColumnsLines = ({ columns, shift, y1, y2 }) => {
|
|
||||||
//Инициализируем старт текущей колонки
|
|
||||||
let prevColumnEnd = 0;
|
|
||||||
return (
|
|
||||||
<g>
|
|
||||||
{columns.map((column, index) => {
|
|
||||||
//Аккумулируем окончание последней колонки с учетом сдвига
|
|
||||||
prevColumnEnd = index !== 0 ? prevColumnEnd + (columns[index - 1].end - columns[index - 1].start) * shift : 0;
|
|
||||||
return <line x1={prevColumnEnd} y1={y1} x2={prevColumnEnd} y2={y2} stroke="#e0e0e0" key={index} />;
|
|
||||||
})}
|
|
||||||
<line
|
|
||||||
x1={prevColumnEnd + (columns[columns.length - 1].end - columns[columns.length - 1].start) * shift}
|
|
||||||
y1={y1}
|
|
||||||
x2={prevColumnEnd + (columns[columns.length - 1].end - columns[columns.length - 1].start) * shift}
|
|
||||||
y2={y2}
|
|
||||||
stroke="#e0e0e0"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств - Линии колонок таблицы
|
|
||||||
P8PCyclogramColumnsLines.propTypes = {
|
|
||||||
columns: PropTypes.array.isRequired,
|
|
||||||
shift: PropTypes.number.isRequired,
|
|
||||||
y1: PropTypes.number.isRequired,
|
|
||||||
y2: PropTypes.number.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
//Фон таблицы циклограммы
|
|
||||||
const P8PCyclogramGrid = ({ tasks, columns, shift, maxWidth, maxHeight, lineHeight }) => {
|
|
||||||
//Формируем массив строк исходя из максимального значения строки задачи
|
|
||||||
const rows = Array.from(Array(Math.max(...tasks.map(o => o.lineNumb)) + 1).keys());
|
|
||||||
return (
|
|
||||||
<g className="grid">
|
|
||||||
<rect x="0" y="0" width={maxWidth} height={maxHeight}></rect>
|
|
||||||
<P8PCyclogramRowsGrid rows={rows} maxWidth={maxWidth} lineHeight={lineHeight} />
|
|
||||||
<P8PCyclogramRowsLines rows={rows} maxWidth={maxWidth} lineHeight={lineHeight} />
|
|
||||||
<P8PCyclogramColumnsLines columns={columns} shift={shift} y1={NDEFAULT_HEADER_HEIGHT} y2={maxHeight} />
|
|
||||||
</g>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств - Фон таблицы циклограммы
|
|
||||||
P8PCyclogramGrid.propTypes = {
|
|
||||||
tasks: PropTypes.array.isRequired,
|
|
||||||
columns: PropTypes.array.isRequired,
|
|
||||||
shift: PropTypes.number.isRequired,
|
|
||||||
maxWidth: PropTypes.number.isRequired,
|
|
||||||
maxHeight: PropTypes.number.isRequired,
|
|
||||||
lineHeight: PropTypes.number.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
//Колонка заголовка циклограммы
|
|
||||||
const P8PCyclogramHeaderColumn = ({ column, start, shift, columnRenderer }) => {
|
|
||||||
//Рассчитываем ширину колонки
|
|
||||||
const columnWidth = column.end - column.start;
|
|
||||||
//Формируем собственное отображение, если требуется
|
|
||||||
const customView = columnRenderer ? columnRenderer({ column }) : null;
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<foreignObject x={start} y="0" width={columnWidth * shift} height={NDEFAULT_HEADER_HEIGHT}>
|
|
||||||
{customView ? (
|
|
||||||
customView
|
|
||||||
) : (
|
|
||||||
<Typography sx={{ ...STYLES.HEADER_COLUMN, height: NDEFAULT_HEADER_HEIGHT }} title={column.name}>
|
|
||||||
{column.name}
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
</foreignObject>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств - Колонка заголовка циклограммы
|
|
||||||
P8PCyclogramHeaderColumn.propTypes = {
|
|
||||||
column: PropTypes.object.isRequired,
|
|
||||||
start: PropTypes.number.isRequired,
|
|
||||||
shift: PropTypes.number.isRequired,
|
|
||||||
maxHeight: PropTypes.number.isRequired,
|
|
||||||
lastElement: PropTypes.bool,
|
|
||||||
columnRenderer: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
//Заголовок циклограммы
|
|
||||||
const P8PCyclogramHeader = ({ columns, shift, maxWidth, maxHeight, columnRenderer, headerBlock }) => {
|
|
||||||
//Инициализируем старт текущей колонки
|
|
||||||
let prevColumnEnd = 0;
|
|
||||||
return (
|
|
||||||
<g className="header" ref={headerBlock}>
|
|
||||||
<rect x="0" y="0" width={maxWidth} height={NDEFAULT_HEADER_HEIGHT} fill="#ffffff" stroke="#e0e0e0" strokeWidth="1.4"></rect>
|
|
||||||
{columns.map((column, index) => {
|
|
||||||
//Аккумулируем окончание последней колонки с учетом сдвига
|
|
||||||
prevColumnEnd = index !== 0 ? prevColumnEnd + (columns[index - 1].end - columns[index - 1].start) * shift : 0;
|
|
||||||
return (
|
|
||||||
<P8PCyclogramHeaderColumn
|
|
||||||
column={column}
|
|
||||||
shift={shift}
|
|
||||||
start={prevColumnEnd}
|
|
||||||
maxHeight={maxHeight}
|
|
||||||
lastElement={columns.length - 1 === index}
|
|
||||||
columnRenderer={columnRenderer}
|
|
||||||
key={index}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
<g className="columnsDividers">
|
|
||||||
<P8PCyclogramColumnsLines columns={columns} shift={shift} y1={0} y2={NDEFAULT_HEADER_HEIGHT} />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств - Заголовок циклограммы
|
|
||||||
P8PCyclogramHeader.propTypes = {
|
|
||||||
columns: PropTypes.array.isRequired,
|
|
||||||
shift: PropTypes.number.isRequired,
|
|
||||||
maxWidth: PropTypes.number.isRequired,
|
|
||||||
maxHeight: PropTypes.number.isRequired,
|
|
||||||
columnRenderer: PropTypes.func,
|
|
||||||
headerBlock: PropTypes.object
|
|
||||||
};
|
|
||||||
|
|
||||||
//Задача циклограммы
|
|
||||||
const P8PCyclogramTask = ({ task, indexGrp, shift, lineHeight, openTaskEditor, taskRenderer }) => {
|
|
||||||
//Рассчитываем ширину задачи
|
|
||||||
const width = task.end !== 0 ? (task.end - task.start) * shift : 0;
|
|
||||||
//Формируем собственное отображение, если требуется
|
|
||||||
const customView = taskRenderer ? taskRenderer({ task, taskHeight: lineHeight, taskWidth: width }) || {} : {};
|
|
||||||
return (
|
|
||||||
<foreignObject
|
|
||||||
x={task.start !== 0 ? task.start * shift : 0}
|
|
||||||
y={NDEFAULT_HEADER_HEIGHT + task.lineNumb * lineHeight}
|
|
||||||
width={width}
|
|
||||||
height={lineHeight}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
className={hasValue(indexGrp) ? `TaskGrp${indexGrp}` : null}
|
|
||||||
sx={{ ...STYLES.TASK_BOX(lineHeight, task.bgColor, task.textColor, task.highlightColor), ...customView.taskStyle }}
|
|
||||||
{...customView.taskProps}
|
|
||||||
onClick={() => openTaskEditor(task)}
|
|
||||||
>
|
|
||||||
{customView.data ? (
|
|
||||||
customView.data
|
|
||||||
) : (
|
|
||||||
<Typography sx={STYLES.TASK(lineHeight)} title={task.name}>
|
|
||||||
{task.name}
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
</foreignObject>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств - Группы циклограммы
|
|
||||||
P8PCyclogramTask.propTypes = {
|
|
||||||
task: PropTypes.object.isRequired,
|
|
||||||
indexGrp: PropTypes.number,
|
|
||||||
shift: PropTypes.number.isRequired,
|
|
||||||
lineHeight: PropTypes.number.isRequired,
|
|
||||||
openTaskEditor: PropTypes.func.isRequired,
|
|
||||||
taskRenderer: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
//Основная информация циклограммы
|
|
||||||
const P8PCyclogramMain = ({
|
|
||||||
columns,
|
|
||||||
groups,
|
|
||||||
tasks,
|
|
||||||
shift,
|
|
||||||
lineHeight,
|
|
||||||
maxWidth,
|
|
||||||
maxHeight,
|
|
||||||
openTaskEditor,
|
|
||||||
groupHeaderRenderer,
|
|
||||||
taskRenderer,
|
|
||||||
columnRenderer,
|
|
||||||
headerBlock
|
|
||||||
}) => {
|
|
||||||
//Инициализируем коллекцию тасков с группами
|
|
||||||
const tasksWithGroup = tasks.filter(task => hasValue(task.groupName));
|
|
||||||
//Инициализируем коллекцию тасков без групп
|
|
||||||
const tasksWithoutGroup = tasks.filter(task => !hasValue(task.groupName));
|
|
||||||
//Инициализируем коллекцию отображаемых групп
|
|
||||||
const visibleGroups = groups ? groups.filter(group => group.visible) : [];
|
|
||||||
return (
|
|
||||||
<g className="main">
|
|
||||||
<g className="tasks">
|
|
||||||
{visibleGroups.length !== 0
|
|
||||||
? visibleGroups.map((grp, indexGrp) => {
|
|
||||||
//Считываем задачи группы
|
|
||||||
let groupTasks = tasksWithGroup.filter(task => task.groupName === grp.name);
|
|
||||||
//Если по данной группе нет тасков - ничего не выводим
|
|
||||||
if (groupTasks.length === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<g className={`TaskGrp${indexGrp}`} key={indexGrp}>
|
|
||||||
{groupTasks.map((task, index) => (
|
|
||||||
<P8PCyclogramTask
|
|
||||||
task={task}
|
|
||||||
indexGrp={indexGrp}
|
|
||||||
shift={shift}
|
|
||||||
lineHeight={lineHeight}
|
|
||||||
openTaskEditor={openTaskEditor}
|
|
||||||
taskRenderer={taskRenderer}
|
|
||||||
key={index}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
<style>{getGroupStyles(indexGrp, grp.highlightColor)}</style>
|
|
||||||
</g>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
: null}
|
|
||||||
<g className={`TasksWithoutGroups`}>
|
|
||||||
{tasksWithoutGroup.map((task, index) => {
|
|
||||||
return (
|
|
||||||
<P8PCyclogramTask
|
|
||||||
task={task}
|
|
||||||
shift={shift}
|
|
||||||
lineHeight={lineHeight}
|
|
||||||
openTaskEditor={openTaskEditor}
|
|
||||||
taskRenderer={taskRenderer}
|
|
||||||
key={index}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<P8PCyclogramHeader
|
|
||||||
columns={columns}
|
|
||||||
shift={shift}
|
|
||||||
maxWidth={maxWidth}
|
|
||||||
maxHeight={maxHeight}
|
|
||||||
columnRenderer={columnRenderer}
|
|
||||||
headerBlock={headerBlock}
|
|
||||||
/>
|
|
||||||
{visibleGroups.length !== 0 ? (
|
|
||||||
<g className="groups">
|
|
||||||
{visibleGroups.map((grp, indexGrp) => {
|
|
||||||
//Инициализируем параметры группы
|
|
||||||
let defaultView = null;
|
|
||||||
let customView = null;
|
|
||||||
let groupHeaderX = 0;
|
|
||||||
let groupHeaderY = 0;
|
|
||||||
let groupTasks = tasksWithGroup.filter(task => task.groupName === grp.name);
|
|
||||||
//Если по данной группе нет тасков - ничего не выводим
|
|
||||||
if (groupTasks.length === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
//Если требуется отображать заголовок группы
|
|
||||||
if (grp.visible) {
|
|
||||||
//Формируем отображение по умолчанию
|
|
||||||
defaultView = (
|
|
||||||
<Box sx={{ ...STYLES.GROUP_HEADER_BOX, height: grp.height }}>
|
|
||||||
<Typography sx={{ ...STYLES.GROUP_HEADER, maxWidth: grp.width, maxHeight: grp.height }}>{grp.name}</Typography>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
//Формируем собственное отображение, если требуется
|
|
||||||
customView = groupHeaderRenderer ? groupHeaderRenderer({ group: grp }) : null;
|
|
||||||
//Рассчитываем координаты заголовка группы
|
|
||||||
groupHeaderX = Math.min(...groupTasks.map(o => o.start)) * shift;
|
|
||||||
groupHeaderY = NDEFAULT_HEADER_HEIGHT + Math.min(...groupTasks.map(o => o.lineNumb)) * lineHeight - grp.height - 5;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<foreignObject
|
|
||||||
x={groupHeaderX}
|
|
||||||
y={groupHeaderY}
|
|
||||||
width={grp.width}
|
|
||||||
height={grp.height}
|
|
||||||
className={`TaskGrpHeader${indexGrp}`}
|
|
||||||
display="none"
|
|
||||||
key={indexGrp}
|
|
||||||
>
|
|
||||||
{customView ? customView : defaultView}
|
|
||||||
</foreignObject>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</g>
|
|
||||||
) : null}
|
|
||||||
</g>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств - Основная информация циклограммы
|
|
||||||
P8PCyclogramMain.propTypes = {
|
|
||||||
columns: PropTypes.array.isRequired,
|
|
||||||
groups: PropTypes.array,
|
|
||||||
tasks: PropTypes.array.isRequired,
|
|
||||||
shift: PropTypes.number.isRequired,
|
|
||||||
lineHeight: PropTypes.number.isRequired,
|
|
||||||
maxWidth: PropTypes.number.isRequired,
|
|
||||||
maxHeight: PropTypes.number.isRequired,
|
|
||||||
openTaskEditor: PropTypes.func.isRequired,
|
|
||||||
groupHeaderRenderer: PropTypes.func,
|
|
||||||
taskRenderer: PropTypes.func,
|
|
||||||
columnRenderer: PropTypes.func,
|
|
||||||
headerBlock: PropTypes.object
|
|
||||||
};
|
|
||||||
|
|
||||||
//Редактор задачи
|
|
||||||
const P8PCyclogramTaskEditor = ({
|
|
||||||
task,
|
|
||||||
taskAttributes,
|
|
||||||
onOk,
|
|
||||||
onCancel,
|
|
||||||
taskAttributeRenderer,
|
|
||||||
taskDialogRenderer,
|
|
||||||
nameCaption,
|
|
||||||
okBtnCaption,
|
|
||||||
cancelBtnCaption
|
|
||||||
}) => {
|
|
||||||
//Собственное состояние
|
|
||||||
const [state] = useState({
|
|
||||||
start: task.start,
|
|
||||||
end: task.end
|
|
||||||
});
|
|
||||||
|
|
||||||
//Отображаемые атрибуты
|
|
||||||
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() : null);
|
|
||||||
|
|
||||||
//При отмене
|
|
||||||
const handleCancel = () => (onCancel ? onCancel() : null);
|
|
||||||
|
|
||||||
//Генерация содержимого
|
|
||||||
return (
|
|
||||||
<Dialog open onClose={handleCancel}>
|
|
||||||
{taskDialogRenderer ? (
|
|
||||||
taskDialogRenderer({ task, taskAttributes, close: handleCancel })
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<DialogContent sx={STYLES.TASK_EDITOR_CONTENT}>
|
|
||||||
<List sx={STYLES.TASK_EDITOR_LIST}>
|
|
||||||
<ListItem alignItems="flex-start">
|
|
||||||
<ListItemText primary={nameCaption} secondary={task.fullName} />
|
|
||||||
</ListItem>
|
|
||||||
{dispTaskAttributes.length > 0 ? <Divider component="li" /> : null}
|
|
||||||
{dispTaskAttributes.length > 0
|
|
||||||
? dispTaskAttributes.map((attr, i) => {
|
|
||||||
const defaultView = task[attr.name];
|
|
||||||
const customView = taskAttributeRenderer ? taskAttributeRenderer({ task, attribute: attr }) : null;
|
|
||||||
return (
|
|
||||||
<React.Fragment key={i}>
|
|
||||||
<ListItem alignItems="flex-start">
|
|
||||||
<ListItemText
|
|
||||||
primary={attr.caption}
|
|
||||||
secondaryTypographyProps={{ component: "span" }}
|
|
||||||
secondary={customView ? customView : defaultView}
|
|
||||||
/>
|
|
||||||
</ListItem>
|
|
||||||
{i < dispTaskAttributes.length - 1 ? <Divider component="li" /> : null}
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
: null}
|
|
||||||
</List>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button onClick={handleOk}>{okBtnCaption}</Button>
|
|
||||||
<Button onClick={handleCancel}>{cancelBtnCaption}</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств - Редактор задачи
|
|
||||||
P8PCyclogramTaskEditor.propTypes = {
|
|
||||||
task: P8P_CYCLOGRAM_TASK_SHAPE,
|
|
||||||
taskAttributes: PropTypes.arrayOf(P8P_CYCLOGRAM_TASK_ATTRIBUTE_SHAPE),
|
|
||||||
onOk: PropTypes.func,
|
|
||||||
onCancel: PropTypes.func,
|
|
||||||
taskAttributeRenderer: PropTypes.func,
|
|
||||||
taskDialogRenderer: PropTypes.func,
|
|
||||||
nameCaption: PropTypes.string.isRequired,
|
|
||||||
okBtnCaption: PropTypes.string.isRequired,
|
|
||||||
cancelBtnCaption: PropTypes.string.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
//Циклограмма
|
|
||||||
const P8PCyclogram = ({
|
|
||||||
containerStyle,
|
|
||||||
lineHeight,
|
|
||||||
title,
|
|
||||||
titleStyle,
|
|
||||||
onTitleClick,
|
|
||||||
zoomBar,
|
|
||||||
zoom,
|
|
||||||
columns,
|
|
||||||
columnRenderer,
|
|
||||||
groups,
|
|
||||||
groupHeaderRenderer,
|
|
||||||
tasks,
|
|
||||||
taskRenderer,
|
|
||||||
taskAttributes,
|
|
||||||
taskAttributeRenderer,
|
|
||||||
taskDialogRenderer,
|
|
||||||
noDataFoundText,
|
|
||||||
nameTaskEditorCaption,
|
|
||||||
okTaskEditorBtnCaption,
|
|
||||||
cancelTaskEditorBtnCaption
|
|
||||||
}) => {
|
|
||||||
//Хук основного блока (для последующего определения доступной ширины)
|
|
||||||
const mainBlock = useRef(null);
|
|
||||||
//Хук для заголовка таблицы
|
|
||||||
const headerBlock = useRef(null);
|
|
||||||
//Собственное состояние
|
|
||||||
const [state, setState] = useState({
|
|
||||||
noData: true,
|
|
||||||
loaded: false,
|
|
||||||
lineHeight: NDEFAULT_LINE_HEIGHT,
|
|
||||||
maxWidth: 0,
|
|
||||||
maxHeight: 0,
|
|
||||||
shift: 0,
|
|
||||||
zoom: P8P_CYCLOGRAM_ZOOM.includes(zoom) ? zoom : 1,
|
|
||||||
tasks: [],
|
|
||||||
editTask: null
|
|
||||||
});
|
|
||||||
|
|
||||||
//Обновление масштаба циклограммы
|
|
||||||
const handleZoomChange = direction => {
|
|
||||||
//Считываем текущий индекс
|
|
||||||
const currentIndex = P8P_CYCLOGRAM_ZOOM.indexOf(state.zoom);
|
|
||||||
setState(pv => ({
|
|
||||||
...pv,
|
|
||||||
zoom:
|
|
||||||
currentIndex + direction !== P8P_CYCLOGRAM_ZOOM.length && currentIndex + direction !== -1
|
|
||||||
? P8P_CYCLOGRAM_ZOOM[currentIndex + direction]
|
|
||||||
: pv.zoom
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
//Открытие редактора задачи
|
|
||||||
const openTaskEditor = task => setState(pv => ({ ...pv, editTask: { ...task } }));
|
|
||||||
|
|
||||||
//При сохранении задачи в редакторе
|
|
||||||
const handleTaskEditorSave = () => {
|
|
||||||
setState(pv => ({ ...pv, editTask: null }));
|
|
||||||
};
|
|
||||||
|
|
||||||
//При закрытии редактора задачи без сохранения
|
|
||||||
const handleTaskEditorCancel = () => setState(pv => ({ ...pv, editTask: null }));
|
|
||||||
|
|
||||||
//При скролле блока
|
|
||||||
const handleScroll = e => {
|
|
||||||
//Изменяем позицию заголовка таблицы относительно скролла
|
|
||||||
headerBlock.current.setAttribute("transform", "translate(0," + e.currentTarget.scrollTop + ")");
|
|
||||||
};
|
|
||||||
|
|
||||||
//При изменении данных
|
|
||||||
useEffect(() => {
|
|
||||||
//Если есть колонки и задачи
|
|
||||||
if (Array.isArray(columns) && columns.length > 0 && Array.isArray(tasks) && tasks.length > 0) {
|
|
||||||
//Определяем текущую максимальную ширину колонок
|
|
||||||
let currentColumnsMaxWidth = Math.max(...columns.map(o => o.end));
|
|
||||||
//Определяем доступный сдвиг для ширины колонок (16 - паддинг по бокам)
|
|
||||||
let columnShift = getShift(columns, currentColumnsMaxWidth, mainBlock.current.offsetWidth - 16) * state.zoom;
|
|
||||||
//Устанавливаем значения исходя из колонок/задач
|
|
||||||
setState(pv => ({
|
|
||||||
...pv,
|
|
||||||
loaded: true,
|
|
||||||
lineHeight: lineHeight ? lineHeight : NDEFAULT_LINE_HEIGHT,
|
|
||||||
maxWidth: columnShift !== 0 ? currentColumnsMaxWidth * columnShift : currentColumnsMaxWidth,
|
|
||||||
maxHeight: NDEFAULT_HEADER_HEIGHT + (Math.max(...tasks.map(o => o.lineNumb)) + 1) * (lineHeight ? lineHeight : NDEFAULT_LINE_HEIGHT),
|
|
||||||
shift: columnShift,
|
|
||||||
tasks: tasks,
|
|
||||||
noData: false
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
//Устанавливаем значения исходя из колонок/задач
|
|
||||||
setState(pv => ({
|
|
||||||
...pv,
|
|
||||||
noData: true
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}, [columns, lineHeight, state.zoom, tasks]);
|
|
||||||
|
|
||||||
//Генерация содержимого
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div ref={mainBlock} style={{ ...(containerStyle ? containerStyle : {}) }}>
|
|
||||||
{state.noData ? <P8PAppInlineError text={noDataFoundText} /> : null}
|
|
||||||
{state.loaded ? (
|
|
||||||
<>
|
|
||||||
{title ? (
|
|
||||||
<Typography
|
|
||||||
p={1}
|
|
||||||
sx={{ ...STYLES.CYCLOGRAM_TITLE, ...(titleStyle ? titleStyle : {}) }}
|
|
||||||
align="center"
|
|
||||||
color="textSecondary"
|
|
||||||
variant="subtitle1"
|
|
||||||
>
|
|
||||||
{onTitleClick ? (
|
|
||||||
<Link component="button" variant="body2" underline="hover" onClick={() => onTitleClick()}>
|
|
||||||
{title}
|
|
||||||
</Link>
|
|
||||||
) : (
|
|
||||||
title
|
|
||||||
)}
|
|
||||||
</Typography>
|
|
||||||
) : null}
|
|
||||||
{zoomBar ? (
|
|
||||||
<Box p={1} sx={STYLES.CYCLOGRAM_ZOOM}>
|
|
||||||
<IconButton
|
|
||||||
onClick={() => handleZoomChange(1)}
|
|
||||||
disabled={state.zoom == P8P_CYCLOGRAM_ZOOM[P8P_CYCLOGRAM_ZOOM.length - 1]}
|
|
||||||
>
|
|
||||||
<Icon>zoom_in</Icon>
|
|
||||||
</IconButton>
|
|
||||||
<IconButton onClick={() => handleZoomChange(-1)} disabled={state.zoom == P8P_CYCLOGRAM_ZOOM[0]}>
|
|
||||||
<Icon>zoom_out</Icon>
|
|
||||||
</IconButton>
|
|
||||||
</Box>
|
|
||||||
) : null}
|
|
||||||
<Box className="scroll" sx={STYLES.CYCLOGRAM_BOX(state.noData, title, zoomBar)} onScroll={handleScroll}>
|
|
||||||
<svg id="cyclogram" width={state.maxWidth} height={state.maxHeight}>
|
|
||||||
<P8PCyclogramGrid
|
|
||||||
tasks={state.tasks}
|
|
||||||
columns={columns}
|
|
||||||
shift={state.shift}
|
|
||||||
maxWidth={state.maxWidth}
|
|
||||||
maxHeight={state.maxHeight}
|
|
||||||
lineHeight={state.lineHeight}
|
|
||||||
/>
|
|
||||||
<P8PCyclogramMain
|
|
||||||
columns={columns}
|
|
||||||
groups={groups}
|
|
||||||
tasks={state.tasks}
|
|
||||||
shift={state.shift}
|
|
||||||
lineHeight={state.lineHeight}
|
|
||||||
maxWidth={state.maxWidth}
|
|
||||||
maxHeight={state.maxHeight}
|
|
||||||
groupHeaderRenderer={groupHeaderRenderer}
|
|
||||||
openTaskEditor={openTaskEditor}
|
|
||||||
taskRenderer={taskRenderer}
|
|
||||||
columnRenderer={columnRenderer}
|
|
||||||
headerBlock={headerBlock}
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
) : null}
|
|
||||||
{state.editTask ? (
|
|
||||||
<P8PCyclogramTaskEditor
|
|
||||||
task={state.editTask}
|
|
||||||
taskAttributes={taskAttributes}
|
|
||||||
onOk={handleTaskEditorSave}
|
|
||||||
onCancel={handleTaskEditorCancel}
|
|
||||||
taskAttributeRenderer={taskAttributeRenderer}
|
|
||||||
taskDialogRenderer={taskDialogRenderer}
|
|
||||||
nameCaption={nameTaskEditorCaption}
|
|
||||||
okBtnCaption={okTaskEditorBtnCaption}
|
|
||||||
cancelBtnCaption={cancelTaskEditorBtnCaption}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств - Циклограмма
|
|
||||||
P8PCyclogram.propTypes = {
|
|
||||||
containerStyle: PropTypes.object,
|
|
||||||
lineHeight: PropTypes.number,
|
|
||||||
title: PropTypes.string,
|
|
||||||
titleStyle: PropTypes.object,
|
|
||||||
onTitleClick: PropTypes.func,
|
|
||||||
zoomBar: PropTypes.bool,
|
|
||||||
zoom: PropTypes.number,
|
|
||||||
columns: PropTypes.arrayOf(P8P_CYCLOGRAM_COLUMN_SHAPE).isRequired,
|
|
||||||
columnRenderer: PropTypes.func,
|
|
||||||
groups: PropTypes.arrayOf(P8P_CYCLOGRAM_GROUP_SHAPE),
|
|
||||||
groupHeaderRenderer: PropTypes.func,
|
|
||||||
tasks: PropTypes.arrayOf(P8P_CYCLOGRAM_TASK_SHAPE).isRequired,
|
|
||||||
taskRenderer: PropTypes.func,
|
|
||||||
taskAttributes: PropTypes.arrayOf(P8P_CYCLOGRAM_TASK_ATTRIBUTE_SHAPE),
|
|
||||||
taskAttributeRenderer: PropTypes.func,
|
|
||||||
taskDialogRenderer: PropTypes.func,
|
|
||||||
noDataFoundText: PropTypes.string.isRequired,
|
|
||||||
nameTaskEditorCaption: PropTypes.string.isRequired,
|
|
||||||
okTaskEditorBtnCaption: PropTypes.string.isRequired,
|
|
||||||
cancelTaskEditorBtnCaption: PropTypes.string.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export { P8PCyclogram };
|
|
||||||
@ -36,15 +36,15 @@ const P8P_DATA_GRID_FILTERS_HEIGHT = P8P_TABLE_FILTERS_HEIGHT;
|
|||||||
|
|
||||||
//Таблица данных
|
//Таблица данных
|
||||||
const P8PDataGrid = ({
|
const P8PDataGrid = ({
|
||||||
columnsDef = [],
|
columnsDef,
|
||||||
filtersInitial,
|
filtersInitial,
|
||||||
groups = [],
|
groups,
|
||||||
rows = [],
|
rows,
|
||||||
size,
|
size,
|
||||||
fixedHeader = false,
|
fixedHeader = false,
|
||||||
fixedColumns = 0,
|
fixedColumns = 0,
|
||||||
morePages = false,
|
morePages = false,
|
||||||
reloading = false,
|
reloading,
|
||||||
expandable,
|
expandable,
|
||||||
orderAscMenuItemCaption,
|
orderAscMenuItemCaption,
|
||||||
orderDescMenuItemCaption,
|
orderDescMenuItemCaption,
|
||||||
@ -154,15 +154,15 @@ const P8PDataGrid = ({
|
|||||||
|
|
||||||
//Контроль свойств - Таблица данных
|
//Контроль свойств - Таблица данных
|
||||||
P8PDataGrid.propTypes = {
|
P8PDataGrid.propTypes = {
|
||||||
columnsDef: PropTypes.array,
|
columnsDef: PropTypes.array.isRequired,
|
||||||
filtersInitial: PropTypes.arrayOf(P8P_DATA_GRID_FILTER_SHAPE),
|
filtersInitial: PropTypes.arrayOf(P8P_DATA_GRID_FILTER_SHAPE),
|
||||||
groups: PropTypes.array,
|
groups: PropTypes.array,
|
||||||
rows: PropTypes.array,
|
rows: PropTypes.array.isRequired,
|
||||||
size: PropTypes.string,
|
size: PropTypes.string,
|
||||||
fixedHeader: PropTypes.bool,
|
fixedHeader: PropTypes.bool,
|
||||||
fixedColumns: PropTypes.number,
|
fixedColumns: PropTypes.number,
|
||||||
morePages: PropTypes.bool,
|
morePages: PropTypes.bool,
|
||||||
reloading: PropTypes.bool,
|
reloading: PropTypes.bool.isRequired,
|
||||||
expandable: PropTypes.bool,
|
expandable: PropTypes.bool,
|
||||||
orderAscMenuItemCaption: PropTypes.string.isRequired,
|
orderAscMenuItemCaption: PropTypes.string.isRequired,
|
||||||
orderDescMenuItemCaption: PropTypes.string.isRequired,
|
orderDescMenuItemCaption: PropTypes.string.isRequired,
|
||||||
|
|||||||
@ -27,7 +27,7 @@ const STYLES = {
|
|||||||
//-----------
|
//-----------
|
||||||
|
|
||||||
//Полноэкранный диалог
|
//Полноэкранный диалог
|
||||||
const P8PFullScreenDialog = ({ title, onClose, contentProps, children }) => {
|
const P8PFullScreenDialog = ({ title, onClose, children }) => {
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
onClose ? onClose() : null;
|
onClose ? onClose() : null;
|
||||||
};
|
};
|
||||||
@ -46,7 +46,7 @@ const P8PFullScreenDialog = ({ title, onClose, contentProps, children }) => {
|
|||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogContent {...(contentProps ? contentProps : {})}>{children}</DialogContent>
|
<DialogContent>{children}</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -55,8 +55,7 @@ const P8PFullScreenDialog = ({ title, onClose, contentProps, children }) => {
|
|||||||
P8PFullScreenDialog.propTypes = {
|
P8PFullScreenDialog.propTypes = {
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.string.isRequired,
|
||||||
onClose: PropTypes.func,
|
onClose: PropTypes.func,
|
||||||
children: PropTypes.element,
|
children: PropTypes.element
|
||||||
contentProps: PropTypes.object
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//----------------
|
//----------------
|
||||||
|
|||||||
@ -33,7 +33,7 @@ import { P8PAppInlineError } from "./p8p_app_message"; //Встраиваемо
|
|||||||
//---------
|
//---------
|
||||||
|
|
||||||
//Уровни масштаба
|
//Уровни масштаба
|
||||||
const P8P_GANTT_ZOOM = [0, 1, 2, 3, 4, 5];
|
const P8P_GANTT_ZOOM = [0, 1, 2, 3, 4];
|
||||||
|
|
||||||
//Уровни масштаба (строковые наименования в терминах библиотеки)
|
//Уровни масштаба (строковые наименования в терминах библиотеки)
|
||||||
const P8P_GANTT_ZOOM_VIEW_MODES = {
|
const P8P_GANTT_ZOOM_VIEW_MODES = {
|
||||||
@ -41,8 +41,7 @@ const P8P_GANTT_ZOOM_VIEW_MODES = {
|
|||||||
1: "Half Day",
|
1: "Half Day",
|
||||||
2: "Day",
|
2: "Day",
|
||||||
3: "Week",
|
3: "Week",
|
||||||
4: "Month",
|
4: "Month"
|
||||||
5: "Year"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//Структура задачи
|
//Структура задачи
|
||||||
|
|||||||
@ -62,15 +62,8 @@ const STYLES = {
|
|||||||
GRID_PANEL_CARD_CONTENT_TITLE_ICON: { paddingTop: "4px" },
|
GRID_PANEL_CARD_CONTENT_TITLE_ICON: { paddingTop: "4px" },
|
||||||
GRID_PANEL_CARD_ACTIONS: { marginTop: "auto", display: "flex", justifyContent: "flex-end", alignItems: "flex-start" },
|
GRID_PANEL_CARD_ACTIONS: { marginTop: "auto", display: "flex", justifyContent: "flex-end", alignItems: "flex-start" },
|
||||||
DESKTOP_GROUP_HEADER: { fontWeight: "bold", fontFamily: "tahoma, arial, verdana, sans-serif!important", fontSize: "13px!important" },
|
DESKTOP_GROUP_HEADER: { fontWeight: "bold", fontFamily: "tahoma, arial, verdana, sans-serif!important", fontSize: "13px!important" },
|
||||||
DESKTOP_ITEM_BUTTON: {
|
DESKTOP_ITEM_BUTTON: { fontSize: "12px", textTransform: "none", "&:hover": { backgroundColor: "#c3e1ff" }, maxWidth: "150px" },
|
||||||
fontSize: "12px",
|
DESKTOP_ITEM_STACK: { justifyContent: "center", alignItems: "center", fontSize: "12px" },
|
||||||
textTransform: "none",
|
|
||||||
"&:hover": { backgroundColor: "#c3e1ff" },
|
|
||||||
width: "150px",
|
|
||||||
height: "90px",
|
|
||||||
flexDirection: "column",
|
|
||||||
justifyContent: "flex-start"
|
|
||||||
},
|
|
||||||
DESKTOP_ITEM_ICON: { width: "48px", height: "48px", fontSize: "48px" },
|
DESKTOP_ITEM_ICON: { width: "48px", height: "48px", fontSize: "48px" },
|
||||||
DESKTOP_ITEM_CATION: {
|
DESKTOP_ITEM_CATION: {
|
||||||
display: "-webkit-box",
|
display: "-webkit-box",
|
||||||
@ -135,14 +128,7 @@ const getPanelsLinks = ({ variant, panels, selectedPanel, group, defaultGroupTyt
|
|||||||
<Card sx={STYLES.GRID_PANEL_CARD}>
|
<Card sx={STYLES.GRID_PANEL_CARD}>
|
||||||
{panel.preview ? (
|
{panel.preview ? (
|
||||||
<CardMedia component="img" alt={panel.name} image={panel.preview} sx={STYLES.GRID_PANEL_CARD_MEDIA} />
|
<CardMedia component="img" alt={panel.name} image={panel.preview} sx={STYLES.GRID_PANEL_CARD_MEDIA} />
|
||||||
) : (
|
) : null}
|
||||||
<CardMedia
|
|
||||||
component="img"
|
|
||||||
alt={panel.name}
|
|
||||||
image={"./img/default_preview.png"}
|
|
||||||
sx={STYLES.GRID_PANEL_CARD_MEDIA}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Stack gap={1} direction="row" sx={STYLES.GRID_PANEL_CARD_CONTENT_TITLE}>
|
<Stack gap={1} direction="row" sx={STYLES.GRID_PANEL_CARD_CONTENT_TITLE}>
|
||||||
{panel.icon ? <Icon sx={STYLES.GRID_PANEL_CARD_CONTENT_TITLE_ICON}>{panel.icon}</Icon> : null}
|
{panel.icon ? <Icon sx={STYLES.GRID_PANEL_CARD_CONTENT_TITLE_ICON}>{panel.icon}</Icon> : null}
|
||||||
@ -179,10 +165,12 @@ const getPanelsLinks = ({ variant, panels, selectedPanel, group, defaultGroupTyt
|
|||||||
sx={STYLES.DESKTOP_ITEM_BUTTON}
|
sx={STYLES.DESKTOP_ITEM_BUTTON}
|
||||||
title={panel.caption}
|
title={panel.caption}
|
||||||
>
|
>
|
||||||
<Icon sx={STYLES.DESKTOP_ITEM_ICON}>{panel.icon}</Icon>
|
<Stack sx={STYLES.DESKTOP_ITEM_STACK}>
|
||||||
<Typography sx={STYLES.DESKTOP_ITEM_CATION} variant="body1">
|
<Icon sx={STYLES.DESKTOP_ITEM_ICON}>{panel.icon}</Icon>
|
||||||
{panel.caption}
|
<Typography sx={STYLES.DESKTOP_ITEM_CATION} variant="body1">
|
||||||
</Typography>
|
{panel.caption}
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -242,12 +230,7 @@ const P8PPanelsMenuDesktop = ({ group, onItemNavigate, panels = [], defaultGroup
|
|||||||
const panelsLinks = getPanelsLinks({ variant: P8P_PANELS_MENU_VARIANT.DESKTOP, panels, group, defaultGroupTytle, onItemNavigate });
|
const panelsLinks = getPanelsLinks({ variant: P8P_PANELS_MENU_VARIANT.DESKTOP, panels, group, defaultGroupTytle, onItemNavigate });
|
||||||
|
|
||||||
//Генерация содержимого
|
//Генерация содержимого
|
||||||
return (
|
return <Box p={2}>{panelsLinks}</Box>;
|
||||||
<Box p={2}>
|
|
||||||
{panelsLinks[0]}
|
|
||||||
<Stack direction="row">{panelsLinks.map((l, i) => (i > 0 ? l : null))}</Stack>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//Контроль свойств - Меню панелей - рабочий стол
|
//Контроль свойств - Меню панелей - рабочий стол
|
||||||
|
|||||||
@ -118,9 +118,7 @@ const STYLES = {
|
|||||||
},
|
},
|
||||||
TABLE_CELL_EXPAND_CONTAINER: {
|
TABLE_CELL_EXPAND_CONTAINER: {
|
||||||
paddingBottom: 0,
|
paddingBottom: 0,
|
||||||
paddingTop: 0,
|
paddingTop: 0
|
||||||
paddingLeft: 0,
|
|
||||||
paddingRight: 0
|
|
||||||
},
|
},
|
||||||
TABLE_CELL_GROUP_HEADER: {
|
TABLE_CELL_GROUP_HEADER: {
|
||||||
backgroundColor: "lightgray"
|
backgroundColor: "lightgray"
|
||||||
@ -488,16 +486,16 @@ P8PTableFiltersChips.propTypes = {
|
|||||||
|
|
||||||
//Таблица
|
//Таблица
|
||||||
const P8PTable = ({
|
const P8PTable = ({
|
||||||
columnsDef = [],
|
columnsDef,
|
||||||
groups = [],
|
groups,
|
||||||
rows = [],
|
rows,
|
||||||
orders,
|
orders,
|
||||||
filters,
|
filters,
|
||||||
size,
|
size,
|
||||||
fixedHeader = false,
|
fixedHeader = false,
|
||||||
fixedColumns = 0,
|
fixedColumns = 0,
|
||||||
morePages = false,
|
morePages = false,
|
||||||
reloading = false,
|
reloading,
|
||||||
expandable,
|
expandable,
|
||||||
orderAscMenuItemCaption,
|
orderAscMenuItemCaption,
|
||||||
orderDescMenuItemCaption,
|
orderDescMenuItemCaption,
|
||||||
@ -533,9 +531,7 @@ const P8PTable = ({
|
|||||||
const [expanded, setExpanded] = useState({});
|
const [expanded, setExpanded] = useState({});
|
||||||
|
|
||||||
//Собственное состояния - развёрнутые группы
|
//Собственное состояния - развёрнутые группы
|
||||||
const [expandedGroups, setExpandedGroups] = useState(
|
const [expandedGroups, setExpandedGroups] = useState({});
|
||||||
Array.isArray(groups) && groups.length > 0 ? Object.assign({}, ...groups.map(g => ({ [g.name]: g.expanded }))) : {}
|
|
||||||
);
|
|
||||||
|
|
||||||
//Собственное состояние - колонка с отображаемой подсказкой
|
//Собственное состояние - колонка с отображаемой подсказкой
|
||||||
const [displayHintColumn, setDisplayHintColumn] = useState(null);
|
const [displayHintColumn, setDisplayHintColumn] = useState(null);
|
||||||
@ -935,7 +931,7 @@ P8PTable.propTypes = {
|
|||||||
fixedHeader: PropTypes.bool,
|
fixedHeader: PropTypes.bool,
|
||||||
fixedColumns: PropTypes.number,
|
fixedColumns: PropTypes.number,
|
||||||
morePages: PropTypes.bool,
|
morePages: PropTypes.bool,
|
||||||
reloading: PropTypes.bool,
|
reloading: PropTypes.bool.isRequired,
|
||||||
expandable: PropTypes.bool,
|
expandable: PropTypes.bool,
|
||||||
orderAscMenuItemCaption: PropTypes.string.isRequired,
|
orderAscMenuItemCaption: PropTypes.string.isRequired,
|
||||||
orderDescMenuItemCaption: PropTypes.string.isRequired,
|
orderDescMenuItemCaption: PropTypes.string.isRequired,
|
||||||
|
|||||||
@ -15,7 +15,6 @@ import { P8PAppWorkspace } from "./components/p8p_app_workspace"; //Рабоче
|
|||||||
import { P8PTable, P8P_TABLE_DATA_TYPE, P8P_TABLE_SIZE, P8P_TABLE_FILTER_SHAPE } from "./components/p8p_table"; //Таблица данных
|
import { P8PTable, P8P_TABLE_DATA_TYPE, P8P_TABLE_SIZE, P8P_TABLE_FILTER_SHAPE } from "./components/p8p_table"; //Таблица данных
|
||||||
import { P8PDataGrid, P8P_DATA_GRID_DATA_TYPE, P8P_DATA_GRID_SIZE, P8P_DATA_GRID_FILTER_SHAPE } from "./components/p8p_data_grid"; //Таблица данных
|
import { P8PDataGrid, P8P_DATA_GRID_DATA_TYPE, P8P_DATA_GRID_SIZE, P8P_DATA_GRID_FILTER_SHAPE } from "./components/p8p_data_grid"; //Таблица данных
|
||||||
import { P8PGantt, P8P_GANTT_TASK_SHAPE, P8P_GANTT_TASK_ATTRIBUTE_SHAPE, P8P_GANTT_TASK_COLOR_SHAPE } from "./components/p8p_gantt"; //Диаграмма Ганта
|
import { P8PGantt, P8P_GANTT_TASK_SHAPE, P8P_GANTT_TASK_ATTRIBUTE_SHAPE, P8P_GANTT_TASK_COLOR_SHAPE } from "./components/p8p_gantt"; //Диаграмма Ганта
|
||||||
import { P8PCyclogram } from "./components/p8p_cyclogram"; //Циклограмма
|
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
//Константы
|
//Константы
|
||||||
@ -77,14 +76,6 @@ const P8P_GANTT_CONFIG_PROPS = {
|
|||||||
cancelTaskEditorBtnCaption: BUTTONS.CANCEL
|
cancelTaskEditorBtnCaption: BUTTONS.CANCEL
|
||||||
};
|
};
|
||||||
|
|
||||||
//Конфигурируемые свойства "Циклограммы" (P8PCyclogram)
|
|
||||||
const P8P_CYCLOGRAM_CONFIG_PROPS = {
|
|
||||||
noDataFoundText: TEXTS.NO_DATA_FOUND,
|
|
||||||
nameTaskEditorCaption: CAPTIONS.NAME,
|
|
||||||
okTaskEditorBtnCaption: BUTTONS.OK,
|
|
||||||
cancelTaskEditorBtnCaption: BUTTONS.CANCEL
|
|
||||||
};
|
|
||||||
|
|
||||||
//-----------------------
|
//-----------------------
|
||||||
//Вспомогательные функции
|
//Вспомогательные функции
|
||||||
//-----------------------
|
//-----------------------
|
||||||
@ -99,7 +90,6 @@ const addConfigChildProps = children =>
|
|||||||
if (child.type.name === "P8PTable") configProps = P8P_TABLE_CONFIG_PROPS;
|
if (child.type.name === "P8PTable") configProps = P8P_TABLE_CONFIG_PROPS;
|
||||||
if (child.type.name === "P8PDataGrid") configProps = P8P_DATA_GRID_CONFIG_PROPS;
|
if (child.type.name === "P8PDataGrid") configProps = P8P_DATA_GRID_CONFIG_PROPS;
|
||||||
if (child.type.name === "P8PGantt") configProps = P8P_GANTT_CONFIG_PROPS;
|
if (child.type.name === "P8PGantt") configProps = P8P_GANTT_CONFIG_PROPS;
|
||||||
if (child.type.name === "P8PCyclogram") configProps = P8P_CYCLOGRAM_CONFIG_PROPS;
|
|
||||||
return React.createElement(child.type, { ...configProps, ...restProps }, addConfigChildProps(children));
|
return React.createElement(child.type, { ...configProps, ...restProps }, addConfigChildProps(children));
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -122,9 +112,6 @@ const P8PDataGridConfigWrapped = (props = {}) => <P8PDataGrid {...P8P_DATA_GRID_
|
|||||||
//Обёртка для компонента "Диаграмма Ганта" (P8PGantt)
|
//Обёртка для компонента "Диаграмма Ганта" (P8PGantt)
|
||||||
const P8PGanttConfigWrapped = (props = {}) => <P8PGantt {...P8P_GANTT_CONFIG_PROPS} {...props} />;
|
const P8PGanttConfigWrapped = (props = {}) => <P8PGantt {...P8P_GANTT_CONFIG_PROPS} {...props} />;
|
||||||
|
|
||||||
//Обёртка для компонента "Циклограмма" (P8PCyclogram)
|
|
||||||
const P8PCyclogramConfigWrapped = (props = {}) => <P8PCyclogram {...P8P_GANTT_CONFIG_PROPS} {...props} />;
|
|
||||||
|
|
||||||
//Универсальный элемент-обёртка в параметры конфигурации
|
//Универсальный элемент-обёртка в параметры конфигурации
|
||||||
const ConfigWrapper = ({ children }) => addConfigChildProps(children);
|
const ConfigWrapper = ({ children }) => addConfigChildProps(children);
|
||||||
|
|
||||||
@ -145,7 +132,6 @@ export {
|
|||||||
P8P_DATA_GRID_SIZE,
|
P8P_DATA_GRID_SIZE,
|
||||||
P8P_DATA_GRID_FILTER_SHAPE,
|
P8P_DATA_GRID_FILTER_SHAPE,
|
||||||
P8P_GANTT_CONFIG_PROPS,
|
P8P_GANTT_CONFIG_PROPS,
|
||||||
P8P_CYCLOGRAM_CONFIG_PROPS,
|
|
||||||
P8P_GANTT_TASK_SHAPE,
|
P8P_GANTT_TASK_SHAPE,
|
||||||
P8P_GANTT_TASK_ATTRIBUTE_SHAPE,
|
P8P_GANTT_TASK_ATTRIBUTE_SHAPE,
|
||||||
P8P_GANTT_TASK_COLOR_SHAPE,
|
P8P_GANTT_TASK_COLOR_SHAPE,
|
||||||
@ -154,6 +140,5 @@ export {
|
|||||||
P8PTableConfigWrapped,
|
P8PTableConfigWrapped,
|
||||||
P8PDataGridConfigWrapped,
|
P8PDataGridConfigWrapped,
|
||||||
P8PGanttConfigWrapped,
|
P8PGanttConfigWrapped,
|
||||||
P8PCyclogramConfigWrapped,
|
|
||||||
ConfigWrapper
|
ConfigWrapper
|
||||||
};
|
};
|
||||||
|
|||||||
@ -22,8 +22,7 @@ const P8O_API = window.top?.parus?.clientApi;
|
|||||||
|
|
||||||
//Структура объекта с описанием ошибок
|
//Структура объекта с описанием ошибок
|
||||||
const APPLICATION_CONTEXT_ERRORS_SHAPE = PropTypes.shape({
|
const APPLICATION_CONTEXT_ERRORS_SHAPE = PropTypes.shape({
|
||||||
P8O_API_UNAVAILABLE: PropTypes.string.isRequired,
|
P8O_API_UNAVAILABLE: PropTypes.string.isRequired
|
||||||
P8O_API_UNSUPPORTED: PropTypes.string.isRequired
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//----------------
|
//----------------
|
||||||
@ -73,38 +72,21 @@ export const ApplicationContext = ({ errors, displaySizeGetter, guidGenerator, c
|
|||||||
|
|
||||||
//Отображение раздела "ПАРУС 8 Онлайн"
|
//Отображение раздела "ПАРУС 8 Онлайн"
|
||||||
const pOnlineShowUnit = useCallback(
|
const pOnlineShowUnit = useCallback(
|
||||||
({ unitCode, showMethod = "main", inputParameters, modal = true }) => {
|
({ unitCode, showMethod = "main", inputParameters }) => {
|
||||||
if (P8O_API)
|
if (P8O_API) P8O_API.fn.openDocumentModal({ unitcode: unitCode, method: showMethod, inputParameters });
|
||||||
modal
|
|
||||||
? P8O_API.fn.openDocumentModal({ unitcode: unitCode, method: showMethod, inputParameters })
|
|
||||||
: P8O_API.fn.openDocument
|
|
||||||
? P8O_API.fn.openDocument({ unitcode: unitCode, method: showMethod, inputParameters })
|
|
||||||
: showMsgErr(errors.P8O_API_UNSUPPORTED);
|
|
||||||
else showMsgErr(errors.P8O_API_UNAVAILABLE);
|
else showMsgErr(errors.P8O_API_UNAVAILABLE);
|
||||||
},
|
},
|
||||||
[showMsgErr, errors.P8O_API_UNAVAILABLE, errors.P8O_API_UNSUPPORTED]
|
[showMsgErr, errors.P8O_API_UNAVAILABLE]
|
||||||
);
|
);
|
||||||
|
|
||||||
//Отображение документа "ПАРУС 8 Онлайн"
|
//Отображение документа "ПАРУС 8 Онлайн"
|
||||||
const pOnlineShowDocument = useCallback(
|
const pOnlineShowDocument = useCallback(
|
||||||
({ unitCode, document, showMethod = "main", inRnParameter = "in_RN", modal = true }) => {
|
({ unitCode, document, showMethod = "main", inRnParameter = "in_RN" }) => {
|
||||||
if (P8O_API)
|
if (P8O_API)
|
||||||
modal
|
P8O_API.fn.openDocumentModal({ unitcode: unitCode, method: showMethod, inputParameters: [{ name: inRnParameter, value: document }] });
|
||||||
? P8O_API.fn.openDocumentModal({
|
|
||||||
unitcode: unitCode,
|
|
||||||
method: showMethod,
|
|
||||||
inputParameters: [{ name: inRnParameter, value: document }]
|
|
||||||
})
|
|
||||||
: P8O_API.fn.openDocument
|
|
||||||
? P8O_API.fn.openDocument({
|
|
||||||
unitcode: unitCode,
|
|
||||||
method: showMethod,
|
|
||||||
inputParameters: [{ name: inRnParameter, value: document }]
|
|
||||||
})
|
|
||||||
: showMsgErr(errors.P8O_API_UNSUPPORTED);
|
|
||||||
else showMsgErr(errors.P8O_API_UNAVAILABLE);
|
else showMsgErr(errors.P8O_API_UNAVAILABLE);
|
||||||
},
|
},
|
||||||
[showMsgErr, errors.P8O_API_UNAVAILABLE, errors.P8O_API_UNSUPPORTED]
|
[showMsgErr, errors.P8O_API_UNAVAILABLE]
|
||||||
);
|
);
|
||||||
|
|
||||||
//Отображение словаря "ПАРУС 8 Онлайн"
|
//Отображение словаря "ПАРУС 8 Онлайн"
|
||||||
|
|||||||
@ -33,42 +33,34 @@ const DISPLAY_SIZE = {
|
|||||||
//Типовые пути конвертации в массив (при переводе XML -> JSON)
|
//Типовые пути конвертации в массив (при переводе XML -> JSON)
|
||||||
const XML_ALWAYS_ARRAY_PATHS = [
|
const XML_ALWAYS_ARRAY_PATHS = [
|
||||||
"XRESPOND.XPAYLOAD.XOUT_ARGUMENTS",
|
"XRESPOND.XPAYLOAD.XOUT_ARGUMENTS",
|
||||||
"XRESPOND.XPAYLOAD.XDATA_GRID.rows",
|
"XRESPOND.XPAYLOAD.XROWS",
|
||||||
"XRESPOND.XPAYLOAD.XDATA_GRID.columnsDef",
|
"XRESPOND.XPAYLOAD.XCOLUMNS_DEF",
|
||||||
"XRESPOND.XPAYLOAD.XDATA_GRID.columnsDef.values",
|
"XRESPOND.XPAYLOAD.XCOLUMNS_DEF.values",
|
||||||
"XRESPOND.XPAYLOAD.XDATA_GRID.groups",
|
"XRESPOND.XPAYLOAD.XGROUPS",
|
||||||
"XRESPOND.XPAYLOAD.XGANTT.taskAttributes",
|
"XRESPOND.XPAYLOAD.XGANTT_DEF.taskAttributes",
|
||||||
"XRESPOND.XPAYLOAD.XGANTT.taskColors",
|
"XRESPOND.XPAYLOAD.XGANTT_DEF.taskColors",
|
||||||
"XRESPOND.XPAYLOAD.XGANTT.tasks",
|
"XRESPOND.XPAYLOAD.XGANTT_TASKS",
|
||||||
"XRESPOND.XPAYLOAD.XGANTT.tasks.dependencies",
|
"XRESPOND.XPAYLOAD.XGANTT_TASKS.dependencies",
|
||||||
"XRESPOND.XPAYLOAD.XCHART.labels",
|
"XRESPOND.XPAYLOAD.XCHART.labels",
|
||||||
"XRESPOND.XPAYLOAD.XCHART.datasets",
|
"XRESPOND.XPAYLOAD.XCHART.datasets",
|
||||||
"XRESPOND.XPAYLOAD.XCHART.datasets.data",
|
"XRESPOND.XPAYLOAD.XCHART.datasets.data",
|
||||||
"XRESPOND.XPAYLOAD.XCHART.datasets.items",
|
"XRESPOND.XPAYLOAD.XCHART.datasets.items"
|
||||||
"XRESPOND.XPAYLOAD.XCYCLOGRAM.taskAttributes",
|
|
||||||
"XRESPOND.XPAYLOAD.XCYCLOGRAM.columns",
|
|
||||||
"XRESPOND.XPAYLOAD.XCYCLOGRAM.groups",
|
|
||||||
"XRESPOND.XPAYLOAD.XCYCLOGRAM.tasks"
|
|
||||||
];
|
];
|
||||||
|
|
||||||
//Типовые шаблоны конвертации в массив (при переводе XML -> JSON)
|
//Типовые шаблоны конвертации в массив (при переводе XML -> JSON)
|
||||||
const XML_ALWAYS_ARRAY_PATH_PATTERNS = [
|
const XML_ALWAYS_ARRAY_PATH_PATTERNS = [
|
||||||
/(.*)XDATA_GRID.rows$/,
|
/(.*)XROWS$/,
|
||||||
/(.*)XDATA_GRID.columnsDef$/,
|
/(.*)XCOLUMNS_DEF$/,
|
||||||
/(.*)XDATA_GRID.columnsDef.values$/,
|
/(.*)XCOLUMNS_DEF.values$/,
|
||||||
/(.*)XDATA_GRID.groups$/,
|
/(.*)XGROUPS$/,
|
||||||
/(.*)XGANTT.taskAttributes$/,
|
/(.*)XGANTT_DEF.taskAttributes$/,
|
||||||
/(.*)XGANTT.taskColors$/,
|
/(.*)XGANTT_DEF.taskColors$/,
|
||||||
/(.*)XGANTT.tasks$/,
|
/(.*)XGANTT_TASKS$/,
|
||||||
/(.*)XGANTT.tasks.dependencies$/,
|
/(.*)XGANTT_TASKS.dependencies$/,
|
||||||
/(.*)XCHART.labels$/,
|
/(.*)XCHART.labels$/,
|
||||||
/(.*)XCHART.datasets$/,
|
/(.*)XCHART.datasets$/,
|
||||||
/(.*)XCHART.datasets.data$/,
|
/(.*)XCHART.datasets.data$/,
|
||||||
/(.*)XCHART.datasets.items$/,
|
/(.*)XCHART.datasets.items$/
|
||||||
/(.*)XCYCLOGRAM.taskAttributes$/,
|
|
||||||
/(.*)XCYCLOGRAM.columns$/,
|
|
||||||
/(.*)XCYCLOGRAM.groups$/,
|
|
||||||
/(.*)XCYCLOGRAM.tasks$/
|
|
||||||
];
|
];
|
||||||
|
|
||||||
//Типовой постфикс тега для массива (при переводе XML -> JSON)
|
//Типовой постфикс тега для массива (при переводе XML -> JSON)
|
||||||
@ -76,13 +68,11 @@ const XML_ALWAYS_ARRAY_POSTFIX = "__SYSTEM__ARRAY__";
|
|||||||
|
|
||||||
//Типовые шаблоны конвертации значения атрибута в строку (при переводе XML -> JSON)
|
//Типовые шаблоны конвертации значения атрибута в строку (при переводе XML -> JSON)
|
||||||
const XML_ATTR_ALWAYS_STR_PATH_PATTERNS = [
|
const XML_ATTR_ALWAYS_STR_PATH_PATTERNS = [
|
||||||
/(.*)XDATA_GRID.columnsDef.name$/,
|
/(.*)XCOLUMNS_DEF.name$/,
|
||||||
/(.*)XDATA_GRID.columnsDef.caption$/,
|
/(.*)XCOLUMNS_DEF.caption$/,
|
||||||
/(.*)XDATA_GRID.columnsDef.parent$/,
|
/(.*)XCOLUMNS_DEF.parent$/,
|
||||||
/(.*)XDATA_GRID.groups.name$/,
|
/(.*)XGROUPS.name$/,
|
||||||
/(.*)XDATA_GRID.groups.caption$/,
|
/(.*)XGROUPS.caption$/
|
||||||
/(.*)XCYCLOGRAM.columns.name$/,
|
|
||||||
/(.*)XCYCLOGRAM.groups.name$/
|
|
||||||
];
|
];
|
||||||
|
|
||||||
//-----------
|
//-----------
|
||||||
@ -107,6 +97,7 @@ const deepCopyObject = obj => JSON.parse(JSON.stringify(obj));
|
|||||||
//Конвертация объекта в Base64 XML
|
//Конвертация объекта в Base64 XML
|
||||||
const object2Base64XML = (obj, builderOptions) => {
|
const object2Base64XML = (obj, builderOptions) => {
|
||||||
const builder = new XMLBuilder(builderOptions);
|
const builder = new XMLBuilder(builderOptions);
|
||||||
|
//onOrderChanged({ orders: btoa(ordersBuilder.build(newOrders)) });
|
||||||
return btoa(unescape(encodeURIComponent(builder.build(obj))));
|
return btoa(unescape(encodeURIComponent(builder.build(obj))));
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -149,9 +140,6 @@ const xml2JSON = ({ xmlDoc, isArray, transformTagName, tagValueProcessor, attrib
|
|||||||
//Форматирование даты в формат РФ
|
//Форматирование даты в формат РФ
|
||||||
const formatDateRF = value => (value ? dayjs(value).format("DD.MM.YYYY") : null);
|
const formatDateRF = value => (value ? dayjs(value).format("DD.MM.YYYY") : null);
|
||||||
|
|
||||||
//Форматирование даты и времени в формат РФ
|
|
||||||
const formatDateTimeRF = value => (value ? dayjs(value).format("DD.MM.YYYY HH:mm:ss") : null);
|
|
||||||
|
|
||||||
//Форматирование даты в формат JSON (только дата, без времени)
|
//Форматирование даты в формат JSON (только дата, без времени)
|
||||||
const formatDateJSONDateOnly = value => (value ? dayjs(value).format("YYYY-MM-DD") : null);
|
const formatDateJSONDateOnly = value => (value ? dayjs(value).format("YYYY-MM-DD") : null);
|
||||||
|
|
||||||
@ -175,7 +163,6 @@ export {
|
|||||||
object2Base64XML,
|
object2Base64XML,
|
||||||
xml2JSON,
|
xml2JSON,
|
||||||
formatDateRF,
|
formatDateRF,
|
||||||
formatDateTimeRF,
|
|
||||||
formatDateJSONDateOnly,
|
formatDateJSONDateOnly,
|
||||||
formatNumberRFCurrency,
|
formatNumberRFCurrency,
|
||||||
genGUID
|
genGUID
|
||||||
|
|||||||
271
app/panels/clnt_task_board/clnt_task_board.js
Normal file
271
app/panels/clnt_task_board/clnt_task_board.js
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - УДП - Доски задач
|
||||||
|
Панель мониторинга: Корневая панель доски задач
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React, { useEffect, useState, useCallback } from "react"; //Классы React
|
||||||
|
import { DragDropContext, Droppable } from "react-beautiful-dnd"; //Работа с drag&drop
|
||||||
|
import { Stack, Box, IconButton, Icon } from "@mui/material"; //Интерфейсные компоненты
|
||||||
|
import { StatusCard } from "./components/status_card.js";
|
||||||
|
import { TaskDialog } from "./task_dialog.js"; //Компонент формы события
|
||||||
|
import { Filter } from "./filter.js"; //Компонент фильтров
|
||||||
|
import { useExtraData, useTasks, useSettings } from "./hooks/hooks.js"; //Вспомогательные хуки
|
||||||
|
import { useFilters } from "./hooks/filter_hooks.js";
|
||||||
|
import { APP_STYLES } from "../../../app.styles"; //Типовые стили
|
||||||
|
import { NoteDialog } from "./components/note_dialog.js"; //Диалог примечания
|
||||||
|
import { SettingsDialog } from "./components/settings_dialog.js"; //Диалог дополнительных настроек
|
||||||
|
import { deepCopyObject } from "../../core/utils.js"; //Вспомогательные функции
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Высота заголовка
|
||||||
|
const TITLE_HEIGHT = "64px";
|
||||||
|
|
||||||
|
//Нижний отступ заголовка
|
||||||
|
const TITLE_PADDING_BOTTOM = "16px";
|
||||||
|
|
||||||
|
//Высота фильтра
|
||||||
|
const FILTER_HEIGHT = "56px";
|
||||||
|
|
||||||
|
//Стили
|
||||||
|
const STYLES = {
|
||||||
|
CONTAINER: { width: "100%", padding: 0 },
|
||||||
|
FS_BOX: { display: "flex", alignItems: "center" },
|
||||||
|
SETTINGS_MARGIN: { marginLeft: "auto" },
|
||||||
|
STATUSES_STACK: { maxWidth: "99vw", paddingBottom: "5px", overflowX: "auto", ...APP_STYLES.SCROLL },
|
||||||
|
STATUS_BLOCK: statusColor => {
|
||||||
|
return {
|
||||||
|
width: "350px",
|
||||||
|
height: `calc(100vh - ${TITLE_HEIGHT} - ${TITLE_PADDING_BOTTOM} - ${FILTER_HEIGHT} - 8px)`,
|
||||||
|
backgroundColor: statusColor,
|
||||||
|
padding: "8px"
|
||||||
|
};
|
||||||
|
},
|
||||||
|
BLOCK_OPACITY: isAvailable => {
|
||||||
|
return isAvailable ? { opacity: 1 } : { opacity: 0.5 };
|
||||||
|
},
|
||||||
|
STATUSES_DIV: { position: "fixed", left: "8px", top: `calc(${TITLE_HEIGHT} + ${FILTER_HEIGHT})` },
|
||||||
|
CARD_CONTENT: {
|
||||||
|
padding: 0,
|
||||||
|
paddingRight: "5px",
|
||||||
|
paddingBottom: "5px !important",
|
||||||
|
overflowY: "auto",
|
||||||
|
maxHeight: `calc(100vh - ${TITLE_HEIGHT} - ${TITLE_PADDING_BOTTOM} - ${FILTER_HEIGHT} - 85px)`,
|
||||||
|
...APP_STYLES.SCROLL
|
||||||
|
},
|
||||||
|
MARK_INFO: {
|
||||||
|
textAlign: "left",
|
||||||
|
textOverflow: "ellipsis",
|
||||||
|
overflow: "hidden",
|
||||||
|
display: "-webkit-box",
|
||||||
|
hyphens: "auto",
|
||||||
|
WebkitBoxOrient: "vertical",
|
||||||
|
WebkitLineClamp: 1,
|
||||||
|
maxWidth: "calc(300px)",
|
||||||
|
width: "-webkit-fill-available",
|
||||||
|
fontSize: "1.2rem",
|
||||||
|
cursor: "default"
|
||||||
|
},
|
||||||
|
PADDING_0: { padding: 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Корневая панель доски задач
|
||||||
|
const ClntTaskBoard = () => {
|
||||||
|
//Состояние вспомогательных диалогов
|
||||||
|
const [dialogsState, setDialogsState] = useState({
|
||||||
|
filterOpen: false,
|
||||||
|
settingsOpen: false,
|
||||||
|
note: { isOpen: false, callback: null },
|
||||||
|
taskDialogOpen: false
|
||||||
|
});
|
||||||
|
|
||||||
|
//Открыть-закрыть диалог фильтра
|
||||||
|
const handleFilterOpen = isOpen => {
|
||||||
|
setDialogsState(pv => ({ ...pv, filterOpen: isOpen }));
|
||||||
|
};
|
||||||
|
|
||||||
|
//Открыть-закрыть диалог дополнительных настроек
|
||||||
|
const handleSettingsOpen = () => setDialogsState(pv => ({ ...pv, settingsOpen: !dialogsState.settingsOpen }));
|
||||||
|
|
||||||
|
//Открыть-закрыть диалог примечания
|
||||||
|
const handleNoteOpen = (f = null) => setDialogsState(pv => ({ ...pv, note: { isOpen: !dialogsState.noteOpen, callback: f ? v => f(v) : null } }));
|
||||||
|
|
||||||
|
//Открыть-закрыть диалог события
|
||||||
|
const handleTaskDialogOpen = () => setDialogsState(pv => ({ ...pv, taskDialogOpen: !dialogsState.taskDialogOpen }));
|
||||||
|
|
||||||
|
//Состояние фильтров
|
||||||
|
const [filters, handleFiltersChange] = useFilters(handleFilterOpen);
|
||||||
|
|
||||||
|
//Состояние сортировок
|
||||||
|
const [orders, setOrders] = useState([]);
|
||||||
|
|
||||||
|
//Состояние дополнительных данных
|
||||||
|
const [extraData, getDocLinks, needUpdateExtraData] = useExtraData(filters.values.type);
|
||||||
|
|
||||||
|
//Состояние событий
|
||||||
|
const [tasks, handleReload, onDragEnd, needUpdateTasks] = useTasks({ filters, orders, extraData, getDocLinks });
|
||||||
|
|
||||||
|
//Состояние дополнительных настроек
|
||||||
|
const [settings, handleSettingsChange] = useSettings(tasks.statuses);
|
||||||
|
|
||||||
|
//Состояние доступных маршрутов события
|
||||||
|
const [availableRoutes, setAvailableRoutes] = useState({ sorce: "", routes: [] });
|
||||||
|
|
||||||
|
//При изменении сортировки
|
||||||
|
const handleOrderChanged = useCallback(
|
||||||
|
columnName => {
|
||||||
|
let newOrders = deepCopyObject(orders);
|
||||||
|
const colOrder = newOrders.find(o => o.name == columnName);
|
||||||
|
const newDirection = colOrder?.direction == "ASC" ? "DESC" : colOrder?.direction == "DESC" ? null : "ASC";
|
||||||
|
if (newDirection == null && colOrder) newOrders.splice(newOrders.indexOf(colOrder), 1);
|
||||||
|
if (newDirection != null && !colOrder) newOrders.push({ name: columnName, direction: newDirection });
|
||||||
|
if (newDirection != null && colOrder) colOrder.direction = newDirection;
|
||||||
|
setOrders(newOrders);
|
||||||
|
},
|
||||||
|
[orders]
|
||||||
|
);
|
||||||
|
|
||||||
|
//Очистка состояния доступных маршрутов события
|
||||||
|
const clearAvailableRoutesState = () => {
|
||||||
|
setAvailableRoutes({ sorce: "", routes: [] });
|
||||||
|
};
|
||||||
|
|
||||||
|
//Состояние перетаскиваемого события
|
||||||
|
const [dragItem, setDragItem] = useState({ type: "", status: "" });
|
||||||
|
|
||||||
|
//Захватить перетаскиваемый объект
|
||||||
|
const handleDragItemChange = (filtersType, statusCode) =>
|
||||||
|
setDragItem({
|
||||||
|
type: filtersType,
|
||||||
|
status: statusCode
|
||||||
|
});
|
||||||
|
|
||||||
|
//Отпустить перетаскиваемый объект
|
||||||
|
const handleDragItemClear = () => {
|
||||||
|
setDragItem({ type: "", status: "" });
|
||||||
|
};
|
||||||
|
|
||||||
|
//Проверка доступности карточки события
|
||||||
|
const isCardAvailable = code => {
|
||||||
|
return availableRoutes.sorce === code || availableRoutes.routes.find(r => r.dest === code) || !availableRoutes.sorce ? true : false;
|
||||||
|
};
|
||||||
|
|
||||||
|
//При смене типа события
|
||||||
|
useEffect(() => {
|
||||||
|
if (filters.values.type) {
|
||||||
|
//Обновление вспомогательных данных
|
||||||
|
filters.values.type !== extraData.typeLoaded ? needUpdateExtraData() : null;
|
||||||
|
//Обновление событий
|
||||||
|
needUpdateTasks();
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [filters.values.type]);
|
||||||
|
|
||||||
|
//Генерация содержимого
|
||||||
|
return (
|
||||||
|
<Box sx={STYLES.CONTAINER}>
|
||||||
|
{dialogsState.settingsOpen ? (
|
||||||
|
<SettingsDialog initial={settings} onSettingsChange={handleSettingsChange} onOpen={handleSettingsOpen} />
|
||||||
|
) : null}
|
||||||
|
{dialogsState.taskDialogOpen ? (
|
||||||
|
<TaskDialog
|
||||||
|
taskType={dragItem.type}
|
||||||
|
taskStatus={dragItem.status}
|
||||||
|
onReload={handleReload}
|
||||||
|
onClose={() => {
|
||||||
|
handleTaskDialogOpen();
|
||||||
|
handleDragItemClear();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
<Box sx={STYLES.FS_BOX}>
|
||||||
|
<Stack direction="row">
|
||||||
|
<Filter
|
||||||
|
isFilterDialogOpen={dialogsState.filterOpen}
|
||||||
|
filter={filters.values}
|
||||||
|
docs={extraData.docLinks}
|
||||||
|
selectedDoc={filters.values.docLink ? extraData.docLinks.find(d => d.id === filters.values.docLink) : null}
|
||||||
|
onFilterChange={handleFiltersChange}
|
||||||
|
getDocLinks={getDocLinks}
|
||||||
|
onFilterOpen={() => handleFilterOpen(true)}
|
||||||
|
onFilterClose={() => handleFilterOpen(false)}
|
||||||
|
onReload={handleReload}
|
||||||
|
orders={orders}
|
||||||
|
onOrderChanged={handleOrderChanged}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
<IconButton title="Настройки" onClick={handleSettingsOpen} sx={STYLES.SETTINGS_MARGIN}>
|
||||||
|
<Icon>settings</Icon>
|
||||||
|
</IconButton>
|
||||||
|
</Box>
|
||||||
|
{dialogsState.note.isOpen ? (
|
||||||
|
<NoteDialog noteTypes={extraData.noteTypes} onCallback={n => dialogsState.note.callback(n)} onNoteOpen={handleNoteOpen} />
|
||||||
|
) : null}
|
||||||
|
{filters.loaded && filters.values.type && extraData.dataLoaded && tasks.groupsLoaded && tasks.tasksLoaded ? (
|
||||||
|
<DragDropContext
|
||||||
|
onDragStart={e => {
|
||||||
|
let srcCode = tasks.statuses.find(s => s.id == e.source.droppableId).code;
|
||||||
|
setAvailableRoutes({ sorce: srcCode, routes: [...extraData.evRoutes.filter(r => r.src === srcCode)] });
|
||||||
|
}}
|
||||||
|
onDragEnd={e => {
|
||||||
|
onDragEnd(e, extraData.evPoints, handleNoteOpen);
|
||||||
|
clearAvailableRoutesState();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={STYLES.STATUSES_DIV}>
|
||||||
|
<Droppable droppableId="Statuses" type="droppableTask">
|
||||||
|
{provided => (
|
||||||
|
<div ref={provided.innerRef}>
|
||||||
|
<Stack direction="row" spacing={2} sx={STYLES.STATUSES_STACK}>
|
||||||
|
{settings.statusesSort.sorted
|
||||||
|
? settings.statusesSort.statuses.map((status, index) => (
|
||||||
|
<div key={index}>
|
||||||
|
<Droppable isDropDisabled={!isCardAvailable(status.code)} droppableId={status.id.toString()}>
|
||||||
|
{provided => (
|
||||||
|
<div ref={provided.innerRef}>
|
||||||
|
<StatusCard
|
||||||
|
tasks={tasks}
|
||||||
|
status={status}
|
||||||
|
settings={settings}
|
||||||
|
extraData={extraData}
|
||||||
|
filtersType={filters.values.type}
|
||||||
|
isCardAvailable={isCardAvailable}
|
||||||
|
onReload={handleReload}
|
||||||
|
onDragItemChange={handleDragItemChange}
|
||||||
|
onTaskDialogOpen={handleTaskDialogOpen}
|
||||||
|
onNoteDialogOpen={handleNoteOpen}
|
||||||
|
placeholder={provided.placeholder}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Droppable>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
: null}
|
||||||
|
</Stack>
|
||||||
|
{provided.placeholder}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Droppable>
|
||||||
|
</div>
|
||||||
|
</DragDropContext>
|
||||||
|
) : null}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { ClntTaskBoard };
|
||||||
341
app/panels/clnt_task_board/components/filter_dialog.js
Normal file
341
app/panels/clnt_task_board/components/filter_dialog.js
Normal file
@ -0,0 +1,341 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - УДП - Доски задач
|
||||||
|
Компонент: Диалоговое окно фильтра отбора
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React, { useState, useContext, useEffect, useCallback } from "react"; //Классы React
|
||||||
|
import { BackEndСtx } from "../../../context/backend"; //Контекст взаимодействия с сервером
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogTitle,
|
||||||
|
IconButton,
|
||||||
|
Icon,
|
||||||
|
DialogContent,
|
||||||
|
DialogActions,
|
||||||
|
Button,
|
||||||
|
Box,
|
||||||
|
Stack,
|
||||||
|
Checkbox,
|
||||||
|
FormControlLabel,
|
||||||
|
Radio,
|
||||||
|
RadioGroup
|
||||||
|
} from "@mui/material"; //Интерфейсные компоненты
|
||||||
|
import { FilterInputField } from "./filter_input_field"; //Компонент поля ввода
|
||||||
|
import { ApplicationСtx } from "../../../context/application"; //Контекст приложения
|
||||||
|
import { hasValue } from "../../../core/utils"; //Вспомогательные функции
|
||||||
|
import { APP_STYLES } from "../../../../app.styles"; //Типовые стили
|
||||||
|
import { EVENT_STATES } from "../layouts"; //Перечисление состояний события
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Стили
|
||||||
|
const STYLES = {
|
||||||
|
FILTERS_SCROLL: { overflowY: "auto", ...APP_STYLES.SCROLL },
|
||||||
|
DIALOG_ACTIONS: { justifyContent: "end", paddingRight: "24px", paddingLeft: "24px" },
|
||||||
|
CLOSE_BUTTON: {
|
||||||
|
position: "absolute",
|
||||||
|
right: 8,
|
||||||
|
top: 8,
|
||||||
|
color: theme => theme.palette.grey[500]
|
||||||
|
},
|
||||||
|
DOCLINK_STACK: { alignItems: "baseline" },
|
||||||
|
SELECT: { width: "450px" },
|
||||||
|
BOX_WITH_LEGEND: { border: "1px solid #939393" },
|
||||||
|
LEGEND: { textAlign: "left" }
|
||||||
|
};
|
||||||
|
|
||||||
|
//-----------------------
|
||||||
|
//Вспомогательные функции
|
||||||
|
//-----------------------
|
||||||
|
|
||||||
|
//Выбор типа события
|
||||||
|
const selectEventType = (value, showDictionary, callBack) => {
|
||||||
|
showDictionary({
|
||||||
|
unitCode: "ClientEventTypes",
|
||||||
|
showMethod: "dictionary",
|
||||||
|
inputParameters: [{ name: "pos_eventtypecode", value: value }],
|
||||||
|
callBack: res => (res.success === true ? callBack(res.outParameters.eventtypecode) : callBack(null))
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//Выбор каталога
|
||||||
|
const selectCatalog = (value, showDictionary, callBack) => {
|
||||||
|
showDictionary({
|
||||||
|
unitCode: "CatalogTree",
|
||||||
|
inputParameters: [
|
||||||
|
{ name: "in_DOCNAME", value: "ClientEvents" },
|
||||||
|
{ name: "in_NAME", value: value }
|
||||||
|
],
|
||||||
|
callBack: res => (res.success === true ? callBack(res.outParameters.out_NAME) : callBack(null))
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//Выбор исполнителя
|
||||||
|
const selectSendPerson = (value, showDictionary, callBack) => {
|
||||||
|
showDictionary({
|
||||||
|
unitCode: "AGNLIST",
|
||||||
|
showMethod: "agents",
|
||||||
|
inputParameters: [{ name: "pos_agnmnemo", value: value }],
|
||||||
|
callBack: res => (res.success === true ? callBack(res.outParameters.agnmnemo) : callBack(null))
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//Выбор подразделения
|
||||||
|
const selectSendDivision = (value, showDictionary, callBack) => {
|
||||||
|
showDictionary({
|
||||||
|
unitCode: "INS_DEPARTMENT",
|
||||||
|
inputParameters: [{ name: "in_CODE", value: value }],
|
||||||
|
callBack: res => (res.success === true ? callBack(res.outParameters.out_CODE) : callBack(null))
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//Выбор группы пользователей
|
||||||
|
const selectSendUsrGrp = (value, showDictionary, callBack) => {
|
||||||
|
showDictionary({
|
||||||
|
unitCode: "CostStaffGroups",
|
||||||
|
inputParameters: [{ name: "in_CODE", value: value }],
|
||||||
|
callBack: res => (res.success === true ? callBack(res.outParameters.out_CODE) : callBack(null))
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//---------------
|
||||||
|
//Тело компонента
|
||||||
|
//---------------
|
||||||
|
|
||||||
|
//Диалоговое окно фильтра отбора
|
||||||
|
const FilterDialog = ({ initial, docs, onFilterChange, onFilterOpen, getDocLinks }) => {
|
||||||
|
//Собственное состояние
|
||||||
|
const [filter, setFilter] = useState({ ...initial });
|
||||||
|
|
||||||
|
//Состояние текущего типа события
|
||||||
|
const [curType, setCurType] = useState(initial.type);
|
||||||
|
|
||||||
|
//Состояние учётных документов
|
||||||
|
const [curDocLinks, setCurDocLinks] = useState(docs);
|
||||||
|
|
||||||
|
//Подключение к контексту взаимодействия с сервером
|
||||||
|
const { executeStored } = useContext(BackEndСtx);
|
||||||
|
|
||||||
|
//Получение подкаталогов
|
||||||
|
const getSubCatalogs = useCallback(async () => {
|
||||||
|
const data = await executeStored({
|
||||||
|
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_SUBCATALOGS_GET",
|
||||||
|
args: {
|
||||||
|
SCRN_NAME: filter.catalog,
|
||||||
|
NSUBCAT: filter.wSubcatalogs ? 1 : 0
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return data.SRESULT;
|
||||||
|
}, [executeStored, filter.catalog, filter.wSubcatalogs]);
|
||||||
|
|
||||||
|
//Подключение к контексту приложения
|
||||||
|
const { pOnlineShowDictionary } = useContext(ApplicationСtx);
|
||||||
|
|
||||||
|
//При закрытии диалога без изменения фильтра
|
||||||
|
const handleCancel = () => onFilterOpen();
|
||||||
|
|
||||||
|
//При очистке фильтра
|
||||||
|
const handleClear = () => {
|
||||||
|
setFilter({
|
||||||
|
evState: EVENT_STATES[1],
|
||||||
|
type: "",
|
||||||
|
catalog: "",
|
||||||
|
crn: "",
|
||||||
|
wSubcatalogs: false,
|
||||||
|
sendPerson: "",
|
||||||
|
sendDivision: "",
|
||||||
|
sendUsrGrp: "",
|
||||||
|
docLink: ""
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//При закрытии диалога с изменением фильтра
|
||||||
|
const handleOk = async () => {
|
||||||
|
if (filter.catalog && !filter.crn) {
|
||||||
|
const crns = await getSubCatalogs();
|
||||||
|
let filterCopy = { ...filter };
|
||||||
|
crns ? (filterCopy.crn = crns) : null;
|
||||||
|
onFilterChange(filterCopy);
|
||||||
|
onFilterOpen();
|
||||||
|
} else {
|
||||||
|
onFilterChange(filter);
|
||||||
|
onFilterOpen();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//При изменении значения элемента
|
||||||
|
const handleFilterItemChange = (item, value) => {
|
||||||
|
item === "type" && filter.docLink ? setFilter(pv => ({ ...pv, [item]: value, docLink: "" })) : setFilter(pv => ({ ...pv, [item]: value }));
|
||||||
|
};
|
||||||
|
|
||||||
|
//Очистка учётного документа
|
||||||
|
const clearDocLink = () => setFilter(pv => ({ ...pv, docLink: "" }));
|
||||||
|
|
||||||
|
//При изменении типа события
|
||||||
|
useEffect(() => {
|
||||||
|
if (curType) {
|
||||||
|
if (curType !== filter.type) {
|
||||||
|
clearDocLink();
|
||||||
|
setCurDocLinks([]);
|
||||||
|
} else if (curType === filter.type && curType === initial.type && !curDocLinks.length) setCurDocLinks(docs);
|
||||||
|
}
|
||||||
|
}, [curDocLinks, curType, docs, filter.type, initial.type]);
|
||||||
|
|
||||||
|
//Обработка изменений с каталогами
|
||||||
|
useEffect(() => {
|
||||||
|
if (!filter.catalog && filter.wSubcatalogs) setFilter(pv => ({ ...pv, wSubcatalogs: false }));
|
||||||
|
if (filter.catalog !== initial.catalog && filter.crn) setFilter(pv => ({ ...pv, crn: "" }));
|
||||||
|
if (filter.catalog === initial.catalog && filter.crn !== initial.crn && filter.wSubcatalogs === initial.wSubcatalogs)
|
||||||
|
setFilter(pv => ({ ...pv, crn: initial.crn }));
|
||||||
|
if (filter.catalog === initial.catalog && filter.wSubcatalogs !== initial.wSubcatalogs && !filter.wSubcatalogs)
|
||||||
|
setFilter(pv => ({ ...pv, crn: initial.crn.split(";")[0] }));
|
||||||
|
if (filter.catalog === initial.catalog && filter.wSubcatalogs !== initial.wSubcatalogs && filter.wSubcatalogs)
|
||||||
|
setFilter(pv => ({ ...pv, crn: "" }));
|
||||||
|
}, [filter.catalog, filter.crn, filter.wSubcatalogs, initial.catalog, initial.crn, initial.wSubcatalogs]);
|
||||||
|
|
||||||
|
//Генерация содержимого
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Dialog open onClose={handleCancel} fullWidth maxWidth="sm">
|
||||||
|
<DialogTitle>Фильтр отбора</DialogTitle>
|
||||||
|
<IconButton aria-label="close" onClick={handleCancel} sx={STYLES.CLOSE_BUTTON}>
|
||||||
|
<Icon>close</Icon>
|
||||||
|
</IconButton>
|
||||||
|
<DialogContent sx={STYLES.FILTERS_SCROLL}>
|
||||||
|
<Box sx={STYLES.BOX_WITH_LEGEND} component="fieldset">
|
||||||
|
<legend style={STYLES.LEGEND}>Состояние</legend>
|
||||||
|
<RadioGroup
|
||||||
|
row
|
||||||
|
aria-labelledby="evState-label"
|
||||||
|
id="evState"
|
||||||
|
name="evState"
|
||||||
|
value={filter.evState}
|
||||||
|
onChange={e => handleFilterItemChange(e.target.name, e.target.value)}
|
||||||
|
>
|
||||||
|
{Object.keys(EVENT_STATES).map(function (k) {
|
||||||
|
return <FormControlLabel key={k} value={EVENT_STATES[k]} control={<Radio />} label={EVENT_STATES[k]} />;
|
||||||
|
})}
|
||||||
|
</RadioGroup>
|
||||||
|
</Box>
|
||||||
|
<Box component="section" p={1}>
|
||||||
|
<FilterInputField
|
||||||
|
elementCode="type"
|
||||||
|
elementValue={filter.type}
|
||||||
|
labelText="Тип"
|
||||||
|
dictionary={callBack => selectEventType(filter.type, pOnlineShowDictionary, callBack)}
|
||||||
|
onChange={handleFilterItemChange}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box component="section" p={1}>
|
||||||
|
<FilterInputField
|
||||||
|
elementCode="catalog"
|
||||||
|
elementValue={filter.catalog}
|
||||||
|
labelText="Каталог"
|
||||||
|
dictionary={callBack => selectCatalog(filter.catalog, pOnlineShowDictionary, callBack)}
|
||||||
|
onChange={handleFilterItemChange}
|
||||||
|
/>
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
id="wSubcatalogs"
|
||||||
|
name="wSubcatalogs"
|
||||||
|
checked={filter.wSubcatalogs}
|
||||||
|
disabled={filter.catalog ? false : true}
|
||||||
|
onChange={e => handleFilterItemChange(e.target.name, e.target.checked)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label="Включая подкаталоги"
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box component="section" p={1}>
|
||||||
|
<FilterInputField
|
||||||
|
elementCode="sendPerson"
|
||||||
|
elementValue={filter.sendPerson}
|
||||||
|
labelText="Исполнитель"
|
||||||
|
dictionary={callBack => selectSendPerson(filter.sendPerson, pOnlineShowDictionary, callBack)}
|
||||||
|
onChange={handleFilterItemChange}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box component="section" p={1}>
|
||||||
|
<FilterInputField
|
||||||
|
elementCode="sendDivision"
|
||||||
|
elementValue={filter.sendDivision}
|
||||||
|
labelText="Подразделение"
|
||||||
|
dictionary={callBack => selectSendDivision(filter.sendDivision, pOnlineShowDictionary, callBack)}
|
||||||
|
onChange={handleFilterItemChange}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box component="section" p={1}>
|
||||||
|
<FilterInputField
|
||||||
|
elementCode="sendUsrGrp"
|
||||||
|
elementValue={filter.sendUsrGrp}
|
||||||
|
labelText="Группа пользователей"
|
||||||
|
dictionary={callBack => selectSendUsrGrp(filter.sendUsrGrp, pOnlineShowDictionary, callBack)}
|
||||||
|
onChange={handleFilterItemChange}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box component="section" p={1}>
|
||||||
|
<Stack direction="row" sx={STYLES.DOCLINK_STACK}>
|
||||||
|
<FilterInputField
|
||||||
|
elementCode="docLink"
|
||||||
|
elementValue={filter.docLink}
|
||||||
|
labelText="Учётный документ"
|
||||||
|
items={curDocLinks}
|
||||||
|
disabled={!curDocLinks.length ? true : false}
|
||||||
|
onChange={handleFilterItemChange}
|
||||||
|
sx={STYLES.SELECT}
|
||||||
|
/>
|
||||||
|
<IconButton title="Очистить" disabled={!filter.docLink} onClick={clearDocLink}>
|
||||||
|
<Icon>clear</Icon>
|
||||||
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
title="Обновить"
|
||||||
|
disabled={!((!curType || curType !== filter.type) && filter.type)}
|
||||||
|
onClick={() => {
|
||||||
|
setCurType(filter.type);
|
||||||
|
clearDocLink();
|
||||||
|
getDocLinks(filter.type).then(dl => setCurDocLinks(dl));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon>refresh</Icon>
|
||||||
|
</IconButton>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions sx={STYLES.DIALOG_ACTIONS}>
|
||||||
|
<Button disabled={!hasValue(filter.type)} variant="text" onClick={handleOk}>
|
||||||
|
ОК
|
||||||
|
</Button>
|
||||||
|
<Button variant="text" onClick={handleClear}>
|
||||||
|
Очистить
|
||||||
|
</Button>
|
||||||
|
<Button variant="text" onClick={handleCancel}>
|
||||||
|
Отмена
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств компонента - Диалоговое окно фильтра отбора
|
||||||
|
FilterDialog.propTypes = {
|
||||||
|
initial: PropTypes.object.isRequired,
|
||||||
|
docs: PropTypes.arrayOf(PropTypes.object),
|
||||||
|
onFilterChange: PropTypes.func.isRequired,
|
||||||
|
onFilterOpen: PropTypes.func.isRequired,
|
||||||
|
getDocLinks: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
//--------------------
|
||||||
|
//Интерфейс компонента
|
||||||
|
//--------------------
|
||||||
|
|
||||||
|
export { FilterDialog };
|
||||||
134
app/panels/clnt_task_board/components/filter_input_field.js
Normal file
134
app/panels/clnt_task_board/components/filter_input_field.js
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - УДП - Доски задач
|
||||||
|
Компонент: Поле ввода диалога фильтра
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React, { useEffect, useState } from "react"; //Классы React
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
import { FormControl, InputLabel, Input, InputAdornment, IconButton, Icon, FormHelperText, Select, MenuItem } from "@mui/material"; //Интерфейсные компоненты
|
||||||
|
import { APP_STYLES } from "../../../../app.styles"; //Типовые стили
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Стили
|
||||||
|
const STYLES = {
|
||||||
|
HELPER_TEXT: { color: "red" },
|
||||||
|
SELECT_MENU: w => {
|
||||||
|
return { overflowY: "auto", ...APP_STYLES.SCROLL, width: w ? w : null };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//---------------
|
||||||
|
//Тело компонента
|
||||||
|
//---------------
|
||||||
|
|
||||||
|
//Поле ввода
|
||||||
|
const FilterInputField = ({ elementCode, elementValue, labelText, onChange, required = false, items = null, dictionary, ...other }) => {
|
||||||
|
//Значение элемента
|
||||||
|
const [value, setValue] = useState(elementValue);
|
||||||
|
|
||||||
|
//При получении нового значения из вне
|
||||||
|
useEffect(() => {
|
||||||
|
setValue(elementValue);
|
||||||
|
}, [elementValue]);
|
||||||
|
|
||||||
|
//Выбор значения из словаря
|
||||||
|
const handleDictionaryClick = () => {
|
||||||
|
dictionary ? dictionary(res => (res ? handleChange({ target: { name: elementCode, value: res } }) : null)) : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
//Изменение значения элемента
|
||||||
|
const handleChange = e => {
|
||||||
|
setValue(e.target.value);
|
||||||
|
if (onChange) onChange(e.target.name, e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Генерация поля с выбором из словаря Парус
|
||||||
|
const renderInput = validationError => {
|
||||||
|
return (
|
||||||
|
<Input
|
||||||
|
error={validationError}
|
||||||
|
id={elementCode}
|
||||||
|
name={elementCode}
|
||||||
|
value={value}
|
||||||
|
endAdornment={
|
||||||
|
dictionary ? (
|
||||||
|
<InputAdornment position="end">
|
||||||
|
<IconButton aria-label={`${elementCode} select`} onClick={handleDictionaryClick} edge="end">
|
||||||
|
<Icon>list</Icon>
|
||||||
|
</IconButton>
|
||||||
|
</InputAdornment>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
aria-describedby={`${elementCode}-helper-text`}
|
||||||
|
label={labelText}
|
||||||
|
onChange={handleChange}
|
||||||
|
{...other}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Генерация поля с выпадающим списком
|
||||||
|
const renderSelect = (items, validationError) => {
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
error={validationError}
|
||||||
|
id={elementCode}
|
||||||
|
name={elementCode}
|
||||||
|
value={value}
|
||||||
|
aria-describedby={`${elementCode}-helper-text`}
|
||||||
|
label={labelText}
|
||||||
|
MenuProps={{ slotProps: { paper: { sx: STYLES.SELECT_MENU(document.getElementById(elementCode)?.parentElement.clientWidth) } } }}
|
||||||
|
onChange={handleChange}
|
||||||
|
{...other}
|
||||||
|
>
|
||||||
|
{items
|
||||||
|
? items.map((item, i) => (
|
||||||
|
<MenuItem key={i} value={item.id}>
|
||||||
|
{item.descr}
|
||||||
|
</MenuItem>
|
||||||
|
))
|
||||||
|
: null}
|
||||||
|
</Select>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Признак ошибки валидации
|
||||||
|
const validationError = !value && required ? true : false;
|
||||||
|
|
||||||
|
//Генерация содержимого
|
||||||
|
return (
|
||||||
|
<FormControl fullWidth variant="standard">
|
||||||
|
<InputLabel htmlFor={elementCode}>{labelText}</InputLabel>
|
||||||
|
{items ? renderSelect(items, validationError) : renderInput(validationError)}
|
||||||
|
{validationError ? (
|
||||||
|
<FormHelperText id={`${elementCode}-helper-text`} sx={STYLES.HELPER_TEXT}>
|
||||||
|
*Обязательное поле
|
||||||
|
</FormHelperText>
|
||||||
|
) : null}
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств - Поле ввода
|
||||||
|
FilterInputField.propTypes = {
|
||||||
|
elementCode: PropTypes.string.isRequired,
|
||||||
|
elementValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||||
|
labelText: PropTypes.string.isRequired,
|
||||||
|
required: PropTypes.bool,
|
||||||
|
items: PropTypes.arrayOf(PropTypes.object),
|
||||||
|
dictionary: PropTypes.func,
|
||||||
|
onChange: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
//--------------------
|
||||||
|
//Интерфейс компонента
|
||||||
|
//--------------------
|
||||||
|
|
||||||
|
export { FilterInputField };
|
||||||
139
app/panels/clnt_task_board/components/note_dialog.js
Normal file
139
app/panels/clnt_task_board/components/note_dialog.js
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - УДП - Доски задач
|
||||||
|
Компонент: Диалог примечания
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React, { useState } from "react"; //Классы React
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogTitle,
|
||||||
|
IconButton,
|
||||||
|
Icon,
|
||||||
|
DialogContent,
|
||||||
|
DialogActions,
|
||||||
|
Button,
|
||||||
|
TextField,
|
||||||
|
FormControl,
|
||||||
|
InputLabel,
|
||||||
|
Select,
|
||||||
|
MenuItem
|
||||||
|
} from "@mui/material"; //Интерфейсные компоненты
|
||||||
|
import { APP_STYLES } from "../../../../app.styles"; //Типовые стили
|
||||||
|
import { arrayFormer } from "../layouts"; //Формировщик массива
|
||||||
|
|
||||||
|
//Стили
|
||||||
|
const STYLES = {
|
||||||
|
DIALOG_ACTIONS: { justifyContent: "end", paddingRight: "24px", paddingLeft: "24px" },
|
||||||
|
CLOSE_BUTTON: {
|
||||||
|
position: "absolute",
|
||||||
|
right: 8,
|
||||||
|
top: 8,
|
||||||
|
color: theme => theme.palette.grey[500]
|
||||||
|
},
|
||||||
|
DIALOG_CONTENT: { paddingTop: 0, paddingBottom: 0 },
|
||||||
|
TEXT_FIELD: {
|
||||||
|
overflowY: "auto",
|
||||||
|
...APP_STYLES.SCROLL
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//---------------
|
||||||
|
//Тело компонента
|
||||||
|
//---------------
|
||||||
|
|
||||||
|
//Диалог примечания
|
||||||
|
const NoteDialog = ({ noteTypes, onCallback, onNoteOpen }) => {
|
||||||
|
//Собственное состояние
|
||||||
|
const [note, setNote] = useState({ headerV: 0, text: "" });
|
||||||
|
|
||||||
|
//При изменении примечания
|
||||||
|
const handleNoteChange = value => setNote(pv => ({ ...pv, text: value }));
|
||||||
|
|
||||||
|
//При изменении заголовка примечания
|
||||||
|
const handleNoteHeaderChange = h => {
|
||||||
|
setNote(pv => ({ ...pv, headerV: h }));
|
||||||
|
};
|
||||||
|
|
||||||
|
//При закрытии диалога с изменением фильтра
|
||||||
|
const handleOK = () => {
|
||||||
|
onCallback({ header: noteTypes[note.headerV], text: note.text });
|
||||||
|
onNoteOpen();
|
||||||
|
};
|
||||||
|
|
||||||
|
//При закрытии диалога без изменения фильтра
|
||||||
|
const handleCancel = () => {
|
||||||
|
onNoteOpen();
|
||||||
|
};
|
||||||
|
|
||||||
|
//Генерация содержимого
|
||||||
|
return (
|
||||||
|
<Dialog open onClose={handleCancel} fullWidth maxWidth="sm">
|
||||||
|
<DialogTitle>Примечание</DialogTitle>
|
||||||
|
<IconButton aria-label="close" onClick={handleCancel} sx={STYLES.CLOSE_BUTTON}>
|
||||||
|
<Icon>close</Icon>
|
||||||
|
</IconButton>
|
||||||
|
<DialogContent sx={STYLES.DIALOG_CONTENT}>
|
||||||
|
<FormControl fullWidth variant="standard">
|
||||||
|
<InputLabel htmlFor="noteHeader">Заголовок примечания</InputLabel>
|
||||||
|
<Select
|
||||||
|
id="noteHeader"
|
||||||
|
name="noteHeader"
|
||||||
|
value={note.headerV}
|
||||||
|
aria-describedby="noteHeader-helper-text"
|
||||||
|
label="Заголовок примечания"
|
||||||
|
margin="dense"
|
||||||
|
onChange={e => handleNoteHeaderChange(e.target.value)}
|
||||||
|
>
|
||||||
|
{noteTypes
|
||||||
|
? arrayFormer(noteTypes).map((item, i) => (
|
||||||
|
<MenuItem key={i} value={i}>
|
||||||
|
{item}
|
||||||
|
</MenuItem>
|
||||||
|
))
|
||||||
|
: null}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
<TextField
|
||||||
|
id="note"
|
||||||
|
label="Описание"
|
||||||
|
variant="standard"
|
||||||
|
fullWidth
|
||||||
|
required
|
||||||
|
multiline
|
||||||
|
minRows={7}
|
||||||
|
maxRows={7}
|
||||||
|
value={note.text}
|
||||||
|
margin="normal"
|
||||||
|
inputProps={{ sx: STYLES.TEXT_FIELD }}
|
||||||
|
onChange={e => handleNoteChange(e.target.value)}
|
||||||
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions sx={STYLES.DIALOG_ACTIONS}>
|
||||||
|
<Button disabled={!note.text} variant="text" onClick={handleOK}>
|
||||||
|
ОК
|
||||||
|
</Button>
|
||||||
|
<Button variant="text" onClick={onNoteOpen}>
|
||||||
|
Отмена
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств - Диалог примечания
|
||||||
|
NoteDialog.propTypes = {
|
||||||
|
noteTypes: PropTypes.array,
|
||||||
|
onCallback: PropTypes.func.isRequired,
|
||||||
|
onNoteOpen: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { NoteDialog };
|
||||||
93
app/panels/clnt_task_board/components/rules_select.js
Normal file
93
app/panels/clnt_task_board/components/rules_select.js
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - УДП - Доски задач
|
||||||
|
Компонент: Выпадающий список выбора заливки событий
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React, { useEffect, useState } from "react"; //Классы React
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
import { FormControl, InputLabel, Select, MenuItem } from "@mui/material"; //Интерфейсные компоненты
|
||||||
|
import { useColorRules } from "../hooks/hooks.js"; //Вспомогательные хуки
|
||||||
|
import { APP_STYLES } from "../../../../app.styles"; //Типовые стили
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Стили
|
||||||
|
const STYLES = {
|
||||||
|
SELECT_MENU: w => {
|
||||||
|
return { overflowY: "auto", ...APP_STYLES.SCROLL, width: w ? w : null };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//---------------
|
||||||
|
//Тело компонента
|
||||||
|
//---------------
|
||||||
|
|
||||||
|
//Выпадающий список выбора заливки событий
|
||||||
|
const RulesSelect = ({ initRule, handleChange, ...other }) => {
|
||||||
|
//Состояние пользовательских настроек заливки событий
|
||||||
|
const [colorRules] = useColorRules();
|
||||||
|
|
||||||
|
//Собственное состояние
|
||||||
|
const [curRule, setCurRule] = useState(initRule > -1 ? "" : initRule);
|
||||||
|
|
||||||
|
//При получении нового значения заливки из вне
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
(colorRules.loaded && initRule > -1 && curRule === "") ||
|
||||||
|
(Number.isInteger(initRule) && Number.isInteger(curRule) && initRule !== curRule)
|
||||||
|
)
|
||||||
|
setCurRule(initRule);
|
||||||
|
}, [colorRules, curRule, initRule]);
|
||||||
|
|
||||||
|
//При изменении заливки событий
|
||||||
|
const handleRuleChange = e => {
|
||||||
|
let id = e.target.value;
|
||||||
|
setCurRule(id);
|
||||||
|
handleChange(id > -1 ? colorRules.rules[id] : {});
|
||||||
|
};
|
||||||
|
|
||||||
|
//Генерация содержимого
|
||||||
|
return colorRules ? (
|
||||||
|
<FormControl size="small" variant="standard" {...other}>
|
||||||
|
<InputLabel htmlFor="clrRules">Заливка событий</InputLabel>
|
||||||
|
<Select
|
||||||
|
id="clrRules"
|
||||||
|
name="clrRules"
|
||||||
|
value={curRule}
|
||||||
|
aria-describedby="clrRules-helper-text"
|
||||||
|
label="Заливка событий"
|
||||||
|
MenuProps={{ slotProps: { paper: { sx: STYLES.SELECT_MENU(document.getElementById("clrRules")?.parentElement.clientWidth) } } }}
|
||||||
|
onChange={handleRuleChange}
|
||||||
|
>
|
||||||
|
<MenuItem key={-1} value={-1}>
|
||||||
|
{"-"}
|
||||||
|
</MenuItem>
|
||||||
|
{colorRules.rules
|
||||||
|
? colorRules.rules.map((item, i) => (
|
||||||
|
<MenuItem key={i} value={item.id}>
|
||||||
|
{item.propName}
|
||||||
|
</MenuItem>
|
||||||
|
))
|
||||||
|
: null}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
) : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств - Выпадающий список выбора заливки событий
|
||||||
|
RulesSelect.propTypes = {
|
||||||
|
initRule: PropTypes.number.isRequired,
|
||||||
|
handleChange: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
//--------------------
|
||||||
|
//Интерфейс компонента
|
||||||
|
//--------------------
|
||||||
|
|
||||||
|
export { RulesSelect };
|
||||||
135
app/panels/clnt_task_board/components/settings_dialog.js
Normal file
135
app/panels/clnt_task_board/components/settings_dialog.js
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - УДП - Доски задач
|
||||||
|
Компонент: Диалог дополнительных настроек
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React, { useState } from "react"; //Классы React
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
import { Dialog, DialogTitle, DialogContent, DialogActions, IconButton, Icon, Button, Box, Stack } from "@mui/material"; //Интерфейсные компоненты
|
||||||
|
import { RulesSelect } from "./rules_select.js"; //Выпадающий список выбора заливки событий
|
||||||
|
import { FilterInputField } from "./filter_input_field.js"; //Поле ввода
|
||||||
|
import { APP_STYLES } from "../../../../app.styles"; //Типовые стили
|
||||||
|
import { sortAttrs, sortDest } from "../layouts.js"; //Допустимые значение поля и направления сортировки
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Стили
|
||||||
|
const STYLES = {
|
||||||
|
FILTERS_SCROLL: { overflowY: "auto", ...APP_STYLES.SCROLL },
|
||||||
|
DIALOG_ACTIONS: { justifyContent: "end", paddingRight: "24px", paddingLeft: "24px" },
|
||||||
|
CLOSE_BUTTON: {
|
||||||
|
position: "absolute",
|
||||||
|
right: 8,
|
||||||
|
top: 8,
|
||||||
|
color: theme => theme.palette.grey[500]
|
||||||
|
},
|
||||||
|
DOCLINK_STACK: { alignItems: "baseline" },
|
||||||
|
SELECT: { width: "100%" },
|
||||||
|
BOX_WITH_LEGEND: { border: "1px solid #939393" },
|
||||||
|
LEGEND: { textAlign: "left" },
|
||||||
|
SELECT_MENU: w => {
|
||||||
|
return { overflowY: "auto", ...APP_STYLES.SCROLL, width: w ? w : null };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//---------------
|
||||||
|
//Тело компонента
|
||||||
|
//---------------
|
||||||
|
|
||||||
|
//Диалог дополнительных настроек
|
||||||
|
const SettingsDialog = ({ initial, onSettingsChange, onOpen, ...other }) => {
|
||||||
|
//Состояние дополнительных настроек
|
||||||
|
const [settings, setSettings] = useState(
|
||||||
|
initial.statusesSort.attr ? { ...initial } : { ...initial, statusesSort: { sorted: true, attr: "name", dest: "asc" } }
|
||||||
|
);
|
||||||
|
|
||||||
|
//Изменение заливки событий
|
||||||
|
const handleColorRuleChange = cr => setSettings(pv => ({ ...pv, colorRule: cr }));
|
||||||
|
|
||||||
|
//Изменение поля сортировки
|
||||||
|
const handleSortAttrChange = (item, value) => setSettings(pv => ({ ...pv, statusesSort: { ...pv.statusesSort, [item]: value } }));
|
||||||
|
|
||||||
|
//Изменение направления сортировки
|
||||||
|
const handleSortDestChange = d => setSettings(pv => ({ ...pv, statusesSort: { ...pv.statusesSort, dest: d } }));
|
||||||
|
|
||||||
|
//Генерация содержимого
|
||||||
|
return (
|
||||||
|
<div {...other}>
|
||||||
|
<Dialog open onClose={onOpen} fullWidth maxWidth="sm">
|
||||||
|
<DialogTitle>Настройки</DialogTitle>
|
||||||
|
<IconButton aria-label="close" onClick={onOpen} sx={STYLES.CLOSE_BUTTON}>
|
||||||
|
<Icon>close</Icon>
|
||||||
|
</IconButton>
|
||||||
|
<DialogContent sx={STYLES.FILTERS_SCROLL}>
|
||||||
|
<Box component="section" p={1}>
|
||||||
|
<RulesSelect
|
||||||
|
initRule={settings.colorRule.id !== undefined ? settings.colorRule.id : -1}
|
||||||
|
handleChange={handleColorRuleChange}
|
||||||
|
sx={STYLES.SELECT}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box component="section" p={1}>
|
||||||
|
<Stack direction="row" sx={STYLES.DOCLINK_STACK}>
|
||||||
|
<FilterInputField
|
||||||
|
elementCode="attr"
|
||||||
|
elementValue={settings.statusesSort.attr}
|
||||||
|
labelText="Поле сортировки"
|
||||||
|
items={sortAttrs}
|
||||||
|
onChange={handleSortAttrChange}
|
||||||
|
MenuProps={{
|
||||||
|
slotProps: { paper: { sx: STYLES.SELECT_MENU(document.getElementById("attr")?.parentElement.clientWidth) } }
|
||||||
|
}}
|
||||||
|
sx={STYLES.SELECT}
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
title={settings.statusesSort.dest === "asc" ? "По возрастанию" : "По убыванию"}
|
||||||
|
onClick={() => handleSortDestChange(sortDest[sortDest.indexOf(settings.statusesSort.dest) * -1])}
|
||||||
|
>
|
||||||
|
<Icon>{settings.statusesSort.dest === "asc" ? "arrow_upward" : "arrow_downward"}</Icon>
|
||||||
|
</IconButton>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions sx={STYLES.DIALOG_ACTIONS}>
|
||||||
|
<Button
|
||||||
|
variant="text"
|
||||||
|
onClick={() => {
|
||||||
|
onSettingsChange(settings);
|
||||||
|
onOpen();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
ОК
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="text"
|
||||||
|
onClick={() => setSettings(pv => ({ ...pv, statusesSort: { ...pv.statusesSort, attr: "name", dest: "asc" }, colorRule: {} }))}
|
||||||
|
>
|
||||||
|
Очистить
|
||||||
|
</Button>
|
||||||
|
<Button variant="text" onClick={onOpen}>
|
||||||
|
Отмена
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств компонента - Диалог дополнительных настроек
|
||||||
|
SettingsDialog.propTypes = {
|
||||||
|
initial: PropTypes.object.isRequired,
|
||||||
|
onSettingsChange: PropTypes.func.isRequired,
|
||||||
|
onOpen: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
//--------------------
|
||||||
|
//Интерфейс компонента
|
||||||
|
//--------------------
|
||||||
|
|
||||||
|
export { SettingsDialog };
|
||||||
172
app/panels/clnt_task_board/components/status_card.js
Normal file
172
app/panels/clnt_task_board/components/status_card.js
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - УДП - Доски задач
|
||||||
|
Компонент: Карточка статуса событий
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React, { useState } from "react"; //Классы React
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
import { Card, CardHeader, CardContent, Button, IconButton, Icon, Typography, Stack } from "@mui/material"; //Интерфейсные компоненты
|
||||||
|
import { TaskCard } from "./task_card.js"; //Компонент Карточка события
|
||||||
|
import { TaskCardSettings } from "./task_card_settings.js"; //Компонент Диалог настройки карточки событий
|
||||||
|
import { APP_STYLES } from "../../../../app.styles"; //Типовые стили
|
||||||
|
import { COLORS } from "../layouts.js"; //Цвета статусов
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Высота заголовка
|
||||||
|
const TITLE_HEIGHT = "64px";
|
||||||
|
|
||||||
|
//Нижний отступ заголовка
|
||||||
|
const TITLE_PADDING_BOTTOM = "16px";
|
||||||
|
|
||||||
|
//Высота фильтра
|
||||||
|
const FILTER_HEIGHT = "56px";
|
||||||
|
|
||||||
|
//Стили
|
||||||
|
const STYLES = {
|
||||||
|
STATUS_BLOCK: statusColor => {
|
||||||
|
return {
|
||||||
|
width: "350px",
|
||||||
|
height: `calc(100vh - ${TITLE_HEIGHT} - ${TITLE_PADDING_BOTTOM} - ${FILTER_HEIGHT} - 8px)`,
|
||||||
|
backgroundColor: statusColor,
|
||||||
|
padding: "8px"
|
||||||
|
};
|
||||||
|
},
|
||||||
|
BLOCK_OPACITY: isAvailable => {
|
||||||
|
return isAvailable ? { opacity: 1 } : { opacity: 0.5 };
|
||||||
|
},
|
||||||
|
MARK_INFO: {
|
||||||
|
textAlign: "left",
|
||||||
|
textOverflow: "ellipsis",
|
||||||
|
overflow: "hidden",
|
||||||
|
display: "-webkit-box",
|
||||||
|
hyphens: "auto",
|
||||||
|
WebkitBoxOrient: "vertical",
|
||||||
|
WebkitLineClamp: 1,
|
||||||
|
maxWidth: "calc(300px)",
|
||||||
|
width: "-webkit-fill-available",
|
||||||
|
fontSize: "1.2rem",
|
||||||
|
cursor: "default"
|
||||||
|
},
|
||||||
|
PADDING_0: { padding: 0 },
|
||||||
|
CARD_CONTENT: {
|
||||||
|
padding: 0,
|
||||||
|
paddingRight: "5px",
|
||||||
|
paddingBottom: "5px !important",
|
||||||
|
overflowY: "auto",
|
||||||
|
maxHeight: `calc(100vh - ${TITLE_HEIGHT} - ${TITLE_PADDING_BOTTOM} - ${FILTER_HEIGHT} - 85px)`,
|
||||||
|
...APP_STYLES.SCROLL
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//---------------
|
||||||
|
//Тело компонента
|
||||||
|
//---------------
|
||||||
|
|
||||||
|
//Карточка статуса события
|
||||||
|
const StatusCard = ({
|
||||||
|
tasks,
|
||||||
|
status,
|
||||||
|
settings,
|
||||||
|
extraData,
|
||||||
|
filtersType,
|
||||||
|
isCardAvailable,
|
||||||
|
onReload,
|
||||||
|
onDragItemChange,
|
||||||
|
onTaskDialogOpen,
|
||||||
|
onNoteDialogOpen,
|
||||||
|
placeholder
|
||||||
|
}) => {
|
||||||
|
//Состояние диалога настройки
|
||||||
|
const [statusCardSettingsOpen, setStatusCardSettingsOpen] = useState(false);
|
||||||
|
|
||||||
|
//Открыть/закрыть диалог настройки
|
||||||
|
const handleStatusCardSettingsOpen = () => setStatusCardSettingsOpen(!statusCardSettingsOpen);
|
||||||
|
|
||||||
|
//Генерация содержимого
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{statusCardSettingsOpen ? (
|
||||||
|
<TaskCardSettings statuses={tasks.statuses} availableClrs={COLORS} onDialogOpen={handleStatusCardSettingsOpen} />
|
||||||
|
) : null}
|
||||||
|
<Card
|
||||||
|
className="category-card"
|
||||||
|
sx={{
|
||||||
|
...STYLES.STATUS_BLOCK(status.color),
|
||||||
|
...STYLES.BLOCK_OPACITY(isCardAvailable(status.code))
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CardHeader
|
||||||
|
action={
|
||||||
|
<IconButton aria-label="settings" onClick={handleStatusCardSettingsOpen}>
|
||||||
|
<Icon>more_vert</Icon>
|
||||||
|
</IconButton>
|
||||||
|
}
|
||||||
|
title={
|
||||||
|
<Typography sx={STYLES.MARK_INFO} title={status[settings.statusesSort.attr] || status.name} variant="h5">
|
||||||
|
{status[settings.statusesSort.attr] || status.name}
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
subheader={
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
onDragItemChange(filtersType, status.code);
|
||||||
|
onTaskDialogOpen();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
+ Добавить
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
sx={STYLES.PADDING_0}
|
||||||
|
/>
|
||||||
|
<CardContent sx={STYLES.CARD_CONTENT}>
|
||||||
|
<Stack spacing={1}>
|
||||||
|
{tasks.rows
|
||||||
|
.filter(item => item.category === status.id)
|
||||||
|
.map((item, index) => (
|
||||||
|
<TaskCard
|
||||||
|
task={item}
|
||||||
|
avatar={extraData.accounts.find(a => a.agnAbbr === item.sSender).image}
|
||||||
|
index={index}
|
||||||
|
onReload={onReload}
|
||||||
|
key={item.id}
|
||||||
|
eventPoints={extraData.evPoints}
|
||||||
|
colorRule={settings.colorRule}
|
||||||
|
pointSettings={extraData.evPoints.find(p => p.point === status.code)}
|
||||||
|
onOpenNoteDialog={onNoteDialogOpen}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{placeholder}
|
||||||
|
</Stack>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств - Карточка статуса события
|
||||||
|
StatusCard.propTypes = {
|
||||||
|
tasks: PropTypes.object.isRequired,
|
||||||
|
status: PropTypes.object.isRequired,
|
||||||
|
settings: PropTypes.object.isRequired,
|
||||||
|
extraData: PropTypes.object.isRequired,
|
||||||
|
filtersType: PropTypes.string.isRequired,
|
||||||
|
isCardAvailable: PropTypes.func.isRequired,
|
||||||
|
onReload: PropTypes.func.isRequired,
|
||||||
|
onDragItemChange: PropTypes.func.isRequired,
|
||||||
|
onTaskDialogOpen: PropTypes.func.isRequired,
|
||||||
|
onNoteDialogOpen: PropTypes.func.isRequired,
|
||||||
|
placeholder: PropTypes.object.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
//--------------------
|
||||||
|
//Интерфейс компонента
|
||||||
|
//--------------------
|
||||||
|
|
||||||
|
export { StatusCard };
|
||||||
577
app/panels/clnt_task_board/components/task_card.js
Normal file
577
app/panels/clnt_task_board/components/task_card.js
Normal file
@ -0,0 +1,577 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - УДП - Доски задач
|
||||||
|
Компонент: Карточка события
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React, { useState, useContext, useCallback } from "react"; //Классы React
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
import { Draggable } from "react-beautiful-dnd"; //Работа с drag&drop
|
||||||
|
import { Card, CardHeader, Typography, IconButton, Icon, Box, Menu, MenuItem, CardContent, Avatar, Stack } from "@mui/material"; //Интерфейсные компоненты
|
||||||
|
import { TaskDialog } from "../task_dialog"; //Форма события
|
||||||
|
import { ApplicationСtx } from "../../../context/application"; //Контекст приложения
|
||||||
|
import { BackEndСtx } from "../../../context/backend"; //Контекст взаимодействия с сервером
|
||||||
|
import { MessagingСtx } from "../../../context/messaging"; //Контекст сообщений
|
||||||
|
import { EVENT_INDICATORS, indicatorColorRule, bgColorRule } from "../layouts"; //Дополнительная разметка и вёрстка клиентских элементов
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Стили
|
||||||
|
const STYLES = {
|
||||||
|
CONTAINER: { margin: "5px 0px", textAlign: "center" },
|
||||||
|
MENU_ITEM_DELIMITER: { borderBottom: "1px solid lightgrey" },
|
||||||
|
CARD: (indicatorClr, bgClr) => {
|
||||||
|
const i = indicatorClr ? { borderLeft: `solid ${indicatorClr}` } : null;
|
||||||
|
const bc = bgClr ? { backgroundColor: bgClr } : null;
|
||||||
|
return { ...i, ...bc };
|
||||||
|
},
|
||||||
|
CARD_HEADER_TITLE: {
|
||||||
|
padding: "4px",
|
||||||
|
width: "292px",
|
||||||
|
display: "-webkit-box",
|
||||||
|
hyphens: "auto",
|
||||||
|
WebkitBoxOrient: "vertical",
|
||||||
|
WebkitLineClamp: 2,
|
||||||
|
overflow: "hidden"
|
||||||
|
},
|
||||||
|
CARD_HEADER_DESC: { padding: 0, cursor: "pointer" },
|
||||||
|
CARD_CONTENT: { padding: "4px !important" },
|
||||||
|
CARD_CONTENT_BOX: { display: "flex", alignItems: "center" },
|
||||||
|
ACCOUNT_STACK: { alignItems: "center", marginLeft: "auto" },
|
||||||
|
SECONDARY_TEXT: {
|
||||||
|
color: "text.secondary",
|
||||||
|
fontSize: 14
|
||||||
|
},
|
||||||
|
ICON_COLOR: linked => {
|
||||||
|
return { color: theme => (linked ? EVENT_INDICATORS.LINKED : theme.palette.grey[500]) };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------
|
||||||
|
//Вспомогательные функции и компоненты
|
||||||
|
//------------------------------------
|
||||||
|
|
||||||
|
//Действия карточки события
|
||||||
|
const DataCellCardActions = ({
|
||||||
|
taskRn,
|
||||||
|
menuItems,
|
||||||
|
cardActions,
|
||||||
|
onMethodsMenuButtonClick,
|
||||||
|
onMethodsMenuClose,
|
||||||
|
onReload,
|
||||||
|
eventPoints,
|
||||||
|
pointSettings,
|
||||||
|
onOpenNoteDialog
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Box sx={STYLES.BOX_ROW}>
|
||||||
|
<IconButton id={`${taskRn}_menu_button`} aria-haspopup="true" onClick={onMethodsMenuButtonClick}>
|
||||||
|
<Icon>more_vert</Icon>
|
||||||
|
</IconButton>
|
||||||
|
<Menu id={`${taskRn}_menu`} anchorEl={cardActions.anchorMenuMethods} open={cardActions.openMethods} onClose={onMethodsMenuClose}>
|
||||||
|
{menuItems.map(action => {
|
||||||
|
if (action.visible)
|
||||||
|
return (
|
||||||
|
<MenuItem
|
||||||
|
sx={action.delimiter ? STYLES.MENU_ITEM_DELIMITER : {}}
|
||||||
|
key={`${taskRn}_${action.method}`}
|
||||||
|
onClick={() => {
|
||||||
|
if (onOpenNoteDialog && action.method === "TASK_STATE_CHANGE") {
|
||||||
|
action.func(taskRn, action.needReload ? onReload : null, eventPoints, onOpenNoteDialog);
|
||||||
|
} else if (onOpenNoteDialog && action.method === "TASK_SEND" && pointSettings.addNoteOnSend) {
|
||||||
|
onOpenNoteDialog(n => action.func(taskRn, action.needReload ? onReload : null, n));
|
||||||
|
} else {
|
||||||
|
//Выполняем действие
|
||||||
|
action.func(taskRn, action.needReload ? onReload : null);
|
||||||
|
}
|
||||||
|
//Закрываем меню
|
||||||
|
onMethodsMenuClose();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon>{action.icon}</Icon>
|
||||||
|
<Typography pl={1}>{action.name}</Typography>
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Menu>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств - Действия карточки события
|
||||||
|
DataCellCardActions.propTypes = {
|
||||||
|
taskRn: PropTypes.number.isRequired,
|
||||||
|
menuItems: PropTypes.array.isRequired,
|
||||||
|
cardActions: PropTypes.object.isRequired,
|
||||||
|
onMethodsMenuButtonClick: PropTypes.func.isRequired,
|
||||||
|
onMethodsMenuClose: PropTypes.func.isRequired,
|
||||||
|
onReload: PropTypes.func,
|
||||||
|
eventPoints: PropTypes.array,
|
||||||
|
pointSettings: PropTypes.object,
|
||||||
|
onOpenNoteDialog: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Карточка события
|
||||||
|
const TaskCard = ({ task, avatar, index, onReload, eventPoints, colorRule, pointSettings, onOpenNoteDialog }) => {
|
||||||
|
//Состояние диалога события
|
||||||
|
const [taskDialogOpen, setTaskDialogOpen] = useState(false);
|
||||||
|
|
||||||
|
//Состояние действий события
|
||||||
|
const [cardActions, setCardActions] = useState({ anchorMenuMethods: null, openMethods: false });
|
||||||
|
|
||||||
|
//Подключение к контексту взаимодействия с сервером
|
||||||
|
const { executeStored } = useContext(BackEndСtx);
|
||||||
|
|
||||||
|
//Подключение к контексту сообщений
|
||||||
|
const { showMsgWarn } = useContext(MessagingСtx);
|
||||||
|
|
||||||
|
//Подключение к контексту приложения
|
||||||
|
const { pOnlineShowDictionary } = useContext(ApplicationСtx);
|
||||||
|
|
||||||
|
//Подключение к контексту приложения
|
||||||
|
const { pOnlineShowDocument } = useContext(ApplicationСtx);
|
||||||
|
|
||||||
|
//Удаление контрагента
|
||||||
|
const deleteTask = useCallback(
|
||||||
|
async (nEvent, handleReload) => {
|
||||||
|
await executeStored({
|
||||||
|
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_DELETE",
|
||||||
|
args: { NCLNEVENTS: nEvent }
|
||||||
|
});
|
||||||
|
//Если требуется перезагрузить данные
|
||||||
|
if (handleReload) {
|
||||||
|
handleReload();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[executeStored]
|
||||||
|
);
|
||||||
|
|
||||||
|
//Возврат в предыдущую точку события
|
||||||
|
const returnTask = useCallback(
|
||||||
|
async (nEvent, handleReload) => {
|
||||||
|
await executeStored({
|
||||||
|
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_RETURN",
|
||||||
|
args: { NCLNEVENTS: nEvent }
|
||||||
|
});
|
||||||
|
//Если требуется перезагрузить данные
|
||||||
|
if (handleReload) {
|
||||||
|
handleReload();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[executeStored]
|
||||||
|
);
|
||||||
|
|
||||||
|
//По нажатию на открытие меню действий
|
||||||
|
const handleMethodsMenuButtonClick = event => {
|
||||||
|
setCardActions(pv => ({ ...pv, anchorMenuMethods: event.currentTarget, openMethods: true }));
|
||||||
|
};
|
||||||
|
|
||||||
|
//При закрытии меню
|
||||||
|
const handleMethodsMenuClose = () => {
|
||||||
|
setCardActions(pv => ({ ...pv, anchorMenuMethods: null, openMethods: false }));
|
||||||
|
};
|
||||||
|
|
||||||
|
//По нажатия действия "Редактировать"
|
||||||
|
const handleTaskEdit = () => {
|
||||||
|
setTaskDialogOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
//По нажатия действия "Редактировать в разделе"
|
||||||
|
const handleTaskEditClient = useCallback(
|
||||||
|
async nEvent => {
|
||||||
|
const data = await executeStored({
|
||||||
|
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_SELECT",
|
||||||
|
args: {
|
||||||
|
NCLNEVENTS: nEvent
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (data.NIDENT) {
|
||||||
|
pOnlineShowDictionary({
|
||||||
|
unitCode: "ClientEvents",
|
||||||
|
inputParameters: [{ name: "in_Ident", value: data.NIDENT }]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[executeStored, pOnlineShowDictionary]
|
||||||
|
);
|
||||||
|
|
||||||
|
//По нажатию действия "Удалить"
|
||||||
|
const handleTaskDelete = (nEvent, handleReload) => {
|
||||||
|
showMsgWarn("Удалить событие?", () => deleteTask(nEvent, handleReload));
|
||||||
|
};
|
||||||
|
|
||||||
|
//По нажатию действия "Выполнить возврат"
|
||||||
|
const handleTaskReturn = (nEvent, handleReload) => {
|
||||||
|
showMsgWarn("Выполнить возврат события в предыдущую точку?", () => returnTask(nEvent, handleReload));
|
||||||
|
};
|
||||||
|
|
||||||
|
//По нажатию действия "Примечания"
|
||||||
|
const handleEventNotesOpen = useCallback(
|
||||||
|
async nEvent => {
|
||||||
|
pOnlineShowDictionary({
|
||||||
|
unitCode: "ClientEventsNotes",
|
||||||
|
showMethod: "main",
|
||||||
|
inputParameters: [{ name: "in_PRN", value: nEvent }]
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[pOnlineShowDictionary]
|
||||||
|
);
|
||||||
|
|
||||||
|
//По нажатию действия "Присоединенные документы"
|
||||||
|
const handleFileLinksOpen = useCallback(
|
||||||
|
async nEvent => {
|
||||||
|
pOnlineShowDictionary({
|
||||||
|
unitCode: "FileLinks",
|
||||||
|
showMethod: "main_link",
|
||||||
|
inputParameters: [
|
||||||
|
{ name: "in_PRN", value: nEvent },
|
||||||
|
{ name: "in_UNITCODE", value: "ClientEvents" }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[pOnlineShowDictionary]
|
||||||
|
);
|
||||||
|
|
||||||
|
//По нажатию действия "Перейти"
|
||||||
|
const handleStateChange = useCallback(
|
||||||
|
async (nEvent, handleReload, evPoints, handleNote) => {
|
||||||
|
//Выполняем инициализацию параметров
|
||||||
|
const firstStep = await executeStored({
|
||||||
|
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE",
|
||||||
|
args: {
|
||||||
|
NSTEP: 1,
|
||||||
|
NEVENT: nEvent
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (firstStep) {
|
||||||
|
//Открываем раздел "Маршруты событий (точки перехода)" для выбора следующей точки
|
||||||
|
pOnlineShowDictionary({
|
||||||
|
unitCode: "EventRoutesPointsPasses",
|
||||||
|
showMethod: "main_passes",
|
||||||
|
inputParameters: [
|
||||||
|
{ name: "in_ENVTYPE_CODE", value: firstStep.SEVENT_TYPE },
|
||||||
|
{ name: "in_ENVSTAT_CODE", value: firstStep.SEVENT_STAT },
|
||||||
|
{ name: "in_POINT", value: firstStep.NPOINT }
|
||||||
|
],
|
||||||
|
callBack: async point => {
|
||||||
|
//Выполняем проверку необходимости выбора исполнителя
|
||||||
|
const secondStep = await executeStored({
|
||||||
|
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE",
|
||||||
|
args: {
|
||||||
|
NIDENT: firstStep.NIDENT,
|
||||||
|
NSTEP: 2,
|
||||||
|
NPASS: point.outParameters.out_RN
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const pointSettings = evPoints.find(ep => ep.point === point.outParameters.out_NEXT_POINT);
|
||||||
|
if (secondStep) {
|
||||||
|
//Если требуется выбрать получателя
|
||||||
|
if (secondStep.NSELECT_EXEC === 1) {
|
||||||
|
//Открываем раздел "Маршруты событий (исполнители в точках)" для выбора исполнителя
|
||||||
|
pOnlineShowDictionary({
|
||||||
|
unitCode: "EventRoutesPointExecuters",
|
||||||
|
showMethod: "executers",
|
||||||
|
inputParameters: [
|
||||||
|
{ name: "in_IDENT", value: firstStep.NIDENT },
|
||||||
|
{ name: "in_EVENT", value: nEvent },
|
||||||
|
{ name: "in_EVENT_TYPE", value: firstStep.SEVENT_TYPE },
|
||||||
|
{ name: "in_EVENT_STAT", value: firstStep.SEVENT_STAT },
|
||||||
|
{ name: "in_INIT_PERSON", value: firstStep.SINIT_PERSON },
|
||||||
|
{ name: "in_INIT_AUTHNAME", value: firstStep.SINIT_AUTHNAME },
|
||||||
|
{ name: "in_CLIENT_CLIENT", value: firstStep.SCLIENT_CLIENT },
|
||||||
|
{ name: "in_CLIENT_PERSON", value: firstStep.SCLIENT_PERSON }
|
||||||
|
],
|
||||||
|
callBack: async send => {
|
||||||
|
//Общие аргументы
|
||||||
|
const mainArgs = {
|
||||||
|
NIDENT: firstStep.NIDENT,
|
||||||
|
NSTEP: 3,
|
||||||
|
NEVENT: nEvent,
|
||||||
|
SEVENT_STAT: point.outParameters.out_NEXT_POINT,
|
||||||
|
SSEND_CLIENT: send.outParameters.out_CLIENT_CODE,
|
||||||
|
SSEND_DIVISION: send.outParameters.out_DIVISION_CODE,
|
||||||
|
SSEND_POST: send.outParameters.out_POST_CODE,
|
||||||
|
SSEND_PERFORM: send.outParameters.out_POST_IN_DIV_CODE,
|
||||||
|
SSEND_PERSON: send.outParameters.out_PERSON_CODE,
|
||||||
|
SSEND_STAFFGRP: send.outParameters.out_STAFFGRP_CODE,
|
||||||
|
SSEND_USER_GROUP: send.outParameters.out_USER_GROUP_CODE,
|
||||||
|
SSEND_USER_NAME: send.outParameters.out_USER_NAME,
|
||||||
|
NSEND_PREDEFINED_EXEC: send.outParameters.out_PREDEFINED_EXEC,
|
||||||
|
NSEND_PREDEFINED_PROC: send.outParameters.out_PREDEFINED_PROC
|
||||||
|
};
|
||||||
|
//Выполняем переход к выбранной точке с исполнителем
|
||||||
|
pointSettings.addNoteOnChst
|
||||||
|
? handleNote(async n => {
|
||||||
|
//Если требуется примечание
|
||||||
|
await executeStored({
|
||||||
|
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE",
|
||||||
|
args: {
|
||||||
|
...mainArgs,
|
||||||
|
...{ SNOTE_HEADER: n.header, SNOTE: n.text }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//Если требуется перезагрузить данные
|
||||||
|
if (handleReload) {
|
||||||
|
handleReload();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
: await executeStored({
|
||||||
|
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE",
|
||||||
|
args: mainArgs
|
||||||
|
});
|
||||||
|
//Если требуется перезагрузить данные
|
||||||
|
if (handleReload && !pointSettings.addNoteOnChst) {
|
||||||
|
handleReload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
//Общие аргументы
|
||||||
|
const mainArgs = {
|
||||||
|
NIDENT: firstStep.NIDENT,
|
||||||
|
NSTEP: 3,
|
||||||
|
NEVENT: nEvent,
|
||||||
|
SEVENT_STAT: point.outParameters.out_NEXT_POINT
|
||||||
|
};
|
||||||
|
//Выполняем переход к выбранной точке с предопределенным исполнителем
|
||||||
|
pointSettings.addNoteOnChst
|
||||||
|
? handleNote(async n => {
|
||||||
|
//Если требуется примечание
|
||||||
|
await executeStored({
|
||||||
|
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE",
|
||||||
|
args: {
|
||||||
|
...mainArgs,
|
||||||
|
...{ SNOTE_HEADER: n.header, SNOTE: n.text }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//Если требуется перезагрузить данные
|
||||||
|
if (handleReload) {
|
||||||
|
handleReload();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
: await executeStored({
|
||||||
|
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE",
|
||||||
|
args: mainArgs
|
||||||
|
});
|
||||||
|
//Если требуется перезагрузить данные
|
||||||
|
if (handleReload && !pointSettings.addNoteOnChst) {
|
||||||
|
handleReload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
[executeStored, pOnlineShowDictionary]
|
||||||
|
);
|
||||||
|
|
||||||
|
//Изменение статуса события
|
||||||
|
const handleSend = useCallback(
|
||||||
|
async (nEvent, handleReload, note = null) => {
|
||||||
|
//Выполняем инициализацию параметров
|
||||||
|
const firstStep = await executeStored({
|
||||||
|
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_SEND",
|
||||||
|
args: {
|
||||||
|
NSTEP: 1,
|
||||||
|
NEVENT: nEvent
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (firstStep) {
|
||||||
|
//Открываем раздел "Маршруты событий (исполнители в точках)" для выбора исполнителя
|
||||||
|
pOnlineShowDictionary({
|
||||||
|
unitCode: "EventRoutesPointExecuters",
|
||||||
|
showMethod: "executers",
|
||||||
|
inputParameters: [
|
||||||
|
{ name: "in_IDENT", value: firstStep.NIDENT },
|
||||||
|
{ name: "in_EVENT", value: nEvent },
|
||||||
|
{ name: "in_PERSON_CODE", value: firstStep.SSEND_PERSON },
|
||||||
|
{ name: "in_USER_NAME", value: firstStep.SSEND_USER_NAME },
|
||||||
|
{ name: "in_EVENT_TYPE", value: firstStep.SEVENT_TYPE },
|
||||||
|
{ name: "in_EVENT_STAT", value: firstStep.SEVENT_STAT },
|
||||||
|
{ name: "in_INIT_PERSON", value: firstStep.SINIT_PERSON },
|
||||||
|
{ name: "in_INIT_AUTHNAME", value: firstStep.SINIT_AUTHNAME },
|
||||||
|
{ name: "in_CLIENT_CLIENT", value: firstStep.SCLIENT_CLIENT },
|
||||||
|
{ name: "in_CLIENT_PERSON", value: firstStep.SCLIENT_PERSON }
|
||||||
|
],
|
||||||
|
callBack: async send => {
|
||||||
|
//Выполняем проверку необходимости выбора исполнителя
|
||||||
|
await executeStored({
|
||||||
|
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_SEND",
|
||||||
|
args: {
|
||||||
|
NIDENT: firstStep.NIDENT,
|
||||||
|
NSTEP: 2,
|
||||||
|
NEVENT: nEvent,
|
||||||
|
SSEND_CLIENT: send.outParameters.out_CLIENT_CODE,
|
||||||
|
SSEND_DIVISION: send.outParameters.out_DIVISION_CODE,
|
||||||
|
SSEND_POST: send.outParameters.out_POST_CODE,
|
||||||
|
SSEND_PERFORM: send.outParameters.out_POST_IN_DIV_CODE,
|
||||||
|
SSEND_PERSON: send.outParameters.out_PERSON_CODE,
|
||||||
|
SSEND_STAFFGRP: send.outParameters.out_STAFFGRP_CODE,
|
||||||
|
SSEND_USER_GROUP: send.outParameters.out_USER_GROUP_CODE,
|
||||||
|
SSEND_USER_NAME: send.outParameters.out_USER_NAME,
|
||||||
|
NSEND_PREDEFINED_EXEC: send.outParameters.out_PREDEFINED_EXEC,
|
||||||
|
NSEND_PREDEFINED_PROC: send.outParameters.out_PREDEFINED_PROC,
|
||||||
|
SNOTE_HEADER: note.text ? note.header : null,
|
||||||
|
SNOTE: note.text ? note.text : null
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//Если требуется перезагрузить данные
|
||||||
|
if (handleReload) {
|
||||||
|
handleReload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[executeStored, pOnlineShowDictionary]
|
||||||
|
);
|
||||||
|
|
||||||
|
const mItems = [
|
||||||
|
{ method: "EDIT", name: "Исправить", icon: "edit", visible: false, delimiter: false, needReload: false, func: handleTaskEdit },
|
||||||
|
{
|
||||||
|
method: "EDIT_CLIENT",
|
||||||
|
name: "Исправить в разделе",
|
||||||
|
icon: "edit_note",
|
||||||
|
visible: true,
|
||||||
|
delimiter: false,
|
||||||
|
needReload: false,
|
||||||
|
func: handleTaskEditClient
|
||||||
|
},
|
||||||
|
{ method: "DELETE", name: "Удалить", icon: "delete", visible: true, delimiter: true, needReload: true, func: handleTaskDelete },
|
||||||
|
{
|
||||||
|
method: "TASK_STATE_CHANGE",
|
||||||
|
name: "Перейти",
|
||||||
|
icon: "turn_right",
|
||||||
|
visible: true,
|
||||||
|
delimiter: false,
|
||||||
|
needReload: true,
|
||||||
|
func: handleStateChange
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: "TASK_RETURN",
|
||||||
|
name: "Выполнить возврат",
|
||||||
|
icon: "turn_left",
|
||||||
|
visible: true,
|
||||||
|
delimiter: false,
|
||||||
|
needReload: true,
|
||||||
|
func: handleTaskReturn
|
||||||
|
},
|
||||||
|
{ method: "TASK_SEND", name: "Направить", icon: "send", visible: true, delimiter: true, needReload: true, func: handleSend },
|
||||||
|
{ method: "NOTES", name: "Примечания", icon: "event_note", visible: true, delimiter: true, needReload: false, func: handleEventNotesOpen },
|
||||||
|
{
|
||||||
|
method: "FILE_LINKS",
|
||||||
|
name: "Присоединенные документы",
|
||||||
|
icon: "attach_file",
|
||||||
|
visible: true,
|
||||||
|
delimiter: false,
|
||||||
|
needReload: false,
|
||||||
|
func: handleFileLinksOpen
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
//Генерация содержимого
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
{taskDialogOpen ? (
|
||||||
|
<TaskDialog
|
||||||
|
taskRn={task.nrn}
|
||||||
|
taskType={task.stype}
|
||||||
|
editable={pointSettings.banUpdate ? false : true}
|
||||||
|
onReload={onReload}
|
||||||
|
onClose={() => {
|
||||||
|
setTaskDialogOpen(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
<Draggable draggableId={task.id.toString()} key={task.id} index={index}>
|
||||||
|
{provided => (
|
||||||
|
<Card
|
||||||
|
ref={provided.innerRef}
|
||||||
|
{...provided.draggableProps}
|
||||||
|
{...provided.dragHandleProps}
|
||||||
|
sx={STYLES.CARD(indicatorColorRule(task), colorRule.color ? bgColorRule(task, colorRule) : null)}
|
||||||
|
>
|
||||||
|
<CardHeader
|
||||||
|
title={
|
||||||
|
<Typography
|
||||||
|
className="task-info"
|
||||||
|
sx={STYLES.CARD_HEADER_TITLE}
|
||||||
|
lang="ru"
|
||||||
|
onClick={() => {
|
||||||
|
mItems.find(a => (a.method === "EDIT" ? a.func(task.nrn, a.needReload ? onReload : null) : null));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{task.sdescription}
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
sx={STYLES.CARD_HEADER_DESC}
|
||||||
|
action={
|
||||||
|
<DataCellCardActions
|
||||||
|
taskRn={task.nrn}
|
||||||
|
menuItems={mItems}
|
||||||
|
cardActions={cardActions}
|
||||||
|
onMethodsMenuButtonClick={handleMethodsMenuButtonClick}
|
||||||
|
onMethodsMenuClose={handleMethodsMenuClose}
|
||||||
|
onReload={onReload}
|
||||||
|
eventPoints={eventPoints}
|
||||||
|
pointSettings={pointSettings}
|
||||||
|
onOpenNoteDialog={onOpenNoteDialog}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<CardContent sx={STYLES.CARD_CONTENT}>
|
||||||
|
<Box sx={STYLES.CARD_CONTENT_BOX}>
|
||||||
|
<IconButton
|
||||||
|
title={task.nlinked_rn ? "Событие получено по статусной модели" : null}
|
||||||
|
onClick={
|
||||||
|
task.nlinked_rn ? () => pOnlineShowDocument({ unitCode: task.slinked_unit, document: task.nlinked_rn }) : null
|
||||||
|
}
|
||||||
|
sx={STYLES.ICON_COLOR(task.nlinked_rn)}
|
||||||
|
disabled={!task.nlinked_rn}
|
||||||
|
>
|
||||||
|
<Icon>assignment</Icon>
|
||||||
|
</IconButton>
|
||||||
|
<Typography sx={STYLES.SECONDARY_TEXT}>{task.name}</Typography>
|
||||||
|
{task.sSender ? (
|
||||||
|
<Stack direction="row" spacing={0.5} sx={STYLES.ACCOUNT_STACK}>
|
||||||
|
<Typography sx={STYLES.SECONDARY_TEXT}>{task.sSender}</Typography>
|
||||||
|
<Avatar src={avatar ? `data:image/png;base64,${avatar}` : null} />
|
||||||
|
</Stack>
|
||||||
|
) : null}
|
||||||
|
</Box>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
</Draggable>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств - Карточка события
|
||||||
|
TaskCard.propTypes = {
|
||||||
|
task: PropTypes.object.isRequired,
|
||||||
|
avatar: PropTypes.string,
|
||||||
|
index: PropTypes.number.isRequired,
|
||||||
|
onReload: PropTypes.func,
|
||||||
|
eventPoints: PropTypes.array,
|
||||||
|
colorRule: PropTypes.object,
|
||||||
|
pointSettings: PropTypes.object,
|
||||||
|
onOpenNoteDialog: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { TaskCard };
|
||||||
126
app/panels/clnt_task_board/components/task_card_settings.js
Normal file
126
app/panels/clnt_task_board/components/task_card_settings.js
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - УДП - Доски задач
|
||||||
|
Компонент: Диалог настройки карточки событий
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React, { useState } from "react"; //Классы React
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogTitle,
|
||||||
|
IconButton,
|
||||||
|
Icon,
|
||||||
|
DialogContent,
|
||||||
|
DialogActions,
|
||||||
|
Button,
|
||||||
|
Box,
|
||||||
|
FormControl,
|
||||||
|
InputLabel,
|
||||||
|
Select,
|
||||||
|
MenuItem
|
||||||
|
} from "@mui/material"; //Интерфейсные компоненты
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Стили
|
||||||
|
const STYLES = {
|
||||||
|
DIALOG_ACTIONS: { justifyContent: "end", paddingRight: "24px", paddingLeft: "24px" },
|
||||||
|
CLOSE_BUTTON: {
|
||||||
|
position: "absolute",
|
||||||
|
right: 8,
|
||||||
|
top: 8,
|
||||||
|
color: theme => theme.palette.grey[500]
|
||||||
|
},
|
||||||
|
BCKG_COLOR: backgroundColor => ({ backgroundColor: backgroundColor })
|
||||||
|
};
|
||||||
|
|
||||||
|
//---------------
|
||||||
|
//Тело компонента
|
||||||
|
//---------------
|
||||||
|
|
||||||
|
//Диалог настройки карточки событий
|
||||||
|
const TaskCardSettings = ({ statuses, availableClrs, onDialogOpen }) => {
|
||||||
|
//Собственное состояние
|
||||||
|
const [settings, setSettings] = useState({});
|
||||||
|
|
||||||
|
//Применение настройки статуса
|
||||||
|
const handleOk = settings => {
|
||||||
|
//Считываем статусы
|
||||||
|
let cloneS = statuses.slice();
|
||||||
|
//Изменяем статус у выбранного
|
||||||
|
cloneS[statuses.findIndex(x => x.id === settings.id)] = { ...settings };
|
||||||
|
setSettings(cloneS);
|
||||||
|
onDialogOpen();
|
||||||
|
};
|
||||||
|
|
||||||
|
//При изменении значения элемента
|
||||||
|
const handleSettingsItemChange = e => {
|
||||||
|
setSettings(pv => ({ ...pv, color: e.target.value }));
|
||||||
|
};
|
||||||
|
|
||||||
|
//Генерация содержимого
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Dialog open onClose={onDialogOpen} fullWidth maxWidth="sm">
|
||||||
|
<DialogTitle>Настройки</DialogTitle>
|
||||||
|
<IconButton aria-label="close" onClick={onDialogOpen} sx={STYLES.CLOSE_BUTTON}>
|
||||||
|
<Icon>close</Icon>
|
||||||
|
</IconButton>
|
||||||
|
<DialogContent>
|
||||||
|
<Box component="section" p={1}>
|
||||||
|
<FormControl fullWidth>
|
||||||
|
<InputLabel id="color-label">Цвет</InputLabel>
|
||||||
|
<Select
|
||||||
|
defaultValue={settings.color}
|
||||||
|
labelId="color-label"
|
||||||
|
id="color"
|
||||||
|
label="Цвет"
|
||||||
|
variant="standard"
|
||||||
|
sx={STYLES.BCKG_COLOR(settings.color)}
|
||||||
|
onChange={handleSettingsItemChange}
|
||||||
|
>
|
||||||
|
<MenuItem key={0} value={settings.color} sx={STYLES.BCKG_COLOR(settings.color)}>
|
||||||
|
{settings.color}
|
||||||
|
</MenuItem>
|
||||||
|
{availableClrs.map((clr, i) => {
|
||||||
|
return (
|
||||||
|
<MenuItem key={i + 1} value={clr} sx={STYLES.BCKG_COLOR(clr)}>
|
||||||
|
{clr}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</Box>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions sx={STYLES.DIALOG_ACTIONS}>
|
||||||
|
<Button variant="text" onClick={handleOk}>
|
||||||
|
Применить
|
||||||
|
</Button>
|
||||||
|
<Button variant="text" onClick={onDialogOpen}>
|
||||||
|
Отмена
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств - Диалог настройки карточки событий
|
||||||
|
TaskCardSettings.propTypes = {
|
||||||
|
statuses: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
availableClrs: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||||
|
onDialogOpen: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
//--------------------
|
||||||
|
//Интерфейс компонента
|
||||||
|
//--------------------
|
||||||
|
|
||||||
|
export { TaskCardSettings };
|
||||||
154
app/panels/clnt_task_board/components/task_form.js
Normal file
154
app/panels/clnt_task_board/components/task_form.js
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - УДП - Доски задач
|
||||||
|
Компонент панели: Форма события
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React, { useState, useEffect } from "react"; //Классы React
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
import { Box, Typography, Tabs, Tab, InputAdornment, IconButton, Icon } from "@mui/material"; //Интерфейсные компоненты
|
||||||
|
import { TaskFormTabInfo } from "./task_form_tab_info"; //Вкладка основной информации
|
||||||
|
import { TaskFormTabExecutor } from "./task_form_tab_executor"; //Вкладка информации об исполнителе
|
||||||
|
import { TaskFormTabProps } from "./task_form_tab_props"; //Вкладка информации со свойствами
|
||||||
|
import { useDocsProps } from "../hooks/task_dialog_hooks"; //Хук для получения свойств раздела "События"
|
||||||
|
import { DP_TYPE_PREFIX } from "../layouts"; //Префикс типа данных свойства
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Стили
|
||||||
|
const STYLES = {
|
||||||
|
CONTAINER: { margin: "5px 0px", textAlign: "center" }
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------
|
||||||
|
//Вспомогательные функции и компоненты
|
||||||
|
//------------------------------------
|
||||||
|
|
||||||
|
//Свойства вкладки
|
||||||
|
function a11yProps(index) {
|
||||||
|
return {
|
||||||
|
id: `simple-tab-${index}`,
|
||||||
|
"aria-controls": `simple-tabpanel-${index}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//Вкладка информации
|
||||||
|
function CustomTabPanel(props) {
|
||||||
|
const { children, value, index, ...other } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box role="tabpanel" hidden={value !== index} id={`simple-tabpanel-${index}`} aria-labelledby={`simple-tab-${index}`} {...other}>
|
||||||
|
{value === index && <Box pt={1}>{children}</Box>}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Контроль свойств - Вкладка информации
|
||||||
|
CustomTabPanel.propTypes = {
|
||||||
|
children: PropTypes.node,
|
||||||
|
index: PropTypes.number.isRequired,
|
||||||
|
value: PropTypes.number.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
//Формирование кнопки для открытия раздела
|
||||||
|
export const getInputProps = (onClick, disabled = false, icon = "list") => {
|
||||||
|
return {
|
||||||
|
endAdornment: (
|
||||||
|
<InputAdornment position="end">
|
||||||
|
<IconButton disabled={disabled} aria-label={`select`} onClick={onClick} edge="end">
|
||||||
|
<Icon>{icon}</Icon>
|
||||||
|
</IconButton>
|
||||||
|
</InputAdornment>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Форма события
|
||||||
|
const TaskForm = ({ task, taskType, setTask, editable, onEventNextNumbGet, onDPReady }) => {
|
||||||
|
//Состояние вкладки
|
||||||
|
const [tab, setTab] = useState(0);
|
||||||
|
|
||||||
|
//Состояние допустимых дополнительных свойств
|
||||||
|
const [docProps] = useDocsProps(taskType);
|
||||||
|
|
||||||
|
//При изменении вкладки
|
||||||
|
const handleTabChange = (e, newValue) => {
|
||||||
|
setTab(newValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
//При изменении поля
|
||||||
|
const handleFieldEdit = e => {
|
||||||
|
setTask(pv => ({
|
||||||
|
...pv,
|
||||||
|
[e.target.id]: e.target.value,
|
||||||
|
//Связанные значения, если меняется одно, то необходимо обнулить другое
|
||||||
|
...(e.target.id === "sclnt_clnperson" ? { sclnt_clnclients: "" } : {}),
|
||||||
|
...(e.target.id === "sclnt_clnclients" ? { sclnt_clnperson: "" } : {})
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
//При изменении свойства
|
||||||
|
const handlePropEdit = e => {
|
||||||
|
setTask(pv => ({
|
||||||
|
...pv,
|
||||||
|
docProps: { ...pv.docProps, [e.target.id]: e.target.value }
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
//При заполнении всех обязательных свойств
|
||||||
|
useEffect(() => {
|
||||||
|
let i = 0;
|
||||||
|
docProps.props.filter(dp => dp.require === true).map(prop => (!task.docProps[`${DP_TYPE_PREFIX[prop.format]}DP_${prop.rn}`] ? i++ : null));
|
||||||
|
docProps.loaded && i === 0 ? onDPReady(true) : onDPReady(false);
|
||||||
|
}, [docProps, onDPReady, task.docProps]);
|
||||||
|
|
||||||
|
//Генерация содержимого
|
||||||
|
return (
|
||||||
|
<Box sx={STYLES.CONTAINER}>
|
||||||
|
<Typography pb={1} variant="h6">
|
||||||
|
{task.nrn ? "Исправление события" : "Добавление события"}
|
||||||
|
</Typography>
|
||||||
|
<Tabs value={tab} onChange={handleTabChange} aria-label="tabs of values">
|
||||||
|
<Tab label="Событие" {...a11yProps(0)} />
|
||||||
|
<Tab label="Исполнитель" {...a11yProps(1)} />
|
||||||
|
{docProps.props.length > 0 ? <Tab label="Свойства" {...a11yProps(2)} /> : null}
|
||||||
|
</Tabs>
|
||||||
|
<CustomTabPanel value={tab} index={0}>
|
||||||
|
<TaskFormTabInfo task={task} editable={editable} onFieldEdit={handleFieldEdit} onEventNextNumbGet={onEventNextNumbGet} />
|
||||||
|
</CustomTabPanel>
|
||||||
|
<CustomTabPanel value={tab} index={1}>
|
||||||
|
<TaskFormTabExecutor task={task} onFieldEdit={handleFieldEdit} />
|
||||||
|
</CustomTabPanel>
|
||||||
|
{docProps.props.length > 0 ? (
|
||||||
|
<CustomTabPanel value={tab} index={2}>
|
||||||
|
<TaskFormTabProps task={task} taskType={taskType} docProps={docProps} onPropEdit={handlePropEdit} />
|
||||||
|
</CustomTabPanel>
|
||||||
|
) : null}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств - Форма события
|
||||||
|
TaskForm.propTypes = {
|
||||||
|
task: PropTypes.object.isRequired,
|
||||||
|
taskType: PropTypes.string.isRequired,
|
||||||
|
setTask: PropTypes.func.isRequired,
|
||||||
|
editable: PropTypes.bool.isRequired,
|
||||||
|
onEventNextNumbGet: PropTypes.func.isRequired,
|
||||||
|
onDPReady: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { TaskForm };
|
||||||
176
app/panels/clnt_task_board/components/task_form_tab_executor.js
Normal file
176
app/panels/clnt_task_board/components/task_form_tab_executor.js
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - УДП - Доски задач
|
||||||
|
Компонент: Вкладка информации об исполнителе
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React, { useContext, useCallback } from "react"; //Классы React
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
import { Box, TextField } from "@mui/material"; //Интерфейсные компоненты
|
||||||
|
import { ApplicationСtx } from "../../../context/application"; //Контекст приложения
|
||||||
|
import { getInputProps } from "./task_form"; //Формирование кнопки доступа к разделу
|
||||||
|
import dayjs from "dayjs"; //Работа с датами
|
||||||
|
import customParseFormat from "dayjs/plugin/customParseFormat"; //Настройка пользовательского формата даты
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Стили
|
||||||
|
const STYLES = {
|
||||||
|
BOX_WITH_LEGEND: { border: "1px solid #939393" },
|
||||||
|
BOX_SINGLE_COLUMN: { display: "flex", flexDirection: "column", gap: "10px" },
|
||||||
|
BOX_LEFT_ALIGN: { display: "flex", justifyContent: "flex-start" },
|
||||||
|
LEGEND: { textAlign: "left" }
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------
|
||||||
|
//Вспомогательные функции и компоненты
|
||||||
|
//------------------------------------
|
||||||
|
|
||||||
|
//Подключение настройки пользовательского формата даты
|
||||||
|
dayjs.extend(customParseFormat);
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Вкладка информации об исполнителе
|
||||||
|
const TaskFormTabExecutor = ({ task, onFieldEdit }) => {
|
||||||
|
//Подключение к контексту приложения
|
||||||
|
const { pOnlineShowDictionary } = useContext(ApplicationСtx);
|
||||||
|
|
||||||
|
//Отображение раздела "Сотрудники" для инициатора
|
||||||
|
const handleClientPersonOpen = useCallback(async () => {
|
||||||
|
pOnlineShowDictionary({
|
||||||
|
unitCode: "ClientPersons",
|
||||||
|
showMethod: "main",
|
||||||
|
inputParameters: [{ name: "in_CODE", value: task.sinit_clnperson }],
|
||||||
|
callBack: res => {
|
||||||
|
res.success
|
||||||
|
? onFieldEdit({
|
||||||
|
target: {
|
||||||
|
id: "sinit_clnperson",
|
||||||
|
value: res.outParameters.out_CODE
|
||||||
|
}
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [onFieldEdit, pOnlineShowDictionary, task.sinit_clnperson]);
|
||||||
|
|
||||||
|
//Генерация содержимого
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box sx={{ ...STYLES.BOX_WITH_LEGEND, ...STYLES.BOX_LEFT_ALIGN }} component="fieldset">
|
||||||
|
<legend style={STYLES.LEGEND}>Планирование</legend>
|
||||||
|
<TextField
|
||||||
|
id="dplan_date"
|
||||||
|
label="Начало работ"
|
||||||
|
InputLabelProps={{ shrink: true }}
|
||||||
|
type="datetime-local"
|
||||||
|
variant="standard"
|
||||||
|
value={task.dplan_date ? dayjs(task.dplan_date, "DD.MM.YYYY HH:mm").format("YYYY-MM-DD HH:mm") : ""}
|
||||||
|
onChange={onFieldEdit}
|
||||||
|
disabled={task.isUpdate}
|
||||||
|
></TextField>
|
||||||
|
</Box>
|
||||||
|
<Box sx={{ ...STYLES.BOX_WITH_LEGEND, ...STYLES.BOX_SINGLE_COLUMN }} component="fieldset">
|
||||||
|
<legend style={STYLES.LEGEND}>Инициатор</legend>
|
||||||
|
<TextField
|
||||||
|
id="sinit_clnperson"
|
||||||
|
label="Сотрудник"
|
||||||
|
value={task.sinit_clnperson}
|
||||||
|
variant="standard"
|
||||||
|
onChange={onFieldEdit}
|
||||||
|
disabled={task.isUpdate}
|
||||||
|
InputProps={getInputProps(() => handleClientPersonOpen(), task.isUpdate)}
|
||||||
|
></TextField>
|
||||||
|
<TextField
|
||||||
|
id="sinit_user"
|
||||||
|
label="Пользователь"
|
||||||
|
value={task.sinit_user}
|
||||||
|
variant="standard"
|
||||||
|
onChange={onFieldEdit}
|
||||||
|
disabled
|
||||||
|
></TextField>
|
||||||
|
<TextField
|
||||||
|
id="sinit_reason"
|
||||||
|
label="Основание"
|
||||||
|
value={task.sinit_reason}
|
||||||
|
variant="standard"
|
||||||
|
onChange={onFieldEdit}
|
||||||
|
disabled={task.isUpdate}
|
||||||
|
></TextField>
|
||||||
|
</Box>
|
||||||
|
<Box sx={{ ...STYLES.BOX_WITH_LEGEND, ...STYLES.BOX_SINGLE_COLUMN }} component="fieldset">
|
||||||
|
<legend style={STYLES.LEGEND}>Направить</legend>
|
||||||
|
<TextField
|
||||||
|
id="sto_company"
|
||||||
|
label="Организация"
|
||||||
|
value={task.sto_company}
|
||||||
|
variant="standard"
|
||||||
|
onChange={onFieldEdit}
|
||||||
|
disabled
|
||||||
|
></TextField>
|
||||||
|
<TextField
|
||||||
|
id="sto_department"
|
||||||
|
label="Подразделение"
|
||||||
|
value={task.sto_department}
|
||||||
|
variant="standard"
|
||||||
|
onChange={onFieldEdit}
|
||||||
|
disabled
|
||||||
|
></TextField>
|
||||||
|
<TextField id="sto_clnpost" label="Должность" value={task.sto_clnpost} variant="standard" onChange={onFieldEdit} disabled></TextField>
|
||||||
|
<TextField
|
||||||
|
id="sto_clnpsdep"
|
||||||
|
label="Штатная должность"
|
||||||
|
value={task.sto_clnpsdep}
|
||||||
|
variant="standard"
|
||||||
|
onChange={onFieldEdit}
|
||||||
|
disabled
|
||||||
|
></TextField>
|
||||||
|
<TextField
|
||||||
|
id="sto_clnperson"
|
||||||
|
label="Сотрудник"
|
||||||
|
value={task.sto_clnperson}
|
||||||
|
variant="standard"
|
||||||
|
onChange={onFieldEdit}
|
||||||
|
disabled
|
||||||
|
></TextField>
|
||||||
|
<TextField
|
||||||
|
id="sto_fcstaffgrp"
|
||||||
|
label="Нештатная должность"
|
||||||
|
value={task.sto_fcstaffgrp}
|
||||||
|
variant="standard"
|
||||||
|
onChange={onFieldEdit}
|
||||||
|
disabled
|
||||||
|
></TextField>
|
||||||
|
<TextField id="sto_user" label="Пользователь" value={task.sto_user} variant="standard" onChange={onFieldEdit} disabled></TextField>
|
||||||
|
<TextField
|
||||||
|
id="sto_usergrp"
|
||||||
|
label="Группа пользователей"
|
||||||
|
value={task.sto_usergrp}
|
||||||
|
variant="standard"
|
||||||
|
onChange={onFieldEdit}
|
||||||
|
disabled
|
||||||
|
></TextField>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств - Вкладка информации об исполнителе
|
||||||
|
TaskFormTabExecutor.propTypes = {
|
||||||
|
task: PropTypes.object.isRequired,
|
||||||
|
onFieldEdit: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { TaskFormTabExecutor };
|
||||||
225
app/panels/clnt_task_board/components/task_form_tab_info.js
Normal file
225
app/panels/clnt_task_board/components/task_form_tab_info.js
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - УДП - Доски задач
|
||||||
|
Компонент: Вкладка основной информации
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React, { useContext, useCallback } from "react"; //Классы React
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
import { Box, TextField } from "@mui/material"; //Интерфейсные компоненты
|
||||||
|
import { ApplicationСtx } from "../../../context/application"; //Контекст приложения
|
||||||
|
import { getInputProps } from "./task_form"; //Формирование кнопки доступа к разделу
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Стили
|
||||||
|
const STYLES = {
|
||||||
|
BOX_WITH_LEGEND: { border: "1px solid #939393" },
|
||||||
|
BOX_SINGLE_COLUMN: { display: "flex", flexDirection: "column", gap: "10px" },
|
||||||
|
BOX_FEW_COLUMNS: { display: "flex", flexWrap: "wrap", justifyContent: "space-between" },
|
||||||
|
LEGEND: { textAlign: "left" },
|
||||||
|
TEXT_FIELD: (widthVal, greyDisabled = false) => ({
|
||||||
|
margin: "4px",
|
||||||
|
...(widthVal ? { width: widthVal } : {}),
|
||||||
|
...(greyDisabled
|
||||||
|
? {
|
||||||
|
"& .MuiInputBase-input.Mui-disabled": {
|
||||||
|
WebkitTextFillColor: "rgba(0, 0, 0, 0.87)"
|
||||||
|
},
|
||||||
|
"& .MuiInputLabel-root.Mui-disabled": {
|
||||||
|
WebkitTextFillColor: "rgba(0, 0, 0, 0.6)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: {})
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Вкладка основной информации
|
||||||
|
const TaskFormTabInfo = ({ task, editable, onFieldEdit, onEventNextNumbGet }) => {
|
||||||
|
//Подключение к контексту приложения
|
||||||
|
const { pOnlineShowDictionary } = useContext(ApplicationСtx);
|
||||||
|
|
||||||
|
//Отображение раздела "Каталоги" для событий
|
||||||
|
const handleCrnOpen = useCallback(async () => {
|
||||||
|
pOnlineShowDictionary({
|
||||||
|
unitCode: "CatalogTree",
|
||||||
|
showMethod: "main",
|
||||||
|
inputParameters: [
|
||||||
|
{ name: "in_DOCNAME", value: "ClientEvents" },
|
||||||
|
{ name: "in_NAME", value: task.scrn }
|
||||||
|
],
|
||||||
|
callBack: res => {
|
||||||
|
res.success
|
||||||
|
? onFieldEdit({
|
||||||
|
target: {
|
||||||
|
id: "scrn",
|
||||||
|
value: res.outParameters.out_NAME
|
||||||
|
}
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [onFieldEdit, pOnlineShowDictionary, task.scrn]);
|
||||||
|
|
||||||
|
//Отображение раздела "Сотрудники" для клиента
|
||||||
|
const handleClientPersonOpen = useCallback(async () => {
|
||||||
|
pOnlineShowDictionary({
|
||||||
|
unitCode: "ClientPersons",
|
||||||
|
showMethod: "main",
|
||||||
|
inputParameters: [{ name: "in_CODE", value: task.sclnt_clnperson }],
|
||||||
|
callBack: res => {
|
||||||
|
res.success
|
||||||
|
? onFieldEdit({
|
||||||
|
target: {
|
||||||
|
id: "sclnt_clnperson",
|
||||||
|
value: res.outParameters.out_CODE
|
||||||
|
}
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [onFieldEdit, pOnlineShowDictionary, task.sclnt_clnperson]);
|
||||||
|
|
||||||
|
//Отображение раздела "Клиенты"
|
||||||
|
const handleClientClientsOpen = useCallback(async () => {
|
||||||
|
pOnlineShowDictionary({
|
||||||
|
unitCode: "ClientClients",
|
||||||
|
showMethod: "main",
|
||||||
|
inputParameters: [{ name: "in_CLIENT_CODE", value: task.sclnt_clnclients }],
|
||||||
|
callBack: res => {
|
||||||
|
res.success
|
||||||
|
? onFieldEdit({
|
||||||
|
target: {
|
||||||
|
id: "sclnt_clnclients",
|
||||||
|
value: res.outParameters.out_CLIENT_CODE
|
||||||
|
}
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [onFieldEdit, pOnlineShowDictionary, task.sclnt_clnclients]);
|
||||||
|
|
||||||
|
//Генерация содержимого
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box sx={STYLES.BOX_WITH_LEGEND} component="fieldset">
|
||||||
|
<legend style={STYLES.LEGEND}>Событие</legend>
|
||||||
|
<Box sx={STYLES.BOX_FEW_COLUMNS}>
|
||||||
|
<TextField
|
||||||
|
sx={STYLES.TEXT_FIELD()}
|
||||||
|
id="scrn"
|
||||||
|
label="Каталог"
|
||||||
|
fullWidth
|
||||||
|
value={task.scrn}
|
||||||
|
variant="standard"
|
||||||
|
onChange={onFieldEdit}
|
||||||
|
InputProps={getInputProps(handleCrnOpen)}
|
||||||
|
required
|
||||||
|
disabled={task.isUpdate}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
sx={STYLES.TEXT_FIELD("225px")}
|
||||||
|
id="sprefix"
|
||||||
|
label="Префикс"
|
||||||
|
value={task.sprefix}
|
||||||
|
variant="standard"
|
||||||
|
onChange={onFieldEdit}
|
||||||
|
required
|
||||||
|
disabled={task.isUpdate}
|
||||||
|
></TextField>
|
||||||
|
<TextField
|
||||||
|
sx={STYLES.TEXT_FIELD("225px")}
|
||||||
|
id="snumber"
|
||||||
|
label="Номер"
|
||||||
|
value={task.snumber}
|
||||||
|
variant="standard"
|
||||||
|
onChange={onFieldEdit}
|
||||||
|
required
|
||||||
|
disabled={task.isUpdate}
|
||||||
|
InputProps={getInputProps(onEventNextNumbGet, !task.sprefix || task.isUpdate, "refresh")}
|
||||||
|
></TextField>
|
||||||
|
<TextField
|
||||||
|
sx={STYLES.TEXT_FIELD("225px", !task.isUpdate)}
|
||||||
|
id="stype"
|
||||||
|
label="Тип"
|
||||||
|
value={task.stype}
|
||||||
|
variant="standard"
|
||||||
|
onChange={onFieldEdit}
|
||||||
|
disabled
|
||||||
|
required
|
||||||
|
></TextField>
|
||||||
|
<TextField
|
||||||
|
sx={STYLES.TEXT_FIELD("225px", !task.isUpdate)}
|
||||||
|
id="sstatus"
|
||||||
|
label="Статус"
|
||||||
|
value={task.sstatus}
|
||||||
|
variant="standard"
|
||||||
|
disabled
|
||||||
|
required
|
||||||
|
onChange={onFieldEdit}
|
||||||
|
></TextField>
|
||||||
|
<TextField
|
||||||
|
sx={STYLES.TEXT_FIELD()}
|
||||||
|
fullWidth
|
||||||
|
id="sdescription"
|
||||||
|
label="Описание"
|
||||||
|
value={task.sdescription}
|
||||||
|
variant="standard"
|
||||||
|
onChange={onFieldEdit}
|
||||||
|
disabled={!task.stype || !editable}
|
||||||
|
required
|
||||||
|
multiline
|
||||||
|
minRows={7}
|
||||||
|
maxRows={7}
|
||||||
|
></TextField>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
<Box sx={{ ...STYLES.BOX_WITH_LEGEND, ...STYLES.BOX_SINGLE_COLUMN }} component="fieldset">
|
||||||
|
<legend style={STYLES.LEGEND}>Клиент</legend>
|
||||||
|
<TextField
|
||||||
|
sx={STYLES.TEXT_FIELD()}
|
||||||
|
id="sclnt_clnclients"
|
||||||
|
label="Организация"
|
||||||
|
value={task.sclnt_clnclients}
|
||||||
|
variant="standard"
|
||||||
|
onChange={onFieldEdit}
|
||||||
|
disabled={!task.stype}
|
||||||
|
InputProps={getInputProps(handleClientClientsOpen, !task.stype)}
|
||||||
|
></TextField>
|
||||||
|
<TextField
|
||||||
|
sx={STYLES.TEXT_FIELD()}
|
||||||
|
id="sclnt_clnperson"
|
||||||
|
label="Сотрудник"
|
||||||
|
value={task.sclnt_clnperson}
|
||||||
|
variant="standard"
|
||||||
|
onChange={onFieldEdit}
|
||||||
|
disabled={!task.stype}
|
||||||
|
InputProps={getInputProps(() => handleClientPersonOpen(), !task.stype)}
|
||||||
|
></TextField>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств - Вкладка основной информации
|
||||||
|
TaskFormTabInfo.propTypes = {
|
||||||
|
task: PropTypes.object.isRequired,
|
||||||
|
editable: PropTypes.bool.isRequired,
|
||||||
|
onFieldEdit: PropTypes.func.isRequired,
|
||||||
|
onEventNextNumbGet: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { TaskFormTabInfo };
|
||||||
184
app/panels/clnt_task_board/components/task_form_tab_props.js
Normal file
184
app/panels/clnt_task_board/components/task_form_tab_props.js
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - УДП - Доски задач
|
||||||
|
Компонент: Вкладка информации со свойствами
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React, { useContext } from "react"; //Классы React
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
import { Box, TextField } from "@mui/material"; //Интерфейсные компоненты
|
||||||
|
import { getInputProps } from "./task_form"; //Формирование кнопки доступа к разделу
|
||||||
|
import { ApplicationСtx } from "../../../context/application"; //Контекст приложения
|
||||||
|
import dayjs from "dayjs"; //Работа с датами
|
||||||
|
import customParseFormat from "dayjs/plugin/customParseFormat"; //Настройка пользовательского формата даты
|
||||||
|
import { DP_DEFAULT_VALUE, DP_TYPE_PREFIX, DP_IN_VALUE, DP_RETURN_VALUE, validationError, timeFromSqlFormat } from "../layouts"; //Дополнительная разметка и вёрстка клиентских элементов
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Стили
|
||||||
|
const STYLES = {
|
||||||
|
BOX_WITH_LEGEND: { border: "1px solid #939393" },
|
||||||
|
LEGEND: { textAlign: "left" },
|
||||||
|
TEXT_FIELD: (widthVal, greyDisabled = false) => ({
|
||||||
|
margin: "4px",
|
||||||
|
...(widthVal ? { width: widthVal } : {}),
|
||||||
|
...(greyDisabled
|
||||||
|
? {
|
||||||
|
"& .MuiInputBase-input.Mui-disabled": {
|
||||||
|
WebkitTextFillColor: "rgba(0, 0, 0, 0.87)"
|
||||||
|
},
|
||||||
|
"& .MuiInputLabel-root.Mui-disabled": {
|
||||||
|
WebkitTextFillColor: "rgba(0, 0, 0, 0.6)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: {})
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------
|
||||||
|
//Вспомогательные функции и компоненты
|
||||||
|
//------------------------------------
|
||||||
|
|
||||||
|
//Подключение настройки пользовательского формата даты
|
||||||
|
dayjs.extend(customParseFormat);
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Вкладка информации со свойствами
|
||||||
|
const TaskFormTabProps = ({ task, docProps, onPropEdit }) => {
|
||||||
|
//Подключение к контексту приложения
|
||||||
|
const { pOnlineShowDictionary } = useContext(ApplicationСtx);
|
||||||
|
|
||||||
|
//Выбор из словаря или дополнительного словаря
|
||||||
|
const handleDict = async (dp, curValue = null) => {
|
||||||
|
dp.entryType === 1
|
||||||
|
? pOnlineShowDictionary({
|
||||||
|
unitCode: dp.unitcode,
|
||||||
|
showMethod: dp.showMethodCode,
|
||||||
|
inputParameters: dp.paramRn ? [{ name: dp.paramIn, value: curValue }] : null,
|
||||||
|
callBack: res => {
|
||||||
|
res.success
|
||||||
|
? onPropEdit({
|
||||||
|
target: {
|
||||||
|
id: `${DP_TYPE_PREFIX[dp.format]}DP_${dp.rn}`,
|
||||||
|
value: res.outParameters[dp.paramOut]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
: pOnlineShowDictionary({
|
||||||
|
unitCode: "ExtraDictionaries",
|
||||||
|
showMethod: "values",
|
||||||
|
inputParameters: [
|
||||||
|
{ name: "pos_rn", value: dp.extraDictRn },
|
||||||
|
{ name: DP_IN_VALUE[dp.format], value: curValue }
|
||||||
|
],
|
||||||
|
callBack: res => {
|
||||||
|
res.success
|
||||||
|
? onPropEdit({
|
||||||
|
target: {
|
||||||
|
id: `${DP_TYPE_PREFIX[dp.format]}DP_${dp.rn}`,
|
||||||
|
value: res.outParameters[DP_RETURN_VALUE[dp.format]]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//Инициализация дополнительного свойства
|
||||||
|
const initProp = prop => {
|
||||||
|
//Значение свойства
|
||||||
|
const value = task.docProps[`${DP_TYPE_PREFIX[prop.format]}DP_${prop.rn}`];
|
||||||
|
if (
|
||||||
|
(task.nrn || task.docProps[`${DP_TYPE_PREFIX[prop.format]}DP_${prop.rn}`]) &&
|
||||||
|
task.docProps[`${DP_TYPE_PREFIX[prop.format]}DP_${prop.rn}`] !== undefined
|
||||||
|
) {
|
||||||
|
//Строка или число
|
||||||
|
if (prop.format < 2) return prop.numPrecision ? String(value).replace(".", ",") : value;
|
||||||
|
//Дата
|
||||||
|
else if (prop.format === 2) {
|
||||||
|
//Дата без времени
|
||||||
|
if (prop.dataSubtype === 0) return dayjs(value).format("YYYY-MM-DD");
|
||||||
|
//Дата + время без секунд
|
||||||
|
else if (prop.dataSubtype === 1) return dayjs(value).format("YYYY-MM-DD HH:mm");
|
||||||
|
//Дата + время с секундами
|
||||||
|
else return dayjs(value).format("YYYY-MM-DD HH:mm:ss");
|
||||||
|
}
|
||||||
|
//Время
|
||||||
|
else {
|
||||||
|
return timeFromSqlFormat(value);
|
||||||
|
}
|
||||||
|
} else if (task.nrn) {
|
||||||
|
return "";
|
||||||
|
} else return prop[DP_DEFAULT_VALUE[prop.format]];
|
||||||
|
};
|
||||||
|
|
||||||
|
//Генерация содержимого
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box sx={STYLES.BOX_WITH_LEGEND} component="fieldset">
|
||||||
|
{docProps.props.map(dp => {
|
||||||
|
return dp.showInGrid ? (
|
||||||
|
<TextField
|
||||||
|
error={
|
||||||
|
!validationError(
|
||||||
|
task.docProps[`${DP_TYPE_PREFIX[dp.format]}DP_${dp.rn}`],
|
||||||
|
dp.format,
|
||||||
|
dp.numWidth,
|
||||||
|
dp.numPrecision,
|
||||||
|
dp.strWidth
|
||||||
|
)
|
||||||
|
}
|
||||||
|
key={dp.id}
|
||||||
|
sx={STYLES.TEXT_FIELD()}
|
||||||
|
id={`${DP_TYPE_PREFIX[dp.format]}DP_${dp.rn}`}
|
||||||
|
type={dp.format < 2 ? "string" : dp.format === 2 ? (dp.dataSubtype === 0 ? "date" : "datetime-local") : "time"}
|
||||||
|
label={dp.name}
|
||||||
|
fullWidth
|
||||||
|
value={initProp(dp)}
|
||||||
|
variant="standard"
|
||||||
|
onChange={onPropEdit}
|
||||||
|
inputProps={(dp.format === 2 && dp.dataSubtype === 2) || (dp.format === 3 && dp.dataSubtype === 1) ? { step: 1 } : {}}
|
||||||
|
InputProps={
|
||||||
|
dp.entryType > 0
|
||||||
|
? getInputProps(() => handleDict(dp, task.docProps[`${DP_TYPE_PREFIX[dp.format]}DP_${dp.rn}`]))
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
InputLabelProps={
|
||||||
|
dp.format < 2
|
||||||
|
? {}
|
||||||
|
: {
|
||||||
|
shrink: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
required={dp.require}
|
||||||
|
disabled={dp.readonly}
|
||||||
|
/>
|
||||||
|
) : null;
|
||||||
|
})}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств - Вкладка информации со свойствами
|
||||||
|
TaskFormTabProps.propTypes = {
|
||||||
|
task: PropTypes.object.isRequired,
|
||||||
|
docProps: PropTypes.object.isRequired,
|
||||||
|
onPropEdit: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { TaskFormTabProps };
|
||||||
204
app/panels/clnt_task_board/filter.js
Normal file
204
app/panels/clnt_task_board/filter.js
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - УДП - Доски задач
|
||||||
|
Компонент: Фильтр отбора
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React, { useState } from "react"; //Классы React
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
import { Chip, Stack, Icon, IconButton, Box, Menu, MenuItem, Typography } from "@mui/material"; //Интерфейсные компоненты
|
||||||
|
import { APP_STYLES } from "../../../app.styles"; //Типовые стили
|
||||||
|
import { FilterDialog } from "./components/filter_dialog.js"; //Диалог фильтра
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Стили
|
||||||
|
const STYLES = {
|
||||||
|
ICON_ORDERS: orders => {
|
||||||
|
return orders.length > 0 ? { color: "#1976d2" } : {};
|
||||||
|
},
|
||||||
|
ORDER_MENU: {
|
||||||
|
width: "260px"
|
||||||
|
},
|
||||||
|
ORDER_MENU_ITEM: {
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between"
|
||||||
|
},
|
||||||
|
FILTERS_STACK: {
|
||||||
|
paddingBottom: "5px",
|
||||||
|
overflowX: "auto",
|
||||||
|
...APP_STYLES.SCROLL
|
||||||
|
},
|
||||||
|
FILTER_MAXW: { maxWidth: "99vw" }
|
||||||
|
};
|
||||||
|
|
||||||
|
//--------------------------
|
||||||
|
//Вспомогательные компоненты
|
||||||
|
//--------------------------
|
||||||
|
|
||||||
|
//Элемент меню сортировок
|
||||||
|
const SortMenuItem = ({ item, caption, orders, onOrderChanged }) => {
|
||||||
|
//Кнопка сортировки
|
||||||
|
const order = orders.find(o => o.name == item);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MenuItem sx={STYLES.ORDER_MENU_ITEM} key={item} onClick={() => onOrderChanged(item)}>
|
||||||
|
<Typography>{caption}</Typography>
|
||||||
|
{order ? order.direction === "ASC" ? <Icon>arrow_upward</Icon> : <Icon>arrow_downward</Icon> : null}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств компонента - Элемент меню сортировок
|
||||||
|
SortMenuItem.propTypes = {
|
||||||
|
item: PropTypes.string.isRequired,
|
||||||
|
caption: PropTypes.string.isRequired,
|
||||||
|
orders: PropTypes.array,
|
||||||
|
onOrderChanged: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
//Меню сортировок
|
||||||
|
const SortMenu = ({ menuOrders, onOrdersMenuClose, orders, onOrderChanged }) => {
|
||||||
|
return (
|
||||||
|
<Menu
|
||||||
|
id={`sort_menu`}
|
||||||
|
anchorEl={menuOrders.anchorMenuOrders}
|
||||||
|
open={menuOrders.openOrders}
|
||||||
|
onClose={onOrdersMenuClose}
|
||||||
|
MenuListProps={{ sx: STYLES.ORDER_MENU }}
|
||||||
|
>
|
||||||
|
<SortMenuItem item={"DCHANGE_DATE"} caption={"Дата последнего изменения"} orders={orders} onOrderChanged={onOrderChanged} />
|
||||||
|
<SortMenuItem item={"DPLAN_DATE"} caption={"Дата начала работ"} orders={orders} onOrderChanged={onOrderChanged} />
|
||||||
|
<SortMenuItem item={"SPREF_NUMB"} caption={"Номер"} orders={orders} onOrderChanged={onOrderChanged} />
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств компонента - Меню сортировок
|
||||||
|
SortMenu.propTypes = {
|
||||||
|
menuOrders: PropTypes.object.isRequired,
|
||||||
|
onOrdersMenuClose: PropTypes.func.isRequired,
|
||||||
|
orders: PropTypes.array,
|
||||||
|
onOrderChanged: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
//Элемент фильтра
|
||||||
|
const FilterItem = ({ caption, value, onClick }) => {
|
||||||
|
//При нажатии на элемент
|
||||||
|
const handleClick = () => (onClick ? onClick() : null);
|
||||||
|
|
||||||
|
//Генерация содержимого
|
||||||
|
return (
|
||||||
|
<Chip
|
||||||
|
label={
|
||||||
|
<Stack direction={"row"} alignItems={"center"}>
|
||||||
|
<strong>{caption}</strong>
|
||||||
|
{value ? `:\u00A0${value}` : null}
|
||||||
|
</Stack>
|
||||||
|
}
|
||||||
|
variant="outlined"
|
||||||
|
onClick={handleClick}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств компонента - Элемент фильтра
|
||||||
|
FilterItem.propTypes = {
|
||||||
|
caption: PropTypes.string.isRequired,
|
||||||
|
value: PropTypes.any,
|
||||||
|
onClick: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
//---------------
|
||||||
|
//Тело компонента
|
||||||
|
//---------------
|
||||||
|
|
||||||
|
//Фильтр отбора
|
||||||
|
const Filter = ({
|
||||||
|
isFilterDialogOpen,
|
||||||
|
filter,
|
||||||
|
docs,
|
||||||
|
selectedDoc,
|
||||||
|
onFilterChange,
|
||||||
|
getDocLinks,
|
||||||
|
onFilterOpen,
|
||||||
|
onFilterClose,
|
||||||
|
onReload,
|
||||||
|
orders,
|
||||||
|
onOrderChanged,
|
||||||
|
...other
|
||||||
|
}) => {
|
||||||
|
//Состояние меню сортировки
|
||||||
|
const [menuOrders, setMenuOrders] = useState({ anchorMenuOrders: null, openOrders: false });
|
||||||
|
|
||||||
|
//По нажатию на открытие меню сортировки
|
||||||
|
const handleOrdersMenuButtonClick = event => {
|
||||||
|
setMenuOrders(pv => ({ ...pv, anchorMenuOrders: event.currentTarget, openOrders: true }));
|
||||||
|
};
|
||||||
|
|
||||||
|
//При закрытии меню
|
||||||
|
const handleOrdersMenuClose = () => {
|
||||||
|
setMenuOrders(pv => ({ ...pv, anchorMenuOrders: null, openOrders: false }));
|
||||||
|
};
|
||||||
|
|
||||||
|
//Генерация содержимого
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{isFilterDialogOpen ? (
|
||||||
|
<FilterDialog initial={filter} docs={docs} onFilterChange={onFilterChange} onFilterOpen={onFilterClose} getDocLinks={getDocLinks} />
|
||||||
|
) : null}
|
||||||
|
<Box {...other}>
|
||||||
|
<Stack direction="row" spacing={1} p={1} alignItems={"center"} sx={STYLES.FILTER_MAXW}>
|
||||||
|
<IconButton title="Обновить" onClick={onReload}>
|
||||||
|
<Icon>refresh</Icon>
|
||||||
|
</IconButton>
|
||||||
|
<IconButton title="Сортировать" sx={STYLES.ICON_ORDERS(orders)} onClick={handleOrdersMenuButtonClick}>
|
||||||
|
<Icon>sort</Icon>
|
||||||
|
</IconButton>
|
||||||
|
<IconButton title="Фильтр" onClick={onFilterOpen}>
|
||||||
|
<Icon>filter_alt</Icon>
|
||||||
|
</IconButton>
|
||||||
|
<Stack direction="row" spacing={1} alignItems={"center"} sx={STYLES.FILTERS_STACK}>
|
||||||
|
{filter.evState ? <FilterItem caption={"Состояние"} value={filter.evState} onClick={onFilterOpen} /> : null}
|
||||||
|
{filter.type ? <FilterItem caption={"Тип"} value={filter.type} onClick={onFilterOpen} /> : null}
|
||||||
|
{filter.catalog ? <FilterItem caption={"Каталог"} value={filter.catalog} onClick={onFilterOpen} /> : null}
|
||||||
|
{filter.wSubcatalogs ? <FilterItem caption={"Включая подкаталоги"} onClick={onFilterOpen} /> : null}
|
||||||
|
{filter.sendPerson ? <FilterItem caption={"Исполнитель"} value={filter.sendPerson} onClick={onFilterOpen} /> : null}
|
||||||
|
{filter.sendDivision ? <FilterItem caption={"Подразделение"} value={filter.sendDivision} onClick={onFilterOpen} /> : null}
|
||||||
|
{filter.sendUsrGrp ? <FilterItem caption={"Группа пользователей"} value={filter.sendUsrGrp} onClick={onFilterOpen} /> : null}
|
||||||
|
{filter.docLink && selectedDoc ? (
|
||||||
|
<FilterItem caption={"Учётный документ"} value={selectedDoc.descr} onClick={onFilterOpen} />
|
||||||
|
) : null}
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
<SortMenu menuOrders={menuOrders} onOrdersMenuClose={handleOrdersMenuClose} orders={orders} onOrderChanged={onOrderChanged} />
|
||||||
|
</Box>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств компонента - Фильтр отбора
|
||||||
|
Filter.propTypes = {
|
||||||
|
isFilterDialogOpen: PropTypes.bool.isRequired,
|
||||||
|
filter: PropTypes.object.isRequired,
|
||||||
|
docs: PropTypes.arrayOf(PropTypes.object),
|
||||||
|
selectedDoc: PropTypes.object,
|
||||||
|
onFilterChange: PropTypes.func.isRequired,
|
||||||
|
getDocLinks: PropTypes.func,
|
||||||
|
onFilterOpen: PropTypes.func.isRequired,
|
||||||
|
onFilterClose: PropTypes.func.isRequired,
|
||||||
|
onReload: PropTypes.func.isRequired,
|
||||||
|
orders: PropTypes.array,
|
||||||
|
onOrderChanged: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
//--------------------
|
||||||
|
//Интерфейс компонента
|
||||||
|
//--------------------
|
||||||
|
|
||||||
|
export { Filter };
|
||||||
126
app/panels/clnt_task_board/hooks/filter_hooks.js
Normal file
126
app/panels/clnt_task_board/hooks/filter_hooks.js
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - УДП - Доски задач
|
||||||
|
Пользовательские хуки: Хуки фильтра
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import { useState, useEffect, useCallback } from "react"; //Классы React
|
||||||
|
import { EVENT_STATES } from "../layouts"; //Перечисление состояний события
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Хук фильтра
|
||||||
|
const useFilters = filterOpen => {
|
||||||
|
//Состояние фильтра
|
||||||
|
const [filters, setFilters] = useState({
|
||||||
|
loaded: false,
|
||||||
|
isSetByUser: false,
|
||||||
|
needSave: false,
|
||||||
|
values: {
|
||||||
|
evState: EVENT_STATES[1],
|
||||||
|
type: "",
|
||||||
|
catalog: "",
|
||||||
|
crn: "",
|
||||||
|
wSubcatalogs: false,
|
||||||
|
sendPerson: "",
|
||||||
|
sendDivision: "",
|
||||||
|
sendUsrGrp: "",
|
||||||
|
docLink: ""
|
||||||
|
},
|
||||||
|
fArray: [
|
||||||
|
{ name: "NCLOSED", from: 0, to: 1 },
|
||||||
|
{ name: "SEVTYPE_CODE", from: "", to: "" },
|
||||||
|
{ name: "NCRN", from: "", to: "" },
|
||||||
|
{ name: "SSEND_PERSON", from: "", to: "" },
|
||||||
|
{ name: "SSEND_DIVISION", from: "", to: "" },
|
||||||
|
{ name: "SSEND_USRGRP", from: "", to: "" },
|
||||||
|
{ name: "NLINKED_RN", from: "", to: "" }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
//Изменение фильтра
|
||||||
|
const handleFiltersChange = filters => {
|
||||||
|
setFilterValues(filters);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Установить значение фильтра
|
||||||
|
const setFilterValues = (values, ns = true) => {
|
||||||
|
//Считываем массив фильтров
|
||||||
|
let filtersArr = filters.fArray.slice();
|
||||||
|
//Состояние
|
||||||
|
if (values.evState) {
|
||||||
|
if (values.evState === EVENT_STATES[0]) {
|
||||||
|
filtersArr.find(f => f.name === "NCLOSED").from = 0;
|
||||||
|
filtersArr.find(f => f.name === "NCLOSED").to = 1;
|
||||||
|
} else if (values.evState === EVENT_STATES[1]) {
|
||||||
|
filtersArr.find(f => f.name === "NCLOSED").from = 0;
|
||||||
|
filtersArr.find(f => f.name === "NCLOSED").to = 0;
|
||||||
|
} else if (values.evState === EVENT_STATES[2]) {
|
||||||
|
filtersArr.find(f => f.name === "NCLOSED").from = 1;
|
||||||
|
filtersArr.find(f => f.name === "NCLOSED").to = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Тип
|
||||||
|
filtersArr.find(f => f.name === "SEVTYPE_CODE").from = values.type ? values.type : null;
|
||||||
|
//Каталог
|
||||||
|
filtersArr.find(f => f.name === "NCRN").from = values.crn ? values.crn : null;
|
||||||
|
//Исполнитель
|
||||||
|
filtersArr.find(f => f.name === "SSEND_PERSON").from = values.sendPerson ? values.sendPerson : null;
|
||||||
|
//Подразделение
|
||||||
|
filtersArr.find(f => f.name === "SSEND_DIVISION").from = values.sendDivision ? values.sendDivision : null;
|
||||||
|
//Группа пользователей
|
||||||
|
filtersArr.find(f => f.name === "SSEND_USRGRP").from = values.sendUsrGrp ? values.sendUsrGrp : null;
|
||||||
|
//Учётный документ
|
||||||
|
filtersArr.find(f => f.name === "NLINKED_RN").from = values.docLink ? values.docLink : null;
|
||||||
|
//Устанавливаем фильтры
|
||||||
|
setFilters({ loaded: true, isSetByUser: true, needSave: ns, values: values, fArray: filtersArr });
|
||||||
|
};
|
||||||
|
|
||||||
|
//Загрузка значений фильтра из локального хранилища браузера
|
||||||
|
const loadLocalFilter = useCallback(async () => {
|
||||||
|
let vs = { ...filters.values };
|
||||||
|
if (localStorage.getItem("type")) {
|
||||||
|
Object.keys(vs).map(function (k) {
|
||||||
|
if (k === "wSubcatalogs") vs[k] = localStorage.getItem(k) === "true";
|
||||||
|
else k !== "docLink" ? (vs[k] = localStorage.getItem(k)) : null;
|
||||||
|
});
|
||||||
|
setFilterValues(vs, false);
|
||||||
|
filterOpen(false);
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
//При закрытии панели
|
||||||
|
useEffect(() => {
|
||||||
|
filters.needSave
|
||||||
|
? window.addEventListener("beforeunload", function () {
|
||||||
|
Object.keys(filters.values).map(function (k) {
|
||||||
|
k !== "docLink" ? localStorage.setItem(k, filters.values[k] ? filters.values[k] : "") : null;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
}, [filters.needSave, filters.values]);
|
||||||
|
|
||||||
|
//При отсутствии пользовательских настроек фильтра
|
||||||
|
useEffect(() => {
|
||||||
|
if (!filters.isSetByUser) filterOpen(true);
|
||||||
|
}, [filterOpen, filters.isSetByUser]);
|
||||||
|
|
||||||
|
//При подключении к странице
|
||||||
|
useEffect(() => {
|
||||||
|
localStorage.length ? loadLocalFilter() : null;
|
||||||
|
}, [loadLocalFilter]);
|
||||||
|
|
||||||
|
return [filters, handleFiltersChange];
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { useFilters };
|
||||||
531
app/panels/clnt_task_board/hooks/hooks.js
Normal file
531
app/panels/clnt_task_board/hooks/hooks.js
Normal file
@ -0,0 +1,531 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - УДП - Доски задач
|
||||||
|
Пользовательские хуки: Хуки основных данных
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import { useState, useContext, useEffect, useCallback } from "react"; //Классы React
|
||||||
|
import { ApplicationСtx } from "../../../context/application"; //Контекст приложения
|
||||||
|
import { BackEndСtx } from "../../../context/backend"; //Контекст взаимодействия с сервером
|
||||||
|
import { object2Base64XML } from "../../../core/utils"; //Вспомогательные функции
|
||||||
|
import { arrayFormer, randomColor } from "../layouts"; //Формировщик массива и формирование случайного цвета
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Хук получения событий
|
||||||
|
const useTasks = ({ filters, orders, extraData, getDocLinks }) => {
|
||||||
|
//Состояние событий
|
||||||
|
const [tasks, setTasks] = useState({
|
||||||
|
groupsLoaded: false,
|
||||||
|
tasksLoaded: false,
|
||||||
|
rows: [],
|
||||||
|
statuses: [],
|
||||||
|
reload: true
|
||||||
|
});
|
||||||
|
|
||||||
|
//Подключение к контексту взаимодействия с сервером
|
||||||
|
const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
|
||||||
|
|
||||||
|
//Подключение к контексту приложения
|
||||||
|
const { pOnlineShowDictionary } = useContext(ApplicationСtx);
|
||||||
|
|
||||||
|
//Надо обновить данные
|
||||||
|
const needUpdateTasks = () => setTasks(pv => ({ ...pv, groupsLoaded: false, tasksLoaded: false, reload: true }));
|
||||||
|
|
||||||
|
//Инициализация параметров события
|
||||||
|
const initTask = (id, gp, task) => {
|
||||||
|
//Добавление дополнительных свойств
|
||||||
|
let newDocProps = {};
|
||||||
|
Object.keys(task)
|
||||||
|
.filter(k => k.includes("DP_"))
|
||||||
|
.map(dp => (newDocProps = { ...newDocProps, [dp]: task[dp] }));
|
||||||
|
return {
|
||||||
|
id: id,
|
||||||
|
name: task.SPREF_NUMB,
|
||||||
|
category: gp,
|
||||||
|
nrn: task.NRN,
|
||||||
|
scrn: "",
|
||||||
|
sprefix: task.SEVPREF,
|
||||||
|
snumber: task.SEVNUMB,
|
||||||
|
stype: task.SEVTYPE_CODE,
|
||||||
|
sstatus: task.SEVSTAT_NAME,
|
||||||
|
sdescription: task.SEVDESCR,
|
||||||
|
sclnt_clnclients: "",
|
||||||
|
sclnt_clnperson: "",
|
||||||
|
dchange_date: task.DCHANGE_DATE,
|
||||||
|
dstart_date: task.DREG_DATE,
|
||||||
|
dexpire_date: task.DEXPIRE_DATE,
|
||||||
|
dplan_date: task.DPLAN_DATE,
|
||||||
|
sinit_clnperson: task.SINIT_PERSON,
|
||||||
|
sinit_user: "",
|
||||||
|
sinit_reason: "",
|
||||||
|
//SEND_CLIENT
|
||||||
|
sto_company: "",
|
||||||
|
//SEND_DIVISION
|
||||||
|
sto_department: task.SSEND_DIVISION,
|
||||||
|
//SEND_POST
|
||||||
|
sto_clnpost: "",
|
||||||
|
//SEND_PERFORM
|
||||||
|
sto_clnpsdep: "",
|
||||||
|
//SEND_PERSON
|
||||||
|
sto_clnperson: task.SSEND_PERSON,
|
||||||
|
//SEND_STAFFGRP
|
||||||
|
sto_fcstaffgrp: "",
|
||||||
|
//SEND_USER_AUTHID
|
||||||
|
sto_user: "",
|
||||||
|
//SEND_USER_GROUP
|
||||||
|
sto_usergrp: task.SSEND_USRGRP,
|
||||||
|
sSender: task.SSENDER,
|
||||||
|
scurrent_user: "",
|
||||||
|
slinked_unit: task.SLINKED_UNIT,
|
||||||
|
nlinked_rn: task.NLINKED_RN,
|
||||||
|
docProps: newDocProps
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
//Изменение статуса события (переносом)
|
||||||
|
const handleStateChange = useCallback(
|
||||||
|
async (nEvent, sNextStat, note) => {
|
||||||
|
try {
|
||||||
|
//Выполняем инициализацию параметров
|
||||||
|
const firstStep = await executeStored({
|
||||||
|
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE",
|
||||||
|
args: {
|
||||||
|
NSTEP: 1,
|
||||||
|
NEVENT: nEvent,
|
||||||
|
SNEXT_STAT: sNextStat
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (firstStep) {
|
||||||
|
//Если требуется выбрать получателя
|
||||||
|
if (firstStep.NSELECT_EXEC === 1) {
|
||||||
|
//Открываем раздел "Маршруты событий (исполнители в точках)" для выбора исполнителя
|
||||||
|
pOnlineShowDictionary({
|
||||||
|
unitCode: "EventRoutesPointExecuters",
|
||||||
|
showMethod: "executers",
|
||||||
|
inputParameters: [
|
||||||
|
{ name: "in_IDENT", value: firstStep.NIDENT },
|
||||||
|
{ name: "in_EVENT", value: nEvent },
|
||||||
|
{ name: "in_EVENT_TYPE", value: firstStep.SEVENT_TYPE },
|
||||||
|
{ name: "in_EVENT_STAT", value: firstStep.SEVENT_STAT },
|
||||||
|
{ name: "in_INIT_PERSON", value: firstStep.SINIT_PERSON },
|
||||||
|
{ name: "in_INIT_AUTHNAME", value: firstStep.SINIT_AUTHNAME },
|
||||||
|
{ name: "in_CLIENT_CLIENT", value: firstStep.SCLIENT_CLIENT },
|
||||||
|
{ name: "in_CLIENT_PERSON", value: firstStep.SCLIENT_PERSON }
|
||||||
|
],
|
||||||
|
callBack: async send => {
|
||||||
|
//Общие аргументы
|
||||||
|
const mainArgs = {
|
||||||
|
NIDENT: firstStep.NIDENT,
|
||||||
|
NSTEP: 3,
|
||||||
|
NEVENT: nEvent,
|
||||||
|
SEVENT_STAT: firstStep.SEVENT_STAT,
|
||||||
|
SSEND_CLIENT: send.outParameters.out_CLIENT_CODE,
|
||||||
|
SSEND_DIVISION: send.outParameters.out_DIVISION_CODE,
|
||||||
|
SSEND_POST: send.outParameters.out_POST_CODE,
|
||||||
|
SSEND_PERFORM: send.outParameters.out_POST_IN_DIV_CODE,
|
||||||
|
SSEND_PERSON: send.outParameters.out_PERSON_CODE,
|
||||||
|
SSEND_STAFFGRP: send.outParameters.out_STAFFGRP_CODE,
|
||||||
|
SSEND_USER_GROUP: send.outParameters.out_USER_GROUP_CODE,
|
||||||
|
SSEND_USER_NAME: send.outParameters.out_USER_NAME,
|
||||||
|
NSEND_PREDEFINED_EXEC: send.outParameters.out_PREDEFINED_EXEC,
|
||||||
|
NSEND_PREDEFINED_PROC: send.outParameters.out_PREDEFINED_PROC
|
||||||
|
};
|
||||||
|
//Выполняем переход к выбранной точке с исполнителем
|
||||||
|
await executeStored({
|
||||||
|
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE",
|
||||||
|
args: note
|
||||||
|
? {
|
||||||
|
...mainArgs,
|
||||||
|
SNOTE_HEADER: note.header,
|
||||||
|
SNOTE: note.text
|
||||||
|
}
|
||||||
|
: mainArgs
|
||||||
|
});
|
||||||
|
//Необходимо обновить данные
|
||||||
|
setTasks(pv => ({ ...pv, reload: true }));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
//Общие аргументы
|
||||||
|
const mainArgs = { NIDENT: firstStep.NIDENT, NSTEP: 3, NEVENT: nEvent, SEVENT_STAT: firstStep.SEVENT_STAT };
|
||||||
|
//Выполняем переход к выбранной точке с предопределенным исполнителем
|
||||||
|
await executeStored({
|
||||||
|
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE",
|
||||||
|
args: note
|
||||||
|
? {
|
||||||
|
...mainArgs,
|
||||||
|
...{ SNOTE_HEADER: note.header, SNOTE: note.text }
|
||||||
|
}
|
||||||
|
: mainArgs
|
||||||
|
});
|
||||||
|
//Необходимо обновить данные
|
||||||
|
setTasks(pv => ({ ...pv, reload: true }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
//Необходимо обновить данные
|
||||||
|
setTasks(pv => ({ ...pv, reload: true }));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[executeStored, pOnlineShowDictionary]
|
||||||
|
);
|
||||||
|
|
||||||
|
//Надо обновить события
|
||||||
|
const handleReload = useCallback(() => {
|
||||||
|
setTasks(pv => ({ ...pv, reload: true }));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
//Взаимодействие с событием (через перенос)
|
||||||
|
const onDragEnd = useCallback(
|
||||||
|
(result, eventPoints, openNoteDialog) => {
|
||||||
|
//Определяем нужные параметры
|
||||||
|
const { source, destination } = result;
|
||||||
|
//Если путь не указан
|
||||||
|
if (!destination) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//Если происходит изменение статуса
|
||||||
|
if (destination.droppableId !== source.droppableId) {
|
||||||
|
//Считываем строку, у которой изменяется статус
|
||||||
|
let row = tasks.rows.find(f => f.id === parseInt(result.draggableId));
|
||||||
|
//Формируем события с учетом изменения
|
||||||
|
let rows = tasks.rows.map(task =>
|
||||||
|
task.id === parseInt(result.draggableId)
|
||||||
|
? {
|
||||||
|
...task,
|
||||||
|
category: parseInt(result.destination.droppableId)
|
||||||
|
}
|
||||||
|
: task
|
||||||
|
);
|
||||||
|
//Мнемокод точки назначения
|
||||||
|
const destCode = tasks.statuses.find(s => s.id == destination.droppableId).code;
|
||||||
|
//Получение настройки точки назначения
|
||||||
|
const pointSettings = eventPoints.find(ep => ep.point === destCode);
|
||||||
|
//Если необходимо примечание при переходе
|
||||||
|
if (pointSettings.addNoteOnChst) {
|
||||||
|
//Изменяем статус события с добавлением примечания
|
||||||
|
openNoteDialog(n => {
|
||||||
|
setTasks(pv => ({ ...pv, rows: [...rows] }));
|
||||||
|
handleStateChange(row.nrn, destCode, n);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//Изменяем статус события
|
||||||
|
else {
|
||||||
|
//Переинициализируем строки с учетом изменений (для визуального отображения)
|
||||||
|
setTasks(pv => ({ ...pv, rows: [...rows] }));
|
||||||
|
handleStateChange(row.nrn, destCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[handleStateChange, tasks.rows, tasks.statuses]
|
||||||
|
);
|
||||||
|
|
||||||
|
//Перезагружать при изменении фильтра или сортировки
|
||||||
|
useEffect(() => {
|
||||||
|
filters || orders ? handleReload() : null;
|
||||||
|
}, [filters, orders, handleReload]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
//Считывание данных с учетом фильтрации
|
||||||
|
let getTasks = async () => {
|
||||||
|
const ds = await executeStored({
|
||||||
|
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_DATASET",
|
||||||
|
args: {
|
||||||
|
CFILTERS: { VALUE: object2Base64XML(filters.fArray, { arrayNodeName: "filters" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
|
||||||
|
CORDERS: { VALUE: object2Base64XML(orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
|
||||||
|
NINCLUDE_DEF: tasks.tasksLoaded ? 0 : 1
|
||||||
|
},
|
||||||
|
respArg: "COUT"
|
||||||
|
});
|
||||||
|
//Инициализируем статусы и события
|
||||||
|
let newGroups = [];
|
||||||
|
let newRows = [];
|
||||||
|
//Если статусы есть
|
||||||
|
if (ds.XDATA_GRID.groups) {
|
||||||
|
//Формируем структуру статусов
|
||||||
|
arrayFormer(ds.XDATA_GRID.groups).map((group, i) => {
|
||||||
|
newGroups.push({ id: i, code: group.name, name: group.caption, color: randomColor(i + 1) });
|
||||||
|
});
|
||||||
|
//Если есть события
|
||||||
|
if (ds.XDATA_GRID.rows) {
|
||||||
|
//Формируем структуру событий
|
||||||
|
arrayFormer(ds.XDATA_GRID.rows).map((task, i) => {
|
||||||
|
newRows.push(initTask(i, newGroups.find(x => x.name === task.groupName).id, task));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Возвращаем информацию
|
||||||
|
return { statuses: [...newGroups], rows: [...newRows] };
|
||||||
|
};
|
||||||
|
//Считывание данных
|
||||||
|
let getData = async () => {
|
||||||
|
//Считываем информацию о задачах
|
||||||
|
let eventTasks = await getTasks();
|
||||||
|
//Добавление описания точки маршрута
|
||||||
|
eventTasks.statuses.map(s => (s["pointDescr"] = extraData.evPoints.find(ep => ep.point === s.code).pointDescr));
|
||||||
|
//Загружаем данные
|
||||||
|
setTasks(pv => ({
|
||||||
|
...pv,
|
||||||
|
groupsLoaded: true,
|
||||||
|
tasksLoaded: true,
|
||||||
|
statuses: eventTasks.statuses,
|
||||||
|
rows: eventTasks.rows,
|
||||||
|
reload: false
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
//Если необходимо загрузить данные и указан тип событий и загружены все необходимые вспомогательные данные
|
||||||
|
if (
|
||||||
|
tasks.reload &&
|
||||||
|
filters.loaded &&
|
||||||
|
filters.values.type &&
|
||||||
|
extraData.dataLoaded &&
|
||||||
|
filters.values.type === extraData.typeLoaded &&
|
||||||
|
extraData.evPoints.length
|
||||||
|
) {
|
||||||
|
//Загружаем данные
|
||||||
|
getData();
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
tasks.reload,
|
||||||
|
filters.values.type,
|
||||||
|
filters.fArray,
|
||||||
|
orders,
|
||||||
|
executeStored,
|
||||||
|
SERV_DATA_TYPE_CLOB,
|
||||||
|
tasks.tasksLoaded,
|
||||||
|
extraData,
|
||||||
|
getDocLinks,
|
||||||
|
filters.loaded
|
||||||
|
]);
|
||||||
|
|
||||||
|
return [tasks, handleReload, onDragEnd, needUpdateTasks];
|
||||||
|
};
|
||||||
|
|
||||||
|
//Хук дополнительных данных
|
||||||
|
const useExtraData = filtersType => {
|
||||||
|
//Состояние дополнительных данных
|
||||||
|
const [extraData, setExtraData] = useState({
|
||||||
|
dataLoaded: false,
|
||||||
|
typeLoaded: "",
|
||||||
|
evRoutes: [],
|
||||||
|
evPoints: [],
|
||||||
|
noteTypes: [],
|
||||||
|
docLinks: [],
|
||||||
|
accounts: []
|
||||||
|
});
|
||||||
|
|
||||||
|
//Подключение к контексту взаимодействия с сервером
|
||||||
|
const { executeStored } = useContext(BackEndСtx);
|
||||||
|
|
||||||
|
//Надо обновить данные
|
||||||
|
const needUpdateExtraData = () => setExtraData(pv => ({ ...pv, dataLoaded: false }));
|
||||||
|
|
||||||
|
//Получение учётных документов
|
||||||
|
const getDocLinks = useCallback(
|
||||||
|
async (type = filtersType) => {
|
||||||
|
const data = await executeStored({
|
||||||
|
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_DOCLINKS",
|
||||||
|
args: {
|
||||||
|
SEVNTYPE_CODE: type
|
||||||
|
},
|
||||||
|
respArg: "COUT"
|
||||||
|
});
|
||||||
|
//Инициализируем учётные документы
|
||||||
|
let newDocLinks = [];
|
||||||
|
//Если найдены учётные документы
|
||||||
|
if (data.XDOCLINKS) {
|
||||||
|
arrayFormer(data.XDOCLINKS).map(d => {
|
||||||
|
newDocLinks.push({ id: d.NRN, descr: d.SDESCR });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//Возвращаем сформированные учётные документы
|
||||||
|
return newDocLinks;
|
||||||
|
},
|
||||||
|
[executeStored, filtersType]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
//Получение вспомогательных данных
|
||||||
|
const getExtraData = async () => {
|
||||||
|
const data = await executeStored({
|
||||||
|
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_INFO_BY_CODE",
|
||||||
|
args: {
|
||||||
|
SEVNTYPE_CODE: filtersType
|
||||||
|
},
|
||||||
|
respArg: "COUT"
|
||||||
|
});
|
||||||
|
//Инициализируем маршруты событий
|
||||||
|
let newRoutes = data.XEVROUTES
|
||||||
|
? arrayFormer(data.XEVROUTES).reduce((prev, cur) => {
|
||||||
|
prev.push({ src: cur.SSOURCE, dest: cur.SDESTINATION });
|
||||||
|
return prev;
|
||||||
|
}, [])
|
||||||
|
: [];
|
||||||
|
//Инициализируем точки событий
|
||||||
|
let newPoints = data.XEVPOINTS
|
||||||
|
? arrayFormer(data.XEVPOINTS).reduce((prev, cur) => {
|
||||||
|
prev.push({
|
||||||
|
point: cur.SEVPOINT,
|
||||||
|
pointDescr: cur.SEVPOINT_DESCR,
|
||||||
|
addNoteOnChst: cur.ADDNOTE_ONCHST,
|
||||||
|
addNoteOnSend: cur.ADDNOTE_ONSEND,
|
||||||
|
banUpdate: cur.BAN_UPDATE
|
||||||
|
});
|
||||||
|
return prev;
|
||||||
|
}, [])
|
||||||
|
: [];
|
||||||
|
//Инициализируем типы заголовков примечаний
|
||||||
|
let newNoteTypes = data.XNOTETYPES
|
||||||
|
? arrayFormer(data.XNOTETYPES).reduce((prev, cur) => {
|
||||||
|
prev.push(cur.SNAME);
|
||||||
|
return prev;
|
||||||
|
}, [])
|
||||||
|
: [];
|
||||||
|
//Инициализируем пользователей
|
||||||
|
let newAccounts = data.XACCOUNTS
|
||||||
|
? arrayFormer(data.XACCOUNTS).reduce((prev, cur) => {
|
||||||
|
prev.push({
|
||||||
|
agnAbbr: cur.SAGNABBR,
|
||||||
|
image: cur.BIMAGE
|
||||||
|
});
|
||||||
|
return prev;
|
||||||
|
}, [])
|
||||||
|
: [];
|
||||||
|
//Загружаем учётные документы
|
||||||
|
let docLinks = await getDocLinks(filtersType);
|
||||||
|
//Возвращаем результат
|
||||||
|
return {
|
||||||
|
dataLoaded: true,
|
||||||
|
typeLoaded: filtersType,
|
||||||
|
evRoutes: [...newRoutes],
|
||||||
|
evPoints: [...newPoints],
|
||||||
|
noteTypes: [...newNoteTypes],
|
||||||
|
docLinks: [...docLinks],
|
||||||
|
accounts: [...newAccounts]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
//Считывание данных
|
||||||
|
const updateExtraData = async () => {
|
||||||
|
let newExtraData = await getExtraData();
|
||||||
|
setExtraData(newExtraData);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Если указан тип событий
|
||||||
|
if (filtersType) {
|
||||||
|
//Загружаем дополнительные данные
|
||||||
|
if (!extraData.typeLoaded || filtersType !== extraData.typeLoaded) {
|
||||||
|
//setExtraData(pv => ({ ...pv, dataLoaded: false }));
|
||||||
|
updateExtraData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [executeStored, extraData.typeLoaded, filtersType, getDocLinks]);
|
||||||
|
|
||||||
|
return [extraData, getDocLinks, needUpdateExtraData];
|
||||||
|
};
|
||||||
|
|
||||||
|
//Хук для получения пользовательских настроек разметки
|
||||||
|
const useColorRules = () => {
|
||||||
|
//Собственное состояние
|
||||||
|
const [clrRules, setClrRules] = useState({ loaded: false, rules: [] });
|
||||||
|
|
||||||
|
//Подключение к контексту взаимодействия с сервером
|
||||||
|
const { executeStored } = useContext(BackEndСtx);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let getClrRules = async () => {
|
||||||
|
//Получаем массив пользовательских настроек
|
||||||
|
const data = await executeStored({
|
||||||
|
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_DP_RULES_GET",
|
||||||
|
respArg: "COUT"
|
||||||
|
});
|
||||||
|
//Инициализируем
|
||||||
|
let newClrRules = [];
|
||||||
|
if (data) {
|
||||||
|
//Формируем структуру настройки
|
||||||
|
arrayFormer(data.XRULES).map((cr, i) => {
|
||||||
|
let fromV;
|
||||||
|
let toV;
|
||||||
|
if (cr.STYPE === "number") {
|
||||||
|
fromV = cr.NFROM;
|
||||||
|
toV = cr.NTO;
|
||||||
|
} else if (cr.STYPE === "string") {
|
||||||
|
fromV = cr.SFROM;
|
||||||
|
toV = cr.STO;
|
||||||
|
} else {
|
||||||
|
fromV = cr.DFROM;
|
||||||
|
toV = cr.DTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
newClrRules.push({ id: i, fieldCode: cr.SFIELD, propName: cr.SDP_NAME, color: cr.SCOLOR, vType: cr.STYPE, from: fromV, to: toV });
|
||||||
|
});
|
||||||
|
setClrRules({ loaded: true, rules: [...newClrRules] });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!clrRules.loaded) getClrRules();
|
||||||
|
}, [clrRules.loaded, executeStored]);
|
||||||
|
|
||||||
|
return [clrRules];
|
||||||
|
};
|
||||||
|
|
||||||
|
//Хук дополнительных настроек
|
||||||
|
const useSettings = statuses => {
|
||||||
|
//Собственное состояние
|
||||||
|
const [settings, setSettings] = useState({
|
||||||
|
statusesSort: {
|
||||||
|
sorted: false,
|
||||||
|
attr: localStorage.getItem("settingsSortAttr") ? localStorage.getItem("settingsSortAttr") : "name",
|
||||||
|
dest: localStorage.getItem("settingsSortDest") ? localStorage.getItem("settingsSortDest") : "asc",
|
||||||
|
statuses: []
|
||||||
|
},
|
||||||
|
colorRule: localStorage.getItem("settingsColorRule") ? JSON.parse(localStorage.getItem("settingsColorRule")) : {}
|
||||||
|
});
|
||||||
|
|
||||||
|
//Изменение состояния после сортировки
|
||||||
|
const afterSort = statuses => setSettings(pv => ({ ...pv, statusesSort: { ...pv.statusesSort, sorted: true, statuses: statuses } }));
|
||||||
|
|
||||||
|
//При закрытии диалога дополнительных настроек по кнопке ОК
|
||||||
|
const handleSettingsChange = s => {
|
||||||
|
setSettings({ ...s, statusesSort: { ...s.statusesSort, sorted: false } });
|
||||||
|
};
|
||||||
|
|
||||||
|
//При получении новых настроек сортировки
|
||||||
|
useEffect(() => {
|
||||||
|
//Подгрузкка новых статусов
|
||||||
|
if (statuses.length > 0 && statuses.toString() !== settings.statusesSort.statuses.toString() && settings.statusesSort.sorted)
|
||||||
|
setSettings(pv => ({ ...pv, statusesSort: { ...pv.statusesSort, sorted: false } }));
|
||||||
|
//Сортировка
|
||||||
|
if (statuses.length > 0 && !settings.statusesSort.sorted) {
|
||||||
|
const attr = settings.statusesSort.attr;
|
||||||
|
const d = settings.statusesSort.dest;
|
||||||
|
let s = statuses;
|
||||||
|
s.sort((a, b) => (d === "asc" ? a[attr].localeCompare(b[attr]) : b[attr].localeCompare(a[attr])));
|
||||||
|
afterSort(s);
|
||||||
|
}
|
||||||
|
}, [settings.statusesSort.attr, settings.statusesSort.dest, settings.statusesSort.sorted, settings.statusesSort.statuses, statuses]);
|
||||||
|
|
||||||
|
//Сохранение при закрытии панели
|
||||||
|
useEffect(() => {
|
||||||
|
window.addEventListener("beforeunload", function () {
|
||||||
|
localStorage.setItem("settingsSortAttr", settings.statusesSort.attr);
|
||||||
|
localStorage.setItem("settingsSortDest", settings.statusesSort.dest);
|
||||||
|
localStorage.setItem("settingsColorRule", JSON.stringify(settings.colorRule));
|
||||||
|
});
|
||||||
|
}, [settings.colorRule, settings.statusesSort.attr, settings.statusesSort.dest]);
|
||||||
|
|
||||||
|
return [settings, handleSettingsChange];
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { useExtraData, useTasks, useSettings, useColorRules };
|
||||||
327
app/panels/clnt_task_board/hooks/task_dialog_hooks.js
Normal file
327
app/panels/clnt_task_board/hooks/task_dialog_hooks.js
Normal file
@ -0,0 +1,327 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - УДП - Доски задач
|
||||||
|
Пользовательские хуки: Хуки диалога события
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import { useState, useContext, useEffect, useCallback } from "react"; //Классы React
|
||||||
|
import { BackEndСtx } from "../../../context/backend"; //Контекст взаимодействия с сервером
|
||||||
|
import { object2Base64XML } from "../../../core/utils"; //Вспомогательные функции
|
||||||
|
import { arrayFormer } from "../layouts"; //Формировщик массива
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Хук для события
|
||||||
|
const useClientEvent = (taskRn, taskType = "", taskStatus = "") => {
|
||||||
|
//Собственное состояние
|
||||||
|
const [task, setTask] = useState({
|
||||||
|
init: true,
|
||||||
|
nrn: taskRn,
|
||||||
|
scrn: "",
|
||||||
|
sprefix: "",
|
||||||
|
snumber: "",
|
||||||
|
stype: taskType,
|
||||||
|
sstatus: taskStatus,
|
||||||
|
sdescription: "",
|
||||||
|
sclnt_clnclients: "",
|
||||||
|
sclnt_clnperson: "",
|
||||||
|
dstart_date: "",
|
||||||
|
sinit_clnperson: "",
|
||||||
|
sinit_user: "",
|
||||||
|
sinit_reason: "",
|
||||||
|
sto_company: "",
|
||||||
|
sto_department: "",
|
||||||
|
sto_clnpost: "",
|
||||||
|
sto_clnpsdep: "",
|
||||||
|
sto_clnperson: "",
|
||||||
|
sto_fcstaffgrp: "",
|
||||||
|
sto_user: "",
|
||||||
|
sto_usergrp: "",
|
||||||
|
scurrent_user: "",
|
||||||
|
isUpdate: false,
|
||||||
|
insertDisabled: true,
|
||||||
|
updateDisabled: true,
|
||||||
|
docProps: {}
|
||||||
|
});
|
||||||
|
|
||||||
|
//Подключение к контексту взаимодействия с сервером
|
||||||
|
const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
|
||||||
|
|
||||||
|
const initEventType = useCallback(async () => {
|
||||||
|
//Считываем параметры исходя из типа события
|
||||||
|
const data = await executeStored({
|
||||||
|
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVNTYPES_INIT",
|
||||||
|
args: {
|
||||||
|
SEVENT_TYPE: task.stype,
|
||||||
|
SCURRENT_PREF: task.sprefix
|
||||||
|
},
|
||||||
|
tagValueProcessor: () => undefined
|
||||||
|
});
|
||||||
|
if (data) {
|
||||||
|
setTask(pv => ({
|
||||||
|
...pv,
|
||||||
|
sprefix: data.SPREF,
|
||||||
|
snumber: data.SNUMB
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}, [task.sprefix, task.stype, executeStored]);
|
||||||
|
|
||||||
|
//Считывание следующего номера события
|
||||||
|
const getEventNextNumb = useCallback(async () => {
|
||||||
|
const data = await executeStored({
|
||||||
|
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_NEXTNUMB_GET",
|
||||||
|
args: {
|
||||||
|
SPREFIX: task.sprefix
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (data) {
|
||||||
|
setTask(pv => ({
|
||||||
|
...pv,
|
||||||
|
snumber: data.SEVENT_NUMB
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}, [executeStored, task.sprefix]);
|
||||||
|
|
||||||
|
//Добавление события
|
||||||
|
const insertEvent = useCallback(
|
||||||
|
async callBack => {
|
||||||
|
await executeStored({
|
||||||
|
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_INSERT",
|
||||||
|
args: {
|
||||||
|
SCRN: task.scrn,
|
||||||
|
SPREF: task.sprefix,
|
||||||
|
SNUMB: task.snumber,
|
||||||
|
STYPE: task.stype,
|
||||||
|
SSTATUS: task.sstatus,
|
||||||
|
SPLAN_DATE: task.dplan_date,
|
||||||
|
SINIT_PERSON: task.sinit_clnperson,
|
||||||
|
SCLIENT_CLIENT: task.sclnt_clnclients,
|
||||||
|
SCLIENT_PERSON: task.sclnt_clnperson,
|
||||||
|
SDESCRIPTION: task.sdescription,
|
||||||
|
SREASON: task.sinit_reason,
|
||||||
|
CPROPS: {
|
||||||
|
VALUE: object2Base64XML(
|
||||||
|
[
|
||||||
|
Object.fromEntries(
|
||||||
|
Object.entries(task.docProps)
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
.filter(([_, v]) => v != (null || ""))
|
||||||
|
)
|
||||||
|
],
|
||||||
|
{
|
||||||
|
arrayNodeName: "props"
|
||||||
|
}
|
||||||
|
),
|
||||||
|
SDATA_TYPE: SERV_DATA_TYPE_CLOB
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
callBack();
|
||||||
|
},
|
||||||
|
[
|
||||||
|
executeStored,
|
||||||
|
task.scrn,
|
||||||
|
task.sprefix,
|
||||||
|
task.snumber,
|
||||||
|
task.stype,
|
||||||
|
task.sstatus,
|
||||||
|
task.dplan_date,
|
||||||
|
task.sinit_clnperson,
|
||||||
|
task.sclnt_clnclients,
|
||||||
|
task.sclnt_clnperson,
|
||||||
|
task.sdescription,
|
||||||
|
task.sinit_reason,
|
||||||
|
task.docProps,
|
||||||
|
SERV_DATA_TYPE_CLOB
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
//Исправление события
|
||||||
|
const updateEvent = useCallback(
|
||||||
|
async callBack => {
|
||||||
|
await executeStored({
|
||||||
|
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_UPDATE",
|
||||||
|
args: {
|
||||||
|
NCLNEVENTS: task.nrn,
|
||||||
|
SCLIENT_CLIENT: task.sclnt_clnclients,
|
||||||
|
SCLIENT_PERSON: task.sclnt_clnperson,
|
||||||
|
SDESCRIPTION: task.sdescription,
|
||||||
|
CPROPS: {
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
VALUE: object2Base64XML([Object.fromEntries(Object.entries(task.docProps).filter(([_, v]) => v != (null || "")))], {
|
||||||
|
arrayNodeName: "props"
|
||||||
|
}),
|
||||||
|
SDATA_TYPE: SERV_DATA_TYPE_CLOB
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
callBack();
|
||||||
|
},
|
||||||
|
[SERV_DATA_TYPE_CLOB, executeStored, task.docProps, task.nrn, task.sclnt_clnclients, task.sclnt_clnperson, task.sdescription]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (task.init) {
|
||||||
|
if (taskRn) {
|
||||||
|
//Считывание параметров события
|
||||||
|
const readEvent = async () => {
|
||||||
|
const data = await executeStored({
|
||||||
|
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_GET",
|
||||||
|
args: {
|
||||||
|
NCLNEVENTS: task.nrn
|
||||||
|
},
|
||||||
|
respArg: "COUT"
|
||||||
|
});
|
||||||
|
let newDocProps = {};
|
||||||
|
Object.keys(data.XEVENT)
|
||||||
|
.filter(k => k.includes("DP_"))
|
||||||
|
.map(dp => (newDocProps = { ...newDocProps, [dp]: data.XEVENT[dp] }));
|
||||||
|
setTask(pv => ({
|
||||||
|
...pv,
|
||||||
|
scrn: data.XEVENT.SCRN,
|
||||||
|
sprefix: data.XEVENT.SPREF,
|
||||||
|
snumber: data.XEVENT.SNUMB,
|
||||||
|
stype: data.XEVENT.STYPE,
|
||||||
|
sstatus: data.XEVENT.SSTATUS,
|
||||||
|
sdescription: data.XEVENT.SDESCRIPTION,
|
||||||
|
sclnt_clnclients: data.XEVENT.SCLIENT_CLIENT,
|
||||||
|
sclnt_clnperson: data.XEVENT.SCLIENT_PERSON,
|
||||||
|
dplan_date: data.XEVENT.SPLAN_DATE,
|
||||||
|
sinit_clnperson: data.XEVENT.SINIT_PERSON,
|
||||||
|
sinit_user: data.XEVENT.SINIT_AUTHID,
|
||||||
|
sinit_reason: data.XEVENT.SREASON,
|
||||||
|
sto_company: data.XEVENT.SSEND_CLIENT,
|
||||||
|
sto_department: data.XEVENT.SSEND_DIVISION,
|
||||||
|
sto_clnpost: data.XEVENT.SSEND_POST,
|
||||||
|
sto_clnpsdep: data.XEVENT.SSEND_PERFORM,
|
||||||
|
sto_clnperson: data.XEVENT.SSEND_PERSON,
|
||||||
|
sto_fcstaffgrp: data.XEVENT.SSEND_STAFFGRP,
|
||||||
|
sto_user: data.XEVENT.SSEND_USER_NAME,
|
||||||
|
sto_usergrp: data.XEVENT.SSEND_USER_GROUP,
|
||||||
|
scurrent_user: data.XEVENT.SINIT_AUTHID,
|
||||||
|
isUpdate: true,
|
||||||
|
init: false,
|
||||||
|
docProps: newDocProps
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
//Инициализация параметров события
|
||||||
|
readEvent();
|
||||||
|
} else {
|
||||||
|
//Считывание изначальных параметров события
|
||||||
|
const initEvent = async () => {
|
||||||
|
const data = await executeStored({
|
||||||
|
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_INIT",
|
||||||
|
args: {}
|
||||||
|
});
|
||||||
|
if (data) {
|
||||||
|
setTask(pv => ({
|
||||||
|
...pv,
|
||||||
|
sprefix: data.SPREF,
|
||||||
|
snumber: data.SNUMB,
|
||||||
|
scurrent_user: data.SINIT_AUTHNAME,
|
||||||
|
sinit_clnperson: data.SINIT_PERSON,
|
||||||
|
sinit_user: !data.SINIT_PERSON ? data.SINIT_AUTHNAME : "",
|
||||||
|
init: false
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//Инициализация изначальных параметров события
|
||||||
|
initEvent();
|
||||||
|
initEventType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!task.init) {
|
||||||
|
setTask(pv => ({ ...pv, sinit_user: !task.sinit_clnperson ? task.scurrent_user : "" }));
|
||||||
|
}
|
||||||
|
}, [executeStored, task.init, task.nrn, task.stype, task.scurrent_user, task.sinit_clnperson, taskRn, initEventType]);
|
||||||
|
|
||||||
|
//Проверка доступности действия
|
||||||
|
useEffect(() => {
|
||||||
|
setTask(pv => ({
|
||||||
|
...pv,
|
||||||
|
insertDisabled:
|
||||||
|
!task.scrn ||
|
||||||
|
!task.sprefix ||
|
||||||
|
!task.snumber ||
|
||||||
|
!task.stype ||
|
||||||
|
!task.sstatus ||
|
||||||
|
!task.sdescription ||
|
||||||
|
(!task.sinit_clnperson && !task.sinit_user),
|
||||||
|
updateDisabled: !task.sdescription
|
||||||
|
}));
|
||||||
|
}, [task.scrn, task.sdescription, task.sinit_clnperson, task.sinit_user, task.snumber, task.sprefix, task.sstatus, task.stype]);
|
||||||
|
|
||||||
|
return [task, setTask, insertEvent, updateEvent, getEventNextNumb];
|
||||||
|
};
|
||||||
|
|
||||||
|
//Хук для получения свойств раздела "События"
|
||||||
|
const useDocsProps = taskType => {
|
||||||
|
//Собственное состояние
|
||||||
|
const [docProps, setDocsProps] = useState({ loaded: false, props: [] });
|
||||||
|
|
||||||
|
//Подключение к контексту взаимодействия с сервером
|
||||||
|
const { executeStored } = useContext(BackEndСtx);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let getDocsProps = async () => {
|
||||||
|
//Получаем массив пользовательских настроек
|
||||||
|
const data = await executeStored({
|
||||||
|
stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_PROPS_GET",
|
||||||
|
args: { SEVNTYPE_CODE: taskType },
|
||||||
|
respArg: "COUT"
|
||||||
|
});
|
||||||
|
//Инициализируем
|
||||||
|
let newDocProps = [];
|
||||||
|
if (data) {
|
||||||
|
//Формируем структуру настройки
|
||||||
|
arrayFormer(data.XPROPS).map((dp, i) => {
|
||||||
|
newDocProps.push({
|
||||||
|
id: i,
|
||||||
|
rn: dp.NRN,
|
||||||
|
name: dp.SNAME,
|
||||||
|
readonly: dp.READONLY,
|
||||||
|
checkValue: dp.CHECK_VALUE,
|
||||||
|
checkUnique: dp.CHECK_UNIQUE,
|
||||||
|
require: dp.REQUIRE,
|
||||||
|
duplicateValue: dp.DUPLICATE_VALUE,
|
||||||
|
accessMode: dp.NACCESS_MODE,
|
||||||
|
showInGrid: dp.SHOW_IN_GRID,
|
||||||
|
defaultStr: dp.SDEFAULT_STR,
|
||||||
|
defaultNum: dp.NDEFAULT_NUM,
|
||||||
|
defaultDate: dp.DDEFAULT_DATE,
|
||||||
|
entryType: dp.NENTRY_TYPE,
|
||||||
|
format: dp.NFORMAT,
|
||||||
|
dataSubtype: dp.NDATA_SUBTYPE,
|
||||||
|
numWidth: dp.NNUM_WIDTH,
|
||||||
|
numPrecision: dp.NNUM_PRECISION,
|
||||||
|
strWidth: dp.NSTR_WIDTH,
|
||||||
|
unitcode: dp.SUNITCODE,
|
||||||
|
paramRn: dp.NPARAM_RN,
|
||||||
|
paramIn: dp.SPARAM_IN_CODE,
|
||||||
|
paramOut: dp.SPARAM_OUT_CODE,
|
||||||
|
showMethodRn: dp.NSHOW_METHOD_RN,
|
||||||
|
showMethodCode: dp.SMETHOD_CODE,
|
||||||
|
extraDictRn: dp.NEXTRA_DICT_RN,
|
||||||
|
initRn: dp.NINIT_RN
|
||||||
|
});
|
||||||
|
});
|
||||||
|
setDocsProps({ loaded: true, props: [...newDocProps] });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!docProps.loaded) getDocsProps();
|
||||||
|
}, [docProps.loaded, executeStored, taskType]);
|
||||||
|
|
||||||
|
return [docProps];
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { useClientEvent, useDocsProps };
|
||||||
16
app/panels/clnt_task_board/index.js
Normal file
16
app/panels/clnt_task_board/index.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - УДП - Доски задач
|
||||||
|
Панель мониторинга: Точка входа
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import { ClntTaskBoard } from "./clnt_task_board"; //Корневая панель выполнения работ
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export const RootClass = ClntTaskBoard;
|
||||||
207
app/panels/clnt_task_board/layouts.js
Normal file
207
app/panels/clnt_task_board/layouts.js
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - УДП - Доски задач
|
||||||
|
Дополнительная разметка и вёрстка клиентских элементов
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Перечисление "Состояние события"
|
||||||
|
export const EVENT_STATES = Object.freeze({ 0: "Все", 1: "Не аннулированные", 2: "Аннулированные" });
|
||||||
|
|
||||||
|
//Допустимые значение поля сортировки
|
||||||
|
export const sortAttrs = [
|
||||||
|
{ id: "code", descr: "Мнемокод" },
|
||||||
|
{ id: "name", descr: "Наименование" },
|
||||||
|
{ id: "pointDescr", descr: "Описание точки маршрута" }
|
||||||
|
];
|
||||||
|
|
||||||
|
//Допустимые значения направления сортировки
|
||||||
|
export const sortDest = [];
|
||||||
|
sortDest[-1] = "desc";
|
||||||
|
sortDest[1] = "asc";
|
||||||
|
|
||||||
|
//Цвета статусов
|
||||||
|
export const COLORS = [
|
||||||
|
"mediumSlateBlue",
|
||||||
|
"lightSalmon",
|
||||||
|
"fireBrick",
|
||||||
|
"orange",
|
||||||
|
"gold",
|
||||||
|
"limeGreen",
|
||||||
|
"yellowGreen",
|
||||||
|
"mediumAquaMarine",
|
||||||
|
"paleTurquoise",
|
||||||
|
"steelBlue",
|
||||||
|
"skyBlue",
|
||||||
|
"tan"
|
||||||
|
];
|
||||||
|
|
||||||
|
//Перечисление "Цвет индикации"
|
||||||
|
export const EVENT_INDICATORS = Object.freeze({ EXPIRED: "#ff0000", EXPIRES_SOON: "#ffdf00", LINKED: "#1e90ff" });
|
||||||
|
|
||||||
|
//Перечисление Доп. свойства "Значение по умолчанию"
|
||||||
|
export const DP_DEFAULT_VALUE = Object.freeze({ 0: "defaultStr", 1: "defaultNum", 2: "defaultDate", 3: "defaultNum" });
|
||||||
|
//Перечисление Доп. свойства "Префикс формата данных"
|
||||||
|
export const DP_TYPE_PREFIX = Object.freeze({ 0: "S", 1: "N", 2: "D", 3: "N" });
|
||||||
|
//Перечисление Доп. свойства "Входящее значение дополнительного словаря"
|
||||||
|
export const DP_IN_VALUE = Object.freeze({ 0: "pos_str_value", 1: "pos_num_value", 2: "pos_date_value", 3: "pos_num_value" });
|
||||||
|
//Перечисление Доп. свойства "Исходящее значение дополнительного словаря"
|
||||||
|
export const DP_RETURN_VALUE = Object.freeze({ 0: "str_value", 1: "num_value", 2: "date_value", 3: "num_value" });
|
||||||
|
|
||||||
|
//Меню действий события
|
||||||
|
export const menuItems = (
|
||||||
|
handleEdit,
|
||||||
|
handleEditClient,
|
||||||
|
handleDelete,
|
||||||
|
handleTaskStateChange,
|
||||||
|
handleTaskReturn,
|
||||||
|
handleTaskSend,
|
||||||
|
handleNotes,
|
||||||
|
handleFileLinks
|
||||||
|
) => [
|
||||||
|
{ method: "EDIT", name: "Исправить", icon: "edit", visible: false, delimiter: false, needReload: false, func: handleEdit },
|
||||||
|
{
|
||||||
|
method: "EDIT_CLIENT",
|
||||||
|
name: "Исправить в разделе",
|
||||||
|
icon: "edit_note",
|
||||||
|
visible: true,
|
||||||
|
delimiter: false,
|
||||||
|
needReload: false,
|
||||||
|
func: handleEditClient
|
||||||
|
},
|
||||||
|
{ method: "DELETE", name: "Удалить", icon: "delete", visible: true, delimiter: true, needReload: true, func: handleDelete },
|
||||||
|
{
|
||||||
|
method: "TASK_STATE_CHANGE",
|
||||||
|
name: "Перейти",
|
||||||
|
icon: "turn_right",
|
||||||
|
visible: true,
|
||||||
|
delimiter: false,
|
||||||
|
needReload: true,
|
||||||
|
func: handleTaskStateChange
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: "TASK_RETURN",
|
||||||
|
name: "Выполнить возврат",
|
||||||
|
icon: "turn_left",
|
||||||
|
visible: true,
|
||||||
|
delimiter: false,
|
||||||
|
needReload: true,
|
||||||
|
func: handleTaskReturn
|
||||||
|
},
|
||||||
|
{ method: "TASK_SEND", name: "Направить", icon: "send", visible: true, delimiter: true, needReload: true, func: handleTaskSend },
|
||||||
|
{ method: "NOTES", name: "Примечания", icon: "event_note", visible: true, delimiter: true, needReload: false, func: handleNotes },
|
||||||
|
{
|
||||||
|
method: "FILE_LINKS",
|
||||||
|
name: "Присоединенные документы",
|
||||||
|
icon: "attach_file",
|
||||||
|
visible: true,
|
||||||
|
delimiter: false,
|
||||||
|
needReload: false,
|
||||||
|
func: handleFileLinks
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Формирование массива из 0, 1 и 1< элементов
|
||||||
|
export const arrayFormer = arr => {
|
||||||
|
return arr ? (arr.length ? arr : [arr]) : [];
|
||||||
|
};
|
||||||
|
|
||||||
|
//Конвертация формата HEX в формат RGB
|
||||||
|
const hexToRGB = hex => {
|
||||||
|
let r = parseInt(hex.slice(1, 3), 16);
|
||||||
|
let g = parseInt(hex.slice(3, 5), 16);
|
||||||
|
let b = parseInt(hex.slice(5, 7), 16);
|
||||||
|
let a = 0.5;
|
||||||
|
r = Math.round((a * (r / 255) + a * (255 / 255)) * 255);
|
||||||
|
g = Math.round((a * (g / 255) + a * (255 / 255)) * 255);
|
||||||
|
b = Math.round((a * (b / 255) + a * (255 / 255)) * 255);
|
||||||
|
return "rgb(" + r + ", " + g + ", " + b + ")";
|
||||||
|
};
|
||||||
|
|
||||||
|
//Проверка выполнения условия заливки события
|
||||||
|
export const bgColorRule = (task, colorRule) => {
|
||||||
|
let ruleCode;
|
||||||
|
let bgColor = null;
|
||||||
|
if (colorRule.vType === "string") ruleCode = `S${colorRule.fieldCode}`;
|
||||||
|
else if (colorRule.vType === "number") ruleCode = `N${colorRule.fieldCode}`;
|
||||||
|
else if (colorRule.vType === "date") ruleCode = `D${colorRule.fieldCode}`;
|
||||||
|
ruleCode ? (task.docProps[ruleCode] == colorRule.from ? (bgColor = hexToRGB(colorRule.color)) : null) : null;
|
||||||
|
return bgColor;
|
||||||
|
};
|
||||||
|
|
||||||
|
//Индикация истечения срока отработки события
|
||||||
|
export const indicatorColorRule = task => {
|
||||||
|
let sysDate = new Date();
|
||||||
|
let expireDate = task.dexpire_date ? new Date(task.dexpire_date) : null;
|
||||||
|
let daysDiff = null;
|
||||||
|
if (expireDate) {
|
||||||
|
daysDiff = ((expireDate.getTime() - sysDate.getTime()) / (1000 * 60 * 60 * 24)).toFixed(2);
|
||||||
|
if (daysDiff < 0) return EVENT_INDICATORS.EXPIRED;
|
||||||
|
else if (daysDiff < 4) return EVENT_INDICATORS.EXPIRES_SOON;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
//Формирование случайного цвета
|
||||||
|
export const randomColor = index => {
|
||||||
|
const hue = index * 137.508;
|
||||||
|
return hslToRgba(hue, 50, 70);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Цвет из hsl формата в rgba формат
|
||||||
|
const hslToRgba = (h, s, l) => {
|
||||||
|
s /= 100;
|
||||||
|
l /= 100;
|
||||||
|
const k = n => (n + h / 30) % 12;
|
||||||
|
const a = s * Math.min(l, 1 - l);
|
||||||
|
const f = n => l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)));
|
||||||
|
return `rgba(${Math.floor(255 * f(0))},${Math.floor(255 * f(8))},${Math.floor(255 * f(4))},0.3)`;
|
||||||
|
};
|
||||||
|
|
||||||
|
//Формат дополнительного свойства типа число (длина, точность)
|
||||||
|
const DPNumFormat = (l, p) => new RegExp("^(\\d{1," + (l - p) + "}" + (p > 0 ? "((\\.|,)\\d{1," + p + "})?" : "") + ")?$");
|
||||||
|
|
||||||
|
//Формат дополнительного свойства типа строка (длина)
|
||||||
|
const DPStrFormat = l => new RegExp("^.{0," + l + "}$");
|
||||||
|
|
||||||
|
//Проверка валидности числа
|
||||||
|
const isValidDPNum = (length, prec, value) => {
|
||||||
|
return DPNumFormat(length, prec).test(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Проверка валидности строки
|
||||||
|
const isValidDPStr = (length, value) => {
|
||||||
|
return DPStrFormat(length).test(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Признак ошибки валидации
|
||||||
|
export const validationError = (value = "", format, numW, numPrec, strW) => {
|
||||||
|
if (format === 0) return isValidDPStr(strW, value);
|
||||||
|
else if (format === 1) {
|
||||||
|
return isValidDPNum(numW, numPrec, value);
|
||||||
|
} else return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
//Конвертация времени в привычный формат
|
||||||
|
export const timeFromSqlFormat = ts => {
|
||||||
|
if (ts.indexOf(".") !== -1) {
|
||||||
|
let s = 24 * 60 * 60 * ts;
|
||||||
|
const h = Math.trunc(s / (60 * 60));
|
||||||
|
s = s % (60 * 60);
|
||||||
|
const m = Math.trunc(s / 60);
|
||||||
|
s = Math.round(s % 60);
|
||||||
|
const formattedTime = ("0" + h).slice(-2) + ":" + ("0" + m).slice(-2) + ":" + ("0" + s).slice(-2);
|
||||||
|
return formattedTime;
|
||||||
|
}
|
||||||
|
return ts;
|
||||||
|
};
|
||||||
97
app/panels/clnt_task_board/task_dialog.js
Normal file
97
app/panels/clnt_task_board/task_dialog.js
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
Парус 8 - Панели мониторинга - УДП - Доски задач
|
||||||
|
Компонент панели: Диалог формы события
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
//Подключение библиотек
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
import React, { useState } from "react"; //Классы React
|
||||||
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
|
import { Dialog, DialogContent, DialogActions, Button } from "@mui/material"; //Интерфейсные компоненты
|
||||||
|
import { useClientEvent } from "./hooks/task_dialog_hooks"; //Хук для события
|
||||||
|
import { APP_STYLES } from "../../../app.styles"; //Типовые стили
|
||||||
|
import { TaskForm } from "./components/task_form"; //Форма события
|
||||||
|
|
||||||
|
//---------
|
||||||
|
//Константы
|
||||||
|
//---------
|
||||||
|
|
||||||
|
//Стили
|
||||||
|
const STYLES = {
|
||||||
|
DIALOG_CONTENT: {
|
||||||
|
paddingBottom: "0px",
|
||||||
|
maxHeight: "740px",
|
||||||
|
minHeight: "740px",
|
||||||
|
...APP_STYLES.SCROLL
|
||||||
|
},
|
||||||
|
DIALOG_ACTIONS: { justifyContent: "end", paddingRight: "24px", paddingLeft: "24px" }
|
||||||
|
};
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
//Тело модуля
|
||||||
|
//-----------
|
||||||
|
|
||||||
|
//Диалог с формой события
|
||||||
|
const TaskDialog = ({ taskRn, taskType, taskStatus, editable, onReload, onClose }) => {
|
||||||
|
//Собственное состояние
|
||||||
|
const [task, setTask, insertEvent, updateEvent, handleEventNextNumbGet] = useClientEvent(taskRn, taskType, taskStatus);
|
||||||
|
|
||||||
|
//Состояние заполненности всех обязательных свойств
|
||||||
|
const [dpReady, setDPReady] = useState(false);
|
||||||
|
|
||||||
|
//Изменение состояния заполненности всех обязательных свойств
|
||||||
|
const handleDPReady = v => setDPReady(v);
|
||||||
|
|
||||||
|
//Генерация содержимого
|
||||||
|
return (
|
||||||
|
<Dialog open onClose={onClose ? onClose : null} fullWidth>
|
||||||
|
<DialogContent sx={STYLES.DIALOG_CONTENT}>
|
||||||
|
<TaskForm
|
||||||
|
task={task}
|
||||||
|
taskType={taskType}
|
||||||
|
setTask={setTask}
|
||||||
|
editable={!taskRn || editable ? true : false}
|
||||||
|
onEventNextNumbGet={handleEventNextNumbGet}
|
||||||
|
onDPReady={handleDPReady}
|
||||||
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
{onClose ? (
|
||||||
|
<DialogActions sx={STYLES.DIALOG_ACTIONS}>
|
||||||
|
{taskRn ? (
|
||||||
|
<Button onClick={() => updateEvent(onClose).then(onReload)} disabled={task.updateDisabled || !editable || !dpReady}>
|
||||||
|
Исправить
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
insertEvent(onClose).then(onReload);
|
||||||
|
}}
|
||||||
|
disabled={task.insertDisabled || !dpReady}
|
||||||
|
>
|
||||||
|
Добавить
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button onClick={onClose}>Закрыть</Button>
|
||||||
|
</DialogActions>
|
||||||
|
) : null}
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Контроль свойств - Диалог с формой события
|
||||||
|
TaskDialog.propTypes = {
|
||||||
|
taskRn: PropTypes.number,
|
||||||
|
taskType: PropTypes.string.isRequired,
|
||||||
|
taskStatus: PropTypes.string,
|
||||||
|
editable: PropTypes.bool,
|
||||||
|
onReload: PropTypes.func.isRequired,
|
||||||
|
onClose: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//Интерфейс модуля
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
export { TaskDialog };
|
||||||
@ -119,8 +119,8 @@ const EqsPrfrm = () => {
|
|||||||
let cF = 0;
|
let cF = 0;
|
||||||
let sF = 0;
|
let sF = 0;
|
||||||
let properties = [];
|
let properties = [];
|
||||||
if (data.XDATA_GRID.rows != null) {
|
if (data.XROWS != null) {
|
||||||
data.XDATA_GRID.rows.map(row => {
|
data.XROWS.map(row => {
|
||||||
properties = [];
|
properties = [];
|
||||||
Object.entries(row).forEach(([key, value]) => properties.push({ name: key, data: value }));
|
Object.entries(row).forEach(([key, value]) => properties.push({ name: key, data: value }));
|
||||||
let info2 = properties.find(element => {
|
let info2 = properties.find(element => {
|
||||||
@ -156,10 +156,11 @@ const EqsPrfrm = () => {
|
|||||||
}
|
}
|
||||||
setDataGrid(pv => ({
|
setDataGrid(pv => ({
|
||||||
...pv,
|
...pv,
|
||||||
...data.XDATA_GRID,
|
columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef,
|
rows: [...(data.XROWS || [])],
|
||||||
rows: [...(data.XDATA_GRID.rows || [])],
|
fixedHeader: data.XDATA_GRID.fixedHeader,
|
||||||
groups: [...(data.XDATA_GRID.groups || [])],
|
fixedColumns: data.XDATA_GRID.fixedColumns,
|
||||||
|
groups: [...(data.XGROUPS || [])],
|
||||||
dataLoaded: true,
|
dataLoaded: true,
|
||||||
reload: false
|
reload: false
|
||||||
}));
|
}));
|
||||||
@ -213,7 +214,7 @@ const EqsPrfrm = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (data.NIDENT) {
|
if (data.NIDENT) {
|
||||||
if (type == 0) pOnlineShowUnit({ unitCode: "EquipTechServices", inputParameters: [{ name: "in_Ident", value: data.NIDENT }] });
|
if (type == 0) pOnlineShowUnit({ unitCode: "EquipTechServices", inputParameters: [{ name: "in_SelectList_Ident", value: data.NIDENT }] });
|
||||||
else pOnlineShowUnit({ unitCode: "EquipRepairSheets", inputParameters: [{ name: "in_Ident", value: data.NIDENT }] });
|
else pOnlineShowUnit({ unitCode: "EquipRepairSheets", inputParameters: [{ name: "in_Ident", value: data.NIDENT }] });
|
||||||
} else showMsgErr(TEXTS.NO_DATA_FOUND);
|
} else showMsgErr(TEXTS.NO_DATA_FOUND);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -15,7 +15,6 @@ import { P8PSVG } from "../../../components/p8p_svg"; //Интерактивны
|
|||||||
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
||||||
import { useCostProductComposition, useProductDetailsTable } from "../hooks"; //Вспомогательные хуки
|
import { useCostProductComposition, useProductDetailsTable } from "../hooks"; //Вспомогательные хуки
|
||||||
import { ProgressBox } from "./progress_box"; //Информация по прогрессу объекта
|
import { ProgressBox } from "./progress_box"; //Информация по прогрессу объекта
|
||||||
import { APP_STYLES } from "../../../../app.styles"; //Типовые стили
|
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
//Константы
|
//Константы
|
||||||
@ -27,7 +26,6 @@ const STYLES = {
|
|||||||
border: "1px solid",
|
border: "1px solid",
|
||||||
borderRadius: "25px",
|
borderRadius: "25px",
|
||||||
height: "35vh",
|
height: "35vh",
|
||||||
minHeight: "250px",
|
|
||||||
backgroundColor: "background.detail_table"
|
backgroundColor: "background.detail_table"
|
||||||
},
|
},
|
||||||
BOX_INFO_SUB: isMessage => ({
|
BOX_INFO_SUB: isMessage => ({
|
||||||
@ -49,7 +47,6 @@ const STYLES = {
|
|||||||
border: "1px solid",
|
border: "1px solid",
|
||||||
borderRadius: "25px",
|
borderRadius: "25px",
|
||||||
height: "17vh",
|
height: "17vh",
|
||||||
minHeight: "120px",
|
|
||||||
backgroundColor: "background.detail_info"
|
backgroundColor: "background.detail_info"
|
||||||
},
|
},
|
||||||
PRODUCT_SELECTOR_CONTAINER: {
|
PRODUCT_SELECTOR_CONTAINER: {
|
||||||
@ -60,7 +57,6 @@ const STYLES = {
|
|||||||
border: "1px solid",
|
border: "1px solid",
|
||||||
borderRadius: "25px",
|
borderRadius: "25px",
|
||||||
height: "53vh",
|
height: "53vh",
|
||||||
minHeight: "379px",
|
|
||||||
marginTop: "16px",
|
marginTop: "16px",
|
||||||
backgroundColor: "background.product_selector"
|
backgroundColor: "background.product_selector"
|
||||||
},
|
},
|
||||||
@ -76,12 +72,7 @@ const STYLES = {
|
|||||||
width: "280px",
|
width: "280px",
|
||||||
borderBottom: "1px solid"
|
borderBottom: "1px solid"
|
||||||
},
|
},
|
||||||
TABLE_DETAILS: {
|
TABLE_DETAILS: { backgroundColor: "background.detail_table", height: "240px" },
|
||||||
backgroundColor: "background.detail_table",
|
|
||||||
height: "28vh",
|
|
||||||
minHeight: "187px",
|
|
||||||
...APP_STYLES.SCROLL
|
|
||||||
},
|
|
||||||
TABLE_DETAILS_HEADER_CELL: maxWidth => ({
|
TABLE_DETAILS_HEADER_CELL: maxWidth => ({
|
||||||
backgroundColor: "background.detail_table",
|
backgroundColor: "background.detail_table",
|
||||||
color: "text.detail_table.fontColor",
|
color: "text.detail_table.fontColor",
|
||||||
@ -116,7 +107,7 @@ const PlanSpecInfo = ({ planSpec }) => {
|
|||||||
<Box sx={STYLES.PLAN_INFO_MAIN}>
|
<Box sx={STYLES.PLAN_INFO_MAIN}>
|
||||||
<Box sx={STYLES.PLAN_INFO_SUB}>
|
<Box sx={STYLES.PLAN_INFO_SUB}>
|
||||||
<Typography variant="PlanSpecInfo" mt={1}>
|
<Typography variant="PlanSpecInfo" mt={1}>
|
||||||
Номер заказа:
|
Номер борта:
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="subtitle2">{planSpec.SNUMB}</Typography>
|
<Typography variant="subtitle2">{planSpec.SNUMB}</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@ -70,11 +70,11 @@ const PlanSpecsListItem = ({ card, cardIndex, onClick }) => {
|
|||||||
return (
|
return (
|
||||||
<Box sx={STYLES.CONTAINER} onClick={() => (onClick ? onClick(card, cardIndex) : null)}>
|
<Box sx={STYLES.CONTAINER} onClick={() => (onClick ? onClick(card, cardIndex) : null)}>
|
||||||
<PlanSpecsListItemImage card={card} />
|
<PlanSpecsListItemImage card={card} />
|
||||||
<Box textAlign="center" height="70px">
|
<Box textAlign="center">
|
||||||
<Typography variant="PlanSpecInfo" sx={STYLES.TEXT_INFO_FIELD}>
|
<Typography variant="PlanSpecInfo" sx={STYLES.TEXT_INFO_FIELD}>
|
||||||
Номер заказа
|
Номер борта
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="h2">{card.SNUMB || "-"}</Typography>
|
<Typography variant="h2">{card.SNUMB}</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<ProgressBox
|
<ProgressBox
|
||||||
progress={card.NPROGRESS}
|
progress={card.NPROGRESS}
|
||||||
@ -84,12 +84,12 @@ const PlanSpecsListItem = ({ card, cardIndex, onClick }) => {
|
|||||||
progressVariant={"h3"}
|
progressVariant={"h3"}
|
||||||
detailVariant={"PlanSpecProgressDetail"}
|
detailVariant={"PlanSpecProgressDetail"}
|
||||||
/>
|
/>
|
||||||
<Box height="70px">
|
<Box>
|
||||||
<Typography variant="PlanSpecInfo" sx={STYLES.TEXT_INFO_FIELD}>
|
<Typography variant="PlanSpecInfo" sx={STYLES.TEXT_INFO_FIELD}>
|
||||||
Год выпуска:
|
Год выпуска:
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="subtitle1" mt={-1}>
|
<Typography variant="subtitle1" mt={-1}>
|
||||||
{card.NYEAR || "-"}
|
{card.NYEAR}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@ -195,10 +195,9 @@ const useProductDetailsTable = (planSpec, product, orders, pageNumber, stored) =
|
|||||||
});
|
});
|
||||||
setData(pv => ({
|
setData(pv => ({
|
||||||
...pv,
|
...pv,
|
||||||
...data.XDATA_GRID,
|
columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef,
|
rows: pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
|
||||||
rows: pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...pv.rows, ...(data.XDATA_GRID.rows || [])],
|
morePages: DATA_GRID_PAGE_SIZE == 0 ? false : (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE,
|
||||||
morePages: DATA_GRID_PAGE_SIZE == 0 ? false : (data.XDATA_GRID.rows || []).length >= DATA_GRID_PAGE_SIZE,
|
|
||||||
init: true
|
init: true
|
||||||
}));
|
}));
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@ -27,7 +27,6 @@ import {
|
|||||||
Icon
|
Icon
|
||||||
} from "@mui/material"; //Интерфейсные элементы
|
} from "@mui/material"; //Интерфейсные элементы
|
||||||
import { ThemeProvider } from "@mui/material/styles"; //Подключение темы
|
import { ThemeProvider } from "@mui/material/styles"; //Подключение темы
|
||||||
import { APP_STYLES } from "../../../app.styles"; //Типовые стили
|
|
||||||
import { PlanSpecsList } from "./components/plans_list"; //Список планов
|
import { PlanSpecsList } from "./components/plans_list"; //Список планов
|
||||||
import { PlanSpecDetail } from "./components/plan_detail"; //Детали плана
|
import { PlanSpecDetail } from "./components/plan_detail"; //Детали плана
|
||||||
import { lightTheme, darkTheme } from "./styles/themes"; //Стиль темы
|
import { lightTheme, darkTheme } from "./styles/themes"; //Стиль темы
|
||||||
@ -64,8 +63,7 @@ const STYLES = {
|
|||||||
display: "inline-block",
|
display: "inline-block",
|
||||||
boxSizing: "border-box",
|
boxSizing: "border-box",
|
||||||
backgroundColor: "background.plans_drawer_paper",
|
backgroundColor: "background.plans_drawer_paper",
|
||||||
color: "text.plans_finder.fontColor",
|
color: "text.plans_finder.fontColor"
|
||||||
...APP_STYLES.SCROLL
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
PLANS_LIST_BOX: { paddingTop: "20px" },
|
PLANS_LIST_BOX: { paddingTop: "20px" },
|
||||||
@ -242,32 +240,26 @@ const MechRecAssemblyMon = () => {
|
|||||||
</Stack>
|
</Stack>
|
||||||
{state.init == true ? (
|
{state.init == true ? (
|
||||||
state.selectedPlanCtlg.NRN ? (
|
state.selectedPlanCtlg.NRN ? (
|
||||||
state.planSpecs.length !== 0 ? (
|
<>
|
||||||
<>
|
<Typography variant="h3" sx={STYLES.MAIN_TITLE} pb={2}>
|
||||||
<Typography variant="h3" sx={STYLES.MAIN_TITLE} pb={2}>
|
{title}
|
||||||
{title}
|
|
||||||
</Typography>
|
|
||||||
{state.planSpecsLoaded == true ? (
|
|
||||||
state.selectedPlanSpec.NRN ? (
|
|
||||||
<PlanSpecDetail
|
|
||||||
planSpec={state.selectedPlanSpec}
|
|
||||||
disableNavigatePrev={planDetailNavigation.disableNavigatePrev}
|
|
||||||
disableNavigateNext={planDetailNavigation.disableNavigateNext}
|
|
||||||
onNavigate={handlePlanDetailNavigateClick}
|
|
||||||
onBack={handlePlanDetailBackClick}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<Box sx={STYLES.PLANS_LIST_BOX}>
|
|
||||||
<PlanSpecsList planSpecs={state.planSpecs} onItemClick={handlePlanClick} />
|
|
||||||
</Box>
|
|
||||||
)
|
|
||||||
) : null}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<Typography variant="h4" sx={STYLES.MAIN_TITLE}>
|
|
||||||
В каталоге планов отсутствуют записи подходящих первичных документов
|
|
||||||
</Typography>
|
</Typography>
|
||||||
)
|
{state.planSpecsLoaded == true ? (
|
||||||
|
state.selectedPlanSpec.NRN ? (
|
||||||
|
<PlanSpecDetail
|
||||||
|
planSpec={state.selectedPlanSpec}
|
||||||
|
disableNavigatePrev={planDetailNavigation.disableNavigatePrev}
|
||||||
|
disableNavigateNext={planDetailNavigation.disableNavigateNext}
|
||||||
|
onNavigate={handlePlanDetailNavigateClick}
|
||||||
|
onBack={handlePlanDetailBackClick}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Box sx={STYLES.PLANS_LIST_BOX}>
|
||||||
|
<PlanSpecsList planSpecs={state.planSpecs} onItemClick={handlePlanClick} />
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<Typography variant="h4" sx={STYLES.MAIN_TITLE}>
|
<Typography variant="h4" sx={STYLES.MAIN_TITLE}>
|
||||||
Укажите каталог планов для отображения спецификаций
|
Укажите каталог планов для отображения спецификаций
|
||||||
|
|||||||
@ -150,13 +150,12 @@ const useCostJobsSpecs = task => {
|
|||||||
});
|
});
|
||||||
setCostJobsSpecs(pv => ({
|
setCostJobsSpecs(pv => ({
|
||||||
...pv,
|
...pv,
|
||||||
...data.XDATA_GRID,
|
|
||||||
task: task,
|
task: task,
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef,
|
columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
|
||||||
rows: pv.pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...pv.rows, ...(data.XDATA_GRID.rows || [])],
|
rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
|
||||||
dataLoaded: true,
|
dataLoaded: true,
|
||||||
reload: false,
|
reload: false,
|
||||||
morePages: (data.XDATA_GRID.rows || []).length >= DATA_GRID_PAGE_SIZE
|
morePages: (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
loadData();
|
loadData();
|
||||||
@ -257,13 +256,12 @@ const useEquipConfiguration = (task, fromAction) => {
|
|||||||
});
|
});
|
||||||
setEquipConfiguration(pv => ({
|
setEquipConfiguration(pv => ({
|
||||||
...pv,
|
...pv,
|
||||||
...data.XDATA_GRID,
|
|
||||||
task: task,
|
task: task,
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef,
|
columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
|
||||||
rows: pv.pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...pv.rows, ...(data.XDATA_GRID.rows || [])],
|
rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
|
||||||
dataLoaded: true,
|
dataLoaded: true,
|
||||||
reload: false,
|
reload: false,
|
||||||
morePages: (data.XDATA_GRID.rows || []).length >= DATA_GRID_PAGE_SIZE
|
morePages: (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
loadData();
|
loadData();
|
||||||
|
|||||||
@ -10,7 +10,6 @@
|
|||||||
import React, { useContext, useState } from "react"; //Классы React
|
import React, { useContext, useState } from "react"; //Классы React
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||||
import { Drawer, Fab, Box, List, ListItemButton, ListItemText, Typography, TextField } from "@mui/material"; //Интерфейсные элементы
|
import { Drawer, Fab, Box, List, ListItemButton, ListItemText, Typography, TextField } from "@mui/material"; //Интерфейсные элементы
|
||||||
import { APP_STYLES } from "../../../app.styles"; //Типовые стили
|
|
||||||
import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений
|
import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений
|
||||||
import { CostJobsSpecsDataGrid } from "./fcjobssp"; //Собственные хуки таблиц
|
import { CostJobsSpecsDataGrid } from "./fcjobssp"; //Собственные хуки таблиц
|
||||||
import { useCostJobs, useFilteredFcjobs } from "./hooks"; //Вспомогательные хуки
|
import { useCostJobs, useFilteredFcjobs } from "./hooks"; //Вспомогательные хуки
|
||||||
@ -36,7 +35,7 @@ const STYLES = {
|
|||||||
width: "350px",
|
width: "350px",
|
||||||
display: "inline-block",
|
display: "inline-block",
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
[`& .MuiDrawer-paper`]: { width: "350px", display: "inline-block", boxSizing: "border-box", ...APP_STYLES.SCROLL }
|
[`& .MuiDrawer-paper`]: { width: "350px", display: "inline-block", boxSizing: "border-box" }
|
||||||
},
|
},
|
||||||
CONTAINER: { textAlign: "center" }
|
CONTAINER: { textAlign: "center" }
|
||||||
};
|
};
|
||||||
|
|||||||
@ -53,15 +53,14 @@ const useCostRouteLists = (task, taskType) => {
|
|||||||
});
|
});
|
||||||
setCostRouteLists(pv => ({
|
setCostRouteLists(pv => ({
|
||||||
...pv,
|
...pv,
|
||||||
...data.XDATA_GRID,
|
columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef,
|
rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
|
||||||
rows: pv.pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...pv.rows, ...(data.XDATA_GRID.rows || [])],
|
|
||||||
dataLoaded: true,
|
dataLoaded: true,
|
||||||
reload: false,
|
reload: false,
|
||||||
morePages: (data.XDATA_GRID.rows || []).length >= DATA_GRID_PAGE_SIZE,
|
morePages: (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE,
|
||||||
quantPlanSum: data.XDATA_GRID.rows ? data.XDATA_GRID.rows.reduce((a, b) => a + b["NQUANT_PLAN"], 0) : 0,
|
quantPlanSum: data.XROWS ? data.XROWS.reduce((a, b) => a + b["NQUANT_PLAN"], 0) : 0,
|
||||||
uniqueNomns: data.XDATA_GRID.rows
|
uniqueNomns: data.XROWS
|
||||||
? data.XDATA_GRID.rows.reduce((accumulator, current) => {
|
? data.XROWS.reduce((accumulator, current) => {
|
||||||
if (!accumulator.find(item => item.SMATRES_PLAN_NOMEN === current.SMATRES_PLAN_NOMEN)) {
|
if (!accumulator.find(item => item.SMATRES_PLAN_NOMEN === current.SMATRES_PLAN_NOMEN)) {
|
||||||
accumulator.push(current);
|
accumulator.push(current);
|
||||||
}
|
}
|
||||||
@ -123,12 +122,11 @@ const useIncomFromDeps = (task, taskType) => {
|
|||||||
});
|
});
|
||||||
setIncomFromDeps(pv => ({
|
setIncomFromDeps(pv => ({
|
||||||
...pv,
|
...pv,
|
||||||
...data.XDATA_GRID,
|
columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef,
|
rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
|
||||||
rows: pv.pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...pv.rows, ...(data.XDATA_GRID.rows || [])],
|
|
||||||
dataLoaded: true,
|
dataLoaded: true,
|
||||||
reload: false,
|
reload: false,
|
||||||
morePages: (data.XDATA_GRID.rows || []).length >= DATA_GRID_PAGE_SIZE
|
morePages: (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@ -174,12 +172,11 @@ const useGoodsParties = mainRowRN => {
|
|||||||
});
|
});
|
||||||
setGoodsParties(pv => ({
|
setGoodsParties(pv => ({
|
||||||
...pv,
|
...pv,
|
||||||
...data.XDATA_GRID,
|
columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef,
|
rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
|
||||||
rows: pv.pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...pv.rows, ...(data.XDATA_GRID.rows || [])],
|
|
||||||
dataLoaded: true,
|
dataLoaded: true,
|
||||||
reload: false,
|
reload: false,
|
||||||
morePages: (data.XDATA_GRID.rows || []).length >= DATA_GRID_PAGE_SIZE
|
morePages: (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@ -226,12 +223,11 @@ const useCostDeliveryLists = mainRowRN => {
|
|||||||
});
|
});
|
||||||
setCostDeliveryLists(pv => ({
|
setCostDeliveryLists(pv => ({
|
||||||
...pv,
|
...pv,
|
||||||
...data.XDATA_GRID,
|
columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef,
|
rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
|
||||||
rows: pv.pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...pv.rows, ...(data.XDATA_GRID.rows || [])],
|
|
||||||
dataLoaded: true,
|
dataLoaded: true,
|
||||||
reload: false,
|
reload: false,
|
||||||
morePages: (data.XDATA_GRID.rows || []).length >= DATA_GRID_PAGE_SIZE
|
morePages: (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
|||||||
@ -43,12 +43,11 @@ import { MessagingСtx } from "../../context/messaging"; //Контекст со
|
|||||||
import { NavigationCtx } from "../../context/navigation"; //Контекст навигации
|
import { NavigationCtx } from "../../context/navigation"; //Контекст навигации
|
||||||
import { P8P_GANTT_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
import { P8P_GANTT_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
||||||
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
|
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
|
||||||
import { APP_STYLES } from "../../../app.styles"; //Типовые стили
|
|
||||||
import { P8PGantt, taskLegendDesc } from "../../components/p8p_gantt"; //Диаграмма Ганта
|
import { P8PGantt, taskLegendDesc } from "../../components/p8p_gantt"; //Диаграмма Ганта
|
||||||
import { xml2JSON, formatDateJSONDateOnly, formatDateRF, hasValue } from "../../core/utils"; //Вспомогательные функции
|
import { xml2JSON, formatDateJSONDateOnly, formatDateRF, hasValue } from "../../core/utils"; //Вспомогательные функции
|
||||||
import { useFilteredPlanCtlgs } from "./hooks"; //Вспомогательные хуки
|
import { useFilteredPlanCtlgs } from "./hooks"; //Вспомогательные хуки
|
||||||
import { CostRouteListsDataGrid } from "./datagrids/fcroutlst"; //Таблица "Маршрутные листы"
|
import { CostRouteListsDataGrid } from "./datagrids/fcroutlst";
|
||||||
import { IncomFromDepsDataGrid } from "./datagrids/incomefromdeps"; //Таблица "Приходы из подразделений"
|
import { IncomFromDepsDataGrid } from "./datagrids/incomefromdeps";
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
//Константы
|
//Константы
|
||||||
@ -73,7 +72,7 @@ const STYLES = {
|
|||||||
width: "350px",
|
width: "350px",
|
||||||
display: "inline-block",
|
display: "inline-block",
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
[`& .MuiDrawer-paper`]: { width: "350px", display: "inline-block", boxSizing: "border-box", ...APP_STYLES.SCROLL }
|
[`& .MuiDrawer-paper`]: { width: "350px", display: "inline-block", boxSizing: "border-box" }
|
||||||
},
|
},
|
||||||
GANTT_CONTAINER: { height: `calc(100vh - ${APP_BAR_HEIGHT})`, width: "100vw", paddingTop: "24px" },
|
GANTT_CONTAINER: { height: `calc(100vh - ${APP_BAR_HEIGHT})`, width: "100vw", paddingTop: "24px" },
|
||||||
GANTT_TITLE: { paddingLeft: "250px", paddingRight: "250px" },
|
GANTT_TITLE: { paddingLeft: "250px", paddingRight: "250px" },
|
||||||
@ -98,7 +97,7 @@ const parseProdPlanSpXML = async xmlDoc => {
|
|||||||
attributeValueProcessor: (name, val) =>
|
attributeValueProcessor: (name, val) =>
|
||||||
["numb", "title"].includes(name) ? undefined : ["start", "end"].includes(name) ? formatDateJSONDateOnly(val) : val
|
["numb", "title"].includes(name) ? undefined : ["start", "end"].includes(name) ? formatDateJSONDateOnly(val) : val
|
||||||
});
|
});
|
||||||
return data.XDATA.XGANTT;
|
return data.XDATA;
|
||||||
};
|
};
|
||||||
|
|
||||||
//Форматирование для отображения количества документов
|
//Форматирование для отображения количества документов
|
||||||
@ -238,7 +237,8 @@ const MechRecCostProdPlans = () => {
|
|||||||
selectedPlanCtlgLevel: null,
|
selectedPlanCtlgLevel: null,
|
||||||
selectedPlanCtlgSort: null,
|
selectedPlanCtlgSort: null,
|
||||||
selectedPlanCtlgMenuItems: null,
|
selectedPlanCtlgMenuItems: null,
|
||||||
gantt: {},
|
selectedPlanCtlgGanttDef: {},
|
||||||
|
selectedPlanCtlgSpecs: [],
|
||||||
selectedTaskDetail: null,
|
selectedTaskDetail: null,
|
||||||
selectedTaskDetailType: null,
|
selectedTaskDetailType: null,
|
||||||
planSpec: null
|
planSpec: null
|
||||||
@ -282,7 +282,8 @@ const MechRecCostProdPlans = () => {
|
|||||||
selectedPlanCtlgLevel: null,
|
selectedPlanCtlgLevel: null,
|
||||||
selectedPlanCtlgSort: null,
|
selectedPlanCtlgSort: null,
|
||||||
selectedPlanCtlgMenuItems: null,
|
selectedPlanCtlgMenuItems: null,
|
||||||
gantt: {},
|
selectedPlanCtlgSpecs: [],
|
||||||
|
selectedPlanCtlgGanttDef: {},
|
||||||
showPlanList: false,
|
showPlanList: false,
|
||||||
selectedTaskDetail: null,
|
selectedTaskDetail: null,
|
||||||
selectedTaskDetailType: null
|
selectedTaskDetailType: null
|
||||||
@ -299,7 +300,8 @@ const MechRecCostProdPlans = () => {
|
|||||||
selectedPlanCtlgLevel: null,
|
selectedPlanCtlgLevel: null,
|
||||||
selectedPlanCtlgSort: null,
|
selectedPlanCtlgSort: null,
|
||||||
selectedPlanCtlgMenuItems: null,
|
selectedPlanCtlgMenuItems: null,
|
||||||
gantt: {},
|
selectedPlanCtlgSpecs: [],
|
||||||
|
selectedPlanCtlgGanttDef: {},
|
||||||
showPlanList: false,
|
showPlanList: false,
|
||||||
selectedTaskDetail: null,
|
selectedTaskDetail: null,
|
||||||
selectedTaskDetailType: null
|
selectedTaskDetailType: null
|
||||||
@ -322,7 +324,8 @@ const MechRecCostProdPlans = () => {
|
|||||||
? state.selectedPlanCtlgMenuItems
|
? state.selectedPlanCtlgMenuItems
|
||||||
: [...Array(data.NMAX_LEVEL).keys()].map(el => el + 1),
|
: [...Array(data.NMAX_LEVEL).keys()].map(el => el + 1),
|
||||||
selectedPlanCtlgSpecsLoaded: true,
|
selectedPlanCtlgSpecsLoaded: true,
|
||||||
gantt: { ...doc, tasks: [...(doc?.tasks || [])] }
|
selectedPlanCtlgGanttDef: doc.XGANTT_DEF ? { ...doc.XGANTT_DEF } : {},
|
||||||
|
selectedPlanCtlgSpecs: [...(doc?.XGANTT_TASKS || [])]
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@ -398,7 +401,7 @@ const MechRecCostProdPlans = () => {
|
|||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
{state.selectedPlanCtlgSpecsLoaded ? (
|
{state.selectedPlanCtlgSpecsLoaded ? (
|
||||||
state.gantt.tasks.length === 0 ? (
|
state.selectedPlanCtlgSpecs.length === 0 ? (
|
||||||
<Box pt={3}>
|
<Box pt={3}>
|
||||||
<InlineMsgInfo
|
<InlineMsgInfo
|
||||||
okBtn={false}
|
okBtn={false}
|
||||||
@ -456,9 +459,10 @@ const MechRecCostProdPlans = () => {
|
|||||||
) : null}
|
) : null}
|
||||||
<P8PGantt
|
<P8PGantt
|
||||||
{...P8P_GANTT_CONFIG_PROPS}
|
{...P8P_GANTT_CONFIG_PROPS}
|
||||||
{...state.gantt}
|
{...state.selectedPlanCtlgGanttDef}
|
||||||
containerStyle={STYLES.GANTT_CONTAINER}
|
containerStyle={STYLES.GANTT_CONTAINER}
|
||||||
titleStyle={STYLES.GANTT_TITLE}
|
titleStyle={STYLES.GANTT_TITLE}
|
||||||
|
tasks={state.selectedPlanCtlgSpecs}
|
||||||
taskDialogRenderer={prms => taskDialogRenderer({ ...prms, handleTaskDetailOpen })}
|
taskDialogRenderer={prms => taskDialogRenderer({ ...prms, handleTaskDetailOpen })}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@ -62,12 +62,13 @@ const useMechRecDeptCostJobs = (subdiv, fullDate, workHours) => {
|
|||||||
});
|
});
|
||||||
setCostJobs(pv => ({
|
setCostJobs(pv => ({
|
||||||
...pv,
|
...pv,
|
||||||
...data.XDATA_GRID,
|
fixedHeader: data.XDATA_GRID.fixedHeader,
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef,
|
fixedColumns: data.XDATA_GRID.fixedColumns,
|
||||||
rows: pv.pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...pv.rows, ...(data.XDATA_GRID.rows || [])],
|
columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
|
||||||
|
rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
|
||||||
dataLoaded: true,
|
dataLoaded: true,
|
||||||
reload: false,
|
reload: false,
|
||||||
morePages: (data.XDATA_GRID.rows || []).length >= DATA_GRID_PAGE_SIZE_LARGE,
|
morePages: (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE_LARGE,
|
||||||
date: fullDate
|
date: fullDate
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
@ -108,12 +109,11 @@ const useInsDepartment = fullDate => {
|
|||||||
});
|
});
|
||||||
setInsDepartments(pv => ({
|
setInsDepartments(pv => ({
|
||||||
...pv,
|
...pv,
|
||||||
...data.XDATA_GRID,
|
columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef,
|
rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
|
||||||
rows: pv.pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...pv.rows, ...(data.XDATA_GRID.rows || [])],
|
|
||||||
dataLoaded: true,
|
dataLoaded: true,
|
||||||
reload: false,
|
reload: false,
|
||||||
morePages: (data.XDATA_GRID.rows || []).length >= DATA_GRID_PAGE_SIZE_SMALL
|
morePages: (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE_SMALL
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
if (insDepartments.reload) {
|
if (insDepartments.reload) {
|
||||||
|
|||||||
@ -75,12 +75,13 @@ const useDeptCostProdPlans = () => {
|
|||||||
});
|
});
|
||||||
setState(pv => ({
|
setState(pv => ({
|
||||||
...pv,
|
...pv,
|
||||||
...data.XDATA_GRID,
|
fixedHeader: data.XDATA_GRID.fixedHeader,
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef,
|
fixedColumns: data.XDATA_GRID.fixedColumns,
|
||||||
rows: pv.pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...pv.rows, ...(data.XDATA_GRID.rows || [])],
|
columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
|
||||||
|
rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
|
||||||
dataLoaded: true,
|
dataLoaded: true,
|
||||||
reload: false,
|
reload: false,
|
||||||
morePages: (data.XDATA_GRID.rows || []).length >= DATA_GRID_PAGE_SIZE_LARGE
|
morePages: (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE_LARGE
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
if (state.reload) {
|
if (state.reload) {
|
||||||
@ -143,12 +144,11 @@ const useCostRouteLists = task => {
|
|||||||
});
|
});
|
||||||
setCostRouteLists(pv => ({
|
setCostRouteLists(pv => ({
|
||||||
...pv,
|
...pv,
|
||||||
...data.XDATA_GRID,
|
columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef,
|
rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
|
||||||
rows: pv.pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...pv.rows, ...(data.XDATA_GRID.rows || [])],
|
|
||||||
dataLoaded: true,
|
dataLoaded: true,
|
||||||
reload: false,
|
reload: false,
|
||||||
morePages: (data.XDATA_GRID.rows || []).length >= DATA_GRID_PAGE_SIZE_SMALL
|
morePages: (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE_SMALL
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
if (costRouteLists.reload && task) {
|
if (costRouteLists.reload && task) {
|
||||||
@ -202,12 +202,11 @@ const useCostRouteListsSpecs = mainRowRN => {
|
|||||||
});
|
});
|
||||||
setCostRouteListsSpecs(pv => ({
|
setCostRouteListsSpecs(pv => ({
|
||||||
...pv,
|
...pv,
|
||||||
...data.XDATA_GRID,
|
columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef,
|
rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
|
||||||
rows: pv.pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...pv.rows, ...(data.XDATA_GRID.rows || [])],
|
|
||||||
dataLoaded: true,
|
dataLoaded: true,
|
||||||
reload: false,
|
reload: false,
|
||||||
morePages: (data.XDATA_GRID.rows || []).length >= DATA_GRID_PAGE_SIZE_LARGE
|
morePages: (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE_LARGE
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
if (costRouteListsSpecs.reload) {
|
if (costRouteListsSpecs.reload) {
|
||||||
@ -259,12 +258,11 @@ const useIncomFromDeps = task => {
|
|||||||
});
|
});
|
||||||
setIncomFromDeps(pv => ({
|
setIncomFromDeps(pv => ({
|
||||||
...pv,
|
...pv,
|
||||||
...data.XDATA_GRID,
|
columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef,
|
rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
|
||||||
rows: pv.pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...pv.rows, ...(data.XDATA_GRID.rows || [])],
|
|
||||||
dataLoaded: true,
|
dataLoaded: true,
|
||||||
reload: false,
|
reload: false,
|
||||||
morePages: (data.XDATA_GRID.rows || []).length >= DATA_GRID_PAGE_SIZE_LARGE
|
morePages: (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE_LARGE
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
if (incomFromDeps.reload) {
|
if (incomFromDeps.reload) {
|
||||||
|
|||||||
@ -41,7 +41,7 @@ const STYLES = {
|
|||||||
width: "350px",
|
width: "350px",
|
||||||
display: "inline-block",
|
display: "inline-block",
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
[`& .MuiDrawer-paper`]: { width: "350px", display: "inline-block", boxSizing: "border-box", ...APP_STYLES.SCROLL }
|
[`& .MuiDrawer-paper`]: { width: "350px", display: "inline-block", boxSizing: "border-box" }
|
||||||
},
|
},
|
||||||
CONTAINER: { textAlign: "center" },
|
CONTAINER: { textAlign: "center" },
|
||||||
TITLE: { height: TITLE_HEIGHT, overflow: "hidden", paddingTop: TITLE_PADDING_TOP, paddingBottom: TITLE_PADDING_BOTTOM, display: "inline-table" },
|
TITLE: { height: TITLE_HEIGHT, overflow: "hidden", paddingTop: TITLE_PADDING_TOP, paddingBottom: TITLE_PADDING_BOTTOM, display: "inline-table" },
|
||||||
|
|||||||
@ -23,13 +23,6 @@ export const PANEL_UNITS = {
|
|||||||
PROJECT_STAGE_ARTS: "PROJECT_STAGE_ARTS"
|
PROJECT_STAGE_ARTS: "PROJECT_STAGE_ARTS"
|
||||||
};
|
};
|
||||||
|
|
||||||
//Общие стили
|
|
||||||
export const COMMON_PROJECTS_STYLES = {
|
|
||||||
FULL_SCREEN_DIALOG_CONTENT: {
|
|
||||||
padding: 0
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//-----------
|
//-----------
|
||||||
//Тело модуля
|
//Тело модуля
|
||||||
//-----------
|
//-----------
|
||||||
@ -257,7 +250,6 @@ export const rowExpandRender = ({
|
|||||||
columnsDef,
|
columnsDef,
|
||||||
row,
|
row,
|
||||||
pOnlineShowDocument,
|
pOnlineShowDocument,
|
||||||
pOnlineShowUnit,
|
|
||||||
showStages,
|
showStages,
|
||||||
showPayNotes,
|
showPayNotes,
|
||||||
showCostNotes,
|
showCostNotes,
|
||||||
@ -283,55 +275,42 @@ export const rowExpandRender = ({
|
|||||||
const linkButtons = () =>
|
const linkButtons = () =>
|
||||||
panelUnit === PANEL_UNITS.PROJECTS ? (
|
panelUnit === PANEL_UNITS.PROJECTS ? (
|
||||||
<>
|
<>
|
||||||
<Button variant="outlined" onClick={() => showStages({ sender: row })}>
|
<Button fullWidth variant="contained" onClick={() => showStages({ sender: row })}>
|
||||||
Этапы
|
Этапы
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="outlined" onClick={() => pOnlineShowDocument({ unitCode: "Projects", document: row.NRN, modal: false })}>
|
<Button fullWidth variant="contained" onClick={() => pOnlineShowDocument({ unitCode: "Projects", document: row.NRN })}>
|
||||||
К проекту
|
В раздел
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
) : panelUnit === PANEL_UNITS.PROJECT_STAGES ? (
|
) : panelUnit === PANEL_UNITS.PROJECT_STAGES ? (
|
||||||
<>
|
<>
|
||||||
<Button variant="outlined" onClick={() => showStageArts({ sender: row })}>
|
<Button fullWidth variant="contained" onClick={() => showStageArts({ sender: row })}>
|
||||||
Статьи
|
Статьи
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="outlined" onClick={() => showContracts({ sender: row })}>
|
<Button fullWidth variant="contained" onClick={() => showContracts({ sender: row })}>
|
||||||
Сисполнители
|
Сисполнители
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button fullWidth variant="contained" onClick={() => pOnlineShowDocument({ unitCode: "ProjectsStages", document: row.NRN })}>
|
||||||
variant="outlined"
|
В раздел
|
||||||
onClick={() =>
|
|
||||||
pOnlineShowUnit({
|
|
||||||
unitCode: "Projects",
|
|
||||||
inputParameters: [
|
|
||||||
{ name: "in_RN", value: row.NPROJECT },
|
|
||||||
{ name: "in_STAGE_RN", value: row.NRN }
|
|
||||||
],
|
|
||||||
modal: false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
К этапу
|
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
) : panelUnit === PANEL_UNITS.PROJECT_STAGE_CONTRACTS ? (
|
) : panelUnit === PANEL_UNITS.PROJECT_STAGE_CONTRACTS ? (
|
||||||
<Button
|
<Button
|
||||||
variant="outlined"
|
fullWidth
|
||||||
onClick={() => pOnlineShowDocument({ unitCode: row.SLNK_UNIT_SDOC_PREF, document: row.NLNK_DOCUMENT_SDOC_PREF, modal: false })}
|
variant="contained"
|
||||||
|
onClick={() => pOnlineShowDocument({ unitCode: row.SLNK_UNIT_SDOC_PREF, document: row.NLNK_DOCUMENT_SDOC_PREF })}
|
||||||
>
|
>
|
||||||
К договору
|
В раздел
|
||||||
</Button>
|
</Button>
|
||||||
) : null;
|
) : null;
|
||||||
//Сборка содержимого
|
//Сборка содержимого
|
||||||
return (
|
return (
|
||||||
<Box p={2}>
|
<Box p={2}>
|
||||||
<Grid container spacing={2}>
|
<Grid container spacing={2}>
|
||||||
<Grid item xs={12} md={12}>
|
<Grid item xs={12} md={1}>
|
||||||
<Stack spacing={2} direction="row">
|
<Stack spacing={2}>{linkButtons()}</Stack>
|
||||||
{linkButtons()}
|
|
||||||
</Stack>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} md={12}>
|
<Grid item xs={12} md={11}>
|
||||||
<Paper elevation={5}>
|
<Paper elevation={5}>
|
||||||
<Table sx={{ width: "100%" }} size="small">
|
<Table sx={{ width: "100%" }} size="small">
|
||||||
<TableBody>
|
<TableBody>
|
||||||
|
|||||||
@ -11,34 +11,23 @@ import React, { useState, useCallback, useEffect, useContext } from "react"; //
|
|||||||
import { Box, Grid, Paper, Fab, Icon } from "@mui/material"; //Интерфейсные компоненты
|
import { Box, Grid, Paper, Fab, Icon } from "@mui/material"; //Интерфейсные компоненты
|
||||||
import { object2Base64XML } from "../../core/utils"; //Вспомогательные процедуры и функции
|
import { object2Base64XML } from "../../core/utils"; //Вспомогательные процедуры и функции
|
||||||
import { TEXTS } from "../../../app.text"; //Тектовые ресурсы и константы
|
import { TEXTS } from "../../../app.text"; //Тектовые ресурсы и константы
|
||||||
import { APP_STYLES } from "../../../app.styles"; //Типовые стили
|
import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../components/p8p_data_grid"; //Таблица данных
|
||||||
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
|
|
||||||
import { P8PDataGrid, P8P_DATA_GRID_SIZE, P8P_DATA_GRID_MORE_HEIGHT, P8P_DATA_GRID_FILTERS_HEIGHT } from "../../components/p8p_data_grid"; //Таблица данных
|
|
||||||
import { P8PFullScreenDialog } from "../../components/p8p_fullscreen_dialog"; //Полноэкранный диалог
|
import { P8PFullScreenDialog } from "../../components/p8p_fullscreen_dialog"; //Полноэкранный диалог
|
||||||
import { P8PChart } from "../../components/p8p_chart"; //График
|
import { P8PChart } from "../../components/p8p_chart"; //График
|
||||||
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
||||||
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
||||||
import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений
|
import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений
|
||||||
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
||||||
import { COMMON_PROJECTS_STYLES, PANEL_UNITS, headCellRender, dataCellRender, valueFormatter, rowExpandRender } from "./layouts"; //Дополнительная разметка и вёрстка клиентских элементов
|
import { PANEL_UNITS, headCellRender, dataCellRender, valueFormatter, rowExpandRender } from "./layouts"; //Дополнительная разметка и вёрстка клиентских элементов
|
||||||
import { Stages } from "./stages"; //Список этапов проекта
|
import { Stages } from "./stages"; //Список этапов проекта
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
//Константы
|
//Константы
|
||||||
//---------
|
//---------
|
||||||
|
|
||||||
//Высота графиков
|
|
||||||
const CHART_HEIGHT = "300px";
|
|
||||||
|
|
||||||
//Стили
|
//Стили
|
||||||
const STYLES = {
|
const STYLES = {
|
||||||
TABLE_PROJECTS: (showCharts, morePages, filters) => ({
|
CHART: { maxHeight: "300px", display: "flex", justifyContent: "center" },
|
||||||
height: `calc(100vh - ${APP_BAR_HEIGHT} - ${showCharts ? CHART_HEIGHT : "0px"} - ${morePages ? P8P_DATA_GRID_MORE_HEIGHT : "0px"} - ${
|
|
||||||
filters ? P8P_DATA_GRID_FILTERS_HEIGHT : "0px"
|
|
||||||
} - 25px)`,
|
|
||||||
...APP_STYLES.SCROLL
|
|
||||||
}),
|
|
||||||
CHART: { maxHeight: CHART_HEIGHT, display: "flex", justifyContent: "center" },
|
|
||||||
CHART_PAPER: { height: "100%" },
|
CHART_PAPER: { height: "100%" },
|
||||||
CHART_FAB: { position: "absolute", top: 80, left: 16 }
|
CHART_FAB: { position: "absolute", top: 80, left: 16 }
|
||||||
};
|
};
|
||||||
@ -95,12 +84,11 @@ const Projects = () => {
|
|||||||
});
|
});
|
||||||
setProjectsDataGrid(pv => ({
|
setProjectsDataGrid(pv => ({
|
||||||
...pv,
|
...pv,
|
||||||
...data.XDATA_GRID,
|
columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef,
|
rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
|
||||||
rows: pv.pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...pv.rows, ...(data.XDATA_GRID.rows || [])],
|
|
||||||
dataLoaded: true,
|
dataLoaded: true,
|
||||||
reload: false,
|
reload: false,
|
||||||
morePages: (data.XDATA_GRID.rows || []).length >= configSystemPageSize
|
morePages: (data.XROWS || []).length >= configSystemPageSize
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
@ -231,16 +219,12 @@ const Projects = () => {
|
|||||||
{projectsDataGrid.dataLoaded ? (
|
{projectsDataGrid.dataLoaded ? (
|
||||||
<P8PDataGrid
|
<P8PDataGrid
|
||||||
{...P8P_DATA_GRID_CONFIG_PROPS}
|
{...P8P_DATA_GRID_CONFIG_PROPS}
|
||||||
containerComponentProps={{
|
|
||||||
sx: STYLES.TABLE_PROJECTS(showCharts, projectsDataGrid.morePages, (projectsDataGrid.filters || []).length > 0)
|
|
||||||
}}
|
|
||||||
columnsDef={projectsDataGrid.columnsDef}
|
columnsDef={projectsDataGrid.columnsDef}
|
||||||
rows={projectsDataGrid.rows}
|
rows={projectsDataGrid.rows}
|
||||||
size={P8P_DATA_GRID_SIZE.SMALL}
|
size={P8P_DATA_GRID_SIZE.SMALL}
|
||||||
filtersInitial={projectsDataGrid.filters}
|
filtersInitial={projectsDataGrid.filters}
|
||||||
morePages={projectsDataGrid.morePages}
|
morePages={projectsDataGrid.morePages}
|
||||||
reloading={projectsDataGrid.reload}
|
reloading={projectsDataGrid.reload}
|
||||||
fixedHeader={true}
|
|
||||||
expandable={true}
|
expandable={true}
|
||||||
headCellRender={headCellRender}
|
headCellRender={headCellRender}
|
||||||
dataCellRender={prms => dataCellRender({ ...prms, panelUnit: PANEL_UNITS.PROJECTS, showStages })}
|
dataCellRender={prms => dataCellRender({ ...prms, panelUnit: PANEL_UNITS.PROJECTS, showStages })}
|
||||||
@ -260,11 +244,7 @@ const Projects = () => {
|
|||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{projectsDataGrid.selectedProject ? (
|
{projectsDataGrid.selectedProject ? (
|
||||||
<P8PFullScreenDialog
|
<P8PFullScreenDialog title={`Этапы проекта "${projectsDataGrid.selectedProject.SNAME_USL}"`} onClose={handleStagesClose}>
|
||||||
title={`Этапы проекта "${projectsDataGrid.selectedProject.SNAME_USL}"`}
|
|
||||||
onClose={handleStagesClose}
|
|
||||||
contentProps={{ sx: COMMON_PROJECTS_STYLES.FULL_SCREEN_DIALOG_CONTENT }}
|
|
||||||
>
|
|
||||||
<Stages
|
<Stages
|
||||||
project={projectsDataGrid.selectedProject.NRN}
|
project={projectsDataGrid.selectedProject.NRN}
|
||||||
projectName={projectsDataGrid.selectedProject.SNAME_USL}
|
projectName={projectsDataGrid.selectedProject.SNAME_USL}
|
||||||
|
|||||||
@ -12,27 +12,13 @@ import PropTypes from "prop-types"; //Контроль свойств компо
|
|||||||
import { Box } from "@mui/material"; //Интерфейсные компоненты
|
import { Box } from "@mui/material"; //Интерфейсные компоненты
|
||||||
import { object2Base64XML } from "../../core/utils"; //Вспомогательные процедуры и функции
|
import { object2Base64XML } from "../../core/utils"; //Вспомогательные процедуры и функции
|
||||||
import { TEXTS } from "../../../app.text"; //Тектовые ресурсы и константы
|
import { TEXTS } from "../../../app.text"; //Тектовые ресурсы и константы
|
||||||
import { APP_STYLES } from "../../../app.styles"; //Типовые стили
|
import { P8PDataGrid, P8P_DATA_GRID_SIZE, P8P_DATA_GRID_FILTER_SHAPE } from "../../components/p8p_data_grid"; //Таблица данных
|
||||||
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
|
|
||||||
import { P8PDataGrid, P8P_DATA_GRID_SIZE, P8P_DATA_GRID_FILTER_SHAPE, P8P_DATA_GRID_FILTERS_HEIGHT } from "../../components/p8p_data_grid"; //Таблица данных
|
|
||||||
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
||||||
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
||||||
import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений
|
import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений
|
||||||
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
||||||
import { PANEL_UNITS, dataCellRender, valueFormatter } from "./layouts"; //Дополнительная разметка и вёрстка клиентских элементов
|
import { PANEL_UNITS, dataCellRender, valueFormatter } from "./layouts"; //Дополнительная разметка и вёрстка клиентских элементов
|
||||||
|
|
||||||
//---------
|
|
||||||
//Константы
|
|
||||||
//---------
|
|
||||||
|
|
||||||
//Стили
|
|
||||||
const STYLES = {
|
|
||||||
TABLE_ARTS: filters => ({
|
|
||||||
height: `calc(100vh - ${APP_BAR_HEIGHT} - ${filters ? P8P_DATA_GRID_FILTERS_HEIGHT : "0px"} - 16px)`,
|
|
||||||
...APP_STYLES.SCROLL
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
//-----------
|
//-----------
|
||||||
//Тело модуля
|
//Тело модуля
|
||||||
//-----------
|
//-----------
|
||||||
@ -71,9 +57,8 @@ const StageArts = ({ stage, filters }) => {
|
|||||||
});
|
});
|
||||||
setStageArtsDataGrid(pv => ({
|
setStageArtsDataGrid(pv => ({
|
||||||
...pv,
|
...pv,
|
||||||
...data.XDATA_GRID,
|
columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef,
|
rows: [...(data.XROWS || [])],
|
||||||
rows: [...(data.XDATA_GRID.rows || [])],
|
|
||||||
dataLoaded: true,
|
dataLoaded: true,
|
||||||
reload: false
|
reload: false
|
||||||
}));
|
}));
|
||||||
@ -114,12 +99,10 @@ const StageArts = ({ stage, filters }) => {
|
|||||||
{stageArtsDataGrid.dataLoaded ? (
|
{stageArtsDataGrid.dataLoaded ? (
|
||||||
<P8PDataGrid
|
<P8PDataGrid
|
||||||
{...P8P_DATA_GRID_CONFIG_PROPS}
|
{...P8P_DATA_GRID_CONFIG_PROPS}
|
||||||
containerComponentProps={{ sx: STYLES.TABLE_ARTS((stageArtsDataGrid.filters || []).length > 0), elevation: 0 }}
|
|
||||||
columnsDef={stageArtsDataGrid.columnsDef}
|
columnsDef={stageArtsDataGrid.columnsDef}
|
||||||
filtersInitial={filters}
|
filtersInitial={filters}
|
||||||
rows={stageArtsDataGrid.rows}
|
rows={stageArtsDataGrid.rows}
|
||||||
size={P8P_DATA_GRID_SIZE.SMALL}
|
size={P8P_DATA_GRID_SIZE.SMALL}
|
||||||
fixedHeader={true}
|
|
||||||
morePages={false}
|
morePages={false}
|
||||||
reloading={stageArtsDataGrid.reload}
|
reloading={stageArtsDataGrid.reload}
|
||||||
dataCellRender={prms => dataCellRender({ ...prms, panelUnit: PANEL_UNITS.PROJECT_STAGE_ARTS, showCostNotes, showContracts })}
|
dataCellRender={prms => dataCellRender({ ...prms, panelUnit: PANEL_UNITS.PROJECT_STAGE_ARTS, showCostNotes, showContracts })}
|
||||||
|
|||||||
@ -12,35 +12,13 @@ import PropTypes from "prop-types"; //Контроль свойств компо
|
|||||||
import { Box } from "@mui/material"; //Интерфейсные компоненты
|
import { Box } from "@mui/material"; //Интерфейсные компоненты
|
||||||
import { object2Base64XML } from "../../core/utils"; //Вспомогательные процедуры и функции
|
import { object2Base64XML } from "../../core/utils"; //Вспомогательные процедуры и функции
|
||||||
import { TEXTS } from "../../../app.text"; //Тектовые ресурсы и константы
|
import { TEXTS } from "../../../app.text"; //Тектовые ресурсы и константы
|
||||||
import { APP_STYLES } from "../../../app.styles"; //Типовые стили
|
import { P8PDataGrid, P8P_DATA_GRID_SIZE, P8P_DATA_GRID_FILTER_SHAPE } from "../../components/p8p_data_grid"; //Таблица данных
|
||||||
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
|
|
||||||
import {
|
|
||||||
P8PDataGrid,
|
|
||||||
P8P_DATA_GRID_SIZE,
|
|
||||||
P8P_DATA_GRID_FILTER_SHAPE,
|
|
||||||
P8P_DATA_GRID_MORE_HEIGHT,
|
|
||||||
P8P_DATA_GRID_FILTERS_HEIGHT
|
|
||||||
} from "../../components/p8p_data_grid"; //Таблица данных
|
|
||||||
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
||||||
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
||||||
import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений
|
import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений
|
||||||
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
||||||
import { PANEL_UNITS, dataCellRender, valueFormatter, rowExpandRender } from "./layouts"; //Дополнительная разметка и вёрстка клиентских элементов
|
import { PANEL_UNITS, dataCellRender, valueFormatter, rowExpandRender } from "./layouts"; //Дополнительная разметка и вёрстка клиентских элементов
|
||||||
|
|
||||||
//---------
|
|
||||||
//Константы
|
|
||||||
//---------
|
|
||||||
|
|
||||||
//Стили
|
|
||||||
const STYLES = {
|
|
||||||
TABLE_CONTRACTS: (morePages, filters) => ({
|
|
||||||
height: `calc(100vh - ${APP_BAR_HEIGHT} - ${morePages ? P8P_DATA_GRID_MORE_HEIGHT : "0px"} - ${
|
|
||||||
filters ? P8P_DATA_GRID_FILTERS_HEIGHT : "0px"
|
|
||||||
} - 16px)`,
|
|
||||||
...APP_STYLES.SCROLL
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
//-----------
|
//-----------
|
||||||
//Тело модуля
|
//Тело модуля
|
||||||
//-----------
|
//-----------
|
||||||
@ -89,12 +67,11 @@ const StageContracts = ({ stage, filters }) => {
|
|||||||
});
|
});
|
||||||
setStageContractsDataGrid(pv => ({
|
setStageContractsDataGrid(pv => ({
|
||||||
...pv,
|
...pv,
|
||||||
...data.XDATA_GRID,
|
columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef,
|
rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
|
||||||
rows: pv.pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...pv.rows, ...(data.XDATA_GRID.rows || [])],
|
|
||||||
dataLoaded: true,
|
dataLoaded: true,
|
||||||
reload: false,
|
reload: false,
|
||||||
morePages: (data.XDATA_GRID.rows || []).length >= configSystemPageSize
|
morePages: (data.XROWS || []).length >= configSystemPageSize
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
@ -159,17 +136,12 @@ const StageContracts = ({ stage, filters }) => {
|
|||||||
{stageContractsDataGrid.dataLoaded ? (
|
{stageContractsDataGrid.dataLoaded ? (
|
||||||
<P8PDataGrid
|
<P8PDataGrid
|
||||||
{...P8P_DATA_GRID_CONFIG_PROPS}
|
{...P8P_DATA_GRID_CONFIG_PROPS}
|
||||||
containerComponentProps={{
|
|
||||||
sx: STYLES.TABLE_CONTRACTS(stageContractsDataGrid.morePages, (stageContractsDataGrid.filters || []).length > 0),
|
|
||||||
elevation: 0
|
|
||||||
}}
|
|
||||||
columnsDef={stageContractsDataGrid.columnsDef}
|
columnsDef={stageContractsDataGrid.columnsDef}
|
||||||
filtersInitial={filters}
|
filtersInitial={filters}
|
||||||
rows={stageContractsDataGrid.rows}
|
rows={stageContractsDataGrid.rows}
|
||||||
size={P8P_DATA_GRID_SIZE.SMALL}
|
size={P8P_DATA_GRID_SIZE.SMALL}
|
||||||
morePages={stageContractsDataGrid.morePages}
|
morePages={stageContractsDataGrid.morePages}
|
||||||
reloading={stageContractsDataGrid.reload}
|
reloading={stageContractsDataGrid.reload}
|
||||||
fixedHeader={true}
|
|
||||||
expandable={true}
|
expandable={true}
|
||||||
dataCellRender={prms => dataCellRender({ ...prms, panelUnit: PANEL_UNITS.PROJECT_STAGE_CONTRACTS, pOnlineShowDocument })}
|
dataCellRender={prms => dataCellRender({ ...prms, panelUnit: PANEL_UNITS.PROJECT_STAGE_CONTRACTS, pOnlineShowDocument })}
|
||||||
rowExpandRender={prms =>
|
rowExpandRender={prms =>
|
||||||
|
|||||||
@ -12,15 +12,7 @@ import PropTypes from "prop-types"; //Контроль свойств компо
|
|||||||
import { Box } from "@mui/material"; //Интерфейсные компоненты
|
import { Box } from "@mui/material"; //Интерфейсные компоненты
|
||||||
import { object2Base64XML } from "../../core/utils"; //Вспомогательные процедуры и функции
|
import { object2Base64XML } from "../../core/utils"; //Вспомогательные процедуры и функции
|
||||||
import { TEXTS } from "../../../app.text"; //Тектовые ресурсы и константы
|
import { TEXTS } from "../../../app.text"; //Тектовые ресурсы и константы
|
||||||
import { APP_STYLES } from "../../../app.styles"; //Типовые стили
|
import { P8PDataGrid, P8P_DATA_GRID_SIZE, P8P_DATA_GRID_FILTER_SHAPE } from "../../components/p8p_data_grid"; //Таблица данных
|
||||||
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
|
|
||||||
import {
|
|
||||||
P8PDataGrid,
|
|
||||||
P8P_DATA_GRID_SIZE,
|
|
||||||
P8P_DATA_GRID_FILTER_SHAPE,
|
|
||||||
P8P_DATA_GRID_MORE_HEIGHT,
|
|
||||||
P8P_DATA_GRID_FILTERS_HEIGHT
|
|
||||||
} from "../../components/p8p_data_grid"; //Таблица данных
|
|
||||||
import { P8PFullScreenDialog } from "../../components/p8p_fullscreen_dialog"; //Полноэкранный диалог
|
import { P8PFullScreenDialog } from "../../components/p8p_fullscreen_dialog"; //Полноэкранный диалог
|
||||||
import { StageArts } from "./stage_arts"; //Калькуляция этапа проекта
|
import { StageArts } from "./stage_arts"; //Калькуляция этапа проекта
|
||||||
import { StageContracts } from "./stage_contracts"; //Договоры с соисполнителями этапа проекта
|
import { StageContracts } from "./stage_contracts"; //Договоры с соисполнителями этапа проекта
|
||||||
@ -28,21 +20,7 @@ import { BackEndСtx } from "../../context/backend"; //Контекст взаи
|
|||||||
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
||||||
import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений
|
import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений
|
||||||
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
||||||
import { COMMON_PROJECTS_STYLES, PANEL_UNITS, headCellRender, dataCellRender, valueFormatter, rowExpandRender } from "./layouts"; //Дополнительная разметка и вёрстка клиентских элементов
|
import { PANEL_UNITS, headCellRender, dataCellRender, valueFormatter, rowExpandRender } from "./layouts"; //Дополнительная разметка и вёрстка клиентских элементов
|
||||||
|
|
||||||
//---------
|
|
||||||
//Константы
|
|
||||||
//---------
|
|
||||||
|
|
||||||
//Стили
|
|
||||||
const STYLES = {
|
|
||||||
TABLE_STAGES: (morePages, filters) => ({
|
|
||||||
height: `calc(100vh - ${APP_BAR_HEIGHT} - ${morePages ? P8P_DATA_GRID_MORE_HEIGHT : "0px"} - ${
|
|
||||||
filters ? P8P_DATA_GRID_FILTERS_HEIGHT : "0px"
|
|
||||||
} - 16px)`,
|
|
||||||
...APP_STYLES.SCROLL
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
//-----------
|
//-----------
|
||||||
//Тело модуля
|
//Тело модуля
|
||||||
@ -93,12 +71,11 @@ const Stages = ({ project, projectName, filters }) => {
|
|||||||
});
|
});
|
||||||
setStagesDataGrid(pv => ({
|
setStagesDataGrid(pv => ({
|
||||||
...pv,
|
...pv,
|
||||||
...data.XDATA_GRID,
|
columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef,
|
rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
|
||||||
rows: pv.pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...pv.rows, ...(data.XDATA_GRID.rows || [])],
|
|
||||||
dataLoaded: true,
|
dataLoaded: true,
|
||||||
reload: false,
|
reload: false,
|
||||||
morePages: (data.XDATA_GRID.rows || []).length >= configSystemPageSize
|
morePages: (data.XROWS || []).length >= configSystemPageSize
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
@ -177,17 +154,12 @@ const Stages = ({ project, projectName, filters }) => {
|
|||||||
{stagesDataGrid.dataLoaded ? (
|
{stagesDataGrid.dataLoaded ? (
|
||||||
<P8PDataGrid
|
<P8PDataGrid
|
||||||
{...P8P_DATA_GRID_CONFIG_PROPS}
|
{...P8P_DATA_GRID_CONFIG_PROPS}
|
||||||
containerComponentProps={{
|
|
||||||
sx: STYLES.TABLE_STAGES(stagesDataGrid.morePages, (stagesDataGrid.filters || []).length > 0),
|
|
||||||
elevation: 0
|
|
||||||
}}
|
|
||||||
columnsDef={stagesDataGrid.columnsDef}
|
columnsDef={stagesDataGrid.columnsDef}
|
||||||
filtersInitial={filters}
|
filtersInitial={filters}
|
||||||
rows={stagesDataGrid.rows}
|
rows={stagesDataGrid.rows}
|
||||||
size={P8P_DATA_GRID_SIZE.SMALL}
|
size={P8P_DATA_GRID_SIZE.SMALL}
|
||||||
morePages={stagesDataGrid.morePages}
|
morePages={stagesDataGrid.morePages}
|
||||||
reloading={stagesDataGrid.reload}
|
reloading={stagesDataGrid.reload}
|
||||||
fixedHeader={true}
|
|
||||||
expandable={true}
|
expandable={true}
|
||||||
headCellRender={headCellRender}
|
headCellRender={headCellRender}
|
||||||
dataCellRender={prms => dataCellRender({ ...prms, panelUnit: PANEL_UNITS.PROJECT_STAGES, showStageArts, showContracts })}
|
dataCellRender={prms => dataCellRender({ ...prms, panelUnit: PANEL_UNITS.PROJECT_STAGES, showStageArts, showContracts })}
|
||||||
@ -196,7 +168,6 @@ const Stages = ({ project, projectName, filters }) => {
|
|||||||
...prms,
|
...prms,
|
||||||
panelUnit: PANEL_UNITS.PROJECT_STAGES,
|
panelUnit: PANEL_UNITS.PROJECT_STAGES,
|
||||||
pOnlineShowDocument,
|
pOnlineShowDocument,
|
||||||
pOnlineShowUnit,
|
|
||||||
showStageArts,
|
showStageArts,
|
||||||
showContracts,
|
showContracts,
|
||||||
showPayNotes,
|
showPayNotes,
|
||||||
@ -214,7 +185,6 @@ const Stages = ({ project, projectName, filters }) => {
|
|||||||
<P8PFullScreenDialog
|
<P8PFullScreenDialog
|
||||||
title={`Договоры этапа "${stagesDataGrid.selectedStageNumb}" проекта "${projectName}"`}
|
title={`Договоры этапа "${stagesDataGrid.selectedStageNumb}" проекта "${projectName}"`}
|
||||||
onClose={handleStageContractsClose}
|
onClose={handleStageContractsClose}
|
||||||
contentProps={{ sx: COMMON_PROJECTS_STYLES.FULL_SCREEN_DIALOG_CONTENT }}
|
|
||||||
>
|
>
|
||||||
<StageContracts stage={stagesDataGrid.showStageContracts} filters={stagesDataGrid.stageContractsFilters} />
|
<StageContracts stage={stagesDataGrid.showStageContracts} filters={stagesDataGrid.stageContractsFilters} />
|
||||||
</P8PFullScreenDialog>
|
</P8PFullScreenDialog>
|
||||||
@ -223,7 +193,6 @@ const Stages = ({ project, projectName, filters }) => {
|
|||||||
<P8PFullScreenDialog
|
<P8PFullScreenDialog
|
||||||
title={`Калькуляция этапа "${stagesDataGrid.selectedStageNumb}" проекта "${projectName}"`}
|
title={`Калькуляция этапа "${stagesDataGrid.selectedStageNumb}" проекта "${projectName}"`}
|
||||||
onClose={handleStageArtsClose}
|
onClose={handleStageArtsClose}
|
||||||
contentProps={{ sx: COMMON_PROJECTS_STYLES.FULL_SCREEN_DIALOG_CONTENT }}
|
|
||||||
>
|
>
|
||||||
<StageArts stage={stagesDataGrid.showStageArts} filters={stagesDataGrid.stageArtsFilters} />
|
<StageArts stage={stagesDataGrid.showStageArts} filters={stagesDataGrid.stageArtsFilters} />
|
||||||
</P8PFullScreenDialog>
|
</P8PFullScreenDialog>
|
||||||
|
|||||||
@ -55,10 +55,11 @@ const PrjGraph = () => {
|
|||||||
const data = await executeStored({ stored: "PKG_P8PANELS_PROJECTS.GRAPH", args: {}, respArg: "COUT" });
|
const data = await executeStored({ stored: "PKG_P8PANELS_PROJECTS.GRAPH", args: {}, respArg: "COUT" });
|
||||||
setdataGrid(pv => ({
|
setdataGrid(pv => ({
|
||||||
...pv,
|
...pv,
|
||||||
...data.XDATA_GRID,
|
fixedHeader: data.XDATA_GRID.fixedHeader,
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef,
|
fixedColumns: data.XDATA_GRID.fixedColumns,
|
||||||
rows: [...(data.XDATA_GRID.rows || [])],
|
columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
|
||||||
groups: [...(data.XDATA_GRID.groups || [])],
|
rows: [...(data.XROWS || [])],
|
||||||
|
groups: [...(data.XGROUPS || [])],
|
||||||
dataLoaded: true,
|
dataLoaded: true,
|
||||||
reload: false
|
reload: false
|
||||||
}));
|
}));
|
||||||
|
|||||||
@ -57,12 +57,11 @@ const LabFactRptDtl = ({ periodId, title, onHide }) => {
|
|||||||
});
|
});
|
||||||
setFactRptDtl(pv => ({
|
setFactRptDtl(pv => ({
|
||||||
...pv,
|
...pv,
|
||||||
...data.XDATA_GRID,
|
columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef,
|
rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
|
||||||
rows: pv.pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...pv.rows, ...(data.XDATA_GRID.rows || [])],
|
|
||||||
dataLoaded: true,
|
dataLoaded: true,
|
||||||
reload: false,
|
reload: false,
|
||||||
morePages: (data.XDATA_GRID.rows || []).length >= configSystemPageSize
|
morePages: (data.XROWS || []).length >= configSystemPageSize
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
|
|||||||
@ -56,12 +56,11 @@ const LabPlanFOTDtl = ({ periodId, title, onHide }) => {
|
|||||||
});
|
});
|
||||||
setPlanFOTDtl(pv => ({
|
setPlanFOTDtl(pv => ({
|
||||||
...pv,
|
...pv,
|
||||||
...data.XDATA_GRID,
|
columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef,
|
rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
|
||||||
rows: pv.pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...pv.rows, ...(data.XDATA_GRID.rows || [])],
|
|
||||||
dataLoaded: true,
|
dataLoaded: true,
|
||||||
reload: false,
|
reload: false,
|
||||||
morePages: (data.XDATA_GRID.rows || []).length >= configSystemPageSize
|
morePages: (data.XROWS || []).length >= configSystemPageSize
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
|
|||||||
@ -61,12 +61,11 @@ const LabPlanJobsDtl = ({ periodId, title, onHide, onProjectClick }) => {
|
|||||||
});
|
});
|
||||||
setPlanJobsDtl(pv => ({
|
setPlanJobsDtl(pv => ({
|
||||||
...pv,
|
...pv,
|
||||||
...data.XDATA_GRID,
|
columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef,
|
rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
|
||||||
rows: pv.pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...pv.rows, ...(data.XDATA_GRID.rows || [])],
|
|
||||||
dataLoaded: true,
|
dataLoaded: true,
|
||||||
reload: false,
|
reload: false,
|
||||||
morePages: (data.XDATA_GRID.rows || []).length >= configSystemPageSize
|
morePages: (data.XROWS || []).length >= configSystemPageSize
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
|
|||||||
@ -265,7 +265,8 @@ const PrjJobs = () => {
|
|||||||
selectedProjectJobsLoaded: false,
|
selectedProjectJobsLoaded: false,
|
||||||
selectedProject: null,
|
selectedProject: null,
|
||||||
selectedProjectDocRn: null,
|
selectedProjectDocRn: null,
|
||||||
gantt: {},
|
selectedProjectGanttDef: {},
|
||||||
|
selectedProjectTasks: [],
|
||||||
showInitDialog: false
|
showInitDialog: false
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -307,9 +308,8 @@ const PrjJobs = () => {
|
|||||||
setState(pv => ({
|
setState(pv => ({
|
||||||
...pv,
|
...pv,
|
||||||
selectedProjectJobsLoaded: true,
|
selectedProjectJobsLoaded: true,
|
||||||
gantt: {
|
selectedProjectGanttDef: tasksOnly === true ? { ...pv.selectedProjectGanttDef } : data.XGANTT_DEF ? { ...data.XGANTT_DEF } : {},
|
||||||
...(tasksOnly === true ? { ...pv.gantt, tasks: [...data.XGANTT.tasks] } : data.XGANTT ? { ...data.XGANTT } : {})
|
selectedProjectTasks: [...data.XGANTT_TASKS]
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
[executeStored, state.ident, state.selectedProject]
|
[executeStored, state.ident, state.selectedProject]
|
||||||
@ -394,7 +394,8 @@ const PrjJobs = () => {
|
|||||||
selectedProject: project,
|
selectedProject: project,
|
||||||
selectedProjectDocRn: projectDocRn,
|
selectedProjectDocRn: projectDocRn,
|
||||||
selectedProjectJobsLoaded: false,
|
selectedProjectJobsLoaded: false,
|
||||||
gantt: {},
|
selectedProjectTasks: [],
|
||||||
|
selectedProjectGanttDef: {},
|
||||||
showProjectsList: false
|
showProjectsList: false
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
@ -406,7 +407,8 @@ const PrjJobs = () => {
|
|||||||
selectedProjectJobsLoaded: false,
|
selectedProjectJobsLoaded: false,
|
||||||
selectedProject: null,
|
selectedProject: null,
|
||||||
selectedProjectDocRn: null,
|
selectedProjectDocRn: null,
|
||||||
gantt: {},
|
selectedProjectTasks: [],
|
||||||
|
selectedProjectGanttDef: {},
|
||||||
showProjectsList: false
|
showProjectsList: false
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -513,10 +515,11 @@ const PrjJobs = () => {
|
|||||||
{state.selectedProjectJobsLoaded ? (
|
{state.selectedProjectJobsLoaded ? (
|
||||||
<P8PGantt
|
<P8PGantt
|
||||||
{...P8P_GANTT_CONFIG_PROPS}
|
{...P8P_GANTT_CONFIG_PROPS}
|
||||||
{...state.gantt}
|
{...state.selectedProjectGanttDef}
|
||||||
containerStyle={STYLES.GANTT_CONTAINER}
|
containerStyle={STYLES.GANTT_CONTAINER}
|
||||||
titleStyle={STYLES.GANTT_TITLE}
|
titleStyle={STYLES.GANTT_TITLE}
|
||||||
onTitleClick={handleTitleClick}
|
onTitleClick={handleTitleClick}
|
||||||
|
tasks={state.selectedProjectTasks}
|
||||||
onTaskDatesChange={handleTaskDatesChange}
|
onTaskDatesChange={handleTaskDatesChange}
|
||||||
taskAttributeRenderer={taskAttributeRenderer}
|
taskAttributeRenderer={taskAttributeRenderer}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -79,12 +79,11 @@ const ResMon = ({ ident, onPlanJobsDtlProjectClick }) => {
|
|||||||
});
|
});
|
||||||
setPeriods(pv => ({
|
setPeriods(pv => ({
|
||||||
...pv,
|
...pv,
|
||||||
...data.XDATA_GRID,
|
columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef,
|
rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
|
||||||
rows: pv.pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...pv.rows, ...(data.XDATA_GRID.rows || [])],
|
|
||||||
dataLoaded: true,
|
dataLoaded: true,
|
||||||
reload: false,
|
reload: false,
|
||||||
morePages: (data.XDATA_GRID.rows || []).length >= configSystemPageSize
|
morePages: (data.XROWS || []).length >= configSystemPageSize
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}, [ident, peridos.reload, peridos.orders, peridos.dataLoaded, peridos.pageNumber, executeStored, configSystemPageSize, SERV_DATA_TYPE_CLOB]);
|
}, [ident, peridos.reload, peridos.orders, peridos.dataLoaded, peridos.pageNumber, executeStored, configSystemPageSize, SERV_DATA_TYPE_CLOB]);
|
||||||
|
|||||||
@ -97,15 +97,16 @@ const useConf = (currentTab, handleSectionChange) => {
|
|||||||
sections.map(s => {
|
sections.map(s => {
|
||||||
let dg = {};
|
let dg = {};
|
||||||
Object.assign(dg, dataGrid, {
|
Object.assign(dg, dataGrid, {
|
||||||
...s.XDATA.XDATA_GRID,
|
|
||||||
rn: s.NRN,
|
rn: s.NRN,
|
||||||
code: s.SCODE,
|
code: s.SCODE,
|
||||||
name: s.SNAME,
|
name: s.SNAME,
|
||||||
delete_allow: s.NDELETE_ALLOW,
|
delete_allow: s.NDELETE_ALLOW,
|
||||||
dataLoaded: true,
|
dataLoaded: true,
|
||||||
columnsDef: [...(s.XDATA.XDATA_GRID.columnsDef || [])],
|
columnsDef: [...(s.XDATA.XCOLUMNS_DEF || [])],
|
||||||
groups: [...(s.XDATA.XDATA_GRID.groups || [])],
|
groups: [...(s.XDATA.XGROUPS || [])],
|
||||||
rows: [...(s.XDATA.XDATA_GRID.rows || [])],
|
rows: [...(s.XDATA.XROWS || [])],
|
||||||
|
fixedHeader: s.XDATA.XDATA_GRID.fixedHeader,
|
||||||
|
fixedColumns: s.XDATA.XDATA_GRID.fixedColumns,
|
||||||
reload: false
|
reload: false
|
||||||
});
|
});
|
||||||
//Если раздел имеет составы показателей
|
//Если раздел имеет составы показателей
|
||||||
|
|||||||
@ -33,7 +33,7 @@ const STYLES = {
|
|||||||
//Пример: Графики "P8PChart"
|
//Пример: Графики "P8PChart"
|
||||||
const Chart = ({ title }) => {
|
const Chart = ({ title }) => {
|
||||||
//Собственное состояние - график
|
//Собственное состояние - график
|
||||||
const [chart, setChart] = useState({ loaded: false });
|
const [chart, setChart] = useState({ loaded: false, labels: [], datasets: [] });
|
||||||
|
|
||||||
//Подключение к контексту взаимодействия с сервером
|
//Подключение к контексту взаимодействия с сервером
|
||||||
const { executeStored } = useContext(BackEndСtx);
|
const { executeStored } = useContext(BackEndСtx);
|
||||||
|
|||||||
@ -1,301 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - Примеры для разработчиков
|
|
||||||
Пример: Циклограмма "P8PCyclogram"
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
//Подключение библиотек
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
import React, { useState, useContext, useCallback, useEffect } from "react"; //Классы React
|
|
||||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
|
||||||
import {
|
|
||||||
Typography,
|
|
||||||
Grid,
|
|
||||||
Button,
|
|
||||||
Box,
|
|
||||||
DialogContent,
|
|
||||||
List,
|
|
||||||
ListItem,
|
|
||||||
ListItemText,
|
|
||||||
Divider,
|
|
||||||
TextField,
|
|
||||||
DialogActions,
|
|
||||||
Stack,
|
|
||||||
Icon
|
|
||||||
} from "@mui/material"; //Интерфейсные элементы
|
|
||||||
import { formatDateJSONDateOnly, formatDateRF } from "../../core/utils"; //Вспомогательные функции
|
|
||||||
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
|
|
||||||
import { P8PCyclogram } from "../../components/p8p_cyclogram"; //Циклограмма
|
|
||||||
import { P8P_CYCLOGRAM_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
|
||||||
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
|
||||||
|
|
||||||
//---------
|
|
||||||
//Константы
|
|
||||||
//---------
|
|
||||||
|
|
||||||
//Отступ контейнера страницы от заголовка
|
|
||||||
const CONTAINER_PADDING_TOP = "20px";
|
|
||||||
|
|
||||||
//Высота заголовка страницы
|
|
||||||
const TITLE_HEIGHT = "47px";
|
|
||||||
|
|
||||||
//Высота строк
|
|
||||||
const LINE_HEIGHT = 30;
|
|
||||||
|
|
||||||
//Стили
|
|
||||||
const STYLES = {
|
|
||||||
CONTAINER: { textAlign: "center", paddingTop: CONTAINER_PADDING_TOP },
|
|
||||||
TITLE: { paddingBottom: "15px", height: TITLE_HEIGHT },
|
|
||||||
CYCLOGRAM_CONTAINER: {
|
|
||||||
height: `calc(100vh - ${APP_BAR_HEIGHT} - ${TITLE_HEIGHT} - ${CONTAINER_PADDING_TOP})`,
|
|
||||||
width: "100vw",
|
|
||||||
paddingTop: "5px"
|
|
||||||
},
|
|
||||||
TASK_EDITOR_CONTENT: { minWidth: 400, overflowX: "auto" },
|
|
||||||
TASK_EDITOR_LIST: { width: "100%", minWidth: 300, maxWidth: 700, bgcolor: "background.paper" },
|
|
||||||
GROUP_HEADER: height => ({
|
|
||||||
border: "1px solid",
|
|
||||||
backgroundColor: "#ecf8fb",
|
|
||||||
height: height,
|
|
||||||
borderRadius: "10px",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "space-around"
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
//---------------------------------------------
|
|
||||||
//Вспомогательные функции форматирования данных
|
|
||||||
//---------------------------------------------
|
|
||||||
|
|
||||||
//Диалог открытия задачи
|
|
||||||
const CustomTaskDialog = ({ task, ident, handleReload, close }) => {
|
|
||||||
//Собственное состояние
|
|
||||||
const [taskDates, setTaskDates] = useState({ start: task.ddate_start, end: task.ddate_end });
|
|
||||||
|
|
||||||
//Тип проекта
|
|
||||||
const textType = task.type === 0 ? "Задачи проекта" : task.type === 1 ? "Этап проекта" : "Работа проекта";
|
|
||||||
|
|
||||||
//Подключение к контексту взаимодействия с сервером
|
|
||||||
const { executeStored } = useContext(BackEndСtx);
|
|
||||||
|
|
||||||
//Изменение дат задачи
|
|
||||||
const changeDates = useCallback(async () => {
|
|
||||||
//Изменяем даты задачи
|
|
||||||
await executeStored({
|
|
||||||
stored: "PKG_P8PANELS_SAMPLES.CYCLOGRAM_TASK_MODIFY",
|
|
||||||
args: {
|
|
||||||
NIDENT: ident,
|
|
||||||
NRN: task.rn,
|
|
||||||
SDATE_FROM: formatDateRF(taskDates.start),
|
|
||||||
SDATE_TO: formatDateRF(taskDates.end)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
handleReload();
|
|
||||||
close();
|
|
||||||
}, [close, executeStored, handleReload, ident, task.rn, taskDates.end, taskDates.start]);
|
|
||||||
|
|
||||||
//При нажатии OK
|
|
||||||
const handleOk = () => {
|
|
||||||
//Изменяем даты задачи
|
|
||||||
changeDates();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<DialogContent sx={STYLES.TASK_EDITOR_CONTENT}>
|
|
||||||
<List sx={STYLES.TASK_EDITOR_LIST}>
|
|
||||||
<ListItem alignItems="flex-start">
|
|
||||||
<ListItemText primary={"Наименование"} secondary={task.fullName} />
|
|
||||||
</ListItem>
|
|
||||||
<Divider component="li" />
|
|
||||||
<ListItem alignItems="flex-start">
|
|
||||||
<ListItemText
|
|
||||||
secondaryTypographyProps={{ component: "span" }}
|
|
||||||
primary={"Начало"}
|
|
||||||
secondary={
|
|
||||||
<TextField
|
|
||||||
error={!taskDates.start}
|
|
||||||
disabled={task.type !== 2}
|
|
||||||
name="start"
|
|
||||||
fullWidth
|
|
||||||
required
|
|
||||||
InputLabelProps={{ shrink: true }}
|
|
||||||
type={"date"}
|
|
||||||
value={taskDates.start}
|
|
||||||
onChange={e => setTaskDates(pv => ({ ...pv, start: e.target.value }))}
|
|
||||||
variant="standard"
|
|
||||||
size="small"
|
|
||||||
margin="normal"
|
|
||||||
></TextField>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</ListItem>
|
|
||||||
<Divider component="li" />
|
|
||||||
<ListItem alignItems="flex-start">
|
|
||||||
<ListItemText
|
|
||||||
secondaryTypographyProps={{ component: "span" }}
|
|
||||||
primary={"Окончание"}
|
|
||||||
secondary={
|
|
||||||
<TextField
|
|
||||||
error={!taskDates.end}
|
|
||||||
disabled={task.type !== 2}
|
|
||||||
name="end"
|
|
||||||
fullWidth
|
|
||||||
required
|
|
||||||
InputLabelProps={{ shrink: true }}
|
|
||||||
type={"date"}
|
|
||||||
value={taskDates.end}
|
|
||||||
onChange={e => setTaskDates(pv => ({ ...pv, end: e.target.value }))}
|
|
||||||
variant="standard"
|
|
||||||
size="small"
|
|
||||||
margin="normal"
|
|
||||||
></TextField>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</ListItem>
|
|
||||||
<Divider component="li" />
|
|
||||||
<ListItem alignItems="flex-start">
|
|
||||||
<ListItemText
|
|
||||||
primary={"Тип"}
|
|
||||||
secondaryTypographyProps={{ component: "span" }}
|
|
||||||
secondary={
|
|
||||||
<Stack direction="row" gap={0.5}>
|
|
||||||
<Icon title={textType}>{task.type === 0 ? "description" : task.type === 1 ? "check" : "work_outline"}</Icon>
|
|
||||||
{textType}
|
|
||||||
</Stack>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</ListItem>
|
|
||||||
</List>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button onClick={handleOk}>ОК</Button>
|
|
||||||
<Button onClick={close}>Отмена</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств - Диалог открытия задачи
|
|
||||||
CustomTaskDialog.propTypes = {
|
|
||||||
task: PropTypes.object.isRequired,
|
|
||||||
ident: PropTypes.number.isRequired,
|
|
||||||
handleReload: PropTypes.func.isRequired,
|
|
||||||
close: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
//Заголовок группы
|
|
||||||
const CustomGroupHeader = ({ group }) => {
|
|
||||||
return (
|
|
||||||
<Box sx={STYLES.GROUP_HEADER(group.height)}>
|
|
||||||
<Typography variant="body2">{group.name}</Typography>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств - Заголовок группы
|
|
||||||
CustomGroupHeader.propTypes = {
|
|
||||||
group: PropTypes.object.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
//Отображение задачи
|
|
||||||
const taskRenderer = ({ task }) => {
|
|
||||||
//Если это задачи проекта
|
|
||||||
if (task.type === 0) {
|
|
||||||
return {
|
|
||||||
taskStyle: { border: "3px solid #ebe058" }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
//Тело модуля
|
|
||||||
//-----------
|
|
||||||
|
|
||||||
//Пример: Циклограмма "P8PCyclogram"
|
|
||||||
const Cyclogram = ({ title }) => {
|
|
||||||
//Собственное состояние
|
|
||||||
const [state, setState] = useState({
|
|
||||||
init: false,
|
|
||||||
dataLoaded: false,
|
|
||||||
reload: true,
|
|
||||||
ident: null
|
|
||||||
});
|
|
||||||
|
|
||||||
//Подключение к контексту взаимодействия с сервером
|
|
||||||
const { executeStored } = useContext(BackEndСtx);
|
|
||||||
|
|
||||||
//При необходимости перезагрузки
|
|
||||||
const handleReload = () => {
|
|
||||||
setState(pv => ({ ...pv, reload: true }));
|
|
||||||
};
|
|
||||||
|
|
||||||
//При необходимости обновить данные таблицы
|
|
||||||
useEffect(() => {
|
|
||||||
//Загрузка данных циклограммы с сервера
|
|
||||||
const loadData = async () => {
|
|
||||||
const data = await executeStored({
|
|
||||||
stored: "PKG_P8PANELS_SAMPLES.CYCLOGRAM",
|
|
||||||
args: { NIDENT: state.ident },
|
|
||||||
attributeValueProcessor: (name, val) => (["ddate_start", "ddate_end"].includes(name) ? formatDateJSONDateOnly(val) : val),
|
|
||||||
respArg: "COUT"
|
|
||||||
});
|
|
||||||
setState(pv => ({ ...pv, dataLoaded: true, ...data.XCYCLOGRAM, reload: false }));
|
|
||||||
};
|
|
||||||
//Если указан идентификатор и требуется перезагрузить
|
|
||||||
if (state.ident && state.reload) loadData();
|
|
||||||
}, [state.ident, state.reload, executeStored]);
|
|
||||||
|
|
||||||
//При подключении компонента к странице
|
|
||||||
useEffect(() => {
|
|
||||||
//Инициализация данных циклограммы
|
|
||||||
const initData = async () => {
|
|
||||||
const data = await executeStored({ stored: "PKG_P8PANELS_SAMPLES.CYCLOGRAM_INIT", args: { NIDENT: state.ident } });
|
|
||||||
setState(pv => ({ ...pv, init: true, ident: data.NIDENT, reload: true }));
|
|
||||||
};
|
|
||||||
//Если требуется проинициализировать
|
|
||||||
if (!state.init) {
|
|
||||||
initData();
|
|
||||||
}
|
|
||||||
}, [executeStored, state.ident, state.init]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box>
|
|
||||||
<div style={STYLES.CONTAINER}>
|
|
||||||
<Typography sx={STYLES.TITLE} variant={"h6"}>
|
|
||||||
{title}
|
|
||||||
</Typography>
|
|
||||||
<Grid container direction="column" alignItems="center">
|
|
||||||
<Grid item xs={12}>
|
|
||||||
{state.dataLoaded ? (
|
|
||||||
<P8PCyclogram
|
|
||||||
{...P8P_CYCLOGRAM_CONFIG_PROPS}
|
|
||||||
{...state}
|
|
||||||
containerStyle={STYLES.CYCLOGRAM_CONTAINER}
|
|
||||||
lineHeight={LINE_HEIGHT}
|
|
||||||
taskDialogRenderer={prms => (
|
|
||||||
<CustomTaskDialog task={prms.task} ident={state.ident} handleReload={handleReload} close={prms.close} />
|
|
||||||
)}
|
|
||||||
taskRenderer={prms => taskRenderer(prms)}
|
|
||||||
groupHeaderRenderer={prms => <CustomGroupHeader group={prms.group} />}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</div>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Контроль свойств - Пример: Циклограмма "P8PCyclogram"
|
|
||||||
Cyclogram.propTypes = {
|
|
||||||
title: PropTypes.string.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------
|
|
||||||
//Интерфейс модуля
|
|
||||||
//----------------
|
|
||||||
|
|
||||||
export { Cyclogram };
|
|
||||||
@ -15,7 +15,6 @@ import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../components/p8p_data_grid"
|
|||||||
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
||||||
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
||||||
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
||||||
import { APP_STYLES } from "../../../app.styles"; //Типовые стили
|
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
//Константы
|
//Константы
|
||||||
@ -28,7 +27,7 @@ const DATA_GRID_PAGE_SIZE = 5;
|
|||||||
const STYLES = {
|
const STYLES = {
|
||||||
CONTAINER: { textAlign: "center", paddingTop: "20px" },
|
CONTAINER: { textAlign: "center", paddingTop: "20px" },
|
||||||
TITLE: { paddingBottom: "15px" },
|
TITLE: { paddingBottom: "15px" },
|
||||||
DATA_GRID_CONTAINER: { maxWidth: 700, maxHeight: 500, minHeight: 500, ...APP_STYLES.SCROLL }
|
DATA_GRID_CONTAINER: { maxWidth: 700, maxHeight: 500, minHeight: 500 }
|
||||||
};
|
};
|
||||||
|
|
||||||
//---------------------------------------------
|
//---------------------------------------------
|
||||||
@ -87,14 +86,18 @@ export const groupCellRender = () => ({ cellStyle: { padding: "2px" } });
|
|||||||
//Пример: Таблица данных "P8PDataGrid"
|
//Пример: Таблица данных "P8PDataGrid"
|
||||||
const DataGrid = ({ title }) => {
|
const DataGrid = ({ title }) => {
|
||||||
//Собственное состояние - таблица данных
|
//Собственное состояние - таблица данных
|
||||||
const [dataGrid, setDataGrid] = useState({
|
const [dataGrid, setdataGrid] = useState({
|
||||||
dataLoaded: false,
|
dataLoaded: false,
|
||||||
|
columnsDef: [],
|
||||||
filters: null,
|
filters: null,
|
||||||
orders: null,
|
orders: null,
|
||||||
|
groups: [],
|
||||||
|
rows: [],
|
||||||
|
reload: true,
|
||||||
pageNumber: 1,
|
pageNumber: 1,
|
||||||
morePages: true,
|
morePages: true,
|
||||||
expandable: true,
|
fixedHeader: false,
|
||||||
reloading: true
|
fixedColumns: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
//Подключение к контексту взаимодействия с сервером
|
//Подключение к контексту взаимодействия с сервером
|
||||||
@ -105,7 +108,7 @@ const DataGrid = ({ title }) => {
|
|||||||
|
|
||||||
//Загрузка данных таблицы с сервера
|
//Загрузка данных таблицы с сервера
|
||||||
const loadData = useCallback(async () => {
|
const loadData = useCallback(async () => {
|
||||||
if (dataGrid.reloading) {
|
if (dataGrid.reload) {
|
||||||
const data = await executeStored({
|
const data = await executeStored({
|
||||||
stored: "PKG_P8PANELS_SAMPLES.DATA_GRID",
|
stored: "PKG_P8PANELS_SAMPLES.DATA_GRID",
|
||||||
args: {
|
args: {
|
||||||
@ -117,31 +120,32 @@ const DataGrid = ({ title }) => {
|
|||||||
},
|
},
|
||||||
respArg: "COUT"
|
respArg: "COUT"
|
||||||
});
|
});
|
||||||
setDataGrid(pv => ({
|
setdataGrid(pv => ({
|
||||||
...pv,
|
...pv,
|
||||||
...data.XDATA_GRID,
|
fixedHeader: data.XDATA_GRID.fixedHeader,
|
||||||
columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef || [],
|
fixedColumns: data.XDATA_GRID.fixedColumns,
|
||||||
rows: pv.pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...(pv.rows || []), ...(data.XDATA_GRID.rows || [])],
|
columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
|
||||||
groups: data.XDATA_GRID.groups
|
rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
|
||||||
|
groups: data.XGROUPS
|
||||||
? pv.pageNumber == 1
|
? pv.pageNumber == 1
|
||||||
? [...data.XDATA_GRID.groups]
|
? [...data.XGROUPS]
|
||||||
: [...(pv.groups || []), ...data.XDATA_GRID.groups.filter(g => !pv.groups.find(pg => pg.name == g.name))]
|
: [...pv.groups, ...data.XGROUPS.filter(g => !pv.groups.find(pg => pg.name == g.name))]
|
||||||
: [...(pv.groups || [])],
|
: [...pv.groups],
|
||||||
dataLoaded: true,
|
dataLoaded: true,
|
||||||
reloading: false,
|
reload: false,
|
||||||
morePages: (data.XDATA_GRID.rows || []).length >= DATA_GRID_PAGE_SIZE
|
morePages: (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}, [dataGrid.reloading, dataGrid.filters, dataGrid.orders, dataGrid.dataLoaded, dataGrid.pageNumber, executeStored, SERV_DATA_TYPE_CLOB]);
|
}, [dataGrid.reload, dataGrid.filters, dataGrid.orders, dataGrid.dataLoaded, dataGrid.pageNumber, executeStored, SERV_DATA_TYPE_CLOB]);
|
||||||
|
|
||||||
//При изменении состояния фильтра
|
//При изменении состояния фильтра
|
||||||
const handleFilterChanged = ({ filters }) => setDataGrid(pv => ({ ...pv, filters: [...filters], pageNumber: 1, reloading: true }));
|
const handleFilterChanged = ({ filters }) => setdataGrid(pv => ({ ...pv, filters: [...filters], pageNumber: 1, reload: true }));
|
||||||
|
|
||||||
//При изменении состояния сортировки
|
//При изменении состояния сортировки
|
||||||
const handleOrderChanged = ({ orders }) => setDataGrid(pv => ({ ...pv, orders: [...orders], pageNumber: 1, reloading: true }));
|
const handleOrderChanged = ({ orders }) => setdataGrid(pv => ({ ...pv, orders: [...orders], pageNumber: 1, reload: true }));
|
||||||
|
|
||||||
//При изменении количества отображаемых страниц
|
//При изменении количества отображаемых страниц
|
||||||
const handlePagesCountChanged = () => setDataGrid(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reloading: true }));
|
const handlePagesCountChanged = () => setdataGrid(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reload: true }));
|
||||||
|
|
||||||
//При нажатии на копку контрагента
|
//При нажатии на копку контрагента
|
||||||
const handleAgnButtonClicked = agnCode => pOnlineShowDocument({ unitCode: "AGNLIST", document: agnCode, inRnParameter: "in_AGNABBR" });
|
const handleAgnButtonClicked = agnCode => pOnlineShowDocument({ unitCode: "AGNLIST", document: agnCode, inRnParameter: "in_AGNABBR" });
|
||||||
@ -149,7 +153,7 @@ const DataGrid = ({ title }) => {
|
|||||||
//При необходимости обновить данные таблицы
|
//При необходимости обновить данные таблицы
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadData();
|
loadData();
|
||||||
}, [dataGrid.reloading, loadData]);
|
}, [dataGrid.reload, loadData]);
|
||||||
|
|
||||||
//Генерация содержимого
|
//Генерация содержимого
|
||||||
return (
|
return (
|
||||||
@ -163,9 +167,16 @@ const DataGrid = ({ title }) => {
|
|||||||
{dataGrid.dataLoaded ? (
|
{dataGrid.dataLoaded ? (
|
||||||
<P8PDataGrid
|
<P8PDataGrid
|
||||||
{...P8P_DATA_GRID_CONFIG_PROPS}
|
{...P8P_DATA_GRID_CONFIG_PROPS}
|
||||||
{...dataGrid}
|
containerComponentProps={{ elevation: 6, style: STYLES.DATA_GRID_CONTAINER }}
|
||||||
|
columnsDef={dataGrid.columnsDef}
|
||||||
|
groups={dataGrid.groups}
|
||||||
|
rows={dataGrid.rows}
|
||||||
size={P8P_DATA_GRID_SIZE.LARGE}
|
size={P8P_DATA_GRID_SIZE.LARGE}
|
||||||
containerComponentProps={{ elevation: 6, sx: STYLES.DATA_GRID_CONTAINER }}
|
fixedHeader={dataGrid.fixedHeader}
|
||||||
|
fixedColumns={dataGrid.fixedColumns}
|
||||||
|
filtersInitial={dataGrid.filters}
|
||||||
|
morePages={dataGrid.morePages}
|
||||||
|
reloading={dataGrid.reload}
|
||||||
valueFormatter={valueFormatter}
|
valueFormatter={valueFormatter}
|
||||||
headCellRender={headCellRender}
|
headCellRender={headCellRender}
|
||||||
dataCellRender={dataCellRender}
|
dataCellRender={dataCellRender}
|
||||||
@ -173,6 +184,7 @@ const DataGrid = ({ title }) => {
|
|||||||
onOrderChanged={handleOrderChanged}
|
onOrderChanged={handleOrderChanged}
|
||||||
onFilterChanged={handleFilterChanged}
|
onFilterChanged={handleFilterChanged}
|
||||||
onPagesCountChanged={handlePagesCountChanged}
|
onPagesCountChanged={handlePagesCountChanged}
|
||||||
|
expandable={true}
|
||||||
rowExpandRender={({ row }) => (
|
rowExpandRender={({ row }) => (
|
||||||
<Button onClick={() => handleAgnButtonClicked(row.SAGNABBR)}>Показать в разделе</Button>
|
<Button onClick={() => handleAgnButtonClicked(row.SAGNABBR)}>Показать в разделе</Button>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -97,10 +97,12 @@ const taskDialogRenderer = ({ task, close }) => {
|
|||||||
//Пример: Диаграмма Ганта "P8Gantt"
|
//Пример: Диаграмма Ганта "P8Gantt"
|
||||||
const Gantt = ({ title }) => {
|
const Gantt = ({ title }) => {
|
||||||
//Собственное состояние
|
//Собственное состояние
|
||||||
const [gantt, setGantt] = useState({
|
const [state, setState] = useState({
|
||||||
init: false,
|
init: false,
|
||||||
dataLoaded: false,
|
dataLoaded: false,
|
||||||
ident: null,
|
ident: null,
|
||||||
|
ganttDef: {},
|
||||||
|
ganttTasks: [],
|
||||||
useCustomTaskDialog: false
|
useCustomTaskDialog: false
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -111,21 +113,21 @@ const Gantt = ({ title }) => {
|
|||||||
const loadData = useCallback(async () => {
|
const loadData = useCallback(async () => {
|
||||||
const data = await executeStored({
|
const data = await executeStored({
|
||||||
stored: "PKG_P8PANELS_SAMPLES.GANTT",
|
stored: "PKG_P8PANELS_SAMPLES.GANTT",
|
||||||
args: { NIDENT: gantt.ident },
|
args: { NIDENT: state.ident },
|
||||||
attributeValueProcessor: (name, val) =>
|
attributeValueProcessor: (name, val) =>
|
||||||
name == "numb" ? undefined : ["start", "end"].includes(name) ? formatDateJSONDateOnly(val) : val,
|
name == "numb" ? undefined : ["start", "end"].includes(name) ? formatDateJSONDateOnly(val) : val,
|
||||||
respArg: "COUT"
|
respArg: "COUT"
|
||||||
});
|
});
|
||||||
setGantt(pv => ({ ...pv, dataLoaded: true, ...data.XGANTT }));
|
setState(pv => ({ ...pv, dataLoaded: true, ganttDef: { ...data.XGANTT_DEF }, ganttTasks: [...data.XGANTT_TASKS] }));
|
||||||
}, [gantt.ident, executeStored]);
|
}, [state.ident, executeStored]);
|
||||||
|
|
||||||
//Инициализация данных диаграммы
|
//Инициализация данных диаграммы
|
||||||
const initData = useCallback(async () => {
|
const initData = useCallback(async () => {
|
||||||
if (!gantt.init) {
|
if (!state.init) {
|
||||||
const data = await executeStored({ stored: "PKG_P8PANELS_SAMPLES.GANTT_INIT", args: { NIDENT: gantt.ident } });
|
const data = await executeStored({ stored: "PKG_P8PANELS_SAMPLES.GANTT_INIT", args: { NIDENT: state.ident } });
|
||||||
setGantt(pv => ({ ...pv, init: true, ident: data.NIDENT }));
|
setState(pv => ({ ...pv, init: true, ident: data.NIDENT }));
|
||||||
}
|
}
|
||||||
}, [gantt.init, gantt.ident, executeStored]);
|
}, [state.init, state.ident, executeStored]);
|
||||||
|
|
||||||
//Изменение данных диаграммы
|
//Изменение данных диаграммы
|
||||||
const modifyData = useCallback(
|
const modifyData = useCallback(
|
||||||
@ -133,13 +135,13 @@ const Gantt = ({ title }) => {
|
|||||||
try {
|
try {
|
||||||
await executeStored({
|
await executeStored({
|
||||||
stored: "PKG_P8PANELS_SAMPLES.GANTT_MODIFY",
|
stored: "PKG_P8PANELS_SAMPLES.GANTT_MODIFY",
|
||||||
args: { NIDENT: gantt.ident, NRN: rn, DDATE_FROM: new Date(start), DDATE_TO: new Date(end) }
|
args: { NIDENT: state.ident, NRN: rn, DDATE_FROM: new Date(start), DDATE_TO: new Date(end) }
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
loadData();
|
loadData();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[gantt.ident, executeStored, loadData]
|
[state.ident, executeStored, loadData]
|
||||||
);
|
);
|
||||||
|
|
||||||
//Обработка измненения сроков задачи в диаграмме Гантта
|
//Обработка измненения сроков задачи в диаграмме Гантта
|
||||||
@ -149,8 +151,8 @@ const Gantt = ({ title }) => {
|
|||||||
|
|
||||||
//При необходимости обновить данные таблицы
|
//При необходимости обновить данные таблицы
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (gantt.ident) loadData();
|
if (state.ident) loadData();
|
||||||
}, [gantt.ident, loadData]);
|
}, [state.ident, loadData]);
|
||||||
|
|
||||||
//При подключении компонента к странице
|
//При подключении компонента к странице
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -166,19 +168,20 @@ const Gantt = ({ title }) => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
sx={STYLES.CONTROL}
|
sx={STYLES.CONTROL}
|
||||||
control={<Checkbox onChange={() => setGantt(pv => ({ ...pv, useCustomTaskDialog: !pv.useCustomTaskDialog }))} />}
|
control={<Checkbox onChange={() => setState(pv => ({ ...pv, useCustomTaskDialog: !pv.useCustomTaskDialog }))} />}
|
||||||
label="Отображать пользовательский диалог задачи"
|
label="Отображать пользовательский диалог задачи"
|
||||||
/>
|
/>
|
||||||
<Grid container direction="column" alignItems="center">
|
<Grid container direction="column" alignItems="center">
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
{gantt.dataLoaded ? (
|
{state.dataLoaded ? (
|
||||||
<P8PGantt
|
<P8PGantt
|
||||||
{...P8P_GANTT_CONFIG_PROPS}
|
{...P8P_GANTT_CONFIG_PROPS}
|
||||||
{...gantt}
|
{...state.ganttDef}
|
||||||
containerStyle={STYLES.GANTT_CONTAINER}
|
containerStyle={STYLES.GANTT_CONTAINER}
|
||||||
|
tasks={state.ganttTasks}
|
||||||
onTaskDatesChange={handleTaskDatesChange}
|
onTaskDatesChange={handleTaskDatesChange}
|
||||||
taskAttributeRenderer={taskAttributeRenderer}
|
taskAttributeRenderer={taskAttributeRenderer}
|
||||||
taskDialogRenderer={gantt.useCustomTaskDialog ? taskDialogRenderer : null}
|
taskDialogRenderer={state.useCustomTaskDialog ? taskDialogRenderer : null}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@ -18,7 +18,6 @@ import { DataGrid } from "./data_grid"; //Пример: Таблица данн
|
|||||||
import { Chart } from "./chart"; //Пример: Графики "P8PChart"
|
import { Chart } from "./chart"; //Пример: Графики "P8PChart"
|
||||||
import { Gantt } from "./gantt"; //Пример: Диаграмма Ганта "P8PGantt"
|
import { Gantt } from "./gantt"; //Пример: Диаграмма Ганта "P8PGantt"
|
||||||
import { Svg } from "./svg"; //Пример: Интерактивные изображения "P8PSVG"
|
import { Svg } from "./svg"; //Пример: Интерактивные изображения "P8PSVG"
|
||||||
import { Cyclogram } from "./cyclogram"; //Пример: Циклограмма "P8PCyclogram"
|
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
//Константы
|
//Константы
|
||||||
@ -33,8 +32,7 @@ const MODES = {
|
|||||||
DATAGRID: { name: "DATAGRID", caption: 'Таблица данных "P8PDataGrid"', component: DataGrid },
|
DATAGRID: { name: "DATAGRID", caption: 'Таблица данных "P8PDataGrid"', component: DataGrid },
|
||||||
CHART: { name: "CHART", caption: 'Графики "P8PChart"', component: Chart },
|
CHART: { name: "CHART", caption: 'Графики "P8PChart"', component: Chart },
|
||||||
GANTT: { name: "GANTT", caption: 'Диаграмма Ганта "P8PGantt"', component: Gantt },
|
GANTT: { name: "GANTT", caption: 'Диаграмма Ганта "P8PGantt"', component: Gantt },
|
||||||
SVG: { name: "SVG", caption: 'Интерактивные изображения "P8PSVG"', component: Svg },
|
SVG: { name: "SVG", caption: 'Интерактивные изображения "P8PSVG"', component: Svg }
|
||||||
CYCLOGRAM: { name: "CYCLOGRAM", caption: 'Циклограмма "P8PCyclogram"', component: Cyclogram }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//Стили
|
//Стили
|
||||||
|
|||||||
@ -19,8 +19,8 @@ create table P8PNL_JB_JOBS
|
|||||||
EDITABLE number(1) default 0 not null, -- Признак возможности редактирования (0 - нет, 1 - да)
|
EDITABLE number(1) default 0 not null, -- Признак возможности редактирования (0 - нет, 1 - да)
|
||||||
CHANGED number(1) default 0 not null, -- Признак наличия изменений, требующих сохранения (0 - нет, 1 - да)
|
CHANGED number(1) default 0 not null, -- Признак наличия изменений, требующих сохранения (0 - нет, 1 - да)
|
||||||
constraint C_P8PNL_JB_JOBS_RN_PK primary key (RN),
|
constraint C_P8PNL_JB_JOBS_RN_PK primary key (RN),
|
||||||
constraint C_P8PNL_JB_JOBS_PRN_FK foreign key (PRN) references P8PNL_JB_PRJCTS (RN) on delete cascade,
|
constraint C_P8PNL_JB_JOBS_PRN_FK foreign key (PRN) references P8PNL_JB_PRJCTS (RN),
|
||||||
constraint C_P8PNL_JB_JOBS_HRN_FK foreign key (HRN) references P8PNL_JB_JOBS (RN) on delete cascade,
|
constraint C_P8PNL_JB_JOBS_HRN_FK foreign key (HRN) references P8PNL_JB_JOBS (RN),
|
||||||
constraint C_P8PNL_JB_JOBS_STAGE_VAL check (STAGE in (0, 1)),
|
constraint C_P8PNL_JB_JOBS_STAGE_VAL check (STAGE in (0, 1)),
|
||||||
constraint C_P8PNL_JB_JOBS_EDTBL_VAL check (EDITABLE in (0, 1)),
|
constraint C_P8PNL_JB_JOBS_EDTBL_VAL check (EDITABLE in (0, 1)),
|
||||||
constraint C_P8PNL_JB_JOBS_CHNGD_VAL check (CHANGED in (0, 1)),
|
constraint C_P8PNL_JB_JOBS_CHNGD_VAL check (CHANGED in (0, 1)),
|
||||||
|
|||||||
@ -9,7 +9,7 @@ create table P8PNL_JB_JOBSPREV
|
|||||||
PRN number(17) not null, -- Рег. номер родителя
|
PRN number(17) not null, -- Рег. номер родителя
|
||||||
JB_JOBS number(17) not null, -- Рег. номер предшествующей работы/этапа
|
JB_JOBS number(17) not null, -- Рег. номер предшествующей работы/этапа
|
||||||
constraint C_P8PNL_JB_JOBSPREV_RN_PK primary key (RN),
|
constraint C_P8PNL_JB_JOBSPREV_RN_PK primary key (RN),
|
||||||
constraint C_P8PNL_JB_JOBSPREV_PRN_FK foreign key (PRN) references P8PNL_JB_JOBS (RN) on delete cascade,
|
constraint C_P8PNL_JB_JOBSPREV_PRN_FK foreign key (PRN) references P8PNL_JB_JOBS (RN),
|
||||||
constraint C_P8PNL_JB_JOBSPREV_JB_JOBS_FK foreign key (JB_JOBS) references P8PNL_JB_JOBS (RN) on delete cascade,
|
constraint C_P8PNL_JB_JOBSPREV_JB_JOBS_FK foreign key (JB_JOBS) references P8PNL_JB_JOBS (RN),
|
||||||
constraint C_P8PNL_JB_JOBSPREV_UN unique (IDENT, PRN, JB_JOBS)
|
constraint C_P8PNL_JB_JOBSPREV_UN unique (IDENT, PRN, JB_JOBS)
|
||||||
);
|
);
|
||||||
|
|||||||
@ -15,7 +15,7 @@ create table P8PNL_JB_PERIODS
|
|||||||
LAB_PLAN_JOBS number(17,3) default 0 not null, -- Трудоёмкость (план, по графику)
|
LAB_PLAN_JOBS number(17,3) default 0 not null, -- Трудоёмкость (план, по графику)
|
||||||
constraint C_P8PNL_JB_PERIODS_RN_PK primary key (RN),
|
constraint C_P8PNL_JB_PERIODS_RN_PK primary key (RN),
|
||||||
constraint C_P8PNL_JB_PERIODS_DATE_VAL check (DATE_FROM <= DATE_TO),
|
constraint C_P8PNL_JB_PERIODS_DATE_VAL check (DATE_FROM <= DATE_TO),
|
||||||
constraint C_P8PNL_JB_PERIODS_INS_DEP_FK foreign key (INS_DEPARTMENT) references INS_DEPARTMENT (RN) on delete cascade,
|
constraint C_P8PNL_JB_PERIODS_INS_DEP_FK foreign key (INS_DEPARTMENT) references INS_DEPARTMENT (RN),
|
||||||
constraint C_P8PNL_JB_PERIODS_FCMNPWR_FK foreign key (FCMANPOWER) references FCMANPOWER (RN) on delete cascade,
|
constraint C_P8PNL_JB_PERIODS_FCMNPWR_FK foreign key (FCMANPOWER) references FCMANPOWER (RN),
|
||||||
constraint C_P8PNL_JB_PERIODS_UN unique (IDENT, DATE_FROM, INS_DEPARTMENT, FCMANPOWER)
|
constraint C_P8PNL_JB_PERIODS_UN unique (IDENT, DATE_FROM, INS_DEPARTMENT, FCMANPOWER)
|
||||||
);
|
);
|
||||||
|
|||||||
@ -11,7 +11,7 @@ create table P8PNL_JB_PRJCTS
|
|||||||
EDITABLE number(1) default 0 not null, -- Признак возможности редактирования (0 - нет, 1 - да)
|
EDITABLE number(1) default 0 not null, -- Признак возможности редактирования (0 - нет, 1 - да)
|
||||||
CHANGED number(1) default 0 not null, -- Признак наличия изменений, требующих сохранения (0 - нет, 1 - да)
|
CHANGED number(1) default 0 not null, -- Признак наличия изменений, требующих сохранения (0 - нет, 1 - да)
|
||||||
constraint C_P8PNL_JB_PRJCTS_RN_PK primary key (RN),
|
constraint C_P8PNL_JB_PRJCTS_RN_PK primary key (RN),
|
||||||
constraint C_P8PNL_JB_PRJCTS_PROJECT_FK foreign key (PROJECT) references PROJECT (RN) on delete cascade,
|
constraint C_P8PNL_JB_PRJCTS_PROJECT_FK foreign key (PROJECT) references PROJECT (RN),
|
||||||
constraint C_P8PNL_JB_PRJCTS_JOBS_VAL check (JOBS in (0, 1)),
|
constraint C_P8PNL_JB_PRJCTS_JOBS_VAL check (JOBS in (0, 1)),
|
||||||
constraint C_P8PNL_JB_PRJCTS_EDTBL_VAL check (EDITABLE in (0, 1)),
|
constraint C_P8PNL_JB_PRJCTS_EDTBL_VAL check (EDITABLE in (0, 1)),
|
||||||
constraint C_P8PNL_JB_PRJCTS_CHNGD_VAL check (CHANGED in (0, 1)),
|
constraint C_P8PNL_JB_PRJCTS_CHNGD_VAL check (CHANGED in (0, 1)),
|
||||||
|
|||||||
@ -1,18 +0,0 @@
|
|||||||
/*
|
|
||||||
Парус 8 - Панели мониторинга - Примеры
|
|
||||||
Буфер для циклограммы
|
|
||||||
*/
|
|
||||||
create table P8PNL_SMPL_CYCLOGRAM
|
|
||||||
(
|
|
||||||
RN number(17) not null, -- Рег. номер записи
|
|
||||||
IDENT number(17) not null, -- Идентификатор процесса
|
|
||||||
TYPE number(1) not null, -- Тип (0 - колонка, 1 - группа, 2 - задача)
|
|
||||||
NAME varchar2(200) not null, -- Наименование
|
|
||||||
POS_START number(17) default null, -- Начальная позиция на циклограмме
|
|
||||||
POS_END number(17) default null, -- Конечная позиция на циклограмме
|
|
||||||
DATE_FROM date default null, -- Дата начала
|
|
||||||
DATE_TO date default null, -- Дата окончания
|
|
||||||
TASK_GROUP number(17) default null, -- Группа задач
|
|
||||||
constraint C_P8PNL_SMPL_CYCLOGRAM_RN_PK primary key (RN),
|
|
||||||
constraint C_P8PNL_SMPL_CYCLOGRAM_TP_VAL check (TYPE in (0, 1, 2))
|
|
||||||
);
|
|
||||||
@ -27,13 +27,6 @@ create or replace package PKG_P8PANELS_BASE as
|
|||||||
NDOCUMENT in number -- Рег. номер документа
|
NDOCUMENT in number -- Рег. номер документа
|
||||||
) return number; -- Флаг доступности (см. константы NACCESS_*)
|
) return number; -- Флаг доступности (см. константы NACCESS_*)
|
||||||
|
|
||||||
/* Подготовка пользовательской строки поиска для вставки в запрос */
|
|
||||||
procedure UTL_SEARCH_PREPARE
|
|
||||||
(
|
|
||||||
SSEARCH in varchar2, -- Пользовательская строка поиска
|
|
||||||
SSEARCH_PREPARED out varchar2 -- Подготовленная строка поиска
|
|
||||||
);
|
|
||||||
|
|
||||||
/* Базовое исполнение действий */
|
/* Базовое исполнение действий */
|
||||||
procedure PROCESS
|
procedure PROCESS
|
||||||
(
|
(
|
||||||
@ -198,36 +191,6 @@ create or replace package body PKG_P8PANELS_BASE as
|
|||||||
return NACCESS_NO;
|
return NACCESS_NO;
|
||||||
end UTL_DOC_ACCESS_CHECK;
|
end UTL_DOC_ACCESS_CHECK;
|
||||||
|
|
||||||
/* Подготовка пользовательской строки поиска для вставки в запрос */
|
|
||||||
procedure UTL_SEARCH_PREPARE
|
|
||||||
(
|
|
||||||
SSEARCH in varchar2, -- Пользовательская строка поиска
|
|
||||||
SSEARCH_PREPARED out varchar2 -- Подготовленная строка поиска
|
|
||||||
)
|
|
||||||
is
|
|
||||||
/* Локальные константы */
|
|
||||||
SANY_SYS constant char(1) := '%'; -- Маска "любое количество любых символов" Oracle
|
|
||||||
SONE_SYS constant char(1) := '_'; -- Маска "любой один символ" Oracle
|
|
||||||
SENT_WRD_DELIM constant varchar2(1) := ' '; -- Разделитель слов в предложении
|
|
||||||
SANY_PRS constant varchar2(240) := PKG_OPTIONS.STARSYMB; -- Маска "любое количество любых символов" Парус
|
|
||||||
SONE_PRS constant varchar2(240) := PKG_OPTIONS.QUESTSYMB; -- Маска "любой один символ" Парус
|
|
||||||
begin
|
|
||||||
/* Если пользовательская строка пустая - то это всё что угодно */
|
|
||||||
if (SSEARCH is null)
|
|
||||||
then
|
|
||||||
SSEARCH_PREPARED := SANY_SYS;
|
|
||||||
else
|
|
||||||
/* Подменим пользовательские маски на системные и соберем подготовленную строку поиска */
|
|
||||||
SSEARCH_PREPARED := '%' || replace(replace(replace(SSEARCH
|
|
||||||
,SANY_PRS
|
|
||||||
,SANY_SYS)
|
|
||||||
,SONE_PRS
|
|
||||||
,SONE_SYS)
|
|
||||||
,SENT_WRD_DELIM
|
|
||||||
,SANY_SYS) || '%';
|
|
||||||
end if;
|
|
||||||
end UTL_SEARCH_PREPARE;
|
|
||||||
|
|
||||||
/* Формирование сообщения об отсутствии значения */
|
/* Формирование сообщения об отсутствии значения */
|
||||||
function MSG_NO_DATA_MAKE
|
function MSG_NO_DATA_MAKE
|
||||||
(
|
(
|
||||||
|
|||||||
@ -227,10 +227,10 @@ create or replace package body PKG_P8PANELS_EQUIPSRV as
|
|||||||
NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Рег. номер организации
|
NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Рег. номер организации
|
||||||
SPRJ_GROUP_NAME PKG_STD.TSTRING; -- Наименование группы для проекта
|
SPRJ_GROUP_NAME PKG_STD.TSTRING; -- Наименование группы для проекта
|
||||||
BEXPANDED boolean; -- Флаг раскрытости уровня
|
BEXPANDED boolean; -- Флаг раскрытости уровня
|
||||||
RDG PKG_P8PANELS_VISUAL.TDG; -- Описание таблицы
|
RDG PKG_P8PANELS_VISUAL.TDATA_GRID; -- Описание таблицы
|
||||||
RDG_ROW_INFO PKG_P8PANELS_VISUAL.TDG_ROW; -- Строка таблицы с информацией по объекту ремонта
|
RDG_ROW_INFO PKG_P8PANELS_VISUAL.TROW; -- Строка таблицы с информацией по объекту ремонта
|
||||||
RDG_ROW_PLAN PKG_P8PANELS_VISUAL.TDG_ROW; -- Строка таблицы с планом по объекту ремонта
|
RDG_ROW_PLAN PKG_P8PANELS_VISUAL.TROW; -- Строка таблицы с планом по объекту ремонта
|
||||||
RDG_ROW_FACT PKG_P8PANELS_VISUAL.TDG_ROW; -- Строка таблицы с фактом по объекту ремонта
|
RDG_ROW_FACT PKG_P8PANELS_VISUAL.TROW; -- Строка таблицы с фактом по объекту ремонта
|
||||||
NCURYEAR PKG_STD.TNUMBER; -- Текущий год
|
NCURYEAR PKG_STD.TNUMBER; -- Текущий год
|
||||||
NCURMONTH PKG_STD.TNUMBER; -- Текущий месяц
|
NCURMONTH PKG_STD.TNUMBER; -- Текущий месяц
|
||||||
NTOTALDAYS PKG_STD.TNUMBER; -- Дней в текущем месяце
|
NTOTALDAYS PKG_STD.TNUMBER; -- Дней в текущем месяце
|
||||||
@ -399,99 +399,99 @@ create or replace package body PKG_P8PANELS_EQUIPSRV as
|
|||||||
/* Определим дату конца периода */
|
/* Определим дату конца периода */
|
||||||
NTODATE := LAST_DAY(TO_DATE('01.' || LPAD(TO_CHAR(NTOMONTH), 2, '0') || '.' || TO_CHAR(NTOYEAR), 'dd.mm.yyyy'));
|
NTODATE := LAST_DAY(TO_DATE('01.' || LPAD(TO_CHAR(NTOMONTH), 2, '0') || '.' || TO_CHAR(NTOYEAR), 'dd.mm.yyyy'));
|
||||||
/* Инициализируем таблицу данных */
|
/* Инициализируем таблицу данных */
|
||||||
RDG := PKG_P8PANELS_VISUAL.TDG_MAKE(BFIXED_HEADER => true, NFIXED_COLUMNS => 2);
|
RDG := PKG_P8PANELS_VISUAL.TDATA_GRID_MAKE(BFIXED_HEADER => true, NFIXED_COLUMNS => 2);
|
||||||
/* Формируем структуру заголовка */
|
/* Формируем структуру заголовка */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'SOBJINFO',
|
SNAME => 'SOBJINFO',
|
||||||
SCAPTION => 'Информация по объекту ремонта',
|
SCAPTION => 'Информация по объекту ремонта',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
||||||
NWIDTH => 80);
|
NWIDTH => 80);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'SINFO',
|
SNAME => 'SINFO',
|
||||||
SCAPTION => 'Объект ремонта',
|
SCAPTION => 'Объект ремонта',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
||||||
NWIDTH => 80);
|
NWIDTH => 80);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'SWRKTYPE',
|
SNAME => 'SWRKTYPE',
|
||||||
SCAPTION => 'Тип работ',
|
SCAPTION => 'Тип работ',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
||||||
SPARENT => 'SINFO',
|
SPARENT => 'SINFO',
|
||||||
NWIDTH => 80);
|
NWIDTH => 80);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'NRN',
|
SNAME => 'NRN',
|
||||||
SCAPTION => 'Рег. номер',
|
SCAPTION => 'Рег. номер',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
|
||||||
BVISIBLE => false);
|
BVISIBLE => false);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'SWORKNAME',
|
SNAME => 'SWORKNAME',
|
||||||
SCAPTION => 'Наименование работы',
|
SCAPTION => 'Наименование работы',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
||||||
BVISIBLE => false);
|
BVISIBLE => false);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'STECHOBJCODE',
|
SNAME => 'STECHOBJCODE',
|
||||||
SCAPTION => 'Код тех. объекта',
|
SCAPTION => 'Код тех. объекта',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
||||||
BVISIBLE => false);
|
BVISIBLE => false);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'STECHOBJNAME',
|
SNAME => 'STECHOBJNAME',
|
||||||
SCAPTION => 'Наименование тех. объекта',
|
SCAPTION => 'Наименование тех. объекта',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
||||||
BVISIBLE => false);
|
BVISIBLE => false);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'SBELONG',
|
SNAME => 'SBELONG',
|
||||||
SCAPTION => 'Принадлежность',
|
SCAPTION => 'Принадлежность',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
||||||
BVISIBLE => false);
|
BVISIBLE => false);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'SPRODOBJ',
|
SNAME => 'SPRODOBJ',
|
||||||
SCAPTION => 'Производственный объект',
|
SCAPTION => 'Производственный объект',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
||||||
BVISIBLE => false);
|
BVISIBLE => false);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'STECHSERV',
|
SNAME => 'STECHSERV',
|
||||||
SCAPTION => 'Тех. служба',
|
SCAPTION => 'Тех. служба',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
||||||
BVISIBLE => false);
|
BVISIBLE => false);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'SRESPDEP',
|
SNAME => 'SRESPDEP',
|
||||||
SCAPTION => 'Отвественное подразделение',
|
SCAPTION => 'Отвественное подразделение',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
||||||
BVISIBLE => false);
|
BVISIBLE => false);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'STECSERVCODE',
|
SNAME => 'STECSERVCODE',
|
||||||
SCAPTION => 'Вид ремонта',
|
SCAPTION => 'Вид ремонта',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
||||||
BVISIBLE => false);
|
BVISIBLE => false);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'DDATEPLANBEG',
|
SNAME => 'DDATEPLANBEG',
|
||||||
SCAPTION => 'Начало работы (план)',
|
SCAPTION => 'Начало работы (план)',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_DATE,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_DATE,
|
||||||
BVISIBLE => false);
|
BVISIBLE => false);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'DDATEPLANEND',
|
SNAME => 'DDATEPLANEND',
|
||||||
SCAPTION => 'Окончание работы (план)',
|
SCAPTION => 'Окончание работы (план)',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_DATE,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_DATE,
|
||||||
BVISIBLE => false);
|
BVISIBLE => false);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'DDATEFACTBEG',
|
SNAME => 'DDATEFACTBEG',
|
||||||
SCAPTION => 'Начало работы (факт)',
|
SCAPTION => 'Начало работы (факт)',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_DATE,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_DATE,
|
||||||
BVISIBLE => false);
|
BVISIBLE => false);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'DDATEFACTEND',
|
SNAME => 'DDATEFACTEND',
|
||||||
SCAPTION => 'Окончание работы (факт)',
|
SCAPTION => 'Окончание работы (факт)',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_DATE,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_DATE,
|
||||||
BVISIBLE => false);
|
BVISIBLE => false);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'STECSRVKINDCODE',
|
SNAME => 'STECSRVKINDCODE',
|
||||||
SCAPTION => 'Код типа работы',
|
SCAPTION => 'Код типа работы',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
||||||
BVISIBLE => false);
|
BVISIBLE => false);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'STECSRVKINDNAME',
|
SNAME => 'STECSRVKINDNAME',
|
||||||
SCAPTION => 'Наименование типа работы',
|
SCAPTION => 'Наименование типа работы',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
||||||
BVISIBLE => false);
|
BVISIBLE => false);
|
||||||
/* Очистка коллекций */
|
/* Очистка коллекций */
|
||||||
PKG_CONTVALLOC1S.PURGE(RCONTAINER => YM);
|
PKG_CONTVALLOC1S.PURGE(RCONTAINER => YM);
|
||||||
PKG_CONTVALLOC1S.PURGE(RCONTAINER => MCLR);
|
PKG_CONTVALLOC1S.PURGE(RCONTAINER => MCLR);
|
||||||
@ -530,12 +530,12 @@ create or replace package body PKG_P8PANELS_EQUIPSRV as
|
|||||||
else
|
else
|
||||||
BEXPANDED := false;
|
BEXPANDED := false;
|
||||||
end if;
|
end if;
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => '_' || TO_CHAR(Y) || '_' || TO_CHAR(M),
|
SNAME => '_' || TO_CHAR(Y) || '_' || TO_CHAR(M),
|
||||||
SCAPTION => LPAD(TO_CHAR(M), 2, '0') || ' ' || TO_CHAR(Y),
|
SCAPTION => LPAD(TO_CHAR(M), 2, '0') || ' ' || TO_CHAR(Y),
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
||||||
BEXPANDABLE => true,
|
BEXPANDABLE => true,
|
||||||
BEXPANDED => BEXPANDED);
|
BEXPANDED => BEXPANDED);
|
||||||
/* Подсчёт кол-ва дней в месяце */
|
/* Подсчёт кол-ва дней в месяце */
|
||||||
NTOTALDAYS := TO_NUMBER(TO_CHAR(LAST_DAY(TO_DATE('01.' || LPAD(TO_CHAR(M), 2, '0') || '.' || TO_CHAR(Y),
|
NTOTALDAYS := TO_NUMBER(TO_CHAR(LAST_DAY(TO_DATE('01.' || LPAD(TO_CHAR(M), 2, '0') || '.' || TO_CHAR(Y),
|
||||||
'dd.mm.yyyy')),
|
'dd.mm.yyyy')),
|
||||||
@ -544,11 +544,12 @@ create or replace package body PKG_P8PANELS_EQUIPSRV as
|
|||||||
/* Цикл по дням месяца */
|
/* Цикл по дням месяца */
|
||||||
for D in 1 .. NTOTALDAYS
|
for D in 1 .. NTOTALDAYS
|
||||||
loop
|
loop
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => '_' || TO_CHAR(Y) || '_' || TO_CHAR(M) || '_' || TO_CHAR(D),
|
SNAME => '_' || TO_CHAR(Y) || '_' || TO_CHAR(M) || '_' ||
|
||||||
SCAPTION => TO_CHAR(D, '99'),
|
TO_CHAR(D),
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
SCAPTION => TO_CHAR(D, '99'),
|
||||||
SPARENT => '_' || TO_CHAR(Y) || '_' || TO_CHAR(M));
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
||||||
|
SPARENT => '_' || TO_CHAR(Y) || '_' || TO_CHAR(M));
|
||||||
end loop;
|
end loop;
|
||||||
end loop;
|
end loop;
|
||||||
end loop;
|
end loop;
|
||||||
@ -572,7 +573,7 @@ create or replace package body PKG_P8PANELS_EQUIPSRV as
|
|||||||
if (NFROMYEAR = NTOYEAR) then
|
if (NFROMYEAR = NTOYEAR) then
|
||||||
NMS := NFROMMONTH;
|
NMS := NFROMMONTH;
|
||||||
NME := NTOMONTH;
|
NME := NTOMONTH;
|
||||||
/* Иначе вычисляем кол-во месяцев в каждом году периода отчёта */
|
/* Иначе вычисляем кол-во месяцев в каждом году периода отчёта */
|
||||||
else
|
else
|
||||||
if (Y = NFROMYEAR) then
|
if (Y = NFROMYEAR) then
|
||||||
NMS := NFROMMONTH;
|
NMS := NFROMMONTH;
|
||||||
@ -589,14 +590,14 @@ create or replace package body PKG_P8PANELS_EQUIPSRV as
|
|||||||
for M in NMS .. NME
|
for M in NMS .. NME
|
||||||
loop
|
loop
|
||||||
SPERIODNAME := '_' || TO_CHAR(Y) || '_' || TO_CHAR(M);
|
SPERIODNAME := '_' || TO_CHAR(Y) || '_' || TO_CHAR(M);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW_INFO,
|
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW_INFO,
|
||||||
SNAME => SPERIODNAME,
|
SNAME => SPERIODNAME,
|
||||||
SVALUE => 'план: ' || HOURS_STR(NHOURS => TRUNC(PKG_CONTVALLOC1S.GETN(RCONTAINER => YM,
|
SVALUE => 'план: ' || HOURS_STR(NHOURS => TRUNC(PKG_CONTVALLOC1S.GETN(RCONTAINER => YM,
|
||||||
SROWID => SPERIODNAME || '_P'),
|
SROWID => SPERIODNAME || '_P'),
|
||||||
1)) || ' факт: ' ||
|
1)) || ' факт: ' ||
|
||||||
HOURS_STR(NHOURS => TRUNC(PKG_CONTVALLOC1S.GETN(RCONTAINER => YM,
|
HOURS_STR(NHOURS => TRUNC(PKG_CONTVALLOC1S.GETN(RCONTAINER => YM,
|
||||||
SROWID => SPERIODNAME || '_F'),
|
SROWID => SPERIODNAME || '_F'),
|
||||||
1)));
|
1)));
|
||||||
/* Добавление в коллекцию трудоёмкость план */
|
/* Добавление в коллекцию трудоёмкость план */
|
||||||
PKG_CONTVALLOC1S.PUTN(RCONTAINER => YM, SROWID => SPERIODNAME || '_P', NVALUE => 0);
|
PKG_CONTVALLOC1S.PUTN(RCONTAINER => YM, SROWID => SPERIODNAME || '_P', NVALUE => 0);
|
||||||
/* Добавление в коллекцию трудоёмкость факт */
|
/* Добавление в коллекцию трудоёмкость факт */
|
||||||
@ -604,17 +605,17 @@ create or replace package body PKG_P8PANELS_EQUIPSRV as
|
|||||||
end loop;
|
end loop;
|
||||||
end loop;
|
end loop;
|
||||||
/* Добавление строки с трудоёмкостью */
|
/* Добавление строки с трудоёмкостью */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW_INFO);
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW_INFO);
|
||||||
end if;
|
end if;
|
||||||
/* Добавление группы с объектом ремонта */
|
/* Добавление группы с объектом ремонта */
|
||||||
SCURTECHOBJ := QQ.STECHOBJNAME;
|
SCURTECHOBJ := QQ.STECHOBJNAME;
|
||||||
SPRJ_GROUP_NAME := SCURTECHOBJ;
|
SPRJ_GROUP_NAME := SCURTECHOBJ;
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_GROUP(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_GROUP(RDATA_GRID => RDG,
|
||||||
SNAME => SPRJ_GROUP_NAME,
|
SNAME => SPRJ_GROUP_NAME,
|
||||||
SCAPTION => QQ.STECHOBJNAME,
|
SCAPTION => QQ.STECHOBJNAME,
|
||||||
BEXPANDABLE => false);
|
BEXPANDABLE => false);
|
||||||
RDG_ROW_INFO := PKG_P8PANELS_VISUAL.TDG_ROW_MAKE(SGROUP => SPRJ_GROUP_NAME);
|
RDG_ROW_INFO := PKG_P8PANELS_VISUAL.TROW_MAKE(SGROUP => SPRJ_GROUP_NAME);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW_INFO, SNAME => 'SOBJINFO', SVALUE => SCURTECHOBJ);
|
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW_INFO, SNAME => 'SOBJINFO', SVALUE => SCURTECHOBJ);
|
||||||
end if;
|
end if;
|
||||||
/* Формируем имя группы для вида ремонта */
|
/* Формируем имя группы для вида ремонта */
|
||||||
SCURTSKCODE := SCURTECHOBJ || '_' || QQ.STECSRVKINDCODE;
|
SCURTSKCODE := SCURTECHOBJ || '_' || QQ.STECSRVKINDCODE;
|
||||||
@ -622,7 +623,7 @@ create or replace package body PKG_P8PANELS_EQUIPSRV as
|
|||||||
if (PKG_CONTVALLOC1S.EXISTS_(RCONTAINER => GF, SROWID => SCURTSKCODE) = false) then
|
if (PKG_CONTVALLOC1S.EXISTS_(RCONTAINER => GF, SROWID => SCURTSKCODE) = false) then
|
||||||
/* Добавляем строку плана */
|
/* Добавляем строку плана */
|
||||||
if (RDG_ROW_PLAN.RCOLS is not null) then
|
if (RDG_ROW_PLAN.RCOLS is not null) then
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW_PLAN);
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW_PLAN);
|
||||||
end if;
|
end if;
|
||||||
/* Добавляем строку факта */
|
/* Добавляем строку факта */
|
||||||
if (RDG_ROW_FACT.RCOLS is not null) then
|
if (RDG_ROW_FACT.RCOLS is not null) then
|
||||||
@ -630,27 +631,27 @@ create or replace package body PKG_P8PANELS_EQUIPSRV as
|
|||||||
/* Цикл по коллекции для закрашивания месяцев */
|
/* Цикл по коллекции для закрашивания месяцев */
|
||||||
for Z in 1 .. PKG_CONTVALLOC1S.COUNT_(RCONTAINER => MCLR)
|
for Z in 1 .. PKG_CONTVALLOC1S.COUNT_(RCONTAINER => MCLR)
|
||||||
loop
|
loop
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW_FACT,
|
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW_FACT,
|
||||||
SNAME => CR,
|
SNAME => CR,
|
||||||
SVALUE => PKG_CONTVALLOC1S.GETS(RCONTAINER => MCLR, SROWID => CR));
|
SVALUE => PKG_CONTVALLOC1S.GETS(RCONTAINER => MCLR, SROWID => CR));
|
||||||
CR := PKG_CONTVALLOC1S.NEXT_(RCONTAINER => MCLR, SROWID => CR);
|
CR := PKG_CONTVALLOC1S.NEXT_(RCONTAINER => MCLR, SROWID => CR);
|
||||||
end loop;
|
end loop;
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW_FACT);
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW_FACT);
|
||||||
end if;
|
end if;
|
||||||
PKG_CONTVALLOC1S.PURGE(RCONTAINER => MCLR);
|
PKG_CONTVALLOC1S.PURGE(RCONTAINER => MCLR);
|
||||||
/* Добвим группу для вида ремонта */
|
/* Добвим группу для вида ремонта */
|
||||||
SPRJ_GROUP_NAME := SCURTSKCODE;
|
SPRJ_GROUP_NAME := SCURTSKCODE;
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_GROUP(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_GROUP(RDATA_GRID => RDG,
|
||||||
SNAME => SPRJ_GROUP_NAME,
|
SNAME => SPRJ_GROUP_NAME,
|
||||||
SCAPTION => QQ.STECSRVKINDCODE,
|
SCAPTION => QQ.STECSRVKINDCODE,
|
||||||
BEXPANDABLE => false);
|
BEXPANDABLE => false);
|
||||||
/* Строка плана */
|
/* Строка плана */
|
||||||
RDG_ROW_PLAN := PKG_P8PANELS_VISUAL.TDG_ROW_MAKE(SGROUP => SPRJ_GROUP_NAME);
|
RDG_ROW_PLAN := PKG_P8PANELS_VISUAL.TROW_MAKE(SGROUP => SPRJ_GROUP_NAME);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW_PLAN, SNAME => 'SOBJINFO', SVALUE => QQ.STECSRVKINDCODE);
|
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW_PLAN, SNAME => 'SOBJINFO', SVALUE => QQ.STECSRVKINDCODE);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW_PLAN, SNAME => 'SWRKTYPE', SVALUE => 'План');
|
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW_PLAN, SNAME => 'SWRKTYPE', SVALUE => 'План');
|
||||||
/* Строка факта */
|
/* Строка факта */
|
||||||
RDG_ROW_FACT := PKG_P8PANELS_VISUAL.TDG_ROW_MAKE(SGROUP => SPRJ_GROUP_NAME);
|
RDG_ROW_FACT := PKG_P8PANELS_VISUAL.TROW_MAKE(SGROUP => SPRJ_GROUP_NAME);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW_FACT, SNAME => 'SWRKTYPE', SVALUE => 'Факт');
|
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW_FACT, SNAME => 'SWRKTYPE', SVALUE => 'Факт');
|
||||||
/* Добавляем в заполненные группы */
|
/* Добавляем в заполненные группы */
|
||||||
PKG_CONTVALLOC1S.PUTS(RCONTAINER => GF, SROWID => SPRJ_GROUP_NAME, SVALUE => '');
|
PKG_CONTVALLOC1S.PUTS(RCONTAINER => GF, SROWID => SPRJ_GROUP_NAME, SVALUE => '');
|
||||||
end if;
|
end if;
|
||||||
@ -675,7 +676,7 @@ create or replace package body PKG_P8PANELS_EQUIPSRV as
|
|||||||
/* Закрашивание месяца плана синим */
|
/* Закрашивание месяца плана синим */
|
||||||
if (PKG_CONTVALLOC1S.EXISTS_(RCONTAINER => COLS, SROWID => SPRJ_GROUP_NAME || ' ' || SPERIODNAME || ' PLAN') =
|
if (PKG_CONTVALLOC1S.EXISTS_(RCONTAINER => COLS, SROWID => SPRJ_GROUP_NAME || ' ' || SPERIODNAME || ' PLAN') =
|
||||||
false) then
|
false) then
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW_PLAN, SNAME => SPERIODNAME, SVALUE => 'blue');
|
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW_PLAN, SNAME => SPERIODNAME, SVALUE => 'blue');
|
||||||
PKG_CONTVALLOC1S.PUTS(RCONTAINER => COLS,
|
PKG_CONTVALLOC1S.PUTS(RCONTAINER => COLS,
|
||||||
SROWID => SPRJ_GROUP_NAME || ' ' || SPERIODNAME || ' PLAN',
|
SROWID => SPRJ_GROUP_NAME || ' ' || SPERIODNAME || ' PLAN',
|
||||||
SVALUE => '');
|
SVALUE => '');
|
||||||
@ -685,7 +686,7 @@ create or replace package body PKG_P8PANELS_EQUIPSRV as
|
|||||||
/* Закрашивание дня плана синим */
|
/* Закрашивание дня плана синим */
|
||||||
if (PKG_CONTVALLOC1S.EXISTS_(RCONTAINER => COLS, SROWID => SPRJ_GROUP_NAME || ' ' || SPERIODNAME || ' PLAN') =
|
if (PKG_CONTVALLOC1S.EXISTS_(RCONTAINER => COLS, SROWID => SPRJ_GROUP_NAME || ' ' || SPERIODNAME || ' PLAN') =
|
||||||
false) then
|
false) then
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW_PLAN, SNAME => SPERIODNAME, SVALUE => 'blue');
|
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW_PLAN, SNAME => SPERIODNAME, SVALUE => 'blue');
|
||||||
PKG_CONTVALLOC1S.PUTS(RCONTAINER => COLS,
|
PKG_CONTVALLOC1S.PUTS(RCONTAINER => COLS,
|
||||||
SROWID => SPRJ_GROUP_NAME || ' ' || SPERIODNAME || ' PLAN',
|
SROWID => SPRJ_GROUP_NAME || ' ' || SPERIODNAME || ' PLAN',
|
||||||
SVALUE => '');
|
SVALUE => '');
|
||||||
@ -782,37 +783,37 @@ create or replace package body PKG_P8PANELS_EQUIPSRV as
|
|||||||
for M in NMS .. NME
|
for M in NMS .. NME
|
||||||
loop
|
loop
|
||||||
SPERIODNAME := '_' || TO_CHAR(Y) || '_' || TO_CHAR(M);
|
SPERIODNAME := '_' || TO_CHAR(Y) || '_' || TO_CHAR(M);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW_INFO,
|
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW_INFO,
|
||||||
SNAME => SPERIODNAME,
|
SNAME => SPERIODNAME,
|
||||||
SVALUE => 'план: ' || HOURS_STR(NHOURS => TRUNC(PKG_CONTVALLOC1S.GETN(RCONTAINER => YM,
|
SVALUE => 'план: ' || HOURS_STR(NHOURS => TRUNC(PKG_CONTVALLOC1S.GETN(RCONTAINER => YM,
|
||||||
SROWID => SPERIODNAME || '_P'),
|
SROWID => SPERIODNAME || '_P'),
|
||||||
1)) || ' факт: ' ||
|
1)) || ' факт: ' ||
|
||||||
HOURS_STR(NHOURS => TRUNC(PKG_CONTVALLOC1S.GETN(RCONTAINER => YM,
|
HOURS_STR(NHOURS => TRUNC(PKG_CONTVALLOC1S.GETN(RCONTAINER => YM,
|
||||||
SROWID => SPERIODNAME || '_F'),
|
SROWID => SPERIODNAME || '_F'),
|
||||||
1)));
|
1)));
|
||||||
end loop;
|
end loop;
|
||||||
end loop;
|
end loop;
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW_INFO);
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW_INFO);
|
||||||
end if;
|
end if;
|
||||||
/* План для последней записи */
|
/* План для последней записи */
|
||||||
if ((RDG_ROW_PLAN.RCOLS is not null) and (NROWS = 0)) then
|
if ((RDG_ROW_PLAN.RCOLS is not null) and (NROWS = 0)) then
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW_PLAN);
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW_PLAN);
|
||||||
end if;
|
end if;
|
||||||
/* Факт для последней записи */
|
/* Факт для последней записи */
|
||||||
if ((RDG_ROW_FACT.RCOLS is not null) and (NROWS = 0)) then
|
if ((RDG_ROW_FACT.RCOLS is not null) and (NROWS = 0)) then
|
||||||
CR := PKG_CONTVALLOC1S.FIRST_(RCONTAINER => MCLR);
|
CR := PKG_CONTVALLOC1S.FIRST_(RCONTAINER => MCLR);
|
||||||
for Z in 1 .. PKG_CONTVALLOC1S.COUNT_(RCONTAINER => MCLR)
|
for Z in 1 .. PKG_CONTVALLOC1S.COUNT_(RCONTAINER => MCLR)
|
||||||
loop
|
loop
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW_FACT,
|
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW_FACT,
|
||||||
SNAME => CR,
|
SNAME => CR,
|
||||||
SVALUE => PKG_CONTVALLOC1S.GETS(RCONTAINER => MCLR, SROWID => CR));
|
SVALUE => PKG_CONTVALLOC1S.GETS(RCONTAINER => MCLR, SROWID => CR));
|
||||||
CR := PKG_CONTVALLOC1S.NEXT_(RCONTAINER => MCLR, SROWID => CR);
|
CR := PKG_CONTVALLOC1S.NEXT_(RCONTAINER => MCLR, SROWID => CR);
|
||||||
end loop;
|
end loop;
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW_FACT);
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW_FACT);
|
||||||
end if;
|
end if;
|
||||||
end loop;
|
end loop;
|
||||||
/* Сериализуем описание */
|
/* Сериализуем описание */
|
||||||
COUT := PKG_P8PANELS_VISUAL.TDG_TO_XML(RDATA_GRID => RDG, NINCLUDE_DEF => 1);
|
COUT := PKG_P8PANELS_VISUAL.TDATA_GRID_TO_XML(RDATA_GRID => RDG, NINCLUDE_DEF => 1);
|
||||||
/* Очищаем контейнеры */
|
/* Очищаем контейнеры */
|
||||||
PKG_CONTVALLOC1S.PURGE(RCONTAINER => YM);
|
PKG_CONTVALLOC1S.PURGE(RCONTAINER => YM);
|
||||||
PKG_CONTVALLOC1S.PURGE(RCONTAINER => MCLR);
|
PKG_CONTVALLOC1S.PURGE(RCONTAINER => MCLR);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -784,8 +784,8 @@ create or replace package body PKG_P8PANELS_RRPCONFED as
|
|||||||
is
|
is
|
||||||
NVERSION PKG_STD.TREF; -- Рег. номер версии словаря контрагентов
|
NVERSION PKG_STD.TREF; -- Рег. номер версии словаря контрагентов
|
||||||
NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Организация сеанса
|
NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Организация сеанса
|
||||||
RDG PKG_P8PANELS_VISUAL.TDG; -- Описание таблицы
|
RDG PKG_P8PANELS_VISUAL.TDATA_GRID; -- Описание таблицы
|
||||||
RDG_ROW PKG_P8PANELS_VISUAL.TDG_ROW; -- Строка таблицы
|
RDG_ROW PKG_P8PANELS_VISUAL.TROW; -- Строка таблицы
|
||||||
CDG clob; -- XML данных раздела
|
CDG clob; -- XML данных раздела
|
||||||
CXML PKG_CONTVALLOC2NS.TCONTAINER; -- Контейнер для данных XML
|
CXML PKG_CONTVALLOC2NS.TCONTAINER; -- Контейнер для данных XML
|
||||||
RRRPCONFSCTNMRK RRPCONFSCTNMRK%rowtype; -- Рег. номер показателя
|
RRRPCONFSCTNMRK RRPCONFSCTNMRK%rowtype; -- Рег. номер показателя
|
||||||
@ -798,8 +798,8 @@ create or replace package body PKG_P8PANELS_RRPCONFED as
|
|||||||
/* Инициализация колонок граф показателей */
|
/* Инициализация колонок граф показателей */
|
||||||
procedure MARKS_COLUMNS_INIT
|
procedure MARKS_COLUMNS_INIT
|
||||||
(
|
(
|
||||||
RDG in out nocopy PKG_P8PANELS_VISUAL.TDG, -- Описание таблицы
|
RDG in out nocopy PKG_P8PANELS_VISUAL.TDATA_GRID, -- Описание таблицы
|
||||||
NRRPCONFSCTN in number -- Рег. номер раздела
|
NRRPCONFSCTN in number -- Рег. номер раздела
|
||||||
)
|
)
|
||||||
is
|
is
|
||||||
begin
|
begin
|
||||||
@ -816,35 +816,35 @@ create or replace package body PKG_P8PANELS_RRPCONFED as
|
|||||||
order by C.CODE)
|
order by C.CODE)
|
||||||
loop
|
loop
|
||||||
/* Наименование графы */
|
/* Наименование графы */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'SCOL_' || REC.CODE,
|
SNAME => 'SCOL_' || REC.CODE,
|
||||||
SCAPTION => REC.NAME,
|
SCAPTION => REC.NAME,
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
||||||
NWIDTH => 200);
|
NWIDTH => 200);
|
||||||
/* Рег. номер графы */
|
/* Рег. номер графы */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'NCOL_RN_' || REC.CODE,
|
SNAME => 'NCOL_RN_' || REC.CODE,
|
||||||
SCAPTION => REC.NAME || ' рег. номер',
|
SCAPTION => REC.NAME || ' рег. номер',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
|
||||||
BVISIBLE => false);
|
BVISIBLE => false);
|
||||||
/* Рег. номер показателя */
|
/* Рег. номер показателя */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'NMARK_RN_' || REC.CODE,
|
SNAME => 'NMARK_RN_' || REC.CODE,
|
||||||
SCAPTION => REC.NAME || ' рег. номер показателя',
|
SCAPTION => REC.NAME || ' рег. номер показателя',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
|
||||||
BVISIBLE => false);
|
BVISIBLE => false);
|
||||||
/* Мнемокод показателя */
|
/* Мнемокод показателя */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'SMARK_CODE_' || REC.CODE,
|
SNAME => 'SMARK_CODE_' || REC.CODE,
|
||||||
SCAPTION => REC.NAME || ' мнемокод показателя',
|
SCAPTION => REC.NAME || ' мнемокод показателя',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
||||||
BVISIBLE => false);
|
BVISIBLE => false);
|
||||||
/* Для составов показтелей */
|
/* Для составов показтелей */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'MARK_CNS_' || REC.CODE,
|
SNAME => 'MARK_CNS_' || REC.CODE,
|
||||||
SCAPTION => REC.NAME || ' состав показателя',
|
SCAPTION => REC.NAME || ' состав показателя',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
||||||
BVISIBLE => false);
|
BVISIBLE => false);
|
||||||
end loop;
|
end loop;
|
||||||
end MARKS_COLUMNS_INIT;
|
end MARKS_COLUMNS_INIT;
|
||||||
|
|
||||||
@ -897,25 +897,25 @@ create or replace package body PKG_P8PANELS_RRPCONFED as
|
|||||||
order by T.CODE)
|
order by T.CODE)
|
||||||
loop
|
loop
|
||||||
/* Инициализируем таблицу данных */
|
/* Инициализируем таблицу данных */
|
||||||
RDG := PKG_P8PANELS_VISUAL.TDG_MAKE(BFIXED_HEADER => true, NFIXED_COLUMNS => 1);
|
RDG := PKG_P8PANELS_VISUAL.TDATA_GRID_MAKE(BFIXED_HEADER => true, NFIXED_COLUMNS => 1);
|
||||||
/* Формируем структуру заголовка */
|
/* Формируем структуру заголовка */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'SROW_NAME',
|
SNAME => 'SROW_NAME',
|
||||||
SCAPTION => '',
|
SCAPTION => '',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
||||||
NWIDTH => 150);
|
NWIDTH => 150);
|
||||||
/* Формируем структуру заголовка */
|
/* Формируем структуру заголовка */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'SROW_CODE',
|
SNAME => 'SROW_CODE',
|
||||||
SCAPTION => 'Мнемокод строки',
|
SCAPTION => 'Мнемокод строки',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
||||||
BVISIBLE => false);
|
BVISIBLE => false);
|
||||||
/* Формируем структуру заголовка */
|
/* Формируем структуру заголовка */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'NROW_RN',
|
SNAME => 'NROW_RN',
|
||||||
SCAPTION => 'Рег. номер строки',
|
SCAPTION => 'Рег. номер строки',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
|
||||||
BVISIBLE => false);
|
BVISIBLE => false);
|
||||||
/* Если раздел содержит показатели */
|
/* Если раздел содержит показатели */
|
||||||
if (S.NMARKS_EXISTS = 1) then
|
if (S.NMARKS_EXISTS = 1) then
|
||||||
/* Инициализируем колонки граф */
|
/* Инициализируем колонки граф */
|
||||||
@ -938,11 +938,11 @@ create or replace package body PKG_P8PANELS_RRPCONFED as
|
|||||||
LPAD(R.CODE, 20, '0'))
|
LPAD(R.CODE, 20, '0'))
|
||||||
loop
|
loop
|
||||||
/* Заполняем наименование строки */
|
/* Заполняем наименование строки */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SROW_NAME', SVALUE => R.NAME, BCLEAR => true);
|
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SROW_NAME', SVALUE => R.NAME, BCLEAR => true);
|
||||||
/* Заполняем мнемокод строки */
|
/* Заполняем мнемокод строки */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SROW_CODE', SVALUE => R.CODE);
|
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SROW_CODE', SVALUE => R.CODE);
|
||||||
/* Заполняем рег. номер строки */
|
/* Заполняем рег. номер строки */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NROW_RN', NVALUE => R.RN);
|
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NROW_RN', NVALUE => R.RN);
|
||||||
/* Обходим графы раздела */
|
/* Обходим графы раздела */
|
||||||
for C in (select C.RN,
|
for C in (select C.RN,
|
||||||
C.CODE,
|
C.CODE,
|
||||||
@ -963,29 +963,29 @@ create or replace package body PKG_P8PANELS_RRPCONFED as
|
|||||||
/* Считываем показатель по строке/графе */
|
/* Считываем показатель по строке/графе */
|
||||||
RRRPCONFSCTNMRK := RRPCONFSCTNMRK_GET_ROWCOL(NRRPCONFSCTN => S.NRN, NRRPROW => R.RN, NRRPCOLUMN => C.RN);
|
RRRPCONFSCTNMRK := RRPCONFSCTNMRK_GET_ROWCOL(NRRPCONFSCTN => S.NRN, NRRPROW => R.RN, NRRPCOLUMN => C.RN);
|
||||||
/* Заполняем рег. номер графы */
|
/* Заполняем рег. номер графы */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SCOL_' || C.CODE, SVALUE => C.NAME);
|
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SCOL_' || C.CODE, SVALUE => C.NAME);
|
||||||
/* Заполняем рег. номер графы */
|
/* Заполняем рег. номер графы */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NCOL_RN_' || C.CODE, NVALUE => C.RN);
|
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NCOL_RN_' || C.CODE, NVALUE => C.RN);
|
||||||
/* Заполняем рег. номер показателя */
|
/* Заполняем рег. номер показателя */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW,
|
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW,
|
||||||
SNAME => 'NMARK_RN_' || C.CODE,
|
SNAME => 'NMARK_RN_' || C.CODE,
|
||||||
NVALUE => RRRPCONFSCTNMRK.RN);
|
NVALUE => RRRPCONFSCTNMRK.RN);
|
||||||
/* Если ошибка считывания показателя */
|
/* Если ошибка считывания показателя */
|
||||||
if (RRRPCONFSCTNMRK.RN is not null) then
|
if (RRRPCONFSCTNMRK.RN is not null) then
|
||||||
/* Заполняем мнемокод показателя */
|
/* Заполняем мнемокод показателя */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW,
|
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW,
|
||||||
SNAME => 'SMARK_CODE_' || C.CODE,
|
SNAME => 'SMARK_CODE_' || C.CODE,
|
||||||
SVALUE => RRRPCONFSCTNMRK.CODE);
|
SVALUE => RRRPCONFSCTNMRK.CODE);
|
||||||
/* Добавляем атрибут состава показателей */
|
/* Добавляем атрибут состава показателей */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'MARK_CNS_' || C.CODE, SVALUE => null);
|
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'MARK_CNS_' || C.CODE, SVALUE => null);
|
||||||
end if;
|
end if;
|
||||||
end loop;
|
end loop;
|
||||||
/* Добавим строку для раздела */
|
/* Добавим строку для раздела */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW);
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW);
|
||||||
end loop;
|
end loop;
|
||||||
end if;
|
end if;
|
||||||
/* Сериализуем описание */
|
/* Сериализуем описание */
|
||||||
CDG := PKG_P8PANELS_VISUAL.TDG_TO_XML(RDATA_GRID => RDG, NINCLUDE_DEF => 1);
|
CDG := PKG_P8PANELS_VISUAL.TDATA_GRID_TO_XML(RDATA_GRID => RDG, NINCLUDE_DEF => 1);
|
||||||
/* Заполняем контейнер данными о разделе */
|
/* Заполняем контейнер данными о разделе */
|
||||||
PKG_CONTVALLOC2NS.PUTN(RCONTAINER => CXML, NTABID => S.RNUM, SROWID => 'RN', NVALUE => S.NRN);
|
PKG_CONTVALLOC2NS.PUTN(RCONTAINER => CXML, NTABID => S.RNUM, SROWID => 'RN', NVALUE => S.NRN);
|
||||||
PKG_CONTVALLOC2NS.PUTS(RCONTAINER => CXML, NTABID => S.RNUM, SROWID => 'CODE', SVALUE => S.SCODE);
|
PKG_CONTVALLOC2NS.PUTS(RCONTAINER => CXML, NTABID => S.RNUM, SROWID => 'CODE', SVALUE => S.SCODE);
|
||||||
|
|||||||
@ -59,35 +59,10 @@ create or replace package PKG_P8PANELS_SAMPLES as
|
|||||||
DDATE_TO in date -- Дата окончания задачи
|
DDATE_TO in date -- Дата окончания задачи
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Инициализация буфера данных для диаграммы Ганта */
|
|
||||||
procedure CYCLOGRAM_INIT
|
|
||||||
(
|
|
||||||
NIDENT in out number -- Идентификатор буфера сформированных данных (null - сгенерировать новый, !null - удалить старые данные и пересоздать с указанным идентификатором)
|
|
||||||
);
|
|
||||||
|
|
||||||
/* Сбор данных для отображения циклограммы */
|
|
||||||
procedure CYCLOGRAM
|
|
||||||
(
|
|
||||||
NIDENT in number, -- Идентификатор процесса
|
|
||||||
COUT out clob -- Сериализованные данные для циклограммы
|
|
||||||
);
|
|
||||||
|
|
||||||
/* Изменение задачи циклограммы */
|
|
||||||
procedure CYCLOGRAM_TASK_MODIFY
|
|
||||||
(
|
|
||||||
NIDENT in number, -- Идентификатор буфера
|
|
||||||
NRN in number, -- Рег. номер записи
|
|
||||||
SDATE_FROM in varchar2, -- Дата начала (в строковом представлении)
|
|
||||||
SDATE_TO in varchar2 -- Дата окончания (в строковом представлении)
|
|
||||||
);
|
|
||||||
|
|
||||||
end PKG_P8PANELS_SAMPLES;
|
end PKG_P8PANELS_SAMPLES;
|
||||||
/
|
/
|
||||||
create or replace package body PKG_P8PANELS_SAMPLES as
|
create or replace package body PKG_P8PANELS_SAMPLES as
|
||||||
|
|
||||||
/* Константы - циклограмма */
|
|
||||||
NCG_MULTIPLIER constant PKG_STD.TNUMBER := 5; -- Множитель для ширины отображения
|
|
||||||
|
|
||||||
/* Получение списка контрагентов */
|
/* Получение списка контрагентов */
|
||||||
procedure AGNLIST_GET
|
procedure AGNLIST_GET
|
||||||
(
|
(
|
||||||
@ -230,7 +205,6 @@ create or replace package body PKG_P8PANELS_SAMPLES as
|
|||||||
NCONTACT_METHOD => null,
|
NCONTACT_METHOD => null,
|
||||||
SMF_ID => null,
|
SMF_ID => null,
|
||||||
SOKOGU => null,
|
SOKOGU => null,
|
||||||
NJURPERS_SUBDIV => null,
|
|
||||||
NRN => NRN);
|
NRN => NRN);
|
||||||
end AGNLIST_INSERT;
|
end AGNLIST_INSERT;
|
||||||
|
|
||||||
@ -257,11 +231,11 @@ create or replace package body PKG_P8PANELS_SAMPLES as
|
|||||||
is
|
is
|
||||||
NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Организация сеанса
|
NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Организация сеанса
|
||||||
NIDENT PKG_STD.TREF := GEN_IDENT(); -- Идентификатор отбора
|
NIDENT PKG_STD.TREF := GEN_IDENT(); -- Идентификатор отбора
|
||||||
RF PKG_P8PANELS_VISUAL.TDG_FILTERS; -- Фильтры
|
RF PKG_P8PANELS_VISUAL.TFILTERS; -- Фильтры
|
||||||
RO PKG_P8PANELS_VISUAL.TDG_ORDERS; -- Сортировки
|
RO PKG_P8PANELS_VISUAL.TORDERS; -- Сортировки
|
||||||
RDG PKG_P8PANELS_VISUAL.TDG; -- Описание таблицы
|
RDG PKG_P8PANELS_VISUAL.TDATA_GRID; -- Описание таблицы
|
||||||
RAGN_TYPES PKG_P8PANELS_VISUAL.TDG_COL_VALS; -- Предопределенные значения "Типа контрагентов"
|
RAGN_TYPES PKG_P8PANELS_VISUAL.TCOL_VALS; -- Предопределенные значения "Типа контрагентов"
|
||||||
RDG_ROW PKG_P8PANELS_VISUAL.TDG_ROW; -- Строка таблицы
|
RDG_ROW PKG_P8PANELS_VISUAL.TROW; -- Строка таблицы
|
||||||
NROW_FROM PKG_STD.TREF; -- Номер строки с
|
NROW_FROM PKG_STD.TREF; -- Номер строки с
|
||||||
NROW_TO PKG_STD.TREF; -- Номер строки по
|
NROW_TO PKG_STD.TREF; -- Номер строки по
|
||||||
CSQL clob; -- Буфер для запроса
|
CSQL clob; -- Буфер для запроса
|
||||||
@ -272,71 +246,71 @@ create or replace package body PKG_P8PANELS_SAMPLES as
|
|||||||
NAGNTYPE PKG_STD.TREF; -- Буфер для "Типа"
|
NAGNTYPE PKG_STD.TREF; -- Буфер для "Типа"
|
||||||
begin
|
begin
|
||||||
/* Читаем фильтры */
|
/* Читаем фильтры */
|
||||||
RF := PKG_P8PANELS_VISUAL.TDG_FILTERS_FROM_XML(CFILTERS => CFILTERS);
|
RF := PKG_P8PANELS_VISUAL.TFILTERS_FROM_XML(CFILTERS => CFILTERS);
|
||||||
/* Читем сортировки */
|
/* Читем сортировки */
|
||||||
RO := PKG_P8PANELS_VISUAL.TDG_ORDERS_FROM_XML(CORDERS => CORDERS);
|
RO := PKG_P8PANELS_VISUAL.TORDERS_FROM_XML(CORDERS => CORDERS);
|
||||||
/* Преобразуем номер и размер страницы в номер строк с и по */
|
/* Преобразуем номер и размер страницы в номер строк с и по */
|
||||||
PKG_P8PANELS_VISUAL.UTL_ROWS_LIMITS_CALC(NPAGE_NUMBER => NPAGE_NUMBER,
|
PKG_P8PANELS_VISUAL.UTL_ROWS_LIMITS_CALC(NPAGE_NUMBER => NPAGE_NUMBER,
|
||||||
NPAGE_SIZE => NPAGE_SIZE,
|
NPAGE_SIZE => NPAGE_SIZE,
|
||||||
NROW_FROM => NROW_FROM,
|
NROW_FROM => NROW_FROM,
|
||||||
NROW_TO => NROW_TO);
|
NROW_TO => NROW_TO);
|
||||||
/* Инициализируем таблицу данных */
|
/* Инициализируем таблицу данных */
|
||||||
RDG := PKG_P8PANELS_VISUAL.TDG_MAKE(BFIXED_HEADER => true, NFIXED_COLUMNS => 2);
|
RDG := PKG_P8PANELS_VISUAL.TDATA_GRID_MAKE(BFIXED_HEADER => true, NFIXED_COLUMNS => 2);
|
||||||
/* Описываем колонки таблицы данных */
|
/* Описываем колонки таблицы данных */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'SAGNABBR',
|
SNAME => 'SAGNABBR',
|
||||||
SCAPTION => 'Мнемокод',
|
SCAPTION => 'Мнемокод',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
||||||
SCOND_FROM => 'AgentAbbr',
|
SCOND_FROM => 'AgentAbbr',
|
||||||
BVISIBLE => true,
|
BVISIBLE => true,
|
||||||
BORDER => true,
|
BORDER => true,
|
||||||
BFILTER => true,
|
BFILTER => true,
|
||||||
NWIDTH => 150);
|
NWIDTH => 150);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'SAGNINFO',
|
SNAME => 'SAGNINFO',
|
||||||
SCAPTION => 'Сведения',
|
SCAPTION => 'Сведения',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
||||||
BVISIBLE => true,
|
BVISIBLE => true,
|
||||||
BORDER => false,
|
BORDER => false,
|
||||||
BFILTER => false,
|
BFILTER => false,
|
||||||
BEXPANDABLE => true,
|
BEXPANDABLE => true,
|
||||||
NWIDTH => 300);
|
NWIDTH => 300);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'SAGNNAME',
|
SNAME => 'SAGNNAME',
|
||||||
SCAPTION => 'Наименование',
|
SCAPTION => 'Наименование',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
|
||||||
SCOND_FROM => 'AgentName',
|
SCOND_FROM => 'AgentName',
|
||||||
BVISIBLE => true,
|
BVISIBLE => true,
|
||||||
BORDER => true,
|
BORDER => true,
|
||||||
BFILTER => true,
|
BFILTER => true,
|
||||||
SPARENT => 'SAGNINFO',
|
SPARENT => 'SAGNINFO',
|
||||||
NWIDTH => 200);
|
NWIDTH => 200);
|
||||||
PKG_P8PANELS_VISUAL.TDG_COL_VALS_ADD(RCOL_VALS => RAGN_TYPES, NVALUE => 0);
|
PKG_P8PANELS_VISUAL.TCOL_VALS_ADD(RCOL_VALS => RAGN_TYPES, NVALUE => 0);
|
||||||
PKG_P8PANELS_VISUAL.TDG_COL_VALS_ADD(RCOL_VALS => RAGN_TYPES, NVALUE => 1);
|
PKG_P8PANELS_VISUAL.TCOL_VALS_ADD(RCOL_VALS => RAGN_TYPES, NVALUE => 1);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'NAGNTYPE',
|
SNAME => 'NAGNTYPE',
|
||||||
SCAPTION => 'Тип',
|
SCAPTION => 'Тип',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
|
||||||
SCOND_FROM => 'AgentType',
|
SCOND_FROM => 'AgentType',
|
||||||
BVISIBLE => true,
|
BVISIBLE => true,
|
||||||
BORDER => true,
|
BORDER => true,
|
||||||
BFILTER => true,
|
BFILTER => true,
|
||||||
SPARENT => 'SAGNINFO',
|
SPARENT => 'SAGNINFO',
|
||||||
NWIDTH => 100,
|
NWIDTH => 100,
|
||||||
RCOL_VALS => RAGN_TYPES,
|
RCOL_VALS => RAGN_TYPES,
|
||||||
SHINT => 'В Системе бывают контрагенты двух типов:<br>' ||
|
SHINT => 'В Системе бывают контрагенты двух типов:<br>' ||
|
||||||
'<b style="color:blue">Юридическое лицо</b> - организация, которая имеет в собственности, хозяйственном ведении ' ||
|
'<b style="color:blue">Юридическое лицо</b> - организация, которая имеет в собственности, хозяйственном ведении ' ||
|
||||||
'или оперативном управлении обособленное имущество, отвечает по своим обязательствам этим имуществом, может от своего ' ||
|
'или оперативном управлении обособленное имущество, отвечает по своим обязательствам этим имуществом, может от своего ' ||
|
||||||
'имени приобретать и осуществлять имущественные и личные неимущественные права, отвечать по своим обязанностям.<br>' ||
|
'имени приобретать и осуществлять имущественные и личные неимущественные права, отвечать по своим обязанностям.<br>' ||
|
||||||
'<b style="color:green">Физическое лицо</b> - субъект правовых отношений, представляющий собой одного человека.');
|
'<b style="color:green">Физическое лицо</b> - субъект правовых отношений, представляющий собой одного человека.');
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'SFULLNAME',
|
SNAME => 'SFULLNAME',
|
||||||
SCAPTION => 'Полное наименование',
|
SCAPTION => 'Полное наименование',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR);
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
|
||||||
SNAME => 'SAGNIDNUMB',
|
SNAME => 'SAGNIDNUMB',
|
||||||
SCAPTION => 'ИНН',
|
SCAPTION => 'ИНН',
|
||||||
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR);
|
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR);
|
||||||
/* Обходим данные */
|
/* Обходим данные */
|
||||||
begin
|
begin
|
||||||
/* Добавляем подсказку совместимости */
|
/* Добавляем подсказку совместимости */
|
||||||
@ -362,17 +336,14 @@ create or replace package body PKG_P8PANELS_SAMPLES as
|
|||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and AG.RN in (select ID from COND_BROKER_IDSMART where IDENT = :NIDENT) %ORDER_BY%) D) F');
|
PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' and AG.RN in (select ID from COND_BROKER_IDSMART where IDENT = :NIDENT) %ORDER_BY%) D) F');
|
||||||
PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' where F.NROW between :NROW_FROM and :NROW_TO');
|
PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' where F.NROW between :NROW_FROM and :NROW_TO');
|
||||||
/* Учтём сортировки */
|
/* Учтём сортировки */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ORDERS_SET_QUERY(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TORDERS_SET_QUERY(RDATA_GRID => RDG, RORDERS => RO, SPATTERN => '%ORDER_BY%', CSQL => CSQL);
|
||||||
RORDERS => RO,
|
|
||||||
SPATTERN => '%ORDER_BY%',
|
|
||||||
CSQL => CSQL);
|
|
||||||
/* Учтём фильтры */
|
/* Учтём фильтры */
|
||||||
PKG_P8PANELS_VISUAL.TDG_FILTERS_SET_QUERY(NIDENT => NIDENT,
|
PKG_P8PANELS_VISUAL.TFILTERS_SET_QUERY(NIDENT => NIDENT,
|
||||||
NCOMPANY => NCOMPANY,
|
NCOMPANY => NCOMPANY,
|
||||||
SUNIT => 'AGNLIST',
|
SUNIT => 'AGNLIST',
|
||||||
SPROCEDURE => 'P_AGNLIST_BASE_COND',
|
SPROCEDURE => 'P_AGNLIST_BASE_COND',
|
||||||
RDATA_GRID => RDG,
|
RDATA_GRID => RDG,
|
||||||
RFILTERS => RF);
|
RFILTERS => RF);
|
||||||
/* Разбираем его */
|
/* Разбираем его */
|
||||||
ICURSOR := PKG_SQL_DML.OPEN_CURSOR(SWHAT => 'SELECT');
|
ICURSOR := PKG_SQL_DML.OPEN_CURSOR(SWHAT => 'SELECT');
|
||||||
PKG_SQL_DML.PARSE(ICURSOR => ICURSOR, SQUERY => CSQL);
|
PKG_SQL_DML.PARSE(ICURSOR => ICURSOR, SQUERY => CSQL);
|
||||||
@ -400,38 +371,29 @@ create or replace package body PKG_P8PANELS_SAMPLES as
|
|||||||
if (NAGNTYPE = 0) then
|
if (NAGNTYPE = 0) then
|
||||||
SGROUP := 'JUR';
|
SGROUP := 'JUR';
|
||||||
SAGNINFO := SAGNNAME || ', ЮЛ';
|
SAGNINFO := SAGNNAME || ', ЮЛ';
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_GROUP(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_GROUP(RDATA_GRID => RDG,
|
||||||
SNAME => SGROUP,
|
SNAME => SGROUP,
|
||||||
SCAPTION => 'Юридические лица',
|
SCAPTION => 'Юридические лица',
|
||||||
BEXPANDABLE => true,
|
BEXPANDABLE => true,
|
||||||
BEXPANDED => false);
|
BEXPANDED => false);
|
||||||
else
|
else
|
||||||
SGROUP := 'PERS';
|
SGROUP := 'PERS';
|
||||||
SAGNINFO := SAGNNAME || ', ФЛ';
|
SAGNINFO := SAGNNAME || ', ФЛ';
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_GROUP(RDATA_GRID => RDG,
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_GROUP(RDATA_GRID => RDG,
|
||||||
SNAME => SGROUP,
|
SNAME => SGROUP,
|
||||||
SCAPTION => 'Физические лица',
|
SCAPTION => 'Физические лица',
|
||||||
BEXPANDABLE => true,
|
BEXPANDABLE => true,
|
||||||
BEXPANDED => false);
|
BEXPANDED => false);
|
||||||
end if;
|
end if;
|
||||||
RDG_ROW := PKG_P8PANELS_VISUAL.TDG_ROW_MAKE(SGROUP => SGROUP);
|
RDG_ROW := PKG_P8PANELS_VISUAL.TROW_MAKE(SGROUP => SGROUP);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLS(RROW => RDG_ROW,
|
PKG_P8PANELS_VISUAL.TROW_ADD_CUR_COLS(RROW => RDG_ROW, SNAME => 'SAGNABBR', ICURSOR => ICURSOR, NPOSITION => 1);
|
||||||
SNAME => 'SAGNABBR',
|
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SAGNINFO', SVALUE => SAGNINFO);
|
||||||
ICURSOR => ICURSOR,
|
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SAGNNAME', SVALUE => SAGNNAME);
|
||||||
NPOSITION => 1);
|
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NAGNTYPE', NVALUE => NAGNTYPE);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SAGNINFO', SVALUE => SAGNINFO);
|
PKG_P8PANELS_VISUAL.TROW_ADD_CUR_COLS(RROW => RDG_ROW, SNAME => 'SFULLNAME', ICURSOR => ICURSOR, NPOSITION => 4);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SAGNNAME', SVALUE => SAGNNAME);
|
PKG_P8PANELS_VISUAL.TROW_ADD_CUR_COLS(RROW => RDG_ROW, SNAME => 'SAGNIDNUMB', ICURSOR => ICURSOR, NPOSITION => 5);
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NAGNTYPE', NVALUE => NAGNTYPE);
|
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLS(RROW => RDG_ROW,
|
|
||||||
SNAME => 'SFULLNAME',
|
|
||||||
ICURSOR => ICURSOR,
|
|
||||||
NPOSITION => 4);
|
|
||||||
PKG_P8PANELS_VISUAL.TDG_ROW_ADD_CUR_COLS(RROW => RDG_ROW,
|
|
||||||
SNAME => 'SAGNIDNUMB',
|
|
||||||
ICURSOR => ICURSOR,
|
|
||||||
NPOSITION => 5);
|
|
||||||
/* Добавляем строку в таблицу */
|
/* Добавляем строку в таблицу */
|
||||||
PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW);
|
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW);
|
||||||
end loop;
|
end loop;
|
||||||
/* Освобождаем курсор */
|
/* Освобождаем курсор */
|
||||||
PKG_SQL_DML.CLOSE_CURSOR(ICURSOR => ICURSOR);
|
PKG_SQL_DML.CLOSE_CURSOR(ICURSOR => ICURSOR);
|
||||||
@ -441,7 +403,7 @@ create or replace package body PKG_P8PANELS_SAMPLES as
|
|||||||
raise;
|
raise;
|
||||||
end;
|
end;
|
||||||
/* Сериализуем описание */
|
/* Сериализуем описание */
|
||||||
COUT := PKG_P8PANELS_VISUAL.TDG_TO_XML(RDATA_GRID => RDG, NINCLUDE_DEF => NINCLUDE_DEF);
|
COUT := PKG_P8PANELS_VISUAL.TDATA_GRID_TO_XML(RDATA_GRID => RDG, NINCLUDE_DEF => NINCLUDE_DEF);
|
||||||
end DATA_GRID;
|
end DATA_GRID;
|
||||||
|
|
||||||
/* График */
|
/* График */
|
||||||
@ -705,532 +667,5 @@ create or replace package body PKG_P8PANELS_SAMPLES as
|
|||||||
end loop;
|
end loop;
|
||||||
end GANTT_MODIFY;
|
end GANTT_MODIFY;
|
||||||
|
|
||||||
/* Очистка буфера данных для циклограммы */
|
|
||||||
procedure CYCLOGRAM_BASE_CLEAN
|
|
||||||
(
|
|
||||||
NIDENT in number -- Идентификатор буфера
|
|
||||||
)
|
|
||||||
is
|
|
||||||
begin
|
|
||||||
/* Удалим из буфера всё с указанным идентификатором */
|
|
||||||
delete from P8PNL_SMPL_CYCLOGRAM T where T.IDENT = NIDENT;
|
|
||||||
end CYCLOGRAM_BASE_CLEAN;
|
|
||||||
|
|
||||||
/* Добавление данных в буфер циклограммы */
|
|
||||||
procedure CYCLOGRAM_BASE_INSERT
|
|
||||||
(
|
|
||||||
NIDENT in number, -- Идентификатор буфера
|
|
||||||
NTYPE in number, -- Тип (0 - колонка, 1 - группа, 2 - задача)
|
|
||||||
SNAME in varchar2, -- Наименование
|
|
||||||
NPOS_START in number := null, -- Позиция начала элемента
|
|
||||||
NPOS_END in number := null, -- Позиция окончания элемента
|
|
||||||
DDATE_FROM in date := null, -- Дата начала
|
|
||||||
DDATE_TO in date := null, -- Дата окончания
|
|
||||||
NTASK_GROUP in number := null, -- Рег. номер группы
|
|
||||||
NRN out number -- Рег. номер записи
|
|
||||||
)
|
|
||||||
is
|
|
||||||
begin
|
|
||||||
/* Генерируем рег. номер записи */
|
|
||||||
NRN := GEN_ID();
|
|
||||||
/* Добавим запись */
|
|
||||||
insert into P8PNL_SMPL_CYCLOGRAM
|
|
||||||
(RN, IDENT, type, name, POS_START, POS_END, DATE_FROM, DATE_TO, TASK_GROUP)
|
|
||||||
values
|
|
||||||
(NRN, NIDENT, NTYPE, SNAME, NPOS_START, NPOS_END, DDATE_FROM, DDATE_TO, NTASK_GROUP);
|
|
||||||
end CYCLOGRAM_BASE_INSERT;
|
|
||||||
|
|
||||||
/* Исправление данных в буфере циклограммы */
|
|
||||||
procedure CYCLOGRAM_BASE_UPDATE
|
|
||||||
(
|
|
||||||
NIDENT in number, -- Идентификатор буфера
|
|
||||||
NRN in number, -- Рег. номер записи
|
|
||||||
NTYPE in number, -- Тип задачи (0 - этап/веха, 1 - работа)
|
|
||||||
SNAME in varchar2, -- Наименование
|
|
||||||
NPOS_START in number, -- Позиция начала
|
|
||||||
NPOS_END in number, -- Позиция окончания
|
|
||||||
DDATE_FROM in date, -- Дата начала
|
|
||||||
DDATE_TO in date, -- Дата окончания
|
|
||||||
NTASK_GROUP in number -- Рег. номер группы
|
|
||||||
)
|
|
||||||
is
|
|
||||||
begin
|
|
||||||
/* Изменим запись */
|
|
||||||
update P8PNL_SMPL_CYCLOGRAM T
|
|
||||||
set T.TYPE = NTYPE,
|
|
||||||
T.NAME = SNAME,
|
|
||||||
T.POS_START = NPOS_START,
|
|
||||||
T.POS_END = NPOS_END,
|
|
||||||
T.DATE_FROM = DDATE_FROM,
|
|
||||||
T.DATE_TO = DDATE_TO,
|
|
||||||
T.TASK_GROUP = NTASK_GROUP
|
|
||||||
where T.RN = NRN
|
|
||||||
and T.IDENT = NIDENT;
|
|
||||||
end CYCLOGRAM_BASE_UPDATE;
|
|
||||||
|
|
||||||
/* Инициализация буфера данных для циклограммы */
|
|
||||||
procedure CYCLOGRAM_INIT
|
|
||||||
(
|
|
||||||
NIDENT in out number -- Идентификатор буфера сформированных данных (null - сгенерировать новый, !null - удалить старые данные и пересоздать с указанным идентификатором)
|
|
||||||
)
|
|
||||||
is
|
|
||||||
NYEAR PKG_STD.TNUMBER; -- Текущий год
|
|
||||||
DCYCLOGRAM_START PKG_STD.TLDATE; -- Дата начала циклограммы
|
|
||||||
DMONTH_CUR PKG_STD.TLDATE; -- Текущий месяц (для расчетов)
|
|
||||||
DMONTH_START PKG_STD.TLDATE; -- Начало месяца (для расчетов)
|
|
||||||
DMONTH_END PKG_STD.TLDATE; -- Окончание месяца (для расчетов)
|
|
||||||
NMONTH_DAYS PKG_STD.TNUMBER; -- Количество дней месяца
|
|
||||||
NSTART PKG_STD.TNUMBER := 0; -- Позиция начала элемента
|
|
||||||
NEND PKG_STD.TNUMBER := 0; -- Позиция окончания элемента
|
|
||||||
NGROUP PKG_STD.TNUMBER; -- Рег. номер группы
|
|
||||||
NDUMMY PKG_STD.TNUMBER; -- Буфер для рег. номера
|
|
||||||
NMONTH PKG_STD.TNUMBER; -- Месяц даты
|
|
||||||
|
|
||||||
/* Инициализация группы */
|
|
||||||
procedure INIT_GROUP
|
|
||||||
(
|
|
||||||
NIDENT in number, -- Идентификатор буфера
|
|
||||||
DDATE in date, -- Текущая обрабатываемая дата
|
|
||||||
NRN in out number -- Рег. номер группы
|
|
||||||
)
|
|
||||||
is
|
|
||||||
NMONTH PKG_STD.TNUMBER; -- Месяц даты
|
|
||||||
begin
|
|
||||||
/* Считываем текущий месяц */
|
|
||||||
NMONTH := D_MONTH(DDATE => DDATE);
|
|
||||||
/* Исходим от даты (формируем группу на начало каждого квартала) */
|
|
||||||
case NMONTH
|
|
||||||
/* Первый квартал */
|
|
||||||
when 1 then
|
|
||||||
/* Добавляем группу */
|
|
||||||
CYCLOGRAM_BASE_INSERT(NIDENT => NIDENT, NTYPE => 1, SNAME => 'I группа', NRN => NRN);
|
|
||||||
/* Второй квартал */
|
|
||||||
when 4 then
|
|
||||||
/* Добавляем группу */
|
|
||||||
CYCLOGRAM_BASE_INSERT(NIDENT => NIDENT, NTYPE => 1, SNAME => 'II группа', NRN => NRN);
|
|
||||||
/* Третий квартал */
|
|
||||||
when 7 then
|
|
||||||
/* Добавляем группу */
|
|
||||||
CYCLOGRAM_BASE_INSERT(NIDENT => NIDENT, NTYPE => 1, SNAME => 'III группа', NRN => NRN);
|
|
||||||
/* Четвертый квартал */
|
|
||||||
when 10 then
|
|
||||||
/* ДОбавляем группу */
|
|
||||||
CYCLOGRAM_BASE_INSERT(NIDENT => NIDENT, NTYPE => 1, SNAME => 'IV группа', NRN => NRN);
|
|
||||||
else
|
|
||||||
null;
|
|
||||||
end case;
|
|
||||||
end INIT_GROUP;
|
|
||||||
begin
|
|
||||||
/* Удаляем старые данные из буфера */
|
|
||||||
if (NIDENT is not null) then
|
|
||||||
CYCLOGRAM_BASE_CLEAN(NIDENT => NIDENT);
|
|
||||||
else
|
|
||||||
/* Илиформируем новый идентификатор, если не задан */
|
|
||||||
NIDENT := GEN_IDENT();
|
|
||||||
end if;
|
|
||||||
/* Фиксируем текущий год */
|
|
||||||
NYEAR := EXTRACT(year from sysdate);
|
|
||||||
/* Фиксируем дату начала циклограммы */
|
|
||||||
DCYCLOGRAM_START := TO_DATE('01.01.' || NYEAR, 'dd.mm.yyyy');
|
|
||||||
/* Добавляем колонки и групповые задачи (месяцы года) */
|
|
||||||
for I in 0 .. 11
|
|
||||||
loop
|
|
||||||
/* Рассчитываем текущий месяц */
|
|
||||||
DMONTH_CUR := ADD_MONTHS(DCYCLOGRAM_START, I);
|
|
||||||
/* Считываем первый и последний день месяца */
|
|
||||||
P_FIRST_LAST_DAY(DCALCDATE => DMONTH_CUR, DBGNDATE => DMONTH_START, DENDDATE => DMONTH_END);
|
|
||||||
/* Рассчитываем количество дней месяца */
|
|
||||||
NMONTH_DAYS := DMONTH_END - DMONTH_START + 1;
|
|
||||||
/* Рассчитываем позицию окончания элемента */
|
|
||||||
NEND := NSTART + (NMONTH_DAYS * NCG_MULTIPLIER);
|
|
||||||
/* Определяем номер месяца */
|
|
||||||
NMONTH := D_MONTH(DDATE => DMONTH_CUR);
|
|
||||||
/* Добавляем колонку в таблицу */
|
|
||||||
CYCLOGRAM_BASE_INSERT(NIDENT => NIDENT,
|
|
||||||
NTYPE => 0,
|
|
||||||
SNAME => TO_CHAR(NMONTH),
|
|
||||||
NPOS_START => NSTART,
|
|
||||||
NPOS_END => NEND,
|
|
||||||
NRN => NDUMMY);
|
|
||||||
/* Инициализируем группу */
|
|
||||||
INIT_GROUP(NIDENT => NIDENT, DDATE => DMONTH_CUR, NRN => NGROUP);
|
|
||||||
/* Добавляем задачу */
|
|
||||||
CYCLOGRAM_BASE_INSERT(NIDENT => NIDENT,
|
|
||||||
NTYPE => 2,
|
|
||||||
SNAME => 'Работа ' || TO_CHAR(I + 1),
|
|
||||||
NPOS_START => NSTART,
|
|
||||||
NPOS_END => NEND,
|
|
||||||
DDATE_FROM => DMONTH_START,
|
|
||||||
DDATE_TO => DMONTH_END,
|
|
||||||
NTASK_GROUP => NGROUP,
|
|
||||||
NRN => NDUMMY);
|
|
||||||
/* Если это февраль, май, август или ноябрь - добавляем особосбленную задачу */
|
|
||||||
if (NMONTH in (2, 5, 8, 11)) then
|
|
||||||
/* Добавляем обособленную задачу */
|
|
||||||
CYCLOGRAM_BASE_INSERT(NIDENT => NIDENT,
|
|
||||||
NTYPE => 2,
|
|
||||||
SNAME => 'Обособленная работа ' || NMONTH,
|
|
||||||
NPOS_START => NSTART,
|
|
||||||
NPOS_END => NEND,
|
|
||||||
DDATE_FROM => DMONTH_START,
|
|
||||||
DDATE_TO => DMONTH_END,
|
|
||||||
NRN => NDUMMY);
|
|
||||||
end if;
|
|
||||||
/* Рассчитываем начало следующего месяца */
|
|
||||||
NSTART := NEND;
|
|
||||||
end loop;
|
|
||||||
end CYCLOGRAM_INIT;
|
|
||||||
|
|
||||||
/* Сбор данных для отображения циклограммы */
|
|
||||||
procedure CYCLOGRAM
|
|
||||||
(
|
|
||||||
NIDENT in number, -- Идентификатор процесса
|
|
||||||
COUT out clob -- Сериализованные данные для циклограммы
|
|
||||||
)
|
|
||||||
is
|
|
||||||
CG PKG_P8PANELS_VISUAL.TCYCLOGRAM; -- Описание циклограммы
|
|
||||||
RTASK PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK; -- Описание задачи циклограммы
|
|
||||||
NLINE_NUMB PKG_STD.TNUMBER := 0; -- Номер строки
|
|
||||||
NLINE_NUMB_TMP PKG_STD.TNUMBER := 0; -- Номер строки (буфер)
|
|
||||||
DTASK_DATE_START PKG_STD.TLDATE; -- Дата начала этапа
|
|
||||||
DTASK_DATE_END PKG_STD.TLDATE; -- Дата окончания этапа
|
|
||||||
NTASK_START PKG_STD.TNUMBER := 0; -- Позиция начала этапа
|
|
||||||
NTASK_END PKG_STD.TNUMBER := 0; -- Позиция окончания этапа
|
|
||||||
STASK_NAME PKG_STD.TSTRING; -- Наименование задачи
|
|
||||||
SCOLOR_WHITE PKG_STD.TSTRING := 'white'; -- Цвет - белый
|
|
||||||
SBG_TASK_COLOR_W_GRP PKG_STD.TSTRING := '#6bc982'; -- Цвет задачи с группой
|
|
||||||
SHL_TASK_COLOR_W_GRP PKG_STD.TSTRING := '#7dd592'; -- Цвет наведения задачи с группой
|
|
||||||
SBG_TASK_COLOR_WO_GRP PKG_STD.TSTRING := '#e36d6d'; -- Цвет задачи без группы
|
|
||||||
STEXT_COLOR_TASK_WO_GRP PKG_STD.TSTRING := '#e5e5e5'; -- Цвет текста задачи без группы
|
|
||||||
SBG_TASK_COLOR_GRP PKG_STD.TSTRING := 'cadetblue'; -- Цвет групповой задачи
|
|
||||||
SHL_TASK_COLOR_GRP PKG_STD.TSTRING := '#6fadaf'; -- Цвет наведения групповой задачи
|
|
||||||
|
|
||||||
/* Считывание значений группирующей задачи */
|
|
||||||
procedure GROUP_TASK_GET
|
|
||||||
(
|
|
||||||
NIDENT in number, -- Идентификатор процесса
|
|
||||||
NGROUP in number := null, -- Рег. номер группы
|
|
||||||
NFLAG_WO_GROUP in number := 0, -- Признак отбора задач без групп (0 - нет, 1 - да)
|
|
||||||
DTASK_DATE_START out date, -- Дата начала этапа
|
|
||||||
DTASK_DATE_END out date, -- Дата окончания этапа
|
|
||||||
NTASK_START out number, -- Позиция начала этапа
|
|
||||||
NTASK_END out number -- Позиция окончания этапа
|
|
||||||
)
|
|
||||||
is
|
|
||||||
begin
|
|
||||||
/* Считываем начало и окончание этапа */
|
|
||||||
begin
|
|
||||||
select min(T.DATE_FROM),
|
|
||||||
max(T.DATE_TO),
|
|
||||||
min(T.POS_START),
|
|
||||||
max(T.POS_END)
|
|
||||||
into DTASK_DATE_START,
|
|
||||||
DTASK_DATE_END,
|
|
||||||
NTASK_START,
|
|
||||||
NTASK_END
|
|
||||||
from P8PNL_SMPL_CYCLOGRAM T
|
|
||||||
where T.IDENT = NIDENT
|
|
||||||
and (((NFLAG_WO_GROUP = 1) and (T.TASK_GROUP is null)) or ((NFLAG_WO_GROUP = 0) and (T.TASK_GROUP is not null)))
|
|
||||||
and ((NGROUP is null) or ((NGROUP is not null) and (T.TASK_GROUP = NGROUP)))
|
|
||||||
and T.TYPE = 2;
|
|
||||||
end;
|
|
||||||
end GROUP_TASK_GET;
|
|
||||||
begin
|
|
||||||
/* Инициализируем циклограмму */
|
|
||||||
CG := PKG_P8PANELS_VISUAL.TCYCLOGRAM_MAKE(STITLE => 'Задачи на ' || TO_CHAR(EXTRACT(year from sysdate)) || ' год');
|
|
||||||
/* Добавляем атрибуты */
|
|
||||||
PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_TASK_ATTR(RCYCLOGRAM => CG,
|
|
||||||
SNAME => 'ddate_start',
|
|
||||||
SCAPTION => 'Дата начала',
|
|
||||||
BVISIBLE => true,
|
|
||||||
BCLEAR => true);
|
|
||||||
PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_TASK_ATTR(RCYCLOGRAM => CG,
|
|
||||||
SNAME => 'ddate_end',
|
|
||||||
SCAPTION => 'Дата окончания',
|
|
||||||
BVISIBLE => true);
|
|
||||||
PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_TASK_ATTR(RCYCLOGRAM => CG,
|
|
||||||
SNAME => 'type',
|
|
||||||
SCAPTION => 'Тип',
|
|
||||||
BVISIBLE => false);
|
|
||||||
/* Обходим колонки */
|
|
||||||
for CLMN in (select T.NAME,
|
|
||||||
T.POS_START,
|
|
||||||
T.POS_END
|
|
||||||
from P8PNL_SMPL_CYCLOGRAM T
|
|
||||||
where T.IDENT = NIDENT
|
|
||||||
and T.TYPE = 0)
|
|
||||||
loop
|
|
||||||
/* Добавляем колонку */
|
|
||||||
PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_COLUMN(RCYCLOGRAM => CG,
|
|
||||||
SNAME => CLMN.NAME,
|
|
||||||
NSTART => CLMN.POS_START,
|
|
||||||
NEND => CLMN.POS_END);
|
|
||||||
end loop;
|
|
||||||
/* Считываем значения для задач проекта */
|
|
||||||
GROUP_TASK_GET(NIDENT => NIDENT,
|
|
||||||
NFLAG_WO_GROUP => 0,
|
|
||||||
DTASK_DATE_START => DTASK_DATE_START,
|
|
||||||
DTASK_DATE_END => DTASK_DATE_END,
|
|
||||||
NTASK_START => NTASK_START,
|
|
||||||
NTASK_END => NTASK_END);
|
|
||||||
/* Формируем задачу (этап) */
|
|
||||||
RTASK := PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_MAKE(NRN => 1,
|
|
||||||
SCAPTION => 'Задачи проекта',
|
|
||||||
SNAME => 'Задачи проекта',
|
|
||||||
NLINE_NUMB => NLINE_NUMB,
|
|
||||||
NSTART => NTASK_START,
|
|
||||||
NEND => NTASK_END,
|
|
||||||
SBG_COLOR => SCOLOR_WHITE);
|
|
||||||
/* Добавляем атрибуты */
|
|
||||||
PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG,
|
|
||||||
RTASK => RTASK,
|
|
||||||
SNAME => 'ddate_start',
|
|
||||||
SVALUE => PKG_XCONVERT.DATE_TO_XML(DVALUE => DTASK_DATE_START),
|
|
||||||
BCLEAR => true);
|
|
||||||
PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG,
|
|
||||||
RTASK => RTASK,
|
|
||||||
SNAME => 'ddate_end',
|
|
||||||
SVALUE => PKG_XCONVERT.DATE_TO_XML(DVALUE => DTASK_DATE_END));
|
|
||||||
PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG, RTASK => RTASK, SNAME => 'type', SVALUE => 0);
|
|
||||||
/* Добавляем задачу в циклограмму */
|
|
||||||
PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_TASK(RCYCLOGRAM => CG, RTASK => RTASK);
|
|
||||||
/* Указываем следующую строку */
|
|
||||||
NLINE_NUMB := NLINE_NUMB + 1;
|
|
||||||
/* Обходим группы */
|
|
||||||
for GRP in (select T.RN,
|
|
||||||
T.NAME,
|
|
||||||
ROWNUM RNUM
|
|
||||||
from P8PNL_SMPL_CYCLOGRAM T
|
|
||||||
where T.IDENT = NIDENT
|
|
||||||
and T.TYPE = 1
|
|
||||||
order by T.RN asc)
|
|
||||||
loop
|
|
||||||
/* Если это вторая группа - сохраним номер строки */
|
|
||||||
if (GRP.RNUM = 2) then
|
|
||||||
NLINE_NUMB_TMP := NLINE_NUMB;
|
|
||||||
end if;
|
|
||||||
/* Если это третья группа - вернемся на уровень второй группы */
|
|
||||||
if (GRP.RNUM = 3) then
|
|
||||||
NLINE_NUMB := NLINE_NUMB_TMP;
|
|
||||||
end if;
|
|
||||||
/* Добавляем группу */
|
|
||||||
PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_GROUP(RCYCLOGRAM => CG,
|
|
||||||
SNAME => GRP.NAME,
|
|
||||||
NHEADER_HEIGHT => 30,
|
|
||||||
NHEADER_WIDTH => 200);
|
|
||||||
/* Считываем значения этапа группы */
|
|
||||||
GROUP_TASK_GET(NIDENT => NIDENT,
|
|
||||||
NGROUP => GRP.RN,
|
|
||||||
DTASK_DATE_START => DTASK_DATE_START,
|
|
||||||
DTASK_DATE_END => DTASK_DATE_END,
|
|
||||||
NTASK_START => NTASK_START,
|
|
||||||
NTASK_END => NTASK_END);
|
|
||||||
/* Формируем задачу (этап) */
|
|
||||||
RTASK := PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_MAKE(NRN => GRP.RN,
|
|
||||||
SCAPTION => 'Этап ' || TO_CHAR(GRP.RNUM),
|
|
||||||
SNAME => 'Этап ' || TO_CHAR(GRP.RNUM),
|
|
||||||
NLINE_NUMB => NLINE_NUMB,
|
|
||||||
NSTART => NTASK_START,
|
|
||||||
NEND => NTASK_END,
|
|
||||||
SBG_COLOR => SBG_TASK_COLOR_GRP,
|
|
||||||
STEXT_COLOR => SCOLOR_WHITE,
|
|
||||||
SHIGHLIGHT_COLOR => SHL_TASK_COLOR_GRP);
|
|
||||||
/* Добавляем атрибуты */
|
|
||||||
PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG,
|
|
||||||
RTASK => RTASK,
|
|
||||||
SNAME => 'ddate_start',
|
|
||||||
SVALUE => PKG_XCONVERT.DATE_TO_XML(DVALUE => DTASK_DATE_START),
|
|
||||||
BCLEAR => true);
|
|
||||||
PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG,
|
|
||||||
RTASK => RTASK,
|
|
||||||
SNAME => 'ddate_end',
|
|
||||||
SVALUE => PKG_XCONVERT.DATE_TO_XML(DVALUE => DTASK_DATE_END));
|
|
||||||
PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG, RTASK => RTASK, SNAME => 'type', SVALUE => 1);
|
|
||||||
/* Добавляем задачу в циклограмму */
|
|
||||||
PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_TASK(RCYCLOGRAM => CG, RTASK => RTASK);
|
|
||||||
/* Обходим задачи группы */
|
|
||||||
for TASK in (select T.RN,
|
|
||||||
T.NAME,
|
|
||||||
T.POS_START,
|
|
||||||
T.POS_END,
|
|
||||||
T.DATE_FROM,
|
|
||||||
T.DATE_TO,
|
|
||||||
ROWNUM RNUM
|
|
||||||
from P8PNL_SMPL_CYCLOGRAM T
|
|
||||||
where T.IDENT = NIDENT
|
|
||||||
and T.TYPE = 2
|
|
||||||
and T.TASK_GROUP = GRP.RN)
|
|
||||||
loop
|
|
||||||
/* Указываем следующую строку */
|
|
||||||
NLINE_NUMB := NLINE_NUMB + 1;
|
|
||||||
/* Формируем наименование задачи */
|
|
||||||
STASK_NAME := 'Работа ' || TO_CHAR(TASK.RNUM) || ' этапа ' || TO_CHAR(GRP.RNUM);
|
|
||||||
/* Формируем задачу */
|
|
||||||
RTASK := PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_MAKE(NRN => TASK.RN,
|
|
||||||
SCAPTION => STASK_NAME,
|
|
||||||
SNAME => STASK_NAME,
|
|
||||||
NLINE_NUMB => NLINE_NUMB,
|
|
||||||
NSTART => TASK.POS_START,
|
|
||||||
NEND => TASK.POS_END,
|
|
||||||
SGROUP => GRP.NAME,
|
|
||||||
SBG_COLOR => SBG_TASK_COLOR_W_GRP,
|
|
||||||
STEXT_COLOR => SCOLOR_WHITE,
|
|
||||||
SHIGHLIGHT_COLOR => SHL_TASK_COLOR_W_GRP);
|
|
||||||
/* Добавляем атрибуты */
|
|
||||||
PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG,
|
|
||||||
RTASK => RTASK,
|
|
||||||
SNAME => 'ddate_start',
|
|
||||||
SVALUE => PKG_XCONVERT.DATE_TO_XML(DVALUE => TASK.DATE_FROM),
|
|
||||||
BCLEAR => true);
|
|
||||||
PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG,
|
|
||||||
RTASK => RTASK,
|
|
||||||
SNAME => 'ddate_end',
|
|
||||||
SVALUE => PKG_XCONVERT.DATE_TO_XML(DVALUE => TASK.DATE_TO));
|
|
||||||
PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG,
|
|
||||||
RTASK => RTASK,
|
|
||||||
SNAME => 'type',
|
|
||||||
SVALUE => 2);
|
|
||||||
/* Добавляем задачу в циклограмму */
|
|
||||||
PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_TASK(RCYCLOGRAM => CG, RTASK => RTASK);
|
|
||||||
end loop;
|
|
||||||
/* Указываем следующую строку */
|
|
||||||
NLINE_NUMB := NLINE_NUMB + 1;
|
|
||||||
end loop;
|
|
||||||
/* Указываем следующую строку */
|
|
||||||
NLINE_NUMB := NLINE_NUMB + 1;
|
|
||||||
/* Считываем значения для обособленных задач */
|
|
||||||
GROUP_TASK_GET(NIDENT => NIDENT,
|
|
||||||
NFLAG_WO_GROUP => 1,
|
|
||||||
DTASK_DATE_START => DTASK_DATE_START,
|
|
||||||
DTASK_DATE_END => DTASK_DATE_END,
|
|
||||||
NTASK_START => NTASK_START,
|
|
||||||
NTASK_END => NTASK_END);
|
|
||||||
/* Формируем задачу (этап) */
|
|
||||||
RTASK := PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_MAKE(NRN => 1,
|
|
||||||
SCAPTION => 'Обособленные задачи',
|
|
||||||
SNAME => 'Обособленные задачи',
|
|
||||||
NLINE_NUMB => NLINE_NUMB,
|
|
||||||
NSTART => NTASK_START,
|
|
||||||
NEND => NTASK_END,
|
|
||||||
SBG_COLOR => SCOLOR_WHITE);
|
|
||||||
/* Добавляем атрибуты */
|
|
||||||
PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG,
|
|
||||||
RTASK => RTASK,
|
|
||||||
SNAME => 'ddate_start',
|
|
||||||
SVALUE => PKG_XCONVERT.DATE_TO_XML(DVALUE => DTASK_DATE_START),
|
|
||||||
BCLEAR => true);
|
|
||||||
PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG,
|
|
||||||
RTASK => RTASK,
|
|
||||||
SNAME => 'ddate_end',
|
|
||||||
SVALUE => PKG_XCONVERT.DATE_TO_XML(DVALUE => DTASK_DATE_END));
|
|
||||||
PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG, RTASK => RTASK, SNAME => 'type', SVALUE => 0);
|
|
||||||
/* Добавляем задачу в циклограмму */
|
|
||||||
PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_TASK(RCYCLOGRAM => CG, RTASK => RTASK);
|
|
||||||
/* Указываем следующую строку */
|
|
||||||
NLINE_NUMB := NLINE_NUMB + 1;
|
|
||||||
/* Цикл по обособленным задачам */
|
|
||||||
for REC in (select T.RN,
|
|
||||||
T.NAME,
|
|
||||||
T.POS_START,
|
|
||||||
T.POS_END,
|
|
||||||
T.DATE_FROM,
|
|
||||||
T.DATE_TO,
|
|
||||||
ROWNUM RNUM
|
|
||||||
from P8PNL_SMPL_CYCLOGRAM T
|
|
||||||
where T.IDENT = NIDENT
|
|
||||||
and T.TYPE = 2
|
|
||||||
and T.TASK_GROUP is null)
|
|
||||||
loop
|
|
||||||
/* Формируем наименование задачи */
|
|
||||||
STASK_NAME := 'Работа ' || TO_CHAR(REC.RNUM) || ' без этапа ';
|
|
||||||
/* Формируем задачу */
|
|
||||||
RTASK := PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_MAKE(NRN => REC.RN,
|
|
||||||
SCAPTION => STASK_NAME,
|
|
||||||
SNAME => STASK_NAME,
|
|
||||||
NLINE_NUMB => NLINE_NUMB,
|
|
||||||
NSTART => REC.POS_START,
|
|
||||||
NEND => REC.POS_END,
|
|
||||||
SBG_COLOR => SBG_TASK_COLOR_WO_GRP,
|
|
||||||
STEXT_COLOR => STEXT_COLOR_TASK_WO_GRP);
|
|
||||||
/* Добавляем атрибуты */
|
|
||||||
PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG,
|
|
||||||
RTASK => RTASK,
|
|
||||||
SNAME => 'ddate_start',
|
|
||||||
SVALUE => PKG_XCONVERT.DATE_TO_XML(DVALUE => REC.DATE_FROM),
|
|
||||||
BCLEAR => true);
|
|
||||||
PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG,
|
|
||||||
RTASK => RTASK,
|
|
||||||
SNAME => 'ddate_end',
|
|
||||||
SVALUE => PKG_XCONVERT.DATE_TO_XML(DVALUE => REC.DATE_TO));
|
|
||||||
PKG_P8PANELS_VISUAL.TCYCLOGRAM_TASK_ADD_ATTR_VAL(RCYCLOGRAM => CG, RTASK => RTASK, SNAME => 'type', SVALUE => 2);
|
|
||||||
/* Добавляем задачу в циклограмму */
|
|
||||||
PKG_P8PANELS_VISUAL.TCYCLOGRAM_ADD_TASK(RCYCLOGRAM => CG, RTASK => RTASK);
|
|
||||||
end loop;
|
|
||||||
/* Формируем список */
|
|
||||||
COUT := PKG_P8PANELS_VISUAL.TCYCLOGRAM_TO_XML(RCYCLOGRAM => CG);
|
|
||||||
end CYCLOGRAM;
|
|
||||||
|
|
||||||
/* Изменение задачи циклограммы */
|
|
||||||
procedure CYCLOGRAM_TASK_MODIFY
|
|
||||||
(
|
|
||||||
NIDENT in number, -- Идентификатор буфера
|
|
||||||
NRN in number, -- Рег. номер записи
|
|
||||||
SDATE_FROM in varchar2, -- Дата начала (в строковом представлении)
|
|
||||||
SDATE_TO in varchar2 -- Дата окончания (в строковом представлении)
|
|
||||||
)
|
|
||||||
is
|
|
||||||
DDATE_FROM PKG_STD.TLDATE; -- Дата начала
|
|
||||||
DDATE_TO PKG_STD.TLDATE; -- Дата окончания
|
|
||||||
NYEAR PKG_STD.TNUMBER; -- Текущий год
|
|
||||||
DCYCLOGRAM_START PKG_STD.TLDATE; -- Дата начала циклограммы
|
|
||||||
NSTART PKG_STD.TNUMBER; -- Позиция начала элемента
|
|
||||||
NEND PKG_STD.TNUMBER; -- Позиция окончания элемента
|
|
||||||
begin
|
|
||||||
/* Фиксируем текущий год */
|
|
||||||
NYEAR := EXTRACT(year from sysdate);
|
|
||||||
/* Переводим даты */
|
|
||||||
DDATE_FROM := TO_DATE(SDATE_FROM, 'dd.mm.yyyy');
|
|
||||||
DDATE_TO := TO_DATE(SDATE_TO, 'dd.mm.yyyy');
|
|
||||||
/* Если дата начала выходит за границы года */
|
|
||||||
if (D_YEAR(DDATE => DDATE_FROM) <> NYEAR) then
|
|
||||||
P_EXCEPTION(0,
|
|
||||||
'Дата начала задачи выходит за границы текущего года (%s).',
|
|
||||||
NYEAR);
|
|
||||||
end if;
|
|
||||||
/* Если дата окончания выходит за границы года */
|
|
||||||
if (D_YEAR(DDATE => DDATE_TO) <> NYEAR) then
|
|
||||||
P_EXCEPTION(0,
|
|
||||||
'Дата окончания задачи выходит за границы текущего года (%s).',
|
|
||||||
NYEAR);
|
|
||||||
end if;
|
|
||||||
/* Дата окончания не может быть меньше даты начала */
|
|
||||||
if (DDATE_TO < DDATE_FROM) then
|
|
||||||
P_EXCEPTION(0, 'Дата окончания не может быть меньше даты начала.');
|
|
||||||
end if;
|
|
||||||
/* Фиксируем дату начала циклограммы */
|
|
||||||
DCYCLOGRAM_START := TO_DATE('01.01.' || NYEAR, 'dd.mm.yyyy');
|
|
||||||
/* Рассчитываем новую позицию начала */
|
|
||||||
NSTART := (DDATE_FROM - DCYCLOGRAM_START) * NCG_MULTIPLIER;
|
|
||||||
/* Рассчитываем новую позицию окончания */
|
|
||||||
NEND := NSTART + ((DDATE_TO - DDATE_FROM + 1) * NCG_MULTIPLIER);
|
|
||||||
/* Считываем запись */
|
|
||||||
for REC in (select T.*
|
|
||||||
from P8PNL_SMPL_CYCLOGRAM T
|
|
||||||
where T.RN = NRN
|
|
||||||
and T.IDENT = NIDENT)
|
|
||||||
loop
|
|
||||||
/* Обновляем запись циклограммы */
|
|
||||||
CYCLOGRAM_BASE_UPDATE(NIDENT => REC.IDENT,
|
|
||||||
NRN => REC.RN,
|
|
||||||
NTYPE => REC.TYPE,
|
|
||||||
SNAME => REC.NAME,
|
|
||||||
NPOS_START => NSTART,
|
|
||||||
NPOS_END => NEND,
|
|
||||||
DDATE_FROM => DDATE_FROM,
|
|
||||||
DDATE_TO => DDATE_TO,
|
|
||||||
NTASK_GROUP => REC.TASK_GROUP);
|
|
||||||
end loop;
|
|
||||||
end CYCLOGRAM_TASK_MODIFY;
|
|
||||||
|
|
||||||
end PKG_P8PANELS_SAMPLES;
|
end PKG_P8PANELS_SAMPLES;
|
||||||
/
|
/
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
BIN
dist/app/panels/clnt_task_board/components/default_avatar.d169f535d2a783bef4eb3e98da3ed6c1.png
vendored
Normal file
BIN
dist/app/panels/clnt_task_board/components/default_avatar.d169f535d2a783bef4eb3e98da3ed6c1.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.0 KiB |
BIN
dist/app/panels/clnt_task_board/default_avatar.d169f535d2a783bef4eb3e98da3ed6c1.png
vendored
Normal file
BIN
dist/app/panels/clnt_task_board/default_avatar.d169f535d2a783bef4eb3e98da3ed6c1.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.0 KiB |
BIN
dist/app/panels/clnt_task_board/img/default_avatar.d169f535d2a783bef4eb3e98da3ed6c1.png
vendored
Normal file
BIN
dist/app/panels/clnt_task_board/img/default_avatar.d169f535d2a783bef4eb3e98da3ed6c1.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.0 KiB |
BIN
dist/img/default_avatar.d169f535d2a783bef4eb3e98da3ed6c1.png
vendored
Normal file
BIN
dist/img/default_avatar.d169f535d2a783bef4eb3e98da3ed6c1.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.0 KiB |
102
dist/p8-panels.js
vendored
102
dist/p8-panels.js
vendored
File diff suppressed because one or more lines are too long
BIN
docs/img/72.png
BIN
docs/img/72.png
Binary file not shown.
|
Before Width: | Height: | Size: 44 KiB |
BIN
docs/img/73.png
BIN
docs/img/73.png
Binary file not shown.
|
Before Width: | Height: | Size: 58 KiB |
@ -16,7 +16,7 @@
|
|||||||
<script src="./libs/frappe-gantt/frappe-gantt.js"></script>
|
<script src="./libs/frappe-gantt/frappe-gantt.js"></script>
|
||||||
<link rel="stylesheet" href="./libs/frappe-gantt/frappe-gantt.css" />
|
<link rel="stylesheet" href="./libs/frappe-gantt/frappe-gantt.css" />
|
||||||
</head>
|
</head>
|
||||||
<body style="display: block; margin: 0px" class="scroll">
|
<body style="display: block; margin: 0px">
|
||||||
<div id="app-content"></div>
|
<div id="app-content"></div>
|
||||||
<script src="dist/p8-panels.js"></script>
|
<script src="dist/p8-panels.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user