refactor(add-new):association field add new support button edit (#1854)

* feat: assoociation add new supoort button edit

* feat: assoociation add new supoort button edit

* fix: add new button type

* refactor: add new support button edit
This commit is contained in:
katherinehhh 2023-05-16 09:06:03 +08:00 committed by GitHub
parent 3a77e4376a
commit d58174011c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 170 additions and 120 deletions

View File

@ -44,7 +44,7 @@ export const ActionDesigner = (props) => {
const { getChildrenCollections } = useCollectionManager(); const { getChildrenCollections } = useCollectionManager();
const { dn } = useDesignable(); const { dn } = useDesignable();
const { t } = useTranslation(); const { t } = useTranslation();
const isAction = useLinkageAction() const isAction = useLinkageAction();
const isPopupAction = ['create', 'update', 'view', 'customize:popup'].includes(fieldSchema['x-action'] || ''); const isPopupAction = ['create', 'update', 'view', 'customize:popup'].includes(fieldSchema['x-action'] || '');
const isUpdateModePopupAction = ['customize:bulkUpdate', 'customize:bulkEdit'].includes(fieldSchema['x-action']); const isUpdateModePopupAction = ['customize:bulkUpdate', 'customize:bulkEdit'].includes(fieldSchema['x-action']);
const [initialSchema, setInitialSchema] = useState<ISchema>(); const [initialSchema, setInitialSchema] = useState<ISchema>();
@ -52,6 +52,7 @@ export const ActionDesigner = (props) => {
const isLinkageAction = linkageAction || isAction; const isLinkageAction = linkageAction || isAction;
const isChildCollectionAction = getChildrenCollections(name).length > 0 && fieldSchema['x-action'] === 'create'; const isChildCollectionAction = getChildrenCollections(name).length > 0 && fieldSchema['x-action'] === 'create';
const isLink = fieldSchema['x-component'] === 'Action.Link'; const isLink = fieldSchema['x-component'] === 'Action.Link';
const isDelete = fieldSchema?.parent['x-component'] === 'CollectionField';
useEffect(() => { useEffect(() => {
const schemaUid = uid(); const schemaUid = uid();
const schema: ISchema = { const schema: ISchema = {
@ -329,16 +330,19 @@ export const ActionDesigner = (props) => {
)} )}
{isChildCollectionAction && <SchemaSettings.EnableChildCollections collectionName={name} />} {isChildCollectionAction && <SchemaSettings.EnableChildCollections collectionName={name} />}
<SchemaSettings.Divider />
<SchemaSettings.Remove {!isDelete && [
removeParentsIfNoChildren <SchemaSettings.Divider />,
breakRemoveOn={(s) => { <SchemaSettings.Remove
return s['x-component'] === 'Space' || s['x-component'].endsWith('ActionBar'); removeParentsIfNoChildren
}} breakRemoveOn={(s) => {
confirm={{ return s['x-component'] === 'Space' || s['x-component'].endsWith('ActionBar');
title: t('Delete action'), }}
}} confirm={{
/> title: t('Delete action'),
}}
/>,
]}
</MenuGroup> </MenuGroup>
</GeneralSchemaDesigner> </GeneralSchemaDesigner>
); );

View File

@ -1,15 +1,10 @@
import { LoadingOutlined } from '@ant-design/icons'; import { LoadingOutlined } from '@ant-design/icons';
import { RecursionField, connect, mapProps, observer, useField, useFieldSchema } from '@formily/react'; import { RecursionField, connect, mapProps, observer, useField, useFieldSchema } from '@formily/react';
import { Button, Input } from 'antd'; import { Input } from 'antd';
import React, { useCallback, useEffect, useMemo, useState } from 'react'; import React, { useCallback, useEffect, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { CollectionProvider } from '../../../collection-manager';
import { useFieldTitle } from '../../hooks'; import { useFieldTitle } from '../../hooks';
import { ActionContext } from '../action';
import { useAssociationFieldContext } from './hooks';
import { RemoteSelect, RemoteSelectProps } from '../remote-select'; import { RemoteSelect, RemoteSelectProps } from '../remote-select';
import useServiceOptions, { useInsertSchema } from './hooks'; import useServiceOptions from './hooks';
import schema from './schema';
export type AssociationSelectProps<P = any> = RemoteSelectProps<P> & { export type AssociationSelectProps<P = any> = RemoteSelectProps<P> & {
action?: string; action?: string;
@ -20,13 +15,8 @@ const InternalAssociationSelect = observer((props: AssociationSelectProps) => {
const { fieldNames, objectValue = true } = props; const { fieldNames, objectValue = true } = props;
const field: any = useField(); const field: any = useField();
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const [visibleAddNewer, setVisibleAddNewer] = useState(false);
const service = useServiceOptions(props); const service = useServiceOptions(props);
const { options: collectionField } = useAssociationFieldContext();
const isFilterForm = fieldSchema['x-designer'] === 'FormItem.FilterFormDesigner';
const isAllowAddNew = fieldSchema['x-add-new']; const isAllowAddNew = fieldSchema['x-add-new'];
const insertAddNewer = useInsertSchema('AddNewer');
const { t } = useTranslation();
const normalizeValues = useCallback( const normalizeValues = useCallback(
(obj) => { (obj) => {
if (!objectValue && typeof obj === 'object') { if (!objectValue && typeof obj === 'object') {
@ -36,7 +26,6 @@ const InternalAssociationSelect = observer((props: AssociationSelectProps) => {
}, },
[objectValue, fieldNames?.value], [objectValue, fieldNames?.value],
); );
const value = useMemo(() => { const value = useMemo(() => {
if (props.value === undefined || props.value === null || !Object.keys(props.value).length) { if (props.value === undefined || props.value === null || !Object.keys(props.value).length) {
return; return;
@ -60,31 +49,18 @@ const InternalAssociationSelect = observer((props: AssociationSelectProps) => {
value={value} value={value}
service={service} service={service}
></RemoteSelect> ></RemoteSelect>
{isAllowAddNew && !field.readPretty && !isFilterForm && (
<Button
type={'default'}
onClick={() => {
insertAddNewer(schema.AddNewer);
setVisibleAddNewer(true);
}}
>
{t('Add new')}
</Button>
)}
</Input.Group>
<ActionContext.Provider value={{ openMode: 'drawer', visible: visibleAddNewer, setVisible: setVisibleAddNewer }}> {isAllowAddNew && (
<CollectionProvider name={collectionField.target}>
<RecursionField <RecursionField
onlyRenderProperties onlyRenderProperties
basePath={field.address} basePath={field.address}
schema={fieldSchema} schema={fieldSchema}
filterProperties={(s) => { filterProperties={(s) => {
return s['x-component'] === 'AssociationField.AddNewer'; return s['x-component'] === 'Action';
}} }}
/> />
</CollectionProvider> )}
</ActionContext.Provider> </Input.Group>
</div> </div>
); );
}); });

View File

@ -9,6 +9,7 @@ import { SchemaComponentOptions } from '../../';
import { InternalSubTable } from './InternalSubTable'; import { InternalSubTable } from './InternalSubTable';
import { InternalFileManager } from './FileManager'; import { InternalFileManager } from './FileManager';
import { useAssociationFieldContext } from './hooks'; import { useAssociationFieldContext } from './hooks';
import {CreateRecordAction} from './components/CreateRecordAction'
const EditableAssociationField = observer((props: any) => { const EditableAssociationField = observer((props: any) => {
const { multiple } = props; const { multiple } = props;
@ -44,7 +45,7 @@ const EditableAssociationField = observer((props: any) => {
}; };
}; };
return ( return (
<SchemaComponentOptions scope={{ useCreateActionProps }}> <SchemaComponentOptions scope={{ useCreateActionProps }} components={{CreateRecordAction}}>
{currentMode === 'Picker' && <InternalPicker {...props} />} {currentMode === 'Picker' && <InternalPicker {...props} />}
{currentMode === 'Nester' && <InternalNester {...props} />} {currentMode === 'Nester' && <InternalNester {...props} />}
{currentMode === 'Select' && <AssociationSelect {...props} />} {currentMode === 'Select' && <AssociationSelect {...props} />}

View File

@ -14,7 +14,7 @@ import {
TableSelectorParamsProvider, TableSelectorParamsProvider,
useTableSelectorProps as useTsp, useTableSelectorProps as useTsp,
} from '../../../block-provider/TableSelectorProvider'; } from '../../../block-provider/TableSelectorProvider';
import { CollectionProvider, useCollection, useCollectionManager } from '../../../collection-manager'; import { CollectionProvider } from '../../../collection-manager';
import { useCompile } from '../../hooks'; import { useCompile } from '../../hooks';
import { ActionContext } from '../action'; import { ActionContext } from '../action';
import { useFieldNames, useInsertSchema } from './hooks'; import { useFieldNames, useInsertSchema } from './hooks';
@ -65,17 +65,10 @@ export const InternalPicker = observer((props: any) => {
const { value, multiple, onChange, quickUpload, selectFile, ...others } = props; const { value, multiple, onChange, quickUpload, selectFile, ...others } = props;
const field: any = useField(); const field: any = useField();
const fieldNames = useFieldNames(props); const fieldNames = useFieldNames(props);
const [visibleAddNewer, setVisibleAddNewer] = useState(false);
const [visibleSelector, setVisibleSelector] = useState(false); const [visibleSelector, setVisibleSelector] = useState(false);
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const insertAddNewer = useInsertSchema('AddNewer');
const insertSelector = useInsertSchema('Selector'); const insertSelector = useInsertSchema('Selector');
const { t } = useTranslation();
const { options: collectionField } = useAssociationFieldContext(); const { options: collectionField } = useAssociationFieldContext();
const addbuttonClick = () => {
insertAddNewer(schema.AddNewer);
setVisibleAddNewer(true);
};
const compile = useCompile(); const compile = useCompile();
const labelUiSchema = useLabelUiSchema(collectionField, fieldNames?.label || 'label'); const labelUiSchema = useLabelUiSchema(collectionField, fieldNames?.label || 'label');
const isAllowAddNew = fieldSchema['x-add-new']; const isAllowAddNew = fieldSchema['x-add-new'];
@ -168,29 +161,16 @@ export const InternalPicker = observer((props: any) => {
/> />
</div> </div>
{isAllowAddNew && ( {isAllowAddNew && (
<Button
style={{ width: 'auto' }}
type={'default'}
onClick={() => {
addbuttonClick();
}}
>
{t('Add new')}
</Button>
)}
</Input.Group>
<ActionContext.Provider value={{ openMode: 'drawer', visible: visibleAddNewer, setVisible: setVisibleAddNewer }}>
<CollectionProvider name={collectionField.target}>
<RecursionField <RecursionField
onlyRenderProperties onlyRenderProperties
basePath={field.address} basePath={field.address}
schema={fieldSchema} schema={fieldSchema}
filterProperties={(s) => { filterProperties={(s) => {
return s['x-component'] === 'AssociationField.AddNewer'; return s['x-component'] === 'Action';
}} }}
/> />
</CollectionProvider> )}
</ActionContext.Provider> </Input.Group>
<ActionContext.Provider value={{ openMode: 'drawer', visible: visibleSelector, setVisible: setVisibleSelector }}> <ActionContext.Provider value={{ openMode: 'drawer', visible: visibleSelector, setVisible: setVisibleSelector }}>
<RecordPickerProvider {...pickerProps}> <RecordPickerProvider {...pickerProps}>
<CollectionProvider name={collectionField.target}> <CollectionProvider name={collectionField.target}>

View File

@ -0,0 +1,40 @@
import { RecursionField, observer, useField, useFieldSchema } from '@formily/react';
import React, { useState } from 'react';
import { CollectionProvider } from '../../../../collection-manager';
import { ActionContext, useActionContext } from '../../action';
import { useAssociationFieldContext } from '../hooks';
import { useInsertSchema } from '../hooks';
import { CreateAction } from '../../../../schema-initializer/components';
import schema from '../schema';
export const CreateRecordAction = observer((props) => {
const field: any = useField();
const fieldSchema = useFieldSchema();
const ctx = useActionContext();
const insertAddNewer = useInsertSchema('AddNewer');
const { options: collectionField } = useAssociationFieldContext();
const [visibleAddNewer, setVisibleAddNewer] = useState(false);
const [currentCollection, setCurrentCollection] = useState(collectionField.target);
const addbuttonClick = (name) => {
insertAddNewer(schema.AddNewer);
setVisibleAddNewer(true);
setCurrentCollection(name);
};
return (
<CollectionProvider name={collectionField.target}>
<CreateAction {...props} onClick={(arg) => addbuttonClick(arg)} />
<ActionContext.Provider value={{ ...ctx, visible: visibleAddNewer, setVisible: setVisibleAddNewer }}>
<CollectionProvider name={currentCollection}>
<RecursionField
onlyRenderProperties
basePath={field.address}
schema={fieldSchema}
filterProperties={(s) => {
return s['x-component'] === 'AssociationField.AddNewer';
}}
/>
</CollectionProvider>
</ActionContext.Provider>
</CollectionProvider>
);
});

View File

@ -146,6 +146,7 @@ FormItem.Designer = function Designer() {
value: field?.name, value: field?.name,
label: compile(field?.uiSchema?.title) || field?.name, label: compile(field?.uiSchema?.title) || field?.name,
})); }));
let readOnlyMode = 'editable'; let readOnlyMode = 'editable';
if (fieldSchema['x-disabled'] === true) { if (fieldSchema['x-disabled'] === true) {
readOnlyMode = 'readonly'; readOnlyMode = 'readonly';
@ -588,6 +589,28 @@ FormItem.Designer = function Designer() {
title={t('Allow add new data')} title={t('Allow add new data')}
checked={fieldSchema['x-add-new'] as boolean} checked={fieldSchema['x-add-new'] as boolean}
onChange={(allowAddNew) => { onChange={(allowAddNew) => {
const hasAddNew = fieldSchema.reduceProperties((buf, schema) => {
if (schema['x-component'] === 'Action') {
return schema;
}
return buf;
}, null);
if (!hasAddNew) {
const addNewActionschema = {
'x-action': 'create',
title: "{{t('Add new')}}",
'x-designer': 'Action.Designer',
'x-component': 'Action',
'x-decorator': 'ACLActionProvider',
'x-component-props': {
openMode: 'drawer',
type: 'default',
component: 'CreateRecordAction',
},
};
insertAdjacent('afterBegin', addNewActionschema);
}
const schema = { const schema = {
['x-uid']: fieldSchema['x-uid'], ['x-uid']: fieldSchema['x-uid'],
}; };

View File

@ -3,7 +3,6 @@ import { css } from '@emotion/css';
import { RecursionField, observer, useField, useFieldSchema } from '@formily/react'; import { RecursionField, observer, useField, useFieldSchema } from '@formily/react';
import { Button, Dropdown, Menu } from 'antd'; import { Button, Dropdown, Menu } from 'antd';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useDesignable } from '../../'; import { useDesignable } from '../../';
import { useACLRolesCheck, useRecordPkValue } from '../../acl/ACLProvider'; import { useACLRolesCheck, useRecordPkValue } from '../../acl/ACLProvider';
import { CollectionProvider, useCollection, useCollectionManager } from '../../collection-manager'; import { CollectionProvider, useCollection, useCollectionManager } from '../../collection-manager';
@ -85,11 +84,48 @@ export const CreateRecordAction = observer((props: any) => {
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const collection = useCollection(); const collection = useCollection();
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const field: any = useField();
const [currentCollection, setCurrentCollection] = useState(collection.name);
const linkageRules = fieldSchema?.['x-linkage-rules'] || [];
const values = useRecord();
const ctx = useActionContext();
useEffect(() => {
field.linkageProperty = {};
linkageRules
.filter((k) => !k.disabled)
.map((v) => {
return v.actions?.map((h) => {
linkageAction(h.operator, field, v.condition, values);
});
});
}, [linkageRules, values]);
return (
<div className={actionDesignerCss}>
<ActionContext.Provider value={{ ...ctx, visible, setVisible }}>
<CreateAction
{...props}
onClick={(name) => {
setVisible(true);
setCurrentCollection(name);
}}
/>
<CollectionProvider name={currentCollection}>
<RecursionField schema={fieldSchema} basePath={field.address} onlyRenderProperties />
</CollectionProvider>
</ActionContext.Provider>
</div>
);
});
export const CreateAction = observer((props: any) => {
const { onClick } = props;
const collection = useCollection();
const fieldSchema = useFieldSchema();
const enableChildren = fieldSchema['x-enable-children'] || []; const enableChildren = fieldSchema['x-enable-children'] || [];
const allowAddToCurrent = fieldSchema?.['x-allow-add-to-current']; const allowAddToCurrent = fieldSchema?.['x-allow-add-to-current'];
const field: any = useField(); const field: any = useField();
const { t } = useTranslation();
const componentType = field.componentProps.type || 'primary'; const componentType = field.componentProps.type || 'primary';
console.log(componentType)
const { getChildrenCollections } = useCollectionManager(); const { getChildrenCollections } = useCollectionManager();
const totalChildCollections = getChildrenCollections(collection.name); const totalChildCollections = getChildrenCollections(collection.name);
const inheritsCollections = enableChildren const inheritsCollections = enableChildren
@ -109,10 +145,8 @@ export const CreateRecordAction = observer((props: any) => {
.filter((v) => { .filter((v) => {
return v && actionAclCheck(`${v.name}:create`); return v && actionAclCheck(`${v.name}:create`);
}); });
const [currentCollection, setCurrentCollection] = useState(collection.name);
const linkageRules = fieldSchema?.['x-linkage-rules'] || []; const linkageRules = fieldSchema?.['x-linkage-rules'] || [];
const values = useRecord(); const values = useRecord();
const ctx = useActionContext();
const compile = useCompile(); const compile = useCompile();
const { designable } = useDesignable(); const { designable } = useDesignable();
const icon = props.icon || <PlusOutlined />; const icon = props.icon || <PlusOutlined />;
@ -123,8 +157,7 @@ export const CreateRecordAction = observer((props: any) => {
<Menu.Item <Menu.Item
key={option.name} key={option.name}
onClick={(info) => { onClick={(info) => {
setVisible(true); onClick?.(option.name);
setCurrentCollection(option.name);
}} }}
> >
{compile(option.title)} {compile(option.title)}
@ -145,56 +178,49 @@ export const CreateRecordAction = observer((props: any) => {
}, [linkageRules, values]); }, [linkageRules, values]);
return ( return (
<div className={actionDesignerCss}> <div className={actionDesignerCss}>
<ActionContext.Provider value={{ ...ctx, visible, setVisible }}> {inheritsCollections?.length > 0 ? (
{inheritsCollections?.length > 0 ? ( allowAddToCurrent === undefined || allowAddToCurrent ? (
allowAddToCurrent === undefined || allowAddToCurrent ? ( <Dropdown.Button
<Dropdown.Button
type={componentType}
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);
}}
>
{icon}
{props.children}
</Dropdown.Button>
) : (
<Dropdown overlay={menu}>
{
<Button icon={icon} type={'primary'}>
{props.children} <DownOutlined />
</Button>
}
</Dropdown>
)
) : (
<Button
type={componentType} type={componentType}
disabled={field.disabled} icon={<DownOutlined />}
danger={componentType === 'danger'} buttonsRender={([leftButton, rightButton]) => [
icon={icon} leftButton,
React.cloneElement(rightButton as React.ReactElement<any, string>, { loading: false }),
]}
overlay={menu}
onClick={(info) => { onClick={(info) => {
setVisible(true); onClick?.(collection.name);
setCurrentCollection(collection.name);
}}
style={{
display: !designable && field?.data?.hidden && 'none',
opacity: designable && field?.data?.hidden && 0.1,
}} }}
> >
{icon}
{props.children} {props.children}
</Button> </Dropdown.Button>
)} ) : (
<CollectionProvider name={currentCollection}> <Dropdown overlay={menu}>
<RecursionField schema={fieldSchema} basePath={field.address} onlyRenderProperties /> {
</CollectionProvider> <Button icon={icon} type={componentType}>
</ActionContext.Provider> {props.children} <DownOutlined />
</Button>
}
</Dropdown>
)
) : (
<Button
type={componentType}
disabled={field.disabled}
danger={componentType === 'danger'}
icon={icon}
onClick={(info) => {
onClick?.(collection.name);
}}
style={{
display: !designable && field?.data?.hidden && 'none',
opacity: designable && field?.data?.hidden && 0.1,
}}
>
{props.children}
</Button>
)}
</div> </div>
); );
}); });

View File

@ -1111,13 +1111,13 @@ SchemaSettings.EnableChildCollections = function EnableChildCollectionsItem(prop
fieldSchema['x-enable-children'] = enableChildren; fieldSchema['x-enable-children'] = enableChildren;
fieldSchema['x-allow-add-to-current'] = v.allowAddToCurrent; fieldSchema['x-allow-add-to-current'] = v.allowAddToCurrent;
fieldSchema['x-component-props'] = { fieldSchema['x-component-props'] = {
openMode: 'drawer', ...fieldSchema['x-component-props'],
component: 'CreateRecordAction', component: 'CreateRecordAction',
}; };
schema['x-enable-children'] = enableChildren; schema['x-enable-children'] = enableChildren;
schema['x-allow-add-to-current'] = v.allowAddToCurrent; schema['x-allow-add-to-current'] = v.allowAddToCurrent;
schema['x-component-props'] = { schema['x-component-props'] = {
openMode: 'drawer', ...fieldSchema['x-component-props'],
component: 'CreateRecordAction', component: 'CreateRecordAction',
}; };
dn.emit('patch', { dn.emit('patch', {