From 49001ac7d64f88cf94be46e4975cb4adc7bf1372 Mon Sep 17 00:00:00 2001 From: Mikhail Chechnev Date: Thu, 5 Feb 2026 15:41:43 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A6=D0=98=D0=A2=D0=9A-1035=20-=20=D0=BF?= =?UTF-8?q?=D0=B0=D0=BD=D0=B5=D0=BB=D1=8C=20=20"=D0=9F=D1=80=D0=BE=D0=B8?= =?UTF-8?q?=D0=B7=D0=B2=D0=BE=D0=B4=D1=81=D1=82=D0=B2=D0=B5=D0=BD=D0=BD?= =?UTF-8?q?=D1=8B=D0=B9=20=D0=BF=D0=BB=D0=B0=D0=BD=20=D1=86=D0=B5=D1=85?= =?UTF-8?q?=D0=B0"=20-=20=D1=84=D0=B8=D0=BB=D1=8C=D1=82=D1=80=20=D0=BF?= =?UTF-8?q?=D0=BE=20=D0=BC=D0=B5=D1=81=D1=8F=D1=86=D1=83=20=D0=B2=20=D1=81?= =?UTF-8?q?=D0=BF=D0=B8=D1=81=D0=BA=D0=B5=20=D0=BF=D0=BB=D0=B0=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mech_rec_dept_cost_prod_plans/hooks.js | 121 +++++++++--- .../mech_rec_dept_cost_prod_plans.js | 178 ++++++++++-------- db/PKG_P8PANELS_MECHREC.pck | 6 +- 3 files changed, 199 insertions(+), 106 deletions(-) diff --git a/app/panels/mech_rec_dept_cost_prod_plans/hooks.js b/app/panels/mech_rec_dept_cost_prod_plans/hooks.js index 38b4e2c..4f8244a 100644 --- a/app/panels/mech_rec_dept_cost_prod_plans/hooks.js +++ b/app/panels/mech_rec_dept_cost_prod_plans/hooks.js @@ -7,7 +7,7 @@ //Подключение библиотек //--------------------- -import React, { useState, useEffect, useContext } from "react"; //Классы React +import React, { useState, useEffect, useContext, useCallback } from "react"; //Классы React import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером import { object2Base64XML, formatDateRF } from "../../core/utils"; //Вспомогательные функции @@ -32,17 +32,60 @@ export const useFilteredPlans = (plans, filter) => { return filteredPlans; }; -//Хук для основной таблицы -const useDeptCostProdPlans = () => { +//Хук для планов +const useDeptCostProdPlans = month => { + //Собственное состояние - таблица данных + const [state, setState] = useState({ + init: false, + loaded: false, + showPlanList: false, + rows: [], + reload: true, + selected: {}, + currentMonth: "" + }); + + //Подключение к контексту взаимодействия с сервером + const { executeStored } = useContext(BackEndСtx); + + //При подключении компонента к странице + useEffect(() => { + const initPlans = async () => { + const data = await executeStored({ + stored: "PKG_P8PANELS_MECHREC.FCPRODPLAN_DEPT_INIT", + args: { SMONTH: month }, + respArg: "COUT", + isArray: name => name === "XFCPRODPLANS", + attributeValueProcessor: (name, val) => (name === "SPERIOD" ? undefined : val) + }); + setState(pv => ({ + ...pv, + init: true, + rows: [...(data?.XFCPRODPLANS || [])], + loaded: true, + reload: false, + selected: {}, + currentMonth: month + })); + }; + //Если месяц указан и он не соответствует текущим данным + if (month && month !== state.currentMonth) { + initPlans(); + } + }, [executeStored, month, state.currentMonth]); + + //Возвращаем данные + return [state, setState]; +}; + +//Хук для информации о плане +const useDeptCostProdPlanInfo = plan => { //Собственное состояние - таблица данных const [state, setState] = useState({ init: false, showPlanList: false, showIncomeFromDeps: null, showFcroutelst: null, - planList: [], - planListLoaded: false, - selectedPlan: {}, dataLoaded: false, columnsDef: [], orders: null, @@ -57,14 +100,43 @@ const useDeptCostProdPlans = () => { //Подключение к контексту взаимодействия с сервером const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx); + //При необходимости очистки данных о плане + const handleClear = useCallback( + () => + setState(pv => ({ + ...pv, + init: false, + showPlanList: false, + showIncomeFromDeps: null, + showFcroutelst: null, + dataLoaded: false, + columnsDef: [], + orders: null, + rows: [], + reload: true, + pageNumber: 1, + morePages: true, + fixedHeader: false, + fixedColumns: 0 + })), + [] + ); + + //При изменении состояния сортировки + const handleOrderChanged = ({ orders }) => setState(pv => ({ ...pv, orders: [...orders], pageNumber: 1, reload: true })); + + //При изменении количества отображаемых страниц + const handlePagesCountChanged = () => setState(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reload: true })); + //При необходимости обновить данные таблицы useEffect(() => { - if (state.selectedPlan.NRN) { + //Если план выбран + if (plan.NRN) { const loadData = async () => { const data = await executeStored({ stored: "PKG_P8PANELS_MECHREC.FCPRODPLANSP_DEPT_DG_GET", args: { - NFCPRODPLAN: state.selectedPlan.NRN, + NFCPRODPLAN: plan.NRN, CORDERS: { VALUE: object2Base64XML(state.orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB }, NPAGE_NUMBER: state.pageNumber, NPAGE_SIZE: DATA_GRID_PAGE_SIZE_LARGE, @@ -76,6 +148,7 @@ const useDeptCostProdPlans = () => { setState(pv => ({ ...pv, ...data.XDATA_GRID, + init: true, columnsDef: data.XDATA_GRID.columnsDef ? [...data.XDATA_GRID.columnsDef] : pv.columnsDef, rows: pv.pageNumber == 1 ? [...(data.XDATA_GRID.rows || [])] : [...pv.rows, ...(data.XDATA_GRID.rows || [])], dataLoaded: true, @@ -83,31 +156,20 @@ const useDeptCostProdPlans = () => { morePages: (data.XDATA_GRID.rows || []).length >= DATA_GRID_PAGE_SIZE_LARGE })); }; + //Если необходимо перезагрузить if (state.reload) { loadData(); } } - }, [state.selectedPlan, state.reload, state.orders, state.pageNumber, state.dataLoaded, executeStored, SERV_DATA_TYPE_CLOB]); - - //При подключении компонента к странице - useEffect(() => { - const initPlans = async () => { - const data = await executeStored({ - stored: "PKG_P8PANELS_MECHREC.FCPRODPLAN_DEPT_INIT", - args: {}, - respArg: "COUT", - isArray: name => name === "XFCPRODPLANS", - attributeValueProcessor: (name, val) => (name === "SPERIOD" ? undefined : val) - }); - setState(pv => ({ ...pv, init: true, planList: [...(data?.XFCPRODPLANS || [])], planListLoaded: true })); - }; - if (!state.init) { - initPlans(); + //Если план не выбран и есть какие-то данные + if (!plan.NRN && state.dataLoaded) { + //Очищаем их + handleClear(); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [plan.NRN, state.reload, state.orders, state.pageNumber, state.dataLoaded, executeStored, SERV_DATA_TYPE_CLOB, handleClear]); - return [state, setState]; + //Возвращаем данные + return [state, setState, handleClear, handleOrderChanged, handlePagesCountChanged]; }; //Хук для таблицы маршрутных листов @@ -164,6 +226,7 @@ const useCostRouteLists = task => { task ]); + //Возвращаем данные return [costRouteLists, setCostRouteLists]; }; @@ -223,6 +286,7 @@ const useCostRouteListsSpecs = mainRowRN => { mainRowRN ]); + //Возвращаем данные return [costRouteListsSpecs, setCostRouteListsSpecs]; }; @@ -272,7 +336,8 @@ const useIncomFromDeps = task => { } }, [SERV_DATA_TYPE_CLOB, executeStored, incomFromDeps.dataLoaded, incomFromDeps.orders, incomFromDeps.pageNumber, incomFromDeps.reload, task]); + //Возвращаем данные return [incomFromDeps, setIncomFromDeps]; }; -export { useDeptCostProdPlans, useCostRouteLists, useCostRouteListsSpecs, useIncomFromDeps }; +export { useDeptCostProdPlans, useDeptCostProdPlanInfo, useCostRouteLists, useCostRouteListsSpecs, useIncomFromDeps }; diff --git a/app/panels/mech_rec_dept_cost_prod_plans/mech_rec_dept_cost_prod_plans.js b/app/panels/mech_rec_dept_cost_prod_plans/mech_rec_dept_cost_prod_plans.js index f037bb1..86f3c8a 100644 --- a/app/panels/mech_rec_dept_cost_prod_plans/mech_rec_dept_cost_prod_plans.js +++ b/app/panels/mech_rec_dept_cost_prod_plans/mech_rec_dept_cost_prod_plans.js @@ -12,7 +12,7 @@ import PropTypes from "prop-types"; //Контроль свойств компо import { Drawer, Fab, Box, List, ListItemButton, ListItemText, Typography, TextField, Link, Grid } from "@mui/material"; //Интерфейсные элементы import { APP_STYLES } from "../../../app.styles"; //Типовые стили import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы -import { useDeptCostProdPlans, useFilteredPlans } from "./hooks"; //Вспомогательные хуки +import { useDeptCostProdPlans, useFilteredPlans, useDeptCostProdPlanInfo } from "./hooks"; //Вспомогательные хуки import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../components/p8p_data_grid"; //Таблица данных import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений @@ -34,7 +34,8 @@ const TITLE_PADDING_BOTTOM = "20px"; //Стили const STYLES = { - PLANS_FINDER: { marginTop: "10px", marginLeft: "10px", width: "93%" }, + PLANS_FILTER: { paddingTop: "20px", display: "flex", flexDirection: "column", alignItems: "center", gap: "5px" }, + PLANS_FILTER_ITEM: { margin: "0px 10px", width: "93%" }, PLANS_LIST_ITEM_PRIMARY: { wordWrap: "break-word" }, PLANS_BUTTON: { position: "absolute" }, PLANS_DRAWER: { @@ -60,10 +61,18 @@ const STYLES = { FACT_VALUE: { color: "blue" } }; +//Имена полей компонента +const SFIELD_MONTH = "month"; + //------------------------------------ //Вспомогательные функции и компоненты //------------------------------------ +//Считывание текущего года и месяца в формате "YYYY-MM" +const getCurrentYearMonth = () => { + return `${new Date().getFullYear()}-${String(new Date().getMonth() + 1).padStart(2, "0")}`; +}; + //Генерация представления ячейки заголовка группы export const groupCellRender = ({ group }) => ({ cellStyle: STYLES.DATA_GRID_GROUP_CELL, @@ -88,7 +97,7 @@ const getRowBackgroudColor = row => { }; //Генерация заливки строки исходя от значений -const dataCellRender = ({ row, columnDef, handleProdOrderClick, handleMatresCodeClick }) => { +const dataCellRender = ({ row, columnDef, onProdOrderClick, onMatresCodeClick }) => { //Описываем общие свойства let cellProps = { title: row[columnDef.name] }; //Описываем общий стиль @@ -127,7 +136,7 @@ const dataCellRender = ({ row, columnDef, handleProdOrderClick, handleMatresCode cellProps, cellStyle, data: ( - handleProdOrderClick(row["NRN"])}> + onProdOrderClick(row["NRN"])}> {row[columnDef.name]} ) @@ -139,7 +148,7 @@ const dataCellRender = ({ row, columnDef, handleProdOrderClick, handleMatresCode cellProps, cellStyle: STYLES.DATA_GRID_CELL_MATRES_CODE(cellStyle, row), data: ( - handleMatresCodeClick(row["NRN"])}> + onMatresCodeClick(row["NRN"])}> {row[columnDef.name]} ) @@ -149,21 +158,38 @@ const dataCellRender = ({ row, columnDef, handleProdOrderClick, handleMatresCode }; //Список каталогов планов -const PlanList = ({ plans = [], selectedPlan, filter, setFilter, onClick } = {}) => { +const PlanList = ({ plans = [], selectedPlan, filter, onFilterChange, onClick } = {}) => { + //При изменении фильтра + const handleFilterChange = e => { + onFilterChange && onFilterChange(e); + }; + //Генерация содержимого return (
- { - setFilter(pv => ({ ...pv, planName: event.target.value })); - }} - > + + + + {plans.map(p => ( (onClick ? onClick(p) : null)}> @@ -181,7 +207,7 @@ PlanList.propTypes = { selectedPlan: PropTypes.object, onClick: PropTypes.func, filter: PropTypes.object, - setFilter: PropTypes.func + onFilterChange: PropTypes.func }; //----------- @@ -190,135 +216,135 @@ PlanList.propTypes = { //Корневая панель производственного плана цеха const MechRecDeptCostProdPlans = () => { - //Собственное состояние - таблица данных - const [state, setState] = useDeptCostProdPlans(); - //Состояние для фильтра каталогов - const [filter, setFilter] = useState({ planName: "" }); + const [filter, setFilter] = useState({ planName: "", month: getCurrentYearMonth() }); + + //Собственное состояние - таблица планов + const [plans, setPlans] = useDeptCostProdPlans(filter.month); + + //Собственное состояние - таблица информации + const [planInfo, setPlanInfo, onPlanInfoClear, onPlanInfoOrderChanged, onPlanInfoPagesCountChanged] = useDeptCostProdPlanInfo(plans.selected); //Массив отфильтрованных каталогов - const filteredPlanCtgls = useFilteredPlans(state.planList, filter); + const filteredPlanCtgls = useFilteredPlans(plans.rows, filter); //Подключение к контексту сообщений const { InlineMsgInfo } = useContext(MessagingСtx); //Выбор плана const selectPlan = plan => { - setState(pv => ({ + setPlans(pv => ({ ...pv, - showIncomeFromDeps: null, - showFcroutelst: null, - selectedPlan: plan, - showPlanList: false, - dataLoaded: false, - columnsDef: [], - orders: null, - rows: [], - reload: true, - pageNumber: 1, - morePages: true + selected: plan, + showPlanList: false })); + onPlanInfoClear(); }; //Сброс выбора плана - const unselectPlan = () => - setState(pv => ({ + const unselectPlan = () => { + setPlans(pv => ({ ...pv, - showIncomeFromDeps: null, - showFcroutelst: null, - selectedPlan: {}, - showPlanList: false, - dataLoaded: false, - columnsDef: [], - orders: null, - rows: [], - reload: true, - pageNumber: 1, - morePages: true + selected: {}, + showPlanList: false })); + onPlanInfoClear(); + }; //Обработка нажатия на элемент в списке планов const handlePlanClick = plan => { - if (state.selectedPlan.NRN != plan.NRN) selectPlan(plan); + if (plans.selected.NRN != plan.NRN) selectPlan(plan); else unselectPlan(); }; - //При изменении состояния сортировки - const handleOrderChanged = ({ orders }) => setState(pv => ({ ...pv, orders: [...orders], pageNumber: 1, reload: true })); + //При изменении состояния сортировки информации плана + const handlePlanInfoOrderChanged = ({ orders }) => onPlanInfoOrderChanged({ orders }); - //При изменении количества отображаемых страниц - const handlePagesCountChanged = () => setState(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reload: true })); + //При изменении количества отображаемых страниц информации плана + const handlePlanInfoPagesCountChanged = () => onPlanInfoPagesCountChanged(); //При нажатии на "Заказ" const handleProdOrderClick = planSp => { - setState(pv => ({ ...pv, showIncomeFromDeps: planSp })); + setPlanInfo(pv => ({ ...pv, showIncomeFromDeps: planSp })); }; //При нажатии на "Обозначение" const handleMatresCodeClick = planSp => { - setState(pv => ({ ...pv, showFcroutelst: planSp })); + setPlanInfo(pv => ({ ...pv, showFcroutelst: planSp })); }; + //При изменении фильтров + const handleFilterChange = e => + setFilter(pv => ({ ...pv, [e.target.name]: e.target.name === SFIELD_MONTH && !e.target.value ? getCurrentYearMonth() : e.target.value })); + //Генерация содержимого return ( - setState(pv => ({ ...pv, showPlanList: !pv.showPlanList }))}> + setPlans(pv => ({ ...pv, showPlanList: !pv.showPlanList }))}> Планы setState(pv => ({ ...pv, showPlanList: false }))} + open={plans.showPlanList} + onClose={() => setPlans(pv => ({ ...pv, showPlanList: false }))} sx={STYLES.PLANS_DRAWER} > - {state.dataLoaded ? ( - state.rows.length === 0 ? ( + {planInfo.dataLoaded ? ( + planInfo.rows.length === 0 ? ( ) : ( - {`Производственный план цеха №${state.selectedPlan.SSUBDIV} на ${state.selectedPlan.SPERIOD}`} + {`Производственный план цеха №${plans.selected.SSUBDIV} на ${plans.selected.SPERIOD}`} dataCellRender({ ...prms, handleProdOrderClick, handleMatresCodeClick })} + morePages={planInfo.morePages} + reloading={planInfo.reload} + onOrderChanged={handlePlanInfoOrderChanged} + onPagesCountChanged={handlePlanInfoPagesCountChanged} + dataCellRender={prms => + dataCellRender({ + ...prms, + onProdOrderClick: handleProdOrderClick, + onMatresCodeClick: handleMatresCodeClick + }) + } groupCellRender={groupCellRender} /> ) - ) : !state.selectedPlan.NRN ? ( + ) : !plans.selected.NRN ? ( ) : null} - {state.showIncomeFromDeps ? ( - handleProdOrderClick(null)} /> + {planInfo.showIncomeFromDeps ? ( + handleProdOrderClick(null)} /> + ) : null} + {planInfo.showFcroutelst ? ( + handleMatresCodeClick(null)} /> ) : null} - {state.showFcroutelst ? handleMatresCodeClick(null)} /> : null} ); }; diff --git a/db/PKG_P8PANELS_MECHREC.pck b/db/PKG_P8PANELS_MECHREC.pck index 48eee92..5f72714 100644 --- a/db/PKG_P8PANELS_MECHREC.pck +++ b/db/PKG_P8PANELS_MECHREC.pck @@ -150,7 +150,8 @@ create or replace package PKG_P8PANELS_MECHREC as /* Инициализация записей раздела "Планы и отчеты производства изделий" */ procedure FCPRODPLAN_DEPT_INIT ( - COUT out clob -- Список записей раздела "Планы и отчеты производства изделий" + SMONTH in varchar2, -- Месяц отбора (в формате "YYYY-MM") + COUT out clob -- Список записей раздела "Планы и отчеты производства изделий" ); /* Выдать задания сменного задания */ @@ -4316,6 +4317,7 @@ create or replace package body PKG_P8PANELS_MECHREC as /* Инициализация записей раздела "Планы и отчеты производства изделий" */ procedure FCPRODPLAN_DEPT_INIT ( + SMONTH in varchar2, -- Месяц отбора (в формате "YYYY-MM") COUT out clob -- Список записей раздела "Планы и отчеты производства изделий" ) is @@ -4327,7 +4329,7 @@ create or replace package body PKG_P8PANELS_MECHREC as /* Считываем версию контрагентов */ FIND_VERSION_BY_COMPANY(NCOMPANY => NCOMPANY, SUNITCODE => 'AGNLIST', NVERSION => NVERSION); /* Определяем период записей */ - P_FIRST_LAST_DAY(DCALCDATE => sysdate, DBGNDATE => DDATE_FROM, DENDDATE => DDATE_TO); + P_FIRST_LAST_DAY(DCALCDATE => TO_DATE(SMONTH, 'yyyy-mm'), DBGNDATE => DDATE_FROM, DENDDATE => DDATE_TO); /* Начинаем формирование XML */ PKG_XFAST.PROLOGUE(ITYPE => PKG_XFAST.CONTENT_); /* Открываем корень */