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 { P8PAppErrorPage } from "./components/p8p_app_error_page"; //Страница с ошибкой
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 { P8P_PANELS_MENU_GRID_CONFIG_PROPS, P8P_APP_WORKSPACE_CONFIG_PROPS } from "./config_wrapper"; //Подключение компонентов к настройкам приложения
@ -51,13 +51,29 @@ RouterError.propTypes = {
//Главное меню приложения
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 = {
DRAWER: "DRAWER",
GRID: "GRID"
GRID: "GRID",
DESKTOP: "DESKTOP"
};
//Структура элемента описания панели
@ -53,36 +54,16 @@ const P8P_PANELS_MENU_PANEL_SHAPE = PropTypes.shape({
//Стили
const STYLES = {
CONTAINER: {
display: "flex",
justifyContent: "center",
alignItems: "flex-start",
minHeight: "100vh"
},
GRID: {
maxWidth: 1200,
direction: "row",
justifyContent: "left",
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"
}
GRID_CONTAINER: { display: "flex", justifyContent: "center", alignItems: "flex-start", minHeight: "100vh" },
GRID: { maxWidth: 1200, direction: "row", justifyContent: "left", alignItems: "stretch" },
GRID_PANEL_CARD: { maxWidth: 400, height: "100%", flexDirection: "column", display: "flex" },
GRID_PANEL_CARD_MEDIA: { height: 140 },
GRID_PANEL_CARD_CONTENT_TITLE: { alignItems: "center" },
GRID_PANEL_CARD_ACTIONS: { marginTop: "auto", display: "flex", justifyContent: "flex-end", alignItems: "flex-start" },
DESKTOP_GROUP_HEADER: { fontWeight: "bold", fontFamily: "tahoma, arial, verdana, sans-serif!important", fontSize: "13px!important" },
DESKTOP_ITEM_BUTTON: { fontSize: "12px", textTransform: "none", "&:hover": { backgroundColor: "#c3e1ff" } },
DESKTOP_ITEM_STACK: { justifyContent: "center", alignItems: "center" },
DESKTOP_ITEM_ICON: { width: "64px", height: "64px", fontSize: "64px" }
};
//--------------------------------
@ -90,7 +71,7 @@ const STYLES = {
//--------------------------------
//Формирование групп
const getGroups = panels => {
const getGroups = (panels, group) => {
let res = [];
let addDefaultGroup = false;
for (const panel of panels)
@ -99,13 +80,14 @@ const getGroups = panels => {
if (!panel.group) addDefaultGroup = true;
}
if (addDefaultGroup || res.length == 0) res.push(null);
if (group) res = res.filter(g => g == group);
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 = [];
@ -118,8 +100,14 @@ const getPanelsLinks = ({ variant, panels, selectedPanel, defaultGroupTytle, nav
{grp ? grp : defaultGroupTytle}
</Typography>
</Grid>
) : (
) : variant === P8P_PANELS_MENU_VARIANT.DRAWER ? (
<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) {
@ -127,12 +115,12 @@ const getPanelsLinks = ({ variant, panels, selectedPanel, defaultGroupTytle, nav
panelsLinks.push(
variant === P8P_PANELS_MENU_VARIANT.GRID ? (
<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 ? (
<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}
<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}
<Typography variant="h5">{panel.caption}</Typography>
</Stack>
@ -140,14 +128,14 @@ const getPanelsLinks = ({ variant, panels, selectedPanel, defaultGroupTytle, nav
{panel.desc}
</Typography>
</CardContent>
<CardActions sx={STYLES.PANEL_CARD_ACTIONS}>
<CardActions sx={STYLES.GRID_PANEL_CARD_ACTIONS}>
<Button size="large" onClick={() => (onItemNavigate ? onItemNavigate(panel) : null)}>
{navigateCaption}
</Button>
</CardActions>
</Card>
</Grid>
) : (
) : variant === P8P_PANELS_MENU_VARIANT.DRAWER ? (
<ListItem key={panel.name} disablePadding>
<ListItemButton
selected={selectedPanel?.name === panel.name}
@ -159,6 +147,18 @@ const getPanelsLinks = ({ variant, panels, selectedPanel, defaultGroupTytle, nav
<ListItemText primary={panel.caption} />
</ListItemButton>
</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 (
<Box sx={STYLES.CONTAINER}>
<Box sx={STYLES.GRID_CONTAINER}>
<Grid container spacing={2} p={2} sx={STYLES.GRID}>
{panelsLinks}
</Grid>
@ -211,8 +211,25 @@ P8PPanelsMenuGrid.propTypes = {
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 Онлайн"
const P8O_API = window.parent?.parus?.clientApi;
const P8O_API = window.top?.parus?.clientApi;
//Структура объекта с описанием ошибок
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 });
//Установка базового URL приложения
const setUrlBase = urlBase => dispatch({ type: APP_AT.SET_URL_BASE, payload: urlBase });
//Установка списка панелей
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]);
//Получение базового URL приложения
const configUrlBase = useMemo(() => state.urlBase, [state.urlBase]);
//Инициализация приложения
const initApp = useCallback(async () => {
//Читаем конфигурацию с сервера
let res = await getConfig();
//Сохраняем базовый URL приложения
setUrlBase(getRespPayload(res)?.Panels?.urlBase);
//Сохраняем список панелей
setPanels(getRespPayload(res)?.Panels?.Panel);
//Установим флаг завершения инициализации
@ -151,6 +159,7 @@ export const ApplicationContext = ({ errors, displaySizeGetter, guidGenerator, c
pOnlineUserProcedure,
pOnlineUserReport,
configSystemPageSize,
configUrlBase,
appState: state
}}
>

View File

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

View File

@ -19,10 +19,10 @@ import { BUTTONS, ERRORS } from "../../../app.text"; //Текстовые рес
//Заглушка
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} />;
};
//----------------