refactor: migrate assistant/page-style/hera-version (#817)
Reviewed-on: daoyoucloud/tachybase#817
This commit is contained in:
parent
5b8bd6e19c
commit
090db400a4
@ -0,0 +1,17 @@
|
|||||||
|
import { CalculatorOutlined, CommentOutlined, HighlightOutlined, ToolOutlined } from '@ant-design/icons';
|
||||||
|
import { useDesignable } from '@nocobase/client';
|
||||||
|
import { FloatButton } from 'antd';
|
||||||
|
import React from 'react';
|
||||||
|
export const AssistantProvider = ({ children }) => {
|
||||||
|
const { designable, setDesignable } = useDesignable();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{children}
|
||||||
|
<FloatButton.Group trigger="hover" type="primary" style={{ right: 24, zIndex: 1250 }} icon={<ToolOutlined />}>
|
||||||
|
<FloatButton icon={<HighlightOutlined />} onClick={() => setDesignable(!designable)} />
|
||||||
|
<FloatButton icon={<CalculatorOutlined />} />
|
||||||
|
<FloatButton icon={<CommentOutlined />} />
|
||||||
|
</FloatButton.Group>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,8 @@
|
|||||||
|
import { Plugin } from '@nocobase/client';
|
||||||
|
import { AssistantProvider } from './Assistant.provider';
|
||||||
|
|
||||||
|
export class PluginAssistant extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.use(AssistantProvider);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { useCurrentUserSettingsMenu } from '@nocobase/client';
|
||||||
|
import { usePluginVersion } from '../../hooks/usePluginVersion';
|
||||||
|
import { useTabSettings } from '../page-style/useTabSettings';
|
||||||
|
|
||||||
|
const useHeraVersion = () => {
|
||||||
|
const version = usePluginVersion();
|
||||||
|
return {
|
||||||
|
key: 'hera-version',
|
||||||
|
eventKey: 'hera-version',
|
||||||
|
label: <span>赫拉系统 - {version}</span>,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const HeraVersionProvider = ({ children }) => {
|
||||||
|
const { addMenuItem } = useCurrentUserSettingsMenu();
|
||||||
|
const heraVersion = useHeraVersion();
|
||||||
|
const tabItem = useTabSettings();
|
||||||
|
useEffect(() => {
|
||||||
|
addMenuItem(heraVersion, { before: 'divider_1' });
|
||||||
|
}, [addMenuItem, tabItem, heraVersion]);
|
||||||
|
|
||||||
|
return <>{children}</>;
|
||||||
|
};
|
@ -0,0 +1,8 @@
|
|||||||
|
import { Plugin } from '@nocobase/client';
|
||||||
|
import { HeraVersionProvider } from './HeraVersion.provider';
|
||||||
|
|
||||||
|
export class PluginHeraVersion extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.use(HeraVersionProvider);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
import { useCurrentUserContext, useCurrentUserSettingsMenu } from '@nocobase/client';
|
||||||
|
import React, { createContext, useEffect, useState } from 'react';
|
||||||
|
import { useTabSettings } from './useTabSettings';
|
||||||
|
import { TabsProps } from 'antd';
|
||||||
|
|
||||||
|
export interface PageStyleContextValue {
|
||||||
|
style: string;
|
||||||
|
items: TabsProps['items'];
|
||||||
|
setItems: React.Dispatch<React.SetStateAction<TabsProps['items']>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PageStyleContext = createContext<Partial<PageStyleContextValue>>({
|
||||||
|
style: 'classical',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const PageStyleProvider = ({ children }) => {
|
||||||
|
const currentUser = useCurrentUserContext();
|
||||||
|
const tabItem = useTabSettings();
|
||||||
|
const [items, setItems] = useState<TabsProps['items']>([]);
|
||||||
|
const { addMenuItem } = useCurrentUserSettingsMenu();
|
||||||
|
useEffect(() => {
|
||||||
|
addMenuItem(tabItem, { before: 'divider_3' });
|
||||||
|
}, [addMenuItem, tabItem]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageStyleContext.Provider
|
||||||
|
value={{ style: currentUser.data.data.systemSettings?.pageStyle || 'classical', items, setItems }}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</PageStyleContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,62 @@
|
|||||||
|
import { RemoteSchemaComponent, css, useDocumentTitle } from '@nocobase/client';
|
||||||
|
import { Tabs } from 'antd';
|
||||||
|
import React, { useContext, useEffect } from 'react';
|
||||||
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
|
import { PageStyleContext } from './PageStyle.provider';
|
||||||
|
|
||||||
|
export const PageTab = () => {
|
||||||
|
const params = useParams<{ name?: string }>();
|
||||||
|
const { title, setTitle } = useDocumentTitle();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { items, setItems } = useContext(PageStyleContext);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (params.name && title) {
|
||||||
|
const targetItem = items.find((value) => value.key === params.name);
|
||||||
|
if (!targetItem) {
|
||||||
|
// 现有tab页数组里,不存在之前浏览的tab页面,添加新的tab页进数组
|
||||||
|
setItems([
|
||||||
|
...items,
|
||||||
|
{
|
||||||
|
key: params.name,
|
||||||
|
label: title,
|
||||||
|
children: <MyRouteSchemaComponent name={params.name} />,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
// 如果存在之前浏览的tab页面,只用更新页面标题
|
||||||
|
setTitle(targetItem.label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [params.name, title]);
|
||||||
|
|
||||||
|
const onEdit = (targetKey: React.MouseEvent | React.KeyboardEvent | string, action: 'add' | 'remove') => {
|
||||||
|
if (action === 'remove') {
|
||||||
|
setItems((items) => {
|
||||||
|
return items.filter((item) => item.key !== targetKey);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Tabs
|
||||||
|
className={css`
|
||||||
|
margin: 0;
|
||||||
|
.ant-tabs-nav {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
type="editable-card"
|
||||||
|
items={items}
|
||||||
|
onEdit={onEdit}
|
||||||
|
hideAdd
|
||||||
|
onChange={(key) => {
|
||||||
|
navigate(`/admin/${key}`);
|
||||||
|
}}
|
||||||
|
activeKey={params.name}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export function MyRouteSchemaComponent({ name }: { name: string }) {
|
||||||
|
return <RemoteSchemaComponent onlyRenderProperties uid={name} />;
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
import { Plugin } from '@nocobase/client';
|
||||||
|
import { PageStyleProvider } from './PageStyle.provider';
|
||||||
|
|
||||||
|
export class PluginPageStyle extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.use(PageStyleProvider);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
import { useContext } from 'react';
|
||||||
|
import { PageStyleContext } from './PageStyle.provider';
|
||||||
|
|
||||||
|
export const usePageStyle = () => {
|
||||||
|
return useContext(PageStyleContext).style;
|
||||||
|
};
|
@ -0,0 +1,73 @@
|
|||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import { SelectWithTitle, useAPIClient, useCurrentUserContext } from '@nocobase/client';
|
||||||
|
import { error } from '@nocobase/utils/client';
|
||||||
|
import { useTranslation } from '../../locale';
|
||||||
|
|
||||||
|
export const useTabSettings = () => {
|
||||||
|
return {
|
||||||
|
key: 'tab',
|
||||||
|
eventKey: 'tab',
|
||||||
|
label: <Label />,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function Label() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { updateUserPageStyle } = useUpdatePageStyleSettings();
|
||||||
|
const currentUser = useCurrentUserContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SelectWithTitle
|
||||||
|
title={t('Page style')}
|
||||||
|
defaultValue={currentUser.data.data.systemSettings?.pageStyle || 'classical'}
|
||||||
|
options={[
|
||||||
|
{
|
||||||
|
label: t('classical'),
|
||||||
|
value: 'classical',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('tabs'),
|
||||||
|
value: 'tab',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
onChange={updateUserPageStyle}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useUpdatePageStyleSettings() {
|
||||||
|
const api = useAPIClient();
|
||||||
|
const currentUser = useCurrentUserContext();
|
||||||
|
|
||||||
|
const updateUserPageStyle = useCallback(
|
||||||
|
async (pageStyle: string | null) => {
|
||||||
|
if (pageStyle === currentUser.data.data.systemSettings?.pageStyle) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await api.resource('users').updateProfile({
|
||||||
|
values: {
|
||||||
|
systemSettings: {
|
||||||
|
...(currentUser.data.data.systemSettings || {}),
|
||||||
|
pageStyle,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
currentUser.mutate({
|
||||||
|
data: {
|
||||||
|
...currentUser.data.data,
|
||||||
|
systemSettings: {
|
||||||
|
...(currentUser.data.data.systemSettings || {}),
|
||||||
|
pageStyle,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
error(err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[api, currentUser],
|
||||||
|
);
|
||||||
|
|
||||||
|
return { updateUserPageStyle };
|
||||||
|
}
|
@ -76,6 +76,9 @@ import {
|
|||||||
import { SchemaSettingsSubmitDataType } from './schema-settings/SchemaSettingsSubmitDataType';
|
import { SchemaSettingsSubmitDataType } from './schema-settings/SchemaSettingsSubmitDataType';
|
||||||
import { EmbedPlugin } from './features/embed';
|
import { EmbedPlugin } from './features/embed';
|
||||||
import { DepartmentsPlugin } from './features/departments';
|
import { DepartmentsPlugin } from './features/departments';
|
||||||
|
import { PluginPageStyle } from './features/page-style';
|
||||||
|
import { PluginHeraVersion } from './features/hera-version';
|
||||||
|
import { PluginAssistant } from './features/assistant';
|
||||||
export { usePDFViewerRef } from './schema-initializer';
|
export { usePDFViewerRef } from './schema-initializer';
|
||||||
export * from './components/custom-components/custom-components';
|
export * from './components/custom-components/custom-components';
|
||||||
|
|
||||||
@ -86,6 +89,9 @@ export class PluginCoreClient extends Plugin {
|
|||||||
await this.app.pm.add(GroupBlockPlugin);
|
await this.app.pm.add(GroupBlockPlugin);
|
||||||
await this.app.pm.add(EmbedPlugin);
|
await this.app.pm.add(EmbedPlugin);
|
||||||
await this.app.pm.add(DepartmentsPlugin);
|
await this.app.pm.add(DepartmentsPlugin);
|
||||||
|
await this.app.pm.add(PluginPageStyle);
|
||||||
|
await this.app.pm.add(PluginHeraVersion);
|
||||||
|
await this.app.pm.add(PluginAssistant);
|
||||||
}
|
}
|
||||||
|
|
||||||
async registerSettings() {
|
async registerSettings() {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { useSessionStorageState } from 'ahooks';
|
import { useSessionStorageState } from 'ahooks';
|
||||||
import { App, Layout, Spin, FloatButton } from 'antd';
|
import { App, Layout } from 'antd';
|
||||||
import { ToolOutlined, CommentOutlined, CalculatorOutlined, HighlightOutlined } from '@ant-design/icons';
|
|
||||||
import React, { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
import React, { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { Link, Outlet, useMatch, useNavigate, useParams } from 'react-router-dom';
|
import { Link, Outlet, useMatch, useNavigate, useParams } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
@ -16,27 +15,13 @@ import {
|
|||||||
useRequest,
|
useRequest,
|
||||||
useSystemSettings,
|
useSystemSettings,
|
||||||
useToken,
|
useToken,
|
||||||
useApp,
|
|
||||||
AdminProvider,
|
AdminProvider,
|
||||||
RemoteSchemaComponent,
|
|
||||||
useCurrentUserSettingsMenu,
|
|
||||||
SelectWithTitle,
|
|
||||||
useDesignable,
|
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import { Tabs } from 'antd';
|
|
||||||
import type { TabsProps } from 'antd';
|
|
||||||
import { usePluginVersion } from '../hooks/usePluginVersion';
|
|
||||||
import { OnlineUserDropdown } from '../components/system/OnlineUserProvider';
|
import { OnlineUserDropdown } from '../components/system/OnlineUserProvider';
|
||||||
import { MobileLink } from '../components/system/MobileLink';
|
import { MobileLink } from '../components/system/MobileLink';
|
||||||
import { Notifications } from '../components/system/Notifications';
|
import { Notifications } from '../components/system/Notifications';
|
||||||
import { useTranslation } from '../locale';
|
import { usePageStyle } from '../features/page-style/usePageStyle';
|
||||||
|
import { PageTab } from '../features/page-style/PageTab';
|
||||||
export const useAppSpin = () => {
|
|
||||||
const app = useApp();
|
|
||||||
return {
|
|
||||||
render: () => (app ? app?.renderComponent?.('AppSpin') : React.createElement(Spin)),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const filterByACL = (schema, options) => {
|
const filterByACL = (schema, options) => {
|
||||||
const { allowAll, allowMenuItemIds = [] } = options;
|
const { allowAll, allowMenuItemIds = [] } = options;
|
||||||
@ -90,7 +75,6 @@ const MenuEditor = (props) => {
|
|||||||
setCurrent(schema);
|
setCurrent(schema);
|
||||||
navigate(`/admin/${schema['x-uid']}`);
|
navigate(`/admin/${schema['x-uid']}`);
|
||||||
};
|
};
|
||||||
const { render } = useAppSpin();
|
|
||||||
const adminSchemaUid = useAdminSchemaUid();
|
const adminSchemaUid = useAdminSchemaUid();
|
||||||
const { data, loading } = useRequest<{
|
const { data, loading } = useRequest<{
|
||||||
data: any;
|
data: any;
|
||||||
@ -204,50 +188,13 @@ const MenuEditor = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export function MyRouteSchemaComponent({ name }: { name: string }) {
|
export const InternalAdminLayout = () => {
|
||||||
return <RemoteSchemaComponent onlyRenderProperties uid={name} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const InternalAdminLayout = (props: any) => {
|
|
||||||
const app = useApp();
|
|
||||||
const sideMenuRef = useRef<HTMLDivElement>();
|
const sideMenuRef = useRef<HTMLDivElement>();
|
||||||
const result = useSystemSettings();
|
const result = useSystemSettings();
|
||||||
const params = useParams<{ name?: string }>();
|
const params = useParams<{ name?: string }>();
|
||||||
const { token } = useToken();
|
const { token } = useToken();
|
||||||
const { render } = useAppSpin();
|
|
||||||
const { title, setTitle } = useDocumentTitle();
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const [items, setItems] = useState<TabsProps['items']>([]);
|
|
||||||
const pageStyle = usePageStyle();
|
const pageStyle = usePageStyle();
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (params.name && title && pageStyle === 'tab') {
|
|
||||||
const targetItem = items.find((value) => value.key === params.name);
|
|
||||||
if (!targetItem) {
|
|
||||||
// 现有tab页数组里,不存在之前浏览的tab页面,添加新的tab页进数组
|
|
||||||
setItems([
|
|
||||||
...items,
|
|
||||||
{
|
|
||||||
key: params.name,
|
|
||||||
label: title,
|
|
||||||
children: <MyRouteSchemaComponent name={params.name} />,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
// 如果存在之前浏览的tab页面,只用更新页面标题
|
|
||||||
setTitle(targetItem.label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [params.name, title]);
|
|
||||||
|
|
||||||
const onEdit = (targetKey: React.MouseEvent | React.KeyboardEvent | string, action: 'add' | 'remove') => {
|
|
||||||
if (action === 'remove') {
|
|
||||||
setItems((items) => {
|
|
||||||
return items.filter((item) => item.key !== targetKey);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<Layout.Header
|
<Layout.Header
|
||||||
@ -404,108 +351,17 @@ export const InternalAdminLayout = (props: any) => {
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
`}
|
`}
|
||||||
></header>
|
></header>
|
||||||
<>
|
<>{params.name && pageStyle === 'tab' ? <PageTab /> : <Outlet />}</>
|
||||||
{params.name && pageStyle === 'tab' ? (
|
|
||||||
<Tabs
|
|
||||||
className={css`
|
|
||||||
margin: 0;
|
|
||||||
.ant-tabs-nav {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
type="editable-card"
|
|
||||||
items={items}
|
|
||||||
onEdit={onEdit}
|
|
||||||
hideAdd
|
|
||||||
onChange={(key) => {
|
|
||||||
navigate(`/admin/${key}`);
|
|
||||||
}}
|
|
||||||
activeKey={params.name}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<Outlet />
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
</Layout.Content>
|
</Layout.Content>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useTabSettings = (props) => {
|
|
||||||
return {
|
|
||||||
key: 'tab',
|
|
||||||
eventKey: 'tab',
|
|
||||||
label: <Label {...props} />,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const useHeraVersion = () => {
|
|
||||||
const version = usePluginVersion();
|
|
||||||
return {
|
|
||||||
key: 'hera-version',
|
|
||||||
eventKey: 'hera-version',
|
|
||||||
label: <span>赫拉系统 - {version}</span>,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const usePageStyle = () => {
|
|
||||||
return useContext(PageStyleContext).style;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const AdminLayout = (props) => {
|
export const AdminLayout = (props) => {
|
||||||
const { addMenuItem } = useCurrentUserSettingsMenu();
|
|
||||||
const [style, setStyle] = useState('classical');
|
|
||||||
const tabItem = useTabSettings({ style, setStyle });
|
|
||||||
const heraVersion = useHeraVersion();
|
|
||||||
const { designable, setDesignable } = useDesignable();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
addMenuItem(tabItem, { before: 'divider_3' });
|
|
||||||
}, [addMenuItem, tabItem]);
|
|
||||||
useEffect(() => {
|
|
||||||
addMenuItem(heraVersion, { before: 'divider_1' });
|
|
||||||
}, [addMenuItem, tabItem]);
|
|
||||||
|
|
||||||
const AdminComponent = (
|
const AdminComponent = (
|
||||||
<AdminProvider>
|
<AdminProvider>
|
||||||
<PageStyleContext.Provider value={{ style }}>
|
|
||||||
<InternalAdminLayout {...props} />
|
<InternalAdminLayout {...props} />
|
||||||
</PageStyleContext.Provider>
|
|
||||||
<FloatButton.Group trigger="hover" type="primary" style={{ right: 24, zIndex: 1250 }} icon={<ToolOutlined />}>
|
|
||||||
<FloatButton icon={<HighlightOutlined />} onClick={() => setDesignable(!designable)} />
|
|
||||||
<FloatButton icon={<CalculatorOutlined />} />
|
|
||||||
<FloatButton icon={<CommentOutlined />} />
|
|
||||||
</FloatButton.Group>
|
|
||||||
</AdminProvider>
|
</AdminProvider>
|
||||||
);
|
);
|
||||||
return AdminComponent;
|
return AdminComponent;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface PageStyleContextValue {
|
|
||||||
style: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const PageStyleContext = createContext<PageStyleContextValue>({
|
|
||||||
style: 'classical',
|
|
||||||
});
|
|
||||||
|
|
||||||
function Label({ style, setStyle }) {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
return (
|
|
||||||
<SelectWithTitle
|
|
||||||
title={t('Page style')}
|
|
||||||
defaultValue={style}
|
|
||||||
options={[
|
|
||||||
{
|
|
||||||
label: t('classical'),
|
|
||||||
value: 'classical',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('tabs'),
|
|
||||||
value: 'tab',
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
onChange={setStyle}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useStyles } from './style';
|
import { useStyles } from './style';
|
||||||
import { Carousel, Image } from 'antd';
|
import { Carousel, Image } from 'antd';
|
||||||
import { useRequest } from '@nocobase/client';
|
import { useAppSpin, useRequest } from '@nocobase/client';
|
||||||
import { useAppSpin } from './AdminLayout';
|
|
||||||
|
|
||||||
export const HomePage: React.FC<{}> = () => {
|
export const HomePage: React.FC<{}> = () => {
|
||||||
const { styles } = useStyles();
|
const { styles } = useStyles();
|
||||||
|
Loading…
Reference in New Issue
Block a user