diff --git a/packages/core/client/src/block-provider/hooks/useParsedFilter.ts b/packages/core/client/src/block-provider/hooks/useParsedFilter.ts index 0e7bf9f2f..8f7bd400b 100644 --- a/packages/core/client/src/block-provider/hooks/useParsedFilter.ts +++ b/packages/core/client/src/block-provider/hooks/useParsedFilter.ts @@ -1,17 +1,16 @@ import { reaction } from '@formily/reactive'; -import { flatten } from '@nocobase/utils/client'; +import { flatten, getValuesByPath } from '@nocobase/utils/client'; import _ from 'lodash'; import { useEffect, useState } from 'react'; import { useParseDataScopeFilter } from '../../schema-settings'; import { DEBOUNCE_WAIT } from '../../variables'; import { getPath } from '../../variables/utils/getPath'; +import { getVariableName } from '../../variables/utils/getVariableName'; import { isVariable } from '../../variables/utils/isVariable'; -import { useFormBlockContext } from '../FormBlockProvider'; export function useParsedFilter({ filterOption, currentRecord }: { filterOption: any; currentRecord?: any }) { - const { parseFilter } = useParseDataScopeFilter({ currentRecord }); + const { parseFilter, findVariable } = useParseDataScopeFilter({ currentRecord }); const [filter, setFilter] = useState({}); - const { form } = useFormBlockContext(); useEffect(() => { if (!filterOption) return; @@ -33,7 +32,19 @@ export function useParsedFilter({ filterOption, currentRecord }: { filterOption: if (!isVariable(value)) { return value; } - const result = _.get({ $nForm: form?.values }, getPath(value)); + const variableName = getVariableName(value); + const variable = findVariable(variableName); + + if (process.env.NODE_ENV !== 'production' && !variable) { + throw new Error(`useParsedFilter: can not find variable ${variableName}`); + } + + const result = getValuesByPath( + { + [variableName]: variable?.ctx || {}, + }, + getPath(value), + ); return result; }, }); diff --git a/packages/core/client/src/schema-component/antd/association-field/Nester.tsx b/packages/core/client/src/schema-component/antd/association-field/Nester.tsx index d32951028..742105e26 100644 --- a/packages/core/client/src/schema-component/antd/association-field/Nester.tsx +++ b/packages/core/client/src/schema-component/antd/association-field/Nester.tsx @@ -17,7 +17,6 @@ import { IsAllowToSetDefaultValueParams, interfacesOfUnsupportedDefaultValue, } from '../../../schema-settings/hooks/useIsAllowToSetDefaultValue'; -import { LocalVariablesProvider } from '../../../variables/hooks/useLocalVariables'; import { AssociationFieldContext } from './context'; import { SubFormProvider, useAssociationFieldContext } from './hooks'; @@ -80,11 +79,9 @@ const ToOneNester = (props) => { - - - {props.children} - - + + {props.children} + @@ -190,17 +187,15 @@ const ToManyNester = observer( - - - - - - - + + + + + diff --git a/packages/core/client/src/schema-component/antd/association-field/hooks.ts b/packages/core/client/src/schema-component/antd/association-field/hooks.ts index 2296ae9fe..94231dab6 100644 --- a/packages/core/client/src/schema-component/antd/association-field/hooks.ts +++ b/packages/core/client/src/schema-component/antd/association-field/hooks.ts @@ -5,7 +5,6 @@ import { flatten, getValuesByPath } from '@nocobase/utils/client'; import _, { isString } from 'lodash'; import cloneDeep from 'lodash/cloneDeep'; import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; -import { useFormBlockContext } from '../../../block-provider'; import { mergeFilter } from '../../../block-provider/SharedFilterProvider'; import { useCollection, useCollectionManager } from '../../../collection-manager'; import { isInFilterFormBlock } from '../../../filter-provider'; @@ -13,6 +12,7 @@ import { useRecord } from '../../../record-provider'; import { useParseDataScopeFilter } from '../../../schema-settings'; import { DEBOUNCE_WAIT } from '../../../variables'; import { getPath } from '../../../variables/utils/getPath'; +import { getVariableName } from '../../../variables/utils/getVariableName'; import { isVariable } from '../../../variables/utils/isVariable'; import { useDesignable } from '../../hooks'; import { AssociationFieldContext } from './context'; @@ -48,17 +48,14 @@ export function useAssociationFieldContext() { } export default function useServiceOptions(props) { - const { action = 'list', service, fieldNames } = props; - const params = service?.params || {}; + const { action = 'list', service } = props; const fieldSchema = useFieldSchema(); const field = useField(); const { getField } = useCollection(); const { getCollectionJoinField } = useCollectionManager(); const record = useRecord(); - const { parseFilter } = useParseDataScopeFilter(); + const { parseFilter, findVariable } = useParseDataScopeFilter(); const [fieldServiceFilter, setFieldServiceFilter] = useState(null); - const { form } = useFormBlockContext(); - const { formValue: subFormValue } = useSubFormValue(); useEffect(() => { const filterFromSchema = isString(fieldSchema?.['x-component-props']?.service?.params?.filter) @@ -83,10 +80,16 @@ export default function useServiceOptions(props) { if (!isVariable(value)) { return value; } + const variableName = getVariableName(value); + const variable = findVariable(variableName); + + if (process.env.NODE_ENV !== 'production' && !variable) { + throw new Error(`useServiceOptions: can not find variable ${variableName}`); + } + const result = getValuesByPath( { - $nForm: form?.values, - $iteration: subFormValue, + [variableName]: variable?.ctx || {}, }, getPath(value), ); @@ -100,39 +103,12 @@ export default function useServiceOptions(props) { }, [ field.componentProps?.service?.params?.filter, fieldSchema, - form?.values, + findVariable, parseFilter, record, service?.params?.filter, - subFormValue, ]); - const normalizeValues = useCallback( - (obj) => { - if (obj && typeof obj === 'object') { - return obj[fieldNames?.value]; - } - return obj; - }, - [fieldNames?.value], - ); - - const value = useMemo(() => { - if (props.value === undefined || props.value === null) { - return; - } - - let result: any[] = null; - - if (Array.isArray(props.value)) { - result = props.value.map(normalizeValues); - } else { - result = [normalizeValues(props.value)]; - } - - return result.filter(Boolean); - }, [props.value, normalizeValues]); - const collectionField = useMemo(() => { return getField(fieldSchema.name) || getCollectionJoinField(fieldSchema?.['x-collection-field']); }, [fieldSchema]); diff --git a/packages/core/client/src/schema-component/antd/form-item/hooks/useParseDefaultValue.ts b/packages/core/client/src/schema-component/antd/form-item/hooks/useParseDefaultValue.ts index 33d3c9499..2a1c274b4 100644 --- a/packages/core/client/src/schema-component/antd/form-item/hooks/useParseDefaultValue.ts +++ b/packages/core/client/src/schema-component/antd/form-item/hooks/useParseDefaultValue.ts @@ -3,7 +3,7 @@ import { useField, useFieldSchema } from '@formily/react'; import { reaction } from '@formily/reactive'; import { getValuesByPath } from '@nocobase/utils/client'; import _ from 'lodash'; -import { useEffect } from 'react'; +import { useCallback, useEffect } from 'react'; import { useRecord, useRecordIndex } from '../../../../../src/record-provider'; import { useFormBlockType } from '../../../../block-provider/FormBlockProvider'; import { useCollection } from '../../../../collection-manager'; @@ -31,6 +31,20 @@ const useParseDefaultValue = () => { const index = useRecordIndex(); const { type: formBlockType } = useFormBlockType(); + /** + * name: 如 $user + */ + const findVariable = useCallback( + (name: string) => { + let result = variables?.getVariable(name); + if (!result) { + result = localVariables.find((item) => item.name === name); + } + return result; + }, + [localVariables, variables], + ); + useEffect(() => { if ( fieldSchema.default == null || @@ -94,12 +108,16 @@ const useParseDefaultValue = () => { if (isVariable(fieldSchema.default)) { const variableName = getVariableName(fieldSchema.default); - const variableValue = localVariables.find((item) => item.name === variableName); + const variable = findVariable(variableName); - if (variableValue) { + if (process.env.NODE_ENV !== 'production' && !variable) { + throw new Error(`useParseDefaultValue: can not find variable ${variableName}`); + } + + if (variable) { // 实现联动的效果,当依赖的变量变化时(如 `$nForm` 变量),重新解析默认值 const dispose = reaction(() => { - const obj = { [variableName]: variableValue?.ctx }; + const obj = { [variableName]: variable?.ctx || {} }; const path = getPath(fieldSchema.default); // fix https://nocobase.height.app/T-2212 diff --git a/packages/core/client/src/schema-component/antd/table-v2/Table.tsx b/packages/core/client/src/schema-component/antd/table-v2/Table.tsx index 5ef064adb..f11b6885e 100644 --- a/packages/core/client/src/schema-component/antd/table-v2/Table.tsx +++ b/packages/core/client/src/schema-component/antd/table-v2/Table.tsx @@ -22,7 +22,6 @@ import { useTableSelectorContext, } from '../../../'; import { useACLFieldWhitelist } from '../../../acl/ACLProvider'; -import { LocalVariablesProvider } from '../../../variables/hooks/useLocalVariables'; import { useToken } from '../__builtins__'; import { SubFormProvider } from '../association-field/hooks'; import { ColumnFieldProvider } from './components/ColumnFieldProvider'; @@ -65,17 +64,15 @@ const useTableColumns = (props: { showDel?: boolean; isSubTable?: boolean }) => return ( - - - - - - - + + + + + ); diff --git a/packages/core/client/src/schema-settings/hooks/useParseDataScopeFilter.ts b/packages/core/client/src/schema-settings/hooks/useParseDataScopeFilter.ts index d87635417..d6368c8a4 100644 --- a/packages/core/client/src/schema-settings/hooks/useParseDataScopeFilter.ts +++ b/packages/core/client/src/schema-settings/hooks/useParseDataScopeFilter.ts @@ -20,6 +20,20 @@ const useParseDataScopeFilter = ({ exclude = defaultExclude, currentRecord }: Pr const localVariables = useLocalVariables({ currentRecord }); const variables = useVariables(); + /** + * name: 如 $user + */ + const findVariable = useCallback( + (name: string) => { + let result = variables?.getVariable(name); + if (!result) { + result = localVariables.find((item) => item.name === name); + } + return result; + }, + [localVariables, variables], + ); + const parseFilter = useCallback( async (filterValue: any) => { const flat = flatten(filterValue, { @@ -52,7 +66,7 @@ const useParseDataScopeFilter = ({ exclude = defaultExclude, currentRecord }: Pr [exclude, localVariables, variables?.parseVariable], ); - return { parseFilter }; + return { parseFilter, findVariable }; }; export default useParseDataScopeFilter; diff --git a/packages/core/client/src/variables/hooks/useLocalVariables.tsx b/packages/core/client/src/variables/hooks/useLocalVariables.tsx index f640ecb3e..471d824a9 100644 --- a/packages/core/client/src/variables/hooks/useLocalVariables.tsx +++ b/packages/core/client/src/variables/hooks/useLocalVariables.tsx @@ -1,8 +1,9 @@ import { Form } from '@formily/core'; -import React, { useContext, useMemo } from 'react'; +import { useMemo } from 'react'; import { useFormBlockContext } from '../../block-provider'; import { useCollection } from '../../collection-manager'; import { useRecord } from '../../record-provider'; +import { useSubFormValue } from '../../schema-component/antd/association-field/hooks'; import { useBlockCollection } from '../../schema-settings/VariableInput/hooks/useBlockCollection'; import { VariableOption } from '../types'; @@ -12,21 +13,9 @@ interface Props { currentForm?: Form; } -const LocalVariablesContext = React.createContext<{ iterationCtx: Record }>(null); - -export const LocalVariablesProvider = ({ - children, - iterationCtx, -}: { - iterationCtx: Record; - children: React.ReactNode; -}) => { - return {children}; -}; - const useLocalVariables = (props?: Props) => { const { name: currentCollectionName } = useCollection(); - const { iterationCtx } = useContext(LocalVariablesContext) || {}; + const { formValue: subFormValue } = useSubFormValue(); let { name } = useBlockCollection(); let currentRecord = useRecord(); let { form } = useFormBlockContext(); @@ -86,10 +75,10 @@ const useLocalVariables = (props?: Props) => { ctx: form?.values, collectionName: name, }, - iterationCtx && { name: '$iteration', ctx: iterationCtx, collectionName: currentCollectionName }, + subFormValue && { name: '$iteration', ctx: subFormValue, collectionName: currentCollectionName }, ] as VariableOption[] ).filter(Boolean); - }, [currentRecord, name, form?.values, iterationCtx, currentCollectionName]); // 尽量保持返回的值不变,这样可以减少接口的请求次数,因为关系字段会缓存到变量的 ctx 中 + }, [currentRecord, name, form?.values, subFormValue, currentCollectionName]); // 尽量保持返回的值不变,这样可以减少接口的请求次数,因为关系字段会缓存到变量的 ctx 中 }; export default useLocalVariables;