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 redirect = location?.['query']?.redirect;
 | 
			
		||||
  return useCallback(() => {
 | 
			
		||||
    history.push(redirect || '/admin');
 | 
			
		||||
    history.replace(redirect || '/admin');
 | 
			
		||||
  }, [redirect, history]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -119,6 +119,7 @@ export const useFilterBlock = () => {
 | 
			
		||||
  // 有可能存在页面没有提供 FilterBlockProvider 的情况,比如内部使用的数据表管理页面
 | 
			
		||||
  if (!ctx) {
 | 
			
		||||
    return {
 | 
			
		||||
      inProvider: false,
 | 
			
		||||
      recordDataBlocks: () => {},
 | 
			
		||||
      getDataBlocks: () => [] as DataBlock[],
 | 
			
		||||
      removeDataBlock: () => {},
 | 
			
		||||
@ -142,5 +143,13 @@ export const useFilterBlock = () => {
 | 
			
		||||
    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 { TabPaneProps, Tabs as AntdTabs, TabsProps } from 'antd';
 | 
			
		||||
import classNames from 'classnames';
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import React, { useMemo } from 'react';
 | 
			
		||||
import { Icon } from '../../../icon';
 | 
			
		||||
import { useSchemaInitializer } from '../../../schema-initializer';
 | 
			
		||||
import { DndContext, SortableItem } from '../../common';
 | 
			
		||||
import { useDesigner } from '../../hooks/useDesigner';
 | 
			
		||||
import { useTabsContext } from './context';
 | 
			
		||||
import { TabsContextProvider, useTabsContext } from './context';
 | 
			
		||||
import { TabsDesigner } from './Tabs.Designer';
 | 
			
		||||
 | 
			
		||||
export const Tabs: any = observer(
 | 
			
		||||
@ -15,6 +15,14 @@ export const Tabs: any = observer(
 | 
			
		||||
    const fieldSchema = useFieldSchema();
 | 
			
		||||
    const { render } = useSchemaInitializer(fieldSchema['x-initializer']);
 | 
			
		||||
    const contextProps = useTabsContext();
 | 
			
		||||
 | 
			
		||||
    const PaneProvider = useMemo(() => {
 | 
			
		||||
      if (contextProps.deep === false) {
 | 
			
		||||
        return TabsContextProvider;
 | 
			
		||||
      }
 | 
			
		||||
      return React.Fragment;
 | 
			
		||||
    }, [contextProps.deep]);
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <DndContext>
 | 
			
		||||
        <AntdTabs
 | 
			
		||||
@ -27,7 +35,9 @@ export const Tabs: any = observer(
 | 
			
		||||
          {fieldSchema.mapProperties((schema, key) => {
 | 
			
		||||
            return (
 | 
			
		||||
              <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>
 | 
			
		||||
            );
 | 
			
		||||
          })}
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,12 @@
 | 
			
		||||
import { TabsProps } from 'antd';
 | 
			
		||||
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>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -41,6 +41,7 @@ import {
 | 
			
		||||
  useCollectionManager,
 | 
			
		||||
  useCompile,
 | 
			
		||||
  useDesignable,
 | 
			
		||||
  useFilterBlock,
 | 
			
		||||
  useLinkageCollectionFilterOptions,
 | 
			
		||||
} from '..';
 | 
			
		||||
import { findFilterTargets, updateFilterTargets } from '../block-provider/hooks';
 | 
			
		||||
@ -465,10 +466,15 @@ SchemaSettings.ConnectDataBlocks = function ConnectDataBlocks(props: {
 | 
			
		||||
  const { dn } = useDesignable();
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const collection = useCollection();
 | 
			
		||||
  const { inProvider } = useFilterBlock();
 | 
			
		||||
  const dataBlocks = useSupportedBlocks(type);
 | 
			
		||||
  let { targets = [], uid } = findFilterTargets(fieldSchema);
 | 
			
		||||
  const compile = useCompile();
 | 
			
		||||
 | 
			
		||||
  if (!inProvider) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const Content = dataBlocks.map((block) => {
 | 
			
		||||
    const title = `${compile(block.collection.title)} #${block.uid.slice(0, 4)}`;
 | 
			
		||||
    const onHover = () => {
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,23 @@
 | 
			
		||||
import { Card, Form, Input } from 'antd';
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { useTranslation } from '../locale';
 | 
			
		||||
 | 
			
		||||
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';
 | 
			
		||||
export * from './injects';
 | 
			
		||||
 | 
			
		||||
@ -10,3 +10,5 @@ const jsBridge = (window as any).jsBridge as {
 | 
			
		||||
export const invoke: InvokeFunction = (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 React from 'react';
 | 
			
		||||
import { useTranslation } from '../../../../locale';
 | 
			
		||||
import { generateNTemplate, useTranslation } from '../../../../locale';
 | 
			
		||||
import { Schema, useField, useFieldSchema } from '@formily/react';
 | 
			
		||||
import { uid } from '@formily/shared';
 | 
			
		||||
import { useHistory } from 'react-router-dom';
 | 
			
		||||
import { findSchema } from '../../helpers';
 | 
			
		||||
import { Button } from 'antd';
 | 
			
		||||
import { MenuOutlined } from '@ant-design/icons';
 | 
			
		||||
import { PageSchema } from '../../common';
 | 
			
		||||
 | 
			
		||||
export const ContainerDesigner = () => {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
@ -60,7 +61,7 @@ export const ContainerDesigner = () => {
 | 
			
		||||
                  'x-designer': 'MTabBar.Item.Designer',
 | 
			
		||||
                  'x-component-props': {
 | 
			
		||||
                    icon: 'HomeOutlined',
 | 
			
		||||
                    title: t('Untitled'),
 | 
			
		||||
                    title: generateNTemplate('Untitled'),
 | 
			
		||||
                  },
 | 
			
		||||
                  properties: {
 | 
			
		||||
                    page: pageSchema.toJSON(),
 | 
			
		||||
@ -69,10 +70,12 @@ export const ContainerDesigner = () => {
 | 
			
		||||
              },
 | 
			
		||||
            });
 | 
			
		||||
          } else {
 | 
			
		||||
            const pageSchema = findSchema(tabBarSchema.properties[Object.keys(tabBarSchema.properties)[0]], 'MPage');
 | 
			
		||||
            if (!pageSchema) return;
 | 
			
		||||
            const tabBarSchemaFirstKey = Object.keys(tabBarSchema.properties || {})?.[0];
 | 
			
		||||
            const pageSchema = tabBarSchemaFirstKey
 | 
			
		||||
              ? findSchema(tabBarSchema.properties[tabBarSchemaFirstKey], 'MPage')
 | 
			
		||||
              : null;
 | 
			
		||||
            await dn.remove(tabBarSchema);
 | 
			
		||||
            await dn.insertBeforeEnd(pageSchema, {
 | 
			
		||||
            await dn.insertBeforeEnd(pageSchema || PageSchema, {
 | 
			
		||||
              onSuccess() {
 | 
			
		||||
                history.push('../');
 | 
			
		||||
              },
 | 
			
		||||
 | 
			
		||||
@ -34,7 +34,11 @@ const InternalContainer: React.FC = (props) => {
 | 
			
		||||
  const params = useParams<{ name: string }>();
 | 
			
		||||
  const match = useRouteMatch();
 | 
			
		||||
  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[]>(() => {
 | 
			
		||||
    if (!redirectToUid) {
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,11 @@
 | 
			
		||||
import { css, cx } from '@emotion/css';
 | 
			
		||||
import { SortableItem, useCompile, useDesigner } from '@nocobase/client';
 | 
			
		||||
import { NavBar, NavBarProps } from 'antd-mobile';
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import React, { useEffect } from 'react';
 | 
			
		||||
import { useHistory } from 'react-router-dom';
 | 
			
		||||
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 {
 | 
			
		||||
  title?: string;
 | 
			
		||||
@ -12,10 +13,17 @@ export interface HeaderProps extends NavBarProps {
 | 
			
		||||
}
 | 
			
		||||
const InternalHeader = (props: HeaderProps) => {
 | 
			
		||||
  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 compile = useCompile();
 | 
			
		||||
  const compiledTitle = compile(title);
 | 
			
		||||
  const history = useHistory();
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    // sync title
 | 
			
		||||
    document.title = `${compiledTitle} - NocoBase`;
 | 
			
		||||
  }, [compiledTitle]);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <SortableItem
 | 
			
		||||
      className={cx(
 | 
			
		||||
@ -27,7 +35,7 @@ const InternalHeader = (props: HeaderProps) => {
 | 
			
		||||
      )}
 | 
			
		||||
    >
 | 
			
		||||
      <NavBar backArrow={showBack} onBack={history.goBack}>
 | 
			
		||||
        {compile(title)}
 | 
			
		||||
        {compiledTitle}
 | 
			
		||||
      </NavBar>
 | 
			
		||||
      <Designer />
 | 
			
		||||
    </SortableItem>
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,7 @@ import { useFieldSchema } from '@formily/react';
 | 
			
		||||
import { List } from 'antd-mobile';
 | 
			
		||||
import { useTranslation } from '../../../../locale';
 | 
			
		||||
import { menuItemSchema } from './schema';
 | 
			
		||||
import { PageSchema } from '../../common';
 | 
			
		||||
 | 
			
		||||
const InternalMenu: React.FC = (props) => {
 | 
			
		||||
  const Designer = useDesigner();
 | 
			
		||||
@ -24,31 +25,7 @@ const InternalMenu: React.FC = (props) => {
 | 
			
		||||
 | 
			
		||||
  const onAddMenuItem = (values: any) => {
 | 
			
		||||
    const properties = {
 | 
			
		||||
      page: {
 | 
			
		||||
        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',
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      page: PageSchema,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return insertBeforeEnd({
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
import { GeneralSchemaDesigner, SchemaSettings, useDesignable } from '@nocobase/client';
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { useTranslation } from '../../../../locale';
 | 
			
		||||
import { generateNTemplate, useTranslation } from '../../../../locale';
 | 
			
		||||
import { useField, useFieldSchema } from '@formily/react';
 | 
			
		||||
import { findGridSchema } from '../../helpers';
 | 
			
		||||
import { uid } from '@formily/shared';
 | 
			
		||||
@ -78,7 +78,7 @@ export const PageDesigner = (props) => {
 | 
			
		||||
                  properties: {
 | 
			
		||||
                    tab1: {
 | 
			
		||||
                      type: 'void',
 | 
			
		||||
                      title: '{{t("Untitled")}}',
 | 
			
		||||
                      title: generateNTemplate('Untitled'),
 | 
			
		||||
                      'x-component': 'Tabs.TabPane',
 | 
			
		||||
                      'x-designer': 'Tabs.Designer',
 | 
			
		||||
                      'x-component-props': {},
 | 
			
		||||
 | 
			
		||||
@ -32,16 +32,18 @@ const InternalPage: React.FC = (props) => {
 | 
			
		||||
  let hasGlobalActions = false;
 | 
			
		||||
  if (!tabsSchema) {
 | 
			
		||||
    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;
 | 
			
		||||
  } else {
 | 
			
		||||
  } else if (tabsSchema.properties) {
 | 
			
		||||
    const schema = Object.values(tabsSchema.properties).sort((t1, t2) => t1['x-index'] - t2['x-index'])[0];
 | 
			
		||||
    history.replace({
 | 
			
		||||
      pathname: location.pathname,
 | 
			
		||||
      search: new URLSearchParams({
 | 
			
		||||
        tab: schema.name.toString(),
 | 
			
		||||
      }).toString(),
 | 
			
		||||
    });
 | 
			
		||||
    if (schema) {
 | 
			
		||||
      history.replace({
 | 
			
		||||
        pathname: location.pathname,
 | 
			
		||||
        search: new URLSearchParams({
 | 
			
		||||
          tab: schema.name.toString(),
 | 
			
		||||
        }).toString(),
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const onTabsChange = useCallback<TabsProps['onChange']>(
 | 
			
		||||
@ -110,7 +112,7 @@ const InternalPage: React.FC = (props) => {
 | 
			
		||||
              return 'MHeader' === s['x-component'];
 | 
			
		||||
            }}
 | 
			
		||||
          ></RecursionField>
 | 
			
		||||
          <TabsContextProvider activeKey={query.get('tab')} onChange={onTabsChange}>
 | 
			
		||||
          <TabsContextProvider deep={false} activeKey={query.get('tab')} onChange={onTabsChange}>
 | 
			
		||||
            <RecursionField
 | 
			
		||||
              schema={fieldSchema}
 | 
			
		||||
              filterProperties={(s) => {
 | 
			
		||||
 | 
			
		||||
@ -2,21 +2,30 @@ import { TabBar } from 'antd-mobile';
 | 
			
		||||
import { TabBarItem } from './TabBar.Item';
 | 
			
		||||
import React, { useCallback, useContext } from '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 { css, cx } from '@emotion/css';
 | 
			
		||||
import { uid } from '@formily/shared';
 | 
			
		||||
import { useHistory, useParams } from 'react-router-dom';
 | 
			
		||||
import { tabItemSchema } from './schema';
 | 
			
		||||
import { PageSchema } from '../../common';
 | 
			
		||||
 | 
			
		||||
export const InternalTabBar: React.FC = (props) => {
 | 
			
		||||
  const fieldSchema = useFieldSchema();
 | 
			
		||||
  const { designable } = useDesignable();
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const options = useContext(SchemaOptionsContext);
 | 
			
		||||
  const { insertBeforeEnd, dn } = useDesignable();
 | 
			
		||||
  const { insertBeforeEnd } = useDesignable();
 | 
			
		||||
  const history = useHistory();
 | 
			
		||||
  const params = useParams<{ name: string }>();
 | 
			
		||||
  const compile = useCompile();
 | 
			
		||||
 | 
			
		||||
  const onAddTab = useCallback((values: any) => {
 | 
			
		||||
    return insertBeforeEnd({
 | 
			
		||||
@ -25,25 +34,11 @@ export const InternalTabBar: React.FC = (props) => {
 | 
			
		||||
      'x-component-props': values,
 | 
			
		||||
      'x-designer': 'MTabBar.Item.Designer',
 | 
			
		||||
      properties: {
 | 
			
		||||
        [uid()]: {
 | 
			
		||||
          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,
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        [uid()]: PageSchema,
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <SortableItem
 | 
			
		||||
      className={cx(
 | 
			
		||||
@ -80,7 +75,7 @@ export const InternalTabBar: React.FC = (props) => {
 | 
			
		||||
                key={`tab_${schema['x-uid']}`}
 | 
			
		||||
                title={
 | 
			
		||||
                  <>
 | 
			
		||||
                    {cp.title}
 | 
			
		||||
                    {compile(cp.title)}
 | 
			
		||||
                    <SchemaComponent schema={schema} name={name} />
 | 
			
		||||
                  </>
 | 
			
		||||
                }
 | 
			
		||||
@ -88,7 +83,7 @@ export const InternalTabBar: React.FC = (props) => {
 | 
			
		||||
              ></TabBar.Item>
 | 
			
		||||
            );
 | 
			
		||||
          })}
 | 
			
		||||
          {designable && Object.keys(fieldSchema.properties).length < 5 ? (
 | 
			
		||||
          {designable && (!fieldSchema.properties || Object.keys(fieldSchema.properties).length < 5) ? (
 | 
			
		||||
            <TabBar.Item
 | 
			
		||||
              className={css`
 | 
			
		||||
                .adm-tab-bar-item-icon {
 | 
			
		||||
 | 
			
		||||
@ -13,7 +13,7 @@ export function lang(key: string) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function generateNTemplate(key: string) {
 | 
			
		||||
  return `{{t('${key}', { ns: '${NAMESPACE}' })}}`;
 | 
			
		||||
  return `{{t('${key}', { ns: '${NAMESPACE}', nsMode: 'fallback' })}}`;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function useTranslation() {
 | 
			
		||||
 | 
			
		||||
@ -26,6 +26,8 @@ const locale = {
 | 
			
		||||
  Settings: '设置',
 | 
			
		||||
  'Delete settings block': '删除设置区块',
 | 
			
		||||
  'Delete menu block': '删除菜单区块',
 | 
			
		||||
  'Mobile client access address': '移动端访问地址',
 | 
			
		||||
  'The full address is': '完整的地址是',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default locale;
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { RouteSwitch, useRoutes } from '@nocobase/client';
 | 
			
		||||
import React, { useEffect, useMemo } from 'react';
 | 
			
		||||
import React, { useMemo } from 'react';
 | 
			
		||||
import { HashRouter } from 'react-router-dom';
 | 
			
		||||
import { InterfaceProvider } from './InterfaceProvider';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,17 @@
 | 
			
		||||
import { RouteSwitchProvider, useRouteSwitchContext } from '@nocobase/client';
 | 
			
		||||
import React, { useEffect } from 'react';
 | 
			
		||||
import MApplication from './Application';
 | 
			
		||||
import { isJSBridge } from '../core/bridge';
 | 
			
		||||
 | 
			
		||||
export const RouterSwitchProvider = (props) => {
 | 
			
		||||
  const { routes, components } = useRouteSwitchContext();
 | 
			
		||||
  // redirect to mobile
 | 
			
		||||
  if (isJSBridge) {
 | 
			
		||||
    const redirectRoute = routes[0];
 | 
			
		||||
    if (redirectRoute.type === 'redirect' && redirectRoute.to === '/admin') {
 | 
			
		||||
      redirectRoute.to = '/mobile';
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return (
 | 
			
		||||
    <RouteSwitchProvider
 | 
			
		||||
      routes={routes}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user