feat: improve the user module
This commit is contained in:
parent
ef24f7cfea
commit
5b4dd1800e
@ -1,6 +1,5 @@
|
||||
import {
|
||||
ACLShortcut,
|
||||
Action,
|
||||
AdminLayout,
|
||||
AntdConfigProvider,
|
||||
AntdSchemaComponentProvider,
|
||||
@ -11,12 +10,13 @@ import {
|
||||
DesignableSwitch,
|
||||
DocumentTitleProvider,
|
||||
i18n,
|
||||
Menu,
|
||||
Page,
|
||||
PluginManagerProvider,
|
||||
RouteSchemaComponent,
|
||||
RouteSwitch,
|
||||
RouteSwitchProvider,
|
||||
SchemaComponentProvider,
|
||||
SigninPage,
|
||||
SignupPage,
|
||||
SystemSettingsProvider,
|
||||
SystemSettingsShortcut,
|
||||
useRequest
|
||||
@ -24,7 +24,7 @@ import {
|
||||
import { Spin } from 'antd';
|
||||
import React from 'react';
|
||||
import { I18nextProvider } from 'react-i18next';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { Link, MemoryRouter, NavLink } from 'react-router-dom';
|
||||
import apiClient from './apiClient';
|
||||
|
||||
const providers = [
|
||||
@ -38,10 +38,10 @@ const providers = [
|
||||
PluginManagerProvider,
|
||||
{ components: { ACLShortcut, DesignableSwitch, CollectionManagerShortcut, SystemSettingsShortcut } },
|
||||
],
|
||||
[SchemaComponentProvider, { components: { Page, Menu, Action } }],
|
||||
[SchemaComponentProvider, { components: { Link, NavLink } }],
|
||||
AntdSchemaComponentProvider,
|
||||
[DocumentTitleProvider, { addonAfter: 'NocoBase' }],
|
||||
[RouteSwitchProvider, { components: { AuthLayout, AdminLayout } }],
|
||||
[RouteSwitchProvider, { components: { AuthLayout, AdminLayout, RouteSchemaComponent, SigninPage, SignupPage } }],
|
||||
];
|
||||
|
||||
const App = compose(...providers)(() => {
|
||||
|
@ -145,10 +145,13 @@ export default (apiClient: APIClient) => {
|
||||
routes: [
|
||||
{
|
||||
type: 'route',
|
||||
uiSchemaUid: 'dtf9j0b8p9u',
|
||||
path: '/signin',
|
||||
component: 'RouteSchemaComponent',
|
||||
title: '{{t("Sign in")}}',
|
||||
component: 'SigninPage',
|
||||
},
|
||||
{
|
||||
type: 'route',
|
||||
path: '/signup',
|
||||
component: 'SignupPage',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -1,65 +0,0 @@
|
||||
import { Menu } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import { DatabaseOutlined } from '@ant-design/icons';
|
||||
import { SchemaComponent, useActionVisible, VisibleContext } from '../schema-component';
|
||||
import { ISchema, useForm } from '@formily/react';
|
||||
|
||||
const useCloseAction = () => {
|
||||
const { setVisible } = useActionVisible();
|
||||
const form = useForm();
|
||||
return {
|
||||
async run() {
|
||||
setVisible(false);
|
||||
form.submit((values) => {
|
||||
console.log(values);
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const schema: ISchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
drawer1: {
|
||||
'x-component': 'Action.Drawer',
|
||||
type: 'void',
|
||||
title: 'Drawer Title',
|
||||
properties: {
|
||||
hello1: {
|
||||
'x-content': 'Hello',
|
||||
title: 'T1',
|
||||
},
|
||||
footer1: {
|
||||
'x-component': 'Action.Drawer.Footer',
|
||||
type: 'void',
|
||||
properties: {
|
||||
close1: {
|
||||
title: 'Close',
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
useAction: '{{ useCloseAction }}',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const ProfileAction = () => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
return (
|
||||
<VisibleContext.Provider value={[visible, setVisible]}>
|
||||
<Menu.Item
|
||||
eventKey={'ProfileAction'}
|
||||
onClick={() => {
|
||||
setVisible(true);
|
||||
}}
|
||||
>
|
||||
个人资料
|
||||
</Menu.Item>
|
||||
<SchemaComponent scope={{ useCloseAction }} schema={schema} />
|
||||
</VisibleContext.Provider>
|
||||
);
|
||||
};
|
@ -1,9 +0,0 @@
|
||||
---
|
||||
nav:
|
||||
path: /client
|
||||
group:
|
||||
title: Client
|
||||
path: /client
|
||||
---
|
||||
|
||||
# CurrentUser <Badge>待定</Badge>
|
@ -1,4 +1,4 @@
|
||||
import React, { createContext, useContext, useState } from 'react';
|
||||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
|
||||
interface DocumentTitleContextProps {
|
||||
@ -35,3 +35,10 @@ export const DocumentTitleProvider: React.FC<{ addonBefore?: string; addonAfter?
|
||||
export const useDocumentTitle = () => {
|
||||
return useContext(DocumentTitleContext);
|
||||
};
|
||||
|
||||
export const useCurrentDocumentTitle = (title: string) => {
|
||||
const { setTitle } = useDocumentTitle();
|
||||
useEffect(() => {
|
||||
setTitle(title);
|
||||
}, []);
|
||||
};
|
||||
|
@ -8,7 +8,6 @@ export * from './application';
|
||||
export * from './async-data-provider';
|
||||
export * from './board';
|
||||
export * from './collection-manager';
|
||||
export * from './current-user';
|
||||
export * from './document-title';
|
||||
export * from './i18n';
|
||||
export * from './icon';
|
||||
@ -17,4 +16,5 @@ export * from './record-provider';
|
||||
export * from './route-switch';
|
||||
export * from './schema-component';
|
||||
export * from './system-settings';
|
||||
export * from './user';
|
||||
|
||||
|
@ -34,28 +34,39 @@ interface ToolbarItemProps {
|
||||
pin?: boolean;
|
||||
}
|
||||
|
||||
const splitItems = (items: ToolbarItemProps[]) => {
|
||||
const pinned = [];
|
||||
const unpinned = [];
|
||||
for (const item of items) {
|
||||
if (item.pin) {
|
||||
pinned.push(item);
|
||||
} else {
|
||||
unpinned.push(item);
|
||||
}
|
||||
}
|
||||
return [pinned, unpinned];
|
||||
};
|
||||
|
||||
PluginManager.Toolbar = (props: ToolbarProps) => {
|
||||
const { components } = useContext(PluginManagerContext);
|
||||
const { items = [] } = props;
|
||||
const [pinned, unpinned] = splitItems(items);
|
||||
return (
|
||||
<div style={{ display: 'inline-block' }}>
|
||||
<Menu style={{ width: '100%' }} selectable={false} mode={'horizontal'} theme={'dark'}>
|
||||
{items
|
||||
.filter((item) => item.pin)
|
||||
.map((item, index) => {
|
||||
const Action = get(components, item.component);
|
||||
return (
|
||||
Action && (
|
||||
<ToolbarItemContext.Provider key={index} value={item}>
|
||||
<Action />
|
||||
</ToolbarItemContext.Provider>
|
||||
)
|
||||
);
|
||||
})}
|
||||
<Menu.SubMenu key={'more'} title={<MoreOutlined />}>
|
||||
{items
|
||||
.filter((item) => !item.pin)
|
||||
.map((item, index) => {
|
||||
{pinned.map((item, index) => {
|
||||
const Action = get(components, item.component);
|
||||
return (
|
||||
Action && (
|
||||
<ToolbarItemContext.Provider key={index} value={item}>
|
||||
<Action />
|
||||
</ToolbarItemContext.Provider>
|
||||
)
|
||||
);
|
||||
})}
|
||||
{unpinned.length > 0 && (
|
||||
<Menu.SubMenu popupClassName={'pm-sub-menu'} key={'more'} title={<MoreOutlined />}>
|
||||
{unpinned.map((item, index) => {
|
||||
const Action = get(components, item.component);
|
||||
return (
|
||||
Action && (
|
||||
@ -65,11 +76,12 @@ PluginManager.Toolbar = (props: ToolbarProps) => {
|
||||
)
|
||||
);
|
||||
})}
|
||||
<Menu.Divider key={'divider'}></Menu.Divider>
|
||||
<Menu.Item key={'plugins'} disabled icon={<SettingOutlined />}>
|
||||
管理插件
|
||||
</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
{unpinned.length > 0 && <Menu.Divider key={'divider'}></Menu.Divider>}
|
||||
<Menu.Item key={'plugins'} disabled icon={<SettingOutlined />}>
|
||||
管理插件
|
||||
</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
)}
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
|
@ -7,9 +7,9 @@ import {
|
||||
PluginManager,
|
||||
RemoteSchemaComponent,
|
||||
useDocumentTitle,
|
||||
useRoute,
|
||||
useSystemSettings
|
||||
} from '../../../';
|
||||
import { useRoute } from '../../hooks';
|
||||
|
||||
export function AdminLayout(props: any) {
|
||||
const route = useRoute();
|
||||
@ -64,7 +64,7 @@ export function AdminLayout(props: any) {
|
||||
{ component: 'SystemSettingsShortcut' },
|
||||
]}
|
||||
/>
|
||||
<CurrentUser.Dropdown />
|
||||
<CurrentUser />
|
||||
</div>
|
||||
</Layout.Header>
|
||||
<Layout>
|
||||
|
@ -2,7 +2,13 @@ import React from 'react';
|
||||
|
||||
export function AuthLayout(props: any) {
|
||||
return (
|
||||
<div style={{ maxWidth: 320, margin: '0 auto', paddingTop: '20vh' }}>
|
||||
<div
|
||||
style={{
|
||||
maxWidth: 320,
|
||||
margin: '0 auto',
|
||||
// paddingTop: '20vh'
|
||||
}}
|
||||
>
|
||||
<h1>NocoBase</h1>
|
||||
{props.children}
|
||||
</div>
|
||||
|
@ -1,2 +1,4 @@
|
||||
export * from './admin-layout';
|
||||
export * from './auth-layout';
|
||||
export * from './route-schema-component';
|
||||
|
||||
|
@ -0,0 +1,7 @@
|
||||
import React from 'react';
|
||||
import { RemoteSchemaComponent, useRoute } from '../../../';
|
||||
|
||||
export function RouteSchemaComponent(props: any) {
|
||||
const route = useRoute();
|
||||
return <RemoteSchemaComponent uid={route.uiSchemaUid} />;
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Button, ButtonProps } from 'antd';
|
||||
import { observer, RecursionField, useField, useFieldSchema } from '@formily/react';
|
||||
import { useA } from './hooks';
|
||||
import { VisibleContext } from './context';
|
||||
import { ComposedAction } from './types';
|
||||
import { Button } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import { ActionDrawer } from './Action.Drawer';
|
||||
import { VisibleContext } from './context';
|
||||
import { useA } from './hooks';
|
||||
import { ComposedAction } from './types';
|
||||
|
||||
export const Action: ComposedAction = observer((props) => {
|
||||
const { useAction = useA, onClick, ...others } = props;
|
||||
@ -22,7 +22,7 @@ export const Action: ComposedAction = observer((props) => {
|
||||
run();
|
||||
}}
|
||||
>
|
||||
{schema.title}
|
||||
{field.title}
|
||||
</Button>
|
||||
<RecursionField basePath={field.address} schema={schema} onlyRenderProperties />
|
||||
</VisibleContext.Provider>
|
||||
|
55
packages/client/src/system-settings/demos/demo1.tsx
Normal file
55
packages/client/src/system-settings/demos/demo1.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
import {
|
||||
AntdSchemaComponentProvider,
|
||||
APIClient,
|
||||
APIClientProvider,
|
||||
PluginManager,
|
||||
PluginManagerProvider,
|
||||
SchemaComponentProvider,
|
||||
SystemSettingsProvider,
|
||||
SystemSettingsShortcut,
|
||||
useSystemSettings
|
||||
} from '@nocobase/client';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import React from 'react';
|
||||
|
||||
const apiClient = new APIClient();
|
||||
|
||||
const mock = new MockAdapter(apiClient.axios);
|
||||
|
||||
mock.onGet('/system_settings:get').reply(200, {
|
||||
data: {
|
||||
title: 'NocoBase',
|
||||
},
|
||||
});
|
||||
|
||||
const Demo = () => {
|
||||
const { data } = useSystemSettings();
|
||||
return <div>
|
||||
<h3>以下为测试内容</h3>
|
||||
System title: {data?.data?.title}
|
||||
</div>;
|
||||
};
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
<APIClientProvider apiClient={apiClient}>
|
||||
<SystemSettingsProvider>
|
||||
<SchemaComponentProvider>
|
||||
<AntdSchemaComponentProvider>
|
||||
<PluginManagerProvider components={{ SystemSettingsShortcut }}>
|
||||
<PluginManager.Toolbar
|
||||
items={[
|
||||
{
|
||||
component: 'SystemSettingsShortcut',
|
||||
pin: true,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Demo />
|
||||
</PluginManagerProvider>
|
||||
</AntdSchemaComponentProvider>
|
||||
</SchemaComponentProvider>
|
||||
</SystemSettingsProvider>
|
||||
</APIClientProvider>
|
||||
);
|
||||
};
|
@ -5,11 +5,18 @@ group:
|
||||
path: /client
|
||||
---
|
||||
|
||||
# SystemSettings <Badge>待定</Badge>
|
||||
# SystemSettings
|
||||
|
||||
## SystemSettingsProvider
|
||||
|
||||
## SystemSettingsAction
|
||||
## SystemSettingsShortcut
|
||||
|
||||
## useSystemSettings
|
||||
|
||||
```ts
|
||||
const { data, loading, refresh } = useSystemSettings();
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
<code src="./demos/demo1.tsx"/>
|
||||
|
@ -1,11 +1,10 @@
|
||||
import { Menu } from 'antd';
|
||||
import React from 'react';
|
||||
import { MoreOutlined, DesktopOutlined } from '@ant-design/icons';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { ProfileAction } from './ProfileAction';
|
||||
|
||||
export const CurrentUser = () => null;
|
||||
|
||||
CurrentUser.Dropdown = () => {
|
||||
export const CurrentUser = () => {
|
||||
const history = useHistory();
|
||||
return (
|
||||
<div style={{ display: 'inline-block' }}>
|
||||
<Menu selectable={false} mode={'horizontal'} theme={'dark'}>
|
||||
@ -14,7 +13,13 @@ CurrentUser.Dropdown = () => {
|
||||
<Menu.Item>切换角色</Menu.Item>
|
||||
<Menu.Item>语言设置</Menu.Item>
|
||||
<Menu.Divider />
|
||||
<Menu.Item>注销</Menu.Item>
|
||||
<Menu.Item
|
||||
onClick={() => {
|
||||
history.push('/signin');
|
||||
}}
|
||||
>
|
||||
注销
|
||||
</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
</Menu>
|
||||
</div>
|
105
packages/client/src/user/ProfileAction.tsx
Normal file
105
packages/client/src/user/ProfileAction.tsx
Normal file
@ -0,0 +1,105 @@
|
||||
import { ISchema, useForm } from '@formily/react';
|
||||
import { uid } from '@formily/shared';
|
||||
import { Menu } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import { SchemaComponent, useActionVisible, useRequest, VisibleContext } from '../';
|
||||
|
||||
const useCloseAction = () => {
|
||||
const { setVisible } = useActionVisible();
|
||||
const form = useForm();
|
||||
return {
|
||||
async run() {
|
||||
setVisible(false);
|
||||
form.submit((values) => {
|
||||
console.log(values);
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const useCurrentUserValues = (props, options) => {
|
||||
return useRequest(
|
||||
() =>
|
||||
Promise.resolve({
|
||||
data: {},
|
||||
}),
|
||||
{
|
||||
...options,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const useSaveCurrentUserValues = () => {
|
||||
const { setVisible } = useActionVisible();
|
||||
const form = useForm();
|
||||
return {
|
||||
async run() {
|
||||
form.submit((values) => {
|
||||
setVisible(false);
|
||||
console.log(values);
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const schema: ISchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
[uid()]: {
|
||||
'x-decorator': 'Form',
|
||||
'x-decorator-props': {
|
||||
useValues: '{{ useCurrentUserValues }}',
|
||||
},
|
||||
'x-component': 'Action.Drawer',
|
||||
type: 'void',
|
||||
title: '个人资料',
|
||||
properties: {
|
||||
nickname: {
|
||||
type: 'string',
|
||||
title: "{{t('Nickname')}}",
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Input',
|
||||
required: true,
|
||||
},
|
||||
footer: {
|
||||
'x-component': 'Action.Drawer.Footer',
|
||||
type: 'void',
|
||||
properties: {
|
||||
cancel: {
|
||||
title: 'Cancel',
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
useAction: '{{ useCloseAction }}',
|
||||
},
|
||||
},
|
||||
submit: {
|
||||
title: 'Submit',
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
useAction: '{{ useSaveCurrentUserValues }}',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const ProfileAction = () => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
return (
|
||||
<VisibleContext.Provider value={[visible, setVisible]}>
|
||||
<Menu.Item
|
||||
eventKey={'ProfileAction'}
|
||||
onClick={() => {
|
||||
setVisible(true);
|
||||
}}
|
||||
>
|
||||
个人资料
|
||||
</Menu.Item>
|
||||
<SchemaComponent scope={{ useCurrentUserValues, useCloseAction, useSaveCurrentUserValues }} schema={schema} />
|
||||
</VisibleContext.Provider>
|
||||
);
|
||||
};
|
79
packages/client/src/user/SigninPage.tsx
Normal file
79
packages/client/src/user/SigninPage.tsx
Normal file
@ -0,0 +1,79 @@
|
||||
import { ISchema, useForm } from '@formily/react';
|
||||
import { uid } from '@formily/shared';
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { SchemaComponent, useCurrentDocumentTitle } from '..';
|
||||
|
||||
const schema: ISchema = {
|
||||
type: 'object',
|
||||
name: uid(),
|
||||
'x-component': 'Form',
|
||||
properties: {
|
||||
email: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
'x-component': 'Input',
|
||||
'x-validator': 'email',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component-props': { placeholder: '{{t("Email")}}', style: {} },
|
||||
},
|
||||
password: {
|
||||
type: 'string',
|
||||
'x-component': 'Password',
|
||||
required: true,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component-props': { placeholder: '{{t("Password")}}', style: {} },
|
||||
},
|
||||
actions: {
|
||||
type: 'void',
|
||||
'x-component': 'div',
|
||||
properties: {
|
||||
submit: {
|
||||
title: '{{t("Sign in")}}',
|
||||
type: 'void',
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
block: true,
|
||||
type: 'primary',
|
||||
useAction: '{{ useSignin }}',
|
||||
style: { width: '100%' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
link: {
|
||||
type: 'void',
|
||||
'x-component': 'div',
|
||||
properties: {
|
||||
link: {
|
||||
title: '{{t("Create an account")}}',
|
||||
type: 'void',
|
||||
'x-component': 'Link',
|
||||
'x-content': '{{t("Create an account")}}',
|
||||
'x-component-props': { to: '/signup' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const useSignin = () => {
|
||||
const history = useHistory();
|
||||
const form = useForm();
|
||||
return {
|
||||
async run() {
|
||||
await form.submit();
|
||||
console.log(form.values);
|
||||
history.push('/admin');
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const SigninPage = () => {
|
||||
useCurrentDocumentTitle('Signin');
|
||||
return (
|
||||
<div>
|
||||
<SchemaComponent scope={{ useSignin }} schema={schema} />
|
||||
</div>
|
||||
);
|
||||
};
|
101
packages/client/src/user/SignupPage.tsx
Normal file
101
packages/client/src/user/SignupPage.tsx
Normal file
@ -0,0 +1,101 @@
|
||||
import { ISchema, useForm } from '@formily/react';
|
||||
import { uid } from '@formily/shared';
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { SchemaComponent, useCurrentDocumentTitle } from '..';
|
||||
|
||||
const schema: ISchema = {
|
||||
type: 'object',
|
||||
name: uid(),
|
||||
'x-component': 'Form',
|
||||
properties: {
|
||||
email: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
'x-component': 'Input',
|
||||
'x-validator': 'email',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component-props': { placeholder: '{{t("Email")}}', style: {} },
|
||||
},
|
||||
password: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
'x-component': 'Password',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component-props': { placeholder: '{{t("Password")}}', checkStrength: true, style: {} },
|
||||
'x-reactions': [
|
||||
{
|
||||
dependencies: ['.confirm_password'],
|
||||
fulfill: {
|
||||
state: {
|
||||
selfErrors: '{{$deps[0] && $self.value && $self.value !== $deps[0] ? t("Password mismatch") : ""}}',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
confirm_password: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
'x-component': 'Password',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component-props': { placeholder: '{{t("Confirm password")}}', checkStrength: true, style: {} },
|
||||
'x-reactions': [
|
||||
{
|
||||
dependencies: ['.password'],
|
||||
fulfill: {
|
||||
state: {
|
||||
selfErrors: '{{$deps[0] && $self.value && $self.value !== $deps[0] ? t("Password mismatch") : ""}}',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
actions: {
|
||||
type: 'void',
|
||||
'x-component': 'div',
|
||||
properties: {
|
||||
submit: {
|
||||
title: '{{t("Sign up")}}',
|
||||
type: 'void',
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
block: true,
|
||||
type: 'primary',
|
||||
useAction: '{{ useSignup }}',
|
||||
style: { width: '100%' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
link: {
|
||||
type: 'void',
|
||||
'x-component': 'div',
|
||||
properties: {
|
||||
link: {
|
||||
type: 'void',
|
||||
'x-component': 'Link',
|
||||
'x-component-props': { to: '/signin' },
|
||||
'x-content': '{{t("Log in with an existing account")}}',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const useSignup = () => {
|
||||
const history = useHistory();
|
||||
const form = useForm();
|
||||
return {
|
||||
async run() {
|
||||
await form.submit();
|
||||
console.log(form.values);
|
||||
// history.push('/signin');
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const SignupPage = () => {
|
||||
useCurrentDocumentTitle('Signup');
|
||||
return <SchemaComponent schema={schema} scope={{ useSignup }} />;
|
||||
};
|
23
packages/client/src/user/index.md
Normal file
23
packages/client/src/user/index.md
Normal file
@ -0,0 +1,23 @@
|
||||
---
|
||||
nav:
|
||||
path: /client
|
||||
group:
|
||||
title: Client
|
||||
path: /client
|
||||
---
|
||||
|
||||
# User
|
||||
|
||||
`plugin-users` 插件的前端模块
|
||||
|
||||
## CurrentUser
|
||||
|
||||
当前用户
|
||||
|
||||
## SigninPage
|
||||
|
||||
登录页
|
||||
|
||||
## SignupPage
|
||||
|
||||
注册页
|
4
packages/client/src/user/index.ts
Normal file
4
packages/client/src/user/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export * from './CurrentUser';
|
||||
export * from './SigninPage';
|
||||
export * from './SignupPage';
|
||||
|
Loading…
Reference in New Issue
Block a user