refactor: migrate assistant/page-style/hera-version (#817)

Reviewed-on: daoyoucloud/tachybase#817
This commit is contained in:
sealday 2024-04-23 20:30:12 +08:00
parent 5b8bd6e19c
commit 090db400a4
12 changed files with 251 additions and 152 deletions

View File

@ -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>
</>
);
};

View File

@ -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);
}
}

View File

@ -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}</>;
};

View File

@ -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);
}
}

View File

@ -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>
);
};

View File

@ -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} />;
}

View File

@ -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);
}
}

View File

@ -0,0 +1,6 @@
import { useContext } from 'react';
import { PageStyleContext } from './PageStyle.provider';
export const usePageStyle = () => {
return useContext(PageStyleContext).style;
};

View File

@ -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 };
}

View File

@ -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() {

View File

@ -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}
/>
);
}

View File

@ -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();