diff --git a/packages/client/src/collection-manager/CollectionField.tsx b/packages/client/src/collection-manager/CollectionField.tsx index 7adb00717..c29655f5f 100644 --- a/packages/client/src/collection-manager/CollectionField.tsx +++ b/packages/client/src/collection-manager/CollectionField.tsx @@ -38,7 +38,7 @@ const InternalField: React.FC = (props) => { if (!uiSchema) { return null; } - return React.createElement(component, props); + return React.createElement(component, props, props.children); }; export const CollectionField = connect((props) => { diff --git a/packages/client/src/collection-manager/CollectionManagerSchemaComponentProvider.tsx b/packages/client/src/collection-manager/CollectionManagerSchemaComponentProvider.tsx index e481aaca3..7188bf544 100644 --- a/packages/client/src/collection-manager/CollectionManagerSchemaComponentProvider.tsx +++ b/packages/client/src/collection-manager/CollectionManagerSchemaComponentProvider.tsx @@ -8,13 +8,14 @@ import { useDataSourceFromRAC } from './'; import * as hooks from './action-hooks'; -import { DataSourceProvider, ds } from './sub-table'; +import { DataSourceProvider, ds, SubFieldDataSourceProvider } from './sub-table'; export const CollectionManagerSchemaComponentProvider: React.FC = (props) => { return ( = (props) => { +export const CollectionProvider: React.FC<{ name?: string; collection?: CollectionOptions }> = (props) => { const { name, collection, children } = props; const { get } = useCollectionManager(); return ( diff --git a/packages/client/src/collection-manager/interfaces/subTable.ts b/packages/client/src/collection-manager/interfaces/subTable.ts index 6a16bc2e4..645ec3762 100644 --- a/packages/client/src/collection-manager/interfaces/subTable.ts +++ b/packages/client/src/collection-manager/interfaces/subTable.ts @@ -33,7 +33,7 @@ export const subTable: IField = { ...defaultProps, subtable: { type: 'void', - 'x-component': 'DataSourceProvider', + 'x-component': 'SubFieldDataSourceProvider', properties: { actions: { type: 'void', diff --git a/packages/client/src/collection-manager/sub-table.tsx b/packages/client/src/collection-manager/sub-table.tsx index 32e7ae133..6fb28ee44 100644 --- a/packages/client/src/collection-manager/sub-table.tsx +++ b/packages/client/src/collection-manager/sub-table.tsx @@ -1,4 +1,5 @@ import { observer, useForm } from '@formily/react'; +import { cloneDeep } from 'lodash'; import React, { createContext, useContext, useState } from 'react'; import { CollectionOptions, CollectionProvider, useActionContext, useRecord, useRequest } from '../'; import { useAPIClient } from '../api-client'; @@ -88,11 +89,12 @@ const useCreateAction = () => { const { setVisible } = useActionContext(); return { async run() { - // console.log(ctx.dataSource); + console.log('form.values', form.values); const dataSource = ctx.dataSource || []; - dataSource.push(form.values); + dataSource.push(cloneDeep(form.values)); ctx.setDataSource([...dataSource]); setVisible(false); + await form.reset(); }, }; }; @@ -159,7 +161,7 @@ export const ds = { useDestroyAction, }; -export const DataSourceProvider = observer((props) => { +export const SubFieldDataSourceProvider = observer((props) => { const [selectedRowKeys, setSelectedRowKeys] = useState([]); const [dataSource, setDataSource] = useState([]); const record = useRecord(); @@ -212,3 +214,50 @@ export const DataSourceProvider = observer((props) => { ); }); + +export const DataSourceProvider = observer((props: any) => { + const { rowKey = 'id', collection, association } = props; + const [selectedRowKeys, setSelectedRowKeys] = useState([]); + const [dataSource, setDataSource] = useState([]); + const record = useRecord(); + const api = useAPIClient(); + const resourceOf = record?.[association.targetKey || 'id']; + console.log('record', record); + const service = useRequest( + () => { + if (resourceOf) { + return api + .request({ + resource: `${collection}.${association.name}`, + resourceOf, + action: 'list', + }) + .then((res) => res.data); + } + return Promise.resolve({ + data: record?.[association.name] || [], + }); + }, + { + onSuccess(data) { + setDataSource(data?.data); + }, + }, + ); + return ( + + + {props.children} + + + ); +}); diff --git a/packages/client/src/schema-component/antd/action/Action.Link.tsx b/packages/client/src/schema-component/antd/action/Action.Link.tsx index abea6b89d..e25cdc3b3 100644 --- a/packages/client/src/schema-component/antd/action/Action.Link.tsx +++ b/packages/client/src/schema-component/antd/action/Action.Link.tsx @@ -1,11 +1,10 @@ import { observer } from '@formily/react'; import React from 'react'; -import { Link } from 'react-router-dom'; import Action from './Action'; import { ComposedAction } from './types'; export const ActionLink: ComposedAction = observer((props: any) => { - return ; + return ; }); export default ActionLink; diff --git a/packages/client/src/schema-component/antd/action/Action.tsx b/packages/client/src/schema-component/antd/action/Action.tsx index 0bf6cff6e..a52a73791 100644 --- a/packages/client/src/schema-component/antd/action/Action.tsx +++ b/packages/client/src/schema-component/antd/action/Action.tsx @@ -72,7 +72,9 @@ export const Action: ComposedAction = observer((props: any) => { { + onClick={(e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); const onOk = () => { onClick?.(e); setVisible(true); diff --git a/packages/client/src/schema-initializer/Initializers/Items/SubTableFieldInitializer.tsx b/packages/client/src/schema-initializer/Initializers/Items/SubTableFieldInitializer.tsx new file mode 100644 index 000000000..a5133fe18 --- /dev/null +++ b/packages/client/src/schema-initializer/Initializers/Items/SubTableFieldInitializer.tsx @@ -0,0 +1,171 @@ +import { ISchema } from '@formily/react'; +import { Switch } from 'antd'; +import React from 'react'; +import { SchemaInitializer } from '../../SchemaInitializer'; +import { useCurrentSchema } from '../utils'; + +export const SubTableFieldInitializer = (props) => { + const { item, insert } = props; + const { exists, remove } = useCurrentSchema( + item.schema['x-collection-field'], + 'x-collection-field', + item.find, + item.remove, + ); + return ( + { + console.log(item, exists); + if (exists) { + return remove(); + } + insert({ + ...item.schema, + properties: { + block: { + type: 'void', + 'x-component': 'DataSourceProvider', + 'x-component-props': { + rowKey: 'id', + collection: item?.field?.target, + association: { + name: item.field.name, + sourceKey: item.field.sourceKey, + targetKey: item.field.targetKey, + }, + }, + properties: { + actions: { + type: 'void', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 16, + }, + }, + properties: { + delete: { + type: 'void', + title: '{{ t("Delete") }}', + 'x-component': 'Action', + 'x-component-props': { + useAction: '{{ ds.useBulkDestroyAction }}', + confirm: { + title: "{{t('Delete record')}}", + content: "{{t('Are you sure you want to delete it?')}}", + }, + }, + }, + create: { + type: 'void', + title: '{{ t("Add new") }}', + 'x-action': 'create', + 'x-component': 'Action', + 'x-component-props': { + type: 'primary', + openMode: 'drawer', + }, + properties: { + drawer: { + type: 'void', + title: '{{ t("Add new record") }}', + 'x-component': 'Action.Container', + 'x-component-props': {}, + 'x-decorator': 'Form', + properties: { + grid: { + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'GridFormItemInitializers', + properties: {}, + }, + footer: { + type: 'void', + 'x-component': 'Action.Container.Footer', + properties: { + actions: { + type: 'void', + 'x-component': 'ActionBar', + 'x-component-props': { + layout: 'one-column', + }, + properties: { + cancel: { + title: '{{ t("Cancel") }}', + 'x-action': 'cancel', + 'x-component': 'Action', + 'x-component-props': { + useAction: '{{ cm.useCancelAction }}', + }, + }, + submit: { + title: '{{ t("Submit") }}', + 'x-action': 'submit', + 'x-component': 'Action', + 'x-component-props': { + type: 'primary', + useAction: '{{ ds.useCreateAction }}', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + [item.field.name]: { + type: 'array', + title: '{{t("Fields")}}', + 'x-component': 'Table.Array', + 'x-initializer': 'TableColumnInitializers', + 'x-component-props': { + pagination: false, + expandable: { + childrenColumnName: '__nochildren__', + }, + rowKey: 'id', + rowSelection: { + type: 'checkbox', + }, + useSelectedRowKeys: '{{ ds.useSelectedRowKeys }}', + useDataSource: '{{ ds.useDataSource }}', + // scroll: { x: '100%' }, + }, + properties: { + actions: { + type: 'void', + title: '{{ t("Actions") }}', + 'x-decorator': 'Table.Column.ActionBar', + 'x-component': 'Table.Column', + 'x-designer': 'Table.RowActionDesigner', + 'x-initializer': 'TableRecordActionInitializers', + properties: { + actions: { + type: 'void', + 'x-decorator': 'DndContext', + 'x-component': 'Space', + 'x-component-props': { + split: '|', + }, + properties: {}, + }, + }, + }, + }, + }, + }, + }, + }, + } as ISchema); + }} + > +
+ {item.title} +
+
+ ); +}; diff --git a/packages/client/src/schema-initializer/Initializers/Items/index.ts b/packages/client/src/schema-initializer/Initializers/Items/index.ts index b2e2a2bb9..46aa57269 100644 --- a/packages/client/src/schema-initializer/Initializers/Items/index.ts +++ b/packages/client/src/schema-initializer/Initializers/Items/index.ts @@ -5,5 +5,6 @@ export * from './CollectionFieldInitializer'; export * from './FormBlockInitializer'; export * from './GeneralInitializer'; export * from './MarkdownBlockInitializer'; +export * from './SubTableFieldInitializer'; export * from './TableBlockInitializer'; diff --git a/packages/client/src/schema-initializer/Initializers/TableRecordActionInitializers.tsx b/packages/client/src/schema-initializer/Initializers/TableRecordActionInitializers.tsx index 2b3eb7914..0d9336edb 100644 --- a/packages/client/src/schema-initializer/Initializers/TableRecordActionInitializers.tsx +++ b/packages/client/src/schema-initializer/Initializers/TableRecordActionInitializers.tsx @@ -12,7 +12,6 @@ export const TableRecordActionInitializers = (props: any) => { const api = useAPIClient(); const { refresh } = useDesignable(); const { t } = useTranslation(); - console.log('TableRecordActionInitializers'); return ( { grid: { type: 'void', 'x-component': 'Grid', - 'x-item-initializer': 'FormItemInitializer', + 'x-initializer': 'GridFormItemInitializers', properties: {}, }, footer: { @@ -127,7 +126,7 @@ export const TableRecordActionInitializers = (props: any) => { properties: { actions: { type: 'void', - 'x-action-initializer': 'PopupFormActionInitializer', + 'x-initializer': 'PopupFormActionInitializers', 'x-decorator': 'DndContext', 'x-component': 'ActionBar', 'x-component-props': { @@ -167,4 +166,4 @@ export const TableRecordActionInitializers = (props: any) => { ); -}; +}; \ No newline at end of file diff --git a/packages/client/src/schema-initializer/Initializers/utils.ts b/packages/client/src/schema-initializer/Initializers/utils.ts index ed0c6d00d..770842f5c 100644 --- a/packages/client/src/schema-initializer/Initializers/utils.ts +++ b/packages/client/src/schema-initializer/Initializers/utils.ts @@ -23,9 +23,9 @@ export const gridRowColWrap = (schema: ISchema) => { export const removeTableColumn = (schema, cb) => { cb(schema, { removeParentsIfNoChildren: true, - breakRemoveOn: { - 'x-component': 'ArrayTable', - }, + // breakRemoveOn: (s) => { + // return s['x-component'].startsWith('Table.'); + // }, }); }; @@ -38,6 +38,24 @@ export const removeGridFormItem = (schema, cb) => { }); }; +const findTableColumn = (schema: Schema, key: string, action: string, deepth: number = 0) => { + return schema.reduceProperties((buf, s) => { + if (s[key] === action) { + return s; + } + const c = s.reduceProperties((buf, s) => { + if (s[key] === action) { + return s; + } + return buf; + }); + if (c) { + return c; + } + return buf; + }); +}; + export const useTableColumnInitializerFields = () => { const { name, fields } = useCollection(); return ( @@ -53,6 +71,7 @@ export const useTableColumnInitializerFields = () => { 'x-component': 'CollectionField', }, component: 'CollectionFieldInitializer', + find: findTableColumn, remove: removeTableColumn, } as SchemaInitializerItemOptions; }) @@ -62,6 +81,23 @@ export const useTableColumnInitializerFields = () => { export const useFormItemInitializerFields = () => { const { name, fields } = useCollection(); return fields?.map((field) => { + if (field.interface === 'subTable') { + return { + type: 'item', + title: field?.uiSchema?.title || field.name, + component: 'SubTableFieldInitializer', + remove: removeGridFormItem, + field, + schema: { + type: 'void', + name: field.name, + 'x-designer': 'FormItem.Designer', + 'x-component': 'div', + 'x-decorator': 'FormItem', + 'x-collection-field': `${name}.${field.name}`, + }, + } as SchemaInitializerItemOptions; + } return { type: 'item', title: field?.uiSchema?.title || field.name, diff --git a/packages/client/src/schema-initializer/types.ts b/packages/client/src/schema-initializer/types.ts index 88c292257..d3b15bf46 100644 --- a/packages/client/src/schema-initializer/types.ts +++ b/packages/client/src/schema-initializer/types.ts @@ -39,7 +39,7 @@ interface ItemOptions extends ItemCommonOptions { component?: any; schema?: ISchema; remove?: (schema: Schema, cb: RemoveCallback) => void; - find?: (schema: Schema, current: string) => Schema | null | undefined; + find?: (schema: Schema, key?: string, current?: string) => Schema | null | undefined; [key: string]: any; }