From 585d9dd580a7976940e104618e5756c448d03fd9 Mon Sep 17 00:00:00 2001 From: chenos Date: Fri, 25 Feb 2022 16:24:50 +0800 Subject: [PATCH] feat(client): improve the sub table field --- ...llectionManagerSchemaComponentProvider.tsx | 11 +- .../Configuration/AddSubFieldAction.tsx | 17 +- .../Configuration/EditSubFieldAction.tsx | 43 ++-- .../collection-manager/interfaces/subTable.ts | 22 +- .../src/collection-manager/sub-table.tsx | 214 ++++++++++++++++++ .../antd/table/Table.Array.tsx | 8 +- .../antd/table/demos/demo4.tsx | 181 +++++++++------ .../database/src/magic-attribute-model.ts | 3 + 8 files changed, 387 insertions(+), 112 deletions(-) create mode 100644 packages/client/src/collection-manager/sub-table.tsx diff --git a/packages/client/src/collection-manager/CollectionManagerSchemaComponentProvider.tsx b/packages/client/src/collection-manager/CollectionManagerSchemaComponentProvider.tsx index e60f245ae..e481aaca3 100644 --- a/packages/client/src/collection-manager/CollectionManagerSchemaComponentProvider.tsx +++ b/packages/client/src/collection-manager/CollectionManagerSchemaComponentProvider.tsx @@ -8,12 +8,19 @@ import { useDataSourceFromRAC } from './'; import * as hooks from './action-hooks'; +import { DataSourceProvider, ds } from './sub-table'; export const CollectionManagerSchemaComponentProvider: React.FC = (props) => { return ( {props.children} diff --git a/packages/client/src/collection-manager/Configuration/AddSubFieldAction.tsx b/packages/client/src/collection-manager/Configuration/AddSubFieldAction.tsx index 719d70003..e32747446 100644 --- a/packages/client/src/collection-manager/Configuration/AddSubFieldAction.tsx +++ b/packages/client/src/collection-manager/Configuration/AddSubFieldAction.tsx @@ -6,7 +6,8 @@ import { Button, Dropdown, Menu } from 'antd'; import { cloneDeep } from 'lodash'; import React, { useState } from 'react'; 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 { IField } from '../interfaces/types'; import { options } from './interfaces'; @@ -61,7 +62,7 @@ const getSchema = (schema: IField): ISchema => { 'x-component': 'Action', 'x-component-props': { type: 'primary', - useAction: '{{ useCreateSubField }}', + useAction: '{{ ds.useCreateAction }}', }, }, }, @@ -73,17 +74,9 @@ const getSchema = (schema: IField): ISchema => { }; const useCreateSubField = () => { - // const form = useForm(); - const { form, parent } = useFormBlockContext(); const ctx = useActionContext(); return { 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() || []; // form.setValuesIn( // 'uiSchema.enum', @@ -132,7 +125,9 @@ export const AddSubFieldAction = () => { 添加字段 - + + + ); }; diff --git a/packages/client/src/collection-manager/Configuration/EditSubFieldAction.tsx b/packages/client/src/collection-manager/Configuration/EditSubFieldAction.tsx index 825afed67..80c8e1a99 100644 --- a/packages/client/src/collection-manager/Configuration/EditSubFieldAction.tsx +++ b/packages/client/src/collection-manager/Configuration/EditSubFieldAction.tsx @@ -1,12 +1,13 @@ import { ArrayTable } from '@formily/antd'; -import { ISchema } from '@formily/react'; +import { ISchema, useForm } from '@formily/react'; import { uid } from '@formily/shared'; import cloneDeep from 'lodash/cloneDeep'; import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useAPIClient, useRequest } from '../../api-client'; 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 { IField } from '../interfaces/types'; @@ -54,7 +55,7 @@ const getSchema = (schema: IField): ISchema => { 'x-component': 'Action', 'x-component-props': { type: 'primary', - useAction: '{{ useUpdateSubField }}', + useAction: '{{ ds.useUpdateAction }}', }, }, }, @@ -65,23 +66,25 @@ const getSchema = (schema: IField): ISchema => { }; }; -const useUpdateSubField = () => { - const { form, parent } = useFormBlockContext(); - const ctx = useActionContext(); +const useUpdateCollectionField = () => { + const form = useForm(); + const { run } = useUpdateAction(); + const { refreshCM } = useCollectionManager(); return { async run() { await form.submit(); - const children = parent.form.values?.children?.slice?.(); - parent.form.setValuesIn( - 'children', - children.map((child) => { - if (child.name === form.values.name) { - return cloneDeep(form.values); - } - return child; + const options = form?.values?.uiSchema?.enum?.slice() || []; + form.setValuesIn( + 'uiSchema.enum', + options.map((option) => { + return { + value: uid(), + ...option, + }; }), ); - ctx.setVisible(false); + await run(); + await refreshCM(); }, }; }; @@ -97,15 +100,13 @@ export const EditSubFieldAction = (props) => { { - // const { data } = await api.resource('fields.children', record.key).get({ + // const { data } = await api.resource('collections.fields', record.collectionName).get({ // filterByTk: record.name, - // appends: ['uiSchema'], + // appends: record.interface === 'subTable' ? ['uiSchema', 'children'] : ['uiSchema'], // }); const schema = getSchema({ ...getInterface(record.interface), - default: { - ...record, - }, + default: record, }); setSchema(schema); setVisible(true); @@ -113,7 +114,7 @@ export const EditSubFieldAction = (props) => { > {t('Edit')} - + ); }; diff --git a/packages/client/src/collection-manager/interfaces/subTable.ts b/packages/client/src/collection-manager/interfaces/subTable.ts index 1efb2e35e..6a16bc2e4 100644 --- a/packages/client/src/collection-manager/interfaces/subTable.ts +++ b/packages/client/src/collection-manager/interfaces/subTable.ts @@ -33,15 +33,15 @@ export const subTable: IField = { ...defaultProps, subtable: { type: 'void', - 'x-component': 'div', + 'x-component': 'DataSourceProvider', properties: { actions: { type: 'void', 'x-component': 'ActionBar', 'x-component-props': { - style: { - marginBottom: 16, - }, + // style: { + // marginBottom: 16, + // }, }, properties: { delete: { @@ -49,7 +49,7 @@ export const subTable: IField = { title: '{{ t("Delete") }}', 'x-component': 'Action', 'x-component-props': { - useAction: '{{ useBulkDestroySubField }}', + useAction: '{{ ds.useBulkDestroyAction }}', confirm: { title: "{{t('Delete record')}}", content: "{{t('Are you sure you want to delete it?')}}", @@ -73,11 +73,15 @@ export const subTable: IField = { 'x-component': 'Table.Array', 'x-component-props': { pagination: false, + expandable: { + childrenColumnName: '__nochildren__', + }, rowKey: 'name', rowSelection: { type: 'checkbox', }, - useSelectedRowKeys: '{{ useSelectedRowKeys }}', + useSelectedRowKeys: '{{ ds.useSelectedRowKeys }}', + useDataSource: '{{ ds.useDataSource }}', // scroll: { x: '100%' }, }, properties: { @@ -96,7 +100,6 @@ export const subTable: IField = { column2: { type: 'void', title: '{{ t("Field name") }}', - 'x-decorator': 'Table.Column.Decorator', 'x-component': 'Table.Column', properties: { name: { @@ -108,7 +111,6 @@ export const subTable: IField = { column3: { type: 'void', title: '{{ t("Field interface") }}', - 'x-decorator': 'Table.Column.Decorator', 'x-component': 'Table.Column', properties: { interface: { @@ -134,7 +136,7 @@ export const subTable: IField = { title: '{{ t("Edit") }}', 'x-component': 'EditSubFieldAction', 'x-component-props': { - type: 'primary', + // useAction: '{{ ds.useUpdateAction }}', }, }, delete: { @@ -142,7 +144,7 @@ export const subTable: IField = { title: '{{ t("Delete") }}', 'x-component': 'Action.Link', 'x-component-props': { - useAction: '{{ useDestroySubField }}', + useAction: '{{ ds.useDestroyAction }}', confirm: { title: "{{t('Delete record')}}", content: "{{t('Are you sure you want to delete it?')}}", diff --git a/packages/client/src/collection-manager/sub-table.tsx b/packages/client/src/collection-manager/sub-table.tsx new file mode 100644 index 000000000..32e7ae133 --- /dev/null +++ b/packages/client/src/collection-manager/sub-table.tsx @@ -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 ( + + + {props.children} + + + ); +}); diff --git a/packages/client/src/schema-component/antd/table/Table.Array.tsx b/packages/client/src/schema-component/antd/table/Table.Array.tsx index 01e8c6438..e47d4c6ab 100644 --- a/packages/client/src/schema-component/antd/table/Table.Array.tsx +++ b/packages/client/src/schema-component/antd/table/Table.Array.tsx @@ -121,7 +121,13 @@ export const TableArray: React.FC = observer((props) => { }; return (
- +
); }); diff --git a/packages/client/src/schema-component/antd/table/demos/demo4.tsx b/packages/client/src/schema-component/antd/table/demos/demo4.tsx index 9efb03ea0..789058c40 100644 --- a/packages/client/src/schema-component/antd/table/demos/demo4.tsx +++ b/packages/client/src/schema-component/antd/table/demos/demo4.tsx @@ -13,52 +13,121 @@ import React, { createContext, useContext, useState } from 'react'; 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 = { type: 'object', properties: { context: { type: 'void', - 'x-component': 'Context', + 'x-component': 'DataSourceProvider', properties: { action1: { - title: '提交', + title: '添加', 'x-component': 'Action', 'x-component-props': { - useAction() { - const ctx = useContext(DataSourceContext); - return { - async run() { - console.log(ctx.dataSource); - const dataSource = ctx.dataSource || []; - dataSource.push({ - id: uid(), - name: uid(), - }); - ctx.setDataSource([...dataSource]); - }, - }; - }, + useAction: '{{ ds.useCreateAction }}', }, }, action2: { title: '删除', 'x-component': 'Action', 'x-component-props': { - useAction() { - 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([]); - }, - }; - }, + useAction: '{{ ds.useBulkDestroyAction }}', }, }, input: { @@ -67,28 +136,12 @@ const schema: ISchema = { 'x-component': 'Table.Array', 'x-component-props': { rowKey: 'id', + pagination: false, rowSelection: { type: 'checkbox', }, - useSelectedRowKeys() { - const ctx = useContext(DataSourceContext); - 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)], - }, - ); - }, + useSelectedRowKeys: '{{ ds.useSelectedRowKeys }}', + useDataSource: '{{ ds.useDataSource }}', }, properties: { column1: { @@ -108,24 +161,18 @@ const schema: ISchema = { title: 'Actions', 'x-component': 'Table.Column', properties: { - action: { + action1: { + title: '编辑', + 'x-component': 'Action', + 'x-component-props': { + useAction: '{{ ds.useUpdateAction }}', + }, + }, + action2: { title: '删除', 'x-component': 'Action', 'x-component-props': { - useAction() { - const record = useRecord(); - const ctx = useContext(DataSourceContext); - return { - async run() { - const dataSource = ctx.dataSource || []; - ctx.setDataSource( - dataSource.filter((item) => { - return record.id !== item.id; - }), - ); - }, - }; - }, + useAction: '{{ ds.useDestroyAction }}', }, }, }, @@ -137,7 +184,7 @@ const schema: ISchema = { }, }; -const Context = observer((props) => { +const DataSourceProvider = observer((props) => { const [selectedRowKeys, setSelectedRowKeys] = useState([]); const [dataSource, setDataSource] = useState([]); const service = useRequest( @@ -174,7 +221,7 @@ const Context = observer((props) => { export default () => { return ( - + ); diff --git a/packages/database/src/magic-attribute-model.ts b/packages/database/src/magic-attribute-model.ts index bcc338d66..958d7fb9a 100644 --- a/packages/database/src/magic-attribute-model.ts +++ b/packages/database/src/magic-attribute-model.ts @@ -25,6 +25,9 @@ export class MagicAttributeModel extends Model { } return super.set(`${this.magicAttribute}.${key}`, value, options); } else { + if (!key) { + return; + } Object.keys(key).forEach((k) => { this.set(k, key[k], options); });