diff --git a/packages/core/client/src/schema-component/antd/association-select/useServiceOptions.ts b/packages/core/client/src/schema-component/antd/association-select/useServiceOptions.ts index 069c0241b..80755ee0a 100644 --- a/packages/core/client/src/schema-component/antd/association-select/useServiceOptions.ts +++ b/packages/core/client/src/schema-component/antd/association-select/useServiceOptions.ts @@ -1,45 +1,76 @@ import { useFieldSchema } from '@formily/react'; -import { useMemo } from 'react'; +import { useCallback, useMemo } from 'react'; import { useCollection, useCollectionManager } from '../../../collection-manager'; import { useRecord } from '../../../record-provider'; export default function useServiceOptions(props) { - const { action = 'list', service, value } = props; + const { action = 'list', service, fieldNames } = props; const params = service?.params || {}; const fieldSchema = useFieldSchema(); const { getField } = useCollection(); const { getCollectionFields } = useCollectionManager(); const record = useRecord(); + const recordValue = record[fieldSchema.name]; + + const normalizeValues = useCallback( + (obj) => { + if (obj && typeof obj === 'object') { + return obj[fieldNames.value]; + } + return obj; + }, + [fieldNames.value], + ); + + const value = useMemo(() => { + if (recordValue === undefined || recordValue === null) { + return; + } + if (Array.isArray(recordValue)) { + return recordValue.map(normalizeValues); + } else { + return normalizeValues(recordValue); + } + }, [recordValue, normalizeValues]); const collectionField = useMemo(() => { return getField(fieldSchema.name); }, [fieldSchema.name]); + const sourceValue = record?.[collectionField?.sourceKey]; const filter = useMemo(() => { - if (!collectionField) return params?.filter; - let extraFilter = {}; - if (['oho', 'o2m'].includes(collectionField.interface)) { - const eqValue = record?.[collectionField.sourceKey]; - extraFilter = { - $or: [ - { - [collectionField.foreignKey]: { - $is: null, - }, - }, - eqValue !== undefined && eqValue !== null - ? { - [collectionField.foreignKey]: { - $eq: eqValue, - }, - } - : null, - ].filter(Boolean), - }; - } - - return params?.filter ? { $and: [extraFilter, params?.filter] } : extraFilter; - }, [params?.filter, getCollectionFields, collectionField]); + const isOToAny = ['oho', 'o2m'].includes(collectionField?.interface); + return { + $or: [ + { + $and: [ + isOToAny + ? { + [collectionField.foreignKey]: { + $is: null, + }, + } + : null, + params?.filter, + ], + }, + isOToAny && sourceValue !== undefined && sourceValue !== null + ? { + [collectionField.foreignKey]: { + $eq: sourceValue, + }, + } + : null, + value + ? { + [fieldNames.value]: { + [Array.isArray(value) ? '$in' : '$eq']: value, + }, + } + : null, + ], + }; + }, [params?.filter, getCollectionFields, collectionField, sourceValue, value, fieldNames.value]); return useMemo(() => { return { diff --git a/packages/core/client/src/schema-component/antd/remote-select/RemoteSelect.tsx b/packages/core/client/src/schema-component/antd/remote-select/RemoteSelect.tsx index 051ecbb20..979527f0d 100644 --- a/packages/core/client/src/schema-component/antd/remote-select/RemoteSelect.tsx +++ b/packages/core/client/src/schema-component/antd/remote-select/RemoteSelect.tsx @@ -1,7 +1,7 @@ import { LoadingOutlined } from '@ant-design/icons'; import { connect, mapProps, mapReadPretty } from '@formily/react'; import { SelectProps } from 'antd'; -import React, { useMemo } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef } from 'react'; import { ResourceActionOptions, useRequest } from '../../../api-client'; import { useCompile } from '../../hooks'; import { defaultFieldNames, Select } from '../select'; @@ -17,10 +17,11 @@ export type RemoteSelectProps

= SelectProps & { const InternalRemoteSelect = connect( (props: RemoteSelectProps) => { - const { fieldNames = {}, service = {}, wait = 300, ...others } = props; + const { fieldNames = {}, service = {}, wait = 300, value, objectValue, ...others } = props; const compile = useCompile(); + const firstRun = useRef(false); - const { data, run } = useRequest( + const { data, run, loading } = useRequest( { action: 'list', ...service, @@ -35,11 +36,27 @@ const InternalRemoteSelect = connect( }, }, { + manual: true, debounceWait: wait, - refreshDeps: [service, fieldNames.label, fieldNames.value], }, ); + const runDep = useMemo( + () => + JSON.stringify({ + service, + fieldNames, + }), + [service, fieldNames], + ); + + useEffect(() => { + // Lazy load + if (firstRun.current) { + run(); + } + }, [runDep]); + const onSearch = async (search) => { run({ filter: { @@ -55,14 +72,39 @@ const InternalRemoteSelect = connect( }); }; + const normalizeOptions = useCallback( + (obj) => { + if (objectValue || typeof obj === 'object') { + return { ...obj, [fieldNames.label]: compile(obj[fieldNames.label]) }; + } + return { [fieldNames.value]: obj, [fieldNames.label]: obj }; + }, + [objectValue, fieldNames.value], + ); + const options = useMemo(() => { + if (!data?.data?.length) { + return value !== undefined && value !== null + ? Array.isArray(value) + ? value.map(normalizeOptions) + : [normalizeOptions(value)] + : []; + } return ( data?.data?.map((item) => ({ ...item, [fieldNames.label]: compile(item[fieldNames.label]), })) || [] ); - }, [data, fieldNames.label]); + }, [data, fieldNames.label, objectValue, value]); + + const onDropdownVisibleChange = () => { + if (firstRun.current) { + return; + } + run(); + firstRun.current = true; + }; return (