feat(client): example of CollectionProvider + ResourceActionProvider

This commit is contained in:
chenos 2022-02-16 09:27:13 +08:00
parent 0440591868
commit 6036189063
6 changed files with 180 additions and 105 deletions

View File

@ -2,6 +2,7 @@ import { ArrayField } from '@formily/core';
import { observer, RecursionField, Schema, useField, useFieldSchema } from '@formily/react'; import { observer, RecursionField, Schema, useField, useFieldSchema } from '@formily/react';
import { Table, TableColumnProps } from 'antd'; import { Table, TableColumnProps } from 'antd';
import React from 'react'; import React from 'react';
import { RecordProvider } from '../../../';
import { useComponent } from '../../hooks'; import { useComponent } from '../../hooks';
const isColumnComponent = (schema: Schema) => { const isColumnComponent = (schema: Schema) => {
@ -24,7 +25,11 @@ const useTableColumns = () => {
dataIndex: s.name, dataIndex: s.name,
render: (v, record) => { render: (v, record) => {
const index = field.value?.indexOf(record); const index = field.value?.indexOf(record);
return <RecursionField schema={s} name={index} onlyRenderProperties />; return (
<RecordProvider record={record}>
<RecursionField schema={s} name={index} onlyRenderProperties />
</RecordProvider>
);
}, },
} as TableColumnProps<any>; } as TableColumnProps<any>;
}); });

View File

@ -13,7 +13,7 @@ export interface FormProps {
[key: string]: any; [key: string]: any;
} }
export type FormUseValues = (props?: FormProps, opts?: Opts) => Result<any, any>; export type FormUseValues = (opts?: Opts, props?: FormProps) => Result<any, any>;
const FormComponent: React.FC<FormProps> = (props) => { const FormComponent: React.FC<FormProps> = (props) => {
const { form, children, ...others } = props; const { form, children, ...others } = props;
@ -43,7 +43,7 @@ const useRequestProps = (props: any) => {
}; };
}; };
const useDef = (props: FormProps = {}, opts: any = {}) => { const useDef = (opts: any = {}, props: FormProps = {}) => {
return useRequest(useRequestProps(props), opts); return useRequest(useRequestProps(props), opts);
}; };
@ -51,13 +51,16 @@ export const Form: React.FC<FormProps> = observer((props) => {
const { request, initialValue, useValues = useDef, ...others } = props; const { request, initialValue, useValues = useDef, ...others } = props;
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const form = useMemo(() => createForm(), []); const form = useMemo(() => createForm(), []);
const { loading } = useValues(props, { const { loading } = useValues(
{
uid: fieldSchema['x-uid'], uid: fieldSchema['x-uid'],
async onSuccess(data) { async onSuccess(data) {
await form.reset(); await form.reset();
form.setValues(data?.data); form.setValues(data?.data);
}, },
}); },
props,
);
return ( return (
<Spin spinning={loading}> <Spin spinning={loading}>
<FormComponent form={form} {...others} /> <FormComponent form={form} {...others} />

View File

@ -50,7 +50,7 @@ const useSubmit = () => {
}; };
}; };
const useValues: FormUseValues = (props, opts) => { const useValues: FormUseValues = (opts) => {
return useRequest(() => { return useRequest(() => {
return Promise.resolve({ data: { field1: 'aabb' } }); return Promise.resolve({ data: { field1: 'aabb' } });
}, opts); }, opts);

View File

@ -10,7 +10,7 @@ import { ArrayTable } from '../array-table';
type VoidTableProps = TableProps<any> & { type VoidTableProps = TableProps<any> & {
request?: any; request?: any;
useDataSource?: (data?: any, options?: Options<any, any> & { uid?: string }) => Result<any, any>; useDataSource?: (options?: Options<any, any> & { uid?: string }, props?: any) => Result<any, any>;
}; };
type VoidTableType = React.FC<VoidTableProps> & { type VoidTableType = React.FC<VoidTableProps> & {
@ -66,17 +66,18 @@ const useRequestProps = (props) => {
}; };
}; };
const useDefDataSource = (props, options) => { const useDef = (options, props) => {
return useRequest(useRequestProps(props), options); return useRequest(useRequestProps(props), options);
}; };
export const VoidTable: VoidTableType = observer((props) => { export const VoidTable: VoidTableType = observer((props) => {
const { useDataSource = useDefDataSource } = props; const { useDataSource = useDef } = props;
const field = useField<Field>(); const field = useField<Field>();
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const form = useMemo(() => createForm(), []); const form = useMemo(() => createForm(), []);
const f = useAttach(form.createArrayField({ name: fieldSchema.name })); const f = useAttach(form.createArrayField({ name: fieldSchema.name }));
const result = useDataSource(props, { const result = useDataSource(
{
uid: fieldSchema['x-uid'], uid: fieldSchema['x-uid'],
onSuccess(data) { onSuccess(data) {
form.setValues({ form.setValues({
@ -92,7 +93,9 @@ export const VoidTable: VoidTableType = observer((props) => {
field.componentProps.pagination.current = data?.meta?.page || 1; field.componentProps.pagination.current = data?.meta?.page || 1;
field.componentProps.pagination.pageSize = data?.meta?.pageSize || 10; field.componentProps.pagination.pageSize = data?.meta?.pageSize || 10;
}, },
}); },
props,
);
return ( return (
<AsyncDataProvider value={result}> <AsyncDataProvider value={result}>
<FormContext.Provider value={form}> <FormContext.Provider value={form}>

View File

@ -1,19 +1,18 @@
/**
* title: 勾选
*/
import { ISchema, useField, useFieldSchema } from '@formily/react'; import { ISchema, useField, useFieldSchema } from '@formily/react';
import { uid } from '@formily/shared'; import { uid } from '@formily/shared';
import { import {
AntdSchemaComponentProvider, AntdSchemaComponentProvider,
APIClientProvider, APIClientProvider,
CollectionField, CollectionField,
CollectionManagerProvider,
CollectionProvider, CollectionProvider,
SchemaComponent, SchemaComponent,
SchemaComponentProvider, SchemaComponentProvider,
useCollection useCollection,
useDesignable,
useRequest
} from '@nocobase/client'; } from '@nocobase/client';
import React, { useEffect } from 'react'; import React, { createContext, useContext, useEffect } from 'react';
import { useDesignable } from '../../..';
import { apiClient } from './apiClient'; import { apiClient } from './apiClient';
const schema: ISchema = { const schema: ISchema = {
@ -21,7 +20,22 @@ const schema: ISchema = {
properties: { properties: {
block1: { block1: {
type: 'void', type: 'void',
'x-decorator': 'CollectionBlock', 'x-decorator': 'CollectionProvider',
'x-decorator-props': {
name: 'posts',
},
'x-component': 'ResourceActionProvider',
'x-component-props': {
request: {
resource: 'posts',
action: 'list',
params: {
filter: {},
sort: [],
appends: [],
},
},
},
properties: { properties: {
table1: { table1: {
type: 'void', type: 'void',
@ -32,18 +46,7 @@ const schema: ISchema = {
rowSelection: { rowSelection: {
type: 'checkbox', type: 'checkbox',
}, },
pagination: { useDataSource: '{{ useDataSourceFromRAC }}',
current: 2,
pageSize: 2,
},
request: {
resource: 'posts',
action: 'list',
params: {
filter: {},
// pageSize: 5,
},
},
}, },
properties: { properties: {
column1: { column1: {
@ -142,10 +145,8 @@ const TableColumnDecorator = (props) => {
); );
}; };
const CollectionBlock = (props) => { const collections = [
return ( {
<CollectionProvider
collection={{
name: 'posts', name: 'posts',
fields: [ fields: [
{ {
@ -189,21 +190,40 @@ const CollectionBlock = (props) => {
}, },
}, },
], ],
}} },
> ];
{props.children}
</CollectionProvider> const ResourceActionContext = createContext(null);
);
const ResourceActionProvider = (props) => {
const { name } = useCollection();
const { request } = props;
const service = useRequest(request);
return <ResourceActionContext.Provider value={{ service }}>{props.children}</ResourceActionContext.Provider>;
}; };
const useDataSourceFromRAC = (options: any) => {
const { service } = useContext(ResourceActionContext);
useEffect(() => {
if (!service.loading) {
options?.onSuccess(service.data);
}
}, [service.loading]);
return service;
};
const componets = { CollectionProvider, TableColumnDecorator, ResourceActionProvider, CollectionField };
export default () => { export default () => {
return ( return (
<APIClientProvider apiClient={apiClient}> <APIClientProvider apiClient={apiClient}>
<SchemaComponentProvider components={{ TableColumnDecorator, CollectionBlock, CollectionField }}> <CollectionManagerProvider collections={collections}>
<SchemaComponentProvider components={componets}>
<AntdSchemaComponentProvider> <AntdSchemaComponentProvider>
<SchemaComponent schema={schema} /> <SchemaComponent schema={schema} scope={{ useDataSourceFromRAC }} />
</AntdSchemaComponentProvider> </AntdSchemaComponentProvider>
</SchemaComponentProvider> </SchemaComponentProvider>
</CollectionManagerProvider>
</APIClientProvider> </APIClientProvider>
); );
}; };

View File

@ -202,6 +202,50 @@ export default () => {
<code src="./demos/demo1.tsx"> <code src="./demos/demo1.tsx">
CollectionProvider + CollectionField ### CollectionProvider + ResourceActionProvider
<code src="./demos/demo2.tsx"> <code src="./demos/demo2.tsx">
大纲
```tsx | pure
<CollectionProvider> {/* 属于哪个 collection */}
<ResourceActionProvider> {/* 发起请求,将请求结果存到上下文共享给子组件 */}
<SettingsForm /> {/* 区块的配置表单 */}
<ActionBar /> {/* 操作区 */}
<Table/> {/* 具体的组件,如 Table、Form、Calendar 等 */}
</ResourceActionProvider>
</CollectionProvider>
```
通过 CollectionProvider 和 ResourceActionProvider 来解决数据区块配置和数据请求,与具体组件无关,所有区块通用。在里面可以放任意东西。
嵌套使用的情况
```tsx | pure
<CollectionProvider> {/* 属于哪个 collection */}
<ResourceActionProvider> {/* 发起请求,将请求结果存到上下文共享给子组件 */}
<SettingsForm /> {/* 区块的配置表单 */}
<ActionBar /> {/* 操作区 */}
<Table> {/* 具体的组件 */}
<Table.Column>
<SettingsForm /> {/* 表格列的配置表单 */}
<RecordProvider> {/* 列表数据的行记录 */}
<CollectionField>
<CollectionFieldProvider> {/* 是哪个字段 */}
<CollectionProvider> {/* 关联字段的关联表 collection */}
<ResourceActionProvider> {/* 可能也会发起请求,如查看详情 */}
<SettingsForm />
<ActionBar />
<Table />
</ResourceActionProvider>
</CollectionProvider>
</CollectionFieldProvider>
</CollectionField>
</RecordProvider>
</Table.Column>
<Table.Column></Table.Column> {/* 会有很多列 */}
</Table>
</ResourceActionProvider>
</CollectionProvider>
```