forked from CITKParus/P8-Panels
175 lines
7.5 KiB
JavaScript
175 lines
7.5 KiB
JavaScript
/*
|
||
Парус 8 - Панели мониторинга - Редактор панелей
|
||
Компоненты: Хуки компонентов
|
||
*/
|
||
|
||
//---------------------
|
||
//Подключение библиотек
|
||
//---------------------
|
||
|
||
import { useState, useContext, useEffect, useRef } from "react"; //Классы React
|
||
import client from "../../../core/client"; //Клиент взаимодействия с сервером приложений
|
||
import { formatErrorMessage } from "../../../core/utils"; //Общие вспомогательные функции
|
||
import { BackEndСtx } from "../../../context/backend"; //Контекст взаимодействия с сервером
|
||
import { DATA_SOURCE_TYPE, ARGUMENT_DATA_TYPE } from "./editors_common"; //Общие объекты редакторов
|
||
|
||
//-----------
|
||
//Тело модуля
|
||
//-----------
|
||
|
||
//Загрузка модуля компонента из модуля (можно применять как альтернативу React.lazy)
|
||
const useComponentModule = ({ path = null, module = "view" } = {}) => {
|
||
//Собственное состояние - импортированный модуль компонента
|
||
const [componentModule, setComponentModule] = useState(null);
|
||
|
||
//Собственное состояние - флаг готовности
|
||
const [init, setInit] = useState(false);
|
||
|
||
//При подмонтировании к странице
|
||
useEffect(() => {
|
||
//Динамическая загрузка модуля компонента из библиотеки
|
||
const importComponentModule = async () => {
|
||
setInit(false);
|
||
const moduleContent = await import(`./${path}/${module}`);
|
||
setComponentModule(moduleContent);
|
||
setInit(true);
|
||
};
|
||
if (path) importComponentModule();
|
||
}, [path, module]);
|
||
|
||
//Возвращаем интерфейс хука
|
||
return [componentModule, init];
|
||
};
|
||
|
||
//Описание пользовательской процедуры
|
||
const useUserProcDesc = ({ code, refresh }) => {
|
||
//Собственное состояние - флаг загрузки
|
||
const [isLoading, setLoading] = useState(false);
|
||
|
||
//Собственное состояние - данные
|
||
const [data, setData] = useState(null);
|
||
|
||
//Подключение к контексту взаимодействия с сервером
|
||
const { executeStored } = useContext(BackEndСtx);
|
||
|
||
//При необходимости обновить данные компонента
|
||
useEffect(() => {
|
||
//Загрузка данных с сервера
|
||
const loadData = async () => {
|
||
try {
|
||
setLoading(true);
|
||
const data = await executeStored({
|
||
stored: "PKG_P8PANELS_EDITOR.USERPROCS_DESC",
|
||
args: { SCODE: code },
|
||
respArg: "COUT",
|
||
isArray: name => name === "arguments",
|
||
loader: false
|
||
});
|
||
setData(data?.XUSERPROC || null);
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
//Если надо обновить и есть для чего получать данные
|
||
if (refresh > 0)
|
||
if (code) loadData();
|
||
else setData(null);
|
||
}, [refresh, code, executeStored]);
|
||
|
||
//Возвращаем интерфейс хука
|
||
return [data, isLoading];
|
||
};
|
||
|
||
//Получение данных компонента из источника
|
||
const useComponentDataSource = ({ dataSource, values }) => {
|
||
//Контроллер для прерывания запросов
|
||
const abortController = useRef(null);
|
||
|
||
//Собственное состояние - параметры исполнения
|
||
const [state, setState] = useState({ stored: null, storedArgs: [], respArg: null, reqSet: false });
|
||
|
||
//Собственное состояние - флаг загрузки
|
||
const [isLoading, setLoading] = useState(false);
|
||
|
||
//Собственное состояние - данные
|
||
const [data, setData] = useState({ init: false });
|
||
|
||
//Собственное состояние - ошибка получения данных
|
||
const [error, setError] = useState(null);
|
||
|
||
//Подключение к контексту взаимодействия с сервером
|
||
const { executeStored } = useContext(BackEndСtx);
|
||
|
||
//При необходимости обновить данные
|
||
useEffect(() => {
|
||
//Загрузка данных с сервера
|
||
const loadData = async () => {
|
||
try {
|
||
setLoading(true);
|
||
abortController.current?.abort?.();
|
||
abortController.current = new AbortController();
|
||
const data = await executeStored({
|
||
stored: state.stored,
|
||
args: { ...(state.storedArgs ? state.storedArgs : {}) },
|
||
respArg: state.respArg,
|
||
loader: false,
|
||
signal: abortController.current.signal,
|
||
showErrorMessage: false
|
||
});
|
||
setError(null);
|
||
setData({ ...data, init: true });
|
||
} catch (e) {
|
||
if (e.message !== client.ERR_ABORTED) {
|
||
setError(formatErrorMessage(e.message).text);
|
||
setData({ init: false });
|
||
}
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
if (state.reqSet) {
|
||
if (state.stored) loadData();
|
||
} else setData({ init: false });
|
||
return () => abortController.current?.abort?.();
|
||
}, [state.stored, state.storedArgs, state.respArg, state.reqSet, executeStored]);
|
||
|
||
//При изменении свойств
|
||
useEffect(() => {
|
||
setState(pv => {
|
||
if (dataSource?.type == DATA_SOURCE_TYPE.USER_PROC) {
|
||
const { stored, respArg } = dataSource;
|
||
let reqSet = true;
|
||
const storedArgs = {};
|
||
dataSource.arguments.forEach(argument => {
|
||
let v = argument.valueSource ? values[argument.valueSource] : argument.value;
|
||
storedArgs[argument.name] =
|
||
argument.dataType == ARGUMENT_DATA_TYPE.NUMB
|
||
? isNaN(parseFloat(v))
|
||
? null
|
||
: parseFloat(v)
|
||
: argument.dataType == ARGUMENT_DATA_TYPE.DATE
|
||
? new Date(v)
|
||
: String(v === undefined ? "" : v);
|
||
if (argument.req === true && [undefined, null, ""].includes(storedArgs[argument.name])) reqSet = false;
|
||
});
|
||
if (pv.stored != stored || pv.respArg != respArg || JSON.stringify(pv.storedArgs) != JSON.stringify(storedArgs)) {
|
||
if (!reqSet) {
|
||
setError("Не заданы обязательные параметры источника данных");
|
||
setData({ init: false });
|
||
}
|
||
return { stored, respArg, storedArgs, reqSet };
|
||
} else return pv;
|
||
} else return pv;
|
||
});
|
||
}, [dataSource, values]);
|
||
|
||
//Возвращаем интерфейс хука
|
||
return [data, error, isLoading];
|
||
};
|
||
|
||
//----------------
|
||
//Интерфейс модуля
|
||
//----------------
|
||
|
||
export { useComponentModule, useUserProcDesc, useComponentDataSource };
|