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 { Table, TableColumnProps } from 'antd';
import React from 'react';
import { RecordProvider } from '../../../';
import { useComponent } from '../../hooks';
const isColumnComponent = (schema: Schema) => {
@ -24,7 +25,11 @@ const useTableColumns = () => {
dataIndex: s.name,
render: (v, 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>;
});

View File

@ -13,7 +13,7 @@ export interface FormProps {
[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 { 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);
};
@ -51,13 +51,16 @@ export const Form: React.FC<FormProps> = observer((props) => {
const { request, initialValue, useValues = useDef, ...others } = props;
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);
const { loading } = useValues(
{
uid: fieldSchema['x-uid'],
async onSuccess(data) {
await form.reset();
form.setValues(data?.data);
},
},
});
props,
);
return (
<Spin spinning={loading}>
<FormComponent form={form} {...others} />

View File

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

View File

@ -10,7 +10,7 @@ import { ArrayTable } from '../array-table';
type VoidTableProps = TableProps<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> & {
@ -66,33 +66,36 @@ const useRequestProps = (props) => {
};
};
const useDefDataSource = (props, options) => {
const useDef = (options, props) => {
return useRequest(useRequestProps(props), options);
};
export const VoidTable: VoidTableType = observer((props) => {
const { useDataSource = useDefDataSource } = props;
const { useDataSource = useDef } = props;
const field = useField<Field>();
const fieldSchema = useFieldSchema();
const form = useMemo(() => createForm(), []);
const f = useAttach(form.createArrayField({ name: fieldSchema.name }));
const result = useDataSource(props, {
uid: fieldSchema['x-uid'],
onSuccess(data) {
form.setValues({
[fieldSchema.name]: data?.data,
});
if (field?.componentProps?.pagination === false) {
return;
}
field.componentProps.pagination = field.componentProps.pagination || {};
if (data?.meta?.count) {
field.componentProps.pagination.total = data?.meta?.count;
}
field.componentProps.pagination.current = data?.meta?.page || 1;
field.componentProps.pagination.pageSize = data?.meta?.pageSize || 10;
const result = useDataSource(
{
uid: fieldSchema['x-uid'],
onSuccess(data) {
form.setValues({
[fieldSchema.name]: data?.data,
});
if (field?.componentProps?.pagination === false) {
return;
}
field.componentProps.pagination = field.componentProps.pagination || {};
if (data?.meta?.count) {
field.componentProps.pagination.total = data?.meta?.count;
}
field.componentProps.pagination.current = data?.meta?.page || 1;
field.componentProps.pagination.pageSize = data?.meta?.pageSize || 10;
},
},
});
props,
);
return (
<AsyncDataProvider value={result}>
<FormContext.Provider value={form}>

View File

@ -1,19 +1,18 @@
/**
* title: 勾选
*/
import { ISchema, useField, useFieldSchema } from '@formily/react';
import { uid } from '@formily/shared';
import {
AntdSchemaComponentProvider,
APIClientProvider,
CollectionField,
CollectionManagerProvider,
CollectionProvider,
SchemaComponent,
SchemaComponentProvider,
useCollection
useCollection,
useDesignable,
useRequest
} from '@nocobase/client';
import React, { useEffect } from 'react';
import { useDesignable } from '../../..';
import React, { createContext, useContext, useEffect } from 'react';
import { apiClient } from './apiClient';
const schema: ISchema = {
@ -21,7 +20,22 @@ const schema: ISchema = {
properties: {
block1: {
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: {
table1: {
type: 'void',
@ -32,18 +46,7 @@ const schema: ISchema = {
rowSelection: {
type: 'checkbox',
},
pagination: {
current: 2,
pageSize: 2,
},
request: {
resource: 'posts',
action: 'list',
params: {
filter: {},
// pageSize: 5,
},
},
useDataSource: '{{ useDataSourceFromRAC }}',
},
properties: {
column1: {
@ -142,68 +145,85 @@ const TableColumnDecorator = (props) => {
);
};
const CollectionBlock = (props) => {
return (
<CollectionProvider
collection={{
name: 'posts',
fields: [
{
type: 'integer',
name: 'id',
interface: 'input',
uiSchema: {
title: 'ID1',
type: 'number',
'x-component': 'InputNumber',
required: true,
description: 'description1',
} as ISchema,
const collections = [
{
name: 'posts',
fields: [
{
type: 'integer',
name: 'id',
interface: 'input',
uiSchema: {
title: 'ID1',
type: 'number',
'x-component': 'InputNumber',
required: true,
description: 'description1',
} as ISchema,
},
{
type: 'string',
name: 'name',
interface: 'input',
uiSchema: {
title: 'Name1',
type: 'string',
'x-component': 'Input',
required: true,
description: 'description1',
} as ISchema,
},
{
type: 'string',
name: 'date',
interface: 'datetime',
uiSchema: {
type: 'boolean',
title: `Date1`,
'x-read-pretty': true,
'x-decorator': 'FormItem',
'x-component': 'DatePicker',
'x-component-props': {
dateFormat: 'YYYY/MM/DD',
// showTime: true,
},
{
type: 'string',
name: 'name',
interface: 'input',
uiSchema: {
title: 'Name1',
type: 'string',
'x-component': 'Input',
required: true,
description: 'description1',
} as ISchema,
},
{
type: 'string',
name: 'date',
interface: 'datetime',
uiSchema: {
type: 'boolean',
title: `Date1`,
'x-read-pretty': true,
'x-decorator': 'FormItem',
'x-component': 'DatePicker',
'x-component-props': {
dateFormat: 'YYYY/MM/DD',
// showTime: true,
},
},
},
],
}}
>
{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 () => {
return (
<APIClientProvider apiClient={apiClient}>
<SchemaComponentProvider components={{ TableColumnDecorator, CollectionBlock, CollectionField }}>
<AntdSchemaComponentProvider>
<SchemaComponent schema={schema} />
</AntdSchemaComponentProvider>
</SchemaComponentProvider>
<CollectionManagerProvider collections={collections}>
<SchemaComponentProvider components={componets}>
<AntdSchemaComponentProvider>
<SchemaComponent schema={schema} scope={{ useDataSourceFromRAC }} />
</AntdSchemaComponentProvider>
</SchemaComponentProvider>
</CollectionManagerProvider>
</APIClientProvider>
);
};

View File

@ -202,6 +202,50 @@ export default () => {
<code src="./demos/demo1.tsx">
CollectionProvider + CollectionField
### CollectionProvider + ResourceActionProvider
<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>
```