forked from CITKParus/P8-Panels
517 lines
24 KiB
JavaScript
517 lines
24 KiB
JavaScript
/*
|
||
Парус 8 - Панели мониторинга - ПУП - Выдача сменного задания
|
||
Компонент панели: Таблица информации о строках сменного задания
|
||
*/
|
||
|
||
//---------------------
|
||
//Подключение библиотек
|
||
//---------------------
|
||
|
||
import React, { useState } from "react"; //Классы React
|
||
import PropTypes from "prop-types"; //Контроль свойств компонента
|
||
import {
|
||
Typography,
|
||
Box,
|
||
Checkbox,
|
||
Grid,
|
||
Icon,
|
||
Button,
|
||
Dialog,
|
||
DialogContent,
|
||
TextField,
|
||
DialogActions,
|
||
Tooltip,
|
||
Stack,
|
||
DialogTitle
|
||
} from "@mui/material"; //Интерфейсные элементы
|
||
import { BUTTONS } from "../../../app.text"; //Текстовые ресурсы
|
||
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
|
||
import { P8PDataGrid, P8P_DATA_GRID_SIZE, P8P_DATA_GRID_MORE_HEIGHT } from "../../components/p8p_data_grid"; //Таблица данных
|
||
import { APP_STYLES } from "../../../app.styles"; //Типовые стили
|
||
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
|
||
import { useCostJobsSpecs, useEquipConfiguration } from "./hooks"; //Собственные хуки таблиц
|
||
import { MAIN_HEADER_HEIGHT, SUB_HEADER_HEIGHT } from "./mech_rec_cost_jobs_manage"; //Заглавный компонент панели
|
||
|
||
//---------
|
||
//Константы
|
||
//---------
|
||
|
||
//Мнемокод раздела операций
|
||
const UNIT_COST_JOBS_SPECS = "CostJobsSpecs";
|
||
|
||
//Мнемокод раздела рабочих центров
|
||
const UNIT_COST_EQUIPMENT = "CostEquipment";
|
||
|
||
//Высота заголовка таблицы
|
||
const TABLE_HEADER_HEIGHT = "35px";
|
||
|
||
//Высота панели кнопок таблицы
|
||
const TABLE_BUTTONS_HEIGHT = "35px";
|
||
|
||
//Отступ таблицы
|
||
const TABLE_PADDING_TOP = "15px";
|
||
|
||
//Стили
|
||
const STYLES = {
|
||
CONTAINER: { textAlign: "center" },
|
||
DATA_GRID_CONTAINER: morePages => ({
|
||
height: `calc(100vh - ${APP_BAR_HEIGHT} - ${MAIN_HEADER_HEIGHT} - ${SUB_HEADER_HEIGHT} - ${TABLE_HEADER_HEIGHT} - ${TABLE_BUTTONS_HEIGHT} - ${TABLE_PADDING_TOP} - 32px - ${
|
||
morePages ? P8P_DATA_GRID_MORE_HEIGHT : "0px"
|
||
})`,
|
||
...APP_STYLES.SCROLL
|
||
}),
|
||
TABLE: { paddingTop: TABLE_PADDING_TOP },
|
||
TABLE_HEADER: { height: TABLE_HEADER_HEIGHT, overflow: "hidden" },
|
||
TABLE_BUTTONS: { display: "flex", justifyContent: "flex-end", height: TABLE_BUTTONS_HEIGHT, overflow: "hidden" },
|
||
CHECK_BOX: { textAlign: "center" },
|
||
JOBS_INFO: { textAlign: "center" },
|
||
EQUIPMENT_INFO: { textAlign: "center" }
|
||
};
|
||
|
||
//Цвета
|
||
const colors = {
|
||
LINKED: "#bce0de",
|
||
UNAVAILABLE: "#949494",
|
||
WITH_EQCONFIG: "#82df83"
|
||
};
|
||
|
||
//---------------------------------------------
|
||
//Вспомогательные функции и компоненты
|
||
//---------------------------------------------
|
||
|
||
//Форматирование значения ячейки
|
||
const dataCellRender = ({ row, columnDef, handleSelectChange, sUnit, selectedRow, selectedJobSpec }) => {
|
||
//Стиль
|
||
let cellStyle = {};
|
||
//Если это рабочие центры
|
||
if (sUnit === UNIT_COST_EQUIPMENT) {
|
||
//Признак недоступности
|
||
let disabled = true;
|
||
//Если в выбранной строке смены указано рабочее место
|
||
if (selectedJobSpec.NEQCONFIG) {
|
||
//Если это текущее рабочее место
|
||
if (row["NRN"] === selectedJobSpec.NEQCONFIG) {
|
||
//Подсвечиваем строку рабочего места
|
||
cellStyle = { backgroundColor: colors.LINKED };
|
||
}
|
||
} else {
|
||
//Если выбрана строка смены
|
||
if (selectedJobSpec.NRN) {
|
||
//Если на текущее рабочее место возможно добавить задание
|
||
if (row["NLOADING"] < 100 && row["NEQUIPMENT"] === selectedJobSpec.NEQUIP_PLAN) {
|
||
//Подсвечиваем строку рабочего места
|
||
cellStyle = { backgroundColor: colors.LINKED };
|
||
disabled = false;
|
||
}
|
||
}
|
||
}
|
||
//Если рабочий центр загружен
|
||
if (row["NLOADING"] >= 100) {
|
||
//Если поле не поле выбора
|
||
if (columnDef.name !== "NSELECT") {
|
||
//Указываем, что рабочее место недоступно
|
||
cellStyle = { ...cellStyle, color: colors.UNAVAILABLE };
|
||
}
|
||
}
|
||
//Для колонки выбора
|
||
if (columnDef.name === "NSELECT") {
|
||
return {
|
||
cellStyle,
|
||
data: (
|
||
<Box sx={STYLES.CHECK_BOX}>
|
||
<Checkbox
|
||
disabled={disabled}
|
||
checked={row["NRN"] === selectedRow}
|
||
onChange={() => handleSelectChange({ NRN: row["NRN"], SUNIT: sUnit, BFULL_LOADED: row["NLOADING"] >= 100 })}
|
||
/>
|
||
</Box>
|
||
)
|
||
};
|
||
}
|
||
//Отформатированная колонка
|
||
return {
|
||
cellStyle,
|
||
data: row[columnDef.name]
|
||
};
|
||
}
|
||
//Если это сменное задание
|
||
if (sUnit === UNIT_COST_JOBS_SPECS) {
|
||
//Если указан станок
|
||
if (row["SEQCONFIG"]) {
|
||
//Подсвечиваем сменное задание зеленым
|
||
cellStyle = { ...cellStyle, backgroundColor: colors.WITH_EQCONFIG };
|
||
}
|
||
//Для колонки выбора
|
||
if (columnDef.name === "NSELECT") {
|
||
return {
|
||
cellStyle,
|
||
data: (
|
||
<Box sx={STYLES.CHECK_BOX}>
|
||
<Checkbox
|
||
disabled={row["DBEG_FACT"] ? true : false}
|
||
checked={row["NRN"] === selectedRow}
|
||
onChange={() =>
|
||
handleSelectChange({
|
||
NRN: row["NRN"],
|
||
SUNIT: sUnit,
|
||
NEQCONFIG: row["NEQCONFIG"],
|
||
NEQUIP_PLAN: row["NEQUIP_PLAN"],
|
||
NQUANT_PLAN: row["NQUANT_PLAN"]
|
||
})
|
||
}
|
||
/>
|
||
</Box>
|
||
)
|
||
};
|
||
}
|
||
//Отформатированная колонка
|
||
return {
|
||
cellStyle,
|
||
data: row[columnDef.name]
|
||
};
|
||
}
|
||
//Необрабатываемый раздел
|
||
return {
|
||
data: row[columnDef.name]
|
||
};
|
||
};
|
||
|
||
//Генерация представления ячейки заголовка группы
|
||
export const headCellRender = ({ columnDef }) => {
|
||
if (columnDef.name === "NSELECT") {
|
||
return {
|
||
stackStyle: { padding: "2px", justifyContent: "space-around" },
|
||
data: <Icon>done</Icon>
|
||
};
|
||
} else {
|
||
return {
|
||
stackStyle: { padding: "2px" },
|
||
data: columnDef.caption
|
||
};
|
||
}
|
||
};
|
||
|
||
//Диалог включения станка в сменное задание
|
||
const CostJobsSpecsInclude = ({ includeEquipment, setIncludeEquipment, setCostJobsSpecs, setEquipConfiguration, includeEquipConfiguration }) => {
|
||
//Собственное состояние - Значение приоритета
|
||
const [state, setState] = useState(includeEquipment.NVALUE);
|
||
|
||
//При закрытии включения станка
|
||
const handlePriorEditClose = () => {
|
||
setIncludeEquipment({
|
||
NFCJOBSSP: null,
|
||
NEQCONFIG: null,
|
||
NVALUE: 0
|
||
});
|
||
};
|
||
|
||
//При включении станка в строку сменного задания
|
||
const costJobsSpecIncludeCostEquipment = () => {
|
||
//Делаем асинхронно, чтобы при ошибке ничего не обновлять
|
||
const includeAsync = async () => {
|
||
//Включаем станок в строку сменного задания
|
||
try {
|
||
await includeEquipConfiguration({
|
||
NEQCONFIG: includeEquipment.NEQCONFIG,
|
||
NFCJOBSSP: includeEquipment.NFCJOBSSP,
|
||
NQUANT_PLAN: state
|
||
});
|
||
//Необходимо обновить все данные
|
||
setCostJobsSpecs(pv => ({ ...pv, selectedRow: {}, pageNumber: 1, reload: true }));
|
||
setEquipConfiguration(pv => ({ ...pv, selectedRow: {}, pageNumber: 1, reload: true }));
|
||
handlePriorEditClose();
|
||
} catch (e) {
|
||
throw new Error(e.message);
|
||
}
|
||
};
|
||
//Включаем станок асинхронно
|
||
includeAsync();
|
||
};
|
||
|
||
return (
|
||
<Dialog open onClose={() => handlePriorEditClose()}>
|
||
<DialogTitle>Включить в задание</DialogTitle>
|
||
<DialogContent>
|
||
<Box>
|
||
<TextField
|
||
name="editInculdeValue"
|
||
label="Количество"
|
||
variant="standard"
|
||
fullWidth
|
||
InputProps={{
|
||
type: "number",
|
||
inputProps: {
|
||
max: includeEquipment.NVALUE,
|
||
min: 0
|
||
}
|
||
}}
|
||
value={state}
|
||
onChange={event => {
|
||
var value = parseInt(event.target.value, 10);
|
||
if (value > includeEquipment.NVALUE) {
|
||
value = includeEquipment.NVALUE;
|
||
}
|
||
if (value < 0) {
|
||
value = 0;
|
||
}
|
||
setState(value);
|
||
}}
|
||
/>
|
||
<Box></Box>
|
||
</Box>
|
||
</DialogContent>
|
||
<DialogActions>
|
||
<Button onClick={costJobsSpecIncludeCostEquipment}>{BUTTONS.OK}</Button>
|
||
<Button onClick={() => handlePriorEditClose(null)}>{BUTTONS.CANCEL}</Button>
|
||
</DialogActions>
|
||
</Dialog>
|
||
);
|
||
};
|
||
|
||
//Контроль свойств - Диалог включения станка в сменное задание
|
||
CostJobsSpecsInclude.propTypes = {
|
||
includeEquipment: PropTypes.object.isRequired,
|
||
setIncludeEquipment: PropTypes.func.isRequired,
|
||
setCostJobsSpecs: PropTypes.func.isRequired,
|
||
setEquipConfiguration: PropTypes.func.isRequired,
|
||
includeEquipConfiguration: PropTypes.func.isRequired
|
||
};
|
||
|
||
//-----------
|
||
//Тело модуля
|
||
//-----------
|
||
|
||
//Таблица информации о строках сменного задания
|
||
const CostJobsSpecsDataGrid = ({ task, haveNote, fromAction }) => {
|
||
//Собственное состояние - Включение в задание
|
||
const [includeEquipment, setIncludeEquipment] = useState({ NFCJOBSSP: null, NEQCONFIG: null, NVALUE: 0 });
|
||
|
||
//Собственное состояние - таблица данных сменных заданий
|
||
const [costJobsSpecs, setCostJobsSpecs, issueCostJobsSpecs] = useCostJobsSpecs(task);
|
||
|
||
//Собственное состояние - таблица рабочих центров
|
||
const [equipConfiguration, setEquipConfiguration, includeEquipConfiguration, excludeEquipConfiguration] = useEquipConfiguration(task, fromAction);
|
||
|
||
//При изменении состояния сортировки операций
|
||
const costJobsSpecOrderChanged = ({ orders }) => setCostJobsSpecs(pv => ({ ...pv, orders: [...orders], pageNumber: 1, reload: true }));
|
||
|
||
//При изменении количества отображаемых страниц операций
|
||
const costJobsSpecPagesCountChanged = () => setCostJobsSpecs(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reload: true }));
|
||
|
||
//При изменении состояния сортировки рабочих центров
|
||
const costEquipmentOrderChanged = ({ orders }) => setEquipConfiguration(pv => ({ ...pv, orders: [...orders], pageNumber: 1, reload: true }));
|
||
|
||
//При изменении количества отображаемых страниц рабочих центров
|
||
const costEquipmentPagesCountChanged = () => setEquipConfiguration(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reload: true }));
|
||
|
||
//При исключении станка из строки сменного задания
|
||
const costJobsSpecExcludeCostEquipment = () => {
|
||
//Делаем асинхронно, чтобы при ошибке ничего не обновлять
|
||
const excludeAsync = async () => {
|
||
//Исключаем станок из строки сменного задания
|
||
try {
|
||
await excludeEquipConfiguration({
|
||
NFCJOBSSP: costJobsSpecs.selectedRow.NRN
|
||
});
|
||
//Необходимо обновить данные
|
||
setCostJobsSpecs(pv => ({ ...pv, selectedRow: {}, pageNumber: 1, reload: true }));
|
||
setEquipConfiguration(pv => ({ ...pv, selectedRow: {}, pageNumber: 1, reload: true }));
|
||
} catch (e) {
|
||
throw new Error(e.message);
|
||
}
|
||
};
|
||
//Исключаем станок асинхронно
|
||
excludeAsync();
|
||
};
|
||
|
||
//Выдача задания операции
|
||
const costJobsSpecIssue = () => {
|
||
//Делаем асинхронно, чтобы при ошибке ничего не обновлять
|
||
const issueAsync = async () => {
|
||
//Включаем оборудование в операции
|
||
try {
|
||
await issueCostJobsSpecs({ NFCJOBS: task });
|
||
//Необходимо обновить данные
|
||
setCostJobsSpecs(pv => ({ ...pv, selectedRow: {}, pageNumber: 1, reload: true }));
|
||
setEquipConfiguration(pv => ({ ...pv, selectedRow: {}, pageNumber: 1, reload: true }));
|
||
} catch (e) {
|
||
throw new Error(e.message);
|
||
}
|
||
};
|
||
//Выдаем задание асинхронно
|
||
issueAsync();
|
||
};
|
||
|
||
//При изменение состояния выбора
|
||
const handleSelectChange = prms => {
|
||
//Выбранный элемент
|
||
let selectedRow = null;
|
||
//Исходим от раздела
|
||
switch (prms.SUNIT) {
|
||
//Сменное задание
|
||
case UNIT_COST_JOBS_SPECS:
|
||
//Определяем это новое отмеченное сменное задание или сброс старого
|
||
selectedRow = costJobsSpecs.selectedRow.NRN ? (costJobsSpecs.selectedRow.NRN === prms.NRN ? null : prms.NRN) : prms.NRN;
|
||
//Актуализируем строки
|
||
setCostJobsSpecs(pv => ({
|
||
...pv,
|
||
selectedRow: selectedRow
|
||
? { NRN: selectedRow, NEQCONFIG: prms.NEQCONFIG, NEQUIP_PLAN: prms.NEQUIP_PLAN, NQUANT_PLAN: prms.NQUANT_PLAN }
|
||
: { NRN: null, NEQCONFIG: null, NEQUIP_PLAN: null, NQUANT_PLAN: null }
|
||
}));
|
||
//Выходим
|
||
break;
|
||
//Рабочие центры
|
||
case UNIT_COST_EQUIPMENT:
|
||
//Определяем это новое отмеченное сменное задание или сброс старого
|
||
selectedRow = equipConfiguration.selectedRow.NRN ? (equipConfiguration.selectedRow.NRN === prms.NRN ? null : prms.NRN) : prms.NRN;
|
||
//Актуализируем строки
|
||
setEquipConfiguration(pv => ({
|
||
...pv,
|
||
selectedRow: selectedRow ? { NRN: selectedRow, BFULL_LOADED: prms.BFULL_LOADED } : { NRN: null, BFULL_LOADED: null }
|
||
}));
|
||
//Выходим
|
||
break;
|
||
default:
|
||
return;
|
||
}
|
||
};
|
||
|
||
//При открытии окна включения в задание
|
||
const handleIncludeEquipmentOpen = () => {
|
||
//Актуализируем строки
|
||
setIncludeEquipment({
|
||
NFCJOBSSP: costJobsSpecs.selectedRow.NRN,
|
||
NEQCONFIG: equipConfiguration.selectedRow.NRN,
|
||
NVALUE: costJobsSpecs.selectedRow.NQUANT_PLAN
|
||
});
|
||
};
|
||
|
||
//Генерация содержимого
|
||
return (
|
||
<div style={STYLES.CONTAINER}>
|
||
<Grid container spacing={2}>
|
||
<Grid item sx={STYLES.JOBS_INFO} xs={6}>
|
||
<Typography sx={STYLES.TABLE_HEADER} variant={"h6"} color={"text.secondary"}>
|
||
Сменное задание
|
||
</Typography>
|
||
{costJobsSpecs.dataLoaded ? (
|
||
<>
|
||
<Box sx={STYLES.TABLE_BUTTONS}>
|
||
<Tooltip title={haveNote ? "Сменное задание имеет строку с примечанием" : null}>
|
||
<Button variant="contained" size="small" disabled={haveNote} onClick={costJobsSpecIssue}>
|
||
Выдать задания
|
||
</Button>
|
||
</Tooltip>
|
||
</Box>
|
||
<Box sx={STYLES.TABLE}>
|
||
<P8PDataGrid
|
||
{...P8P_DATA_GRID_CONFIG_PROPS}
|
||
containerComponentProps={{ sx: STYLES.DATA_GRID_CONTAINER(costJobsSpecs.morePages), elevation: 1 }}
|
||
columnsDef={costJobsSpecs.columnsDef}
|
||
rows={costJobsSpecs.rows}
|
||
size={P8P_DATA_GRID_SIZE.SMALL}
|
||
morePages={costJobsSpecs.morePages}
|
||
reloading={costJobsSpecs.reload}
|
||
onOrderChanged={costJobsSpecOrderChanged}
|
||
onPagesCountChanged={costJobsSpecPagesCountChanged}
|
||
dataCellRender={prms =>
|
||
dataCellRender({
|
||
...prms,
|
||
handleSelectChange,
|
||
sUnit: UNIT_COST_JOBS_SPECS,
|
||
selectedRow: costJobsSpecs.selectedRow.NRN,
|
||
selectedJobSpec: costJobsSpecs.selectedRow
|
||
})
|
||
}
|
||
headCellRender={prms => headCellRender({ ...prms })}
|
||
fixedHeader={true}
|
||
/>
|
||
</Box>
|
||
</>
|
||
) : null}
|
||
</Grid>
|
||
<Grid item sx={STYLES.EQUIPMENT_INFO} xs={6}>
|
||
<Typography sx={STYLES.TABLE_HEADER} variant={"h6"} color={"text.secondary"}>
|
||
Рабочие центры
|
||
</Typography>
|
||
{equipConfiguration.dataLoaded ? (
|
||
<>
|
||
<Box sx={STYLES.TABLE_BUTTONS}>
|
||
<Stack direction={"row"} spacing={1}>
|
||
<Button
|
||
variant="contained"
|
||
size="small"
|
||
disabled={
|
||
!equipConfiguration.selectedRow.NRN ||
|
||
!costJobsSpecs.selectedRow.NRN ||
|
||
(equipConfiguration.selectedRow.NRN && equipConfiguration.selectedRow.BFULL_LOADED)
|
||
}
|
||
onClick={handleIncludeEquipmentOpen}
|
||
>
|
||
Включить в задание
|
||
</Button>
|
||
<Button
|
||
variant="contained"
|
||
size="small"
|
||
disabled={!costJobsSpecs.selectedRow.NRN || !costJobsSpecs.selectedRow.NEQCONFIG}
|
||
onClick={costJobsSpecExcludeCostEquipment}
|
||
>
|
||
Исключить из задания
|
||
</Button>
|
||
</Stack>
|
||
</Box>
|
||
<Box sx={STYLES.TABLE}>
|
||
<P8PDataGrid
|
||
{...P8P_DATA_GRID_CONFIG_PROPS}
|
||
containerComponentProps={{ sx: STYLES.DATA_GRID_CONTAINER(equipConfiguration.morePages), elevation: 1 }}
|
||
columnsDef={equipConfiguration.columnsDef}
|
||
rows={equipConfiguration.rows}
|
||
size={P8P_DATA_GRID_SIZE.SMALL}
|
||
morePages={equipConfiguration.morePages}
|
||
reloading={equipConfiguration.reload}
|
||
onOrderChanged={costEquipmentOrderChanged}
|
||
onPagesCountChanged={costEquipmentPagesCountChanged}
|
||
dataCellRender={prms =>
|
||
dataCellRender({
|
||
...prms,
|
||
handleSelectChange,
|
||
sUnit: UNIT_COST_EQUIPMENT,
|
||
selectedRow: equipConfiguration.selectedRow.NRN,
|
||
selectedJobSpec: costJobsSpecs.selectedRow
|
||
})
|
||
}
|
||
headCellRender={prms => headCellRender({ ...prms })}
|
||
fixedHeader={true}
|
||
/>
|
||
</Box>
|
||
</>
|
||
) : null}
|
||
</Grid>
|
||
</Grid>
|
||
{includeEquipment.NFCJOBSSP && includeEquipment.NFCJOBSSP ? (
|
||
<CostJobsSpecsInclude
|
||
includeEquipment={includeEquipment}
|
||
setIncludeEquipment={setIncludeEquipment}
|
||
setCostJobsSpecs={setCostJobsSpecs}
|
||
setEquipConfiguration={setEquipConfiguration}
|
||
includeEquipConfiguration={includeEquipConfiguration}
|
||
/>
|
||
) : null}
|
||
</div>
|
||
);
|
||
};
|
||
|
||
//Контроль свойств - Таблица информации о строках сменного задания
|
||
CostJobsSpecsDataGrid.propTypes = {
|
||
task: PropTypes.number.isRequired,
|
||
haveNote: PropTypes.bool.isRequired,
|
||
fromAction: PropTypes.bool.isRequired
|
||
};
|
||
|
||
//----------------
|
||
//Интерфейс модуля
|
||
//----------------
|
||
|
||
export { CostJobsSpecsDataGrid };
|