fix(mobile-client): some mobile-client bugs (#2017)
* fix: avoid delete last tab will brake mobile client * fix: when schema not exists * feat: support redirect to mobile route in app * fix: re-insert repeat routes * feat: sync title * fix: sync title * fix: when TabBar is empty will cause page crashed * fix: default label i18n not work * fix: delete last menu will cannot add block ,Close T-434 * fix: tab cannot add block in drawer, Close T-437 * fix: hidden connect data block * fix: login should use replace * feat: display configuration of mobile-client address
This commit is contained in:
parent
a6892dfff3
commit
4ac01b28db
@ -52,7 +52,7 @@ export function useRedirect(next = '/admin') {
|
|||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const redirect = location?.['query']?.redirect;
|
const redirect = location?.['query']?.redirect;
|
||||||
return useCallback(() => {
|
return useCallback(() => {
|
||||||
history.push(redirect || '/admin');
|
history.replace(redirect || '/admin');
|
||||||
}, [redirect, history]);
|
}, [redirect, history]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,6 +119,7 @@ export const useFilterBlock = () => {
|
|||||||
// 有可能存在页面没有提供 FilterBlockProvider 的情况,比如内部使用的数据表管理页面
|
// 有可能存在页面没有提供 FilterBlockProvider 的情况,比如内部使用的数据表管理页面
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
return {
|
return {
|
||||||
|
inProvider: false,
|
||||||
recordDataBlocks: () => {},
|
recordDataBlocks: () => {},
|
||||||
getDataBlocks: () => [] as DataBlock[],
|
getDataBlocks: () => [] as DataBlock[],
|
||||||
removeDataBlock: () => {},
|
removeDataBlock: () => {},
|
||||||
@ -142,5 +143,13 @@ export const useFilterBlock = () => {
|
|||||||
setDataBlocks((prev) => prev.filter((item) => item.uid !== uid));
|
setDataBlocks((prev) => prev.filter((item) => item.uid !== uid));
|
||||||
};
|
};
|
||||||
|
|
||||||
return { recordDataBlocks, getDataBlocks, removeDataBlock };
|
return {
|
||||||
|
recordDataBlocks,
|
||||||
|
getDataBlocks,
|
||||||
|
removeDataBlock,
|
||||||
|
/**
|
||||||
|
* running in FilterBlockProvider
|
||||||
|
*/
|
||||||
|
inProvider: true,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
@ -2,12 +2,12 @@ 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 from 'react';
|
import React, { useMemo } 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 { useTabsContext } from './context';
|
import { TabsContextProvider, useTabsContext } from './context';
|
||||||
import { TabsDesigner } from './Tabs.Designer';
|
import { TabsDesigner } from './Tabs.Designer';
|
||||||
|
|
||||||
export const Tabs: any = observer(
|
export const Tabs: any = observer(
|
||||||
@ -15,6 +15,14 @@ export const Tabs: any = observer(
|
|||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const { render } = useSchemaInitializer(fieldSchema['x-initializer']);
|
const { render } = useSchemaInitializer(fieldSchema['x-initializer']);
|
||||||
const contextProps = useTabsContext();
|
const contextProps = useTabsContext();
|
||||||
|
|
||||||
|
const PaneProvider = useMemo(() => {
|
||||||
|
if (contextProps.deep === false) {
|
||||||
|
return TabsContextProvider;
|
||||||
|
}
|
||||||
|
return React.Fragment;
|
||||||
|
}, [contextProps.deep]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DndContext>
|
<DndContext>
|
||||||
<AntdTabs
|
<AntdTabs
|
||||||
@ -27,7 +35,9 @@ export const Tabs: any = observer(
|
|||||||
{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}>
|
||||||
<RecursionField name={key} schema={schema} onlyRenderProperties />
|
<PaneProvider>
|
||||||
|
<RecursionField name={key} schema={schema} onlyRenderProperties />
|
||||||
|
</PaneProvider>
|
||||||
</AntdTabs.TabPane>
|
</AntdTabs.TabPane>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
import { TabsProps } from 'antd';
|
import { TabsProps } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const TabsContext = React.createContext<TabsProps>({});
|
interface TabsContextProps extends TabsProps {
|
||||||
|
deep?: boolean;
|
||||||
|
}
|
||||||
|
const TabsContext = React.createContext<TabsContextProps>({});
|
||||||
|
|
||||||
export const TabsContextProvider: React.FC<TabsProps> = ({ children, ...props }) => {
|
export const TabsContextProvider: React.FC<TabsContextProps> = ({ children, ...props }) => {
|
||||||
return <TabsContext.Provider value={props}>{children}</TabsContext.Provider>;
|
return <TabsContext.Provider value={props}>{children}</TabsContext.Provider>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ import {
|
|||||||
useCollectionManager,
|
useCollectionManager,
|
||||||
useCompile,
|
useCompile,
|
||||||
useDesignable,
|
useDesignable,
|
||||||
|
useFilterBlock,
|
||||||
useLinkageCollectionFilterOptions,
|
useLinkageCollectionFilterOptions,
|
||||||
} from '..';
|
} from '..';
|
||||||
import { findFilterTargets, updateFilterTargets } from '../block-provider/hooks';
|
import { findFilterTargets, updateFilterTargets } from '../block-provider/hooks';
|
||||||
@ -465,10 +466,15 @@ SchemaSettings.ConnectDataBlocks = function ConnectDataBlocks(props: {
|
|||||||
const { dn } = useDesignable();
|
const { dn } = useDesignable();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const collection = useCollection();
|
const collection = useCollection();
|
||||||
|
const { inProvider } = useFilterBlock();
|
||||||
const dataBlocks = useSupportedBlocks(type);
|
const dataBlocks = useSupportedBlocks(type);
|
||||||
let { targets = [], uid } = findFilterTargets(fieldSchema);
|
let { targets = [], uid } = findFilterTargets(fieldSchema);
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
|
|
||||||
|
if (!inProvider) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const Content = dataBlocks.map((block) => {
|
const Content = dataBlocks.map((block) => {
|
||||||
const title = `${compile(block.collection.title)} #${block.uid.slice(0, 4)}`;
|
const title = `${compile(block.collection.title)} #${block.uid.slice(0, 4)}`;
|
||||||
const onHover = () => {
|
const onHover = () => {
|
||||||
|
@ -1,5 +1,23 @@
|
|||||||
|
import { Card, Form, Input } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { useTranslation } from '../locale';
|
||||||
|
|
||||||
export const AppConfiguration = () => {
|
export const AppConfiguration = () => {
|
||||||
return <>App Configuration</>;
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
style={{
|
||||||
|
minHeight: '600px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Form layout="vertical">
|
||||||
|
<Form.Item
|
||||||
|
tooltip={`${t('The full address is')} ${window.origin}/mobile`}
|
||||||
|
label={t('Mobile client access address')}
|
||||||
|
>
|
||||||
|
<Input value="/mobile" disabled />
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@ -1 +1,2 @@
|
|||||||
import './native-call';
|
import './native-call';
|
||||||
|
export * from './injects';
|
||||||
|
@ -10,3 +10,5 @@ const jsBridge = (window as any).jsBridge as {
|
|||||||
export const invoke: InvokeFunction = (params, cb) => {
|
export const invoke: InvokeFunction = (params, cb) => {
|
||||||
jsBridge.invoke(params, cb);
|
jsBridge.invoke(params, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isJSBridge = !!jsBridge;
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
export * from './page';
|
@ -0,0 +1,16 @@
|
|||||||
|
export const PageSchema = {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'MPage',
|
||||||
|
'x-designer': 'MPage.Designer',
|
||||||
|
'x-component-props': {},
|
||||||
|
properties: {
|
||||||
|
grid: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
'x-initializer': 'MBlockInitializers',
|
||||||
|
'x-component-props': {
|
||||||
|
showDivider: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
@ -1,12 +1,13 @@
|
|||||||
import { SchemaSettings, useDesignable } from '@nocobase/client';
|
import { SchemaSettings, useDesignable } from '@nocobase/client';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from '../../../../locale';
|
import { generateNTemplate, useTranslation } from '../../../../locale';
|
||||||
import { Schema, useField, useFieldSchema } from '@formily/react';
|
import { Schema, useField, useFieldSchema } from '@formily/react';
|
||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { findSchema } from '../../helpers';
|
import { findSchema } from '../../helpers';
|
||||||
import { Button } from 'antd';
|
import { Button } from 'antd';
|
||||||
import { MenuOutlined } from '@ant-design/icons';
|
import { MenuOutlined } from '@ant-design/icons';
|
||||||
|
import { PageSchema } from '../../common';
|
||||||
|
|
||||||
export const ContainerDesigner = () => {
|
export const ContainerDesigner = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -60,7 +61,7 @@ export const ContainerDesigner = () => {
|
|||||||
'x-designer': 'MTabBar.Item.Designer',
|
'x-designer': 'MTabBar.Item.Designer',
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
icon: 'HomeOutlined',
|
icon: 'HomeOutlined',
|
||||||
title: t('Untitled'),
|
title: generateNTemplate('Untitled'),
|
||||||
},
|
},
|
||||||
properties: {
|
properties: {
|
||||||
page: pageSchema.toJSON(),
|
page: pageSchema.toJSON(),
|
||||||
@ -69,10 +70,12 @@ export const ContainerDesigner = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const pageSchema = findSchema(tabBarSchema.properties[Object.keys(tabBarSchema.properties)[0]], 'MPage');
|
const tabBarSchemaFirstKey = Object.keys(tabBarSchema.properties || {})?.[0];
|
||||||
if (!pageSchema) return;
|
const pageSchema = tabBarSchemaFirstKey
|
||||||
|
? findSchema(tabBarSchema.properties[tabBarSchemaFirstKey], 'MPage')
|
||||||
|
: null;
|
||||||
await dn.remove(tabBarSchema);
|
await dn.remove(tabBarSchema);
|
||||||
await dn.insertBeforeEnd(pageSchema, {
|
await dn.insertBeforeEnd(pageSchema || PageSchema, {
|
||||||
onSuccess() {
|
onSuccess() {
|
||||||
history.push('../');
|
history.push('../');
|
||||||
},
|
},
|
||||||
|
@ -34,7 +34,11 @@ const InternalContainer: React.FC = (props) => {
|
|||||||
const params = useParams<{ name: string }>();
|
const params = useParams<{ name: string }>();
|
||||||
const match = useRouteMatch();
|
const match = useRouteMatch();
|
||||||
const tabBarSchema = fieldSchema?.properties?.['tabBar'];
|
const tabBarSchema = fieldSchema?.properties?.['tabBar'];
|
||||||
const redirectToUid = tabBarSchema?.properties[Object.keys(tabBarSchema.properties)[0]]['x-uid'];
|
const tabBarCurrentFirstKey = tabBarSchema?.properties ? Object.keys(tabBarSchema.properties)[0] : null;
|
||||||
|
let redirectToUid = null;
|
||||||
|
if (tabBarCurrentFirstKey) {
|
||||||
|
redirectToUid = tabBarSchema?.properties[tabBarCurrentFirstKey]?.['x-uid'];
|
||||||
|
}
|
||||||
|
|
||||||
const tabRoutes = useMemo<RouteProps[]>(() => {
|
const tabRoutes = useMemo<RouteProps[]>(() => {
|
||||||
if (!redirectToUid) {
|
if (!redirectToUid) {
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { css, cx } from '@emotion/css';
|
import { css, cx } from '@emotion/css';
|
||||||
import { SortableItem, useCompile, useDesigner } from '@nocobase/client';
|
import { SortableItem, useCompile, useDesigner } from '@nocobase/client';
|
||||||
import { NavBar, NavBarProps } from 'antd-mobile';
|
import { NavBar, NavBarProps } from 'antd-mobile';
|
||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { HeaderDesigner } from './Header.Designer';
|
import { HeaderDesigner } from './Header.Designer';
|
||||||
import { useField, useFieldSchema } from '@formily/react';
|
import { useField } from '@formily/react';
|
||||||
|
import { generateNTemplate } from '../../../../locale';
|
||||||
|
|
||||||
export interface HeaderProps extends NavBarProps {
|
export interface HeaderProps extends NavBarProps {
|
||||||
title?: string;
|
title?: string;
|
||||||
@ -12,10 +13,17 @@ export interface HeaderProps extends NavBarProps {
|
|||||||
}
|
}
|
||||||
const InternalHeader = (props: HeaderProps) => {
|
const InternalHeader = (props: HeaderProps) => {
|
||||||
const field = useField();
|
const field = useField();
|
||||||
const { title = '{{ t("Untitled") }}', showBack = false } = { ...props, ...field?.componentProps };
|
const { title = generateNTemplate('Untitled'), showBack = false } = { ...props, ...field?.componentProps };
|
||||||
const Designer = useDesigner();
|
const Designer = useDesigner();
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
|
const compiledTitle = compile(title);
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// sync title
|
||||||
|
document.title = `${compiledTitle} - NocoBase`;
|
||||||
|
}, [compiledTitle]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SortableItem
|
<SortableItem
|
||||||
className={cx(
|
className={cx(
|
||||||
@ -27,7 +35,7 @@ const InternalHeader = (props: HeaderProps) => {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<NavBar backArrow={showBack} onBack={history.goBack}>
|
<NavBar backArrow={showBack} onBack={history.goBack}>
|
||||||
{compile(title)}
|
{compiledTitle}
|
||||||
</NavBar>
|
</NavBar>
|
||||||
<Designer />
|
<Designer />
|
||||||
</SortableItem>
|
</SortableItem>
|
||||||
|
@ -14,6 +14,7 @@ import { useFieldSchema } from '@formily/react';
|
|||||||
import { List } from 'antd-mobile';
|
import { List } from 'antd-mobile';
|
||||||
import { useTranslation } from '../../../../locale';
|
import { useTranslation } from '../../../../locale';
|
||||||
import { menuItemSchema } from './schema';
|
import { menuItemSchema } from './schema';
|
||||||
|
import { PageSchema } from '../../common';
|
||||||
|
|
||||||
const InternalMenu: React.FC = (props) => {
|
const InternalMenu: React.FC = (props) => {
|
||||||
const Designer = useDesigner();
|
const Designer = useDesigner();
|
||||||
@ -24,31 +25,7 @@ const InternalMenu: React.FC = (props) => {
|
|||||||
|
|
||||||
const onAddMenuItem = (values: any) => {
|
const onAddMenuItem = (values: any) => {
|
||||||
const properties = {
|
const properties = {
|
||||||
page: {
|
page: PageSchema,
|
||||||
type: 'void',
|
|
||||||
'x-component': 'MPage',
|
|
||||||
'x-designer': 'MPage.Designer',
|
|
||||||
'x-component-props': {},
|
|
||||||
properties: {
|
|
||||||
header: {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'MHeader',
|
|
||||||
'x-designer': 'MHeader.Designer',
|
|
||||||
'x-component-props': {
|
|
||||||
title: values.name,
|
|
||||||
showBack: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'Grid',
|
|
||||||
'x-component-props': {
|
|
||||||
showDivider: false,
|
|
||||||
},
|
|
||||||
'x-initializer': 'MBlockInitializers',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return insertBeforeEnd({
|
return insertBeforeEnd({
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { GeneralSchemaDesigner, SchemaSettings, useDesignable } from '@nocobase/client';
|
import { GeneralSchemaDesigner, SchemaSettings, useDesignable } from '@nocobase/client';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from '../../../../locale';
|
import { generateNTemplate, useTranslation } from '../../../../locale';
|
||||||
import { useField, useFieldSchema } from '@formily/react';
|
import { useField, useFieldSchema } from '@formily/react';
|
||||||
import { findGridSchema } from '../../helpers';
|
import { findGridSchema } from '../../helpers';
|
||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
@ -78,7 +78,7 @@ export const PageDesigner = (props) => {
|
|||||||
properties: {
|
properties: {
|
||||||
tab1: {
|
tab1: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: '{{t("Untitled")}}',
|
title: generateNTemplate('Untitled'),
|
||||||
'x-component': 'Tabs.TabPane',
|
'x-component': 'Tabs.TabPane',
|
||||||
'x-designer': 'Tabs.Designer',
|
'x-designer': 'Tabs.Designer',
|
||||||
'x-component-props': {},
|
'x-component-props': {},
|
||||||
|
@ -32,16 +32,18 @@ const InternalPage: React.FC = (props) => {
|
|||||||
let hasGlobalActions = false;
|
let hasGlobalActions = false;
|
||||||
if (!tabsSchema) {
|
if (!tabsSchema) {
|
||||||
hasGlobalActions = countGridCol(fieldSchema.properties['grid'], 2) === 1;
|
hasGlobalActions = countGridCol(fieldSchema.properties['grid'], 2) === 1;
|
||||||
} else if (query.has('tab') && tabsSchema.properties[query.get('tab')]) {
|
} else if (query.has('tab') && tabsSchema.properties?.[query.get('tab')]) {
|
||||||
hasGlobalActions = countGridCol(tabsSchema.properties[query.get('tab')]?.properties?.['grid'], 2) === 1;
|
hasGlobalActions = countGridCol(tabsSchema.properties[query.get('tab')]?.properties?.['grid'], 2) === 1;
|
||||||
} else {
|
} 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];
|
||||||
history.replace({
|
if (schema) {
|
||||||
pathname: location.pathname,
|
history.replace({
|
||||||
search: new URLSearchParams({
|
pathname: location.pathname,
|
||||||
tab: schema.name.toString(),
|
search: new URLSearchParams({
|
||||||
}).toString(),
|
tab: schema.name.toString(),
|
||||||
});
|
}).toString(),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onTabsChange = useCallback<TabsProps['onChange']>(
|
const onTabsChange = useCallback<TabsProps['onChange']>(
|
||||||
@ -110,7 +112,7 @@ const InternalPage: React.FC = (props) => {
|
|||||||
return 'MHeader' === s['x-component'];
|
return 'MHeader' === s['x-component'];
|
||||||
}}
|
}}
|
||||||
></RecursionField>
|
></RecursionField>
|
||||||
<TabsContextProvider activeKey={query.get('tab')} onChange={onTabsChange}>
|
<TabsContextProvider deep={false} activeKey={query.get('tab')} onChange={onTabsChange}>
|
||||||
<RecursionField
|
<RecursionField
|
||||||
schema={fieldSchema}
|
schema={fieldSchema}
|
||||||
filterProperties={(s) => {
|
filterProperties={(s) => {
|
||||||
|
@ -2,21 +2,30 @@ import { TabBar } from 'antd-mobile';
|
|||||||
import { TabBarItem } from './TabBar.Item';
|
import { TabBarItem } from './TabBar.Item';
|
||||||
import React, { useCallback, useContext } from 'react';
|
import React, { useCallback, useContext } from 'react';
|
||||||
import { SchemaOptionsContext, useFieldSchema } from '@formily/react';
|
import { SchemaOptionsContext, useFieldSchema } from '@formily/react';
|
||||||
import { DndContext, Icon, SchemaComponent, SchemaInitializer, SortableItem, useDesignable } from '@nocobase/client';
|
import {
|
||||||
|
DndContext,
|
||||||
|
Icon,
|
||||||
|
SchemaComponent,
|
||||||
|
SchemaInitializer,
|
||||||
|
SortableItem,
|
||||||
|
useCompile,
|
||||||
|
useDesignable,
|
||||||
|
} from '@nocobase/client';
|
||||||
import { useTranslation } from '../../../../locale';
|
import { useTranslation } from '../../../../locale';
|
||||||
import { css, cx } from '@emotion/css';
|
import { css, cx } from '@emotion/css';
|
||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
import { useHistory, useParams } from 'react-router-dom';
|
import { useHistory, useParams } from 'react-router-dom';
|
||||||
import { tabItemSchema } from './schema';
|
import { tabItemSchema } from './schema';
|
||||||
|
import { PageSchema } from '../../common';
|
||||||
|
|
||||||
export const InternalTabBar: React.FC = (props) => {
|
export const InternalTabBar: React.FC = (props) => {
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const { designable } = useDesignable();
|
const { designable } = useDesignable();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const options = useContext(SchemaOptionsContext);
|
const { insertBeforeEnd } = useDesignable();
|
||||||
const { insertBeforeEnd, dn } = useDesignable();
|
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const params = useParams<{ name: string }>();
|
const params = useParams<{ name: string }>();
|
||||||
|
const compile = useCompile();
|
||||||
|
|
||||||
const onAddTab = useCallback((values: any) => {
|
const onAddTab = useCallback((values: any) => {
|
||||||
return insertBeforeEnd({
|
return insertBeforeEnd({
|
||||||
@ -25,25 +34,11 @@ export const InternalTabBar: React.FC = (props) => {
|
|||||||
'x-component-props': values,
|
'x-component-props': values,
|
||||||
'x-designer': 'MTabBar.Item.Designer',
|
'x-designer': 'MTabBar.Item.Designer',
|
||||||
properties: {
|
properties: {
|
||||||
[uid()]: {
|
[uid()]: PageSchema,
|
||||||
type: 'void',
|
|
||||||
'x-component': 'MPage',
|
|
||||||
'x-designer': 'MPage.Designer',
|
|
||||||
'x-component-props': {},
|
|
||||||
properties: {
|
|
||||||
grid: {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'Grid',
|
|
||||||
'x-initializer': 'MBlockInitializers',
|
|
||||||
'x-component-props': {
|
|
||||||
showDivider: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SortableItem
|
<SortableItem
|
||||||
className={cx(
|
className={cx(
|
||||||
@ -80,7 +75,7 @@ export const InternalTabBar: React.FC = (props) => {
|
|||||||
key={`tab_${schema['x-uid']}`}
|
key={`tab_${schema['x-uid']}`}
|
||||||
title={
|
title={
|
||||||
<>
|
<>
|
||||||
{cp.title}
|
{compile(cp.title)}
|
||||||
<SchemaComponent schema={schema} name={name} />
|
<SchemaComponent schema={schema} name={name} />
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
@ -88,7 +83,7 @@ export const InternalTabBar: React.FC = (props) => {
|
|||||||
></TabBar.Item>
|
></TabBar.Item>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{designable && Object.keys(fieldSchema.properties).length < 5 ? (
|
{designable && (!fieldSchema.properties || Object.keys(fieldSchema.properties).length < 5) ? (
|
||||||
<TabBar.Item
|
<TabBar.Item
|
||||||
className={css`
|
className={css`
|
||||||
.adm-tab-bar-item-icon {
|
.adm-tab-bar-item-icon {
|
||||||
|
@ -13,7 +13,7 @@ export function lang(key: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function generateNTemplate(key: string) {
|
export function generateNTemplate(key: string) {
|
||||||
return `{{t('${key}', { ns: '${NAMESPACE}' })}}`;
|
return `{{t('${key}', { ns: '${NAMESPACE}', nsMode: 'fallback' })}}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useTranslation() {
|
export function useTranslation() {
|
||||||
|
@ -26,6 +26,8 @@ const locale = {
|
|||||||
Settings: '设置',
|
Settings: '设置',
|
||||||
'Delete settings block': '删除设置区块',
|
'Delete settings block': '删除设置区块',
|
||||||
'Delete menu block': '删除菜单区块',
|
'Delete menu block': '删除菜单区块',
|
||||||
|
'Mobile client access address': '移动端访问地址',
|
||||||
|
'The full address is': '完整的地址是',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default locale;
|
export default locale;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { RouteSwitch, useRoutes } from '@nocobase/client';
|
import { RouteSwitch, useRoutes } from '@nocobase/client';
|
||||||
import React, { useEffect, useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { HashRouter } from 'react-router-dom';
|
import { HashRouter } from 'react-router-dom';
|
||||||
import { InterfaceProvider } from './InterfaceProvider';
|
import { InterfaceProvider } from './InterfaceProvider';
|
||||||
|
|
||||||
|
@ -1,9 +1,17 @@
|
|||||||
import { RouteSwitchProvider, useRouteSwitchContext } from '@nocobase/client';
|
import { RouteSwitchProvider, useRouteSwitchContext } from '@nocobase/client';
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import MApplication from './Application';
|
import MApplication from './Application';
|
||||||
|
import { isJSBridge } from '../core/bridge';
|
||||||
|
|
||||||
export const RouterSwitchProvider = (props) => {
|
export const RouterSwitchProvider = (props) => {
|
||||||
const { routes, components } = useRouteSwitchContext();
|
const { routes, components } = useRouteSwitchContext();
|
||||||
|
// redirect to mobile
|
||||||
|
if (isJSBridge) {
|
||||||
|
const redirectRoute = routes[0];
|
||||||
|
if (redirectRoute.type === 'redirect' && redirectRoute.to === '/admin') {
|
||||||
|
redirectRoute.to = '/mobile';
|
||||||
|
}
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<RouteSwitchProvider
|
<RouteSwitchProvider
|
||||||
routes={routes}
|
routes={routes}
|
||||||
|
Loading…
Reference in New Issue
Block a user