feat(client): improve the sub table field

This commit is contained in:
chenos 2022-02-25 16:24:50 +08:00
parent 6d0978f2c2
commit 585d9dd580
8 changed files with 387 additions and 112 deletions

View File

@ -8,12 +8,19 @@ import {
useDataSourceFromRAC useDataSourceFromRAC
} from './'; } from './';
import * as hooks from './action-hooks'; import * as hooks from './action-hooks';
import { DataSourceProvider, ds } from './sub-table';
export const CollectionManagerSchemaComponentProvider: React.FC = (props) => { export const CollectionManagerSchemaComponentProvider: React.FC = (props) => {
return ( return (
<SchemaComponentOptions <SchemaComponentOptions
scope={{ cm: { ...hooks, useDataSourceFromRAC } }} scope={{ cm: { ...hooks, useDataSourceFromRAC }, ds }}
components={{ CollectionField, CollectionFieldProvider, CollectionProvider, ResourceActionProvider }} components={{
DataSourceProvider,
CollectionField,
CollectionFieldProvider,
CollectionProvider,
ResourceActionProvider,
}}
> >
{props.children} {props.children}
</SchemaComponentOptions> </SchemaComponentOptions>

View File

@ -6,7 +6,8 @@ import { Button, Dropdown, Menu } from 'antd';
import { cloneDeep } from 'lodash'; import { cloneDeep } from 'lodash';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { useRequest } from '../../api-client'; import { useRequest } from '../../api-client';
import { ActionContext, SchemaComponent, useActionContext, useCompile, useFormBlockContext } from '../../schema-component'; import { RecordProvider } from '../../record-provider';
import { ActionContext, SchemaComponent, useActionContext, useCompile } from '../../schema-component';
import { useCollectionManager } from '../hooks'; import { useCollectionManager } from '../hooks';
import { IField } from '../interfaces/types'; import { IField } from '../interfaces/types';
import { options } from './interfaces'; import { options } from './interfaces';
@ -61,7 +62,7 @@ const getSchema = (schema: IField): ISchema => {
'x-component': 'Action', 'x-component': 'Action',
'x-component-props': { 'x-component-props': {
type: 'primary', type: 'primary',
useAction: '{{ useCreateSubField }}', useAction: '{{ ds.useCreateAction }}',
}, },
}, },
}, },
@ -73,17 +74,9 @@ const getSchema = (schema: IField): ISchema => {
}; };
const useCreateSubField = () => { const useCreateSubField = () => {
// const form = useForm();
const { form, parent } = useFormBlockContext();
const ctx = useActionContext(); const ctx = useActionContext();
return { return {
async run() { async run() {
await form.submit();
const children = parent.form.values?.children?.slice?.();
children.push(cloneDeep(form.values));
console.log('form.values', form.values, children);
parent.form.setValuesIn('children', children);
ctx.setVisible(false);
// const options = form?.values?.uiSchema?.enum?.slice() || []; // const options = form?.values?.uiSchema?.enum?.slice() || [];
// form.setValuesIn( // form.setValuesIn(
// 'uiSchema.enum', // 'uiSchema.enum',
@ -132,7 +125,9 @@ export const AddSubFieldAction = () => {
</Button> </Button>
</Dropdown> </Dropdown>
<SchemaComponent schema={schema} components={{ ArrayTable }} scope={{ useCreateSubField }} /> <RecordProvider record={{}}>
<SchemaComponent schema={schema} components={{ ArrayTable }} scope={{ useCreateSubField }} />
</RecordProvider>
</ActionContext.Provider> </ActionContext.Provider>
); );
}; };

View File

@ -1,12 +1,13 @@
import { ArrayTable } from '@formily/antd'; import { ArrayTable } from '@formily/antd';
import { ISchema } from '@formily/react'; import { ISchema, useForm } from '@formily/react';
import { uid } from '@formily/shared'; import { uid } from '@formily/shared';
import cloneDeep from 'lodash/cloneDeep'; import cloneDeep from 'lodash/cloneDeep';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useAPIClient, useRequest } from '../../api-client'; import { useAPIClient, useRequest } from '../../api-client';
import { useRecord } from '../../record-provider'; import { useRecord } from '../../record-provider';
import { ActionContext, SchemaComponent, useActionContext, useFormBlockContext } from '../../schema-component'; import { ActionContext, SchemaComponent } from '../../schema-component';
import { useUpdateAction } from '../action-hooks';
import { useCollectionManager } from '../hooks'; import { useCollectionManager } from '../hooks';
import { IField } from '../interfaces/types'; import { IField } from '../interfaces/types';
@ -54,7 +55,7 @@ const getSchema = (schema: IField): ISchema => {
'x-component': 'Action', 'x-component': 'Action',
'x-component-props': { 'x-component-props': {
type: 'primary', type: 'primary',
useAction: '{{ useUpdateSubField }}', useAction: '{{ ds.useUpdateAction }}',
}, },
}, },
}, },
@ -65,23 +66,25 @@ const getSchema = (schema: IField): ISchema => {
}; };
}; };
const useUpdateSubField = () => { const useUpdateCollectionField = () => {
const { form, parent } = useFormBlockContext(); const form = useForm();
const ctx = useActionContext(); const { run } = useUpdateAction();
const { refreshCM } = useCollectionManager();
return { return {
async run() { async run() {
await form.submit(); await form.submit();
const children = parent.form.values?.children?.slice?.(); const options = form?.values?.uiSchema?.enum?.slice() || [];
parent.form.setValuesIn( form.setValuesIn(
'children', 'uiSchema.enum',
children.map((child) => { options.map((option) => {
if (child.name === form.values.name) { return {
return cloneDeep(form.values); value: uid(),
} ...option,
return child; };
}), }),
); );
ctx.setVisible(false); await run();
await refreshCM();
}, },
}; };
}; };
@ -97,15 +100,13 @@ export const EditSubFieldAction = (props) => {
<ActionContext.Provider value={{ visible, setVisible }}> <ActionContext.Provider value={{ visible, setVisible }}>
<a <a
onClick={async () => { onClick={async () => {
// const { data } = await api.resource('fields.children', record.key).get({ // const { data } = await api.resource('collections.fields', record.collectionName).get({
// filterByTk: record.name, // filterByTk: record.name,
// appends: ['uiSchema'], // appends: record.interface === 'subTable' ? ['uiSchema', 'children'] : ['uiSchema'],
// }); // });
const schema = getSchema({ const schema = getSchema({
...getInterface(record.interface), ...getInterface(record.interface),
default: { default: record,
...record,
},
}); });
setSchema(schema); setSchema(schema);
setVisible(true); setVisible(true);
@ -113,7 +114,7 @@ export const EditSubFieldAction = (props) => {
> >
{t('Edit')} {t('Edit')}
</a> </a>
<SchemaComponent schema={schema} components={{ ArrayTable }} scope={{ useUpdateSubField }} /> <SchemaComponent schema={schema} components={{ ArrayTable }} scope={{ useUpdateCollectionField }} />
</ActionContext.Provider> </ActionContext.Provider>
); );
}; };

View File

@ -33,15 +33,15 @@ export const subTable: IField = {
...defaultProps, ...defaultProps,
subtable: { subtable: {
type: 'void', type: 'void',
'x-component': 'div', 'x-component': 'DataSourceProvider',
properties: { properties: {
actions: { actions: {
type: 'void', type: 'void',
'x-component': 'ActionBar', 'x-component': 'ActionBar',
'x-component-props': { 'x-component-props': {
style: { // style: {
marginBottom: 16, // marginBottom: 16,
}, // },
}, },
properties: { properties: {
delete: { delete: {
@ -49,7 +49,7 @@ export const subTable: IField = {
title: '{{ t("Delete") }}', title: '{{ t("Delete") }}',
'x-component': 'Action', 'x-component': 'Action',
'x-component-props': { 'x-component-props': {
useAction: '{{ useBulkDestroySubField }}', useAction: '{{ ds.useBulkDestroyAction }}',
confirm: { confirm: {
title: "{{t('Delete record')}}", title: "{{t('Delete record')}}",
content: "{{t('Are you sure you want to delete it?')}}", content: "{{t('Are you sure you want to delete it?')}}",
@ -73,11 +73,15 @@ export const subTable: IField = {
'x-component': 'Table.Array', 'x-component': 'Table.Array',
'x-component-props': { 'x-component-props': {
pagination: false, pagination: false,
expandable: {
childrenColumnName: '__nochildren__',
},
rowKey: 'name', rowKey: 'name',
rowSelection: { rowSelection: {
type: 'checkbox', type: 'checkbox',
}, },
useSelectedRowKeys: '{{ useSelectedRowKeys }}', useSelectedRowKeys: '{{ ds.useSelectedRowKeys }}',
useDataSource: '{{ ds.useDataSource }}',
// scroll: { x: '100%' }, // scroll: { x: '100%' },
}, },
properties: { properties: {
@ -96,7 +100,6 @@ export const subTable: IField = {
column2: { column2: {
type: 'void', type: 'void',
title: '{{ t("Field name") }}', title: '{{ t("Field name") }}',
'x-decorator': 'Table.Column.Decorator',
'x-component': 'Table.Column', 'x-component': 'Table.Column',
properties: { properties: {
name: { name: {
@ -108,7 +111,6 @@ export const subTable: IField = {
column3: { column3: {
type: 'void', type: 'void',
title: '{{ t("Field interface") }}', title: '{{ t("Field interface") }}',
'x-decorator': 'Table.Column.Decorator',
'x-component': 'Table.Column', 'x-component': 'Table.Column',
properties: { properties: {
interface: { interface: {
@ -134,7 +136,7 @@ export const subTable: IField = {
title: '{{ t("Edit") }}', title: '{{ t("Edit") }}',
'x-component': 'EditSubFieldAction', 'x-component': 'EditSubFieldAction',
'x-component-props': { 'x-component-props': {
type: 'primary', // useAction: '{{ ds.useUpdateAction }}',
}, },
}, },
delete: { delete: {
@ -142,7 +144,7 @@ export const subTable: IField = {
title: '{{ t("Delete") }}', title: '{{ t("Delete") }}',
'x-component': 'Action.Link', 'x-component': 'Action.Link',
'x-component-props': { 'x-component-props': {
useAction: '{{ useDestroySubField }}', useAction: '{{ ds.useDestroyAction }}',
confirm: { confirm: {
title: "{{t('Delete record')}}", title: "{{t('Delete record')}}",
content: "{{t('Are you sure you want to delete it?')}}", content: "{{t('Are you sure you want to delete it?')}}",

View File

@ -0,0 +1,214 @@
import { observer, useForm } from '@formily/react';
import React, { createContext, useContext, useState } from 'react';
import { CollectionOptions, CollectionProvider, useActionContext, useRecord, useRequest } from '../';
import { useAPIClient } from '../api-client';
import { options } from './Configuration/interfaces';
const collection: CollectionOptions = {
name: 'fields',
targetKey: 'name',
fields: [
{
type: 'string',
name: 'type',
interface: 'input',
uiSchema: {
title: '{{ t("Storage type") }}',
type: 'string',
'x-component': 'Select',
enum: [
{
label: 'String',
value: 'string',
},
],
required: true,
},
},
{
type: 'string',
name: 'interface',
interface: 'input',
uiSchema: {
title: '{{ t("Field interface") }}',
type: 'string',
'x-component': 'Select',
enum: options as any,
},
},
{
type: 'string',
name: 'title',
interface: 'input',
uiSchema: {
title: '{{ t("Field display name") }}',
type: 'string',
'x-component': 'Input',
required: true,
},
},
{
type: 'string',
name: 'name',
interface: 'input',
uiSchema: {
title: '{{ t("Field name") }}',
type: 'string',
'x-component': 'Input',
},
},
],
};
export const DataSourceContext = createContext(null);
const useSelectedRowKeys = () => {
const ctx = useContext(DataSourceContext);
return [ctx.selectedRowKeys, ctx.setSelectedRowKeys];
};
const useDataSource = (options) => {
const ctx = useContext(DataSourceContext);
return useRequest(
() => {
return Promise.resolve({
data: ctx.dataSource,
});
},
{
...options,
refreshDeps: [JSON.stringify(ctx.dataSource)],
},
);
};
const useCreateAction = () => {
const ctx = useContext(DataSourceContext);
const form = useForm();
const { setVisible } = useActionContext();
return {
async run() {
// console.log(ctx.dataSource);
const dataSource = ctx.dataSource || [];
dataSource.push(form.values);
ctx.setDataSource([...dataSource]);
setVisible(false);
},
};
};
const useBulkDestroyAction = () => {
const ctx = useContext(DataSourceContext);
const { selectedRowKeys, setSelectedRowKeys } = ctx;
return {
async run() {
const dataSource = ctx.dataSource || [];
ctx.setDataSource(
dataSource.filter((item) => {
return !selectedRowKeys.includes(item[ctx.rowKey]);
}),
);
setSelectedRowKeys([]);
},
};
};
const useUpdateAction = () => {
const record = useRecord();
const form = useForm();
const { setVisible } = useActionContext();
const ctx = useContext(DataSourceContext);
return {
async run() {
const dataSource = ctx?.dataSource || [];
const rowKey = ctx?.rowKey;
const values = dataSource.map((item) => {
if (record[rowKey] === item[rowKey]) {
return { ...form.values };
}
return item;
});
ctx.setDataSource([...values]);
setVisible(false);
},
};
};
const useDestroyAction = () => {
const record = useRecord();
const ctx = useContext(DataSourceContext);
return {
async run() {
const rowKey = ctx.rowKey;
const dataSource = ctx.dataSource || [];
ctx.setDataSource(
dataSource.filter((item) => {
return record[rowKey] !== item[rowKey];
}),
);
},
};
};
export const ds = {
useSelectedRowKeys,
useDataSource,
useCreateAction,
useBulkDestroyAction,
useUpdateAction,
useDestroyAction,
};
export const DataSourceProvider = observer((props) => {
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const [dataSource, setDataSource] = useState([]);
const record = useRecord();
const api = useAPIClient();
const service = useRequest(
() => {
if (record.interface === 'subTable') {
if (record?.children?.length) {
return Promise.resolve({
data: record?.children || [],
});
}
return api
.resource('fields')
.list({
paginate: false,
appends: ['uiSchema'],
sort: 'sort',
filter: {
parentKey: record.key,
},
})
.then((res) => res?.data);
}
return Promise.resolve({
data: [],
});
},
{
onSuccess(data) {
console.log('dataSource1', data?.data);
setDataSource(data?.data);
},
},
);
return (
<CollectionProvider collection={collection}>
<DataSourceContext.Provider
value={{
rowKey: 'name',
service,
dataSource,
setDataSource,
selectedRowKeys,
setSelectedRowKeys,
}}
>
{props.children}
</DataSourceContext.Provider>
</CollectionProvider>
);
});

View File

@ -121,7 +121,13 @@ export const TableArray: React.FC<any> = observer((props) => {
}; };
return ( return (
<div> <div>
<Table {...others} {...restProps} components={components} columns={columns} dataSource={field.value?.slice()} /> <Table
{...others}
{...restProps}
components={components}
columns={columns}
dataSource={field?.value?.slice?.()}
/>
</div> </div>
); );
}); });

View File

@ -13,52 +13,121 @@ import React, { createContext, useContext, useState } from 'react';
const DataSourceContext = createContext(null); const DataSourceContext = createContext(null);
const useSelectedRowKeys = () => {
const ctx = useContext(DataSourceContext);
return [ctx.selectedRowKeys, ctx.setSelectedRowKeys];
};
const useDataSource = (options) => {
const ctx = useContext(DataSourceContext);
return useRequest(
() => {
console.log('ctx.dataSource', ctx.dataSource);
return Promise.resolve({
data: ctx.dataSource,
});
},
{
...options,
refreshDeps: [JSON.stringify(ctx.dataSource)],
},
);
};
const useCreateAction = () => {
const ctx = useContext(DataSourceContext);
return {
async run() {
console.log(ctx.dataSource);
const dataSource = ctx.dataSource || [];
dataSource.push({
id: uid(),
name: uid(),
});
ctx.setDataSource([...dataSource]);
},
};
};
const useBulkDestroyAction = () => {
const ctx = useContext(DataSourceContext);
const { selectedRowKeys, setSelectedRowKeys } = ctx;
return {
async run() {
const dataSource = ctx.dataSource || [];
ctx.setDataSource(
dataSource.filter((item) => {
return !selectedRowKeys.includes(item.id);
}),
);
setSelectedRowKeys([]);
},
};
};
const useUpdateAction = () => {
const record = useRecord();
// const form = useForm();
const ctx = useContext(DataSourceContext);
return {
async run() {
const dataSource = ctx.dataSource || [];
ctx.setDataSource([
...dataSource.map((item) => {
if (record.id === item.id) {
record.name = uid();
return record;
}
return item;
}),
]);
},
};
};
const useDestroyAction = () => {
const record = useRecord();
const ctx = useContext(DataSourceContext);
return {
async run() {
const dataSource = ctx.dataSource || [];
ctx.setDataSource(
dataSource.filter((item) => {
return record.id !== item.id;
}),
);
},
};
};
const ds = {
useSelectedRowKeys,
useDataSource,
useCreateAction,
useBulkDestroyAction,
useUpdateAction,
useDestroyAction,
};
const schema: ISchema = { const schema: ISchema = {
type: 'object', type: 'object',
properties: { properties: {
context: { context: {
type: 'void', type: 'void',
'x-component': 'Context', 'x-component': 'DataSourceProvider',
properties: { properties: {
action1: { action1: {
title: '提交', title: '添加',
'x-component': 'Action', 'x-component': 'Action',
'x-component-props': { 'x-component-props': {
useAction() { useAction: '{{ ds.useCreateAction }}',
const ctx = useContext(DataSourceContext);
return {
async run() {
console.log(ctx.dataSource);
const dataSource = ctx.dataSource || [];
dataSource.push({
id: uid(),
name: uid(),
});
ctx.setDataSource([...dataSource]);
},
};
},
}, },
}, },
action2: { action2: {
title: '删除', title: '删除',
'x-component': 'Action', 'x-component': 'Action',
'x-component-props': { 'x-component-props': {
useAction() { useAction: '{{ ds.useBulkDestroyAction }}',
const ctx = useContext(DataSourceContext);
const { selectedRowKeys, setSelectedRowKeys } = ctx;
return {
async run() {
const dataSource = ctx.dataSource || [];
ctx.setDataSource(
dataSource.filter((item) => {
return !selectedRowKeys.includes(item.id);
}),
);
setSelectedRowKeys([]);
},
};
},
}, },
}, },
input: { input: {
@ -67,28 +136,12 @@ const schema: ISchema = {
'x-component': 'Table.Array', 'x-component': 'Table.Array',
'x-component-props': { 'x-component-props': {
rowKey: 'id', rowKey: 'id',
pagination: false,
rowSelection: { rowSelection: {
type: 'checkbox', type: 'checkbox',
}, },
useSelectedRowKeys() { useSelectedRowKeys: '{{ ds.useSelectedRowKeys }}',
const ctx = useContext(DataSourceContext); useDataSource: '{{ ds.useDataSource }}',
return [ctx.selectedRowKeys, ctx.setSelectedRowKeys];
},
useDataSource(options) {
const ctx = useContext(DataSourceContext);
return useRequest(
() => {
console.log('ctx.dataSource', ctx.dataSource);
return Promise.resolve({
data: ctx.dataSource,
});
},
{
...options,
refreshDeps: [JSON.stringify(ctx.dataSource)],
},
);
},
}, },
properties: { properties: {
column1: { column1: {
@ -108,24 +161,18 @@ const schema: ISchema = {
title: 'Actions', title: 'Actions',
'x-component': 'Table.Column', 'x-component': 'Table.Column',
properties: { properties: {
action: { action1: {
title: '编辑',
'x-component': 'Action',
'x-component-props': {
useAction: '{{ ds.useUpdateAction }}',
},
},
action2: {
title: '删除', title: '删除',
'x-component': 'Action', 'x-component': 'Action',
'x-component-props': { 'x-component-props': {
useAction() { useAction: '{{ ds.useDestroyAction }}',
const record = useRecord();
const ctx = useContext(DataSourceContext);
return {
async run() {
const dataSource = ctx.dataSource || [];
ctx.setDataSource(
dataSource.filter((item) => {
return record.id !== item.id;
}),
);
},
};
},
}, },
}, },
}, },
@ -137,7 +184,7 @@ const schema: ISchema = {
}, },
}; };
const Context = observer((props) => { const DataSourceProvider = observer((props) => {
const [selectedRowKeys, setSelectedRowKeys] = useState([]); const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const [dataSource, setDataSource] = useState([]); const [dataSource, setDataSource] = useState([]);
const service = useRequest( const service = useRequest(
@ -174,7 +221,7 @@ const Context = observer((props) => {
export default () => { export default () => {
return ( return (
<SchemaComponentProvider components={{ Action, Context, Table, Input }}> <SchemaComponentProvider scope={{ ds }} components={{ Action, DataSourceProvider, Table, Input }}>
<SchemaComponent schema={schema} /> <SchemaComponent schema={schema} />
</SchemaComponentProvider> </SchemaComponentProvider>
); );

View File

@ -25,6 +25,9 @@ export class MagicAttributeModel extends Model {
} }
return super.set(`${this.magicAttribute}.${key}`, value, options); return super.set(`${this.magicAttribute}.${key}`, value, options);
} else { } else {
if (!key) {
return;
}
Object.keys(key).forEach((k) => { Object.keys(key).forEach((k) => {
this.set(k, key[k], options); this.set(k, key[k], options);
}); });