WEB APP: Реализована интеграция главного меню в рабочий стол "Парус Онлайн"

This commit is contained in:
Mikhail Chechnev 2023-09-28 17:55:45 +03:00
parent 1c02fc9684
commit 86a8b43c8c
5 changed files with 95 additions and 49 deletions

View File

@ -14,7 +14,7 @@ import { ApplicationСtx } from "./context/application"; //Контекст пр
import { NavigationContext, NavigationCtx, getRootLocation } from "./context/navigation"; //Контекст навигации import { NavigationContext, NavigationCtx, getRootLocation } from "./context/navigation"; //Контекст навигации
import { P8PAppErrorPage } from "./components/p8p_app_error_page"; //Страница с ошибкой import { P8PAppErrorPage } from "./components/p8p_app_error_page"; //Страница с ошибкой
import { P8PAppWorkspace } from "./components/p8p_app_workspace"; //Рабочее пространство панели import { P8PAppWorkspace } from "./components/p8p_app_workspace"; //Рабочее пространство панели
import { P8PPanelsMenuGrid, P8P_PANELS_MENU_PANEL_SHAPE } from "./components/p8p_panels_menu"; //Меню панелей import { P8PPanelsMenuDesktop, P8PPanelsMenuGrid, P8P_PANELS_MENU_PANEL_SHAPE } from "./components/p8p_panels_menu"; //Меню панелей
import { BUTTONS, ERRORS, ERRORS_HTTP } from "../app.text"; //Текстовые ресурсы и константы import { BUTTONS, ERRORS, ERRORS_HTTP } from "../app.text"; //Текстовые ресурсы и константы
import { P8P_PANELS_MENU_GRID_CONFIG_PROPS, P8P_APP_WORKSPACE_CONFIG_PROPS } from "./config_wrapper"; //Подключение компонентов к настройкам приложения import { P8P_PANELS_MENU_GRID_CONFIG_PROPS, P8P_APP_WORKSPACE_CONFIG_PROPS } from "./config_wrapper"; //Подключение компонентов к настройкам приложения
@ -51,13 +51,29 @@ RouterError.propTypes = {
//Главное меню приложения //Главное меню приложения
const MainMenu = ({ panels = [] } = {}) => { const MainMenu = ({ panels = [] } = {}) => {
//Подключение к контексту навигации //Подключение к контексту навигации
const { navigatePanel } = useContext(NavigationCtx); const { navigatePanel, isNavigationSearch, getNavigationSearch } = useContext(NavigationCtx);
//Подключение к контексту приложения
const { configUrlBase, pOnlineShowTab } = useContext(ApplicationСtx);
//Получим параметры запроса из адресной строки
const qS = isNavigationSearch() ? getNavigationSearch() : null;
//Отработка действия навигации элемента меню //Отработка действия навигации элемента меню
const handleItemNavigate = panel => navigatePanel(panel); const handleItemNavigate = (panel, newTab) =>
newTab ? pOnlineShowTab({ id: panel.name, url: `${configUrlBase}${panel.url}`, caption: panel.caption }) : navigatePanel(panel);
//Генерация содержимого //Генерация содержимого
return <P8PPanelsMenuGrid {...P8P_PANELS_MENU_GRID_CONFIG_PROPS} panels={panels} onItemNavigate={handleItemNavigate} />; return qS?.mode === "DESKTOP" ? (
<P8PPanelsMenuDesktop
{...P8P_PANELS_MENU_GRID_CONFIG_PROPS}
group={qS?.group}
panels={panels}
onItemNavigate={panel => handleItemNavigate(panel, true)}
/>
) : (
<P8PPanelsMenuGrid {...P8P_PANELS_MENU_GRID_CONFIG_PROPS} panels={panels} onItemNavigate={panel => handleItemNavigate(panel, false)} />
);
}; };
//Контроль свойств - главное меню приложения //Контроль свойств - главное меню приложения

View File

@ -35,7 +35,8 @@ import {
//Типы меню //Типы меню
const P8P_PANELS_MENU_VARIANT = { const P8P_PANELS_MENU_VARIANT = {
DRAWER: "DRAWER", DRAWER: "DRAWER",
GRID: "GRID" GRID: "GRID",
DESKTOP: "DESKTOP"
}; };
//Структура элемента описания панели //Структура элемента описания панели
@ -53,36 +54,16 @@ const P8P_PANELS_MENU_PANEL_SHAPE = PropTypes.shape({
//Стили //Стили
const STYLES = { const STYLES = {
CONTAINER: { GRID_CONTAINER: { display: "flex", justifyContent: "center", alignItems: "flex-start", minHeight: "100vh" },
display: "flex", GRID: { maxWidth: 1200, direction: "row", justifyContent: "left", alignItems: "stretch" },
justifyContent: "center", GRID_PANEL_CARD: { maxWidth: 400, height: "100%", flexDirection: "column", display: "flex" },
alignItems: "flex-start", GRID_PANEL_CARD_MEDIA: { height: 140 },
minHeight: "100vh" GRID_PANEL_CARD_CONTENT_TITLE: { alignItems: "center" },
}, GRID_PANEL_CARD_ACTIONS: { marginTop: "auto", display: "flex", justifyContent: "flex-end", alignItems: "flex-start" },
GRID: { DESKTOP_GROUP_HEADER: { fontWeight: "bold", fontFamily: "tahoma, arial, verdana, sans-serif!important", fontSize: "13px!important" },
maxWidth: 1200, DESKTOP_ITEM_BUTTON: { fontSize: "12px", textTransform: "none", "&:hover": { backgroundColor: "#c3e1ff" } },
direction: "row", DESKTOP_ITEM_STACK: { justifyContent: "center", alignItems: "center" },
justifyContent: "left", DESKTOP_ITEM_ICON: { width: "64px", height: "64px", fontSize: "64px" }
alignItems: "stretch"
},
PANEL_CARD: {
maxWidth: 400,
height: "100%",
flexDirection: "column",
display: "flex"
},
PANEL_CARD_MEDIA: {
height: 140
},
PANEL_CARD_CONTENT_TITLE: {
alignItems: "center"
},
PANEL_CARD_ACTIONS: {
marginTop: "auto",
display: "flex",
justifyContent: "flex-end",
alignItems: "flex-start"
}
}; };
//-------------------------------- //--------------------------------
@ -90,7 +71,7 @@ const STYLES = {
//-------------------------------- //--------------------------------
//Формирование групп //Формирование групп
const getGroups = panels => { const getGroups = (panels, group) => {
let res = []; let res = [];
let addDefaultGroup = false; let addDefaultGroup = false;
for (const panel of panels) for (const panel of panels)
@ -99,13 +80,14 @@ const getGroups = panels => {
if (!panel.group) addDefaultGroup = true; if (!panel.group) addDefaultGroup = true;
} }
if (addDefaultGroup || res.length == 0) res.push(null); if (addDefaultGroup || res.length == 0) res.push(null);
if (group) res = res.filter(g => g == group);
return res; return res;
}; };
//Формирование ссылок на панели //Формирование ссылок на панели
const getPanelsLinks = ({ variant, panels, selectedPanel, defaultGroupTytle, navigateCaption, onItemNavigate }) => { const getPanelsLinks = ({ variant, panels, selectedPanel, group, defaultGroupTytle, navigateCaption, onItemNavigate }) => {
//Получим группы //Получим группы
let grps = getGroups(panels); let grps = getGroups(panels, group);
//Построим ссылки //Построим ссылки
const panelsLinks = []; const panelsLinks = [];
@ -118,8 +100,14 @@ const getPanelsLinks = ({ variant, panels, selectedPanel, defaultGroupTytle, nav
{grp ? grp : defaultGroupTytle} {grp ? grp : defaultGroupTytle}
</Typography> </Typography>
</Grid> </Grid>
) : ( ) : variant === P8P_PANELS_MENU_VARIANT.DRAWER ? (
<Divider key={grp} /> <Divider key={grp} />
) : (
<Box pb={1} key={grp}>
<Typography variant="h7" sx={STYLES.DESKTOP_GROUP_HEADER}>
{grp ? grp : defaultGroupTytle}
</Typography>
</Box>
) )
); );
for (const panel of panels) { for (const panel of panels) {
@ -127,12 +115,12 @@ const getPanelsLinks = ({ variant, panels, selectedPanel, defaultGroupTytle, nav
panelsLinks.push( panelsLinks.push(
variant === P8P_PANELS_MENU_VARIANT.GRID ? ( variant === P8P_PANELS_MENU_VARIANT.GRID ? (
<Grid item xs={12} sm={6} md={4} lg={4} xl={4} key={panel.name}> <Grid item xs={12} sm={6} md={4} lg={4} xl={4} key={panel.name}>
<Card sx={STYLES.PANEL_CARD}> <Card sx={STYLES.GRID_PANEL_CARD}>
{panel.preview ? ( {panel.preview ? (
<CardMedia component="img" alt={panel.name} image={panel.preview} sx={STYLES.PANEL_CARD_MEDIA} /> <CardMedia component="img" alt={panel.name} image={panel.preview} sx={STYLES.GRID_PANEL_CARD_MEDIA} />
) : null} ) : null}
<CardContent> <CardContent>
<Stack gap={1} direction="row" sx={STYLES.PANEL_CARD_CONTENT_TITLE}> <Stack gap={1} direction="row" sx={STYLES.GRID_PANEL_CARD_CONTENT_TITLE}>
{panel.icon ? <Icon>{panel.icon}</Icon> : null} {panel.icon ? <Icon>{panel.icon}</Icon> : null}
<Typography variant="h5">{panel.caption}</Typography> <Typography variant="h5">{panel.caption}</Typography>
</Stack> </Stack>
@ -140,14 +128,14 @@ const getPanelsLinks = ({ variant, panels, selectedPanel, defaultGroupTytle, nav
{panel.desc} {panel.desc}
</Typography> </Typography>
</CardContent> </CardContent>
<CardActions sx={STYLES.PANEL_CARD_ACTIONS}> <CardActions sx={STYLES.GRID_PANEL_CARD_ACTIONS}>
<Button size="large" onClick={() => (onItemNavigate ? onItemNavigate(panel) : null)}> <Button size="large" onClick={() => (onItemNavigate ? onItemNavigate(panel) : null)}>
{navigateCaption} {navigateCaption}
</Button> </Button>
</CardActions> </CardActions>
</Card> </Card>
</Grid> </Grid>
) : ( ) : variant === P8P_PANELS_MENU_VARIANT.DRAWER ? (
<ListItem key={panel.name} disablePadding> <ListItem key={panel.name} disablePadding>
<ListItemButton <ListItemButton
selected={selectedPanel?.name === panel.name} selected={selectedPanel?.name === panel.name}
@ -159,6 +147,18 @@ const getPanelsLinks = ({ variant, panels, selectedPanel, defaultGroupTytle, nav
<ListItemText primary={panel.caption} /> <ListItemText primary={panel.caption} />
</ListItemButton> </ListItemButton>
</ListItem> </ListItem>
) : (
<Button
p={3}
key={panel.name}
onClick={() => (onItemNavigate ? onItemNavigate(panel) : null)}
sx={STYLES.DESKTOP_ITEM_BUTTON}
>
<Stack sx={STYLES.DESKTOP_ITEM_STACK}>
<Icon sx={STYLES.DESKTOP_ITEM_ICON}>{panel.icon}</Icon>
{panel.caption}
</Stack>
</Button>
) )
); );
} }
@ -195,7 +195,7 @@ const P8PPanelsMenuGrid = ({ onItemNavigate, navigateCaption, panels = [], defau
//Генерация содержимого //Генерация содержимого
return ( return (
<Box sx={STYLES.CONTAINER}> <Box sx={STYLES.GRID_CONTAINER}>
<Grid container spacing={2} p={2} sx={STYLES.GRID}> <Grid container spacing={2} p={2} sx={STYLES.GRID}>
{panelsLinks} {panelsLinks}
</Grid> </Grid>
@ -211,8 +211,25 @@ P8PPanelsMenuGrid.propTypes = {
defaultGroupTytle: PropTypes.string.isRequired defaultGroupTytle: PropTypes.string.isRequired
}; };
//Меню панелей - рабочий стол
const P8PPanelsMenuDesktop = ({ group, onItemNavigate, panels = [], defaultGroupTytle } = {}) => {
//Формируем ссылки на панели
const panelsLinks = getPanelsLinks({ variant: P8P_PANELS_MENU_VARIANT.DESKTOP, panels, group, defaultGroupTytle, onItemNavigate });
//Генерация содержимого
return <Box p={2}>{panelsLinks}</Box>;
};
//Контроль свойств - Меню панелей - рабочий стол
P8PPanelsMenuDesktop.propTypes = {
group: PropTypes.string,
onItemNavigate: PropTypes.func,
panels: PropTypes.arrayOf(P8P_PANELS_MENU_PANEL_SHAPE).isRequired,
defaultGroupTytle: PropTypes.string.isRequired
};
//---------------- //----------------
//Интерфейс модуля //Интерфейс модуля
//---------------- //----------------
export { P8P_PANELS_MENU_PANEL_SHAPE, P8PPanelsMenuDrawer, P8PPanelsMenuGrid }; export { P8P_PANELS_MENU_PANEL_SHAPE, P8PPanelsMenuDrawer, P8PPanelsMenuGrid, P8PPanelsMenuDesktop };

View File

@ -18,7 +18,7 @@ import { BackEndСtx } from "./backend"; //Контекст взаимодейс
//--------- //---------
//Клиентский API "ПАРУС 8 Онлайн" //Клиентский API "ПАРУС 8 Онлайн"
const P8O_API = window.parent?.parus?.clientApi; const P8O_API = window.top?.parus?.clientApi;
//Структура объекта с описанием ошибок //Структура объекта с описанием ошибок
const APPLICATION_CONTEXT_ERRORS_SHAPE = PropTypes.shape({ const APPLICATION_CONTEXT_ERRORS_SHAPE = PropTypes.shape({
@ -49,6 +49,9 @@ export const ApplicationContext = ({ errors, displaySizeGetter, guidGenerator, c
//Установка текущего размера экрана //Установка текущего размера экрана
const setDisplaySize = displaySize => dispatch({ type: APP_AT.SET_DISPLAY_SIZE, payload: displaySize }); const setDisplaySize = displaySize => dispatch({ type: APP_AT.SET_DISPLAY_SIZE, payload: displaySize });
//Установка базового URL приложения
const setUrlBase = urlBase => dispatch({ type: APP_AT.SET_URL_BASE, payload: urlBase });
//Установка списка панелей //Установка списка панелей
const setPanels = panels => dispatch({ type: APP_AT.LOAD_PANELS, payload: panels }); const setPanels = panels => dispatch({ type: APP_AT.LOAD_PANELS, payload: panels });
@ -117,10 +120,15 @@ export const ApplicationContext = ({ errors, displaySizeGetter, guidGenerator, c
//Получение количества записей на странице //Получение количества записей на странице
const configSystemPageSize = useMemo(() => config.SYSTEM.PAGE_SIZE, [config.SYSTEM.PAGE_SIZE]); const configSystemPageSize = useMemo(() => config.SYSTEM.PAGE_SIZE, [config.SYSTEM.PAGE_SIZE]);
//Получение базового URL приложения
const configUrlBase = useMemo(() => state.urlBase, [state.urlBase]);
//Инициализация приложения //Инициализация приложения
const initApp = useCallback(async () => { const initApp = useCallback(async () => {
//Читаем конфигурацию с сервера //Читаем конфигурацию с сервера
let res = await getConfig(); let res = await getConfig();
//Сохраняем базовый URL приложения
setUrlBase(getRespPayload(res)?.Panels?.urlBase);
//Сохраняем список панелей //Сохраняем список панелей
setPanels(getRespPayload(res)?.Panels?.Panel); setPanels(getRespPayload(res)?.Panels?.Panel);
//Установим флаг завершения инициализации //Установим флаг завершения инициализации
@ -151,6 +159,7 @@ export const ApplicationContext = ({ errors, displaySizeGetter, guidGenerator, c
pOnlineUserProcedure, pOnlineUserProcedure,
pOnlineUserReport, pOnlineUserReport,
configSystemPageSize, configSystemPageSize,
configUrlBase,
appState: state appState: state
}} }}
> >

View File

@ -9,6 +9,7 @@
//Типы действий //Типы действий
const APP_AT = { const APP_AT = {
SET_URL_BASE: "SET_URL_BASE", //Установка базового URL приложения
LOAD_PANELS: "LOAD_PANELS", //Загрузка списка панелей LOAD_PANELS: "LOAD_PANELS", //Загрузка списка панелей
SET_INITIALIZED: "SET_INITIALIZED", //Установка флага инициализированности приложения SET_INITIALIZED: "SET_INITIALIZED", //Установка флага инициализированности приложения
SET_DISPLAY_SIZE: "SET_DISPLAY_SIZE" //Установка текущего типового размера экрана SET_DISPLAY_SIZE: "SET_DISPLAY_SIZE" //Установка текущего типового размера экрана
@ -17,6 +18,7 @@ const APP_AT = {
//Состояние приложения по умолчанию //Состояние приложения по умолчанию
const INITIAL_STATE = displaySizeGetter => ({ const INITIAL_STATE = displaySizeGetter => ({
displaySize: displaySizeGetter(), displaySize: displaySizeGetter(),
urlBase: "",
panels: [], panels: [],
panelsLoaded: false, panelsLoaded: false,
initialized: false initialized: false
@ -28,6 +30,8 @@ const INITIAL_STATE = displaySizeGetter => ({
//Обработчики действий //Обработчики действий
const handlers = { const handlers = {
//Установка базового URL приложения
[APP_AT.SET_URL_BASE]: (state, { payload }) => ({ ...state, urlBase: payload }),
//Загрузка списка панелей //Загрузка списка панелей
[APP_AT.LOAD_PANELS]: (state, { payload }) => { [APP_AT.LOAD_PANELS]: (state, { payload }) => {
let panels = []; let panels = [];

View File

@ -19,10 +19,10 @@ import { BUTTONS, ERRORS } from "../../../app.text"; //Текстовые рес
//Заглушка //Заглушка
const Dummy = () => { const Dummy = () => {
//Подключение к контексту навигации //Подключение к контексту навигации
const { navigateBack } = useContext(NavigationCtx); const { navigateRoot } = useContext(NavigationCtx);
//Генерация содержимого //Генерация содержимого
return <P8PAppErrorPage errorMessage={ERRORS.UNDER_CONSTRUCTION} onNavigate={() => navigateBack()} navigateCaption={BUTTONS.NAVIGATE_BACK} />; return <P8PAppErrorPage errorMessage={ERRORS.UNDER_CONSTRUCTION} onNavigate={() => navigateRoot()} navigateCaption={BUTTONS.NAVIGATE_HOME} />;
}; };
//---------------- //----------------