ЦИТК-1006 - Доработка диалога добавления/исправления

Reviewed-on: #40
This commit is contained in:
Mim 2025-11-12 12:34:36 +03:00
commit b7f9daa258
5 changed files with 118 additions and 104 deletions

View File

@ -12,6 +12,7 @@ import PropTypes from "prop-types"; //Контроль свойств компо
import { Dialog, DialogTitle, DialogContent, DialogActions, Button } from "@mui/material"; //Интерфейсные компоненты import { Dialog, DialogTitle, DialogContent, DialogActions, Button } from "@mui/material"; //Интерфейсные компоненты
import { BUTTONS } from "../../app.text"; //Общие текстовые ресурсы import { BUTTONS } from "../../app.text"; //Общие текстовые ресурсы
import { P8P_INPUT, P8PInput } from "./p8p_input"; //Поле ввода import { P8P_INPUT, P8PInput } from "./p8p_input"; //Поле ввода
import { APP_STYLES } from "../../app.styles"; //Типовые стили
//--------- //---------
//Константы //Константы
@ -26,6 +27,12 @@ const P8P_DIALOG_WIDTH = {
XL: "xl" XL: "xl"
}; };
//Стили
const STYLES = {
SCROLL: display =>
display === true ? { overflow: "auto", ...APP_STYLES.SCROLL } : { overflow: "hidden", display: "flex", flexDirection: "column" }
};
//----------------------- //-----------------------
//Вспомогательные функции //Вспомогательные функции
//----------------------- //-----------------------
@ -39,7 +46,19 @@ const buildFormValues = inputsState =>
//----------- //-----------
//Диалог //Диалог
const P8PDialog = ({ title, width, fullWidth, inputs, children, onOk, onCancel, onClose, onInputChange }) => { const P8PDialog = ({
title,
width,
fullWidth,
inputs,
children,
okDisabled = false,
scrollContent = true,
onOk,
onCancel,
onClose,
onInputChange
}) => {
//Состояние элементов ввода диалога //Состояние элементов ввода диалога
const [inputsState, setInputsState] = useState([]); const [inputsState, setInputsState] = useState([]);
@ -75,7 +94,7 @@ const P8PDialog = ({ title, width, fullWidth, inputs, children, onOk, onCancel,
return ( return (
<Dialog onClose={handleClose} open {...{ ...(width ? { maxWidth: width } : {}), ...(fullWidth === true ? { fullWidth: true } : {}) }}> <Dialog onClose={handleClose} open {...{ ...(width ? { maxWidth: width } : {}), ...(fullWidth === true ? { fullWidth: true } : {}) }}>
<DialogTitle>{title}</DialogTitle> <DialogTitle>{title}</DialogTitle>
<DialogContent> <DialogContent sx={STYLES.SCROLL(scrollContent)}>
{inputsState.map((input, i) => ( {inputsState.map((input, i) => (
<P8PInput key={i} {...input} formValues={formValues} onChange={handleInputChange} /> <P8PInput key={i} {...input} formValues={formValues} onChange={handleInputChange} />
))} ))}
@ -83,7 +102,11 @@ const P8PDialog = ({ title, width, fullWidth, inputs, children, onOk, onCancel,
{children} {children}
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
{onOk && <Button onClick={handleOk}>{BUTTONS.OK}</Button>} {onOk && (
<Button disabled={okDisabled} onClick={handleOk}>
{BUTTONS.OK}
</Button>
)}
{onCancel && <Button onClick={handleCancel}>{BUTTONS.CANCEL}</Button>} {onCancel && <Button onClick={handleCancel}>{BUTTONS.CANCEL}</Button>}
{onClose && <Button onClick={handleClose}>{BUTTONS.CLOSE}</Button>} {onClose && <Button onClick={handleClose}>{BUTTONS.CLOSE}</Button>}
</DialogActions> </DialogActions>
@ -98,6 +121,8 @@ P8PDialog.propTypes = {
fullWidth: PropTypes.bool, fullWidth: PropTypes.bool,
inputs: PropTypes.arrayOf(PropTypes.shape(P8P_INPUT)), inputs: PropTypes.arrayOf(PropTypes.shape(P8P_INPUT)),
children: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf(PropTypes.node)]), children: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf(PropTypes.node)]),
okDisabled: PropTypes.bool,
scrollContent: PropTypes.bool,
onOk: PropTypes.func, onOk: PropTypes.func,
onCancel: PropTypes.func, onCancel: PropTypes.func,
onClose: PropTypes.func, onClose: PropTypes.func,

View File

@ -9,10 +9,11 @@
import React, { useState, useCallback } from "react"; //Классы React import React, { useState, useCallback } from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента import PropTypes from "prop-types"; //Контроль свойств компонента
import { Box, Typography, Tabs, Tab, InputAdornment, IconButton, Icon } from "@mui/material"; //Интерфейсные компоненты import { Box, Tabs, Tab, InputAdornment, IconButton, Icon } from "@mui/material"; //Интерфейсные компоненты
import { TaskFormTabInfo } from "./task_form_tab_info"; //Вкладка основной информации import { TaskFormTabInfo } from "./task_form_tab_info"; //Вкладка основной информации
import { TaskFormTabExecutor } from "./task_form_tab_executor"; //Вкладка информации об исполнителе import { TaskFormTabExecutor } from "./task_form_tab_executor"; //Вкладка информации об исполнителе
import { TaskFormTabProps } from "./task_form_tab_props"; //Вкладка информации со свойствами import { TaskFormTabProps } from "./task_form_tab_props"; //Вкладка информации со свойствами
import { COMMON_STYLES } from "../styles"; //Общие стили
//--------- //---------
//Константы //Константы
@ -20,7 +21,8 @@ import { TaskFormTabProps } from "./task_form_tab_props"; //Вкладка ин
//Стили //Стили
const STYLES = { const STYLES = {
CONTAINER: { margin: "5px 0px", textAlign: "center" } CONTAINER: { height: "625px", textAlign: "center", overflow: "hidden", display: "flex", flexDirection: "column" },
BOX_TAB: { height: "575px", overflowY: "auto", ...COMMON_STYLES.SCROLL }
}; };
//------------------------------------ //------------------------------------
@ -40,7 +42,14 @@ function CustomTabPanel(props) {
const { children, value, index, ...other } = props; const { children, value, index, ...other } = props;
//Генерация содержимого //Генерация содержимого
return ( return (
<Box role="tabpanel" hidden={value !== index} id={`simple-tabpanel-${index}`} aria-labelledby={`simple-tab-${index}`} {...other}> <Box
role="tabpanel"
hidden={value !== index}
id={`simple-tabpanel-${index}`}
aria-labelledby={`simple-tab-${index}`}
{...other}
sx={STYLES.BOX_TAB}
>
{value === index && <Box pt={1}>{children}</Box>} {value === index && <Box pt={1}>{children}</Box>}
</Box> </Box>
); );
@ -105,9 +114,6 @@ const TaskForm = ({ task, taskType, editable, docProps, onTaskChange, onEventNex
//Генерация содержимого //Генерация содержимого
return ( return (
<Box sx={STYLES.CONTAINER}> <Box sx={STYLES.CONTAINER}>
<Typography pb={1} variant="h6">
{task.nRn ? `Исправление события${task.nClosed ? " (событые аннулировано)" : ""}` : "Добавление события"}
</Typography>
<Tabs value={tab} onChange={handleTabChange} aria-label="tabs of values"> <Tabs value={tab} onChange={handleTabChange} aria-label="tabs of values">
<Tab label="Событие" {...a11yProps(0)} /> <Tab label="Событие" {...a11yProps(0)} />
<Tab label="Исполнитель" {...a11yProps(1)} /> <Tab label="Исполнитель" {...a11yProps(1)} />

View File

@ -21,6 +21,11 @@ import { useDictionary } from "../hooks/dict_hooks"; //Состояние отк
//Константы //Константы
//--------- //---------
//Стили
const STYLES = {
BOX_FEW_COLUMNS: { display: "flex", flexWrap: "wrap", justifyContent: "space-between" }
};
//------------------------------------ //------------------------------------
//Вспомогательные функции и компоненты //Вспомогательные функции и компоненты
//------------------------------------ //------------------------------------
@ -101,55 +106,59 @@ const TaskFormTabProps = ({ task, docProps, onPropEdit }) => {
return ( return (
<Box> <Box>
<Box sx={COMMON_STYLES.BOX_WITH_LEGEND} component="fieldset"> <Box sx={COMMON_STYLES.BOX_WITH_LEGEND} component="fieldset">
{docProps.map((docProp, index) => { <Box sx={STYLES.BOX_FEW_COLUMNS}>
return docProp.BSHOW_IN_GRID ? ( {docProps.map((docProp, index) => {
<TextField return docProp.BSHOW_IN_GRID ? (
error={ <TextField
!validationError( error={
task.docProps[docProp.SFORMATTED_ID], !validationError(
docProp.NFORMAT, task.docProps[docProp.SFORMATTED_ID],
docProp.NNUM_WIDTH, docProp.NFORMAT,
docProp.NNUM_PRECISION, docProp.NNUM_WIDTH,
docProp.NSTR_WIDTH docProp.NNUM_PRECISION,
) docProp.NSTR_WIDTH
} )
key={index} }
sx={COMMON_STYLES.TASK_FORM_TEXT_FIELD()} key={index}
id={docProp.SFORMATTED_ID} sx={COMMON_STYLES.TASK_FORM_TEXT_FIELD()}
type={ id={docProp.SFORMATTED_ID}
docProp.NFORMAT < 2 type={
? "string" docProp.NFORMAT < 2
: docProp.NFORMAT === 2 ? "string"
? docProp.NDATA_SUBTYPE === 0 : docProp.NFORMAT === 2
? "date" ? docProp.NDATA_SUBTYPE === 0
: "datetime-local" ? "date"
: "time" : "datetime-local"
} : "time"
label={docProp.SNAME} }
fullWidth label={docProp.SNAME}
value={initPropValue(docProp)} fullWidth
variant="standard" value={initPropValue(docProp)}
onChange={e => onPropEdit(e.target.id, e.target.value)} variant="standard"
inputProps={ onChange={e => onPropEdit(e.target.id, e.target.value)}
(docProp.NFORMAT === 2 && docProp.NDATA_SUBTYPE === 2) || (docProp.NFORMAT === 3 && docProp.NDATA_SUBTYPE === 1) inputProps={
? { step: 1 } (docProp.NFORMAT === 2 && docProp.NDATA_SUBTYPE === 2) || (docProp.NFORMAT === 3 && docProp.NDATA_SUBTYPE === 1)
: {} ? { step: 1 }
} : {}
InputProps={ }
docProp.NENTRY_TYPE > 0 ? getInputProps(() => handleDictOpen(docProp, task.docProps[docProp.SFORMATTED_ID])) : null InputProps={
} docProp.NENTRY_TYPE > 0
InputLabelProps={ ? getInputProps(() => handleDictOpen(docProp, task.docProps[docProp.SFORMATTED_ID]))
docProp.NFORMAT < 2 : null
? {} }
: { InputLabelProps={
shrink: true docProp.NFORMAT < 2
} ? {}
} : {
required={docProp.BREQUIRE} shrink: true
disabled={docProp.BREADONLY} }
/> }
) : null; required={docProp.BREQUIRE}
})} disabled={docProp.BREADONLY}
/>
) : null;
})}
</Box>
</Box> </Box>
</Box> </Box>
); );

View File

@ -29,7 +29,7 @@ export const COMMON_STYLES = {
} }
: {}) : {})
}), }),
BOX_WITH_LEGEND: { border: "1px solid #939393" }, BOX_WITH_LEGEND: { border: "1px solid #939393", marginBottom: "1px" },
BOX_SINGLE_COLUMN: { display: "flex", flexDirection: "column", gap: "10px" }, BOX_SINGLE_COLUMN: { display: "flex", flexDirection: "column", gap: "10px" },
LEGEND: { textAlign: "left" }, LEGEND: { textAlign: "left" },
SELECT_MENU: width => { SELECT_MENU: width => {

View File

@ -9,29 +9,18 @@
import React, { useState, useCallback, useContext, useEffect } from "react"; //Классы React import React, { useState, useCallback, useContext, useEffect } from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента import PropTypes from "prop-types"; //Контроль свойств компонента
import { Dialog, DialogContent, DialogActions, Button } from "@mui/material"; //Интерфейсные компоненты
import { useClientEvent } from "./hooks/task_dialog_hooks"; //Хук для события import { useClientEvent } from "./hooks/task_dialog_hooks"; //Хук для события
import { useDocsProps } from "./hooks/task_dialog_hooks"; //Хук для получения доп. свойств раздела "События" import { useDocsProps } from "./hooks/task_dialog_hooks"; //Хук для получения доп. свойств раздела "События"
import { TaskForm } from "./components/task_form"; //Форма события import { TaskForm } from "./components/task_form"; //Форма события
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
import { object2Base64XML } from "../../core/utils"; //Вспомогательные функции import { object2Base64XML } from "../../core/utils"; //Вспомогательные функции
import { COMMON_STYLES } from "./styles"; //Общие стили
import { hasValue } from "../../core/utils"; //Вспомогательные процедуры и функции import { hasValue } from "../../core/utils"; //Вспомогательные процедуры и функции
import { P8PDialog } from "../../components/p8p_dialog"; //Типовой диалог
//--------- //---------
//Константы //Константы
//--------- //---------
//Стили
const STYLES = {
DIALOG_CONTENT: {
paddingBottom: "0px",
maxHeight: "740px",
minHeight: "740px",
...COMMON_STYLES.SCROLL
}
};
//----------- //-----------
//Тело модуля //Тело модуля
//----------- //-----------
@ -149,39 +138,24 @@ const TaskDialog = ({ taskRn, taskType, editable, onTasksReload, onClose }) => {
return ( return (
<> <>
{!task.init && docProps.loaded && ( {!task.init && docProps.loaded && (
<Dialog open onClose={onClose ? onClose : null} fullWidth> <P8PDialog
<DialogContent sx={STYLES.DIALOG_CONTENT}> title={task.nRn ? `Исправление события${task.nClosed ? " [аннулировано]" : ""}` : "Добавление события"}
<TaskForm fullWidth={true}
task={task} onOk={() => (taskRn ? handleUpdateEvent(onClose).then(onTasksReload) : handleInsertTask(onClose).then(onTasksReload))}
taskType={taskType} onClose={onClose ? onClose : null}
editable={!taskRn || editable ? true : false} okDisabled={taskRn ? task.updateDisabled || !editable || !docPropsReady : task.insertDisabled || !docPropsReady}
docProps={docProps.props} scrollContent={false}
onTaskChange={handleTaskChange} >
onEventNextNumbGet={handleEventNextNumbGet} <TaskForm
/> task={task}
</DialogContent> taskType={taskType}
{onClose ? ( editable={!taskRn || editable ? true : false}
<DialogActions sx={COMMON_STYLES.DIALOG_ACTIONS}> docProps={docProps.props}
{taskRn ? ( onTaskChange={handleTaskChange}
<Button onEventNextNumbGet={handleEventNextNumbGet}
onClick={() => handleUpdateEvent(onClose).then(onTasksReload)} />
disabled={task.updateDisabled || !editable || !docPropsReady} </P8PDialog>
> )}
Исправить
</Button>
) : (
<Button
onClick={() => handleInsertTask(onClose).then(onTasksReload)}
disabled={task.insertDisabled || !docPropsReady}
>
Добавить
</Button>
)}
<Button onClick={onClose}>Закрыть</Button>
</DialogActions>
) : null}
</Dialog>
)}{" "}
</> </>
); );
}; };