diff --git a/app/panels/eqs_prfrm/eqs_prfrm.js b/app/panels/eqs_prfrm/eqs_prfrm.js index f5bb5b2..f49142b 100644 --- a/app/panels/eqs_prfrm/eqs_prfrm.js +++ b/app/panels/eqs_prfrm/eqs_prfrm.js @@ -8,7 +8,7 @@ //--------------------- import React, { useState, useContext, useCallback, useEffect } from "react"; //Классы React -import { Grid, Paper, Box, Link } from "@mui/material"; //Интерфейсные компоненты +import { Grid, Paper, Box } from "@mui/material"; //Интерфейсные компоненты import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../components/p8p_data_grid"; //Таблица данных import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером @@ -16,6 +16,7 @@ import { ApplicationСtx } from "../../context/application"; //Контекст import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений import { headCellRender, dataCellRender, groupCellRender, DIGITS_REG_EXP, MONTH_NAME_REG_EXP, DAY_NAME_REG_EXP } from "./layouts"; //Дополнительная разметка и вёрстка клиентских элементов import { TEXTS } from "../../../app.text"; //Тектовые ресурсы и константы +import { Filter } from "./filter"; //Компонент фильтра import { FilterDialog } from "./filter_dialog"; //Компонент диалогового окна фильтра отбора //----------- @@ -35,25 +36,21 @@ const EqsPrfrm = () => { //Состояние фильтра const [filter, setFilter] = useState({ - belong: "", - prodObj: "", - techServ: "", - respDep: "", - fromMonth: 1, - fromYear: 1990, - toMonth: 1, - toYear: 1990 + isOpen: false, + isDefault: false, + isSetByUser: false, + values: { + belong: "", + prodObj: "", + techServ: "", + respDep: "", + fromMonth: 1, + fromYear: 1990, + toMonth: 1, + toYear: 1990 + } }); - //Состояние хранения копии фильтра - const [filterCopy, setFilterCopy] = useState({ ...filter }); - - //Состояние открытия фильтра - const [filterOpen, setFilterOpen] = useState(true); - - //Состояние данных по умолчанию для фильтра - const [defaultLoaded, setDefaultLoaded] = useState(false); - //Состояние ячейки заголовка даты (по раскрытию/скрытию) const [activeRef, setActiveRef] = useState(); @@ -75,14 +72,14 @@ const EqsPrfrm = () => { const data = await executeStored({ stored: "PKG_P8PANELS_EQUIPSRV.EQUIPSRV_GRID", args: { - SBELONG: filter.belong, - SPRODOBJ: filter.prodObj, - STECHSERV: filter.techServ, - SRESPDEP: filter.respDep, - NFROMMONTH: filter.fromMonth, - NFROMYEAR: filter.fromYear, - NTOMONTH: filter.toMonth, - NTOYEAR: filter.toYear + SBELONG: filter.values.belong, + SPRODOBJ: filter.values.prodObj, + STECHSERV: filter.values.techServ, + SRESPDEP: filter.values.respDep, + NFROMMONTH: filter.values.fromMonth, + NFROMYEAR: filter.values.fromYear, + NTOMONTH: filter.values.toMonth, + NTOYEAR: filter.values.toYear }, respArg: "COUT", attributeValueProcessor: (name, val) => (["caption", "name", "parent"].includes(name) ? undefined : val) @@ -144,8 +141,11 @@ const EqsPrfrm = () => { stored: "PKG_P8PANELS_EQUIPSRV.GET_DEFAULT_FP", respArg: "COUT" }); - setFilter(pv => ({ ...pv, belong: data.JURPERS, fromMonth: 1, fromYear: data.YEAR, toMonth: 12, toYear: data.YEAR })); - setDefaultLoaded(true); + setFilter(pv => ({ + ...pv, + values: { ...pv.values, belong: data.JURPERS, fromMonth: 1, fromYear: data.YEAR, toMonth: 12, toYear: data.YEAR }, + isDefault: true + })); }, [executeStored]); //Отбор документа (ТОиР или Ремонтных ведомостей) по ячейке даты @@ -158,10 +158,10 @@ const EqsPrfrm = () => { const data = await executeStored({ stored: "PKG_P8PANELS_EQUIPSRV.SELECT_EQUIPSRV", args: { - SBELONG: filter.belong, - SPRODOBJ: filter.prodObj, - STECHSERV: filter.techServ ? filter.techServ : null, - SRESPDEP: filter.respDep ? filter.respDep : null, + SBELONG: filter.values.belong, + SPRODOBJ: filter.values.prodObj, + STECHSERV: filter.values.techServ ? filter.values.techServ : null, + SRESPDEP: filter.values.respDep ? filter.values.respDep : null, STECHNAME: techName, SSRVKIND: servKind, NYEAR: Number(year), @@ -176,10 +176,11 @@ const EqsPrfrm = () => { } else showMsgErr(TEXTS.NO_DATA_FOUND); }; - //Открыть фильтр - const openFilter = () => { - setFilterOpen(true); - }; + //Показать/скрыть фильтр + const setFilterOpen = isOpen => setFilter(pv => ({ ...pv, isOpen })); + + //Установить значение фильтра + const setFilterValues = values => setFilter(pv => ({ ...pv, isSetByUser: true, values: { ...values } })); //Отработка события скрытия/раскрытия ячейки даты const handleClick = (e, ref) => { @@ -195,16 +196,20 @@ const EqsPrfrm = () => { loadData(); }, [loadData, dataGrid.reload]); - //При открытом фильтре + //При изменении фильтра useEffect(() => { - if (filterOpen) { - { - setFilterCopy({ ...filter }); - if (!defaultLoaded) loadDefaultFilter(); - } - } - //eslint-disable-next-line react-hooks/exhaustive-deps - }, [filterOpen]); + if (filter.isSetByUser) setDataGrid({ reload: true }); + }, [ + filter.isSetByUser, + filter.values.belong, + filter.values.prodObj, + filter.values.techServ, + filter.values.respDep, + filter.values.fromMonth, + filter.values.fromYear, + filter.values.toMonth, + filter.values.toYear + ]); //При нажатии скрытии/раскрытии ячейки даты, фокус на неё useEffect(() => { @@ -218,28 +223,34 @@ const EqsPrfrm = () => { //eslint-disable-next-line react-hooks/exhaustive-deps }, [refIsDeprecated]); + //При загрузке фильтра по умолчанию + useEffect(() => { + if (filter.isDefault) setFilterOpen(true); + }, [filter.isDefault]); + + //При подключении к страницк + useEffect(() => { + loadDefaultFilter(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + //При открытии диалога фильтра + const handleFilterClick = () => setFilterOpen(true); + + //При изменении фильтра в диалоге + const handleFilterOk = filter => { + setFilterValues(filter); + setFilterOpen(false); + }; + + //При закрытии диалога фильтра + const handleFilterCancel = () => setFilterOpen(false); + //Генерация содержимого return (
- - - Фильтр отбора: {filter.belong ? `Принадлежность: ${filter.belong}` : ""}{" "} - {filter.prodObj ? `Производственный объект: ${filter.prodObj}` : ""} {filter.techServ ? `Техническая служба: ${filter.techServ}` : ""}{" "} - {filter.respDep ? `Ответственное подразделение: ${filter.respDep}` : ""}{" "} - {filter.fromMonth && filter.fromYear - ? `Начало периода: ${filter.fromMonth < 10 ? "0" + filter.fromMonth : filter.fromMonth}.${filter.fromYear}` - : ""}{" "} - {filter.toMonth && filter.toYear - ? `Конец периода: ${filter.toMonth < 10 ? "0" + filter.toMonth : filter.toMonth}.${filter.toYear}` - : ""} - + {filter.isOpen ? : null} + {dataGrid.dataLoaded ? ( diff --git a/app/panels/eqs_prfrm/filter.js b/app/panels/eqs_prfrm/filter.js new file mode 100644 index 0000000..ccb3c05 --- /dev/null +++ b/app/panels/eqs_prfrm/filter.js @@ -0,0 +1,91 @@ +/* + Парус 8 - Панели мониторинга - ТОиР - Выполнение работ + Компонент: Фильтр отбора +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { Chip, Stack, Icon, IconButton } from "@mui/material"; //Интерфейсные компоненты + +//-------------------------- +//Вспомогательные компоненты +//-------------------------- + +//Элемент фильтра +const FilterItem = ({ caption, value, onClick }) => { + //При нажатии на элемент + const handleClick = () => (onClick ? onClick() : null); + + //Генерация содержимого + return ( + + {caption}: {value} + + } + variant="outlined" + onClick={handleClick} + /> + ); +}; + +//Контроль свойств компонента - Элемент фильтра +FilterItem.propTypes = { + caption: PropTypes.string.isRequired, + value: PropTypes.any.isRequired, + onClick: PropTypes.func +}; + +//--------------- +//Тело компонента +//--------------- + +//Фильтр отбора +const Filter = ({ filter, onClick }) => { + //При нажатии на фильтр + const handleClick = () => (onClick ? onClick() : null); + + //Генерация содержимого + return ( + + + filter_alt + + {filter.belong ? : null} + {filter.prodObj ? : null} + {filter.techServ ? : null} + {filter.respDep ? : null} + {filter.fromMonth && filter.fromYear ? ( + + ) : null} + {filter.toMonth && filter.toYear ? ( + + ) : null} + + ); +}; + +//Контроль свойств компонента - Фильтр отбора +Filter.propTypes = { + filter: PropTypes.object.isRequired, + onClick: PropTypes.func +}; + +//-------------------- +//Интерфейс компонента +//-------------------- + +export { Filter }; diff --git a/app/panels/eqs_prfrm/filter_dialog.js b/app/panels/eqs_prfrm/filter_dialog.js index 8786ffa..9f79a6e 100644 --- a/app/panels/eqs_prfrm/filter_dialog.js +++ b/app/panels/eqs_prfrm/filter_dialog.js @@ -1,52 +1,89 @@ /* Парус 8 - Панели мониторинга - ТОиР - Выполнение работ - Панель мониторинга: Диалоговое окно фильтра отбора + Компонент: Диалоговое окно фильтра отбора */ //--------------------- //Подключение библиотек //--------------------- -import React, { useState, useContext, useEffect, useCallback } from "react"; //Классы React +import React, { useState, useContext } from "react"; //Классы React import PropTypes from "prop-types"; //Контроль свойств компонента -import { Dialog, DialogTitle, IconButton, Icon, DialogContent, DialogActions, Button, Paper, Box, Grid } from "@mui/material"; //Интерфейсные компоненты +import { Dialog, DialogTitle, IconButton, Icon, DialogContent, DialogActions, Button, Box, Grid } from "@mui/material"; //Интерфейсные компоненты import { FilterInputField } from "./filter_input_field"; //Компонент поля ввода import { ApplicationСtx } from "../../context/application"; //Контекст приложения -import { STYLES } from "./layouts"; //Стили //--------- //Константы //--------- //Массив месяцев -export const MONTH_ARRAY = ["Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь"]; +const MONTH_ARRAY = ["Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь"]; + +//Стили +const STYLES = { + DIALOG_ACTIONS: { justifyContent: "center" }, + CLOSE_BUTTON: { + position: "absolute", + right: 8, + top: 8, + color: theme => theme.palette.grey[500] + } +}; + +//----------------------- +//Вспомогательные функции +//----------------------- + +//Формирование списка лет +const buildYears = () => { + const res = [1990]; + const today = new Date(); + for (let i = res[0] + 1; i <= today.getFullYear(); i++) res.push(i); + return res; +}; + +//Выбор принадлежности +const selectJuridicalPersons = (showDictionary, callBack) => { + showDictionary({ + unitCode: "JuridicalPersons", + callBack: res => (res.success === true ? callBack(res.outParameters.out_CODE) : callBack(null)) + }); +}; + +//Выбор производственного объекта +const selectEquipConfiguration = (showDictionary, callBack) => { + showDictionary({ + unitCode: "EquipConfiguration", + callBack: res => (res.success === true ? callBack(res.outParameters.out_CODE) : callBack(null)) + }); +}; + +//Выбор подразделения +const selectInsDepartment = (showDictionary, callBack) => { + showDictionary({ + unitCode: "INS_DEPARTMENT", + callBack: res => (res.success === true ? callBack(res.outParameters.out_CODE) : callBack(null)) + }); +}; //--------------- //Тело компонента //--------------- //Диалоговое окно фильтра отбора -const FilterDialog = props => { - //Свойства - const { filter, filterCopy, filterOpen, setFilter, setFilterOpen, setDataGrid } = props; - - //Состояние ограничения редактирования фильтра - const [filterLock, setFilterLock] = useState(false); - - //Состояние массива лет - const [years, setYears] = useState({ array: [1990], filled: false }); +const FilterDialog = ({ initial, onCancel, onOk }) => { + //Собственное состояние + const [filter, setFilter] = useState({ ...initial }); //Подключение к контексту приложения const { pOnlineShowDictionary } = useContext(ApplicationСtx); - //Закрыть фильтр - const closeFilter = e => { - if (filterLock && e != undefined) setFilter(filterCopy); - setFilterOpen(false); - }; + //При закрытии диалога без изменения фильтра + const handleCancel = () => (onCancel ? onCancel() : null); - //Очистить фильтр - const clearFilter = () => { + //При очистке фильтра + const handleClear = () => { setFilter({ belong: "", prodObj: "", @@ -59,179 +96,128 @@ const FilterDialog = props => { }); }; - //Заполнение состояния массива лет - const getYearArray = useCallback(async () => { - const today = new Date(); - for (let i = years.array[0] + 1; i <= today.getFullYear(); i++) { - setYears(pv => ({ ...pv, array: [...pv.array, i] })); - } - setYears(pv => ({ ...pv, filled: true })); - //eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + //При закрытии диалога с изменением фильтра + const handleOK = () => (onOk ? onOk(filter) : null); - //Только при первичном рендере - useEffect(() => { - if (filterOpen && !years.filled) getYearArray(); - }, [filterOpen, getYearArray, years.filled]); + //При изменении значения элемента + const handleFilterItemChange = (item, value) => setFilter(pv => ({ ...pv, [item]: value })); //Генерация содержимого return (
- + Фильтр отбора - theme.palette.grey[500] - }} - > + close - - - { - pOnlineShowDictionary({ - unitCode: "JuridicalPersons", - callBack: res => - res.success === true ? setFilter(pv => ({ ...pv, belong: res.outParameters.out_CODE })) : null - }); - }} - required={true} - /> - - - { - pOnlineShowDictionary({ - unitCode: "EquipConfiguration", - callBack: res => - res.success === true ? setFilter(pv => ({ ...pv, prodObj: res.outParameters.out_CODE })) : null - }); - }} - required={true} - /> - - - { - pOnlineShowDictionary({ - unitCode: "INS_DEPARTMENT", - callBack: res => - res.success === true ? setFilter(pv => ({ ...pv, techServ: res.outParameters.out_CODE })) : null - }); - }} - /> - - - { - pOnlineShowDictionary({ - unitCode: "INS_DEPARTMENT", - callBack: res => - res.success === true ? setFilter(pv => ({ ...pv, respDep: res.outParameters.out_CODE })) : null - }); - }} - /> - - - - - Начало периода: - - - setFilter(pv => ({ ...pv, fromMonth: e.target.value }))} - required={true} - isDateField={true} - /> - - - setFilter(pv => ({ ...pv, fromYear: e.target.value }))} - required={true} - isDateField={true} - yearArray={years.array} - /> - + + selectJuridicalPersons(pOnlineShowDictionary, callBack)} + required={true} + onChange={handleFilterItemChange} + /> + + + selectEquipConfiguration(pOnlineShowDictionary, callBack)} + required={true} + onChange={handleFilterItemChange} + /> + + + selectInsDepartment(pOnlineShowDictionary, callBack)} + onChange={handleFilterItemChange} + /> + + + selectInsDepartment(pOnlineShowDictionary, callBack)} + onChange={handleFilterItemChange} + /> + + + + + Начало периода: - - - - - Конец периода: - - - setFilter(pv => ({ ...pv, toMonth: e.target.value }))} - required={true} - isDateField={true} - /> - - - setFilter(pv => ({ ...pv, toYear: e.target.value }))} - required={true} - isDateField={true} - yearArray={years.array} - /> - + + ({ value: i + 1, caption: item }))} + onChange={handleFilterItemChange} + /> - - + + ({ value: item, caption: item }))} + onChange={handleFilterItemChange} + /> + + + + + + + Конец периода: + + + ({ value: i + 1, caption: item }))} + onChange={handleFilterItemChange} + /> + + + ({ value: item, caption: item }))} + onChange={handleFilterItemChange} + /> + + + - + - - @@ -242,12 +228,9 @@ const FilterDialog = props => { //Контроль свойств компонента - Диалоговое окно фильтра отбора FilterDialog.propTypes = { - filter: PropTypes.object.isRequired, - filterCopy: PropTypes.object.isRequired, - filterOpen: PropTypes.bool.isRequired, - setFilter: PropTypes.func.isRequired, - setFilterOpen: PropTypes.func.isRequired, - setDataGrid: PropTypes.func.isRequired + initial: PropTypes.object.isRequired, + onOk: PropTypes.func, + onCancel: PropTypes.func }; //-------------------- diff --git a/app/panels/eqs_prfrm/filter_input_field.js b/app/panels/eqs_prfrm/filter_input_field.js index 65f6a04..5ea5333 100644 --- a/app/panels/eqs_prfrm/filter_input_field.js +++ b/app/panels/eqs_prfrm/filter_input_field.js @@ -1,81 +1,89 @@ /* Парус 8 - Панели мониторинга - ТОиР - Выполнение работ - Панель мониторинга: Компонент поля ввода + Компонент: Поле ввода диалога фильтра */ //--------------------- //Подключение библиотек //--------------------- -import React, { useEffect, useState, useCallback } from "react"; //Классы React +import React, { useEffect, useState } from "react"; //Классы React import PropTypes from "prop-types"; //Контроль свойств компонента import { FormControl, InputLabel, Input, InputAdornment, IconButton, Icon, FormHelperText, Select, MenuItem } from "@mui/material"; //Интерфейсные компоненты -import { MONTH_ARRAY } from "./filter_dialog"; //Название месяцев + +//--------- +//Константы +//--------- + +//Стили +const STYLES = { + HELPER_TEXT: { color: "red" } +}; //--------------- //Тело компонента //--------------- //Поле ввода -const FilterInputField = props => { - //Свойства - const { elementCode, elementValue, labelText, changeFunc, required, isDateField, yearArray } = props; +const FilterInputField = ({ elementCode, elementValue, labelText, onChange, required = false, items = null, dictionary }) => { + //Значение элемента + const [value, setValue] = useState(elementValue); - //Состояние идентификатора элемента - const [elementId, setElementId] = useState(""); - - //Формирование идентификатора элемента - const generateId = useCallback(async () => { - setElementId(!isDateField ? `${elementCode}-input` : `${elementCode}-select`); - }, [elementCode, isDateField]); - - //При рендере поля ввода + //При получении нового значения из вне useEffect(() => { - generateId(); - }, [generateId]); + setValue(elementValue); + }, [elementValue]); + + //Выбор значения из словаря + const handleDictionaryClick = () => + dictionary ? dictionary(res => (res ? handleChange({ target: { name: elementCode, value: res } }) : null)) : null; + + //Изменение значения элемента + const handleChange = e => { + setValue(e.target.value); + if (onChange) onChange(e.target.name, e.target.value); + }; //Генерация поля с выбором из словаря Парус - const renderInput = () => { + const renderInput = validationError => { return ( - - list - - + dictionary ? ( + + + list + + + ) : null } - aria-describedby={`${elementId}-helper-text`} + aria-describedby={`${elementCode}-helper-text`} label={labelText} + onChange={handleChange} /> ); }; //Генерация поля с выпадающим списком - const renderSelect = () => { + const renderSelect = (items, validationError) => { return (