ЦИТК-899 - рефакторинг панели, управление сортировкой строк/граф
This commit is contained in:
parent
6958cfd904
commit
077582bd4c
@ -1,358 +0,0 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга - РО - Редактор настройки регламентированного отчёта
|
||||
Панель мониторинга: Диалог добавления/исправления/удаления компонентов настройки регламентированного отчёта
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import React, { useState, useContext, useCallback, useEffect } from "react"; //Классы React
|
||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||
import { Dialog, DialogTitle, IconButton, Icon, DialogContent, Typography, DialogActions, Button } from "@mui/material"; //Интерфейсные компоненты
|
||||
import { ApplicationСtx } from "../../../context/application"; //Контекст приложения
|
||||
import { BackEndСtx } from "../../../context/backend"; //Контекст взаимодействия с сервером
|
||||
import { IUDFormTextField } from "./iud_form_text_field"; //Компонент поля ввода
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Стили
|
||||
const STYLES = {
|
||||
CLOSE_BUTTON: {
|
||||
position: "absolute",
|
||||
right: 8,
|
||||
top: 8,
|
||||
color: theme => theme.palette.grey[500]
|
||||
},
|
||||
PADDING_DIALOG_BUTTONS_RIGHT: { paddingRight: "32px" }
|
||||
};
|
||||
|
||||
//Статусы диалогового окна
|
||||
export const STATUSES = { CREATE: 0, EDIT: 1, DELETE: 2, RRPCONFSCTNMRK_CREATE: 3, RRPCONFSCTNMRK_EDIT: 4, RRPCONFSCTNMRK_DELETE: 5 };
|
||||
|
||||
//---------------
|
||||
//Тело компонента
|
||||
//---------------
|
||||
|
||||
const IUDFormDialog = ({ initial, onClose, onReload }) => {
|
||||
//Собственное состояние
|
||||
const [formData, setFormData] = useState({ ...initial });
|
||||
|
||||
//Подключение к контексту приложения
|
||||
const { pOnlineShowDictionary } = useContext(ApplicationСtx);
|
||||
|
||||
//Подключение к контексту взаимодействия с сервером
|
||||
const { executeStored } = useContext(BackEndСtx);
|
||||
|
||||
//При закрытии диалога без изменений
|
||||
const handleCancel = () => (onClose ? onClose() : null);
|
||||
|
||||
//При закрытии диалога с изменениями
|
||||
const handleOK = () => {
|
||||
if (onClose) {
|
||||
changeSections();
|
||||
onClose();
|
||||
} else null;
|
||||
};
|
||||
|
||||
//Отработка добавления/изсправления/удаления элемента
|
||||
const handleReload = () => {
|
||||
if (onReload) {
|
||||
onReload();
|
||||
} else null;
|
||||
};
|
||||
|
||||
//При изменении значения элемента
|
||||
const handleDialogItemChange = (item, value) => {
|
||||
setFormData(pv => ({ ...pv, [item]: value }));
|
||||
};
|
||||
|
||||
//Отработка изменений в разделе или показателе раздела
|
||||
const changeSections = useCallback(async () => {
|
||||
switch (formData.status) {
|
||||
case STATUSES.CREATE:
|
||||
await insertSections();
|
||||
break;
|
||||
case STATUSES.EDIT:
|
||||
await updateSections();
|
||||
break;
|
||||
case STATUSES.DELETE:
|
||||
await deleteSections();
|
||||
break;
|
||||
case STATUSES.RRPCONFSCTNMRK_CREATE:
|
||||
await addRRPCONFSCTNMRK();
|
||||
break;
|
||||
case STATUSES.RRPCONFSCTNMRK_EDIT:
|
||||
await editRRPCONFSCTNMRK();
|
||||
break;
|
||||
case STATUSES.RRPCONFSCTNMRK_DELETE:
|
||||
await deleteRRPCONFSCTNMRK();
|
||||
break;
|
||||
}
|
||||
handleReload();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [formData]);
|
||||
|
||||
//Добавление раздела
|
||||
const insertSections = useCallback(async () => {
|
||||
const data = await executeStored({
|
||||
stored: "PKG_P8PANELS_RRPCONFED.RRPCONFSCTN_INSERT",
|
||||
args: {
|
||||
NPRN: formData.prn,
|
||||
SCODE: formData.code,
|
||||
SNAME: formData.name
|
||||
}
|
||||
});
|
||||
setFormData(pv => ({
|
||||
...pv,
|
||||
rn: Number(data.NRN)
|
||||
}));
|
||||
}, [formData.prn, formData.code, formData.name, executeStored]);
|
||||
|
||||
//Исправление раздела
|
||||
const updateSections = useCallback(async () => {
|
||||
await executeStored({
|
||||
stored: "PKG_P8PANELS_RRPCONFED.RRPCONFSCTN_UPDATE",
|
||||
args: {
|
||||
NRN: formData.rn,
|
||||
SCODE: formData.code,
|
||||
SNAME: formData.name
|
||||
}
|
||||
});
|
||||
}, [formData.name, formData.code, formData.rn, executeStored]);
|
||||
|
||||
//Удаление раздела
|
||||
const deleteSections = useCallback(async () => {
|
||||
await executeStored({
|
||||
stored: "PKG_P8PANELS_RRPCONFED.RRPCONFSCTN_DELETE",
|
||||
args: {
|
||||
NRN: formData.rn
|
||||
}
|
||||
});
|
||||
}, [formData.rn, executeStored]);
|
||||
|
||||
//Добавление показателя раздела
|
||||
const addRRPCONFSCTNMRK = useCallback(async () => {
|
||||
await executeStored({
|
||||
stored: "PKG_P8PANELS_RRPCONFED.RRPCONFSCTNMRK_INSERT",
|
||||
args: {
|
||||
NPRN: formData.prn,
|
||||
SCODE: formData.code,
|
||||
SNAME: formData.name,
|
||||
NRRPROW: formData.rowRn,
|
||||
NRRPCOLUMN: formData.colRn
|
||||
}
|
||||
});
|
||||
}, [executeStored, formData.code, formData.colRn, formData.name, formData.prn, formData.rowRn]);
|
||||
|
||||
//Исправление показателя раздела
|
||||
const editRRPCONFSCTNMRK = useCallback(async () => {
|
||||
await executeStored({
|
||||
stored: "PKG_P8PANELS_RRPCONFED.RRPCONFSCTNMRK_UPDATE",
|
||||
args: { NRN: formData.rn, SNAME: formData.name }
|
||||
});
|
||||
}, [executeStored, formData.name, formData.rn]);
|
||||
|
||||
//Удаление показателя раздела
|
||||
const deleteRRPCONFSCTNMRK = useCallback(async () => {
|
||||
await executeStored({ stored: "PKG_P8PANELS_RRPCONFED.RRPCONFSCTNMRK_DELETE", args: { NRN: formData.rn } });
|
||||
}, [executeStored, formData.rn]);
|
||||
|
||||
//Формирование заголовка диалогового окна
|
||||
const formTitle = () => {
|
||||
switch (formData.status) {
|
||||
case STATUSES.CREATE:
|
||||
return "Добавление раздела";
|
||||
case STATUSES.EDIT:
|
||||
return "Исправление раздела";
|
||||
case STATUSES.DELETE:
|
||||
return "Удаление раздела";
|
||||
case STATUSES.RRPCONFSCTNMRK_CREATE:
|
||||
return "Добавление показателя раздела";
|
||||
case STATUSES.RRPCONFSCTNMRK_EDIT:
|
||||
return "Исправление показателя раздела";
|
||||
case STATUSES.RRPCONFSCTNMRK_DELETE:
|
||||
return "Удаление показателя раздела";
|
||||
}
|
||||
};
|
||||
|
||||
//Отрисовка диалогового окна
|
||||
const renderSwitch = () => {
|
||||
let btnText = "";
|
||||
switch (formData.status) {
|
||||
case STATUSES.CREATE:
|
||||
case STATUSES.RRPCONFSCTNMRK_CREATE:
|
||||
btnText = "Добавить";
|
||||
break;
|
||||
case STATUSES.EDIT:
|
||||
case STATUSES.RRPCONFSCTNMRK_EDIT:
|
||||
btnText = "Исправить";
|
||||
break;
|
||||
case STATUSES.DELETE:
|
||||
case STATUSES.RRPCONFSCTNMRK_DELETE:
|
||||
btnText = "Удалить";
|
||||
break;
|
||||
}
|
||||
return (
|
||||
<Button
|
||||
onClick={() => {
|
||||
handleOK({ formData });
|
||||
}}
|
||||
>
|
||||
{btnText}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
//Выбор строки
|
||||
const selectRow = (showDictionary, callBack) => {
|
||||
showDictionary({
|
||||
unitCode: "RRPRow",
|
||||
inputParameters: [{ name: "in_RN", value: formData.rowRn }],
|
||||
callBack: res => {
|
||||
if (res.success === true) {
|
||||
callBack(res.outParameters.out_CODE, res.outParameters.out_RN);
|
||||
setFormData(pv => ({
|
||||
...pv,
|
||||
reload: true,
|
||||
rowCode: res.outParameters.out_CODE,
|
||||
rowRn: res.outParameters.out_RN
|
||||
}));
|
||||
} else callBack(null);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
//Выбор графы
|
||||
const selectColumn = (showDictionary, callBack) => {
|
||||
showDictionary({
|
||||
unitCode: "RRPColumn",
|
||||
inputParameters: [{ name: "in_RN", value: formData.colRn }],
|
||||
callBack: res => {
|
||||
if (res.success === true) {
|
||||
callBack(res.outParameters.out_CODE, res.outParameters.out_RN);
|
||||
setFormData(pv => ({
|
||||
...pv,
|
||||
reload: true,
|
||||
colCode: res.outParameters.out_CODE,
|
||||
colRn: res.outParameters.out_RN
|
||||
}));
|
||||
} else callBack(null);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
//Получение мнемокода и наименования показателя раздела
|
||||
const getSctnMrkCodeName = useCallback(async () => {
|
||||
const data = await executeStored({
|
||||
stored: "PKG_P8PANELS_RRPCONFED.RRPCONFSCTNMRK_GET_CODE_NAME",
|
||||
args: {
|
||||
NRRPCONFSCTN: formData.prn,
|
||||
NRRPROW: formData.rowRn,
|
||||
NRRPCOLUMN: formData.colRn
|
||||
}
|
||||
});
|
||||
setFormData(pv => ({
|
||||
...pv,
|
||||
reload: false,
|
||||
code: data.SCODE,
|
||||
name: data.SNAME
|
||||
}));
|
||||
}, [executeStored, formData.colRn, formData.prn, formData.rowRn]);
|
||||
|
||||
//Считывание наименования показателя
|
||||
const getMarkName = useCallback(async () => {
|
||||
const data = await executeStored({
|
||||
stored: "PKG_P8PANELS_RRPCONFED.RRPCONFSCTNMRK_GET_NAME",
|
||||
args: {
|
||||
NRRPCONFSCTNMRK: formData.rn
|
||||
}
|
||||
});
|
||||
setFormData(pv => ({
|
||||
...pv,
|
||||
reload: false,
|
||||
name: data.SNAME
|
||||
}));
|
||||
}, [executeStored, formData.rn]);
|
||||
|
||||
//Получение наименования и мнемокода показателя раздела при заполнении необходимых полей
|
||||
useEffect(() => {
|
||||
//Если это добавление показателя и требуется сформировать мнемокод и наименование
|
||||
formData.status == STATUSES.RRPCONFSCTNMRK_CREATE && formData.reload && formData.rowRn && formData.colRn ? getSctnMrkCodeName() : null;
|
||||
//Если это исправление и требуется инициализировать наименование показателя
|
||||
formData.status == STATUSES.RRPCONFSCTNMRK_EDIT && formData.reload ? getMarkName() : null;
|
||||
}, [formData.status, formData.reload, formData.rowRn, formData.colRn, getSctnMrkCodeName, getMarkName]);
|
||||
|
||||
//Генерация содержимого
|
||||
return (
|
||||
<Dialog open onClose={handleCancel}>
|
||||
<DialogTitle>{formTitle()}</DialogTitle>
|
||||
<IconButton aria-label="close" onClick={handleCancel} sx={STYLES.CLOSE_BUTTON}>
|
||||
<Icon>close</Icon>
|
||||
</IconButton>
|
||||
<DialogContent>
|
||||
{formData.status == STATUSES.DELETE || formData.status == STATUSES.RRPCONFSCTNMRK_DELETE ? (
|
||||
formData.status == STATUSES.DELETE ? (
|
||||
<Typography>Вы хотите удалить раздел {formData.name}?</Typography>
|
||||
) : (
|
||||
<Typography>Вы хотите удалить показатель раздела {formData.name}?</Typography>
|
||||
)
|
||||
) : (
|
||||
<div>
|
||||
{formData.status != STATUSES.RRPCONFSCTNMRK_EDIT ? (
|
||||
<IUDFormTextField
|
||||
elementCode="code"
|
||||
elementValue={formData.code}
|
||||
labelText="Мнемокод"
|
||||
onChange={handleDialogItemChange}
|
||||
/>
|
||||
) : null}
|
||||
<IUDFormTextField
|
||||
elementCode="name"
|
||||
elementValue={formData.name}
|
||||
labelText="Наименование"
|
||||
onChange={handleDialogItemChange}
|
||||
/>
|
||||
{formData.status == STATUSES.RRPCONFSCTNMRK_CREATE ? (
|
||||
<div>
|
||||
<IUDFormTextField
|
||||
elementCode="row"
|
||||
elementValue={formData.rowCode}
|
||||
labelText="Строка"
|
||||
onChange={handleDialogItemChange}
|
||||
dictionary={callBack => selectRow(pOnlineShowDictionary, callBack)}
|
||||
/>
|
||||
<IUDFormTextField
|
||||
elementCode="column"
|
||||
elementValue={formData.colCode}
|
||||
labelText="Графа"
|
||||
onChange={handleDialogItemChange}
|
||||
dictionary={callBack => selectColumn(pOnlineShowDictionary, callBack)}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
</DialogContent>
|
||||
<DialogActions sx={STYLES.PADDING_DIALOG_BUTTONS_RIGHT}>
|
||||
{renderSwitch()}
|
||||
<Button onClick={handleCancel}>Отмена</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Диалог
|
||||
IUDFormDialog.propTypes = {
|
||||
initial: PropTypes.object.isRequired,
|
||||
onClose: PropTypes.func,
|
||||
onReload: PropTypes.func
|
||||
};
|
||||
|
||||
//--------------------
|
||||
//Интерфейс компонента
|
||||
//--------------------
|
||||
|
||||
export { IUDFormDialog };
|
@ -1,87 +0,0 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга - РО - Редактор настройки регламентированного отчёта
|
||||
Панель мониторинга: Компонент поля ввода
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import React, { useState, useEffect } from "react"; //Классы React
|
||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||
import { Box, FormControl, InputLabel, Input, InputAdornment, IconButton, Icon } from "@mui/material"; //Интерфейсные компоненты
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Стили
|
||||
const STYLES = {
|
||||
DIALOG_WINDOW_WIDTH: { width: 400 }
|
||||
};
|
||||
|
||||
//---------------
|
||||
//Тело компонента
|
||||
//---------------
|
||||
|
||||
//Поле ввода
|
||||
const IUDFormTextField = ({ elementCode, elementValue, labelText, onChange, 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);
|
||||
};
|
||||
|
||||
//Генерация содержимого
|
||||
return (
|
||||
<Box p={1}>
|
||||
<FormControl variant="standard" sx={STYLES.DIALOG_WINDOW_WIDTH} {...other}>
|
||||
<InputLabel htmlFor={elementCode}>{labelText}</InputLabel>
|
||||
<Input
|
||||
id={elementCode}
|
||||
name={elementCode}
|
||||
value={value ? value : ""}
|
||||
endAdornment={
|
||||
dictionary ? (
|
||||
<InputAdornment position="end">
|
||||
<IconButton aria-label={`${elementCode} select`} onClick={handleDictionaryClick} edge="end">
|
||||
<Icon>list</Icon>
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
) : null
|
||||
}
|
||||
onChange={handleChange}
|
||||
multiline
|
||||
maxRows={4}
|
||||
/>
|
||||
</FormControl>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Поле ввода
|
||||
IUDFormTextField.propTypes = {
|
||||
elementCode: PropTypes.string.isRequired,
|
||||
elementValue: PropTypes.string,
|
||||
labelText: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func,
|
||||
dictionary: PropTypes.func
|
||||
};
|
||||
|
||||
//--------------------
|
||||
//Интерфейс компонента
|
||||
//--------------------
|
||||
|
||||
export { IUDFormTextField };
|
43
app/panels/rrp_conf_editor/common.js
Normal file
43
app/panels/rrp_conf_editor/common.js
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга - РО - Редактор настройки регламентированного отчёта
|
||||
Обще стили и константы
|
||||
*/
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Допустимые варианты сортировки строк/граф
|
||||
const COL_ROW_ORDER = [
|
||||
{ name: "Номер", value: 0 },
|
||||
{ name: "Код", value: 1 },
|
||||
{ name: "Мнемокод", value: 2 }
|
||||
];
|
||||
|
||||
//Типовые цвета
|
||||
const BG_GRAY = "#e3e3e3";
|
||||
const BG_BLUE = "#1976d210";
|
||||
|
||||
//Типовые размеры шрифтов
|
||||
const FONT_SIZE_LARGE = "0.85rem";
|
||||
const FONT_SIZE_SMALL = "0.75rem";
|
||||
|
||||
//Стили
|
||||
const STYLES = {
|
||||
TOOLBAR: { position: "absolute", right: 0, backgroundColor: BG_BLUE },
|
||||
LINK: { component: "button", cursor: "pointer", width: "-webkit-fill-available" },
|
||||
FONT_DATA_GRID: { fontSize: FONT_SIZE_SMALL },
|
||||
FONT_MARK_CARD_HEAD: { fontSize: FONT_SIZE_LARGE },
|
||||
FONT_MARK_CARD_BODY: { fontSize: FONT_SIZE_SMALL },
|
||||
BG_DATA_GRID_HEAD_CELL: { backgroundColor: BG_GRAY },
|
||||
BG_DATA_GRID_DATA_CELL: { backgroundColor: BG_GRAY },
|
||||
BG_MARK_CARD: { backgroundColor: BG_BLUE },
|
||||
BORDER_DATA_GRID_HEAD_CELL: { border: "1px solid white" },
|
||||
BORDER_DATA_GRID_DATA_CELL: { borderBottom: `1px solid ${BG_GRAY}` }
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { COL_ROW_ORDER, STYLES };
|
59
app/panels/rrp_conf_editor/components/action_message.js
Normal file
59
app/panels/rrp_conf_editor/components/action_message.js
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга - РО - Редактор настройки регламентированного отчёта
|
||||
Компонент панели: Сообщение с действиями
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import React from "react"; //Классы React
|
||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||
import { Icon, Stack, Typography } from "@mui/material"; //Интерфейсные элементы
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Стили
|
||||
const STYLES = {
|
||||
CONTAINER: { height: "100%", width: "100%" }
|
||||
};
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Сообщение с действиями
|
||||
const ActionMessage = ({ icon, title, desc, children }) => {
|
||||
return (
|
||||
<Stack direction={"column"} justifyContent={"center"} sx={STYLES.CONTAINER}>
|
||||
<Stack direction={"row"} justifyContent={"center"} alignItems={"center"}>
|
||||
<Icon color={"disabled"}>{icon}</Icon>
|
||||
<Typography align={"center"} color={"text.secondary"} variant={"button"}>
|
||||
{title}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Typography align={"center"} variant={"caption"}>
|
||||
{desc}
|
||||
</Typography>
|
||||
<Stack direction={"row"} justifyContent={"center"} alignItems={"center"}>
|
||||
{children}
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Сообщение с действиями
|
||||
ActionMessage.propTypes = {
|
||||
icon: PropTypes.string.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
desc: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired,
|
||||
children: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf(PropTypes.node)])
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { ActionMessage };
|
87
app/panels/rrp_conf_editor/components/dialog_help.js
Normal file
87
app/panels/rrp_conf_editor/components/dialog_help.js
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга - РО - Редактор настройки регламентированного отчёта
|
||||
Компонент панели: Диалог дополнительной информации
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import React from "react"; //Классы React
|
||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||
import { Typography, List, ListItem } from "@mui/material"; //Интерфейсные элементы
|
||||
import { Form } from "./form"; //Типовая форма
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Стили
|
||||
const STYLES = {
|
||||
HELP_LIST_ITEM: { padding: "0px 0px 0px 5px", whiteSpace: "pre" },
|
||||
HELP_LIST_ITEM_NAME: { fontWeight: "bold", minWidth: "45px" }
|
||||
};
|
||||
|
||||
//------------------------------------
|
||||
//Вспомогательные функции и компоненты
|
||||
//------------------------------------
|
||||
|
||||
//Элемент списка расшифровки состава
|
||||
const HelpListItem = ({ name, desc }) => {
|
||||
return (
|
||||
<ListItem sx={STYLES.HELP_LIST_ITEM}>
|
||||
<Typography sx={STYLES.HELP_LIST_ITEM_NAME}>{name}</Typography>
|
||||
<Typography>{` - ${desc}`}</Typography>
|
||||
</ListItem>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Элемент списка расшифровки состава
|
||||
HelpListItem.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
desc: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Диалог дополнительной информации
|
||||
const DialogHelp = ({ onClose }) => {
|
||||
//При закрытии диалога
|
||||
const handleClose = () => onClose && onClose();
|
||||
|
||||
//Генерация содержимого
|
||||
return (
|
||||
<Form title={"Информация"} onClose={handleClose}>
|
||||
<Typography>Карточки показателей содержат сокращенную информацию о типе состава показателя. Список сокращений:</Typography>
|
||||
<List disablePadding={true}>
|
||||
<HelpListItem name={"fx"} desc={"формула"} />
|
||||
<HelpListItem name={"СЗ"} desc={"статическое значение"} />
|
||||
<HelpListItem name={"ХП"} desc={"хранимая процедура"} />
|
||||
<HelpListItem name={"РП"} desc={"расчетный показатель"} />
|
||||
<HelpListItem name={"ХО"} desc={"хозяйственные операции"} />
|
||||
<HelpListItem name={"РСДК"} desc={"расчёты с дебиторами/кредиторами"} />
|
||||
<HelpListItem name={"ОС"} desc={"остатки средств по счетам"} />
|
||||
<HelpListItem name={"ТМЦ"} desc={"остатки товарно-материальных ценностей"} />
|
||||
<HelpListItem name={"ДКЗ"} desc={"дебиторская/кредиторская задолженность"} />
|
||||
<HelpListItem name={"ИК"} desc={"инвентарная картотека"} />
|
||||
<HelpListItem name={"МБП"} desc={"картотека МБП"} />
|
||||
<HelpListItem name={"КОБП"} desc={"картотека операций будущих периодов"} />
|
||||
<HelpListItem name={"ДПНП"} desc={"декларация по налогу на прибыль"} />
|
||||
<HelpListItem name={"РО"} desc={"регламентированный отчет"} />
|
||||
</List>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Диалог дополнительной информации
|
||||
DialogHelp.propTypes = {
|
||||
onClose: PropTypes.func
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { DialogHelp };
|
103
app/panels/rrp_conf_editor/components/dialog_mark_iu.js
Normal file
103
app/panels/rrp_conf_editor/components/dialog_mark_iu.js
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга - РО - Редактор настройки регламентированного отчёта
|
||||
Компонент панели: Диалог добавления/исправления показателя
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import React from "react"; //Классы React
|
||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||
import { useDictionary } from "../hooks"; //Кастомные хуки
|
||||
import { Form } from "./form"; //Типовая форма
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Диалог добавления/исправления показателя
|
||||
const DialogMarkIU = ({
|
||||
code = "",
|
||||
name = "",
|
||||
rowCode = "",
|
||||
rowVersion = "",
|
||||
columnCode = "",
|
||||
columnVersion = "",
|
||||
insert = true,
|
||||
onOk,
|
||||
onCancel
|
||||
}) => {
|
||||
//Нажатие на кнопку "Ok"
|
||||
const handleOk = values => onOk && onOk({ ...values });
|
||||
|
||||
//Нажатие на кнопку "Отмена"
|
||||
const handleCancel = () => onCancel && onCancel();
|
||||
|
||||
//Хуки для работы со словарями
|
||||
const { selectRRPRow, selectRRPColumn } = useDictionary();
|
||||
|
||||
//Выбор строки из словаря
|
||||
const selectRow = (currentFormValues, setFormValues) => {
|
||||
selectRRPRow(
|
||||
currentFormValues.rowCode,
|
||||
currentFormValues.rowVersion,
|
||||
selectResult =>
|
||||
selectResult &&
|
||||
setFormValues([
|
||||
{ name: "rowCode", value: selectResult.code },
|
||||
{ name: "rowVersion", value: selectResult.version }
|
||||
])
|
||||
);
|
||||
};
|
||||
|
||||
//Выбор графы из словаря
|
||||
const selectColumn = (currentFormValues, setFormValues) => {
|
||||
selectRRPColumn(
|
||||
currentFormValues.columnCode,
|
||||
currentFormValues.columnVersion,
|
||||
selectResult =>
|
||||
selectResult &&
|
||||
setFormValues([
|
||||
{ name: "columnCode", value: selectResult.code },
|
||||
{ name: "columnVersion", value: selectResult.version }
|
||||
])
|
||||
);
|
||||
};
|
||||
|
||||
//Генерация содержимого
|
||||
return (
|
||||
<Form
|
||||
title={`${insert === true ? "Добавление" : "Исправление"} показателя`}
|
||||
fields={[
|
||||
{ elementCode: "code", elementValue: code, labelText: "Мнемокод" },
|
||||
{ elementCode: "name", elementValue: name, labelText: "Наименование" },
|
||||
{ elementCode: "rowCode", elementValue: rowCode, labelText: "Строка", dictionary: selectRow },
|
||||
{ elementCode: "rowVersion", elementValue: rowVersion, labelText: "Редакция строки", disabled: true },
|
||||
{ elementCode: "columnCode", elementValue: columnCode, labelText: "Графа", dictionary: selectColumn },
|
||||
{ elementCode: "columnVersion", elementValue: columnVersion, labelText: "Редакция графы", disabled: true }
|
||||
]}
|
||||
onOk={handleOk}
|
||||
onCancel={handleCancel}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Диалог добавления/исправления показателя
|
||||
DialogMarkIU.propTypes = {
|
||||
code: PropTypes.string,
|
||||
name: PropTypes.string,
|
||||
rowCode: PropTypes.string,
|
||||
rowVersion: PropTypes.string,
|
||||
columnCode: PropTypes.string,
|
||||
columnVersion: PropTypes.string,
|
||||
insert: PropTypes.bool,
|
||||
onOk: PropTypes.func,
|
||||
onCancel: PropTypes.func
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { DialogMarkIU };
|
53
app/panels/rrp_conf_editor/components/dialog_order.js
Normal file
53
app/panels/rrp_conf_editor/components/dialog_order.js
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга - РО - Редактор настройки регламентированного отчёта
|
||||
Компонент панели: Диалог сортировки
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import React from "react"; //Классы React
|
||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||
import { Form } from "./form"; //Типовая форма
|
||||
import { COL_ROW_ORDER } from "../common"; //Обще стили и константы
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Диалог сортировки
|
||||
const DialogOrder = ({ rowOrder = 0, columnOrder = 0, onOk, onCancel }) => {
|
||||
//Нажатие на кнопку "Ok"
|
||||
const handleOk = values => onOk && onOk({ ...values });
|
||||
|
||||
//Нажатие на кнопку "Отмена"
|
||||
const handleCancel = () => onCancel && onCancel();
|
||||
|
||||
//Генерация содержимого
|
||||
return (
|
||||
<Form
|
||||
title={"Сортировка"}
|
||||
fields={[
|
||||
{ elementCode: "rowOrder", elementValue: rowOrder, labelText: "Строки", list: COL_ROW_ORDER },
|
||||
{ elementCode: "columnOrder", elementValue: columnOrder, labelText: "Графы", list: COL_ROW_ORDER }
|
||||
]}
|
||||
onOk={handleOk}
|
||||
onCancel={handleCancel}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Диалог сортировки
|
||||
DialogOrder.propTypes = {
|
||||
rowOrder: PropTypes.number,
|
||||
columnOrder: PropTypes.number,
|
||||
onOk: PropTypes.func,
|
||||
onCancel: PropTypes.func
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { DialogOrder };
|
53
app/panels/rrp_conf_editor/components/dialog_section_iu.js
Normal file
53
app/panels/rrp_conf_editor/components/dialog_section_iu.js
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга - РО - Редактор настройки регламентированного отчёта
|
||||
Компонент панели: Диалог добавления/исправления раздела
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import React from "react"; //Классы React
|
||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||
import { Form } from "./form"; //Типовая форма
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Диалог добавления/исправления раздела
|
||||
const DialogSectionIU = ({ code = "", name = "", insert = true, onOk, onCancel }) => {
|
||||
//Нажатие на кнопку "Ok"
|
||||
const handleOk = values => onOk && onOk({ ...values });
|
||||
|
||||
//Нажатие на кнопку "Отмена"
|
||||
const handleCancel = () => onCancel && onCancel();
|
||||
|
||||
//Генерация содержимого
|
||||
return (
|
||||
<Form
|
||||
title={`${insert === true ? "Добавление" : "Исправление"} раздела`}
|
||||
fields={[
|
||||
{ elementCode: "code", elementValue: code, labelText: "Мнемокод" },
|
||||
{ elementCode: "name", elementValue: name, labelText: "Наименование" }
|
||||
]}
|
||||
onOk={handleOk}
|
||||
onCancel={handleCancel}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Диалог добавления/исправления раздела
|
||||
DialogSectionIU.propTypes = {
|
||||
code: PropTypes.string,
|
||||
name: PropTypes.string,
|
||||
insert: PropTypes.bool,
|
||||
onOk: PropTypes.func,
|
||||
onCancel: PropTypes.func
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { DialogSectionIU };
|
76
app/panels/rrp_conf_editor/components/form.js
Normal file
76
app/panels/rrp_conf_editor/components/form.js
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга - РО - Редактор настройки регламентированного отчёта
|
||||
Компонент панели: Форма
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import React, { useEffect, useState } from "react"; //Классы React
|
||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||
import { Dialog, DialogTitle, DialogContent, DialogActions, Button } from "@mui/material"; //Интерфейсные компоненты
|
||||
import { BUTTONS } from "../../../../app.text"; //Общие текстовые ресурсы
|
||||
import { FORM_FILED, FormField } from "./form_field"; //Элемент формы
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Форма
|
||||
const Form = ({ title, fields = [], children, onOk, onCancel, onClose }) => {
|
||||
//Состояние формы
|
||||
const [state, setState] = useState({});
|
||||
|
||||
//При изменении элемента формы
|
||||
const handleFieldChange = (name, value) => setState(pv => ({ ...pv, [name]: value }));
|
||||
|
||||
//При нажатии на "ОК" формы
|
||||
const handleOk = () => onOk && onOk(state);
|
||||
|
||||
//При нажатии на "Отмена" формы
|
||||
const handleCancel = () => onCancel && onCancel();
|
||||
|
||||
//При нажатии на "Закрыть" формы
|
||||
const handleClose = () => (onClose ? onClose() : onCancel ? onCancel() : null);
|
||||
|
||||
//При подключении к старнице
|
||||
useEffect(() => {
|
||||
setState(fields.reduce((res, f) => ({ ...res, [f.elementCode]: f.elementValue == undefined ? null : f.elementValue }), {}));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
//Формирование представления
|
||||
return (
|
||||
<Dialog onClose={handleClose} open>
|
||||
<DialogTitle>{title}</DialogTitle>
|
||||
<DialogContent>
|
||||
{fields.map((f, i) => (
|
||||
<FormField key={i} {...f} elementValue={state[f.elementCode]} formValues={state} onChange={handleFieldChange} />
|
||||
))}
|
||||
{children}
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
{onOk && <Button onClick={handleOk}>{BUTTONS.OK}</Button>}
|
||||
{onCancel && <Button onClick={handleCancel}>{BUTTONS.CANCEL}</Button>}
|
||||
{onClose && <Button onClick={handleClose}>{BUTTONS.CLOSE}</Button>}
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Форма
|
||||
Form.propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
fields: PropTypes.arrayOf(PropTypes.shape(FORM_FILED)),
|
||||
children: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf(PropTypes.node)]),
|
||||
onOk: PropTypes.func,
|
||||
onCancel: PropTypes.func,
|
||||
onClose: PropTypes.func
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { Form };
|
149
app/panels/rrp_conf_editor/components/form_field.js
Normal file
149
app/panels/rrp_conf_editor/components/form_field.js
Normal file
@ -0,0 +1,149 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга - РО - Редактор настройки регламентированного отчёта
|
||||
Компонент панели: Поле ввода формы
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import React, { useState, useEffect } from "react"; //Классы React
|
||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||
import { Box, Icon, Input, InputAdornment, FormControl, Select, InputLabel, MenuItem, IconButton, Autocomplete, TextField } from "@mui/material"; //Интерфейсные компоненты
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Формат свойств поля формы
|
||||
const FORM_FILED = {
|
||||
elementCode: PropTypes.string.isRequired,
|
||||
elementValue: PropTypes.oneOfType([PropTypes.number, PropTypes.string, PropTypes.instanceOf(Date)]),
|
||||
labelText: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func,
|
||||
dictionary: PropTypes.func,
|
||||
list: PropTypes.array,
|
||||
type: PropTypes.string,
|
||||
freeSolo: PropTypes.bool,
|
||||
disabled: PropTypes.bool,
|
||||
formValues: PropTypes.object
|
||||
};
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Поле ввода формы
|
||||
const FormField = ({
|
||||
elementCode,
|
||||
elementValue,
|
||||
labelText,
|
||||
onChange,
|
||||
dictionary,
|
||||
list,
|
||||
type,
|
||||
freeSolo = false,
|
||||
disabled = false,
|
||||
formValues,
|
||||
...other
|
||||
}) => {
|
||||
//Значение элемента
|
||||
const [value, setValue] = useState(elementValue);
|
||||
|
||||
//При получении нового значения из вне
|
||||
useEffect(() => {
|
||||
setValue(elementValue);
|
||||
}, [elementValue]);
|
||||
|
||||
//Выбор значения из словаря
|
||||
const handleDictionaryClick = () => dictionary && dictionary(formValues, res => (res ? res.map(i => handleChangeByName(i.name, i.value)) : null));
|
||||
|
||||
//Изменение значения элемента (по событию)
|
||||
const handleChange = e => {
|
||||
setValue(e.target.value);
|
||||
if (onChange) onChange(e.target.name, e.target.value);
|
||||
};
|
||||
|
||||
//Изменение значения элемента (по имени и значению)
|
||||
const handleChangeByName = (name, value) => {
|
||||
if (name === elementCode) setValue(value);
|
||||
if (onChange) onChange(name, value);
|
||||
};
|
||||
|
||||
//Генерация содержимого
|
||||
return (
|
||||
<Box p={1}>
|
||||
<FormControl variant="standard" fullWidth {...other}>
|
||||
{list ? (
|
||||
freeSolo ? (
|
||||
<Autocomplete
|
||||
id={elementCode}
|
||||
name={elementCode}
|
||||
freeSolo
|
||||
disabled={disabled}
|
||||
inputValue={value ? value : ""}
|
||||
onChange={(event, newValue) => handleChangeByName(elementCode, newValue)}
|
||||
onInputChange={(event, newInputValue) => handleChangeByName(elementCode, newInputValue)}
|
||||
options={list}
|
||||
renderInput={params => <TextField {...params} label={labelText} name={elementCode} variant={"standard"} />}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<InputLabel id={`${elementCode}Lable`} shrink>
|
||||
{labelText}
|
||||
</InputLabel>
|
||||
<Select
|
||||
labelId={`${elementCode}Lable`}
|
||||
id={elementCode}
|
||||
name={elementCode}
|
||||
label={labelText}
|
||||
value={[undefined, null].includes(value) ? "" : value}
|
||||
onChange={handleChange}
|
||||
disabled={disabled}
|
||||
displayEmpty
|
||||
>
|
||||
{list.map((item, i) => (
|
||||
<MenuItem key={i} value={[undefined, null].includes(item.value) ? "" : item.value}>
|
||||
{item.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</>
|
||||
)
|
||||
) : (
|
||||
<>
|
||||
<InputLabel {...(type == "date" ? { shrink: true } : {})} htmlFor={elementCode}>
|
||||
{labelText}
|
||||
</InputLabel>
|
||||
<Input
|
||||
id={elementCode}
|
||||
name={elementCode}
|
||||
value={value ? value : ""}
|
||||
endAdornment={
|
||||
dictionary ? (
|
||||
<InputAdornment position="end">
|
||||
<IconButton aria-label={`${elementCode} select`} onClick={handleDictionaryClick} edge="end">
|
||||
<Icon>list</Icon>
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
) : null
|
||||
}
|
||||
{...(type ? { type } : {})}
|
||||
onChange={handleChange}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</FormControl>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Поле ввода формы
|
||||
FormField.propTypes = FORM_FILED;
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { FORM_FILED, FormField };
|
@ -1,343 +0,0 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга - РО - Редактор настройки регламентированного отчёта
|
||||
Дополнительная разметка и вёрстка клиентских элементов
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import React, { useState } from "react"; //Классы React
|
||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||
import {
|
||||
IconButton,
|
||||
Icon,
|
||||
Link,
|
||||
Card,
|
||||
CardContent,
|
||||
CardHeader,
|
||||
Menu,
|
||||
MenuItem,
|
||||
Table,
|
||||
TableRow,
|
||||
TableCell,
|
||||
TableBody,
|
||||
Box,
|
||||
Typography
|
||||
} from "@mui/material"; //Интерфейсные компоненты
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Стили
|
||||
export const STYLES = {
|
||||
BOX_ROW: { display: "flex", justifyContent: "center", alignItems: "center" },
|
||||
LINK_STYLE: { component: "button", cursor: "pointer", width: "-webkit-fill-available" },
|
||||
DATA_CELL: columnDef => ({
|
||||
padding: "5px 5px",
|
||||
fontSize: "0.775rem",
|
||||
letterSpacing: "0.005em",
|
||||
textAlign: "center",
|
||||
wordBreak: "break-all",
|
||||
backgroundColor: columnDef.name === "SROW_NAME" ? "#b4b4b4" : "trasparent"
|
||||
}),
|
||||
DATA_CELL_CARD: {
|
||||
padding: "0px 3px 3px 0px",
|
||||
border: "1px solid lightgrey"
|
||||
},
|
||||
DATA_CELL_CARD_HEADER: {
|
||||
padding: "0px"
|
||||
},
|
||||
DATA_CELL_CARD_SUBHEADER: {
|
||||
textAlign: "left",
|
||||
paddingLeft: "10px",
|
||||
fontSize: "1rem",
|
||||
fontWeight: "450"
|
||||
},
|
||||
DATA_CELL_CARD_CONTENT: listLength => {
|
||||
return {
|
||||
fontSize: "0.75rem",
|
||||
padding: "5px 0px",
|
||||
minHeight: "105px",
|
||||
maxHeight: "105px",
|
||||
overflowY: "auto",
|
||||
"&::-webkit-scrollbar": {
|
||||
width: "8px"
|
||||
},
|
||||
"&::-webkit-scrollbar-track": {
|
||||
borderRadius: "8px",
|
||||
backgroundColor: "#EBEBEB"
|
||||
},
|
||||
"&::-webkit-scrollbar-thumb": {
|
||||
borderRadius: "8px",
|
||||
backgroundColor: "#b4b4b4"
|
||||
},
|
||||
"&::-webkit-scrollbar-thumb:hover": {
|
||||
backgroundColor: "#808080"
|
||||
},
|
||||
"&:last-child": {
|
||||
paddingBottom: "0px"
|
||||
},
|
||||
...(listLength === 0 ? { display: "flex", justifyContent: "center", alignItems: "center" } : null)
|
||||
};
|
||||
},
|
||||
DATA_CELL_CARD_CONTEXT_FONT: {
|
||||
fontSize: "0.75rem"
|
||||
},
|
||||
DATA_CELL_CARD_CONTEXT_MARK: {
|
||||
padding: "0px 0px 0px 10px",
|
||||
borderBottom: "1px solid #EBEBEB"
|
||||
},
|
||||
DATA_CELL_CN: {
|
||||
textOverflow: "ellipsis",
|
||||
overflow: "hidden",
|
||||
whiteSpace: "pre",
|
||||
padding: "0px 5px",
|
||||
maxWidth: "100px",
|
||||
border: "none"
|
||||
},
|
||||
GRID_PANEL_CARD: { maxWidth: 400, flexDirection: "column", display: "flex" },
|
||||
MARK_INFO: {
|
||||
fontSize: "0.8rem",
|
||||
textAlign: "left",
|
||||
textOverflow: "ellipsis",
|
||||
overflow: "hidden",
|
||||
whiteSpace: "pre",
|
||||
maxWidth: "max-content",
|
||||
width: "-webkit-fill-available"
|
||||
},
|
||||
BUTTON_CN_INSERT: {
|
||||
padding: "0px 8px",
|
||||
marginBottom: "2px",
|
||||
"& .MuiIcon-root": {
|
||||
fontWeight: "bold",
|
||||
fontSize: "1rem"
|
||||
}
|
||||
},
|
||||
HEAD_CELL: {
|
||||
backgroundColor: "#b4b4b4",
|
||||
textAlign: "center"
|
||||
},
|
||||
HEAD_CELL_STACK: {
|
||||
justifyContent: "space-around"
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------
|
||||
//Вспомогательные функции и компоненты
|
||||
//------------------------------------
|
||||
|
||||
//Действия карты показателя
|
||||
const DataCellCardActions = ({ columnDef, menuItems, cellData, markRn }) => {
|
||||
//Собственное состояние
|
||||
const [cardActions, setCardActions] = useState({ anchorMenuMethods: null, openMethods: false });
|
||||
|
||||
//По нажатию на открытие меню действий
|
||||
const handleMethodsMenuButtonClick = event => {
|
||||
setCardActions(pv => ({ ...pv, anchorMenuMethods: event.currentTarget, openMethods: true }));
|
||||
};
|
||||
|
||||
//При закрытии меню
|
||||
const handleMethodsMenuClose = () => {
|
||||
setCardActions(pv => ({ ...pv, anchorMenuMethods: null, openMethods: false }));
|
||||
};
|
||||
return (
|
||||
<Box sx={STYLES.BOX_ROW}>
|
||||
<IconButton id={`${columnDef.name}_menu_button`} aria-haspopup="true" onClick={handleMethodsMenuButtonClick}>
|
||||
<Icon>more_vert</Icon>
|
||||
</IconButton>
|
||||
<Menu
|
||||
id={`${columnDef.name}_menu`}
|
||||
anchorEl={cardActions.anchorMenuMethods}
|
||||
open={cardActions.openMethods}
|
||||
onClose={handleMethodsMenuClose}
|
||||
>
|
||||
{menuItems.map(el => {
|
||||
return (
|
||||
<MenuItem
|
||||
key={`${cellData}_${el.method}`}
|
||||
onClick={() => {
|
||||
el.func(markRn);
|
||||
handleMethodsMenuClose();
|
||||
}}
|
||||
>
|
||||
<Icon>{el.icon}</Icon>
|
||||
{el.name}
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</Menu>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Действия карты показателя
|
||||
DataCellCardActions.propTypes = {
|
||||
columnDef: PropTypes.object.isRequired,
|
||||
menuItems: PropTypes.array,
|
||||
cellData: PropTypes.any,
|
||||
markRn: PropTypes.number
|
||||
};
|
||||
|
||||
//Таблица составов показателя
|
||||
const MarkCnList = ({ markRn, list, handleMarkCnOpen }) => {
|
||||
return (
|
||||
<Table>
|
||||
<TableBody>
|
||||
{list.map((el, index) => {
|
||||
return (
|
||||
<TableRow key={index}>
|
||||
<TableCell
|
||||
sx={{ ...STYLES.DATA_CELL_CN, ...STYLES.DATA_CELL_CARD_CONTEXT_FONT }}
|
||||
title={el.SDESC}
|
||||
align="left"
|
||||
size="small"
|
||||
>
|
||||
<Link sx={STYLES.LINK_STYLE} onClick={() => handleMarkCnOpen(markRn, el.NRN)}>
|
||||
{el.SDESC}
|
||||
</Link>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
</TableBody>
|
||||
</Table>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Таблица составов показателя
|
||||
MarkCnList.propTypes = {
|
||||
markRn: PropTypes.number.isRequired,
|
||||
list: PropTypes.array.isRequired,
|
||||
handleMarkCnOpen: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
//Ячейка таблицы строки
|
||||
const DataCellContent = ({ row, columnDef, menuItems, sectionRn, handleMarkAdd, handleMarkOpen, handleMarkCnOpen, handleMarkCnInsert }) => {
|
||||
//Считываем информацию о показателе
|
||||
let mark = {
|
||||
sectionRn: sectionRn,
|
||||
data: row[columnDef.name],
|
||||
nRn: row["NMARK_RN_" + columnDef.name.substring(5)],
|
||||
sCode: row["SMARK_CODE_" + columnDef.name.substring(5)],
|
||||
sRowCode: row["SROW_CODE"],
|
||||
nRowRn: row["NROW_RN"],
|
||||
sColCode: columnDef.name.substring(5),
|
||||
nColRn: row["NCOL_RN_" + columnDef.name.substring(5)],
|
||||
rCnList: row["MARK_CNS_" + columnDef.name.substring(5)] ? [...row["MARK_CNS_" + columnDef.name.substring(5)]] : []
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{mark.nRn ? (
|
||||
<Card variant={"plain"} sx={STYLES.DATA_CELL_CARD}>
|
||||
<CardHeader
|
||||
sx={STYLES.DATA_CELL_CARD_HEADER}
|
||||
subheader={
|
||||
<Box>
|
||||
<Link sx={STYLES.LINK_STYLE} onClick={() => (handleMarkOpen ? handleMarkOpen(mark.nRn) : null)}>
|
||||
Состав
|
||||
</Link>
|
||||
{mark.rCnList.length !== 0 ? (
|
||||
<IconButton sx={STYLES.BUTTON_CN_INSERT} aria-haspopup="true" onClick={() => handleMarkCnInsert(mark.nRn)}>
|
||||
<Icon>add</Icon>
|
||||
</IconButton>
|
||||
) : null}
|
||||
</Box>
|
||||
}
|
||||
subheaderTypographyProps={STYLES.DATA_CELL_CARD_SUBHEADER}
|
||||
action={<DataCellCardActions columnDef={columnDef} menuItems={menuItems} cellData={mark.data} markRn={mark.nRn} />}
|
||||
></CardHeader>
|
||||
<CardContent sx={STYLES.DATA_CELL_CARD_CONTEXT_MARK}>
|
||||
<Typography sx={STYLES.MARK_INFO} title={mark.sCode}>
|
||||
{mark.sCode}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
<CardContent sx={STYLES.DATA_CELL_CARD_CONTENT(mark.rCnList.length)}>
|
||||
{mark.rCnList.length !== 0 ? (
|
||||
<MarkCnList markRn={mark.nRn} list={mark.rCnList} handleMarkCnOpen={handleMarkCnOpen} />
|
||||
) : (
|
||||
<Box>
|
||||
<Typography sx={STYLES.DATA_CELL_CARD_CONTEXT_FONT}>Показатель не имеет состава</Typography>
|
||||
<Link sx={STYLES.LINK_STYLE} onClick={() => (handleMarkCnInsert ? handleMarkCnInsert(mark.nRn) : null)}>
|
||||
Добавить
|
||||
</Link>
|
||||
</Box>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : (
|
||||
<Box>
|
||||
<Typography sx={STYLES.DATA_CELL_CARD_CONTEXT_FONT}>Показатель отсутствует</Typography>
|
||||
<Link
|
||||
sx={STYLES.LINK_STYLE}
|
||||
onClick={() =>
|
||||
handleMarkOpen ? handleMarkAdd(mark.sectionRn, mark.nRowRn, mark.sRowCode, mark.nColRn, mark.sColCode) : null
|
||||
}
|
||||
>
|
||||
Добавить
|
||||
</Link>
|
||||
</Box>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Ячейка таблицы строки
|
||||
DataCellContent.propTypes = {
|
||||
row: PropTypes.object.isRequired,
|
||||
columnDef: PropTypes.object.isRequired,
|
||||
menuItems: PropTypes.array,
|
||||
sectionRn: PropTypes.number.isRequired,
|
||||
handleMarkAdd: PropTypes.func,
|
||||
handleMarkOpen: PropTypes.func,
|
||||
handleMarkCnOpen: PropTypes.func,
|
||||
handleMarkCnInsert: PropTypes.func
|
||||
};
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Генерация представления ячейки c данными показателя раздела регламентированного отчета
|
||||
export const confSctnMrkCellRender = ({
|
||||
row,
|
||||
columnDef,
|
||||
sectionRn,
|
||||
handleMarkAdd,
|
||||
handleMarkOpen,
|
||||
handleMarkCnOpen,
|
||||
handleMarkCnInsert,
|
||||
menuItems
|
||||
}) => {
|
||||
//Иницализируем стили
|
||||
let cellStyle = STYLES.DATA_CELL(columnDef);
|
||||
//Считываем значение
|
||||
let data = row[columnDef.name];
|
||||
//Если это не наименование строки и есть значение
|
||||
columnDef.name != "SROW_NAME" && data != undefined && columnDef.visible == true
|
||||
? (data = (
|
||||
<DataCellContent
|
||||
row={row}
|
||||
columnDef={columnDef}
|
||||
menuItems={menuItems}
|
||||
sectionRn={sectionRn}
|
||||
handleMarkAdd={handleMarkAdd}
|
||||
handleMarkOpen={handleMarkOpen}
|
||||
handleMarkCnOpen={handleMarkCnOpen}
|
||||
handleMarkCnInsert={handleMarkCnInsert}
|
||||
/>
|
||||
))
|
||||
: null;
|
||||
return { cellStyle: { ...cellStyle }, data: data };
|
||||
};
|
||||
|
||||
//Генерация представления ячейки заголовка группы c данными показателя раздела регламентированного отчета
|
||||
export const confSctnMrkHeadCellRender = ({ columnDef }) => {
|
||||
return {
|
||||
cellStyle: STYLES.HEAD_CELL,
|
||||
stackStyle: STYLES.HEAD_CELL_STACK,
|
||||
data: columnDef.caption
|
||||
};
|
||||
};
|
122
app/panels/rrp_conf_editor/components/mark_card.js
Normal file
122
app/panels/rrp_conf_editor/components/mark_card.js
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга - РО - Редактор настройки регламентированного отчёта
|
||||
Компонент панели: Карточка показателя
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import React, { useState } from "react"; //Классы React
|
||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||
import { Box, Card, CardContent, Typography, Link, Divider } from "@mui/material"; //Интерфейсные компоненты
|
||||
import { APP_STYLES } from "../../../../app.styles"; //Общие стили приложения
|
||||
import { MarkCnList } from "./mark_cn_list"; //Состав показателя
|
||||
import { MarkCardToolbar } from "./mark_card_toolbar"; //Панель инструментов карточки
|
||||
import { STYLES as COMMON_STYLES } from "../common"; //Общие стили и константы
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Стили
|
||||
const STYLES = {
|
||||
CARD: hovered => ({
|
||||
padding: "0px 0px 0px 0px",
|
||||
border: "1px solid lightgrey",
|
||||
position: "relative",
|
||||
...(hovered ? COMMON_STYLES.BG_MARK_CARD : null)
|
||||
}),
|
||||
CARD_CONTENT_MARK_TITLE: { padding: "0px 0px 0px 0px" },
|
||||
CARD_CONTENT_MARK_CONSTITUTION: constitutionExists => ({
|
||||
padding: "8px",
|
||||
height: "105px",
|
||||
"&:last-child": { paddingBottom: "10px" },
|
||||
...COMMON_STYLES.FONT_MARK_CARD_BODY,
|
||||
...(!constitutionExists ? { display: "flex", justifyContent: "center", alignItems: "center" } : null)
|
||||
}),
|
||||
BOX_MARK_CONSTITUTION: {
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
overflowY: "auto",
|
||||
...APP_STYLES.SCROLL
|
||||
}
|
||||
};
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Карточка показателя
|
||||
const MarkCard = ({ mark, code, name, constitution = [], onMarkAdd, onMarkUpdate, onMarkDelete, onMarkOpen, onMarkCnOpen, onMarkCnAdd }) => {
|
||||
//Флаг нахождения указателя мыши в карточке
|
||||
const [hovered, setHovered] = useState(false);
|
||||
|
||||
//При попадании мыши на закладку раздела
|
||||
const handleCardMouseIn = () => setHovered(true);
|
||||
|
||||
//При выходе мыши из закладки раздела
|
||||
const handleCardMouseOut = () => setHovered(false);
|
||||
|
||||
//Флаг наличия данных в составе показателя
|
||||
const constitutionExists = constitution?.length > 0;
|
||||
|
||||
//Формирование представления
|
||||
return mark ? (
|
||||
<Card variant={"plain"} sx={STYLES.CARD(hovered)} onMouseEnter={handleCardMouseIn} onMouseLeave={handleCardMouseOut}>
|
||||
<CardContent sx={STYLES.CARD_CONTENT_MARK_TITLE}>
|
||||
<MarkCardToolbar
|
||||
title={code}
|
||||
desc={name}
|
||||
onOpen={onMarkOpen}
|
||||
onEdit={onMarkUpdate}
|
||||
onDelete={onMarkDelete}
|
||||
onCnAdd={onMarkCnAdd}
|
||||
showButtons={hovered}
|
||||
/>
|
||||
</CardContent>
|
||||
<Divider />
|
||||
<CardContent sx={STYLES.CARD_CONTENT_MARK_CONSTITUTION(constitutionExists)}>
|
||||
{constitutionExists ? (
|
||||
<Box sx={STYLES.BOX_MARK_CONSTITUTION}>
|
||||
<MarkCnList constitution={constitution} onMarkCnOpen={constitutionRn => onMarkCnOpen && onMarkCnOpen(constitutionRn)} />
|
||||
</Box>
|
||||
) : (
|
||||
<Box>
|
||||
<Typography sx={COMMON_STYLES.FONT_MARK_CARD_BODY}>Показатель не имеет состава</Typography>
|
||||
<Link sx={COMMON_STYLES.LINK} onClick={onMarkCnAdd}>
|
||||
Добавить
|
||||
</Link>
|
||||
</Box>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : (
|
||||
<Box>
|
||||
<Typography sx={COMMON_STYLES.FONT_MARK_CARD_BODY}>Показатель отсутствует</Typography>
|
||||
<Link sx={COMMON_STYLES.LINK} onClick={onMarkAdd}>
|
||||
Добавить
|
||||
</Link>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Карточка показателя
|
||||
MarkCard.propTypes = {
|
||||
mark: PropTypes.number,
|
||||
code: PropTypes.string,
|
||||
name: PropTypes.string,
|
||||
constitution: PropTypes.arrayOf(PropTypes.object),
|
||||
onMarkAdd: PropTypes.func.isRequired,
|
||||
onMarkUpdate: PropTypes.func.isRequired,
|
||||
onMarkDelete: PropTypes.func.isRequired,
|
||||
onMarkOpen: PropTypes.func.isRequired,
|
||||
onMarkCnOpen: PropTypes.func.isRequired,
|
||||
onMarkCnAdd: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { MarkCard };
|
73
app/panels/rrp_conf_editor/components/mark_card_toolbar.js
Normal file
73
app/panels/rrp_conf_editor/components/mark_card_toolbar.js
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга - РО - Редактор настройки регламентированного отчёта
|
||||
Компонент панели: Панель инструментов карточки показателя
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import React from "react"; //Классы React
|
||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||
import { Stack, IconButton, Icon, Typography, Link } from "@mui/material"; //Интерфейсные компоненты
|
||||
import { STYLES as COMMON_STYLES } from "../common"; //Общие стили и константы
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Стили
|
||||
const STYLES = {
|
||||
CONTAINER: { ...COMMON_STYLES.TOOLBAR, width: "100%", position: "unset", right: "unset", height: "40px" }
|
||||
};
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Панель инструментов карточки показателя
|
||||
const MarkCardToolbar = ({ title, desc, onOpen, onEdit, onDelete, onCnAdd, showButtons = false }) => {
|
||||
return (
|
||||
<Stack direction={"row"} alignItems={"center"} justifyContent={"space-between"} sx={STYLES.CONTAINER} title={desc}>
|
||||
<Typography noWrap p={1} textAlign={"left"} variant={"subtitle1"} component={"div"} sx={COMMON_STYLES.FONT_MARK_CARD_HEAD}>
|
||||
{showButtons ? (
|
||||
<Link sx={COMMON_STYLES.LINK} onClick={onOpen}>
|
||||
{title}
|
||||
</Link>
|
||||
) : (
|
||||
title
|
||||
)}
|
||||
</Typography>
|
||||
{showButtons && (
|
||||
<Stack direction={"row"}>
|
||||
<IconButton title={"Исправить показатель"} onClick={onEdit}>
|
||||
<Icon>edit</Icon>
|
||||
</IconButton>
|
||||
<IconButton title={"Добавить элемент состава"} onClick={onCnAdd}>
|
||||
<Icon>library_add</Icon>
|
||||
</IconButton>
|
||||
<IconButton title={"Удалить показатель"} onClick={onDelete}>
|
||||
<Icon>delete</Icon>
|
||||
</IconButton>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Панель инструментов карточки показателя
|
||||
MarkCardToolbar.propTypes = {
|
||||
title: PropTypes.string,
|
||||
desc: PropTypes.string,
|
||||
onOpen: PropTypes.func.isRequired,
|
||||
onEdit: PropTypes.func.isRequired,
|
||||
onDelete: PropTypes.func.isRequired,
|
||||
onCnAdd: PropTypes.func.isRequired,
|
||||
showButtons: PropTypes.bool
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { MarkCardToolbar };
|
67
app/panels/rrp_conf_editor/components/mark_cn_list.js
Normal file
67
app/panels/rrp_conf_editor/components/mark_cn_list.js
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга - РО - Редактор настройки регламентированного отчёта
|
||||
Компонент панели: Состав показателя
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import React from "react"; //Классы React
|
||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||
import { Table, TableBody, TableRow, TableCell, Link } from "@mui/material"; //Интерфейсные компоненты
|
||||
import { STYLES as COMMON_STYLES } from "../common"; //Общие стили и константы
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Стили
|
||||
const STYLES = {
|
||||
DATA_CELL_CN: {
|
||||
textOverflow: "ellipsis",
|
||||
overflow: "hidden",
|
||||
whiteSpace: "pre",
|
||||
padding: "0px",
|
||||
maxWidth: "100px",
|
||||
border: "none",
|
||||
...COMMON_STYLES.FONT_MARK_CARD_BODY
|
||||
}
|
||||
};
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Состав показателя
|
||||
const MarkCnList = ({ constitution, onMarkCnOpen }) => {
|
||||
return (
|
||||
<Table>
|
||||
<TableBody>
|
||||
{constitution.map((el, index) => {
|
||||
return (
|
||||
<TableRow key={index}>
|
||||
<TableCell sx={STYLES.DATA_CELL_CN} title={el.SDESC} align={"left"} size={"small"}>
|
||||
<Link sx={COMMON_STYLES.LINK} onClick={() => onMarkCnOpen && onMarkCnOpen(el.NRN)}>
|
||||
{el.SDESC}
|
||||
</Link>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
</TableBody>
|
||||
</Table>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Состав показателя
|
||||
MarkCnList.propTypes = {
|
||||
constitution: PropTypes.array.isRequired,
|
||||
onMarkCnOpen: PropTypes.func
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { MarkCnList };
|
182
app/panels/rrp_conf_editor/components/marks.js
Normal file
182
app/panels/rrp_conf_editor/components/marks.js
Normal file
@ -0,0 +1,182 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга - РО - Редактор настройки регламентированного отчёта
|
||||
Компонент панели: Показатели раздела
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import React, { useState, useContext } from "react"; //Классы React
|
||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||
import { Icon, Button } from "@mui/material"; //Интерфейсные элементы
|
||||
import { MessagingСtx } from "../../../context/messaging"; //Контекст сообщений
|
||||
import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../../components/p8p_data_grid"; //Таблица данных
|
||||
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
||||
import { APP_STYLES } from "../../../../app.styles"; //Типовые стили
|
||||
import { confSctnMrkCellRender, confSctnMrkHeadCellRender } from "../layouts"; //Дополнительная разметка и вёрстка клиентских элементов
|
||||
import { ActionMessage } from "./action_message"; //Сообщение с действиями
|
||||
import { DialogMarkIU } from "./dialog_mark_iu"; //Диалог добавления/исправления показателя
|
||||
import { DialogHelp } from "./dialog_help"; //Диалог помощи
|
||||
import { DialogOrder } from "./dialog_order"; //Диалог сортировки
|
||||
import { useDictionary } from "../hooks"; //Кастомные хуки
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Стили
|
||||
const STYLES = {
|
||||
MARKS_DG_CONTAINER: {
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
overflow: "auto",
|
||||
border: "unset",
|
||||
...APP_STYLES.SCROLL
|
||||
},
|
||||
MARKS_DG_TABLE: { tableLayout: "fixed", width: "auto" }
|
||||
};
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Показатели раздела
|
||||
const Marks = ({ marks, order, marksLoading, marksInit, onRefresh, onMarkInsert, onMarkUpdate, onMarkDelete, onOrderChange }) => {
|
||||
//Состояние - диалог сортировки
|
||||
const [dialogOrder, setDialogOrder] = useState(false);
|
||||
|
||||
//Состояние - диалог помощи
|
||||
const [dialogHelp, setDialogHelp] = useState(false);
|
||||
|
||||
//Состояние - Редактируемый показатель
|
||||
const [modMark, setModMark] = useState(null);
|
||||
|
||||
//Подключение к контексту сообщений
|
||||
const { showMsgWarn } = useContext(MessagingСtx);
|
||||
|
||||
//Подключение к словарям
|
||||
const { showMark, showMarkCn, showMarkCnAdd } = useDictionary();
|
||||
|
||||
//Изменение состояния диалога информации
|
||||
const toggleHelpDialog = () => setDialogHelp(pv => !pv);
|
||||
|
||||
//Изменение состояния диалога сортировки
|
||||
const toggleOrderDialog = () => setDialogOrder(pv => !pv);
|
||||
|
||||
//При необходимости обновления
|
||||
const handleRefresh = () => onRefresh && onRefresh();
|
||||
|
||||
//При вызове сортировки
|
||||
const handleOrder = () => toggleOrderDialog();
|
||||
|
||||
//При вызове помощи
|
||||
const handleHelp = () => toggleHelpDialog();
|
||||
|
||||
//Изменение состояния сортировки строк и граф
|
||||
const handleOrderChange = order => {
|
||||
onOrderChange && onOrderChange(order);
|
||||
toggleOrderDialog();
|
||||
};
|
||||
|
||||
//При добавлении показателя
|
||||
const handleMarkAdd = () => setModMark(true);
|
||||
|
||||
//При добавлении показателя по указанным строке/графе
|
||||
const handleMarkAddByRowCol = (row, column) => onMarkInsert({ row, column });
|
||||
|
||||
//При исправлении показателя
|
||||
const handleMarkUpdate = markDesc => setModMark({ ...markDesc });
|
||||
|
||||
//При удалении показателя
|
||||
const handleMarkDelete = mark => showMsgWarn("Удалить показатель?", () => onMarkDelete && onMarkDelete(mark));
|
||||
|
||||
//При переходе к показателю
|
||||
const handleMarkOpen = mark => showMark(mark, res => res.success && handleRefresh());
|
||||
|
||||
//При добавлении состава показателя
|
||||
const handleMarkCnAdd = mark => showMarkCnAdd(mark, res => res.success && handleRefresh());
|
||||
|
||||
//При переходе к составу показателя
|
||||
const handleMarkCnOpen = (mark, constitution) => showMarkCn(mark, constitution, res => res.success && handleRefresh());
|
||||
|
||||
//При закрытии формы добавления/исправления по "ОК"
|
||||
const handleIUFormOk = values => {
|
||||
if (modMark === true) onMarkInsert && onMarkInsert(values, res => res && setModMark(null));
|
||||
else onMarkUpdate && onMarkUpdate({ ...modMark, ...values }, res => res && setModMark(null));
|
||||
};
|
||||
|
||||
//При закрытии формы добавления/исправления по "Отмена"
|
||||
const handleIUFormCancel = () => setModMark(null);
|
||||
|
||||
//Формирование представления
|
||||
return (
|
||||
<>
|
||||
{dialogOrder && <DialogOrder {...order} onOk={handleOrderChange} onCancel={toggleOrderDialog} />}
|
||||
{dialogHelp && <DialogHelp onClose={toggleHelpDialog} />}
|
||||
{modMark && (
|
||||
<DialogMarkIU {...(modMark === true ? {} : modMark)} insert={modMark === true} onOk={handleIUFormOk} onCancel={handleIUFormCancel} />
|
||||
)}
|
||||
{marksInit &&
|
||||
(marks ? (
|
||||
<P8PDataGrid
|
||||
{...P8P_DATA_GRID_CONFIG_PROPS}
|
||||
{...marks}
|
||||
tableStyle={STYLES.MARKS_DG_TABLE}
|
||||
containerComponentProps={{ elevation: 0, square: true, variant: "outlined", sx: STYLES.MARKS_DG_CONTAINER }}
|
||||
size={P8P_DATA_GRID_SIZE.SMALL}
|
||||
dataCellRender={prms =>
|
||||
confSctnMrkCellRender({
|
||||
...prms,
|
||||
onMarkAdd: handleMarkAddByRowCol,
|
||||
onMarkUpdate: handleMarkUpdate,
|
||||
onMarkDelete: handleMarkDelete,
|
||||
onMarkOpen: handleMarkOpen,
|
||||
onMarkCnOpen: handleMarkCnOpen,
|
||||
onMarkCnAdd: handleMarkCnAdd
|
||||
})
|
||||
}
|
||||
headCellRender={prms =>
|
||||
confSctnMrkHeadCellRender({
|
||||
...prms,
|
||||
onAdd: handleMarkAdd,
|
||||
onRefresh: handleRefresh,
|
||||
onOrder: handleOrder,
|
||||
onHelp: handleHelp
|
||||
})
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
!marksLoading && (
|
||||
<ActionMessage icon={"info"} title={"В разделе нет показателей"} desc={"Добавьте новый"}>
|
||||
<Button startIcon={<Icon>add</Icon>} onClick={handleMarkAdd}>
|
||||
Показатель
|
||||
</Button>
|
||||
</ActionMessage>
|
||||
)
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Показатели раздела
|
||||
Marks.propTypes = {
|
||||
marks: PropTypes.object,
|
||||
order: PropTypes.object.isRequired,
|
||||
marksLoading: PropTypes.bool.isRequired,
|
||||
marksInit: PropTypes.bool.isRequired,
|
||||
onRefresh: PropTypes.func,
|
||||
onMarkInsert: PropTypes.func,
|
||||
onMarkUpdate: PropTypes.func,
|
||||
onMarkDelete: PropTypes.func,
|
||||
onOrderChange: PropTypes.func
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { Marks };
|
60
app/panels/rrp_conf_editor/components/marks_toolbar.js
Normal file
60
app/panels/rrp_conf_editor/components/marks_toolbar.js
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга - РО - Редактор настройки регламентированного отчёта
|
||||
Компонент панели: Панель инструментов показателей
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import React from "react"; //Классы React
|
||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||
import { Stack, Icon, IconButton } from "@mui/material"; //Интерфейсные компоненты
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Стили
|
||||
const STYLES = {
|
||||
CONTAINER: { width: "100%" }
|
||||
};
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Панель инструментов показателей
|
||||
const MarksToolbar = ({ onAdd, onRefresh, onOrder, onHelp }) => {
|
||||
//Формирование представления
|
||||
return (
|
||||
<Stack direction={"row"} alignItems={"center"} justifyContent={"center"} sx={STYLES.CONTAINER}>
|
||||
<IconButton title={"Добавить показатель"} onClick={onAdd}>
|
||||
<Icon>add</Icon>
|
||||
</IconButton>
|
||||
<IconButton title={"Обновить показатели"} onClick={onRefresh}>
|
||||
<Icon>refresh</Icon>
|
||||
</IconButton>
|
||||
<IconButton title={"Порядок сортировки показателей"} onClick={onOrder}>
|
||||
<Icon>sort</Icon>
|
||||
</IconButton>
|
||||
<IconButton title={"Легенда"} onClick={onHelp}>
|
||||
<Icon>help</Icon>
|
||||
</IconButton>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Панель инструментов показателей
|
||||
MarksToolbar.propTypes = {
|
||||
onAdd: PropTypes.func.isRequired,
|
||||
onRefresh: PropTypes.func.isRequired,
|
||||
onOrder: PropTypes.func.isRequired,
|
||||
onHelp: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { MarksToolbar };
|
@ -1,359 +0,0 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга - РО - Редактор настройки регламентированного отчёта
|
||||
Компонент панели: Раздел настройки
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import React, { useState } from "react"; //Классы React
|
||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||
import {
|
||||
Box,
|
||||
IconButton,
|
||||
Icon,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
Button,
|
||||
Typography,
|
||||
List,
|
||||
ListItem,
|
||||
Select,
|
||||
FormControl,
|
||||
InputLabel,
|
||||
MenuItem,
|
||||
DialogActions
|
||||
} from "@mui/material"; //Интерфейсные элементы
|
||||
import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../../components/p8p_data_grid"; //Таблица данных
|
||||
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
||||
import { SectionTabPanel } from "./section_tab_panel"; //Компонент вкладки раздела
|
||||
import { confSctnMrkCellRender, confSctnMrkHeadCellRender } from "./layouts"; //Дополнительная разметка и вёрстка клиентских элементов
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Стили
|
||||
const STYLES = {
|
||||
GRID_SIZES: (height, pxOuterMenuH, pxPanelHeaderH, pxTabsH) => ({
|
||||
padding: 0,
|
||||
minWidth: "98vw",
|
||||
minHeight: (height - pxOuterMenuH - pxPanelHeaderH - pxTabsH) * 0.93,
|
||||
maxWidth: "98vw",
|
||||
maxHeight: (height - pxOuterMenuH - pxPanelHeaderH - pxTabsH) * 0.93
|
||||
}),
|
||||
TABLE_CONTAINER: {
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
paddingTop: 1,
|
||||
paddingBottom: 1
|
||||
},
|
||||
SECTION_ACTIONS: { display: "flex", justifyContent: "space-between", padding: "0px 5px" },
|
||||
TABLE_SCROLL: {
|
||||
"&::-webkit-scrollbar": {
|
||||
width: "12px",
|
||||
height: "12px"
|
||||
},
|
||||
"&::-webkit-scrollbar-track": {
|
||||
borderRadius: "88px",
|
||||
backgroundColor: "#EBEBEB"
|
||||
},
|
||||
"&::-webkit-scrollbar-thumb": {
|
||||
borderRadius: "88px",
|
||||
backgroundColor: "#b4b4b4",
|
||||
backgroundClip: "padding-box",
|
||||
border: "3px solid #EBEBEB"
|
||||
},
|
||||
"&::-webkit-scrollbar-thumb:hover": {
|
||||
backgroundColor: "#808080"
|
||||
}
|
||||
},
|
||||
HELP_LIST_ITEM: {
|
||||
padding: "0px 0px 0px 5px",
|
||||
whiteSpace: "pre",
|
||||
fontSize: "0.95rem"
|
||||
},
|
||||
HELP_LIST_ITEM_NAME: {
|
||||
fontWeight: "bold",
|
||||
fontSize: "inherit",
|
||||
minWidth: "45px"
|
||||
},
|
||||
HELP_LIST_ITEM_DESC: {
|
||||
fontSize: "inherit"
|
||||
},
|
||||
DIALOG_ACTIONS: { justifyContent: "end", paddingRight: "24px", paddingLeft: "24px" }
|
||||
};
|
||||
|
||||
//------------------------------------
|
||||
//Вспомогательные функции и компоненты
|
||||
//------------------------------------
|
||||
|
||||
//Элемент списка расшифровки состава
|
||||
const HelpListItem = ({ name, desc }) => {
|
||||
return (
|
||||
<ListItem sx={STYLES.HELP_LIST_ITEM}>
|
||||
<Typography sx={STYLES.HELP_LIST_ITEM_NAME}>{name}</Typography>
|
||||
<Typography sx={STYLES.HELP_LIST_ITEM_DESC}>{` - ${desc}`}</Typography>
|
||||
</ListItem>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Элемент списка расшифровки состава
|
||||
HelpListItem.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
desc: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
//Диалог дополнительной информации
|
||||
const HelpDialog = ({ handleOpenHelpChange }) => {
|
||||
//Генерация содержимого
|
||||
return (
|
||||
<Dialog open onClose={handleOpenHelpChange}>
|
||||
<DialogTitle>
|
||||
<Box display="flex" alignItems="center">
|
||||
<Box flexGrow={1} textAlign="center">
|
||||
Информация
|
||||
</Box>
|
||||
<Box>
|
||||
<IconButton aria-label="close" onClick={handleOpenHelpChange}>
|
||||
<Icon>close</Icon>
|
||||
</IconButton>
|
||||
</Box>
|
||||
</Box>
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<Typography>Карточки показателей содержат сокращенную информацию о типе состава показателя.</Typography>
|
||||
<Typography>Список сокращений:</Typography>
|
||||
<List disablePadding={true}>
|
||||
<HelpListItem name="fx" desc="формула" />
|
||||
<HelpListItem name="СЗ" desc="статическое значение" />
|
||||
<HelpListItem name="ХП" desc="хранимая процедура" />
|
||||
<HelpListItem name="РП" desc="расчетный показатель" />
|
||||
<HelpListItem name="ХО" desc="хозяйственные операции" />
|
||||
<HelpListItem name="РСДК" desc="расчёты с дебиторами/кредиторами" />
|
||||
<HelpListItem name="ОС" desc="остатки средств по счетам" />
|
||||
<HelpListItem name="ТМЦ" desc="остатки товарно-материальных ценностей" />
|
||||
<HelpListItem name="ДКЗ" desc="дебиторская/кредиторская задолженность" />
|
||||
<HelpListItem name="ИК" desc="инвентарная картотека" />
|
||||
<HelpListItem name="МБП" desc="картотека МБП" />
|
||||
<HelpListItem name="КОБП" desc="картотека операций будущих периодов" />
|
||||
<HelpListItem name="ДПНП" desc="декларация по налогу на прибыль" />
|
||||
<HelpListItem name="РО" desc="регламентированный отчет" />
|
||||
</List>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Диалог дополнительной информации
|
||||
HelpDialog.propTypes = {
|
||||
handleOpenHelpChange: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
//Диалог сортировки
|
||||
const SortDialog = ({ init, handleOpenSortChange, onOrderChange }) => {
|
||||
//Собственное состояние сортировки
|
||||
const [order, setOrder] = useState({ row: init.rowOrder ? init.rowOrder : 0, column: init.columnOrder ? init.columnOrder : 0 });
|
||||
|
||||
//Изменеие сортировки
|
||||
const handleOrderChange = e => setOrder(pv => ({ ...pv, [e.target.name]: e.target.value }));
|
||||
|
||||
//Нажатие на кнопку Ok
|
||||
const handleOk = () => {
|
||||
onOrderChange({ rowOrder: order.row, columnOrder: order.column });
|
||||
handleOpenSortChange();
|
||||
};
|
||||
|
||||
//Кнопка "Очистить", значения по умолчанию
|
||||
const handleClear = () => {
|
||||
setOrder({ row: 0, column: 0 });
|
||||
};
|
||||
|
||||
//Кнопка "Отмена"
|
||||
const handleCancel = () => {
|
||||
handleOpenSortChange();
|
||||
};
|
||||
|
||||
//Генерация содержимого
|
||||
return (
|
||||
<Dialog open onClose={handleOpenSortChange}>
|
||||
<DialogTitle>
|
||||
<Box display="flex" alignItems="center">
|
||||
<Box flexGrow={1} textAlign="center">
|
||||
Сортировка
|
||||
</Box>
|
||||
<Box>
|
||||
<IconButton aria-label="close" onClick={handleCancel}>
|
||||
<Icon>close</Icon>
|
||||
</IconButton>
|
||||
</Box>
|
||||
</Box>
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<Box component="section" p={1}>
|
||||
<FormControl variant="standard" fullWidth>
|
||||
<InputLabel id="row-label">Строки</InputLabel>
|
||||
<Select labelId="row-label" id="row" name="row" value={order.row} label="Строки" onChange={handleOrderChange}>
|
||||
<MenuItem value={0}>Номер</MenuItem>
|
||||
<MenuItem value={1}>Код</MenuItem>
|
||||
<MenuItem value={2}>Мнемокод</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Box>
|
||||
<Box component="section" p={1}>
|
||||
<FormControl variant="standard" fullWidth>
|
||||
<InputLabel id="column-label">Графы</InputLabel>
|
||||
<Select labelId="column-label" id="column" name="column" value={order.column} label="Графы" onChange={handleOrderChange}>
|
||||
<MenuItem value={0}>Номер</MenuItem>
|
||||
<MenuItem value={1}>Код</MenuItem>
|
||||
<MenuItem value={2}>Мнемокод</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<DialogActions sx={STYLES.DIALOG_ACTIONS}>
|
||||
<Button variant="text" onClick={handleOk}>
|
||||
ОК
|
||||
</Button>
|
||||
<Button variant="text" onClick={handleClear}>
|
||||
Очистить
|
||||
</Button>
|
||||
<Button variant="text" onClick={handleCancel}>
|
||||
Отмена
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Диалог сортировки
|
||||
SortDialog.propTypes = {
|
||||
init: PropTypes.object.isRequired,
|
||||
handleOpenSortChange: PropTypes.func.isRequired,
|
||||
onOrderChange: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Раздел настройки
|
||||
const SectionTab = ({
|
||||
section,
|
||||
tabValue,
|
||||
index,
|
||||
order,
|
||||
onOrderChange,
|
||||
containerProps,
|
||||
handleMarkAdd,
|
||||
handleReload,
|
||||
handleMarkOpen,
|
||||
handleMarkCnOpen,
|
||||
handleMarkCnInsert,
|
||||
menuItems
|
||||
}) => {
|
||||
//Состояние - диалог информации
|
||||
const [openHelp, setOpenHelp] = useState(false);
|
||||
|
||||
//Изменение состояния диалога информации
|
||||
const handleOpenHelpChange = () => {
|
||||
setOpenHelp(!openHelp);
|
||||
};
|
||||
|
||||
//Состояние - диалог сортировки
|
||||
const [openSort, setOpenSort] = useState(false);
|
||||
|
||||
//Изменение состояния диалога сортировки
|
||||
const handleOpenSortChange = () => {
|
||||
setOpenSort(!openSort);
|
||||
};
|
||||
|
||||
//Генерация содержимого
|
||||
return (
|
||||
<>
|
||||
<SectionTabPanel key={section.rn} value={tabValue} index={index}>
|
||||
<Box sx={STYLES.SECTION_ACTIONS}>
|
||||
<Box>
|
||||
<IconButton onClick={() => handleMarkAdd(section.rn)}>
|
||||
<Icon>add</Icon>
|
||||
</IconButton>
|
||||
<IconButton onClick={() => handleReload()}>
|
||||
<Icon>refresh</Icon>
|
||||
</IconButton>
|
||||
</Box>
|
||||
<Box>
|
||||
<IconButton onClick={() => handleOpenSortChange()}>
|
||||
<Icon>sort</Icon>
|
||||
</IconButton>
|
||||
<IconButton onClick={() => handleOpenHelpChange()}>
|
||||
<Icon>help</Icon>
|
||||
</IconButton>
|
||||
</Box>
|
||||
</Box>
|
||||
{section.dataLoaded && section.columnsDef.length > 3 ? (
|
||||
<Box sx={STYLES.TABLE_CONTAINER}>
|
||||
<P8PDataGrid
|
||||
{...P8P_DATA_GRID_CONFIG_PROPS}
|
||||
containerComponentProps={{
|
||||
elevation: 6,
|
||||
sx: { ...STYLES.TABLE_SCROLL },
|
||||
style: STYLES.GRID_SIZES(
|
||||
containerProps.height,
|
||||
containerProps.pxOuterMenuH,
|
||||
containerProps.pxPanelHeaderH,
|
||||
containerProps.pxTabsH
|
||||
)
|
||||
}}
|
||||
columnsDef={section.columnsDef}
|
||||
groups={section.groups}
|
||||
rows={section.rows}
|
||||
fixedHeader={section.fixedHeader}
|
||||
fixedColumns={section.fixedColumns}
|
||||
size={P8P_DATA_GRID_SIZE.LARGE}
|
||||
reloading={section.reload}
|
||||
dataCellRender={prms =>
|
||||
confSctnMrkCellRender({
|
||||
...prms,
|
||||
sectionRn: section.rn,
|
||||
handleMarkAdd: handleMarkAdd,
|
||||
handleMarkOpen: handleMarkOpen,
|
||||
handleMarkCnOpen: handleMarkCnOpen,
|
||||
handleMarkCnInsert: handleMarkCnInsert,
|
||||
menuItems: menuItems
|
||||
})
|
||||
}
|
||||
headCellRender={confSctnMrkHeadCellRender}
|
||||
/>
|
||||
</Box>
|
||||
) : null}
|
||||
</SectionTabPanel>
|
||||
{openSort ? <SortDialog init={order} handleOpenSortChange={handleOpenSortChange} onOrderChange={onOrderChange} /> : null}
|
||||
{openHelp ? <HelpDialog handleOpenHelpChange={handleOpenHelpChange} /> : null}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Раздел настройки
|
||||
SectionTab.propTypes = {
|
||||
section: PropTypes.object.isRequired,
|
||||
tabValue: PropTypes.number,
|
||||
index: PropTypes.number,
|
||||
order: PropTypes.object.isRequired,
|
||||
onOrderChange: PropTypes.func.isRequired,
|
||||
containerProps: PropTypes.object,
|
||||
handleMarkAdd: PropTypes.func,
|
||||
handleReload: PropTypes.func,
|
||||
handleMarkOpen: PropTypes.func,
|
||||
handleMarkCnOpen: PropTypes.func,
|
||||
handleMarkCnInsert: PropTypes.func,
|
||||
menuItems: PropTypes.array
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { SectionTab };
|
152
app/panels/rrp_conf_editor/components/section.js
Normal file
152
app/panels/rrp_conf_editor/components/section.js
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга - РО - Редактор настройки регламентированного отчёта
|
||||
Компонент панели: Раздел настройки
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import React, { useState, useContext, useEffect } from "react"; //Классы React
|
||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||
import { Box } from "@mui/material"; //Интерфейсные элементы
|
||||
import { BackEndСtx } from "../../../context/backend"; //Контекст взаимодействия с сервером
|
||||
import { useConfSectionMarks } from "../hooks"; //Кастомные хуки
|
||||
import { ActionMessage } from "./action_message"; //Сообщение с действиями
|
||||
import { Marks } from "./marks"; //Показатели раздела
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Стили
|
||||
const STYLES = {
|
||||
CONTAINER: { width: "100%", height: "100%" },
|
||||
BOX_MARKS: { position: "relative", display: "flex", height: "100%", width: "100%" }
|
||||
};
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Раздел настройки
|
||||
const Section = ({ section = null }) => {
|
||||
//Состояние сортировки строк и граф
|
||||
const [order, setOrder] = useState({ rowOrder: 0, columnOrder: 0 });
|
||||
|
||||
//Состояние - флаг "сокрытия" (на самом деле - отмонтирования) компонента с показателями
|
||||
const [hideMarksThenLoading, setHideMarksThenLoading] = useState(false);
|
||||
|
||||
//Данные раздела (показатели)
|
||||
const [marks, refreshMarks, marksLoading, marksInit] = useConfSectionMarks(section, order.rowOrder, order.columnOrder);
|
||||
|
||||
//Подключение к контексту взаимодействия с сервером
|
||||
const { executeStored } = useContext(BackEndСtx);
|
||||
|
||||
//Добавление показателя
|
||||
const insertMark = async ({ code, name, row, rowCode, rowVersion, column, columnCode, columnVersion }, cb) => {
|
||||
try {
|
||||
await executeStored({
|
||||
stored: "PKG_P8PANELS_RRPCONFED.RRPCONFSCTNMRK_INSERT",
|
||||
args: {
|
||||
NPRN: section,
|
||||
SCODE: code,
|
||||
SNAME: name,
|
||||
NRRPROW: row,
|
||||
SRRPROW: rowCode,
|
||||
SRRPVERSION_ROW: rowVersion,
|
||||
NRRPCOLUMN: column,
|
||||
SRRPCOLUMN: columnCode,
|
||||
SRRPVERSION_COLUMN: columnVersion
|
||||
},
|
||||
loader: false
|
||||
});
|
||||
cb && cb(true);
|
||||
refreshMarks();
|
||||
} catch {
|
||||
cb && cb(false);
|
||||
}
|
||||
};
|
||||
|
||||
//Исправление показателя
|
||||
const updateMark = async ({ rn, code, name, rowCode, rowVersion, columnCode, columnVersion }, cb) => {
|
||||
try {
|
||||
await executeStored({
|
||||
stored: "PKG_P8PANELS_RRPCONFED.RRPCONFSCTNMRK_UPDATE",
|
||||
args: {
|
||||
NRN: rn,
|
||||
SCODE: code,
|
||||
SNAME: name,
|
||||
SRRPROW: rowCode,
|
||||
SRRPVERSION_ROW: rowVersion,
|
||||
SRRPCOLUMN: columnCode,
|
||||
SRRPVERSION_COLUMN: columnVersion
|
||||
},
|
||||
loader: false
|
||||
});
|
||||
cb && cb(true);
|
||||
refreshMarks();
|
||||
} catch {
|
||||
cb && cb(false);
|
||||
}
|
||||
};
|
||||
|
||||
//Удаление показателя
|
||||
const deleteMark = async mark => {
|
||||
await executeStored({
|
||||
stored: "PKG_P8PANELS_RRPCONFED.RRPCONFSCTNMRK_DELETE",
|
||||
args: { NRN: mark },
|
||||
loader: false
|
||||
});
|
||||
refreshMarks();
|
||||
};
|
||||
|
||||
//Изменение сортировки
|
||||
const changeOrder = order => setOrder(order);
|
||||
|
||||
//Сброс ранее выставленного флага "сокрытия" (отмонтирования) компонента с показателями (так он не "мигает", когда происходит не смена раздела, в работа внутри него с показателями)
|
||||
useEffect(() => {
|
||||
if (hideMarksThenLoading) setHideMarksThenLoading(false);
|
||||
}, [hideMarksThenLoading]);
|
||||
|
||||
//При смене раздела - выставим флаг "спрятать" (отмонтировать) компонент с показателями (так он корректно полностью обновляется)
|
||||
useEffect(() => {
|
||||
if (section) setHideMarksThenLoading(true);
|
||||
}, [section]);
|
||||
|
||||
//Формирование представления
|
||||
return (
|
||||
<Box sx={STYLES.CONTAINER}>
|
||||
{section ? (
|
||||
<Box sx={STYLES.BOX_MARKS}>
|
||||
{((hideMarksThenLoading && !marksLoading) || !hideMarksThenLoading) && (
|
||||
<Marks
|
||||
marks={marks}
|
||||
order={order}
|
||||
marksLoading={marksLoading}
|
||||
marksInit={marksInit}
|
||||
onRefresh={refreshMarks}
|
||||
onMarkInsert={insertMark}
|
||||
onMarkUpdate={updateMark}
|
||||
onMarkDelete={deleteMark}
|
||||
onOrderChange={changeOrder}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
) : (
|
||||
<ActionMessage icon={"info"} title={"Раздел настройки не выбран"} desc={"Укажите его, выбрав закладку сверху"} />
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Раздел настройки
|
||||
Section.propTypes = {
|
||||
section: PropTypes.number
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { Section };
|
89
app/panels/rrp_conf_editor/components/section_tab.js
Normal file
89
app/panels/rrp_conf_editor/components/section_tab.js
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга - РО - Редактор настройки регламентированного отчёта
|
||||
Компонент панели: Закладка раздела
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import React, { useState } from "react"; //Классы React
|
||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||
import { Tab, IconButton, Icon, Stack } from "@mui/material"; //Интерфейсные компоненты
|
||||
import { STYLES as COMMON_STYLES } from "../common"; //Общие стили и константы
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Стили
|
||||
const STYLES = {
|
||||
SECTION_TAB: { minWidth: "150px" },
|
||||
SECTION_TAB_LABEL: { width: "100%", height: "100%" },
|
||||
SECTION_TAB_TOOLBAR_STACK: { ...COMMON_STYLES.TOOLBAR, height: "100%", width: "100%" }
|
||||
};
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Закладка раздела
|
||||
const SectionTab = ({ value = false, section, sectionDesc, onSectionEdit, onSectionDelete, ...other }) => {
|
||||
//Флаг нахождения указателя мыши в закладке
|
||||
const [hoveredSection, setHoveredSection] = useState(false);
|
||||
|
||||
//При попадании мыши на закладку раздела
|
||||
const handleSectionTabMouseIn = () => setHoveredSection(true);
|
||||
|
||||
//При выходе мыши из закладки раздела
|
||||
const handleSectionTabMouseOut = () => setHoveredSection(false);
|
||||
|
||||
//При редактировании раздела настройки
|
||||
const handleSectionEdit = () => onSectionEdit && onSectionEdit(sectionDesc.NRN);
|
||||
|
||||
//При удалении раздела настройки
|
||||
const handleSectionDelete = () => onSectionDelete && onSectionDelete(sectionDesc.NRN);
|
||||
|
||||
//Формирование представления
|
||||
return (
|
||||
<Tab
|
||||
component={"div"}
|
||||
wrapped
|
||||
value={value}
|
||||
onMouseEnter={handleSectionTabMouseIn}
|
||||
onMouseLeave={handleSectionTabMouseOut}
|
||||
sx={STYLES.SECTION_TAB}
|
||||
label={
|
||||
<Stack direction={"row"} alignItems={"center"} textAlign={"left"} sx={STYLES.SECTION_TAB_LABEL} title={sectionDesc.SNAME}>
|
||||
{`${sectionDesc.SCODE} - ${sectionDesc.SNAME_SHORT}`}
|
||||
{section === sectionDesc.NRN && hoveredSection && (
|
||||
<Stack direction={"row"} alignItems={"center"} justifyContent={"right"} sx={STYLES.SECTION_TAB_TOOLBAR_STACK} p={1}>
|
||||
<IconButton onClick={handleSectionEdit} title={"Редактировать раздел"}>
|
||||
<Icon>edit</Icon>
|
||||
</IconButton>
|
||||
<IconButton disabled={sectionDesc.NDELETE_ALLOW === 0} onClick={handleSectionDelete} title={"Удалить раздел"}>
|
||||
<Icon>delete</Icon>
|
||||
</IconButton>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
}
|
||||
{...other}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Закладка раздела
|
||||
SectionTab.propTypes = {
|
||||
value: PropTypes.any,
|
||||
section: PropTypes.number,
|
||||
sectionDesc: PropTypes.object.isRequired,
|
||||
onSectionEdit: PropTypes.func,
|
||||
onSectionDelete: PropTypes.func
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { SectionTab };
|
@ -1,50 +0,0 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга - РО - Редактор настройки регламентированного отчёта
|
||||
Панель мониторинга: Компонент вкладки раздела
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import React from "react"; //Классы React
|
||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||
import { Box } from "@mui/material"; //Интерфейсные компоненты
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
const STYLES = {
|
||||
SECTION_INFO: {
|
||||
padding: "24px 5px 0px 5px"
|
||||
}
|
||||
};
|
||||
|
||||
//---------------
|
||||
//Тело компонента
|
||||
//---------------
|
||||
|
||||
const SectionTabPanel = props => {
|
||||
const { children, value, index, ...other } = props;
|
||||
|
||||
//Генерация содержимого
|
||||
return (
|
||||
<div role="tabpanel" hidden={value !== index} id={`tabpanel-${index}`} aria-labelledby={`tab-${index}`} {...other}>
|
||||
{value === index && <Box sx={STYLES.SECTION_INFO}>{children}</Box>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Вкладка раздела
|
||||
SectionTabPanel.propTypes = {
|
||||
children: PropTypes.node,
|
||||
index: PropTypes.number.isRequired,
|
||||
value: PropTypes.number.isRequired
|
||||
};
|
||||
|
||||
//--------------------
|
||||
//Интерфейс компонента
|
||||
//--------------------
|
||||
|
||||
export { SectionTabPanel };
|
207
app/panels/rrp_conf_editor/components/sections.js
Normal file
207
app/panels/rrp_conf_editor/components/sections.js
Normal file
@ -0,0 +1,207 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга - РО - Редактор настройки регламентированного отчёта
|
||||
Компонент панели: Разделы настройки
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import React, { useState, useEffect, useContext, useCallback } from "react"; //Классы React
|
||||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||||
import { Box, Tabs, IconButton, Icon, Stack, Button } from "@mui/material"; //Интерфейсные компоненты
|
||||
import { tabsClasses } from "@mui/material/Tabs"; //Классы закладок
|
||||
import { ApplicationСtx } from "../../../context/application"; //Контекст взаимодействия с приложением
|
||||
import { BackEndСtx } from "../../../context/backend"; //Контекст взаимодействия с сервером
|
||||
import { MessagingСtx } from "../../../context/messaging"; //Контекст сообщений
|
||||
import { useConfSections } from "../hooks"; //Кастомные хуки
|
||||
import { ActionMessage } from "./action_message"; //Сообщение с действиями
|
||||
import { SectionTab } from "./section_tab"; //Закладка раздела
|
||||
import { DialogSectionIU } from "./dialog_section_iu"; //Диалог добавления/исправления раздела
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Стили
|
||||
const STYLES = {
|
||||
CONTAINER: { borderBottom: 1, borderColor: "divider", width: "100%", height: "100%" },
|
||||
TABS_SECTIONS: { width: "100%", [`& .${tabsClasses.scrollButtons}`]: { "&.Mui-disabled": { opacity: 0.3 } } }
|
||||
};
|
||||
|
||||
//-----------------------
|
||||
//Вспомогательные функции
|
||||
//-----------------------
|
||||
|
||||
//Поиск активного раздела после удаления текущего
|
||||
const getNextSectionAfterDelete = (sections, deletedSection) => {
|
||||
//Находим индекс удаляемого раздела
|
||||
const delInd = sections.findIndex(s => s.NRN === deletedSection);
|
||||
//Возвращаем рег. номер либо предыдущего раздела, либо следующего, либо ничего
|
||||
return delInd === -1 ? null : sections[delInd - 1]?.NRN || sections[delInd + 1]?.NRN || null;
|
||||
};
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Разделы настройки
|
||||
const Sections = ({ conf, onSectionChange, onSectionCountChange }) => {
|
||||
//Текущий раздел настройки
|
||||
const [section, setSection] = useState(-1);
|
||||
|
||||
//Редактируемый раздел настройки
|
||||
const [modSection, setModSection] = useState(null);
|
||||
|
||||
//Список разделов и просто описание настройки
|
||||
const [confDesc, sections, refreshSections, sectionsLoading, sectionsInit] = useConfSections(conf);
|
||||
|
||||
//Подключение к контексту приложения
|
||||
const { setAppBarTitle } = useContext(ApplicationСtx);
|
||||
|
||||
//Подключение к контексту взаимодействия с сервером
|
||||
const { executeStored } = useContext(BackEndСtx);
|
||||
|
||||
//Подключение к контексту сообщений
|
||||
const { showMsgWarn } = useContext(MessagingСtx);
|
||||
|
||||
//Выбор раздела
|
||||
const selectSection = useCallback(
|
||||
section => {
|
||||
if (onSectionChange) onSectionChange(section);
|
||||
setSection(section);
|
||||
},
|
||||
[onSectionChange]
|
||||
);
|
||||
|
||||
//Добавление раздела
|
||||
const insertSection = async ({ conf, code, name }) => {
|
||||
const data = await executeStored({
|
||||
stored: "PKG_P8PANELS_RRPCONFED.RRPCONFSCTN_INSERT",
|
||||
args: { NPRN: conf, SCODE: code, SNAME: name },
|
||||
loader: false
|
||||
});
|
||||
selectSection(data.NRN);
|
||||
refreshSections();
|
||||
};
|
||||
|
||||
//Исправление раздела
|
||||
const updateSection = async ({ rn, code, name }) => {
|
||||
await executeStored({
|
||||
stored: "PKG_P8PANELS_RRPCONFED.RRPCONFSCTN_UPDATE",
|
||||
args: { NRN: rn, SCODE: code, SNAME: name },
|
||||
loader: false
|
||||
});
|
||||
refreshSections();
|
||||
};
|
||||
|
||||
//Удаление раздела
|
||||
const deleteSection = async section => {
|
||||
await executeStored({
|
||||
stored: "PKG_P8PANELS_RRPCONFED.RRPCONFSCTN_DELETE",
|
||||
args: { NRN: section },
|
||||
loader: false
|
||||
});
|
||||
selectSection(getNextSectionAfterDelete(sections, section));
|
||||
refreshSections();
|
||||
};
|
||||
|
||||
//При измении закладки текущего раздела
|
||||
const handleSectionTabChange = (event, section) => selectSection(section);
|
||||
|
||||
//При добавлении раздела настройки
|
||||
const handleSectionAdd = () => setModSection(true);
|
||||
|
||||
//При редактировании раздела настройки
|
||||
const handleSectionEdit = section => setModSection(sections.find(s => s.NRN === section) || null);
|
||||
|
||||
//При удалении раздела настройки
|
||||
const handleSectionDelete = section => showMsgWarn("Удалить раздел?", () => deleteSection(section));
|
||||
|
||||
//При закрытии формы добавления/исправления по "ОК"
|
||||
const handleIUFormOk = async values => {
|
||||
if (modSection === true) await insertSection({ conf, ...values });
|
||||
else await updateSection({ rn: modSection.NRN, ...values });
|
||||
setModSection(null);
|
||||
};
|
||||
|
||||
//При закрытии формы добавления/исправления по "Отмена"
|
||||
const handleIUFormCancel = () => setModSection(null);
|
||||
|
||||
//При изменении состава разделов
|
||||
useEffect(() => {
|
||||
//Если ещё не инициализировали выбранный раздел и есть чем
|
||||
if (section === -1 && sections.length > 0) selectSection(sections[0].NRN);
|
||||
}, [section, sections, selectSection]);
|
||||
|
||||
//При изменении количества разделов
|
||||
useEffect(() => {
|
||||
onSectionCountChange && onSectionCountChange(sections.length);
|
||||
}, [sections.length, onSectionCountChange]);
|
||||
|
||||
//При изменении описания раздела
|
||||
useEffect(() => {
|
||||
if (confDesc?.SNAME) setAppBarTitle(confDesc.SNAME);
|
||||
}, [confDesc, setAppBarTitle]);
|
||||
|
||||
//Вычисление подсвеченной закладки раздела
|
||||
const hlSection = sections.find(s => s.NRN === section)?.NRN || false;
|
||||
|
||||
//Формирование представления
|
||||
return (
|
||||
<Stack direction={"row"} sx={STYLES.CONTAINER}>
|
||||
{modSection && (
|
||||
<DialogSectionIU
|
||||
code={modSection?.SCODE}
|
||||
name={modSection?.SNAME}
|
||||
insert={modSection === true}
|
||||
onOk={handleIUFormOk}
|
||||
onCancel={handleIUFormCancel}
|
||||
/>
|
||||
)}
|
||||
{sections.length > 0 ? (
|
||||
<>
|
||||
<Box display={"flex"} justifyContent={"center"} alignItems={"center"} sx={STYLES.PANELS_MAIN_COLOR} title={"Добавить раздел"}>
|
||||
<IconButton onClick={handleSectionAdd}>
|
||||
<Icon>add</Icon>
|
||||
</IconButton>
|
||||
</Box>
|
||||
<Tabs value={hlSection} onChange={handleSectionTabChange} variant={"scrollable"} scrollButtons sx={STYLES.TABS_SECTIONS}>
|
||||
{sections.map((s, i) => (
|
||||
<SectionTab
|
||||
key={i}
|
||||
value={s.NRN}
|
||||
section={section}
|
||||
sectionDesc={s}
|
||||
onSectionEdit={handleSectionEdit}
|
||||
onSectionDelete={handleSectionDelete}
|
||||
/>
|
||||
))}
|
||||
</Tabs>
|
||||
</>
|
||||
) : (
|
||||
sectionsInit &&
|
||||
!sectionsLoading && (
|
||||
<ActionMessage icon={"info"} title={"В настройке нет разделов"} desc={"Добавьте первый..."}>
|
||||
<Button startIcon={<Icon>add</Icon>} onClick={handleSectionAdd}>
|
||||
Раздел
|
||||
</Button>
|
||||
</ActionMessage>
|
||||
)
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
//Контроль свойств - Разделы настройки
|
||||
Sections.propTypes = {
|
||||
conf: PropTypes.number,
|
||||
onSectionChange: PropTypes.func,
|
||||
onSectionCountChange: PropTypes.func
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { Sections };
|
@ -7,403 +7,209 @@
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import { useState, useContext, useEffect, useCallback, useLayoutEffect } from "react"; //Классы React
|
||||
import { useState, useContext, useEffect } from "react"; //Классы React
|
||||
import { xml2JSON } from "../../core/utils"; //Вспомогательные функции
|
||||
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
||||
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
||||
import { NavigationCtx } from "../../context/navigation"; //Контекст навигации
|
||||
import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений
|
||||
import { STATUSES } from "./IUD/iud_form_dialog"; //Статусы диалогов
|
||||
import { TEXTS } from "../../../app.text"; //Тексты для ошибок
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Хук для отработки изменений ширины и высоты рабочей области окна
|
||||
const useWindowResize = () => {
|
||||
//Состояние размера рабочей области
|
||||
const [size, setSize] = useState([0, 0]);
|
||||
//Получение данных о разделах настройки РО
|
||||
const useConfSections = conf => {
|
||||
//Собственное состояние - флаг инициализированности
|
||||
const [isInit, setInit] = useState(false);
|
||||
|
||||
//При изменении размера
|
||||
useLayoutEffect(() => {
|
||||
const updateSize = () => {
|
||||
setSize([document.documentElement.clientWidth, document.documentElement.clientHeight]);
|
||||
};
|
||||
window.addEventListener("resize", updateSize);
|
||||
updateSize();
|
||||
return () => window.removeEventListener("resize", updateSize);
|
||||
}, []);
|
||||
//Собственное состояние - флаг загрузки
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
|
||||
//Вернём размеры
|
||||
return size;
|
||||
};
|
||||
//Собственное состояние - флаг необходимости обновления
|
||||
const [refresh, setRefresh] = useState(true);
|
||||
|
||||
//Хук для настройки регламентированного отчета
|
||||
const useConf = (currentTab, handleSectionChange, order) => {
|
||||
//Собственное состояние - таблица данных
|
||||
const dataGrid = {
|
||||
rn: 0,
|
||||
code: "",
|
||||
name: "",
|
||||
dataLoaded: false,
|
||||
columnsDef: [],
|
||||
groups: [],
|
||||
rows: [],
|
||||
fixedHeader: false,
|
||||
fixedColumns: 0,
|
||||
reload: false
|
||||
};
|
||||
//Собственное состояние - данные настройки
|
||||
const [dataConf, setDataConf] = useState(null);
|
||||
|
||||
//Собственное состояние
|
||||
const [rrpConf, setRrpConf] = useState({
|
||||
docLoaded: false,
|
||||
sections: [],
|
||||
orderChanged: false,
|
||||
reload: true
|
||||
});
|
||||
|
||||
//Состояние массива данных разделов
|
||||
const [dataGrids] = useState([]);
|
||||
//Собственное состояние - данные разделов
|
||||
const [dataSections, setDataSections] = useState([]);
|
||||
|
||||
//Подключение к контексту взаимодействия с сервером
|
||||
const { executeStored } = useContext(BackEndСtx);
|
||||
|
||||
//Подключение к контексту навигации
|
||||
const { getNavigationSearch } = useContext(NavigationCtx);
|
||||
//Обновление данных
|
||||
const doRefresh = () => setRefresh(true);
|
||||
|
||||
//При необходимости обновить
|
||||
const handleReload = useCallback(async () => {
|
||||
setRrpConf(pv => ({ ...pv, reload: true }));
|
||||
}, []);
|
||||
|
||||
//Загрузка данных разделов регламентированного отчёта
|
||||
const loadData = useCallback(
|
||||
async () => {
|
||||
if (rrpConf.reload) {
|
||||
//Переменная номера раздела с фокусом
|
||||
let tabFocus = currentTab ? currentTab : 0;
|
||||
//При необходимости получить/обновить данные
|
||||
useEffect(() => {
|
||||
//Загрузка данных с сервера
|
||||
const loadData = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const data = await executeStored({
|
||||
stored: "PKG_P8PANELS_RRPCONFED.RRPCONF_GET_SECTIONS",
|
||||
args: {
|
||||
NRN_RRPCONF: Number(getNavigationSearch().NRN),
|
||||
NROW_ORDER: Number(order.rowOrder),
|
||||
NCOL_ORDER: Number(order.columnOrder)
|
||||
},
|
||||
stored: "PKG_P8PANELS_RRPCONFED.RRPCONF_GET",
|
||||
args: { NRRPCONF: conf },
|
||||
respArg: "COUT",
|
||||
isArray: name => name === "XSECTIONS",
|
||||
attributeValueProcessor: (name, val) => (name.startsWith("S") ? undefined : val),
|
||||
loader: false
|
||||
});
|
||||
setDataConf(data?.XCONF || null);
|
||||
setDataSections(data?.XSECTIONS || []);
|
||||
setInit(true);
|
||||
} finally {
|
||||
setRefresh(false);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
//Если надо обновить
|
||||
if (refresh)
|
||||
if (conf)
|
||||
//Если есть для чего получать данные
|
||||
loadData();
|
||||
//Нет идентификатора настройки - нет данных
|
||||
else {
|
||||
setDataConf(null);
|
||||
setDataSections([]);
|
||||
}
|
||||
}, [refresh, conf, executeStored]);
|
||||
|
||||
//При изменении входных свойств - поднимаем флаг обновления
|
||||
useEffect(() => setRefresh(true), [conf]);
|
||||
|
||||
//Возвращаем интерфейс хука
|
||||
return [dataConf, dataSections, doRefresh, isLoading, isInit];
|
||||
};
|
||||
|
||||
//Получение данных о показателях раздела настройки РО
|
||||
const useConfSectionMarks = (section, rowOrder = 0, columnOrder = 0) => {
|
||||
//Собственное состояние - флаг инициализированности
|
||||
const [isInit, setInit] = useState(false);
|
||||
|
||||
//Собственное состояние - флаг загрузки
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
|
||||
//Собственное состояние - флаг необходимости обновления
|
||||
const [refresh, setRefresh] = useState(true);
|
||||
|
||||
//Собственное состояние - данные
|
||||
const [data, setData] = useState(null);
|
||||
|
||||
//Подключение к контексту взаимодействия с сервером
|
||||
const { executeStored } = useContext(BackEndСtx);
|
||||
|
||||
//Обновление данных
|
||||
const doRefresh = () => setRefresh(true);
|
||||
|
||||
//При необходимости получить/обновить данные
|
||||
useEffect(() => {
|
||||
//Загрузка данных с сервера
|
||||
const loadData = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const data = await executeStored({
|
||||
stored: "PKG_P8PANELS_RRPCONFED.RRPCONFSCTN_GET",
|
||||
args: { NRRPCONFSCTN: section, NROW_ORDER: rowOrder, NCOL_ORDER: columnOrder },
|
||||
respArg: "COUT"
|
||||
});
|
||||
//Флаг первой загрузки данных
|
||||
let firstLoad = dataGrids.length == 0 ? true : false;
|
||||
//Копирование массива уже загруженных разделов
|
||||
let cloneDGs = dataGrids.slice();
|
||||
//Массив из нескольких разделов и из одного
|
||||
const sections = data.SECTIONS ? (data.SECTIONS.length ? data.SECTIONS : [data.SECTIONS]) : [];
|
||||
//Заполнение очередного раздела по шаблону
|
||||
sections
|
||||
.sort((a, b) => a.SCODE - b.SCODE)
|
||||
.map(s => {
|
||||
let dg = {};
|
||||
Object.assign(dg, dataGrid, {
|
||||
...s.XDATA.XDATA_GRID,
|
||||
rn: s.NRN,
|
||||
code: s.SCODE,
|
||||
name: s.SNAME,
|
||||
delete_allow: s.NDELETE_ALLOW,
|
||||
dataLoaded: true,
|
||||
columnsDef: [...(s.XDATA.XDATA_GRID.columnsDef || [])],
|
||||
groups: [...(s.XDATA.XDATA_GRID.groups || [])],
|
||||
rows: [...(s.XDATA.XDATA_GRID.rows || [])],
|
||||
reload: false
|
||||
});
|
||||
//Если раздел имеет составы показателей
|
||||
if (s.MARK_CNS.MARK_CN) {
|
||||
//Обходим строки раздела
|
||||
dg.rows.map(row => {
|
||||
//Цикл по ключам строки
|
||||
for (let key in row) {
|
||||
//Если это ключ для группы составов показателей
|
||||
if (key.match(/MARK_CNS_.*/)) {
|
||||
//Считываем рег. номер показателя
|
||||
let markRn = key.substring(9);
|
||||
//Переносим из раздела
|
||||
row[key] = Array.isArray(s.MARK_CNS.MARK_CN)
|
||||
? [...s.MARK_CNS.MARK_CN].filter(el => el.NPRN === row[`NMARK_RN_${markRn}`])
|
||||
: s.MARK_CNS.MARK_CN.NPRN === row[`NMARK_RN_${markRn}`]
|
||||
? [s.MARK_CNS.MARK_CN]
|
||||
: null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
//Ищем загружен ли уже раздел с таким же ид.
|
||||
const dgItem = dataGrids.find(x => x.rn === dg.rn);
|
||||
//Его индекс, если нет соответствия, то -1
|
||||
let index = dataGrids.indexOf(dgItem);
|
||||
//Если было соответствие
|
||||
if (dgItem) {
|
||||
//Если в нём не найдено изменений
|
||||
if (JSON.stringify(dgItem, null, 4) === JSON.stringify(dg, null, 4)) {
|
||||
//То из копированного массива его удаляем
|
||||
cloneDGs.splice(cloneDGs.indexOf(cloneDGs.find(x => x.rn === dgItem.rn)), 1);
|
||||
} else {
|
||||
//Иначе обновляем раздел в массиве
|
||||
dataGrids[index] = dg;
|
||||
//Удаляем из копированного массива
|
||||
cloneDGs.splice(cloneDGs.indexOf(cloneDGs.find(x => x.rn === dg.rn)), 1);
|
||||
//Устанавливаем фокус на обновлённый раздел, если был добавлен
|
||||
tabFocus = rrpConf.orderChanged ? 0 : index;
|
||||
}
|
||||
} else {
|
||||
//Если раздел новый, то добавляем его в массив данных
|
||||
dataGrids.push(dg);
|
||||
//И устанавливаем на него фокус, если флаг первой загрузки = false
|
||||
tabFocus = !firstLoad ? dataGrids.length - 1 : 0;
|
||||
}
|
||||
});
|
||||
//Обходим разделы, что остались в копированном массиве (на удаление)
|
||||
cloneDGs.map(s => {
|
||||
let curIndex = dataGrids.indexOf(dataGrids.find(x => x.rn === s.rn));
|
||||
//Устаревший раздел удаляем из массива данных
|
||||
dataGrids.splice(curIndex, 1);
|
||||
//Фокус на предшествующий раздел
|
||||
if (curIndex > 0) tabFocus = curIndex - 1;
|
||||
//Иначе фокус на следующий, если был удалён первый раздел
|
||||
else tabFocus = curIndex;
|
||||
});
|
||||
setRrpConf(pv => ({
|
||||
...pv,
|
||||
docLoaded: true,
|
||||
orderChanged: false,
|
||||
reload: false,
|
||||
sections: dataGrids
|
||||
}));
|
||||
handleSectionChange(tabFocus);
|
||||
if (data) {
|
||||
for (let i = 0; i < data?.XDATA_GRID?.rows?.length; i++)
|
||||
for (const key of Object.keys(data.XDATA_GRID.rows[i]))
|
||||
if (key.startsWith("SCOL_"))
|
||||
data.XDATA_GRID.rows[i][key] = (
|
||||
await xml2JSON({
|
||||
xmlDoc: data.XDATA_GRID.rows[i][key],
|
||||
isArray: name => name === "CONSTITUTION",
|
||||
attributeValueProcessor: (name, val) => (name.startsWith("S") ? undefined : val)
|
||||
})
|
||||
).XMARK;
|
||||
setData(data?.XDATA_GRID || null);
|
||||
} else setData(null);
|
||||
setInit(true);
|
||||
} finally {
|
||||
setRefresh(false);
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[rrpConf.reload, rrpConf.docLoaded, dataGrid.reload, dataGrid.docLoaded, executeStored]
|
||||
);
|
||||
};
|
||||
//Если надо обновить
|
||||
if (refresh)
|
||||
if (section)
|
||||
//Если есть для чего получать данные
|
||||
loadData();
|
||||
//Нет идентификатора раздела настройки - нет данных
|
||||
else setData(null);
|
||||
}, [refresh, section, rowOrder, columnOrder, executeStored]);
|
||||
|
||||
//При изменении сортировок
|
||||
useEffect(() => {
|
||||
setRrpConf(pv => ({ ...pv, orderChanged: true, reload: true }));
|
||||
}, [order]);
|
||||
//При изменении входных свойств - поднимаем флаг обновления
|
||||
useEffect(() => setRefresh(true), [section, rowOrder, columnOrder]);
|
||||
|
||||
//При необходимости обновить данные таблицы
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
}, [rrpConf.reload, dataGrid.reload, loadData]);
|
||||
//При изменении раздела - сбрасываем флаг инициализированности (так карточки показателей грузятся красивее - таблица исчезает и появляется уже загруженная)
|
||||
useEffect(() => setInit(false), [section]);
|
||||
|
||||
return [rrpConf, handleReload];
|
||||
//Возвращаем интерфейс хука
|
||||
return [data, doRefresh, isLoading, isInit];
|
||||
};
|
||||
|
||||
//Хук для вкладки
|
||||
const useTab = () => {
|
||||
//Состояние раздела
|
||||
const [tabValue, setTabValue] = useState("");
|
||||
|
||||
//Переключение раздела
|
||||
const handleSectionChange = useCallback(newValue => {
|
||||
setTabValue(newValue);
|
||||
}, []);
|
||||
|
||||
return [tabValue, handleSectionChange];
|
||||
};
|
||||
|
||||
//Хук для функций открытия записей
|
||||
const useRecOpen = handleReload => {
|
||||
//Подключение к контексту взаимодействия с сервером
|
||||
const { executeStored } = useContext(BackEndСtx);
|
||||
|
||||
//Работа со словарями
|
||||
const useDictionary = () => {
|
||||
//Подключение к контексту приложения
|
||||
const { pOnlineShowDictionary } = useContext(ApplicationСtx);
|
||||
|
||||
//Подключение к контексту сообщений
|
||||
const { showMsgErr } = useContext(MessagingСtx);
|
||||
|
||||
//Отображение показателя раздела
|
||||
const handleMarkOpen = useCallback(
|
||||
async nRrpConfSctnMrk => {
|
||||
const data = await executeStored({
|
||||
stored: "PKG_P8PANELS_RRPCONFED.RRPCONFSCTNMRK_GET_CODES",
|
||||
args: {
|
||||
NRN: nRrpConfSctnMrk
|
||||
},
|
||||
tagValueProcessor: () => undefined
|
||||
});
|
||||
if (data) {
|
||||
pOnlineShowDictionary({
|
||||
unitCode: "RRPConfig",
|
||||
showMethod: "main_mrk_settings",
|
||||
inputParameters: [
|
||||
{ name: "in_CODE", value: data.SRRPCONF },
|
||||
{ name: "in_SCTN_CODE", value: data.SRRPCONFSCTN },
|
||||
{ name: "in_MRK_CODE", value: data.SRRPCONFSCTNMRK }
|
||||
],
|
||||
callBack: res => {
|
||||
res.success ? handleReload() : null;
|
||||
}
|
||||
});
|
||||
} else showMsgErr(TEXTS.NO_DATA_FOUND);
|
||||
},
|
||||
[executeStored, handleReload, pOnlineShowDictionary, showMsgErr]
|
||||
);
|
||||
|
||||
//Отображение показателя раздела
|
||||
const handleMarkCnOpen = useCallback(
|
||||
async (nRrpConfSctnMrk, nRrpConfSctnMrkCn) => {
|
||||
pOnlineShowDictionary({
|
||||
unitCode: "RRPConfigSectionMark",
|
||||
showMethod: "link_cn",
|
||||
inputParameters: [
|
||||
{ name: "in_RN", value: nRrpConfSctnMrk },
|
||||
{ name: "in_RRPCONFSCTNMRKCN", value: nRrpConfSctnMrkCn }
|
||||
],
|
||||
callBack: res => {
|
||||
res.success ? handleReload() : null;
|
||||
}
|
||||
});
|
||||
},
|
||||
[handleReload, pOnlineShowDictionary]
|
||||
);
|
||||
|
||||
//Отображение показателя раздела
|
||||
const handleMarkCnInsert = useCallback(
|
||||
async nRrpConfSctnMrk => {
|
||||
pOnlineShowDictionary({
|
||||
unitCode: "RRPConfigSectionMarkConstitution",
|
||||
showMethod: "link_add",
|
||||
inputParameters: [{ name: "in_PRN", value: nRrpConfSctnMrk }],
|
||||
callBack: res => {
|
||||
res.success ? handleReload() : null;
|
||||
}
|
||||
});
|
||||
},
|
||||
[handleReload, pOnlineShowDictionary]
|
||||
);
|
||||
|
||||
return [handleMarkOpen, handleMarkCnOpen, handleMarkCnInsert];
|
||||
};
|
||||
|
||||
//Хук для форм диалогового окна
|
||||
const useFormDialog = () => {
|
||||
//Состояние открытия диалогового окна
|
||||
const [formOpen, setForm] = useState(false);
|
||||
|
||||
//Состояние диалогового окна
|
||||
const [formData, setFormData] = useState({
|
||||
reload: false,
|
||||
rn: "",
|
||||
prn: "",
|
||||
sctnName: "",
|
||||
sctnCode: "",
|
||||
status: "",
|
||||
code: "",
|
||||
name: "",
|
||||
colCode: "",
|
||||
colRn: null,
|
||||
rowCode: "",
|
||||
rowRn: null
|
||||
});
|
||||
|
||||
//Подключение к контексту навигации
|
||||
const { getNavigationSearch } = useContext(NavigationCtx);
|
||||
|
||||
//Открытие диалогового окна
|
||||
const openForm = () => {
|
||||
setForm(true);
|
||||
};
|
||||
|
||||
//Очистка диалогового окна
|
||||
const clearFormData = () => {
|
||||
setFormData({
|
||||
reload: false,
|
||||
rn: "",
|
||||
prn: "",
|
||||
sctnName: "",
|
||||
sctnCode: "",
|
||||
status: "",
|
||||
code: "",
|
||||
name: "",
|
||||
colCode: "",
|
||||
colRn: null,
|
||||
rowCode: "",
|
||||
rowRn: null
|
||||
//Выбор строки
|
||||
const selectRRPRow = (code, version, callBack) => {
|
||||
pOnlineShowDictionary({
|
||||
unitCode: "RRPRow",
|
||||
inputParameters: [
|
||||
{ name: "in_CODE", value: code },
|
||||
{ name: "in_RRPVERSION_CODE", value: version }
|
||||
],
|
||||
callBack: res =>
|
||||
callBack(res.success === true ? { code: res.outParameters.out_CODE, version: res.outParameters.out_RRPVERSION_CODE } : null)
|
||||
});
|
||||
};
|
||||
|
||||
//Отработка нажатия на кнопку добавления секции
|
||||
const handleSectionAdd = () => {
|
||||
setFormData({ status: STATUSES.CREATE, prn: Number(getNavigationSearch().NRN) });
|
||||
openForm();
|
||||
};
|
||||
|
||||
//Отработка нажатия на кнопку исправления секции
|
||||
const handleSectionEdit = (rn, code, name) => {
|
||||
setFormData({ rn: rn, code: code, name: name, status: STATUSES.EDIT });
|
||||
openForm();
|
||||
};
|
||||
|
||||
//Отработка нажатия на кнопку удаления секции
|
||||
const handleSectionDelete = (rn, code, name) => {
|
||||
setFormData({ rn: rn, code: code, name: name, status: STATUSES.DELETE });
|
||||
openForm();
|
||||
};
|
||||
|
||||
//Отработка нажатия на кнопку добавления показателя раздела
|
||||
const handleMarkAdd = (prn, rowRn = null, rowCode = "", colRn = null, colCode = "") => {
|
||||
setFormData({
|
||||
reload: rowRn && colRn ? true : false,
|
||||
prn: prn,
|
||||
rowRn: rowRn,
|
||||
rowCode: rowCode,
|
||||
colRn: colRn,
|
||||
colCode: colCode,
|
||||
status: STATUSES.RRPCONFSCTNMRK_CREATE
|
||||
//Выбор графы
|
||||
const selectRRPColumn = (code, version, callBack) => {
|
||||
pOnlineShowDictionary({
|
||||
unitCode: "RRPColumn",
|
||||
inputParameters: [
|
||||
{ name: "in_CODE", value: code },
|
||||
{ name: "in_RRPVERSION_CODE", value: version }
|
||||
],
|
||||
callBack: res =>
|
||||
callBack(res.success === true ? { code: res.outParameters.out_CODE, version: res.outParameters.out_RRPVERSION_CODE } : null)
|
||||
});
|
||||
openForm();
|
||||
};
|
||||
|
||||
//Отработка нажатия на кнопку исправления показателя раздела
|
||||
const handleMarkEdit = rn => {
|
||||
setFormData({ reload: true, rn: rn, status: STATUSES.RRPCONFSCTNMRK_EDIT });
|
||||
openForm();
|
||||
};
|
||||
//Отображение показателя раздела
|
||||
const showMark = (mark, callBack) => showMarkCn(mark, null, callBack);
|
||||
|
||||
//Отработка нажатия на кнопку удаления показателя раздела
|
||||
const handleMarkDelete = rn => {
|
||||
setFormData({ rn: rn, status: STATUSES.RRPCONFSCTNMRK_DELETE });
|
||||
openForm();
|
||||
};
|
||||
//Отображение состава показателя раздела
|
||||
const showMarkCn = (mark, constitution, callBack) =>
|
||||
pOnlineShowDictionary({
|
||||
unitCode: "RRPConfigSectionMark",
|
||||
showMethod: "link_cn",
|
||||
inputParameters: [
|
||||
{ name: "in_RN", value: mark },
|
||||
{ name: "in_RRPCONFSCTNMRKCN", value: constitution }
|
||||
],
|
||||
callBack
|
||||
});
|
||||
|
||||
//При закрытии диалога
|
||||
const handleDialogClose = () => {
|
||||
setForm(false);
|
||||
clearFormData();
|
||||
};
|
||||
//Отображение состава показателя раздела с добавлением
|
||||
const showMarkCnAdd = (mark, callBack) =>
|
||||
pOnlineShowDictionary({
|
||||
unitCode: "RRPConfigSectionMarkConstitution",
|
||||
showMethod: "link_add",
|
||||
inputParameters: [{ name: "in_PRN", value: mark }],
|
||||
callBack
|
||||
});
|
||||
|
||||
return [
|
||||
formOpen,
|
||||
formData,
|
||||
handleSectionAdd,
|
||||
handleSectionEdit,
|
||||
handleSectionDelete,
|
||||
handleMarkAdd,
|
||||
handleMarkEdit,
|
||||
handleMarkDelete,
|
||||
handleDialogClose
|
||||
];
|
||||
};
|
||||
|
||||
//Формирование разделов
|
||||
const a11yProps = index => {
|
||||
return {
|
||||
id: `simple-tab-${index}`,
|
||||
"aria-controls": `simple-tabpanel-${index}`
|
||||
};
|
||||
//Возвращаем интерфейс хука
|
||||
return { selectRRPRow, selectRRPColumn, showMark, showMarkCn, showMarkCnAdd };
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
export { useWindowResize, useConf, useTab, useRecOpen, useFormDialog, a11yProps };
|
||||
export { useConfSections, useConfSectionMarks, useDictionary };
|
||||
|
91
app/panels/rrp_conf_editor/layouts.js
Normal file
91
app/panels/rrp_conf_editor/layouts.js
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга - РО - Редактор настройки регламентированного отчёта
|
||||
Дополнительная разметка и вёрстка клиентских элементов
|
||||
*/
|
||||
|
||||
//---------------------
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import React from "react"; //Классы React
|
||||
import { MarksToolbar } from "./components/marks_toolbar"; //Панель инструментов показателей
|
||||
import { MarkCard } from "./components/mark_card"; //Карточка показателя
|
||||
import { STYLES as COMMON_STYLES } from "./common"; //Общие стили и константы
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Стили
|
||||
export const STYLES = {
|
||||
HEAD_CELL: {
|
||||
textAlign: "center",
|
||||
fontWeight: "bold",
|
||||
lineHeight: "unset",
|
||||
...COMMON_STYLES.FONT_DATA_GRID,
|
||||
...COMMON_STYLES.BG_DATA_GRID_HEAD_CELL,
|
||||
...COMMON_STYLES.BORDER_DATA_GRID_HEAD_CELL
|
||||
},
|
||||
HEAD_CELL_STACK: { justifyContent: "center" },
|
||||
DATA_CELL: isMarkRowHead => ({
|
||||
padding: "5px 5px",
|
||||
textAlign: "center",
|
||||
...COMMON_STYLES.FONT_DATA_GRID,
|
||||
...(isMarkRowHead
|
||||
? { ...COMMON_STYLES.BG_DATA_GRID_DATA_CELL, ...COMMON_STYLES.BORDER_DATA_GRID_HEAD_CELL, fontWeight: "bold" }
|
||||
: COMMON_STYLES.BORDER_DATA_GRID_DATA_CELL)
|
||||
})
|
||||
};
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Генерация представления ячейки c данными показателя раздела регламентированного отчета
|
||||
export const confSctnMrkCellRender = ({ row, columnDef, onMarkAdd, onMarkUpdate, onMarkDelete, onMarkOpen, onMarkCnOpen, onMarkCnAdd }) => {
|
||||
//Считываем информацию о показателе
|
||||
const mark = {
|
||||
rn: row[columnDef.name]?.NRN,
|
||||
code: row[columnDef.name]?.SCODE,
|
||||
name: row[columnDef.name]?.SNAME,
|
||||
row: row[columnDef.name]?.NRRPROW,
|
||||
rowCode: row[columnDef.name]?.SRRPROW,
|
||||
rowVersion: row[columnDef.name]?.SRRPVERSION_ROW,
|
||||
column: row[columnDef.name]?.NRRPCOLUMN,
|
||||
columnCode: row[columnDef.name]?.SRRPCOLUMN,
|
||||
columnVersion: row[columnDef.name]?.SRRPVERSION_COLUMN,
|
||||
constitution: row[columnDef.name]?.CONSTITUTION
|
||||
};
|
||||
//Вернём представление ячейки
|
||||
return {
|
||||
cellStyle: STYLES.DATA_CELL(columnDef.name === "SROW_NAME"),
|
||||
data: columnDef.name != "SROW_NAME" && (
|
||||
<MarkCard
|
||||
mark={mark.rn}
|
||||
code={mark.code}
|
||||
name={mark.name}
|
||||
constitution={mark.constitution}
|
||||
onMarkAdd={() => onMarkAdd && onMarkAdd(mark.row, mark.column)}
|
||||
onMarkUpdate={() => onMarkUpdate && onMarkUpdate({ ...mark })}
|
||||
onMarkDelete={() => onMarkDelete && onMarkDelete(mark.rn)}
|
||||
onMarkOpen={() => onMarkOpen && onMarkOpen(mark.rn)}
|
||||
onMarkCnOpen={constitutionRn => onMarkCnOpen && onMarkCnOpen(mark.rn, constitutionRn)}
|
||||
onMarkCnAdd={() => onMarkCnAdd && onMarkCnAdd(mark.rn)}
|
||||
/>
|
||||
)
|
||||
};
|
||||
};
|
||||
|
||||
//Генерация представления ячейки заголовка таблицы показателей
|
||||
export const confSctnMrkHeadCellRender = ({ columnDef, onAdd, onRefresh, onOrder, onHelp }) => {
|
||||
return {
|
||||
cellStyle: STYLES.HEAD_CELL,
|
||||
stackStyle: STYLES.HEAD_CELL_STACK,
|
||||
data:
|
||||
columnDef.name === "SROW_NAME" ? (
|
||||
<MarksToolbar onAdd={onAdd} onRefresh={onRefresh} onOrder={onOrder} onHelp={onHelp} />
|
||||
) : (
|
||||
columnDef.caption
|
||||
)
|
||||
};
|
||||
};
|
@ -7,32 +7,23 @@
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import React, { useState, useEffect } from "react"; //Классы React
|
||||
import { Box, Tab, Tabs, IconButton, Icon, Stack } from "@mui/material"; //Интерфейсные компоненты
|
||||
import { IUDFormDialog } from "./IUD/iud_form_dialog"; //Диалог добавления/исправления/удаления компонентов настройки регламентированного отчёта
|
||||
import { useWindowResize, useTab, useConf, useRecOpen, useFormDialog, a11yProps } from "./hooks"; //Пользовательские хуки
|
||||
import { SectionTab } from "./components/rrp_section"; //Компонент раздела настройки
|
||||
import React, { useState, useEffect, useContext } from "react"; //Классы React
|
||||
import { Grid } from "@mui/material"; //Интерфейсные компоненты
|
||||
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Рабочая область приложения
|
||||
import { NavigationCtx } from "../../context/navigation"; //Контекст навигации
|
||||
import { Sections } from "./components/sections"; //Список разделов настройки
|
||||
import { Section } from "./components/section"; //Раздел настройки
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Высота меню Парус (пиксели)
|
||||
const pxOuterMenuH = 53;
|
||||
//Высота заголовка панели (пиксели)
|
||||
const pxPanelHeaderH = 64;
|
||||
//Ширина кнопки добавления раздела (пиксели)
|
||||
const pxSectionAddButtonW = 40;
|
||||
|
||||
//Стили
|
||||
const STYLES = {
|
||||
CONTAINER: { width: "100%", minHeight: `calc(100vh - ${pxPanelHeaderH})`, maxHeight: `calc(100vh - ${pxPanelHeaderH})` },
|
||||
PANELS_MAIN_COLOR: { backgroundColor: "#1976d2" },
|
||||
ICON_WHITE: { color: "white" },
|
||||
TABS_BOTTOM_LINE: { borderBottom: 1, borderColor: "divider" },
|
||||
TABS_PADDING: { padding: "5px" },
|
||||
TABS_SIZES: (width, pxSectionAddButtonW) => ({ maxHeight: 150, maxWidth: width - pxSectionAddButtonW }),
|
||||
SECTION_ACTIONS: { display: "flex", justifyContent: "center", alignItems: "center" }
|
||||
GRID_MAIN_CONTAINER: { height: `calc(100vh - ${APP_BAR_HEIGHT})` },
|
||||
GRID_MAIN_SECTIONS_ITEM: fullHeight => ({ ...(fullHeight ? { height: "100%" } : {}) }),
|
||||
GRID_SECTIONS_CONTAINER: { width: "100vw", height: "100%" },
|
||||
GRID_SECTION_CONTAINER: { height: "100%" }
|
||||
};
|
||||
|
||||
//-----------
|
||||
@ -41,122 +32,46 @@ const STYLES = {
|
||||
|
||||
//Редактор настройки регламентированного отчёта
|
||||
const RrpConfEditor = () => {
|
||||
//Состояние вкладки
|
||||
const [tabValue, handleSectionChange] = useTab("");
|
||||
//Редактируемая настройка
|
||||
const [conf, setConf] = useState(null);
|
||||
|
||||
//Состояние сортировки строк и граф
|
||||
const [order, setOrder] = useState({ rowOrder: 0, columnOrder: 0 });
|
||||
//Текущий раздел настройки
|
||||
const [section, setSection] = useState(null);
|
||||
|
||||
//Изменение состояния сортировки строк и граф
|
||||
const handleOrder = newOrder => setOrder(newOrder);
|
||||
//Текущее количество разделов настройки
|
||||
const [sectionsCount, setSectionsCount] = useState(0);
|
||||
|
||||
//Состояние настройки
|
||||
const [rrpConf, handleReload] = useConf(tabValue, handleSectionChange, order);
|
||||
//Подключение к контексту навигации
|
||||
const { getNavigationSearch } = useContext(NavigationCtx);
|
||||
|
||||
//Функции открытия разделов
|
||||
const [handleMarkOpen, handleMarkCnOpen, handleMarkCnInsert] = useRecOpen(handleReload);
|
||||
//Изменение текущего раздела настройки
|
||||
const handleSectionChange = section => setSection(section);
|
||||
|
||||
//Состояние форм диалога
|
||||
const [
|
||||
formOpen,
|
||||
formData,
|
||||
handleSectionAdd,
|
||||
handleSectionEdit,
|
||||
handleSectionDelete,
|
||||
handleMarkAdd,
|
||||
handleMarkEdit,
|
||||
handleMarkDelete,
|
||||
handleDialogClose
|
||||
] = useFormDialog();
|
||||
//Изменение количества разделов настройки
|
||||
const handleSectionsCountChange = sectionsCount => setSectionsCount(sectionsCount);
|
||||
|
||||
//Состояние ширины и высоты рабочей области окна
|
||||
const [width, height] = useWindowResize();
|
||||
|
||||
//Состояние высоты вкладок с разделами
|
||||
const [pxTabsH, setPxTabsH] = useState(0);
|
||||
|
||||
//При рендере данных
|
||||
//При подключении к странице
|
||||
useEffect(() => {
|
||||
rrpConf.docLoaded ? setPxTabsH(document.getElementById("sectionTabs").offsetHeight) : null;
|
||||
}, [rrpConf.docLoaded]);
|
||||
|
||||
//Формируем меню показателей
|
||||
const markMenuItems = [
|
||||
{ method: "EDIT", name: "Исправить", icon: "edit", func: handleMarkEdit },
|
||||
{ method: "DELETE", name: "Удалить", icon: "delete", func: handleMarkDelete }
|
||||
];
|
||||
setConf(Number(getNavigationSearch().NRN));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
//Генерация содержимого
|
||||
return (
|
||||
<Box sx={STYLES.CONTAINER}>
|
||||
{formOpen ? <IUDFormDialog initial={formData} onClose={handleDialogClose} onReload={handleReload} /> : null}
|
||||
{rrpConf.docLoaded ? (
|
||||
<Box>
|
||||
<Stack direction="row" sx={STYLES.TABS_BOTTOM_LINE}>
|
||||
<Tabs
|
||||
id="sectionTabs"
|
||||
value={tabValue}
|
||||
onChange={(event, newValue) => handleSectionChange(newValue)}
|
||||
variant="scrollable"
|
||||
scrollButtons={false}
|
||||
visibleScrollbar
|
||||
aria-label="section tab"
|
||||
sx={STYLES.TABS_SIZES(width, pxSectionAddButtonW)}
|
||||
>
|
||||
{rrpConf.sections.map((s, i) => {
|
||||
return (
|
||||
<Tab
|
||||
key={s.rn}
|
||||
{...a11yProps(i)}
|
||||
sx={STYLES.TABS_PADDING}
|
||||
label={
|
||||
<Box sx={STYLES.SECTION_ACTIONS}>
|
||||
{s.name}
|
||||
<IconButton component="span" onClick={() => handleSectionEdit(s.rn, s.code, s.name)}>
|
||||
<Icon>edit</Icon>
|
||||
</IconButton>
|
||||
<IconButton
|
||||
disabled={s.delete_allow === 0}
|
||||
component="span"
|
||||
onClick={() => handleSectionDelete(s.rn, s.code, s.name)}
|
||||
>
|
||||
<Icon>delete</Icon>
|
||||
</IconButton>
|
||||
</Box>
|
||||
}
|
||||
wrapped
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Tabs>
|
||||
<Box display="flex" justifyContent="center" alignItems="center" sx={STYLES.PANELS_MAIN_COLOR}>
|
||||
<IconButton onClick={handleSectionAdd}>
|
||||
<Icon sx={STYLES.ICON_WHITE}>add</Icon>
|
||||
</IconButton>
|
||||
</Box>
|
||||
</Stack>
|
||||
{rrpConf.sections.map((s, i) => {
|
||||
return (
|
||||
<SectionTab
|
||||
key={s.rn}
|
||||
section={s}
|
||||
tabValue={tabValue}
|
||||
index={i}
|
||||
order={order}
|
||||
onOrderChange={handleOrder}
|
||||
containerProps={{ height, pxOuterMenuH, pxPanelHeaderH, pxTabsH }}
|
||||
handleReload={handleReload}
|
||||
handleMarkOpen={handleMarkOpen}
|
||||
handleMarkAdd={handleMarkAdd}
|
||||
handleMarkCnOpen={handleMarkCnOpen}
|
||||
handleMarkCnInsert={handleMarkCnInsert}
|
||||
menuItems={markMenuItems}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
) : null}
|
||||
</Box>
|
||||
<Grid container direction={"column"} sx={STYLES.GRID_MAIN_CONTAINER}>
|
||||
<Grid item sx={STYLES.GRID_MAIN_SECTIONS_ITEM(sectionsCount === 0)}>
|
||||
<Grid container sx={STYLES.GRID_SECTIONS_CONTAINER}>
|
||||
<Sections conf={conf} onSectionChange={handleSectionChange} onSectionCountChange={handleSectionsCountChange} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
{sectionsCount > 0 && (
|
||||
<Grid item xs>
|
||||
<Grid container direction={"row"} sx={STYLES.GRID_SECTION_CONTAINER}>
|
||||
<Section section={section} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user