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',
|
||||||
* 为 true 时允许查询所有关联字段
|
opts?: {
|
||||||
* 为 Array<string> 时仅允许查询指定的关联字段
|
/**
|
||||||
*/
|
* 为 true 时允许查询所有关联字段
|
||||||
association?: boolean | string[];
|
* 为 Array<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,27 +104,70 @@ 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) => {
|
|
||||||
return {
|
|
||||||
key: c.key,
|
|
||||||
type: 'item',
|
type: 'item',
|
||||||
title: c?.title || c.name,
|
title: collection?.title || collection.name,
|
||||||
component: 'RecordReadPrettyFormBlockInitializer',
|
component: 'RecordReadPrettyFormBlockInitializer',
|
||||||
icon: false,
|
icon: false,
|
||||||
targetCollection: c,
|
targetCollection: collection,
|
||||||
actionInitializers,
|
actionInitializers,
|
||||||
};
|
},
|
||||||
});
|
].concat(
|
||||||
|
childrenCollections.map((c) => {
|
||||||
|
return {
|
||||||
|
key: c.name,
|
||||||
|
type: 'item',
|
||||||
|
title: c?.title || c.name,
|
||||||
|
component: 'RecordReadPrettyFormBlockInitializer',
|
||||||
|
icon: false,
|
||||||
|
targetCollection: c,
|
||||||
|
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,26 +179,36 @@ 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: 'item',
|
key: 'details',
|
||||||
title: '{{t("Details")}}',
|
type: 'subMenu',
|
||||||
component: 'RecordReadPrettyFormBlockInitializer',
|
title: '{{t("Details")}}',
|
||||||
actionInitializers,
|
children: useDetailCollections({ ...props, childrenCollections, collection }),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'form',
|
key: 'form',
|
||||||
type: 'item',
|
type: 'subMenu',
|
||||||
title: '{{t("Form")}}',
|
title: '{{t("Form")}}',
|
||||||
component: 'RecordFormBlockInitializer',
|
children: useFormCollections({ ...props, childrenCollections, collection }),
|
||||||
},
|
},
|
||||||
],
|
]
|
||||||
},
|
: [
|
||||||
{
|
{
|
||||||
type: 'itemGroup',
|
key: 'details',
|
||||||
title: '{{t("Children collection blocks")}}',
|
type: 'item',
|
||||||
children: useInheritFields(props),
|
title: '{{t("Details")}}',
|
||||||
|
component: 'RecordReadPrettyFormBlockInitializer',
|
||||||
|
actionInitializers,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'form',
|
||||||
|
type: 'item',
|
||||||
|
title: '{{t("Form")}}',
|
||||||
|
component: 'RecordFormBlockInitializer',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'itemGroup',
|
type: 'itemGroup',
|
||||||
|
@ -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,55 +1,54 @@
|
|||||||
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 />}
|
{...others}
|
||||||
{...others}
|
onClick={async ({ item }) => {
|
||||||
onClick={async ({ item }) => {
|
if (item.template) {
|
||||||
if (item.template) {
|
const s = await getTemplateSchemaByMode(item);
|
||||||
const s = await getTemplateSchemaByMode(item);
|
if (item.template.componentName === 'FormItem') {
|
||||||
if (item.template.componentName === 'FormItem') {
|
const blockSchema = createFormBlockSchema({
|
||||||
const blockSchema = createFormBlockSchema({
|
association,
|
||||||
association,
|
collection: collection.name,
|
||||||
collection: collection.name,
|
action: 'get',
|
||||||
action: 'get',
|
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
||||||
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
useParams: '{{ useParamsFromRecord }}',
|
||||||
useParams: '{{ useParamsFromRecord }}',
|
actionInitializers: 'UpdateFormActionInitializers',
|
||||||
actionInitializers: 'UpdateFormActionInitializers',
|
template: s,
|
||||||
template: s,
|
});
|
||||||
});
|
if (item.mode === 'reference') {
|
||||||
if (item.mode === 'reference') {
|
blockSchema['x-template-key'] = item.template.key;
|
||||||
blockSchema['x-template-key'] = item.template.key;
|
|
||||||
}
|
|
||||||
insert(blockSchema);
|
|
||||||
} else {
|
|
||||||
insert(s);
|
|
||||||
}
|
}
|
||||||
|
insert(blockSchema);
|
||||||
} else {
|
} else {
|
||||||
insert(
|
insert(s);
|
||||||
createFormBlockSchema({
|
|
||||||
association,
|
|
||||||
collection: collection.name,
|
|
||||||
action: 'get',
|
|
||||||
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
|
||||||
useParams: '{{ useParamsFromRecord }}',
|
|
||||||
actionInitializers: 'UpdateFormActionInitializers',
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}}
|
} else {
|
||||||
items={useRecordCollectionDataSourceItems('FormItem')}
|
insert(
|
||||||
/>
|
createFormBlockSchema({
|
||||||
);
|
association,
|
||||||
};
|
collection: collection.name,
|
||||||
|
action: 'get',
|
||||||
|
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
||||||
|
useParams: '{{ useParamsFromRecord }}',
|
||||||
|
actionInitializers: 'UpdateFormActionInitializers',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
items={useRecordCollectionDataSourceItems('FormItem')}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -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