ЦИТК-831, ЦИТК-833 - ТОиР "Выполнение работ" - фиксированные строки, колонки, сохранение фильтра

This commit is contained in:
Mim 2024-08-26 18:03:11 +03:00
commit 59d85451ec
4 changed files with 148 additions and 38 deletions

View File

@ -8,7 +8,7 @@
//---------------------
import React, { useState, useContext, useCallback, useEffect } from "react"; //Классы React
import { Grid, Paper, Box } from "@mui/material"; //Интерфейсные компоненты
import { 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"; //Контекст взаимодействия с сервером
@ -18,6 +18,33 @@ import { headCellRender, dataCellRender, groupCellRender, DIGITS_REG_EXP, MONTH_
import { TEXTS } from "../../../app.text"; //Тектовые ресурсы и константы
import { Filter } from "./filter"; //Компонент фильтра
import { FilterDialog } from "./filter_dialog"; //Компонент диалогового окна фильтра отбора
import { useWindowResize } from "./hooks"; //Пользовательские хуки
//---------
//Константы
//---------
//Высота меню Парус (пиксели)
const pxOuterMenuH = 53;
//Высота заголовка панели (пиксели)
const pxPanelHeaderH = 64;
//Минимальная ширина таблицы (пиксели)
const minGridW = 800;
//Минимальная высота таблицы (пиксели)
const minGridH = 200;
//Стили
const STYLES = {
BOX_ROW: { display: "flex", justifyContent: "center", alignItems: "center" },
GRID_PADDING: { paddingTop: 1, paddingBottom: 1 },
GRID_SIZES: (width, height) => ({
padding: "0px",
minWidth: minGridW,
maxWidth: width * 0.975 > minGridW ? width * 0.975 : minGridW,
minHeight: minGridH,
maxHeight: (height - pxOuterMenuH - pxPanelHeaderH) * 0.975 > minGridH ? (height - pxOuterMenuH - pxPanelHeaderH) * 0.975 : minGridH
})
};
//-----------
//Тело модуля
@ -31,6 +58,8 @@ const EqsPrfrm = () => {
columnsDef: [],
groups: [],
rows: [],
fixedHeader: false,
fixedColumns: 0,
reload: false
});
@ -39,6 +68,7 @@ const EqsPrfrm = () => {
isOpen: false,
isDefault: false,
isSetByUser: false,
needSave: false,
values: {
belong: "",
prodObj: "",
@ -128,6 +158,8 @@ const EqsPrfrm = () => {
...pv,
columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
rows: [...(data.XROWS || [])],
fixedHeader: data.XDATA_GRID.fixedHeader,
fixedColumns: data.XDATA_GRID.fixedColumns,
groups: [...(data.XGROUPS || [])],
dataLoaded: true,
reload: false
@ -135,7 +167,7 @@ const EqsPrfrm = () => {
}
}, [dataGrid.reload, filter, executeStored]);
//Загрузка значений фильра по умолчанию
//Загрузка значений фильтра по умолчанию
const loadDefaultFilter = useCallback(async () => {
const data = await executeStored({
stored: "PKG_P8PANELS_EQUIPSRV.GET_DEFAULT_FP",
@ -148,6 +180,17 @@ const EqsPrfrm = () => {
}));
}, [executeStored]);
//Загрузка значений фильтра из локального хранилища браузера
const loadLocalFilter = useCallback(async () => {
let vs = filter.values;
Object.keys(vs).map(function (k) {
vs[k] =
k == "fromMonth" || k == "fromYear" || k == "toMonth" || k == "toYear" ? Number(localStorage.getItem(k)) : localStorage.getItem(k);
});
setFilter(pv => ({ ...pv, isDefault: true, values: { ...vs } }));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
//Отбор документа (ТОиР или Ремонтных ведомостей) по ячейке даты
const showEquipSrv = async ({ date, workType, info }) => {
const [techName, servKind] = info.split("_");
@ -180,7 +223,7 @@ const EqsPrfrm = () => {
const setFilterOpen = isOpen => setFilter(pv => ({ ...pv, isOpen }));
//Установить значение фильтра
const setFilterValues = values => setFilter(pv => ({ ...pv, isSetByUser: true, values: { ...values } }));
const setFilterValues = values => setFilter(pv => ({ ...pv, isSetByUser: true, needSave: true, values: { ...values } }));
//Отработка события скрытия/раскрытия ячейки даты
const handleClick = (e, ref) => {
@ -223,16 +266,26 @@ const EqsPrfrm = () => {
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [refIsDeprecated]);
//При закрытии панели
useEffect(() => {
filter.needSave
? window.addEventListener("beforeunload", function () {
Object.keys(filter.values).map(function (k) {
localStorage.setItem(k, filter.values[k]);
});
})
: null;
}, [filter.needSave, filter.values]);
//При загрузке фильтра по умолчанию
useEffect(() => {
if (filter.isDefault) setFilterOpen(true);
}, [filter.isDefault]);
//При подключении к страницк
//При подключении к странице
useEffect(() => {
loadDefaultFilter();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
localStorage.getItem("belong") ? loadLocalFilter() : loadDefaultFilter();
}, [loadDefaultFilter, loadLocalFilter]);
//При открытии диалога фильтра
const handleFilterClick = () => setFilterOpen(true);
@ -246,32 +299,37 @@ const EqsPrfrm = () => {
//При закрытии диалога фильтра
const handleFilterCancel = () => setFilterOpen(false);
//Состояние ширины и высоты рабочей области окна
const [width, height] = useWindowResize();
//Генерация содержимого
return (
<div>
{filter.isOpen ? <FilterDialog initial={filter.values} onOk={handleFilterOk} onCancel={handleFilterCancel} /> : null}
<Filter filter={filter.values} onClick={handleFilterClick} />
{dataGrid.dataLoaded ? (
<Paper variant="outlined">
<Grid container spacing={1}>
<Grid item xs={12}>
<Box p={1}>
<P8PDataGrid
{...P8P_DATA_GRID_CONFIG_PROPS}
columnsDef={dataGrid.columnsDef}
groups={dataGrid.groups}
rows={dataGrid.rows}
size={P8P_DATA_GRID_SIZE.LARGE}
reloading={dataGrid.reload}
headCellRender={prms => headCellRender({ ...prms }, handleClick)}
dataCellRender={prms => dataCellRender({ ...prms }, showEquipSrv)}
groupCellRender={prms => groupCellRender({ ...prms })}
showCellRightBorder={true}
/>
</Box>
</Grid>
</Grid>
</Paper>
<Box sx={{ ...STYLES.GRID_PADDING, ...STYLES.BOX_ROW }}>
<P8PDataGrid
{...P8P_DATA_GRID_CONFIG_PROPS}
containerComponentProps={{
elevation: 6,
style: {
...STYLES.GRID_SIZES(width, height)
}
}}
columnsDef={dataGrid.columnsDef}
groups={dataGrid.groups}
rows={dataGrid.rows}
fixedHeader={dataGrid.fixedHeader}
fixedColumns={dataGrid.fixedColumns}
size={P8P_DATA_GRID_SIZE.LARGE}
reloading={dataGrid.reload}
headCellRender={prms => headCellRender({ ...prms }, handleClick)}
dataCellRender={prms => dataCellRender({ ...prms }, width * 0.2, showEquipSrv)}
groupCellRender={prms => groupCellRender({ ...prms })}
showCellRightBorder={true}
/>
</Box>
) : null}
</div>
);

View File

@ -0,0 +1,36 @@
/*
Парус 8 - Панели мониторинга - ТОиР - Выполнение работ
Пользовательские хуки
*/
//---------------------
//Подключение библиотек
//---------------------
import { useState, useLayoutEffect } from "react"; //Классы React
//-----------
//Тело модуля
//-----------
//Хук для отработки изменений ширины и высоты рабочей области окна
const useWindowResize = () => {
//Состояние размера рабочей области
const [size, setSize] = useState([0, 0]);
//При изменении размера
useLayoutEffect(() => {
function updateSize() {
setSize([document.documentElement.clientWidth, document.documentElement.clientHeight]);
}
window.addEventListener("resize", updateSize);
updateSize();
return () => window.removeEventListener("resize", updateSize);
}, []);
return size;
};
//--------------
//Интерфейс хука
//--------------
export { useWindowResize };

View File

@ -20,7 +20,7 @@ export const MONTH_NAME_REG_EXP = /_\d{4}_\d{1,2}/;
export const DAY_NAME_REG_EXP = /_\d{4}_\d{1,2}_\d{1,2}/;
//Стили
export const STYLES = {
const STYLES = {
HIDE_CELL_STYLE: { display: "none" },
HCR_MAIN_STYLE: { border: "1px solid rgba(0, 0, 0)", textAlign: "center" },
HCR_DATE_STYLE: { padding: "5px", minWidth: "25px", maxWidth: "25px" },
@ -31,7 +31,12 @@ export const STYLES = {
DCR_FACT_NOT_RELATED_CELL_STYLE: { cursor: "pointer", backgroundColor: "crimson", border: "1px solid rgba(0, 0, 0) !important" },
DCR_DOUBLE_CELL: { padding: "unset" },
DCR_DOUBLE_CELL_GRID_ITEM: backgroundColor => ({ cursor: "pointer", backgroundColor }),
HIDDEN_PARAGRAPH: { display: "none" }
HIDDEN_PARAGRAPH: { display: "none" },
STICKY_WIDTH_UNSET: { minWidth: "unset", maxWidth: "unset" },
FIRST_STICKY_CELL: { left: "0px" },
OBJINFO_WIDTH: width => ({ minWidth: width, maxWidth: width }),
OBJINFO_WRKNAME_WIDTH: width => ({ minWidth: width * 0.6, maxWidth: width * 0.6 }),
WRKTYPE_WIDTH: width => ({ left: width * 0.6, minWidth: width - width * 0.4, maxWidth: width - width * 0.4 })
};
//-----------
@ -71,7 +76,11 @@ export const headCellRender = ({ columnDef }, hClick) => {
//Объединение нужных колонок и строк
if (columnDef.name == "SINFO" || columnDef.name == "SWRKTYPE") {
cellProps = { colSpan: 2 };
if (columnDef.name == "SINFO") cellProps = { ...cellProps, rowSpan: 2 };
cellStyle = { ...cellStyle, ...STYLES.STICKY_WIDTH_UNSET };
if (columnDef.name == "SINFO") {
cellProps = { ...cellProps, rowSpan: 2 };
cellStyle = { ...cellStyle, ...STYLES.FIRST_STICKY_CELL };
}
}
//Изменения в заголовках с датами
if (columnDef.visible && DAY_NAME_REG_EXP.test(columnDef.name)) {
@ -82,7 +91,7 @@ export const headCellRender = ({ columnDef }, hClick) => {
};
//Генерация представления ячейки
export const dataCellRender = ({ row, columnDef }, showEquipSrv) => {
export const dataCellRender = ({ row, columnDef }, width, showEquipSrv) => {
let curParent = "";
let cellDate;
let cellStyle = STYLES.DCR_MAIN_STYLE;
@ -93,10 +102,10 @@ export const dataCellRender = ({ row, columnDef }, showEquipSrv) => {
//Ячейка "Информация по объекту ремонта"
if (columnDef.name == "SOBJINFO") {
cellProps = { colSpan: 2 };
cellStyle = { ...cellStyle, ...STYLES.DCR_OBJECT_INFO_STYLE };
cellStyle = { ...cellStyle, ...STYLES.DCR_OBJECT_INFO_STYLE, ...STYLES.OBJINFO_WIDTH(width) };
}
//Ячейка "Тип работ"
if (columnDef.name == "SWRKTYPE") cellStyle = STYLES.HIDE_CELL_STYLE;
if (columnDef.name == "SWRKTYPE") cellStyle = { ...STYLES.HIDE_CELL_STYLE };
//Ячейки колонок месяцев
if (columnDef.parent == "" && columnDef.expandable == true && columnDef.expanded == false) {
curParent = columnDef.name;
@ -118,13 +127,17 @@ export const dataCellRender = ({ row, columnDef }, showEquipSrv) => {
}
//Строка плана по объекту ремонта
if (columnDef.name == "SOBJINFO" && row["SWRKTYPE"] == "План") {
cellStyle = { ...cellStyle };
cellStyle = { ...cellStyle, ...STYLES.FIRST_STICKY_CELL, ...STYLES.OBJINFO_WRKNAME_WIDTH(width) };
cellProps = { rowSpan: 2 };
}
//Строка факта по объекту ремонта
if (columnDef.name == "SOBJINFO" && row["SWRKTYPE"] == "Факт") {
cellStyle = { display: "none" };
}
//Ячейка план/факт
if (columnDef.name == "SWRKTYPE") {
cellStyle = { ...cellStyle, ...STYLES.WRKTYPE_WIDTH(width) };
}
//Закрашивание ячеек
switch (row[columnDef.name]) {
case "blue":

View File

@ -399,21 +399,24 @@ create or replace package body PKG_P8PANELS_EQUIPSRV as
/* Определим дату конца периода */
NTODATE := LAST_DAY(TO_DATE('01.' || LPAD(TO_CHAR(NTOMONTH), 2, '0') || '.' || TO_CHAR(NTOYEAR), 'dd.mm.yyyy'));
/* Инициализируем таблицу данных */
RDG := PKG_P8PANELS_VISUAL.TDATA_GRID_MAKE();
RDG := PKG_P8PANELS_VISUAL.TDATA_GRID_MAKE(BFIXED_HEADER => true, NFIXED_COLUMNS => 2);
/* Формируем структуру заголовка */
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
SNAME => 'SOBJINFO',
SCAPTION => 'Информация по объекту ремонта',
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR);
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
NWIDTH => 80);
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
SNAME => 'SINFO',
SCAPTION => 'Объект ремонта',
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR);
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
NWIDTH => 80);
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
SNAME => 'SWRKTYPE',
SCAPTION => 'Тип работ',
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
SPARENT => 'SINFO');
SPARENT => 'SINFO',
NWIDTH => 80);
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
SNAME => 'NRN',
SCAPTION => 'Рег. номер',