feat(client): improve the form component and add more examples

This commit is contained in:
chenos 2022-01-28 15:23:52 +08:00
parent dfa28983d7
commit 5acac725f9
8 changed files with 346 additions and 21 deletions

View File

@ -1,17 +1,18 @@
import React, { useContext, useMemo } from 'react';
import { toJS } from '@formily/reactive';
import { createForm, FormPath } from '@formily/core'; import { createForm, FormPath } from '@formily/core';
import { import {
FieldContext, FieldContext,
FormContext, FormContext,
FormProvider,
observer, observer,
SchemaContext, SchemaContext,
SchemaOptionsContext, SchemaOptionsContext,
useField, useField,
useFieldSchema, useFieldSchema
} from '@formily/react'; } from '@formily/react';
import { toJS } from '@formily/reactive';
import { Spin } from 'antd';
import React, { useContext, useMemo } from 'react';
import { SchemaComponent, useAttach } from '../..'; import { SchemaComponent, useAttach } from '../..';
import { useRequest } from '../../../api-client';
type ComposedForm = React.FC<any> & { type ComposedForm = React.FC<any> & {
__NOCOBASE_FORM?: boolean; __NOCOBASE_FORM?: boolean;
@ -42,17 +43,47 @@ const FormDecorator: React.FC<any> = (props) => {
); );
}; };
export const Form: ComposedForm = observer((props) => { const FormComponent: React.FC<any> = (props) => {
const form = useMemo(() => createForm(), []); const { form } = props;
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const decorator = useFormDecorator(); return (
return decorator ? ( <FieldContext.Provider value={undefined}>
<FormDecorator form={form} /> <FormContext.Provider value={form}>
) : ( <SchemaComponent schema={fieldSchema} />
<FormContext.Provider value={form}> </FormContext.Provider>
<SchemaComponent schema={fieldSchema} /> </FieldContext.Provider>
</FormContext.Provider>
); );
};
const useRequestProps = (props: any) => {
const { request, initialValue } = props;
if (request) {
return request;
}
return () => {
return Promise.resolve({
data: initialValue,
});
};
};
const useDefaultValues = (props: any = {}, opts: any = {}) => {
return useRequest(useRequestProps(props), opts);
};
export const Form: ComposedForm = observer((props) => {
const { useValues = useDefaultValues } = props;
const decorator = useFormDecorator();
const fieldSchema = useFieldSchema();
const form = useMemo(() => createForm(), []);
const { loading } = useValues(props, {
uid: fieldSchema['x-uid'],
async onSuccess(data) {
await form.reset();
form.setValues(data?.data);
},
});
return <Spin spinning={loading}>{decorator ? <FormDecorator form={form} /> : <FormComponent form={form} />}</Spin>;
}); });
Form.__NOCOBASE_FORM = true; Form.__NOCOBASE_FORM = true;

View File

@ -0,0 +1,21 @@
import { uid } from '@formily/shared';
import { APIClient } from '@nocobase/client';
import MockAdapter from 'axios-mock-adapter';
export const apiClient = new APIClient();
const mock = new MockAdapter(apiClient.axios);
const sleep = (value: number) => new Promise((resolve) => setTimeout(resolve, value));
mock.onGet('/posts:get').reply(async (config) => {
await sleep(1000);
return [
200,
{
data: {
field1: uid(),
},
},
];
});

View File

@ -1,7 +1,7 @@
import React from 'react';
import { observer, ISchema } from '@formily/react';
import { SchemaComponent, SchemaComponentProvider, Form, Action, useCloseAction } from '@nocobase/client';
import { FormItem, Input } from '@formily/antd'; import { FormItem, Input } from '@formily/antd';
import { ISchema, observer, useForm } from '@formily/react';
import { Action, Form, SchemaComponent, SchemaComponentProvider, useCloseAction } from '@nocobase/client';
import React from 'react';
export default observer(() => { export default observer(() => {
const schema: ISchema = { const schema: ISchema = {
@ -26,6 +26,9 @@ export default observer(() => {
'x-decorator': 'FormItem', 'x-decorator': 'FormItem',
title: 'T1', title: 'T1',
}, },
out: {
'x-component': 'Output',
},
f1: { f1: {
type: 'void', type: 'void',
'x-component': 'Action.Drawer.Footer', 'x-component': 'Action.Drawer.Footer',
@ -45,8 +48,14 @@ export default observer(() => {
}, },
}, },
}; };
const Output = observer(() => {
const form = useForm();
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
});
return ( return (
<SchemaComponentProvider scope={{ useCloseAction }} components={{ Form, Action, Input, FormItem }}> <SchemaComponentProvider scope={{ useCloseAction }} components={{ Output, Form, Action, Input, FormItem }}>
<SchemaComponent schema={schema} /> <SchemaComponent schema={schema} />
</SchemaComponentProvider> </SchemaComponentProvider>
); );

View File

@ -1,7 +1,8 @@
import React from 'react';
import { observer, ISchema, useForm } from '@formily/react';
import { SchemaComponent, SchemaComponentProvider, Form, Action } from '@nocobase/client';
import { FormItem, Input } from '@formily/antd'; import { FormItem, Input } from '@formily/antd';
import { ISchema, observer, useForm } from '@formily/react';
import { Action, Form, SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
import { Card } from 'antd';
import React from 'react';
export default observer(() => { export default observer(() => {
const schema: ISchema = { const schema: ISchema = {
@ -9,6 +10,10 @@ export default observer(() => {
properties: { properties: {
form1: { form1: {
type: 'void', type: 'void',
'x-decorator': 'Card',
"x-decorator-props": {
title: 'Form Title',
},
'x-component': 'Form', 'x-component': 'Form',
properties: { properties: {
field1: { field1: {
@ -16,6 +21,9 @@ export default observer(() => {
'x-decorator': 'FormItem', 'x-decorator': 'FormItem',
title: 'T1', title: 'T1',
}, },
out: {
'x-component': 'Output',
},
action1: { action1: {
// type: 'void', // type: 'void',
'x-component': 'Action', 'x-component': 'Action',
@ -29,6 +37,11 @@ export default observer(() => {
}, },
}; };
const Output = observer(() => {
const form = useForm();
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
});
const useSubmit = () => { const useSubmit = () => {
const form = useForm(); const form = useForm();
return { return {
@ -39,7 +52,7 @@ export default observer(() => {
}; };
return ( return (
<SchemaComponentProvider scope={{ useSubmit }} components={{ Action, Form, Input, FormItem }}> <SchemaComponentProvider scope={{ useSubmit }} components={{ Card, Output, Action, Form, Input, FormItem }}>
<SchemaComponent schema={schema} /> <SchemaComponent schema={schema} />
</SchemaComponentProvider> </SchemaComponentProvider>
); );

View File

@ -0,0 +1,60 @@
import { FormItem, Input } from '@formily/antd';
import { ISchema, observer, useForm } from '@formily/react';
import { Action, Form, SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
import { Card } from 'antd';
import React from 'react';
export default observer(() => {
const schema: ISchema = {
type: 'object',
properties: {
form1: {
type: 'void',
'x-component': 'Form',
'x-component-props': {
initialValue: {
field1: 'aaa',
},
},
properties: {
field1: {
'x-component': 'Input',
'x-decorator': 'FormItem',
title: 'T1',
},
out: {
'x-component': 'Output',
},
action1: {
// type: 'void',
'x-component': 'Action',
title: 'Submit',
'x-component-props': {
useAction: '{{ useSubmit }}',
},
},
},
},
},
};
const Output = observer(() => {
const form = useForm();
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
});
const useSubmit = () => {
const form = useForm();
return {
async run() {
console.log(form.values);
},
};
};
return (
<SchemaComponentProvider scope={{ useSubmit }} components={{ Card, Output, Action, Form, Input, FormItem }}>
<SchemaComponent schema={schema} />
</SchemaComponentProvider>
);
});

View File

@ -0,0 +1,67 @@
import { FormItem, Input } from '@formily/antd';
import { ISchema, observer, useForm } from '@formily/react';
import { Action, Form, SchemaComponent, SchemaComponentProvider, useCloseAction } from '@nocobase/client';
import React from 'react';
export default observer(() => {
const schema: ISchema = {
type: 'object',
properties: {
a1: {
type: 'void',
'x-component': 'Action',
'x-component-props': {
type: 'primary',
},
title: 'Open',
properties: {
d1: {
type: 'void',
'x-component': 'Action.Drawer',
'x-decorator': 'Form',
'x-decorator-props': {
initialValue: {
field1: 'aaa',
},
},
title: 'Drawer Title',
properties: {
field1: {
title: 'T1',
'x-component': 'Input',
'x-decorator': 'FormItem',
},
out: {
'x-component': 'Output',
},
f1: {
type: 'void',
'x-component': 'Action.Drawer.Footer',
properties: {
a1: {
'x-component': 'Action',
title: 'Close',
'x-component-props': {
useAction: '{{ useCloseAction }}',
},
},
},
},
},
},
},
},
},
};
const Output = observer(() => {
const form = useForm();
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
});
return (
<SchemaComponentProvider scope={{ useCloseAction }} components={{ Output, Form, Action, Input, FormItem }}>
<SchemaComponent schema={schema} />
</SchemaComponentProvider>
);
});

View File

@ -0,0 +1,106 @@
import { FormItem, Input } from '@formily/antd';
import { ISchema, observer, useForm } from '@formily/react';
import {
Action,
APIClientProvider,
Form,
SchemaComponent,
SchemaComponentProvider,
useAPIClient
} from '@nocobase/client';
import { Card, Space } from 'antd';
import React from 'react';
import { apiClient } from './apiClient';
export default observer(() => {
const schema: ISchema = {
type: 'object',
properties: {
form1: {
type: 'void',
'x-uid': 'form1',
'x-component': 'Form',
'x-component-props': {
request: {
resource: 'posts',
action: 'get',
},
},
properties: {
field1: {
title: 'T1',
'x-component': 'Input',
'x-decorator': 'FormItem',
},
field2: {
title: 'T2',
'x-component': 'Input',
'x-decorator': 'FormItem',
required: true,
default: 'default value',
},
out: {
'x-component': 'Output',
},
Space: {
type: 'void',
'x-component': 'Space',
properties: {
action2: {
// type: 'void',
'x-component': 'Action',
title: 'Submit',
'x-component-props': {
useAction: '{{ useSubmit }}',
},
},
action1: {
// type: 'void',
title: 'Refresh',
'x-component': 'Action',
'x-component-props': {
useAction: '{{ useRefresh }}',
},
},
},
},
},
},
},
};
const Output = observer(() => {
const form = useForm();
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
});
const useSubmit = () => {
const form = useForm();
return {
async run() {
await form.submit();
console.log(form.values);
},
};
};
const useRefresh = () => {
const apiClient = useAPIClient();
return {
async run() {
apiClient.service('form1')?.refresh();
},
};
};
return (
<APIClientProvider apiClient={apiClient}>
<SchemaComponentProvider
scope={{ useSubmit, useRefresh }}
components={{ Space, Card, Output, Action, Form, Input, FormItem }}
>
<SchemaComponent schema={schema} />
</SchemaComponentProvider>
</APIClientProvider>
);
});

View File

@ -7,8 +7,26 @@ group:
# Form # Form
## Examples
### 基础用法
<code src="./demos/demo2.tsx"/> <code src="./demos/demo2.tsx"/>
### Form Decorator
Form 也可以作 decorator 存在,和 Action.Drawer 结合就是 DrawerForm 了。 Form 也可以作 decorator 存在,和 Action.Drawer 结合就是 DrawerForm 了。
<code src="./demos/demo1.tsx"/> <code src="./demos/demo1.tsx"/>
### initialValue 初始化
<code src="./demos/demo3.tsx"/>
### decorator 的 initialValue
<code src="./demos/demo4.tsx"/>
### 远程初始化数据
<code src="./demos/demo5.tsx"/>