fix(mobile-client): fix multiple bugs and do some improvement (#2072)

This commit is contained in:
Dunqing 2023-06-21 22:26:15 +08:00 committed by GitHub
parent 6254fceb04
commit b80aaacb38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 103 additions and 103 deletions

View File

@ -42,7 +42,5 @@
} }
html body { html body {
--adm-font-family: -apple-system, BlinkMacSystemFont, Segoe UI, PingFang SC, Hiragino Sans GB, --adm-font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
Microsoft YaHei, Helvetica Neue, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji,
Segoe UI Symbol;
} }

View File

@ -2,45 +2,36 @@ import { css } from '@emotion/css';
import { observer, RecursionField, useField, useFieldSchema } from '@formily/react'; import { observer, RecursionField, useField, useFieldSchema } from '@formily/react';
import { TabPaneProps, Tabs as AntdTabs, TabsProps } from 'antd'; import { TabPaneProps, Tabs as AntdTabs, TabsProps } from 'antd';
import classNames from 'classnames'; import classNames from 'classnames';
import React, { useMemo } from 'react'; import React from 'react';
import { Icon } from '../../../icon'; import { Icon } from '../../../icon';
import { useSchemaInitializer } from '../../../schema-initializer'; import { useSchemaInitializer } from '../../../schema-initializer';
import { DndContext, SortableItem } from '../../common'; import { DndContext, SortableItem } from '../../common';
import { useDesigner } from '../../hooks/useDesigner'; import { useDesigner } from '../../hooks/useDesigner';
import { TabsContextProvider, useTabsContext } from './context'; import { useTabsContext } from './context';
import { TabsDesigner } from './Tabs.Designer'; import { TabsDesigner } from './Tabs.Designer';
import { useDesignable } from '../../hooks';
export const Tabs: any = observer( export const Tabs: any = observer(
(props: TabsProps) => { (props: TabsProps) => {
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const { render } = useSchemaInitializer(fieldSchema['x-initializer']); const { render } = useSchemaInitializer(fieldSchema['x-initializer']);
const { designable } = useDesignable();
const contextProps = useTabsContext(); const contextProps = useTabsContext();
const { PaneRoot = React.Fragment as React.FC<any> } = contextProps;
const PaneProvider = useMemo(() => {
if (contextProps.deep === false) {
return TabsContextProvider;
}
return React.Fragment;
}, [contextProps.deep]);
return ( return (
<DndContext> <DndContext>
<AntdTabs <AntdTabs {...contextProps} style={props.style}>
{...contextProps}
style={props.style}
tabBarExtraContent={{
right: render(),
}}
>
{fieldSchema.mapProperties((schema, key) => { {fieldSchema.mapProperties((schema, key) => {
return ( return (
<AntdTabs.TabPane tab={<RecursionField name={key} schema={schema} onlyRenderSelf />} key={key}> <AntdTabs.TabPane tab={<RecursionField name={key} schema={schema} onlyRenderSelf />} key={key}>
<PaneProvider> <PaneRoot active={key === contextProps.activeKey}>
<RecursionField name={key} schema={schema} onlyRenderProperties /> <RecursionField name={key} schema={schema} onlyRenderProperties />
</PaneProvider> </PaneRoot>
</AntdTabs.TabPane> </AntdTabs.TabPane>
); );
})} })}
{designable && <AntdTabs.TabPane tab={render()} />}
</AntdTabs> </AntdTabs>
</DndContext> </DndContext>
); );

View File

@ -2,7 +2,7 @@ import { TabsProps } from 'antd';
import React from 'react'; import React from 'react';
interface TabsContextProps extends TabsProps { interface TabsContextProps extends TabsProps {
deep?: boolean; PaneRoot?: React.FC<any>;
} }
const TabsContext = React.createContext<TabsContextProps>({}); const TabsContext = React.createContext<TabsContextProps>({});

View File

@ -3,12 +3,13 @@ interface InvokeFunction {
(params: { action: 'moveTaskToBack' }, cb?: () => void): void; (params: { action: 'moveTaskToBack' }, cb?: () => void): void;
} }
const JsBridge = (window as any).JsBridge as { const getJsBridge = () =>
invoke: InvokeFunction; (window as any).JsBridge as {
}; invoke: InvokeFunction;
};
export const invoke: InvokeFunction = (params, cb) => { export const invoke: InvokeFunction = (params, cb) => {
JsBridge.invoke(params, cb); return getJsBridge().invoke(params, cb);
}; };
export const isJSBridge = !!JsBridge; export const isJSBridge = () => !!getJsBridge();

View File

@ -6,6 +6,7 @@ import React from 'react';
import { useLocation, useNavigate, useParams } from 'react-router-dom'; import { useLocation, useNavigate, useParams } from 'react-router-dom';
import { useTranslation } from '../../../../locale'; import { useTranslation } from '../../../../locale';
import { useSchemaPatch } from '../../hooks'; import { useSchemaPatch } from '../../hooks';
import { menuItemSchema } from './schema';
interface MMenuItemProps extends ListItemProps { interface MMenuItemProps extends ListItemProps {
name: string; name: string;
@ -56,24 +57,7 @@ const MenuItemDesigner: React.FC = () => {
<SchemaSettings.ModalItem <SchemaSettings.ModalItem
title={t('Edit menu info')} title={t('Edit menu info')}
initialValues={field.componentProps} initialValues={field.componentProps}
schema={{ schema={menuItemSchema}
properties: {
name: {
type: 'string',
title: t('Menu name'),
required: true,
'x-component': 'Input',
'x-decorator': 'FormItem',
},
icon: {
required: true,
'x-decorator': 'FormItem',
'x-component': 'IconPicker',
title: t('Icon'),
'x-component-props': {},
},
},
}}
onSubmit={onUpdateComponentProps} onSubmit={onUpdateComponentProps}
/> />
<SchemaSettings.Remove <SchemaSettings.Remove

View File

@ -1,8 +1,10 @@
import { generateNTemplate } from '../../../../locale';
export const menuItemSchema = { export const menuItemSchema = {
properties: { properties: {
name: { name: {
type: 'string', type: 'string',
title: `{{t('Menu name')}}`, title: generateNTemplate('Menu name'),
required: true, required: true,
'x-component': 'Input', 'x-component': 'Input',
'x-decorator': 'FormItem', 'x-decorator': 'FormItem',
@ -10,7 +12,7 @@ export const menuItemSchema = {
icon: { icon: {
'x-decorator': 'FormItem', 'x-decorator': 'FormItem',
'x-component': 'IconPicker', 'x-component': 'IconPicker',
title: `{{t('Icon')}}`, title: generateNTemplate('Icon'),
'x-component-props': {}, 'x-component-props': {},
}, },
}, },

View File

@ -2,7 +2,7 @@ import { css, cx } from '@emotion/css';
import { RecursionField, useFieldSchema } from '@formily/react'; import { RecursionField, useFieldSchema } from '@formily/react';
import { ActionBarProvider, SortableItem, TabsContextProvider, useDesigner } from '@nocobase/client'; import { ActionBarProvider, SortableItem, TabsContextProvider, useDesigner } from '@nocobase/client';
import { TabsProps } from 'antd'; import { TabsProps } from 'antd';
import React, { useCallback, useMemo } from 'react'; import React, { useCallback } from 'react';
import { useSearchParams } from 'react-router-dom'; import { useSearchParams } from 'react-router-dom';
import { countGridCol } from '../../helpers'; import { countGridCol } from '../../helpers';
import { PageDesigner } from './Page.Designer'; import { PageDesigner } from './Page.Designer';
@ -28,90 +28,113 @@ const InternalPage: React.FC = (props) => {
// Only support globalActions in page // Only support globalActions in page
const onlyInPage = fieldSchema.root === fieldSchema.parent; const onlyInPage = fieldSchema.root === fieldSchema.parent;
let hasGlobalActions = false; let hasGlobalActions = false;
if (!tabsSchema) { if (!tabsSchema && fieldSchema.properties) {
hasGlobalActions = countGridCol(fieldSchema.properties['grid'], 2) === 1; hasGlobalActions = countGridCol(fieldSchema.properties['grid'], 2) === 1;
} else if (searchParams.has('tab') && tabsSchema.properties?.[searchParams.get('tab')]) { } else if (searchParams.has('tab') && tabsSchema?.properties?.[searchParams.get('tab')]) {
hasGlobalActions = countGridCol(tabsSchema.properties[searchParams.get('tab')]?.properties?.['grid'], 2) === 1; hasGlobalActions = countGridCol(tabsSchema.properties[searchParams.get('tab')]?.properties?.['grid'], 2) === 1;
} else if (tabsSchema.properties) { } else if (tabsSchema?.properties) {
const schema = Object.values(tabsSchema.properties).sort((t1, t2) => t1['x-index'] - t2['x-index'])[0]; const schema = Object.values(tabsSchema.properties).sort((t1, t2) => t1['x-index'] - t2['x-index'])[0];
if (schema) { if (schema) {
setTimeout(() => { setTimeout(() => {
setSearchParams([['tab', schema.name.toString()]]); setSearchParams([['tab', schema.name.toString()]], {
replace: true,
});
}); });
} }
} }
const onTabsChange = useCallback<TabsProps['onChange']>( const onTabsChange = useCallback<TabsProps['onChange']>(
(key) => { (key) => {
setSearchParams([['tab', key]]); setSearchParams([['tab', key]], {
replace: true,
});
}, },
[setSearchParams], [setSearchParams],
); );
const GlobalActionProvider = useMemo(() => { const GlobalActionProvider = useCallback(
if (hasGlobalActions) { (props) => {
return ActionBarProvider; if (hasGlobalActions) {
} return (
return (props) => <>{props.children}</>; <TabsContextProvider>
}, [hasGlobalActions]); <ActionBarProvider
container={
(typeof props.active !== 'undefined' ? props.active : true) && onlyInPage
? document.getElementById('nb-position-container')
: null
}
forceProps={{
layout: 'one-column',
className: globalActionCSS,
}}
>
{props.children}
</ActionBarProvider>
</TabsContextProvider>
);
}
return <>{props.children}</>;
},
[hasGlobalActions, onlyInPage],
);
return ( return (
<GlobalActionProvider <SortableItem
container={hasGlobalActions && onlyInPage ? document.getElementById('nb-position-container') : null} eid="nb-mobile-scroll-wrapper"
forceProps={{ className={cx(
layout: 'one-column', 'nb-mobile-page',
className: globalActionCSS, css`
}} background: #f0f2f5;
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
overflow-x: hidden;
overflow-y: auto;
padding-bottom: var(--nb-spacing);
`,
)}
> >
<SortableItem <Designer {...fieldSchema?.['x-designer-props']}></Designer>
eid="nb-mobile-scroll-wrapper" <div
style={{
paddingBottom: tabsSchema ? null : 'var(--nb-spacing)',
}}
className={cx( className={cx(
'nb-mobile-page', 'nb-mobile-page-header',
css` css`
background: #f0f2f5; & > .ant-tabs > .ant-tabs-nav {
.ant-tabs-tab {
margin: 0 !important;
padding: 0 16px !important;
}
background: #fff;
}
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 100%;
height: 100%;
overflow-x: hidden;
overflow-y: auto;
padding-bottom: var(--nb-spacing);
`, `,
)} )}
> >
<Designer {...fieldSchema?.['x-designer-props']}></Designer> <RecursionField
<div schema={fieldSchema}
style={{ filterProperties={(s) => {
paddingBottom: tabsSchema ? null : 'var(--nb-spacing)', return 'MHeader' === s['x-component'];
}} }}
className={cx( ></RecursionField>
'nb-mobile-page-header', <TabsContextProvider
css` PaneRoot={GlobalActionProvider}
& > .ant-tabs > .ant-tabs-nav { activeKey={searchParams.get('tab')}
background: #fff; onChange={onTabsChange}
padding: 0 var(--nb-spacing);
}
display: flex;
flex-direction: column;
`,
)}
> >
<RecursionField <RecursionField
schema={fieldSchema} schema={fieldSchema}
filterProperties={(s) => { filterProperties={(s) => {
return 'MHeader' === s['x-component']; return 'Tabs' === s['x-component'];
}} }}
></RecursionField> ></RecursionField>
<TabsContextProvider deep={false} activeKey={searchParams.get('tab')} onChange={onTabsChange}> </TabsContextProvider>
<RecursionField </div>
schema={fieldSchema} <GlobalActionProvider>
filterProperties={(s) => {
return 'Tabs' === s['x-component'];
}}
></RecursionField>
</TabsContextProvider>
</div>
{!tabsSchema && ( {!tabsSchema && (
<RecursionField <RecursionField
schema={fieldSchema} schema={fieldSchema}
@ -120,8 +143,8 @@ const InternalPage: React.FC = (props) => {
}} }}
></RecursionField> ></RecursionField>
)} )}
</SortableItem> </GlobalActionProvider>
</GlobalActionProvider> </SortableItem>
); );
}; };

View File

@ -115,6 +115,7 @@ const MApplication: React.FC = (props) => {
{props.children} {props.children}
</RemoteSchemaComponent> </RemoteSchemaComponent>
)} )}
{/* Global action will insert here */}
<div id="nb-position-container"></div> <div id="nb-position-container"></div>
</div> </div>
</ActionContextProvider> </ActionContextProvider>

View File

@ -10,7 +10,7 @@ export const RouterSwitchProvider = (props) => {
const location = useLocation(); const location = useLocation();
const navigate = useNavigate(); const navigate = useNavigate();
useEffect(() => { useEffect(() => {
if (isJSBridge) { if (isJSBridge()) {
if (location.pathname.includes('/admin')) { if (location.pathname.includes('/admin')) {
navigate('/mobile'); navigate('/mobile');
} }