diff --git a/packages/core/client/src/plugin-manager/PinnedPluginListProvider.tsx b/packages/core/client/src/plugin-manager/PinnedPluginListProvider.tsx
index 095a5804c3..137458b9ee 100644
--- a/packages/core/client/src/plugin-manager/PinnedPluginListProvider.tsx
+++ b/packages/core/client/src/plugin-manager/PinnedPluginListProvider.tsx
@@ -88,7 +88,7 @@ const dividerTheme = {
},
};
-export const PinnedPluginList = React.memo(() => {
+export const PinnedPluginList = React.memo((props: { onClick?: () => void }) => {
const { allowAll, snippets } = useACLRoleContext();
const getSnippetsAllow = (aclKey) => {
return allowAll || aclKey === '*' || snippets?.includes(aclKey);
@@ -98,13 +98,15 @@ export const PinnedPluginList = React.memo(() => {
return (
- {Object.keys(ctx.items)
- .sort((a, b) => ctx.items[a].order - ctx.items[b].order)
- .filter((key) => getSnippetsAllow(ctx.items[key].snippet))
- .map((key) => {
- const Action = get(components, ctx.items[key].component);
- return Action ?
: null;
- })}
+
+ {Object.keys(ctx.items)
+ .sort((a, b) => ctx.items[a].order - ctx.items[b].order)
+ .filter((key) => getSnippetsAllow(ctx.items[key].snippet))
+ .map((key) => {
+ const Action = get(components, ctx.items[key].component);
+ return Action ?
: null;
+ })}
+
diff --git a/packages/core/client/src/route-switch/antd/admin-layout/index.tsx b/packages/core/client/src/route-switch/antd/admin-layout/index.tsx
index 73d8bc2f48..244df891d7 100644
--- a/packages/core/client/src/route-switch/antd/admin-layout/index.tsx
+++ b/packages/core/client/src/route-switch/antd/admin-layout/index.tsx
@@ -12,6 +12,7 @@ import ProLayout, { RouteContext, RouteContextType } from '@ant-design/pro-layou
import { HeaderViewProps } from '@ant-design/pro-layout/es/components/Header';
import { css } from '@emotion/css';
import { theme as antdTheme, ConfigProvider, Popover, Tooltip } from 'antd';
+import { createStyles } from 'antd-style';
import React, { createContext, FC, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
import ReactDOM from 'react-dom';
import { Link, Navigate, Outlet, useLocation, useNavigate } from 'react-router-dom';
@@ -52,7 +53,6 @@ import { KeepAlive } from './KeepAlive';
import { NocoBaseDesktopRoute, NocoBaseDesktopRouteType } from './convertRoutesToSchema';
import { MenuSchemaToolbar, ResetThemeTokenAndKeepAlgorithm } from './menuItemSettings';
import { userCenterSettings } from './userCenterSettings';
-import { createGlobalStyle, createStyles } from 'antd-style';
export { KeepAlive, NocoBaseDesktopRouteType };
@@ -75,7 +75,7 @@ const AllAccessDesktopRoutesContext = createContext<{
refresh: () => void;
}>({
allAccessRoutes: emptyArray,
- refresh: () => {},
+ refresh: () => { },
});
AllAccessDesktopRoutesContext.displayName = 'AllAccessDesktopRoutesContext';
@@ -418,9 +418,22 @@ const popoverStyle = css`
const MobileActions: FC = (props) => {
const { token } = useToken();
+ const [open, setOpen] = useState(false);
+
+ // 点击时立即关闭 Popover,避免影响用户操作
+ const handleContentClick = useCallback(() => {
+ setOpen(false);
+ }, []);
return (
-
} color={token.colorBgHeader}>
+
}
+ color={token.colorBgHeader}
+ trigger="click"
+ open={open}
+ onOpenChange={setOpen}
+ >
>;
}>({
isMobileLayout: false,
- setIsMobileLayout: () => {},
+ setIsMobileLayout: () => { },
});
const MobileLayoutProvider: FC = (props) => {
diff --git a/packages/plugins/@nocobase/plugin-mobile/src/client/adaptor-of-desktop/ActionDrawer.tsx b/packages/plugins/@nocobase/plugin-mobile/src/client/adaptor-of-desktop/ActionDrawer.tsx
index bed4abaaa8..1dfbc525d5 100644
--- a/packages/plugins/@nocobase/plugin-mobile/src/client/adaptor-of-desktop/ActionDrawer.tsx
+++ b/packages/plugins/@nocobase/plugin-mobile/src/client/adaptor-of-desktop/ActionDrawer.tsx
@@ -20,55 +20,54 @@ import {
import { ConfigProvider } from 'antd';
import { Popup } from 'antd-mobile';
import { CloseOutline } from 'antd-mobile-icons';
-import React, { useCallback, useEffect, useMemo } from 'react';
+import React, { FC, ReactNode, useCallback, useEffect, useMemo } from 'react';
+import { useTranslation } from 'react-i18next';
import { useMobileActionDrawerStyle } from './ActionDrawer.style';
import { usePopupContainer } from './FilterAction';
import { MIN_Z_INDEX_INCREMENT } from './zIndex';
-export const ActionDrawerUsedInMobile: any = observer((props: { footerNodeName?: string }) => {
- const fieldSchema = useFieldSchema();
- const field = useField();
- const { visible, setVisible } = useActionContext();
- const { popupContainerRef, visiblePopup } = usePopupContainer(visible);
+export interface MobilePopupProps {
+ title?: string;
+ visible: boolean;
+ minHeight?: number | string;
+ onClose: () => void;
+ children: ReactNode;
+}
+
+export const MobilePopup: FC = (props) => {
+ const {
+ title,
+ visible,
+ onClose: closePopup,
+ children,
+ minHeight,
+ } = props;
+ const { t } = useTranslation();
+ const { popupContainerRef } = usePopupContainer(visible);
const { componentCls, hashId } = useMobileActionDrawerStyle();
const parentZIndex = useZIndexContext();
const { theme: globalTheme } = useGlobalTheme();
- // this schema need to add padding in the content area of the popup
- const isSpecialSchema = isChangePasswordSchema(fieldSchema) || isEditProfileSchema(fieldSchema);
-
- const footerNodeName = isSpecialSchema ? 'Action.Drawer.Footer' : props.footerNodeName;
-
- const specialStyle = isSpecialSchema ? { backgroundColor: 'white' } : {};
-
const newZIndex = parentZIndex + MIN_Z_INDEX_INCREMENT;
const zIndexStyle = useMemo(() => {
return {
zIndex: newZIndex,
+ minHeight,
};
- }, [newZIndex]);
-
- const footerSchema = fieldSchema.reduceProperties((buf, s) => {
- if (s['x-component'] === footerNodeName) {
- return s;
- }
- return buf;
- });
-
- const title = field.title || '';
-
- const closePopup = useCallback(() => {
- setVisible(false);
- }, [setVisible]);
+ }, [newZIndex, minHeight]);
const theme = useMemo(() => {
return {
...globalTheme,
token: {
...globalTheme.token,
- marginBlock: 12,
zIndexPopupBase: newZIndex,
+ paddingPageHorizontal: 8,
+ paddingPageVertical: 8,
+ marginBlock: 12,
+ borderRadiusBlock: 8,
+ fontSize: 14,
},
};
}, [globalTheme, newZIndex]);
@@ -78,7 +77,7 @@ export const ActionDrawerUsedInMobile: any = observer((props: { footerNodeName?:
popupContainerRef.current}
@@ -93,43 +92,90 @@ export const ActionDrawerUsedInMobile: any = observer((props: { footerNodeName?:
{title}
-
+
- {isSpecialSchema ? (
-
- {
- return s['x-component'] !== footerNodeName;
- }}
- />
-
- ) : (
-
{
- return s['x-component'] !== footerNodeName;
- }}
- />
- )}
- {footerSchema ? (
-
- {
- return s['x-component'] === footerNodeName;
- }}
- />
-
- ) : null}
+ {children}
+ )
+}
+
+export const ActionDrawerUsedInMobile: any = observer((props: { footerNodeName?: string }) => {
+ const fieldSchema = useFieldSchema();
+ const field = useField();
+ const { visible, setVisible } = useActionContext();
+ const { visiblePopup } = usePopupContainer(visible);
+
+ // this schema need to add padding in the content area of the popup
+ const isSpecialSchema = isChangePasswordSchema(fieldSchema) || isEditProfileSchema(fieldSchema);
+
+ const footerNodeName = isSpecialSchema ? 'Action.Drawer.Footer' : props.footerNodeName;
+
+ const specialStyle = isSpecialSchema ? { backgroundColor: 'white' } : {};
+
+ const footerSchema = fieldSchema.reduceProperties((buf, s) => {
+ if (s['x-component'] === footerNodeName) {
+ return s;
+ }
+ return buf;
+ });
+
+ const title = field.title || '';
+
+ const closePopup = useCallback(() => {
+ setVisible(false);
+ }, [setVisible]);
+
+ const popupContent = isSpecialSchema ? (
+
+ {
+ return s['x-component'] !== footerNodeName;
+ }}
+ />
+
+ ) : (
+ {
+ return s['x-component'] !== footerNodeName;
+ }}
+ />
+ );
+
+ const footerContent = footerSchema ? (
+
+ {
+ return s['x-component'] === footerNodeName;
+ }}
+ />
+
+ ) : null;
+
+ return (
+
+ {popupContent}
+ {footerContent}
+
);
});
diff --git a/packages/plugins/@nocobase/plugin-mobile/src/client/index.tsx b/packages/plugins/@nocobase/plugin-mobile/src/client/index.tsx
index 6016313b48..0fea40523d 100644
--- a/packages/plugins/@nocobase/plugin-mobile/src/client/index.tsx
+++ b/packages/plugins/@nocobase/plugin-mobile/src/client/index.tsx
@@ -55,6 +55,7 @@ import { MobileSettingsBlockSchemaSettings } from './mobile-blocks/settings-bloc
import pkg from './../../package.json';
import { MobileComponentsProvider } from './MobileComponentsProvider';
+export { MobilePopup } from './adaptor-of-desktop/ActionDrawer';
export * from './desktop-mode';
export * from './mobile';
export * from './mobile-layout';
diff --git a/packages/plugins/@nocobase/plugin-mobile/src/client/pages/dynamic-page/content/MobilePageContentContainer.tsx b/packages/plugins/@nocobase/plugin-mobile/src/client/pages/dynamic-page/content/MobilePageContentContainer.tsx
index 1285cf538a..7a35c8b085 100644
--- a/packages/plugins/@nocobase/plugin-mobile/src/client/pages/dynamic-page/content/MobilePageContentContainer.tsx
+++ b/packages/plugins/@nocobase/plugin-mobile/src/client/pages/dynamic-page/content/MobilePageContentContainer.tsx
@@ -12,7 +12,7 @@ import _ from 'lodash';
import React, { FC, useEffect } from 'react';
import { PageBackgroundColor } from '../../../constants';
-export const MobilePageContentContainer: FC<{ hideTabBar?: boolean }> = ({ children, hideTabBar }) => {
+export const MobilePageContentContainer: FC<{ hideTabBar?: boolean; displayPageHeader?: boolean }> = ({ children, hideTabBar, displayPageHeader = true }) => {
const [mobileTabBarHeight, setMobileTabBarHeight] = React.useState(0);
const [mobilePageHeader, setMobilePageHeader] = React.useState(0);
const { token } = useToken();
@@ -29,7 +29,7 @@ export const MobilePageContentContainer: FC<{ hideTabBar?: boolean }> = ({ child
});
return (
<>
- {mobilePageHeader ? : null}
+ {(mobilePageHeader && displayPageHeader) ? : null}
{
- const { title } = useMobileTitle();
+ const { title } = useMobileTitle() || {};
const { displayNavigationBar = true, displayPageTitle = true } = useMobilePage();
const fieldSchema = useFieldSchema();
const { componentCls, hashId } = useStyles();
diff --git a/packages/plugins/@nocobase/plugin-mobile/src/client/pages/dynamic-page/header/styles.ts b/packages/plugins/@nocobase/plugin-mobile/src/client/pages/dynamic-page/header/styles.ts
index 548eb9ebaa..9536c49d93 100644
--- a/packages/plugins/@nocobase/plugin-mobile/src/client/pages/dynamic-page/header/styles.ts
+++ b/packages/plugins/@nocobase/plugin-mobile/src/client/pages/dynamic-page/header/styles.ts
@@ -9,7 +9,6 @@
export const mobilePageHeaderStyle: any = {
position: 'absolute',
- top: 0,
left: 0,
right: 0,
borderBottom: '1px solid var(--adm-color-border)',
diff --git a/packages/plugins/@nocobase/plugin-notification-in-app-message/src/client/components/Inbox.tsx b/packages/plugins/@nocobase/plugin-notification-in-app-message/src/client/components/Inbox.tsx
index 1a04a6ac76..0269a22427 100644
--- a/packages/plugins/@nocobase/plugin-notification-in-app-message/src/client/components/Inbox.tsx
+++ b/packages/plugins/@nocobase/plugin-notification-in-app-message/src/client/components/Inbox.tsx
@@ -16,27 +16,23 @@
* For more information, please rwefer to: https://www.nocobase.com/agreement.
*/
-import React, { useEffect, useCallback } from 'react';
import { reaction } from '@formily/reactive';
-import { Badge, Button, ConfigProvider, Drawer, Tooltip, notification, theme } from 'antd';
-import { CloseOutlined } from '@ant-design/icons';
-import { createStyles } from 'antd-style';
-import { Icon } from '@nocobase/client';
-import { InboxContent } from './InboxContent';
-import { useLocalTranslation } from '../../locale';
-import { fetchChannels } from '../observables';
import { observer } from '@formily/reactive-react';
-import { useCurrentUserContext } from '@nocobase/client';
+import { Icon, useCurrentUserContext, useMobileLayout } from '@nocobase/client';
+import { MobilePopup } from '@nocobase/plugin-mobile/client';
+import { Badge, Button, ConfigProvider, Drawer, notification, theme, Tooltip } from 'antd';
+import { createStyles } from 'antd-style';
+import React, { FC, useCallback, useEffect, useState } from 'react';
+import { useLocalTranslation } from '../../locale';
+import { Channel } from '../../types';
import {
- updateUnreadMsgsCount,
- unreadMsgsCountObs,
- startMsgSSEStreamWithRetry,
- inboxVisible,
- userIdObs,
- liveSSEObs,
+ fetchChannels, inboxVisible, liveSSEObs,
messageMapObs,
- selectedChannelNameObs,
+ selectedChannelNameObs, startMsgSSEStreamWithRetry, unreadMsgsCountObs, updateUnreadMsgsCount, userIdObs
} from '../observables';
+import { InboxContent } from './InboxContent';
+import { MobileChannelPage } from './mobile/ChannelPage';
+import { MobileMessagePage } from './mobile/MessagePage';
const useStyles = createStyles(({ token }) => {
return {
button: {
@@ -46,6 +42,42 @@ const useStyles = createStyles(({ token }) => {
};
});
+const InboxPopup: FC<{ title: string; visible: boolean; onClose: () => void }> = (props) => {
+ const { token } = theme.useToken();
+ const { isMobileLayout } = useMobileLayout();
+ const [selectedChannel, setSelectedChannel] = useState(null);
+
+ if (isMobileLayout) {
+ return (
+ <>
+
+
+
+ setSelectedChannel(null)} minHeight={'60vh'}>
+
+
+ >
+ )
+ }
+
+ return (
+ {props.title}
}
+ open={props.visible}
+ width={900}
+ onClose={props.onClose}
+ styles={{
+ header: {
+ paddingLeft: token.paddingMD,
+ },
+ }}
+ >
+
+
+ )
+}
+
+
const InnerInbox = (props) => {
const { t } = useLocalTranslation();
const { styles } = useStyles();
@@ -89,7 +121,6 @@ const InnerInbox = (props) => {
document.removeEventListener('visibilitychange', onVisibilityChange);
};
}, []);
- const DrawerTitle = {t('Message')}
;
useEffect(() => {
const dispose = reaction(
() => liveSSEObs.value,
@@ -138,21 +169,7 @@ const InnerInbox = (props) => {
- {
- inboxVisible.value = false;
- }}
- styles={{
- header: {
- paddingLeft: token.paddingMD,
- },
- }}
- >
-
-
+ { inboxVisible.value = false; }} />
);
};
diff --git a/packages/plugins/@nocobase/plugin-notification-in-app-message/src/client/components/mobile/ChannelList.tsx b/packages/plugins/@nocobase/plugin-notification-in-app-message/src/client/components/mobile/ChannelList.tsx
index 7acaa97665..b55441c018 100644
--- a/packages/plugins/@nocobase/plugin-notification-in-app-message/src/client/components/mobile/ChannelList.tsx
+++ b/packages/plugins/@nocobase/plugin-notification-in-app-message/src/client/components/mobile/ChannelList.tsx
@@ -7,18 +7,18 @@
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
-import React, { useRef, useEffect, useState } from 'react';
-import { observer } from '@formily/reactive-react';
-import { reaction } from '@formily/reactive';
-import { List, Badge, InfiniteScroll, ListRef } from 'antd-mobile';
-import { useNavigate } from 'react-router-dom';
-import { dayjs } from '@nocobase/utils/client';
-import InfiniteScrollContent from './InfiniteScrollContent';
-import { channelListObs, channelStatusFilterObs, showChannelLoadingMoreObs, fetchChannels } from '../../observables';
import { Schema } from '@formily/react';
+import { reaction } from '@formily/reactive';
+import { observer } from '@formily/reactive-react';
import { useApp } from '@nocobase/client';
+import { dayjs } from '@nocobase/utils/client';
+import { Badge, InfiniteScroll, List, ListRef } from 'antd-mobile';
+import React, { useEffect, useRef, useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { channelListObs, channelStatusFilterObs, fetchChannels, showChannelLoadingMoreObs } from '../../observables';
+import InfiniteScrollContent from './InfiniteScrollContent';
-const InternalChannelList = () => {
+const InternalChannelList = (props: { onClickItem?: (item: any) => void }) => {
const app = useApp();
const navigate = useNavigate();
const channels = channelListObs.value;
@@ -66,7 +66,11 @@ const InternalChannelList = () => {
{
- navigate(`/page/in-app-message/messages?channel=${item.name}`);
+ if (props.onClickItem) {
+ props.onClickItem(item);
+ } else {
+ navigate(`/page/in-app-message/messages?channel=${item.name}`);
+ }
}}
description={
diff --git a/packages/plugins/@nocobase/plugin-notification-in-app-message/src/client/components/mobile/ChannelPage.tsx b/packages/plugins/@nocobase/plugin-notification-in-app-message/src/client/components/mobile/ChannelPage.tsx
index e258b71520..4a34bc9d7b 100644
--- a/packages/plugins/@nocobase/plugin-notification-in-app-message/src/client/components/mobile/ChannelPage.tsx
+++ b/packages/plugins/@nocobase/plugin-notification-in-app-message/src/client/components/mobile/ChannelPage.tsx
@@ -7,20 +7,20 @@
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
-import React, { useEffect, useCallback } from 'react';
-import { Tabs } from 'antd-mobile';
import { observer } from '@formily/reactive-react';
-import { useCurrentUserContext, css } from '@nocobase/client';
+import { css, useCurrentUserContext } from '@nocobase/client';
import {
+ MobilePageContentContainer,
MobilePageHeader,
MobilePageNavigationBar,
MobilePageProvider,
- MobilePageContentContainer,
} from '@nocobase/plugin-mobile/client';
-import { userIdObs, fetchChannels, ChannelStatus, channelStatusFilterObs } from '../../observables';
-import { ChannelList } from './ChannelList';
+import { Tabs } from 'antd-mobile';
+import React, { useEffect } from 'react';
import { useLocalTranslation } from '../../../locale';
-const MobileMessageBoxInner = () => {
+import { ChannelStatus, channelStatusFilterObs, fetchChannels, userIdObs } from '../../observables';
+import { ChannelList } from './ChannelList';
+const MobileMessageBoxInner = (props: { displayNavigationBar?: boolean; onClickItem?: (item: any) => void; }) => {
const { t } = useLocalTranslation();
const ctx = useCurrentUserContext();
const currUserId = ctx.data?.data?.id;
@@ -31,7 +31,7 @@ const MobileMessageBoxInner = () => {
fetchChannels({});
}, []);
return (
-
+
{
-
+
);
diff --git a/packages/plugins/@nocobase/plugin-notification-in-app-message/src/client/components/mobile/MessagePage.tsx b/packages/plugins/@nocobase/plugin-notification-in-app-message/src/client/components/mobile/MessagePage.tsx
index d0befaa9d1..9ef319165f 100644
--- a/packages/plugins/@nocobase/plugin-notification-in-app-message/src/client/components/mobile/MessagePage.tsx
+++ b/packages/plugins/@nocobase/plugin-notification-in-app-message/src/client/components/mobile/MessagePage.tsx
@@ -7,35 +7,34 @@
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
-import React, { useEffect, useCallback, useState } from 'react';
-import { useNavigate } from 'react-router-dom';
-import { List, Badge, InfiniteScroll, NavBar, DotLoading } from 'antd-mobile';
import { observer } from '@formily/reactive-react';
-import { useCurrentUserContext, css, useApp } from '@nocobase/client';
-import { useSearchParams } from 'react-router-dom';
+import { css, useApp, useCurrentUserContext } from '@nocobase/client';
import { dayjs } from '@nocobase/utils/client';
+import { Badge, InfiniteScroll, List, NavBar } from 'antd-mobile';
+import React, { useCallback, useEffect, useState } from 'react';
+import { useNavigate, useSearchParams } from 'react-router-dom';
+import { Schema } from '@formily/react';
import {
- MobilePageHeader,
- MobilePageProvider,
MobilePageContentContainer,
- useMobileTitle,
+ MobilePageHeader,
+ MobilePageProvider
} from '@nocobase/plugin-mobile/client';
+import { useLocalTranslation } from '../../../locale';
import {
- userIdObs,
+ fetchChannels,
+ fetchMessages,
+ inboxVisible,
selectedChannelNameObs,
selectedChannelObs,
selectedMessageListObs,
- fetchChannels,
- updateMessage,
- fetchMessages,
showMsgLoadingMoreObs,
+ updateMessage,
+ userIdObs,
} from '../../observables';
-import { useLocalTranslation } from '../../../locale';
import InfiniteScrollContent from './InfiniteScrollContent';
-import { Schema } from '@formily/react';
-const MobileMessagePageInner = () => {
+const MobileMessagePageInner = (props: { displayPageHeader?: boolean }) => {
const app = useApp();
const { t } = useLocalTranslation();
const navigate = useNavigate();
@@ -58,11 +57,13 @@ const MobileMessagePageInner = () => {
}, [currUserId]);
const messages = selectedMessageListObs.value;
const viewMessageDetail = (message) => {
- const url = message.options?.mobileUrl;
+ const url = message.options?.mobileUrl || message.options?.url;
if (url) {
if (url.startsWith('/m/')) navigate(url.substring(2));
- else if (url.startsWith('/')) navigate(url);
- else {
+ else if (url.startsWith('/')) {
+ navigate(url);
+ inboxVisible.value = false;
+ } else {
window.location.href = url;
}
}
@@ -101,13 +102,13 @@ const MobileMessagePageInner = () => {
const title = Schema.compile(selectedChannelObs.value?.title, { t: app.i18n.t }) || t('Message');
return (
-
+
navigate('/page/in-app-message')}>
{title}
-
+