230 lines
11 KiB
JavaScript
230 lines
11 KiB
JavaScript
/*
|
||
Парус 8 - Панели мониторинга - Редакторы панелей
|
||
Пользовательские хуки компонента "Источник данных"
|
||
*/
|
||
|
||
//---------------------
|
||
//Подключение библиотек
|
||
//---------------------
|
||
|
||
import { useState, useContext, useEffect, useRef } from "react"; //Классы React
|
||
import client from "../../core/client"; //Клиент взаимодействия с сервером приложений
|
||
import { ERRORS } from "../../../app.text"; //Общие текстовые ресурсы
|
||
import { formatErrorMessage } from "../../core/utils"; //Общие вспомогательные функции
|
||
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с сервером
|
||
import { P8P_DATA_SOURCE_TYPE, P8P_DATA_SOURCE_ARGUMENT_DATA_TYPE } from "./p8p_data_source_common"; //Общие ресурсы источника данных
|
||
import { getConditionsValues } from "./p8p_component_condition/util"; //Вспомогательные ресурсы условий
|
||
import { getHandlersByActions } from "./p8p_component_action/util"; //Вспомогательные ресурсы действий
|
||
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
|
||
|
||
//-----------
|
||
//Тело модуля
|
||
//-----------
|
||
|
||
//Описание пользовательской процедуры
|
||
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_PE.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 useDataSource = ({ dataSource, values, componentRespArg = "" }) => {
|
||
//Контроллер для прерывания запросов
|
||
const abortController = useRef(null);
|
||
|
||
//Собственное состояние - параметры исполнения
|
||
const [state, setState] = useState({ stored: null, storedArgs: [], respArg: null, reqSet: false });
|
||
|
||
//Собственное состояние - флаг загрузки
|
||
const [isLoading, setLoading] = useState(false);
|
||
|
||
//Собственное состояние - данные
|
||
const [data, setData] = useState({ componentData: {}, init: false });
|
||
|
||
//Собственное состояние - ошибка получения данных
|
||
const [error, setError] = useState(null);
|
||
|
||
//Собственное состояние - наличие настроек
|
||
const [haveConfing, setHaveConfig] = useState(false);
|
||
|
||
//Собственное состояние - наличие данных
|
||
const [haveData, setHaveData] = useState(false);
|
||
|
||
//Подключение к контексту взаимодействия с сервером
|
||
const { executeStored } = useContext(BackEndСtx);
|
||
|
||
//При необходимости обновление информации о наличии данных
|
||
useEffect(() => {
|
||
setHaveData(data.init === true && !error ? true : false);
|
||
}, [data.init, error]);
|
||
|
||
//При необходимости обновить данные
|
||
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({ componentData: { ...data[componentRespArg] }, init: true });
|
||
} catch (e) {
|
||
if (e.message !== client.ERR_ABORTED) {
|
||
setError(formatErrorMessage(e.message).text);
|
||
setData({ componentData: {}, init: false });
|
||
}
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
if (state.reqSet) {
|
||
if (state.stored) loadData();
|
||
} else setData({ componentData: {}, init: false });
|
||
return () => abortController.current?.abort?.();
|
||
}, [state.stored, state.storedArgs, state.respArg, state.reqSet, executeStored, componentRespArg]);
|
||
|
||
//При изменении свойств
|
||
useEffect(() => {
|
||
//Устанавливаем признак наличия настроек
|
||
setHaveConfig(dataSource?.stored ? true : false);
|
||
//Устанавливаем параметры исполнения
|
||
setState(pv => {
|
||
if (dataSource?.type == P8P_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 == P8P_DATA_SOURCE_ARGUMENT_DATA_TYPE.NUMB
|
||
? isNaN(parseFloat(v))
|
||
? null
|
||
: parseFloat(v)
|
||
: argument.dataType == P8P_DATA_SOURCE_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(ERRORS.DATA_SOURCE_NO_REQ_ARGS);
|
||
setData({ componentData: {}, init: false });
|
||
}
|
||
return { stored, respArg, storedArgs, reqSet };
|
||
} else return pv;
|
||
} else return pv;
|
||
});
|
||
}, [dataSource, values]);
|
||
|
||
//Возвращаем интерфейс хука
|
||
return [data.componentData, error, haveConfing, haveData, isLoading];
|
||
};
|
||
|
||
//Изменение данных компонента с учетом условий
|
||
const useConditions = ({ componentData, conditions }) => {
|
||
//Собственное состояние - текущие условия компонента
|
||
const [currentConditions, setCurrentConditions] = useState([]);
|
||
|
||
//Собственное состояние - данные
|
||
const [data, setData] = useState();
|
||
|
||
//При обновлении условий компонента
|
||
useEffect(() => {
|
||
//Если условия изменились
|
||
if (JSON.stringify(currentConditions) != JSON.stringify(conditions)) {
|
||
//Устанавливаем новые условия
|
||
setCurrentConditions(conditions);
|
||
}
|
||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||
}, [conditions]);
|
||
|
||
//При обновлении данных или условий компонента
|
||
useEffect(() => {
|
||
//Если есть текущие условия
|
||
if (currentConditions.length !== 0) {
|
||
//Устанавливаем данные с учетом условий
|
||
setData({ ...componentData, ...getConditionsValues(componentData, currentConditions) });
|
||
} else {
|
||
//Оставляем данные компонента
|
||
setData({ ...componentData });
|
||
}
|
||
}, [currentConditions, componentData]);
|
||
|
||
//Возвращаем интерфейс хука
|
||
return [data];
|
||
};
|
||
|
||
//Получение обработчиков компонента
|
||
const useComponentHandlers = ({ actions = [], onValuesChange = null, getCustomTypeValue = null }) => {
|
||
//Контроллер для текущего состояния действий
|
||
const currentActions = useRef([]);
|
||
|
||
//Собственное состояние - обработчики компонента
|
||
const [handlers, setHandlers] = useState({});
|
||
|
||
//Подключение к контексту приложения
|
||
const { configUrlBase, pOnlineShowTab, pOnlineShowUnit } = useContext(ApplicationСtx);
|
||
|
||
//При необходимости обновления информации об обработчиках
|
||
useEffect(() => {
|
||
//Если изменились действия или параметры
|
||
if (JSON.stringify(currentActions.current) != JSON.stringify(actions)) {
|
||
//Считываем обработчики компонента
|
||
setHandlers(getHandlersByActions(actions, pOnlineShowUnit, configUrlBase, pOnlineShowTab, onValuesChange, getCustomTypeValue));
|
||
//Устанавливаем контроллер текущих действий
|
||
currentActions.current = actions;
|
||
}
|
||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||
}, [actions]);
|
||
|
||
//Возвращаем интерфейс хука
|
||
return [handlers];
|
||
};
|
||
|
||
//----------------
|
||
//Интерфейс модуля
|
||
//----------------
|
||
|
||
export { useUserProcDesc, useDataSource, useConditions, useComponentHandlers };
|