diff --git a/packages/plugins/mobile-client/package.json b/packages/plugins/mobile-client/package.json index 1dd75b0d8..65fe9bc7f 100644 --- a/packages/plugins/mobile-client/package.json +++ b/packages/plugins/mobile-client/package.json @@ -20,7 +20,8 @@ "react": "^18.0.0", "react-dom": "^18.0.0", "react-i18next": "^11.15.1", - "react-router-dom": "^6.11.2" + "react-router-dom": "^6.11.2", + "ahooks": "^3.7.2" }, "peerDependencies": { "@nocobase/client": "0.x", diff --git a/packages/plugins/mobile-client/src/client/core/schema/components/container/Container.Designer.tsx b/packages/plugins/mobile-client/src/client/core/schema/components/container/Container.Designer.tsx index 18e7e15a8..ceffb08ce 100644 --- a/packages/plugins/mobile-client/src/client/core/schema/components/container/Container.Designer.tsx +++ b/packages/plugins/mobile-client/src/client/core/schema/components/container/Container.Designer.tsx @@ -6,20 +6,21 @@ import { Button } from 'antd'; import React from 'react'; import { useNavigate } from 'react-router-dom'; import { generateNTemplate, useTranslation } from '../../../../locale'; -import { PageSchema } from '../../common'; import { findSchema } from '../../helpers'; +import { useSchemaPatch } from '../../hooks'; export const ContainerDesigner = () => { const { t } = useTranslation(); const fieldSchema = useFieldSchema(); - const { dn } = useDesignable(); - const tabBarSchema = fieldSchema.reduceProperties( - (schema, next) => schema || (next['x-component'] === 'MTabBar' && next), - ) as Schema; - - const navigate = useNavigate(); + const { onUpdateComponentProps } = useSchemaPatch(); const field = useField(); + const { dn } = useDesignable(); + const navigate = useNavigate(); + + const tabBarSchema = findSchema(fieldSchema, 'MTabBar'); + const tabBarEnabled = tabBarSchema && field.componentProps.tabBarEnabled !== false; + const schemaSettingsProps = { dn, field, @@ -43,44 +44,41 @@ export const ContainerDesigner = () => { {...schemaSettingsProps} > { if (v) { - const pageSchema = findSchema(fieldSchema, 'MPage'); - if (!pageSchema) return; - await dn.remove(pageSchema); - await dn.insertBeforeEnd({ - type: 'void', - 'x-component': 'MTabBar', - 'x-component-props': {}, - name: 'tabBar', - properties: { - [uid()]: { - type: 'void', - 'x-component': 'MTabBar.Item', - 'x-designer': 'MTabBar.Item.Designer', - 'x-component-props': { - icon: 'HomeOutlined', - title: generateNTemplate('Untitled'), - }, - properties: { - page: pageSchema.toJSON(), + if (!tabBarSchema) { + const pageSchema = findSchema(fieldSchema, 'MPage'); + await dn.insertBeforeEnd({ + type: 'void', + 'x-component': 'MTabBar', + 'x-component-props': {}, + name: 'tabBar', + properties: { + [uid()]: { + type: 'void', + 'x-component': 'MTabBar.Item', + 'x-designer': 'MTabBar.Item.Designer', + 'x-component-props': { + icon: 'HomeOutlined', + title: generateNTemplate('Untitled'), + }, + properties: { + page: pageSchema.toJSON(), + }, }, }, - }, - }); - } else { - const tabBarSchemaFirstKey = Object.keys(tabBarSchema.properties || {})?.[0]; - const pageSchema = tabBarSchemaFirstKey - ? findSchema(tabBarSchema.properties[tabBarSchemaFirstKey], 'MPage') - : null; - await dn.remove(tabBarSchema); - await dn.insertBeforeEnd(pageSchema || PageSchema, { - onSuccess() { - navigate('../'); - }, - }); + }); + } + } + + await onUpdateComponentProps({ + tabBarEnabled: v, + }); + + if (v === false) { + navigate('../'); } }} /> diff --git a/packages/plugins/mobile-client/src/client/core/schema/components/container/Container.tsx b/packages/plugins/mobile-client/src/client/core/schema/components/container/Container.tsx index df30371a6..f5d726505 100644 --- a/packages/plugins/mobile-client/src/client/core/schema/components/container/Container.tsx +++ b/packages/plugins/mobile-client/src/client/core/schema/components/container/Container.tsx @@ -1,4 +1,4 @@ -import { useFieldSchema } from '@formily/react'; +import { useField, useFieldSchema } from '@formily/react'; import { cx, SchemaComponent, SortableItem, useDesigner, useToken } from '@nocobase/client'; import React, { useEffect } from 'react'; import { Navigate, useLocation, useNavigate, useParams } from 'react-router-dom'; @@ -36,6 +36,8 @@ const InternalContainer: React.FC = (props) => { const navigate = useNavigate(); const params = useParams<{ name: string }>(); const location = useLocation(); + const field = useField(); + const isTabBarEnabled = field.componentProps.tabBarEnabled !== false; const tabBarSchema = fieldSchema?.properties?.['tabBar']; const tabBarCurrentFirstKey = tabBarSchema?.properties ? Object.keys(tabBarSchema.properties)[0] : null; let redirectToUid = null; @@ -69,15 +71,17 @@ const InternalContainer: React.FC = (props) => { /> )} -
- { - return schema['x-component'] === 'MTabBar'; - }} - schema={fieldSchema} - > -
+ {isTabBarEnabled && ( +
+ { + return schema['x-component'] === 'MTabBar'; + }} + schema={fieldSchema} + > +
+ )} ); }; diff --git a/packages/plugins/mobile-client/src/client/core/schema/components/page/Page.Designer.tsx b/packages/plugins/mobile-client/src/client/core/schema/components/page/Page.Designer.tsx index d52c42c28..475af21d3 100644 --- a/packages/plugins/mobile-client/src/client/core/schema/components/page/Page.Designer.tsx +++ b/packages/plugins/mobile-client/src/client/core/schema/components/page/Page.Designer.tsx @@ -6,15 +6,19 @@ import { Button } from 'antd'; import React from 'react'; import { generateNTemplate, useTranslation } from '../../../../locale'; import { findGridSchema } from '../../helpers'; +import { useSchemaPatch } from '../../hooks'; export const PageDesigner = (props) => { const { showBack } = props; const { t } = useTranslation(); + const field = useField(); const fieldSchema = useFieldSchema(); const { dn } = useDesignable(); + const { onUpdateComponentProps } = useSchemaPatch(); const headerSchema = fieldSchema?.properties?.['header']; + const isHeaderEnabled = !!headerSchema && field.componentProps?.headerEnabled !== false; const tabsSchema = fieldSchema?.properties?.['tabs']; - const field = useField(); + const isTabsEnabled = !!tabsSchema && field.componentProps?.tabsEnabled !== false; const schemaSettingsProps = { dn, field, @@ -39,10 +43,10 @@ export const PageDesigner = (props) => { {...schemaSettingsProps} > { - if (v) { + if (!headerSchema) { await dn.insertAfterBegin({ type: 'void', name: 'header', @@ -53,53 +57,49 @@ export const PageDesigner = (props) => { showBack, }, }); - } else { - await dn.remove(headerSchema); } - dn.refresh(); + await onUpdateComponentProps({ + headerEnabled: v, + }); }} /> { - if (v) { + if (!tabsSchema) { const gridSchema = findGridSchema(fieldSchema); - if (gridSchema) { - return dn.remove(gridSchema).then(() => { - return dn.insertBeforeEnd({ + await dn.remove(gridSchema); + return dn.insertBeforeEnd({ + type: 'void', + name: 'tabs', + 'x-component': 'Tabs', + 'x-component-props': {}, + 'x-initializer': 'TabPaneInitializers', + 'x-initializer-props': { + gridInitializer: 'MBlockInitializers', + }, + properties: { + tab1: { type: 'void', - name: 'tabs', - 'x-component': 'Tabs', + title: generateNTemplate('Untitled'), + 'x-component': 'Tabs.TabPane', + 'x-designer': 'Tabs.Designer', 'x-component-props': {}, - 'x-initializer': 'TabPaneInitializers', - 'x-initializer-props': { - gridInitializer: 'MBlockInitializers', - }, properties: { - tab1: { - type: 'void', - title: generateNTemplate('Untitled'), - 'x-component': 'Tabs.TabPane', - 'x-designer': 'Tabs.Designer', - 'x-component-props': {}, - properties: { - grid: { - ...gridSchema, - 'x-uid': uid(), - }, - }, + grid: { + ...gridSchema, + 'x-uid': uid(), }, }, - }); - }); - } - } else { - const gridSchema = findGridSchema(tabsSchema.properties[Object.keys(tabsSchema.properties)[0]]); - if (gridSchema) { - return dn.remove(tabsSchema).then(() => dn.insertBeforeEnd(gridSchema, {})); - } + }, + }, + }); } + + await onUpdateComponentProps({ + tabsEnabled: v, + }); }} /> diff --git a/packages/plugins/mobile-client/src/client/core/schema/components/page/Page.tsx b/packages/plugins/mobile-client/src/client/core/schema/components/page/Page.tsx index 5a6050deb..7464d19df 100644 --- a/packages/plugins/mobile-client/src/client/core/schema/components/page/Page.tsx +++ b/packages/plugins/mobile-client/src/client/core/schema/components/page/Page.tsx @@ -1,18 +1,32 @@ -import { RecursionField, useFieldSchema } from '@formily/react'; +import { RecursionField, useField, useFieldSchema } from '@formily/react'; import { ActionBarProvider, SortableItem, TabsContextProvider, cx, useDesigner } from '@nocobase/client'; import { TabsProps } from 'antd'; import React, { useCallback } from 'react'; import { useSearchParams } from 'react-router-dom'; -import { countGridCol } from '../../helpers'; +import { countGridCol, findSchema } from '../../helpers'; import { PageDesigner } from './Page.Designer'; import useStyles from './style'; const InternalPage: React.FC = (props) => { const { styles } = useStyles(); const Designer = useDesigner(); + const field = useField(); const fieldSchema = useFieldSchema(); const [searchParams, setSearchParams] = useSearchParams(); const tabsSchema = fieldSchema.properties?.['tabs']; + const isHeaderEnabled = field.componentProps.headerEnabled !== false; + const isTabsEnabled = field.componentProps.tabsEnabled !== false; + + let pageSchema = findSchema(fieldSchema, 'MPage'); + if (!isTabsEnabled && !pageSchema && tabsSchema) { + const schemaArr = Object.values(tabsSchema.properties || {}).sort((k1, k2) => { + return k1['x-index'] - k2['x-index']; + }); + if (schemaArr.length !== 0) { + pageSchema = Object.values(schemaArr[0].properties)?.[0]; + } + } + // Only support globalActions in page const onlyInPage = fieldSchema.root === fieldSchema.parent; let hasGlobalActions = false; @@ -42,9 +56,9 @@ const InternalPage: React.FC = (props) => { const GlobalActionProvider = useCallback( (props) => { - if (hasGlobalActions) { - return ( - + return ( + + {hasGlobalActions ? ( { > {props.children} - - ); - } - return <>{props.children}; + ) : ( + props.children + )} + + ); }, - [hasGlobalActions, onlyInPage], + [hasGlobalActions, onlyInPage, styles.globalActionCSS], ); return ( @@ -75,21 +90,23 @@ const InternalPage: React.FC = (props) => { }} className={cx('nb-mobile-page-header', styles.mobilePageHeader)} > - { - return 'MHeader' === s['x-component']; - }} - > + {isHeaderEnabled && ( + { + return 'MHeader' === s['x-component']; + }} + > + )} { - return 'Tabs' === s['x-component']; + return 'Tabs' === s['x-component'] || 'Grid.Row' === s['x-component']; }} > diff --git a/packages/plugins/mobile-client/src/client/core/schema/hooks/useSchemaPatch.ts b/packages/plugins/mobile-client/src/client/core/schema/hooks/useSchemaPatch.ts index 6cc29e982..6ac432f53 100644 --- a/packages/plugins/mobile-client/src/client/core/schema/hooks/useSchemaPatch.ts +++ b/packages/plugins/mobile-client/src/client/core/schema/hooks/useSchemaPatch.ts @@ -1,17 +1,17 @@ import { useField, useFieldSchema } from '@formily/react'; import { useDesignable } from '@nocobase/client'; import lodash from 'lodash'; -import { useCallback } from 'react'; +import { useMemoizedFn } from 'ahooks'; export const useSchemaPatch = () => { const { dn } = useDesignable(); const fieldSchema = useFieldSchema(); const field = useField(); - const onUpdateComponentProps = useCallback((data) => { + const onUpdateComponentProps = useMemoizedFn(async (data) => { lodash.set(fieldSchema, 'x-component-props', data); field.componentProps = { ...field.componentProps, ...data }; - dn.emit('patch', { + await dn.emit('patch', { schema: { ['x-uid']: fieldSchema['x-uid'], 'x-component-props': fieldSchema['x-component-props'], @@ -23,8 +23,8 @@ export const useSchemaPatch = () => { ], }, }); - dn.refresh(); - }, []); + await dn.refresh(); + }); return { onUpdateComponentProps }; };