feat: support add new in block for inheritance collection (#1518)
* feat: support adding inherited child collection in blocks * feat: support adding inherited child collection in blocks * refactor: create form block display on demand when using * feat: support add new in-block for inheritance collection * fix: action open mode support configuration when adding * feat: support the configuration of detail and form for inherited collections in block (#1521) * feat: support the configuration of detail and form for inherited collections in the block * fix: form and detail is not support current collection * fix: inherited blocks within blocks only display their own * style: style improve * style: style improve * refactor: detail and form block to determine whether there are inherited collection * fix: repeated display of child collection * feat: add new for inherited collection, judge the permissions * feat: support child collection add new configure * style: style improve * style: style improve * fix: child collectio repeatable configuration adding new * style: style improve * style: style improve
This commit is contained in:
		
							parent
							
								
									37998d03ad
								
							
						
					
					
						commit
						555378c342
					
				| @ -213,7 +213,7 @@ export const ACLActionProvider = (props) => { | |||||||
|   if (!actionPath && resource && schema['x-action']) { |   if (!actionPath && resource && schema['x-action']) { | ||||||
|     actionPath = `${resource}:${schema['x-action']}`; |     actionPath = `${resource}:${schema['x-action']}`; | ||||||
|   } |   } | ||||||
|   if (!actionPath.includes(':')) { |   if (!actionPath?.includes(':')) { | ||||||
|     actionPath = `${resource}:${actionPath}`; |     actionPath = `${resource}:${actionPath}`; | ||||||
|   } |   } | ||||||
|   if (!actionPath) { |   if (!actionPath) { | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ import { useField } from '@formily/react'; | |||||||
| import { Spin } from 'antd'; | import { Spin } from 'antd'; | ||||||
| import isEmpty from 'lodash/isEmpty'; | import isEmpty from 'lodash/isEmpty'; | ||||||
| import React, { createContext, useContext, useEffect, useMemo, useRef } from 'react'; | import React, { createContext, useContext, useEffect, useMemo, useRef } from 'react'; | ||||||
| import { useCollectionManager } from '../collection-manager'; | import { useCollection } from '../collection-manager'; | ||||||
| import { RecordProvider, useRecord } from '../record-provider'; | import { RecordProvider, useRecord } from '../record-provider'; | ||||||
| import { useDesignable } from '../schema-component'; | import { useDesignable } from '../schema-component'; | ||||||
| import { BlockProvider, useBlockRequestContext } from './BlockProvider'; | import { BlockProvider, useBlockRequestContext } from './BlockProvider'; | ||||||
| @ -51,14 +51,14 @@ const InternalFormBlockProvider = (props) => { | |||||||
| 
 | 
 | ||||||
| export const FormBlockProvider = (props) => { | export const FormBlockProvider = (props) => { | ||||||
|   const record = useRecord(); |   const record = useRecord(); | ||||||
|   const { __tableName } = record; |   const { collection } = props; | ||||||
|   const { getInheritCollections } = useCollectionManager(); |   const { __collection } = record; | ||||||
|   const inheritCollections = getInheritCollections(__tableName); |   const currentCollection = useCollection(); | ||||||
|   const { designable } = useDesignable(); |   const { designable } = useDesignable(); | ||||||
|   const flag = |   const detailFlag = (Object.keys(record).length > 0 && designable) || __collection === collection; | ||||||
|     !designable && __tableName && !inheritCollections.includes(props.collection) && __tableName !== props.collection; |   const createFlag = (currentCollection.name === collection && !Object.keys(record).length) || !currentCollection.name; | ||||||
|   return ( |   return ( | ||||||
|     !flag && ( |     (detailFlag || createFlag) && ( | ||||||
|       <BlockProvider {...props} block={'form'}> |       <BlockProvider {...props} block={'form'}> | ||||||
|         <InternalFormBlockProvider {...props} /> |         <InternalFormBlockProvider {...props} /> | ||||||
|       </BlockProvider> |       </BlockProvider> | ||||||
|  | |||||||
| @ -94,6 +94,17 @@ export const useSortFields = (collectionName: string) => { | |||||||
|     }); |     }); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | export const useChildrenCollections = (collectionName: string) => { | ||||||
|  |   const { getChildrenCollections } = useCollectionManager(); | ||||||
|  |   const childrenCollections = getChildrenCollections(collectionName); | ||||||
|  |   return childrenCollections.map((collection: any) => { | ||||||
|  |     return { | ||||||
|  |       value: collection.name, | ||||||
|  |       label: collection?.title || collection.name, | ||||||
|  |     }; | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| export const useCollectionFilterOptions = (collectionName: string) => { | export const useCollectionFilterOptions = (collectionName: string) => { | ||||||
|   const { getCollectionFields, getInterface } = useCollectionManager(); |   const { getCollectionFields, getInterface } = useCollectionManager(); | ||||||
|   const fields = getCollectionFields(collectionName); |   const fields = getCollectionFields(collectionName); | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| import { clone } from '@formily/shared'; | import { clone } from '@formily/shared'; | ||||||
| import { CascaderProps } from 'antd'; | import { CascaderProps } from 'antd'; | ||||||
| import { reduce, unionBy, uniq } from 'lodash'; | import { reduce, unionBy, uniq, uniqBy } from 'lodash'; | ||||||
| import { useContext } from 'react'; | import { useContext } from 'react'; | ||||||
| import { useCompile } from '../../schema-component'; | import { useCompile } from '../../schema-component'; | ||||||
| import { CollectionManagerContext } from '../context'; | import { CollectionManagerContext } from '../context'; | ||||||
| @ -8,7 +8,7 @@ import { CollectionFieldOptions } from '../types'; | |||||||
| 
 | 
 | ||||||
| export const useCollectionManager = () => { | export const useCollectionManager = () => { | ||||||
|   const { refreshCM, service, interfaces, collections, templates } = useContext(CollectionManagerContext); |   const { refreshCM, service, interfaces, collections, templates } = useContext(CollectionManagerContext); | ||||||
|   const compile = useCompile() |   const compile = useCompile(); | ||||||
|   const getInheritedFields = (name) => { |   const getInheritedFields = (name) => { | ||||||
|     const inheritKeys = getInheritCollections(name); |     const inheritKeys = getInheritCollections(name); | ||||||
|     const inheritedFields = reduce( |     const inheritedFields = reduce( | ||||||
| @ -72,7 +72,7 @@ export const useCollectionManager = () => { | |||||||
|         children.push(v); |         children.push(v); | ||||||
|         return getChildren(collectionKey); |         return getChildren(collectionKey); | ||||||
|       }); |       }); | ||||||
|       return children; |       return uniqBy(children, 'key'); | ||||||
|     }; |     }; | ||||||
|     return getChildren(name); |     return getChildren(name); | ||||||
|   }; |   }; | ||||||
| @ -81,14 +81,17 @@ export const useCollectionManager = () => { | |||||||
|     return collection?.fields || []; |     return collection?.fields || []; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
| 
 |   const getCollectionFieldsOptions = ( | ||||||
|   const getCollectionFieldsOptions = (collectionName: string, type: string | string[] = 'string', opts?: { |     collectionName: string, | ||||||
|  |     type: string | string[] = 'string', | ||||||
|  |     opts?: { | ||||||
|       /** |       /** | ||||||
|        * 为 true 时允许查询所有关联字段 |        * 为 true 时允许查询所有关联字段 | ||||||
|        * 为 Array<string> 时仅允许查询指定的关联字段 |        * 为 Array<string> 时仅允许查询指定的关联字段 | ||||||
|        */ |        */ | ||||||
|       association?: boolean | string[]; |       association?: boolean | string[]; | ||||||
|   }) => { |     }, | ||||||
|  |   ) => { | ||||||
|     const { association = false } = opts || {}; |     const { association = false } = opts || {}; | ||||||
|     if (typeof type === 'string') { |     if (typeof type === 'string') { | ||||||
|       type = [type]; |       type = [type]; | ||||||
| @ -98,9 +101,10 @@ export const useCollectionManager = () => { | |||||||
|       ?.filter( |       ?.filter( | ||||||
|         (field) => |         (field) => | ||||||
|           field.interface && |           field.interface && | ||||||
|           (type.includes(field.type) || (association && field.target && field.target !== collectionName && |           (type.includes(field.type) || | ||||||
|             Array.isArray(association) ? association.includes(field.interface) : false |             (association && field.target && field.target !== collectionName && Array.isArray(association) | ||||||
|           )), |               ? association.includes(field.interface) | ||||||
|  |               : false)), | ||||||
|       ) |       ) | ||||||
|       ?.map((field) => { |       ?.map((field) => { | ||||||
|         const result: CascaderProps<any>['options'][0] = { |         const result: CascaderProps<any>['options'][0] = { | ||||||
| @ -110,13 +114,13 @@ export const useCollectionManager = () => { | |||||||
|         if (association && field.target) { |         if (association && field.target) { | ||||||
|           result.children = getCollectionFieldsOptions(field.target, type, opts); |           result.children = getCollectionFieldsOptions(field.target, type, opts); | ||||||
|           if (!result.children?.length) { |           if (!result.children?.length) { | ||||||
|             return null |             return null; | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|         return result; |         return result; | ||||||
|       }) |       }) | ||||||
|       // 过滤 map 产生为 null 的数据
 |       // 过滤 map 产生为 null 的数据
 | ||||||
|       .filter(Boolean) |       .filter(Boolean); | ||||||
| 
 | 
 | ||||||
|     return options; |     return options; | ||||||
|   }; |   }; | ||||||
|  | |||||||
| @ -5,7 +5,7 @@ import React, { useEffect, useState } from 'react'; | |||||||
| import { useTranslation } from 'react-i18next'; | import { useTranslation } from 'react-i18next'; | ||||||
| import { useDesignable } from '../..'; | import { useDesignable } from '../..'; | ||||||
| import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings'; | import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings'; | ||||||
| import { useCollection } from '../../../collection-manager'; | import { useCollection, useCollectionManager } from '../../../collection-manager'; | ||||||
| import { useRecord } from '../../../record-provider'; | import { useRecord } from '../../../record-provider'; | ||||||
| import { useFormBlockContext } from '../../../block-provider/FormBlockProvider'; | import { useFormBlockContext } from '../../../block-provider/FormBlockProvider'; | ||||||
| 
 | 
 | ||||||
| @ -41,6 +41,7 @@ export const ActionDesigner = (props) => { | |||||||
|   const field = useField(); |   const field = useField(); | ||||||
|   const fieldSchema = useFieldSchema(); |   const fieldSchema = useFieldSchema(); | ||||||
|   const { name } = useCollection(); |   const { name } = useCollection(); | ||||||
|  |   const { getChildrenCollections } = useCollectionManager(); | ||||||
|   const { dn } = useDesignable(); |   const { dn } = useDesignable(); | ||||||
|   const { t } = useTranslation(); |   const { t } = useTranslation(); | ||||||
|   const isPopupAction = ['create', 'update', 'view', 'customize:popup'].includes(fieldSchema['x-action'] || ''); |   const isPopupAction = ['create', 'update', 'view', 'customize:popup'].includes(fieldSchema['x-action'] || ''); | ||||||
| @ -48,7 +49,7 @@ export const ActionDesigner = (props) => { | |||||||
|   const [initialSchema, setInitialSchema] = useState<ISchema>(); |   const [initialSchema, setInitialSchema] = useState<ISchema>(); | ||||||
|   const actionType = fieldSchema['x-action'] ?? ''; |   const actionType = fieldSchema['x-action'] ?? ''; | ||||||
|   const isLinkageAction = Object.keys(useFormBlockContext()).length > 0 && Object.keys(useRecord()).length > 0; |   const isLinkageAction = Object.keys(useFormBlockContext()).length > 0 && Object.keys(useRecord()).length > 0; | ||||||
| 
 |   const isChildCollectionAction = getChildrenCollections(name).length > 0 && fieldSchema['x-action'] === 'create'; | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     const schemaUid = uid(); |     const schemaUid = uid(); | ||||||
|     const schema: ISchema = { |     const schema: ISchema = { | ||||||
| @ -379,6 +380,8 @@ export const ActionDesigner = (props) => { | |||||||
|             }} |             }} | ||||||
|           /> |           /> | ||||||
|         )} |         )} | ||||||
|  | 
 | ||||||
|  |         {isChildCollectionAction && <SchemaSettings.EnableChildCollections collectionName={name} />} | ||||||
|         <SchemaSettings.Divider /> |         <SchemaSettings.Divider /> | ||||||
|         <SchemaSettings.Remove |         <SchemaSettings.Remove | ||||||
|           removeParentsIfNoChildren |           removeParentsIfNoChildren | ||||||
|  | |||||||
| @ -6,7 +6,7 @@ import React, { useState, useEffect } from 'react'; | |||||||
| import { useActionContext } from '../..'; | import { useActionContext } from '../..'; | ||||||
| import { Icon } from '../../../icon'; | import { Icon } from '../../../icon'; | ||||||
| import { SortableItem } from '../../common'; | import { SortableItem } from '../../common'; | ||||||
| import { useCompile, useDesigner } from '../../hooks'; | import { useCompile, useComponent, useDesigner } from '../../hooks'; | ||||||
| import { useProps } from '../../hooks/useProps'; | import { useProps } from '../../hooks/useProps'; | ||||||
| import { useRecord } from '../../../record-provider'; | import { useRecord } from '../../../record-provider'; | ||||||
| import ActionContainer from './Action.Container'; | import ActionContainer from './Action.Container'; | ||||||
| @ -96,7 +96,8 @@ export const Action: ComposedAction = observer((props: any) => { | |||||||
|   const disabled = form.disabled || field.disabled; |   const disabled = form.disabled || field.disabled; | ||||||
|   const openSize = fieldSchema?.['x-component-props']?.['openSize']; |   const openSize = fieldSchema?.['x-component-props']?.['openSize']; | ||||||
|   const linkageRules = fieldSchema?.['x-linkage-rules'] || []; |   const linkageRules = fieldSchema?.['x-linkage-rules'] || []; | ||||||
|   const { designable } = useDesignable(); |   const { designable, } = useDesignable(); | ||||||
|  |   const tarComponent=useComponent(component)||component; | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     linkageRules.map((v) => { |     linkageRules.map((v) => { | ||||||
|       return v.actions?.map((h) => { |       return v.actions?.map((h) => { | ||||||
| @ -130,7 +131,7 @@ export const Action: ComposedAction = observer((props: any) => { | |||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       }} |       }} | ||||||
|       component={component || Button} |       component={tarComponent || Button} | ||||||
|       className={classnames(className, actionDesignerCss)} |       className={classnames(className, actionDesignerCss)} | ||||||
|     > |     > | ||||||
|       {title || compile(fieldSchema.title)} |       {title || compile(fieldSchema.title)} | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| import { Schema, useFieldSchema } from '@formily/react'; | import { Schema, useFieldSchema } from '@formily/react'; | ||||||
| import React from 'react'; | import React from 'react'; | ||||||
| import { useTranslation } from 'react-i18next'; | import { useTranslation } from 'react-i18next'; | ||||||
| import { SchemaInitializer, useCollection, useCollectionManager } from '../..'; | import { SchemaInitializer, useCollection, useCollectionManager, SchemaInitializerItemOptions } from '../..'; | ||||||
| import { gridRowColWrap } from '../utils'; | import { gridRowColWrap } from '../utils'; | ||||||
| 
 | 
 | ||||||
| const recursiveParent = (schema: Schema) => { | const recursiveParent = (schema: Schema) => { | ||||||
| @ -104,14 +104,22 @@ const useRelationFields = () => { | |||||||
|   return relationFields; |   return relationFields; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const useInheritFields = (props) => { | const useDetailCollections = (props) => { | ||||||
|   const { actionInitializers } = props; |   const { actionInitializers, childrenCollections, collection } = props; | ||||||
|   const collection = useCollection(); |   const detailCollections = [ | ||||||
|   const { getChildrenCollections } = useCollectionManager(); |     { | ||||||
|   const childrenCollections = getChildrenCollections(collection.name); |       key: collection.name, | ||||||
|   return childrenCollections.map((c) => { |       type: 'item', | ||||||
|  |       title: collection?.title || collection.name, | ||||||
|  |       component: 'RecordReadPrettyFormBlockInitializer', | ||||||
|  |       icon: false, | ||||||
|  |       targetCollection: collection, | ||||||
|  |       actionInitializers, | ||||||
|  |     }, | ||||||
|  |   ].concat( | ||||||
|  |     childrenCollections.map((c) => { | ||||||
|       return { |       return { | ||||||
|       key: c.key, |         key: c.name, | ||||||
|         type: 'item', |         type: 'item', | ||||||
|         title: c?.title || c.name, |         title: c?.title || c.name, | ||||||
|         component: 'RecordReadPrettyFormBlockInitializer', |         component: 'RecordReadPrettyFormBlockInitializer', | ||||||
| @ -119,12 +127,47 @@ const useInheritFields = (props) => { | |||||||
|         targetCollection: c, |         targetCollection: c, | ||||||
|         actionInitializers, |         actionInitializers, | ||||||
|       }; |       }; | ||||||
|   }); |     }), | ||||||
|  |   ) as SchemaInitializerItemOptions[]; | ||||||
|  |   return detailCollections; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const useFormCollections = (props) => { | ||||||
|  |   const { actionInitializers, childrenCollections, collection } = props; | ||||||
|  |   const formCollections = [ | ||||||
|  |     { | ||||||
|  |       key: collection.name, | ||||||
|  |       type: 'item', | ||||||
|  |       title: collection?.title || collection.name, | ||||||
|  |       component: 'RecordFormBlockInitializer', | ||||||
|  |       icon: false, | ||||||
|  |       targetCollection: collection, | ||||||
|  |       actionInitializers, | ||||||
|  |     }, | ||||||
|  |   ].concat( | ||||||
|  |     childrenCollections.map((c) => { | ||||||
|  |       return { | ||||||
|  |         key: c.name, | ||||||
|  |         type: 'item', | ||||||
|  |         title: c?.title || c.name, | ||||||
|  |         component: 'RecordFormBlockInitializer', | ||||||
|  |         icon: false, | ||||||
|  |         targetCollection: c, | ||||||
|  |         actionInitializers, | ||||||
|  |       }; | ||||||
|  |     }), | ||||||
|  |   ) as SchemaInitializerItemOptions[]; | ||||||
|  | 
 | ||||||
|  |   return formCollections; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export const RecordBlockInitializers = (props: any) => { | export const RecordBlockInitializers = (props: any) => { | ||||||
|   const { t } = useTranslation(); |   const { t } = useTranslation(); | ||||||
|   const { insertPosition, component, actionInitializers } = props; |   const { insertPosition, component, actionInitializers } = props; | ||||||
|  |   const collection = useCollection(); | ||||||
|  |   const { getChildrenCollections } = useCollectionManager(); | ||||||
|  |   const childrenCollections = getChildrenCollections(collection.name); | ||||||
|  |   const hasChildCollection = childrenCollections?.length > 0; | ||||||
|   return ( |   return ( | ||||||
|     <SchemaInitializer.Button |     <SchemaInitializer.Button | ||||||
|       wrap={gridRowColWrap} |       wrap={gridRowColWrap} | ||||||
| @ -136,7 +179,22 @@ export const RecordBlockInitializers = (props: any) => { | |||||||
|         { |         { | ||||||
|           type: 'itemGroup', |           type: 'itemGroup', | ||||||
|           title: '{{t("Current record blocks")}}', |           title: '{{t("Current record blocks")}}', | ||||||
|           children: [ |           children: hasChildCollection | ||||||
|  |             ? [ | ||||||
|  |                 { | ||||||
|  |                   key: 'details', | ||||||
|  |                   type: 'subMenu', | ||||||
|  |                   title: '{{t("Details")}}', | ||||||
|  |                   children: useDetailCollections({ ...props, childrenCollections, collection }), | ||||||
|  |                 }, | ||||||
|  |                 { | ||||||
|  |                   key: 'form', | ||||||
|  |                   type: 'subMenu', | ||||||
|  |                   title: '{{t("Form")}}', | ||||||
|  |                   children: useFormCollections({ ...props, childrenCollections, collection }), | ||||||
|  |                 }, | ||||||
|  |               ] | ||||||
|  |             : [ | ||||||
|                 { |                 { | ||||||
|                   key: 'details', |                   key: 'details', | ||||||
|                   type: 'item', |                   type: 'item', | ||||||
| @ -152,11 +210,6 @@ export const RecordBlockInitializers = (props: any) => { | |||||||
|                 }, |                 }, | ||||||
|               ], |               ], | ||||||
|         }, |         }, | ||||||
|         { |  | ||||||
|           type: 'itemGroup', |  | ||||||
|           title: '{{t("Children collection blocks")}}', |  | ||||||
|           children: useInheritFields(props), |  | ||||||
|         }, |  | ||||||
|         { |         { | ||||||
|           type: 'itemGroup', |           type: 'itemGroup', | ||||||
|           title: '{{t("Relationship blocks")}}', |           title: '{{t("Relationship blocks")}}', | ||||||
|  | |||||||
| @ -0,0 +1,166 @@ | |||||||
|  | import React, { useState } from 'react'; | ||||||
|  | import { DownOutlined, PlusOutlined } from '@ant-design/icons'; | ||||||
|  | import { RecursionField, useFieldSchema, useField } from '@formily/react'; | ||||||
|  | import { Dropdown, Menu, Button } from 'antd'; | ||||||
|  | import { css } from '@emotion/css'; | ||||||
|  | import { observer } from '@formily/react'; | ||||||
|  | import { useCollectionManager, useCollection, CollectionProvider } from '../../collection-manager'; | ||||||
|  | import { ActionContext, useCompile, useActionContext } from '../../schema-component'; | ||||||
|  | import { useRecordPkValue, useACLRolesCheck } from '../../acl/ACLProvider'; | ||||||
|  | 
 | ||||||
|  | export const actionDesignerCss = css` | ||||||
|  |   position: relative; | ||||||
|  |   &:hover { | ||||||
|  |     .general-schema-designer { | ||||||
|  |       display: block; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   .general-schema-designer { | ||||||
|  |     position: absolute; | ||||||
|  |     z-index: 999; | ||||||
|  |     top: 0; | ||||||
|  |     bottom: 0; | ||||||
|  |     left: 0; | ||||||
|  |     right: 0; | ||||||
|  |     display: none; | ||||||
|  |     background: rgba(241, 139, 98, 0.06); | ||||||
|  |     border: 0; | ||||||
|  |     top: 0; | ||||||
|  |     bottom: 0; | ||||||
|  |     left: 0; | ||||||
|  |     right: 0; | ||||||
|  |     pointer-events: none; | ||||||
|  |     > .general-schema-designer-icons { | ||||||
|  |       position: absolute; | ||||||
|  |       right: 2px; | ||||||
|  |       top: 2px; | ||||||
|  |       line-height: 16px; | ||||||
|  |       pointer-events: all; | ||||||
|  |       .ant-space-item { | ||||||
|  |         background-color: #f18b62; | ||||||
|  |         color: #fff; | ||||||
|  |         line-height: 16px; | ||||||
|  |         width: 16px; | ||||||
|  |         padding-left: 1px; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | `;
 | ||||||
|  | 
 | ||||||
|  | const actionAclCheck = (actionPath) => { | ||||||
|  |   const { data, inResources, getResourceActionParams, getStrategyActionParams } = useACLRolesCheck(); | ||||||
|  |   const recordPkValue = useRecordPkValue(); | ||||||
|  |   const collection = useCollection(); | ||||||
|  |   const resource = collection.resource; | ||||||
|  |   const parseAction = (actionPath: string, options: any = {}) => { | ||||||
|  |     const [resourceName] = actionPath.split(':'); | ||||||
|  |     if (data?.allowAll) { | ||||||
|  |       return {}; | ||||||
|  |     } | ||||||
|  |     if (inResources(resourceName)) { | ||||||
|  |       return getResourceActionParams(actionPath); | ||||||
|  |     } | ||||||
|  |     return getStrategyActionParams(actionPath); | ||||||
|  |   }; | ||||||
|  |   if (!actionPath && resource) { | ||||||
|  |     actionPath = `${resource}:create}`; | ||||||
|  |   } | ||||||
|  |   if (!actionPath?.includes(':')) { | ||||||
|  |     actionPath = `${resource}:${actionPath}`; | ||||||
|  |   } | ||||||
|  |   if (!actionPath) { | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |   const params = parseAction(actionPath, { recordPkValue }); | ||||||
|  |   if (!params) { | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |   return true; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export const CreateRecordAction = observer((props) => { | ||||||
|  |   const [visible, setVisible] = useState(false); | ||||||
|  |   const collection = useCollection(); | ||||||
|  |   const fieldSchema = useFieldSchema(); | ||||||
|  |   const enableChildren = fieldSchema['x-enable-children'] || []; | ||||||
|  |   const field = useField(); | ||||||
|  |   const { getChildrenCollections } = useCollectionManager(); | ||||||
|  |   const totalChildCollections = getChildrenCollections(collection.name); | ||||||
|  |   const inheritsCollections = enableChildren | ||||||
|  |     .map((k) => { | ||||||
|  |       const childCollection = totalChildCollections.find((j) => j.name === k.collection); | ||||||
|  |       return { | ||||||
|  |         ...childCollection, | ||||||
|  |         title: k.title||childCollection.title, | ||||||
|  |       }; | ||||||
|  |     }) | ||||||
|  |     .filter((v) => { | ||||||
|  |       return actionAclCheck(`${v.name}:create`); | ||||||
|  |     }); | ||||||
|  |   const [currentCollection, setCurrentCollection] = useState(collection.name); | ||||||
|  |   const ctx = useActionContext(); | ||||||
|  |   const compile = useCompile(); | ||||||
|  |   const menu = ( | ||||||
|  |     <Menu> | ||||||
|  |       {inheritsCollections.map((option) => { | ||||||
|  |         return ( | ||||||
|  |           <Menu.Item | ||||||
|  |             key={option.name} | ||||||
|  |             onClick={(info) => { | ||||||
|  |               setVisible(true); | ||||||
|  |               setCurrentCollection(option.name); | ||||||
|  |             }} | ||||||
|  |           > | ||||||
|  |             {compile(option.title)} | ||||||
|  |           </Menu.Item> | ||||||
|  |         ); | ||||||
|  |       })} | ||||||
|  |     </Menu> | ||||||
|  |   ); | ||||||
|  |   return ( | ||||||
|  |     <div className={actionDesignerCss}> | ||||||
|  |       <ActionContext.Provider value={{ ...ctx, visible, setVisible }}> | ||||||
|  |         {inheritsCollections?.length > 0 ? ( | ||||||
|  |           <Dropdown.Button | ||||||
|  |             type="primary" | ||||||
|  |             icon={<DownOutlined />} | ||||||
|  |             buttonsRender={([leftButton, rightButton]) => [ | ||||||
|  |               leftButton, | ||||||
|  |               React.cloneElement(rightButton as React.ReactElement<any, string>, { loading: false }), | ||||||
|  |             ]} | ||||||
|  |             overlay={menu} | ||||||
|  |             onClick={(info) => { | ||||||
|  |               setVisible(true); | ||||||
|  |               setCurrentCollection(collection.name); | ||||||
|  |             }} | ||||||
|  |           > | ||||||
|  |             <PlusOutlined /> | ||||||
|  |             {props.children} | ||||||
|  |           </Dropdown.Button> | ||||||
|  |         ) : ( | ||||||
|  |           <Button | ||||||
|  |             type={'primary'} | ||||||
|  |             icon={<PlusOutlined />} | ||||||
|  |             onClick={(info) => { | ||||||
|  |               setVisible(true); | ||||||
|  |               setCurrentCollection(collection.name); | ||||||
|  |             }} | ||||||
|  |           > | ||||||
|  |             {props.children} | ||||||
|  |           </Button> | ||||||
|  |         )} | ||||||
|  |         <CollectionProvider name={currentCollection}> | ||||||
|  |           <RecursionField schema={fieldSchema} basePath={field.address} onlyRenderProperties /> | ||||||
|  |         </CollectionProvider> | ||||||
|  |       </ActionContext.Provider> | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | // export const CreateRecordAction = observer((props: any) => {
 | ||||||
|  | //   return (
 | ||||||
|  | //     <Action {...props} component={CreateAction}>
 | ||||||
|  | //       {props.children}
 | ||||||
|  | //     </Action>
 | ||||||
|  | //   );
 | ||||||
|  | // });
 | ||||||
| @ -1,2 +1,3 @@ | |||||||
| export * from './assigned-field'; | export * from './assigned-field'; | ||||||
| export * from './BulkEditField'; | export * from './BulkEditField'; | ||||||
|  | export * from './CreateRecordAction' | ||||||
| @ -4,14 +4,14 @@ import { ActionInitializer } from './ActionInitializer'; | |||||||
| export const CreateActionInitializer = (props) => { | export const CreateActionInitializer = (props) => { | ||||||
|   const schema = { |   const schema = { | ||||||
|     type: 'void', |     type: 'void', | ||||||
|     title: '{{ t("Add new") }}', |  | ||||||
|     'x-action': 'create', |     'x-action': 'create', | ||||||
|  |     title: "{{t('Add new')}}", | ||||||
|     'x-designer': 'Action.Designer', |     'x-designer': 'Action.Designer', | ||||||
|     'x-component': 'Action', |     'x-component': 'Action', | ||||||
|  |     'x-decorator': 'ACLActionProvider', | ||||||
|     'x-component-props': { |     'x-component-props': { | ||||||
|       icon: 'PlusOutlined', |  | ||||||
|       openMode: 'drawer', |       openMode: 'drawer', | ||||||
|       type: 'primary', |       component: 'CreateRecordAction', | ||||||
|     }, |     }, | ||||||
|     properties: { |     properties: { | ||||||
|       drawer: { |       drawer: { | ||||||
|  | |||||||
| @ -1,17 +1,16 @@ | |||||||
| import React from "react"; | import React from 'react'; | ||||||
| import { FormOutlined } from '@ant-design/icons'; | import { FormOutlined } from '@ant-design/icons'; | ||||||
| import { useBlockAssociationContext } from "../../block-provider"; | import { useBlockAssociationContext } from '../../block-provider'; | ||||||
| import { useCollection } from "../../collection-manager"; | import { useCollection } from '../../collection-manager'; | ||||||
| import { useSchemaTemplateManager } from "../../schema-templates"; | import { useSchemaTemplateManager } from '../../schema-templates'; | ||||||
| import { SchemaInitializer } from "../SchemaInitializer"; | import { SchemaInitializer } from '../SchemaInitializer'; | ||||||
| import { createFormBlockSchema, useRecordCollectionDataSourceItems } from "../utils"; | import { createFormBlockSchema, useRecordCollectionDataSourceItems } from '../utils'; | ||||||
| 
 | 
 | ||||||
| export const RecordFormBlockInitializer = (props) => { | export const RecordFormBlockInitializer = (props) => { | ||||||
|     const { onCreateBlockSchema, componentType, createBlockSchema, insert, ...others } = props; |   const { onCreateBlockSchema, componentType, createBlockSchema, insert, targetCollection, ...others } = props; | ||||||
|   const { getTemplateSchemaByMode } = useSchemaTemplateManager(); |   const { getTemplateSchemaByMode } = useSchemaTemplateManager(); | ||||||
|     const collection = useCollection(); |   const collection = targetCollection || useCollection(); | ||||||
|   const association = useBlockAssociationContext(); |   const association = useBlockAssociationContext(); | ||||||
|     console.log('RecordFormBlockInitializer', collection, association); |  | ||||||
|   return ( |   return ( | ||||||
|     <SchemaInitializer.Item |     <SchemaInitializer.Item | ||||||
|       icon={<FormOutlined />} |       icon={<FormOutlined />} | ||||||
|  | |||||||
| @ -0,0 +1,106 @@ | |||||||
|  | import { observer, useForm } from '@formily/react'; | ||||||
|  | import React from 'react'; | ||||||
|  | import { action } from '@formily/reactive'; | ||||||
|  | import { SchemaComponent, useCompile } from '../../schema-component'; | ||||||
|  | import { useCollectionManager } from '../../collection-manager'; | ||||||
|  | 
 | ||||||
|  | export const EnableChildCollections = observer((props: any) => { | ||||||
|  |   const { useProps } = props; | ||||||
|  |   const { defaultValues, collectionName } = useProps(); | ||||||
|  |   const form = useForm(); | ||||||
|  |   const compile = useCompile(); | ||||||
|  |   const { getChildrenCollections } = useCollectionManager(); | ||||||
|  |   const childrenCollections = getChildrenCollections(collectionName); | ||||||
|  | 
 | ||||||
|  |   const useAsyncDataSource = (service: any) => { | ||||||
|  |     return (field: any, options?: any) => { | ||||||
|  |       field.loading = true; | ||||||
|  |       service(field, options).then( | ||||||
|  |         action.bound((data: any) => { | ||||||
|  |           field.dataSource = data; | ||||||
|  |           field.loading = false; | ||||||
|  |           if (field.initialValue) { | ||||||
|  |             field.disabled = true; | ||||||
|  |           } | ||||||
|  |         }), | ||||||
|  |       ); | ||||||
|  |     }; | ||||||
|  |   }; | ||||||
|  |   const loadData = async (field) => { | ||||||
|  |     const { childrenCollections: childCollections } = form.values?.enableChildren; | ||||||
|  |     return childrenCollections | ||||||
|  |       .filter((v) => { | ||||||
|  |         return !childCollections.find((k) => k.collection === v.name) || field.initialValue || v.name === field.value; | ||||||
|  |       }) | ||||||
|  |       ?.map((collection: any) => ({ | ||||||
|  |         label: compile(collection.title), | ||||||
|  |         value: collection.name, | ||||||
|  |       })); | ||||||
|  |   }; | ||||||
|  |   return ( | ||||||
|  |     <SchemaComponent | ||||||
|  |       schema={{ | ||||||
|  |         type: 'object', | ||||||
|  |         properties: { | ||||||
|  |           childrenCollections: { | ||||||
|  |             type: 'array', | ||||||
|  |             default: defaultValues, | ||||||
|  |             'x-component': 'ArrayItems', | ||||||
|  |             'x-decorator': 'FormItem', | ||||||
|  |             items: { | ||||||
|  |               type: 'object', | ||||||
|  |               properties: { | ||||||
|  |                 space: { | ||||||
|  |                   type: 'void', | ||||||
|  |                   'x-component': 'Space', | ||||||
|  |                   properties: { | ||||||
|  |                     sort: { | ||||||
|  |                       type: 'void', | ||||||
|  |                       'x-decorator': 'FormItem', | ||||||
|  |                       'x-component': 'ArrayItems.SortHandle', | ||||||
|  |                     }, | ||||||
|  |                     collection: { | ||||||
|  |                       type: 'string', | ||||||
|  |                       'x-decorator': 'FormItem', | ||||||
|  |                       required: true, | ||||||
|  |                       'x-component': 'Select', | ||||||
|  |                       'x-component-props': { | ||||||
|  |                         style: { | ||||||
|  |                           width: 260, | ||||||
|  |                         }, | ||||||
|  |                       }, | ||||||
|  |                       'x-reactions': ['{{useAsyncDataSource(loadData)}}'], | ||||||
|  |                     }, | ||||||
|  |                     title: { | ||||||
|  |                       type: 'string', | ||||||
|  |                       'x-decorator': 'FormItem', | ||||||
|  |                       'x-component': 'Input', | ||||||
|  |                       'x-component-props': { | ||||||
|  |                         style: { | ||||||
|  |                           width: 235, | ||||||
|  |                         }, | ||||||
|  |                       }, | ||||||
|  |                     }, | ||||||
|  |                     remove: { | ||||||
|  |                       type: 'void', | ||||||
|  |                       'x-decorator': 'FormItem', | ||||||
|  |                       'x-component': 'ArrayItems.Remove', | ||||||
|  |                     }, | ||||||
|  |                   }, | ||||||
|  |                 }, | ||||||
|  |               }, | ||||||
|  |             }, | ||||||
|  |             properties: { | ||||||
|  |               add: { | ||||||
|  |                 type: 'void', | ||||||
|  |                 title: '{{ t("Add collection") }}', | ||||||
|  |                 'x-component': 'ArrayItems.Addition', | ||||||
|  |               }, | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |       }} | ||||||
|  |       scope={{ useAsyncDataSource, loadData }} | ||||||
|  |     /> | ||||||
|  |   ); | ||||||
|  | }); | ||||||
| @ -1,5 +1,5 @@ | |||||||
| import { css } from '@emotion/css'; | import { css } from '@emotion/css'; | ||||||
| import { FormDialog, FormItem, FormLayout, Input, ArrayCollapse } from '@formily/antd'; | import { FormDialog, FormItem, FormLayout, Input, ArrayCollapse, ArrayItems } from '@formily/antd'; | ||||||
| import { createForm, Field, GeneralField } from '@formily/core'; | import { createForm, Field, GeneralField } from '@formily/core'; | ||||||
| import { ISchema, Schema, SchemaOptionsContext, useField, useFieldSchema, useForm } from '@formily/react'; | import { ISchema, Schema, SchemaOptionsContext, useField, useFieldSchema, useForm } from '@formily/react'; | ||||||
| import _ from 'lodash'; | import _ from 'lodash'; | ||||||
| @ -43,6 +43,7 @@ import { useSchemaTemplateManager } from '../schema-templates'; | |||||||
| import { useBlockTemplateContext } from '../schema-templates/BlockTemplate'; | import { useBlockTemplateContext } from '../schema-templates/BlockTemplate'; | ||||||
| import { FormLinkageRules } from './LinkageRules'; | import { FormLinkageRules } from './LinkageRules'; | ||||||
| import { useLinkageCollectionFieldOptions } from './LinkageRules/action-hooks'; | import { useLinkageCollectionFieldOptions } from './LinkageRules/action-hooks'; | ||||||
|  | import { EnableChildCollections } from './EnableChildCollections'; | ||||||
| 
 | 
 | ||||||
| interface SchemaSettingsProps { | interface SchemaSettingsProps { | ||||||
|   title?: any; |   title?: any; | ||||||
| @ -770,3 +771,52 @@ SchemaSettings.LinkageRules = (props) => { | |||||||
|     /> |     /> | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
|  | 
 | ||||||
|  | SchemaSettings.EnableChildCollections = (props) => { | ||||||
|  |   const { collectionName } = props; | ||||||
|  |   const fieldSchema = useFieldSchema(); | ||||||
|  |   const { dn } = useDesignable(); | ||||||
|  |   const { t } = useTranslation(); | ||||||
|  |   return ( | ||||||
|  |     <SchemaSettings.ModalItem | ||||||
|  |       title={t('Enable child collections')} | ||||||
|  |       components={{ ArrayItems, FormLayout }} | ||||||
|  |       width={600} | ||||||
|  |       schema={ | ||||||
|  |         { | ||||||
|  |           type: 'object', | ||||||
|  |           title: t('Enable child collections'), | ||||||
|  |           properties: { | ||||||
|  |             enableChildren: { | ||||||
|  |               'x-component': EnableChildCollections, | ||||||
|  |               'x-component-props': { | ||||||
|  |                 useProps: () => { | ||||||
|  |                   return { | ||||||
|  |                     defaultValues: fieldSchema?.['x-enable-children'], | ||||||
|  |                     collectionName, | ||||||
|  |                   }; | ||||||
|  |                 }, | ||||||
|  |               }, | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |         } as ISchema | ||||||
|  |       } | ||||||
|  |       onSubmit={(v) => { | ||||||
|  |         const enableChildren = []; | ||||||
|  |         for (const item of v.enableChildren.childrenCollections) { | ||||||
|  |           enableChildren.push(_.pickBy(item, _.identity)); | ||||||
|  |         } | ||||||
|  |         const uid = fieldSchema['x-uid']; | ||||||
|  |         const schema = { | ||||||
|  |           ['x-uid']: uid, | ||||||
|  |         }; | ||||||
|  |         fieldSchema['x-enable-children'] = enableChildren; | ||||||
|  |         schema['x-enable-children'] = enableChildren; | ||||||
|  |         dn.emit('patch', { | ||||||
|  |           schema, | ||||||
|  |         }); | ||||||
|  |         dn.refresh(); | ||||||
|  |       }} | ||||||
|  |     /> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user