fix:association appends (#1842)

* fix: getNesterAppends filter bug

* fix: associationSelect

* fix: associationSelect

* fix: associationSelect

* fix: getAssociationAppends

* fix: sub-form collection context

* refactor: code improve

* refactor: code improve

* refactor: code improve

* refactor: code improve

* refactor: code improve
This commit is contained in:
katherinehhh 2023-05-12 07:20:27 +08:00 committed by GitHub
parent 6c149aadfc
commit 3fcdd94549
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 79 additions and 64 deletions

View File

@ -1074,6 +1074,7 @@ export const useAssociationFilterBlockProps = () => {
export const useAssociationNames = (collection) => { export const useAssociationNames = (collection) => {
const { getCollectionFields, getCollectionJoinField } = useCollectionManager(); const { getCollectionFields, getCollectionJoinField } = useCollectionManager();
const { getField } = useCollection();
const collectionFields = getCollectionFields(collection); const collectionFields = getCollectionFields(collection);
const associationFields = new Set(); const associationFields = new Set();
for (const collectionField of collectionFields) { for (const collectionField of collectionFields) {
@ -1098,7 +1099,8 @@ export const useAssociationNames = (collection) => {
const getAssociationAppends = (schema, arr = []) => { const getAssociationAppends = (schema, arr = []) => {
const data = schema.reduceProperties((buf, s) => { const data = schema.reduceProperties((buf, s) => {
const collectionfield = s['x-collection-field'] && getCollectionJoinField(s['x-collection-field']); const collectionfield =
getField(s.name) || (s['x-collection-field'] && getCollectionJoinField(s['x-collection-field']));
if ( if (
collectionfield && collectionfield &&
['createdBy', 'updatedBy', 'o2m', 'obo', 'oho', 'm2o', 'm2m'].includes(collectionfield.interface) ['createdBy', 'updatedBy', 'o2m', 'obo', 'oho', 'm2o', 'm2m'].includes(collectionfield.interface)

View File

@ -1,19 +1,28 @@
import { useField, useFieldSchema } from '@formily/react'; import { useField, useFieldSchema, observer } from '@formily/react';
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { useCollectionManager } from '../../../collection-manager'; import { useCollectionManager } from '../../../collection-manager';
import { AssociationFieldContext } from './context'; import { AssociationFieldContext } from './context';
export function AssociationFieldProvider(props) { export const AssociationFieldProvider = observer((props) => {
const field = useField(); const field = useField();
const { getCollectionField } = useCollectionManager(); const { getCollectionJoinField, getCollection } = useCollectionManager();
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const collectionField = useMemo( const collectionField = useMemo(
() => getCollectionField(fieldSchema['x-collection-field']), () => getCollectionJoinField(fieldSchema['x-collection-field']),
[fieldSchema['x-collection-field'], fieldSchema.name],
);
const isFileCollection = useMemo(
() => getCollection(collectionField?.target)?.template === 'file',
[fieldSchema['x-collection-field']], [fieldSchema['x-collection-field']],
); );
return ( const currentMode = useMemo(
<AssociationFieldContext.Provider value={{ options: collectionField, field }}> () => fieldSchema['x-component-props'].mode || (isFileCollection ? 'FileManager' : 'Select'),
[fieldSchema['x-component-props'].mode],
);
return collectionField ? (
<AssociationFieldContext.Provider value={{ options: collectionField, field, currentMode }}>
{props.children} {props.children}
</AssociationFieldContext.Provider> </AssociationFieldContext.Provider>
); ) : null;
} });

View File

@ -3,9 +3,10 @@ import { RecursionField, connect, mapProps, observer, useField, useFieldSchema }
import { Button, Input } from 'antd'; import { Button, Input } from 'antd';
import React, { useCallback, useEffect, useMemo, useState } from 'react'; import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { CollectionProvider, useCollection } from '../../../collection-manager'; import { CollectionProvider } from '../../../collection-manager';
import { useFieldTitle } from '../../hooks'; import { useFieldTitle } from '../../hooks';
import { ActionContext } from '../action'; 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, { useInsertSchema } from './hooks';
import schema from './schema'; import schema from './schema';
@ -18,11 +19,10 @@ export type AssociationSelectProps<P = any> = RemoteSelectProps<P> & {
const InternalAssociationSelect = observer((props: AssociationSelectProps) => { const InternalAssociationSelect = observer((props: AssociationSelectProps) => {
const { fieldNames, objectValue = true } = props; const { fieldNames, objectValue = true } = props;
const field: any = useField(); const field: any = useField();
const [visibleAddNewer, setVisibleAddNewer] = useState(false);
const { getField } = useCollection();
const collectionField = getField(field.props.name);
const service = useServiceOptions(props);
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const [visibleAddNewer, setVisibleAddNewer] = useState(false);
const service = useServiceOptions(props);
const { options: collectionField } = useAssociationFieldContext();
const isFilterForm = fieldSchema['x-designer'] === 'FormItem.FilterFormDesigner'; const isFilterForm = fieldSchema['x-designer'] === 'FormItem.FilterFormDesigner';
const isAllowAddNew = fieldSchema['x-add-new']; const isAllowAddNew = fieldSchema['x-add-new'];
const insertAddNewer = useInsertSchema('AddNewer'); const insertAddNewer = useInsertSchema('AddNewer');
@ -74,7 +74,7 @@ const InternalAssociationSelect = observer((props: AssociationSelectProps) => {
</Input.Group> </Input.Group>
<ActionContext.Provider value={{ openMode: 'drawer', visible: visibleAddNewer, setVisible: setVisibleAddNewer }}> <ActionContext.Provider value={{ openMode: 'drawer', visible: visibleAddNewer, setVisible: setVisibleAddNewer }}>
<CollectionProvider name={collectionField?.target}> <CollectionProvider name={collectionField.target}>
<RecursionField <RecursionField
onlyRenderProperties onlyRenderProperties
basePath={field.address} basePath={field.address}

View File

@ -5,24 +5,18 @@ import { InternalNester } from './InternalNester';
import { InternalPicker } from './InternalPicker'; import { InternalPicker } from './InternalPicker';
import { AssociationSelect } from './AssociationSelect'; import { AssociationSelect } from './AssociationSelect';
import { useAssociationCreateActionProps as useCAP } from '../../../block-provider/hooks'; import { useAssociationCreateActionProps as useCAP } from '../../../block-provider/hooks';
import { useCollection, useCollectionManager } from '../../../collection-manager';
import { SchemaComponentOptions } from '../../'; import { SchemaComponentOptions } from '../../';
import { InternalSubTable } from './InternalSubTable'; import { InternalSubTable } from './InternalSubTable';
import { InternalFileManager } from './FileManager'; import { InternalFileManager } from './FileManager';
import { useAssociationFieldContext } from './hooks';
export const Editable = observer((props: any) => { const EditableAssociationField = observer((props: any) => {
useEffect(() => {
props.mode && setCurrentMode(props.mode);
}, [props.mode]);
const { multiple } = props; const { multiple } = props;
const field: any = useField(); const field: any = useField();
const form = useForm(); const form = useForm();
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const { getField } = useCollection(); const { options: collectionField, currentMode } = useAssociationFieldContext();
const { getCollection } = useCollectionManager();
const collectionField = getField(field.props.name);
const isFileCollection = getCollection(collectionField?.target)?.template === 'file';
const [currentMode, setCurrentMode] = useState(props.mode || (isFileCollection ? 'FileManager' : 'Select'));
const useCreateActionProps = () => { const useCreateActionProps = () => {
const { onClick } = useCAP(); const { onClick } = useCAP();
const actionField: any = useField(); const actionField: any = useField();
@ -49,15 +43,21 @@ export const Editable = observer((props: any) => {
}, },
}; };
}; };
return (
<SchemaComponentOptions scope={{ useCreateActionProps }}>
{currentMode === 'Picker' && <InternalPicker {...props} />}
{currentMode === 'Nester' && <InternalNester {...props} />}
{currentMode === 'Select' && <AssociationSelect {...props} />}
{currentMode === 'SubTable' && <InternalSubTable {...props} />}
{currentMode === 'FileManager' && <InternalFileManager {...props} />}
</SchemaComponentOptions>
);
});
export const Editable = observer((props) => {
return ( return (
<AssociationFieldProvider> <AssociationFieldProvider>
<SchemaComponentOptions scope={{ useCreateActionProps }}> <EditableAssociationField {...props} />
{currentMode === 'Picker' && <InternalPicker {...props} />}
{currentMode === 'Nester' && <InternalNester {...props} />}
{currentMode === 'Select' && <AssociationSelect {...props} />}
{currentMode === 'SubTable' && <InternalSubTable {...props} />}
{currentMode === 'FileManager' && <InternalFileManager {...props} />}
</SchemaComponentOptions>
</AssociationFieldProvider> </AssociationFieldProvider>
); );
}); });

View File

@ -2,19 +2,21 @@ import { FormLayout } from '@formily/antd';
import { RecursionField, useField, useFieldSchema } from '@formily/react'; import { RecursionField, useField, useFieldSchema } from '@formily/react';
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { CollectionProvider } from '../../../collection-manager'; import { CollectionProvider } from '../../../collection-manager';
import { useAssociationFieldContext, useInsertSchema } from './hooks'; import { useInsertSchema } from './hooks';
import { useAssociationFieldContext } from './hooks';
import schema from './schema'; import schema from './schema';
export const InternalNester = () => { export const InternalNester = () => {
const field = useField(); const field = useField();
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const insertNester = useInsertSchema('Nester'); const insertNester = useInsertSchema('Nester');
const { options } = useAssociationFieldContext(); const { options: collectionField } = useAssociationFieldContext();
useEffect(() => { useEffect(() => {
insertNester(schema.Nester); insertNester(schema.Nester);
}, []); }, []);
return ( return (
<CollectionProvider name={options.target}> <CollectionProvider name={collectionField.target}>
<FormLayout layout={'vertical'}> <FormLayout layout={'vertical'}>
<RecursionField <RecursionField
onlyRenderProperties onlyRenderProperties

View File

@ -14,11 +14,12 @@ import {
TableSelectorParamsProvider, TableSelectorParamsProvider,
useTableSelectorProps as useTsp, useTableSelectorProps as useTsp,
} from '../../../block-provider/TableSelectorProvider'; } from '../../../block-provider/TableSelectorProvider';
import { CollectionProvider, useCollection } from '../../../collection-manager'; import { CollectionProvider, useCollection, useCollectionManager } 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';
import schema from './schema'; import schema from './schema';
import { useAssociationFieldContext } from './hooks';
import { flatData, getLabelFormatValue, useLabelUiSchema } from './util'; import { flatData, getLabelFormatValue, useLabelUiSchema } from './util';
const useTableSelectorProps = () => { const useTableSelectorProps = () => {
@ -69,10 +70,9 @@ export const InternalPicker = observer((props: any) => {
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const insertAddNewer = useInsertSchema('AddNewer'); const insertAddNewer = useInsertSchema('AddNewer');
const insertSelector = useInsertSchema('Selector'); const insertSelector = useInsertSchema('Selector');
const { getField } = useCollection();
const { t } = useTranslation(); const { t } = useTranslation();
const collectionField = getField(field.props.name); const { options: collectionField } = useAssociationFieldContext();
const addbuttonClick = () => { const addbuttonClick = () => {
insertAddNewer(schema.AddNewer); insertAddNewer(schema.AddNewer);
setVisibleAddNewer(true); setVisibleAddNewer(true);
}; };

View File

@ -2,7 +2,7 @@ import { RecursionField, observer, useField, useFieldSchema } from '@formily/rea
import { toArr } from '@formily/shared'; import { toArr } from '@formily/shared';
import React, { Fragment, useRef, useState } from 'react'; import React, { Fragment, useRef, useState } from 'react';
import { BlockAssociationContext, WithoutTableFieldResource } from '../../../block-provider'; import { BlockAssociationContext, WithoutTableFieldResource } from '../../../block-provider';
import { CollectionProvider, useCollection, useCollectionManager } from '../../../collection-manager'; import { CollectionProvider } from '../../../collection-manager';
import { RecordProvider, useRecord } from '../../../record-provider'; import { RecordProvider, useRecord } from '../../../record-provider';
import { FormProvider } from '../../core'; import { FormProvider } from '../../core';
import { useCompile } from '../../hooks'; import { useCompile } from '../../hooks';
@ -11,6 +11,7 @@ import { EllipsisWithTooltip } from '../input/EllipsisWithTooltip';
import { useFieldNames, useInsertSchema } from './hooks'; import { useFieldNames, useInsertSchema } from './hooks';
import schema from './schema'; import schema from './schema';
import { getLabelFormatValue, useLabelUiSchema } from './util'; import { getLabelFormatValue, useLabelUiSchema } from './util';
import { useAssociationFieldContext } from './hooks';
interface IEllipsisWithTooltipRef { interface IEllipsisWithTooltipRef {
setPopoverVisible: (boolean) => void; setPopoverVisible: (boolean) => void;
@ -26,14 +27,12 @@ export const ReadPrettyInternalViewer: React.FC = observer((props: any) => {
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const recordCtx = useRecord(); const recordCtx = useRecord();
const { enableLink } = fieldSchema['x-component-props']; const { enableLink } = fieldSchema['x-component-props'];
const { getCollectionJoinField } = useCollectionManager();
// value 做了转换,但 props.value 和原来 useField().value 的值不一致 // value 做了转换,但 props.value 和原来 useField().value 的值不一致
const field = useField(); const field = useField();
const fieldNames = useFieldNames(props); const fieldNames = useFieldNames(props);
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const insertViewer = useInsertSchema('Viewer'); const insertViewer = useInsertSchema('Viewer');
const { getField } = useCollection(); const { options: collectionField } = useAssociationFieldContext();
const collectionField = getField(fieldSchema.name) || getCollectionJoinField(fieldSchema?.['x-collection-field']);
const [record, setRecord] = useState({}); const [record, setRecord] = useState({});
const compile = useCompile(); const compile = useCompile();
const labelUiSchema = useLabelUiSchema(collectionField, fieldNames?.label || 'label'); const labelUiSchema = useLabelUiSchema(collectionField, fieldNames?.label || 'label');
@ -98,7 +97,7 @@ export const ReadPrettyInternalViewer: React.FC = observer((props: any) => {
); );
}; };
return collectionField ? ( return (
<div> <div>
<BlockAssociationContext.Provider value={`${collectionField.collectionName}.${collectionField.name}`}> <BlockAssociationContext.Provider value={`${collectionField.collectionName}.${collectionField.name}`}>
<CollectionProvider name={collectionField.target ?? collectionField.targetCollection}> <CollectionProvider name={collectionField.target ?? collectionField.targetCollection}>
@ -113,5 +112,5 @@ export const ReadPrettyInternalViewer: React.FC = observer((props: any) => {
</CollectionProvider> </CollectionProvider>
</BlockAssociationContext.Provider> </BlockAssociationContext.Provider>
</div> </div>
) : null; );
}); });

View File

@ -3,26 +3,27 @@ import { useField, observer } from '@formily/react';
import { AssociationFieldProvider } from './AssociationFieldProvider'; import { AssociationFieldProvider } from './AssociationFieldProvider';
import { InternalNester } from './InternalNester'; import { InternalNester } from './InternalNester';
import { ReadPrettyInternalViewer } from './InternalViewer'; import { ReadPrettyInternalViewer } from './InternalViewer';
import { useCollection, useCollectionManager } from '../../../collection-manager';
import { InternalSubTable } from './InternalSubTable'; import { InternalSubTable } from './InternalSubTable';
import { FileManageReadPretty } from './FileManager'; import { FileManageReadPretty } from './FileManager';
import { useAssociationFieldContext } from './hooks';
const ReadPrettyAssociationField = observer((props: any) => {
const { currentMode } = useAssociationFieldContext();
export const ReadPretty = observer((props: any) => {
const field: any = useField();
const { getField } = useCollection();
const { getCollection } = useCollectionManager();
const collectionField = getField(field.props.name);
const isFileCollection = getCollection(collectionField?.target)?.template === 'file';
const [currentMode, setCurrentMode] = useState(props.mode || (isFileCollection ? 'FileManager' : 'Select'));
useEffect(() => {
props.mode && setCurrentMode(props.mode);
}, [props.mode]);
return ( return (
<AssociationFieldProvider> <>
{['Select', 'Picker'].includes(currentMode) && <ReadPrettyInternalViewer {...props} />} {['Select', 'Picker'].includes(currentMode) && <ReadPrettyInternalViewer {...props} />}
{currentMode === 'Nester' && <InternalNester {...props} />} {currentMode === 'Nester' && <InternalNester {...props} />}
{currentMode === 'SubTable' && <InternalSubTable {...props} />} {currentMode === 'SubTable' && <InternalSubTable {...props} />}
{currentMode === 'FileManager' && <FileManageReadPretty {...props} />} {currentMode === 'FileManager' && <FileManageReadPretty {...props} />}
</>
);
});
export const ReadPretty = observer((props) => {
return (
<AssociationFieldProvider>
<ReadPrettyAssociationField {...props} />
</AssociationFieldProvider> </AssociationFieldProvider>
); );
}); });

View File

@ -2,8 +2,9 @@ import { GeneralField } from '@formily/core';
import { createContext } from 'react'; import { createContext } from 'react';
export interface AssociationFieldContextProps { export interface AssociationFieldContextProps {
options: any; options?: any;
field: GeneralField; field?: GeneralField;
currentMode?:string;
} }
export const AssociationFieldContext = createContext<AssociationFieldContextProps>(null); export const AssociationFieldContext = createContext<AssociationFieldContextProps>({});

View File

@ -30,15 +30,15 @@ export const useInsertSchema = (component) => {
}; };
export function useAssociationFieldContext<F extends GeneralField>() { export function useAssociationFieldContext<F extends GeneralField>() {
return useContext(AssociationFieldContext) as { options: any; field: F }; return useContext(AssociationFieldContext) as { options: any; field: F; currentMode: string };
} }
export default function useServiceOptions(props) { export default function useServiceOptions(props) {
const { action = 'list', service, fieldNames } = props; const { action = 'list', service, fieldNames } = props;
const params = service?.params || {}; const params = service?.params || {};
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const { getField, fields } = useCollection(); const { getField } = useCollection();
const { getCollectionFields } = useCollectionManager(); const { getCollectionFields, getCollectionJoinField } = useCollectionManager();
const record = useRecord(); const record = useRecord();
const normalizeValues = useCallback( const normalizeValues = useCallback(
@ -63,8 +63,9 @@ export default function useServiceOptions(props) {
}, [props.value, normalizeValues]); }, [props.value, normalizeValues]);
const collectionField = useMemo(() => { const collectionField = useMemo(() => {
return getField(fieldSchema.name); return getField(fieldSchema.name) || getCollectionJoinField(fieldSchema?.['x-collection-field']);
}, [fieldSchema.name]); }, [fieldSchema.name]);
const sourceValue = record?.[collectionField?.sourceKey]; const sourceValue = record?.[collectionField?.sourceKey];
const filter = useMemo(() => { const filter = useMemo(() => {
const isOToAny = ['oho', 'o2m'].includes(collectionField?.interface); const isOToAny = ['oho', 'o2m'].includes(collectionField?.interface);