diff --git a/app.config.js b/app.config.js index 99cf299..6d68c7b 100644 --- a/app.config.js +++ b/app.config.js @@ -10,11 +10,13 @@ //Системеые параметры const SYSTEM = { //Адрес сервера приложений "ПАРУС 8 Онлайн" - SERVER: "../../DicAcc/" + SERVER: "../../DicAcc/", + //Количество записей на странице + PAGE_SIZE: 50 }; //---------------- //Интерфейс модуля //---------------- -export { SYSTEM }; +export default { SYSTEM }; diff --git a/app/config_wrapper.js b/app/config_wrapper.js new file mode 100644 index 0000000..ca15352 --- /dev/null +++ b/app/config_wrapper.js @@ -0,0 +1,65 @@ +/* + Парус 8 - Панели мониторинга + Обёртки для компонент, обеспечивающие подключение их к настройкам приложения +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React from "react"; //Классы React +import { deepCopyObject } from "./core/utils"; //Вспомогательные процедуры и функции +import { BUTTONS, TEXTS, INPUTS } from "../app.text"; //Текстовые ресурсы и константы +import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "./components/p8p_data_grid"; //Таблица данных + +//--------- +//Константы +//--------- + +//Конфигурируемые свойства "Таблицы данных" (P8PDataGrid) +const P8P_DATA_GRID_CONFIG_PROPS = { + orderAscMenuItemCaption: BUTTONS.ORDER_ASC, + orderDescMenuItemCaption: BUTTONS.ORDER_DESC, + filterMenuItemCaption: BUTTONS.FILTER, + valueFilterCaption: INPUTS.VALUE, + valueFromFilterCaption: INPUTS.VALUE_FROM, + valueToFilterCaption: INPUTS.VALUE_TO, + okFilterBtnCaption: BUTTONS.OK, + clearFilterBtnCaption: BUTTONS.CLEAR, + cancelFilterBtnCaption: BUTTONS.CANCEL, + morePagesBtnCaption: BUTTONS.MORE, + noDataFoundText: TEXTS.NO_DATA_FOUND, + objectsCopier: deepCopyObject +}; + +//----------------------- +//Вспомогательные функции +//----------------------- + +//Рекурсивное добавление свойств элемента, получаемых из конфигурационных файлов +const addConfigChildProps = children => + React.Children.map(children, child => { + if (!React.isValidElement(child)) return child; + const { children, ...restProps } = child.props; + let configProps = {}; + if (child.type.name === "P8PDataGrid") configProps = P8P_DATA_GRID_CONFIG_PROPS; + return React.createElement(child.type, { ...configProps, ...restProps }, addConfigChildProps(children)); + }); + +//----------- +//Тело модуля +//----------- + +//Обёртка для компонента "Таблица данных" (P8PDataGrid) +const P8PDataGridConfigWrapped = (props = {}) => { + return ; +}; + +//Универсальный элемент-обёртка в параметры конфигурации +const ConfigWrapper = ({ children }) => addConfigChildProps(children); + +//---------------- +//Интерфейс модуля +//---------------- + +export { P8P_DATA_GRID_CONFIG_PROPS, P8P_DATA_GRID_SIZE, P8PDataGridConfigWrapped, ConfigWrapper }; diff --git a/app/context/application.js b/app/context/application.js index 1ace5b5..9f2c8c2 100644 --- a/app/context/application.js +++ b/app/context/application.js @@ -7,7 +7,7 @@ //Подключение библиотек //--------------------- -import React, { useReducer, createContext, useEffect, useContext, useCallback } from "react"; //ReactJS +import React, { useReducer, createContext, useEffect, useContext, useCallback, useMemo } from "react"; //ReactJS import PropTypes from "prop-types"; //Контроль свойств компонента import { APP_AT, INITIAL_STATE, applicationReducer } from "./application_reducer"; //Редьюсер состояния import { MessagingСtx } from "./messaging"; //Контекст отображения сообщений @@ -33,7 +33,7 @@ const APPLICATION_CONTEXT_ERRORS_SHAPE = PropTypes.shape({ export const ApplicationСtx = createContext(); //Провайдер контекста приложения -export const ApplicationContext = ({ errors, displaySizeGetter, guidGenerator, children }) => { +export const ApplicationContext = ({ errors, displaySizeGetter, guidGenerator, config, children }) => { //Подключим редьюсер состояния const [state, dispatch] = useReducer(applicationReducer, INITIAL_STATE(displaySizeGetter)); @@ -114,6 +114,9 @@ export const ApplicationContext = ({ errors, displaySizeGetter, guidGenerator, c [showMsgErr, errors.P8O_API_UNAVAILABLE] ); + //Получение количества записей на странице + const configSystemPageSize = useMemo(() => config.SYSTEM.PAGE_SIZE, [config.SYSTEM.PAGE_SIZE]); + //Инициализация приложения const initApp = useCallback(async () => { //Читаем конфигурацию с сервера @@ -147,6 +150,7 @@ export const ApplicationContext = ({ errors, displaySizeGetter, guidGenerator, c pOnlineShowDictionary, pOnlineUserProcedure, pOnlineUserReport, + configSystemPageSize, appState: state }} > @@ -160,5 +164,6 @@ ApplicationContext.propTypes = { errors: APPLICATION_CONTEXT_ERRORS_SHAPE.isRequired, displaySizeGetter: PropTypes.func, guidGenerator: PropTypes.func.isRequired, + config: PropTypes.object.isRequired, children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]) }; diff --git a/app/core/client.js b/app/core/client.js index 75b808b..adae619 100644 --- a/app/core/client.js +++ b/app/core/client.js @@ -9,7 +9,7 @@ import { XMLParser, XMLBuilder } from "fast-xml-parser"; //Конвертация XML в JSON и JSON в XML import dayjs from "dayjs"; //Работа с датами -import { SYSTEM } from "../../app.config"; //Настройки приложения +import config from "../../app.config"; //Настройки приложения //--------- //Константы @@ -165,7 +165,7 @@ const executeStored = async ({ stored, args, respArg, throwError = true, spreadO serverArgs.push({ XARGUMENT: { SNAME: arg, VALUE: value, SDATA_TYPE: dataType } }); } res = await executeAction({ - serverURL: `${SYSTEM.SERVER}${!SYSTEM.SERVER.endsWith("/") ? "/" : ""}Process`, + serverURL: `${config.SYSTEM.SERVER}${!config.SYSTEM.SERVER.endsWith("/") ? "/" : ""}Process`, action: SRV_FN_CODE_EXEC_STORED, payload: { SSTORED: stored, XARGUMENTS: serverArgs, SRESP_ARG: respArg }, isArray: (name, jPath) => XML_ALWAYS_ARRAY_PATHS.indexOf(jPath) !== -1 || jPath.endsWith(XML_ALWAYS_ARRAY_POSTFIX) @@ -189,7 +189,7 @@ const getConfig = async ({ throwError = true } = {}) => { let res = null; try { res = await executeAction({ - serverURL: `${SYSTEM.SERVER}${!SYSTEM.SERVER.endsWith("/") ? "/" : ""}GetConfig` + serverURL: `${config.SYSTEM.SERVER}${!config.SYSTEM.SERVER.endsWith("/") ? "/" : ""}GetConfig` }); } catch (e) { if (throwError) throw e; diff --git a/app/panels/prj_fin/projects.js b/app/panels/prj_fin/projects.js index 5b091f1..05b103e 100644 --- a/app/panels/prj_fin/projects.js +++ b/app/panels/prj_fin/projects.js @@ -10,20 +10,18 @@ import React, { useState, useCallback, useEffect, useContext } from "react"; //Классы React import PropTypes from "prop-types"; //Контроль свойств компонента import { Grid, Icon, Stack, Link, Button, Table, TableBody, TableRow, TableCell, Typography, Box, Paper, IconButton } from "@mui/material"; //Интерфейсные компоненты -import { deepCopyObject, hasValue, formatDateRF, formatNumberRFCurrency, object2Base64XML } from "../../core/utils"; //Вспомогательные процедуры и функции -import { BUTTONS, TEXTS, INPUTS } from "../../../app.text"; //Тектовые ресурсы и константы +import { hasValue, formatDateRF, formatNumberRFCurrency, object2Base64XML } from "../../core/utils"; //Вспомогательные процедуры и функции +import { TEXTS } from "../../../app.text"; //Тектовые ресурсы и константы import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../components/p8p_data_grid"; //Таблица данных import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером import { ApplicationСtx } from "../../context/application"; //Контекст приложения import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений +import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения //----------------------- //Вспомогательные функции //----------------------- -//Количество записей на странице -const PAGE_SIZE = 50; - //Формирование значения для колонки "Состояние проекта" const formatPrjStateValue = (value, addText = false) => { const [text, icon] = @@ -227,7 +225,7 @@ const Projects = ({ onStagesOpen }) => { const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx); //Подключение к контексту приложения - const { pOnlineShowDocument, pOnlineShowUnit } = useContext(ApplicationСtx); + const { pOnlineShowDocument, pOnlineShowUnit, configSystemPageSize } = useContext(ApplicationСtx); //Подключение к контексту сообщений const { showMsgErr } = useContext(MessagingСtx); @@ -241,7 +239,7 @@ const Projects = ({ onStagesOpen }) => { CFILTERS: { VALUE: object2Base64XML(projectsDataGrid.filters, { arrayNodeName: "filters" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB }, CORDERS: { VALUE: object2Base64XML(projectsDataGrid.orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB }, NPAGE_NUMBER: projectsDataGrid.pageNumber, - NPAGE_SIZE: PAGE_SIZE, + NPAGE_SIZE: configSystemPageSize, NINCLUDE_DEF: projectsDataGrid.dataLoaded ? 0 : 1 }, respArg: "COUT" @@ -252,7 +250,7 @@ const Projects = ({ onStagesOpen }) => { rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])], dataLoaded: true, reload: false, - morePages: (data.XROWS || []).length >= PAGE_SIZE + morePages: (data.XROWS || []).length >= configSystemPageSize })); } }, [ @@ -262,6 +260,7 @@ const Projects = ({ onStagesOpen }) => { projectsDataGrid.dataLoaded, projectsDataGrid.pageNumber, executeStored, + configSystemPageSize, SERV_DATA_TYPE_CLOB ]); @@ -297,23 +296,13 @@ const Projects = ({ onStagesOpen }) => { <> {projectsDataGrid.dataLoaded ? ( dataCellRender(prms, handleStagesOpen)} rowExpandRender={prms => rowExpandRender(prms, pOnlineShowDocument, showProjectPayNotes, handleStagesOpen)} @@ -321,7 +310,6 @@ const Projects = ({ onStagesOpen }) => { onOrderChanged={handleOrderChanged} onFilterChanged={handleFilterChanged} onPagesCountChanged={handlePagesCountChanged} - objectsCopier={deepCopyObject} /> ) : null} diff --git a/app/panels/prj_fin/stage_arts.js b/app/panels/prj_fin/stage_arts.js index bdc17d8..e18e045 100644 --- a/app/panels/prj_fin/stage_arts.js +++ b/app/panels/prj_fin/stage_arts.js @@ -10,12 +10,13 @@ import React, { useState, useCallback, useEffect, useContext } from "react"; //Классы React import PropTypes from "prop-types"; //Контроль свойств компонента import { Box, Icon, Stack, Link } from "@mui/material"; //Интерфейсные компоненты -import { deepCopyObject, hasValue, formatNumberRFCurrency, object2Base64XML } from "../../core/utils"; //Вспомогательные процедуры и функции -import { BUTTONS, TEXTS, INPUTS } from "../../../app.text"; //Тектовые ресурсы и константы +import { hasValue, formatNumberRFCurrency, object2Base64XML } from "../../core/utils"; //Вспомогательные процедуры и функции +import { TEXTS } from "../../../app.text"; //Тектовые ресурсы и константы import { P8PDataGrid, P8P_DATA_GRID_SIZE, P8P_DATA_GRID_FILTER_SHAPE } from "../../components/p8p_data_grid"; //Таблица данных import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером import { ApplicationСtx } from "../../context/application"; //Контекст приложения import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений +import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения //----------------------- //Вспомогательные функции @@ -162,27 +163,16 @@ const StageArts = ({ stage, filters }) => { {stageArtsDataGrid.dataLoaded ? ( dataCellRender(prms, showStageArtCostNotes, showStageArtContracts)} valueFormatter={valueFormatter} onFilterChanged={handleFilterChanged} - objectsCopier={deepCopyObject} /> ) : null} diff --git a/app/panels/prj_fin/stage_contracts.js b/app/panels/prj_fin/stage_contracts.js index b02758d..a284fd0 100644 --- a/app/panels/prj_fin/stage_contracts.js +++ b/app/panels/prj_fin/stage_contracts.js @@ -10,19 +10,16 @@ import React, { useState, useCallback, useEffect, useContext } from "react"; //Классы React import PropTypes from "prop-types"; //Контроль свойств компонента import { Box, Stack, Grid, Paper, Table, TableBody, TableRow, TableCell, Typography, Button, Link } from "@mui/material"; //Интерфейсные компоненты -import { deepCopyObject, hasValue, formatDateRF, formatNumberRFCurrency, object2Base64XML } from "../../core/utils"; //Вспомогательные процедуры и функции -import { BUTTONS, TEXTS, INPUTS } from "../../../app.text"; //Тектовые ресурсы и константы +import { hasValue, formatDateRF, formatNumberRFCurrency, object2Base64XML } from "../../core/utils"; //Вспомогательные процедуры и функции import { P8PDataGrid, P8P_DATA_GRID_SIZE, P8P_DATA_GRID_FILTER_SHAPE } from "../../components/p8p_data_grid"; //Таблица данных import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером import { ApplicationСtx } from "../../context/application"; //Контекст приложения +import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения //----------------------- //Вспомогательные функции //----------------------- -//Количество записей на странице -const PAGE_SIZE = 50; - //Форматирование значений колонок const valueFormatter = ({ value, columnDef }) => { switch (columnDef.name) { @@ -154,7 +151,7 @@ const StageContracts = ({ stage, filters }) => { const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx); //Подключение к контексту приложения - const { pOnlineShowDocument } = useContext(ApplicationСtx); + const { pOnlineShowDocument, configSystemPageSize } = useContext(ApplicationСtx); //Загрузка данных этапов с сервера const loadStageContracts = useCallback(async () => { @@ -169,7 +166,7 @@ const StageContracts = ({ stage, filters }) => { }, CORDERS: { VALUE: object2Base64XML(stageContractsDataGrid.orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB }, NPAGE_NUMBER: stageContractsDataGrid.pageNumber, - NPAGE_SIZE: PAGE_SIZE, + NPAGE_SIZE: configSystemPageSize, NINCLUDE_DEF: stageContractsDataGrid.dataLoaded ? 0 : 1 }, respArg: "COUT" @@ -180,7 +177,7 @@ const StageContracts = ({ stage, filters }) => { rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])], dataLoaded: true, reload: false, - morePages: (data.XROWS || []).length >= PAGE_SIZE + morePages: (data.XROWS || []).length >= configSystemPageSize })); } }, [ @@ -191,6 +188,7 @@ const StageContracts = ({ stage, filters }) => { stageContractsDataGrid.dataLoaded, stageContractsDataGrid.pageNumber, executeStored, + configSystemPageSize, SERV_DATA_TYPE_CLOB ]); @@ -213,6 +211,7 @@ const StageContracts = ({ stage, filters }) => { {stageContractsDataGrid.dataLoaded ? ( { morePages={stageContractsDataGrid.morePages} reloading={stageContractsDataGrid.reload} expandable={true} - orderAscMenuItemCaption={BUTTONS.ORDER_ASC} - orderDescMenuItemCaption={BUTTONS.ORDER_DESC} - filterMenuItemCaption={BUTTONS.FILTER} - valueFilterCaption={INPUTS.VALUE} - valueFromFilterCaption={INPUTS.VALUE_FROM} - valueToFilterCaption={INPUTS.VALUE_TO} - okFilterBtnCaption={BUTTONS.OK} - clearFilterBtnCaption={BUTTONS.CLEAR} - cancelFilterBtnCaption={BUTTONS.CANCEL} - morePagesBtnCaption={BUTTONS.MORE} - noDataFoundText={TEXTS.NO_DATA_FOUND} dataCellRender={prms => dataCellRender(prms, pOnlineShowDocument)} rowExpandRender={prms => rowExpandRender(prms, pOnlineShowDocument)} valueFormatter={valueFormatter} onOrderChanged={handleOrderChanged} onFilterChanged={handleFilterChanged} onPagesCountChanged={handlePagesCountChanged} - objectsCopier={deepCopyObject} /> ) : null} diff --git a/app/panels/prj_fin/stages.js b/app/panels/prj_fin/stages.js index 6dfd9d9..e9653c4 100644 --- a/app/panels/prj_fin/stages.js +++ b/app/panels/prj_fin/stages.js @@ -10,8 +10,8 @@ import React, { useState, useCallback, useEffect, useContext } from "react"; //Классы React import PropTypes from "prop-types"; //Контроль свойств компонента import { Box, Icon, Stack, Grid, Paper, Table, TableBody, TableRow, TableCell, Typography, Button, IconButton, Link } from "@mui/material"; //Интерфейсные компоненты -import { deepCopyObject, hasValue, formatDateRF, formatNumberRFCurrency, object2Base64XML } from "../../core/utils"; //Вспомогательные процедуры и функции -import { BUTTONS, TEXTS, INPUTS } from "../../../app.text"; //Тектовые ресурсы и константы +import { hasValue, formatDateRF, formatNumberRFCurrency, object2Base64XML } from "../../core/utils"; //Вспомогательные процедуры и функции +import { TEXTS } from "../../../app.text"; //Тектовые ресурсы и константы import { P8PDataGrid, P8P_DATA_GRID_SIZE, P8P_DATA_GRID_FILTER_SHAPE } from "../../components/p8p_data_grid"; //Таблица данных import { P8PFullScreenDialog } from "../../components/p8p_fullscreen_dialog"; //Полноэкранный диалог import { StageArts } from "./stage_arts"; //Калькуляция этапа проекта @@ -19,14 +19,12 @@ import { StageContracts } from "./stage_contracts"; //Договоры с сои import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером import { ApplicationСtx } from "../../context/application"; //Контекст приложения import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений +import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения //----------------------- //Вспомогательные функции //----------------------- -//Количество записей на странице -const PAGE_SIZE = 50; - //Формирование значения для колонки "Состояние" const formatStageStatusValue = (value, addText = false) => { const [text, icon] = @@ -250,7 +248,7 @@ const Stages = ({ project, projectName, filters }) => { const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx); //Подключение к контексту приложения - const { pOnlineShowDocument, pOnlineShowUnit } = useContext(ApplicationСtx); + const { pOnlineShowDocument, pOnlineShowUnit, configSystemPageSize } = useContext(ApplicationСtx); //Подключение к контексту сообщений const { showMsgErr } = useContext(MessagingСtx); @@ -265,7 +263,7 @@ const Stages = ({ project, projectName, filters }) => { CFILTERS: { VALUE: object2Base64XML(stagesDataGrid.filters, { arrayNodeName: "filters" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB }, CORDERS: { VALUE: object2Base64XML(stagesDataGrid.orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB }, NPAGE_NUMBER: stagesDataGrid.pageNumber, - NPAGE_SIZE: PAGE_SIZE, + NPAGE_SIZE: configSystemPageSize, NINCLUDE_DEF: stagesDataGrid.dataLoaded ? 0 : 1 }, respArg: "COUT" @@ -276,7 +274,7 @@ const Stages = ({ project, projectName, filters }) => { rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])], dataLoaded: true, reload: false, - morePages: (data.XROWS || []).length >= PAGE_SIZE + morePages: (data.XROWS || []).length >= configSystemPageSize })); } }, [ @@ -287,6 +285,7 @@ const Stages = ({ project, projectName, filters }) => { stagesDataGrid.dataLoaded, stagesDataGrid.pageNumber, executeStored, + configSystemPageSize, SERV_DATA_TYPE_CLOB ]); @@ -345,6 +344,7 @@ const Stages = ({ project, projectName, filters }) => { {stagesDataGrid.dataLoaded ? ( { morePages={stagesDataGrid.morePages} reloading={stagesDataGrid.reload} expandable={true} - orderAscMenuItemCaption={BUTTONS.ORDER_ASC} - orderDescMenuItemCaption={BUTTONS.ORDER_DESC} - filterMenuItemCaption={BUTTONS.FILTER} - valueFilterCaption={INPUTS.VALUE} - valueFromFilterCaption={INPUTS.VALUE_FROM} - valueToFilterCaption={INPUTS.VALUE_TO} - okFilterBtnCaption={BUTTONS.OK} - clearFilterBtnCaption={BUTTONS.CLEAR} - cancelFilterBtnCaption={BUTTONS.CANCEL} - morePagesBtnCaption={BUTTONS.MORE} - noDataFoundText={TEXTS.NO_DATA_FOUND} headCellRender={headCellRender} dataCellRender={prms => dataCellRender(prms, showStageArts)} rowExpandRender={prms => @@ -372,7 +361,6 @@ const Stages = ({ project, projectName, filters }) => { onOrderChanged={handleOrderChanged} onFilterChanged={handleFilterChanged} onPagesCountChanged={handlePagesCountChanged} - objectsCopier={deepCopyObject} /> ) : null} {stagesDataGrid.showStageContracts ? ( diff --git a/app/root.js b/app/root.js index f3c8c3e..b428c24 100644 --- a/app/root.js +++ b/app/root.js @@ -14,6 +14,7 @@ import { ApplicationContext } from "./context/application"; //Контекст import { App } from "./app"; //Приложение import { ERRORS, TITLES, TEXTS, BUTTONS } from "../app.text"; //Текстовые ресурсы и константы import { getDisplaySize, genGUID } from "./core/utils"; //Вспомогательные функции +import config from "../app.config"; //Настройки приложения import client from "./core/client"; //Клиент для взаимодействия с сервером //----------- @@ -25,7 +26,7 @@ const Root = () => { return ( - +