fix(variable): compat old variable names (#2889)

* fix(variable): compat old variable names

* refactor: remove LocalVariablesProvider
This commit is contained in:
被雨水过滤的空气-Rain 2023-10-21 14:23:06 +08:00 committed by GitHub
parent 2ec3130f46
commit cde25ed871
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 91 additions and 91 deletions

View File

@ -1,17 +1,16 @@
import { reaction } from '@formily/reactive'; import { reaction } from '@formily/reactive';
import { flatten } from '@nocobase/utils/client'; import { flatten, getValuesByPath } from '@nocobase/utils/client';
import _ from 'lodash'; import _ from 'lodash';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useParseDataScopeFilter } from '../../schema-settings'; import { useParseDataScopeFilter } from '../../schema-settings';
import { DEBOUNCE_WAIT } from '../../variables'; import { DEBOUNCE_WAIT } from '../../variables';
import { getPath } from '../../variables/utils/getPath'; import { getPath } from '../../variables/utils/getPath';
import { getVariableName } from '../../variables/utils/getVariableName';
import { isVariable } from '../../variables/utils/isVariable'; import { isVariable } from '../../variables/utils/isVariable';
import { useFormBlockContext } from '../FormBlockProvider';
export function useParsedFilter({ filterOption, currentRecord }: { filterOption: any; currentRecord?: any }) { export function useParsedFilter({ filterOption, currentRecord }: { filterOption: any; currentRecord?: any }) {
const { parseFilter } = useParseDataScopeFilter({ currentRecord }); const { parseFilter, findVariable } = useParseDataScopeFilter({ currentRecord });
const [filter, setFilter] = useState({}); const [filter, setFilter] = useState({});
const { form } = useFormBlockContext();
useEffect(() => { useEffect(() => {
if (!filterOption) return; if (!filterOption) return;
@ -33,7 +32,19 @@ export function useParsedFilter({ filterOption, currentRecord }: { filterOption:
if (!isVariable(value)) { if (!isVariable(value)) {
return 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; return result;
}, },
}); });

View File

@ -17,7 +17,6 @@ import {
IsAllowToSetDefaultValueParams, IsAllowToSetDefaultValueParams,
interfacesOfUnsupportedDefaultValue, interfacesOfUnsupportedDefaultValue,
} from '../../../schema-settings/hooks/useIsAllowToSetDefaultValue'; } from '../../../schema-settings/hooks/useIsAllowToSetDefaultValue';
import { LocalVariablesProvider } from '../../../variables/hooks/useLocalVariables';
import { AssociationFieldContext } from './context'; import { AssociationFieldContext } from './context';
import { SubFormProvider, useAssociationFieldContext } from './hooks'; import { SubFormProvider, useAssociationFieldContext } from './hooks';
@ -80,11 +79,9 @@ const ToOneNester = (props) => {
<FormActiveFieldsProvider name="nester"> <FormActiveFieldsProvider name="nester">
<SubFormProvider value={field.value}> <SubFormProvider value={field.value}>
<RecordProvider record={field.value}> <RecordProvider record={field.value}>
<LocalVariablesProvider iterationCtx={field.value}> <DefaultValueProvider isAllowToSetDefaultValue={isAllowToSetDefaultValue}>
<DefaultValueProvider isAllowToSetDefaultValue={isAllowToSetDefaultValue}> <Card bordered={true}>{props.children}</Card>
<Card bordered={true}>{props.children}</Card> </DefaultValueProvider>
</DefaultValueProvider>
</LocalVariablesProvider>
</RecordProvider> </RecordProvider>
</SubFormProvider> </SubFormProvider>
</FormActiveFieldsProvider> </FormActiveFieldsProvider>
@ -190,17 +187,15 @@ const ToManyNester = observer(
<FormActiveFieldsProvider name="nester"> <FormActiveFieldsProvider name="nester">
<SubFormProvider value={value}> <SubFormProvider value={value}>
<RecordProvider record={value}> <RecordProvider record={value}>
<LocalVariablesProvider iterationCtx={value}> <RecordIndexProvider index={index}>
<RecordIndexProvider index={index}> <DefaultValueProvider isAllowToSetDefaultValue={isAllowToSetDefaultValue}>
<DefaultValueProvider isAllowToSetDefaultValue={isAllowToSetDefaultValue}> <RecursionField
<RecursionField onlyRenderProperties
onlyRenderProperties basePath={field.address.concat(index)}
basePath={field.address.concat(index)} schema={fieldSchema}
schema={fieldSchema} />
/> </DefaultValueProvider>
</DefaultValueProvider> </RecordIndexProvider>
</RecordIndexProvider>
</LocalVariablesProvider>
</RecordProvider> </RecordProvider>
</SubFormProvider> </SubFormProvider>
</FormActiveFieldsProvider> </FormActiveFieldsProvider>

View File

@ -5,7 +5,6 @@ import { flatten, getValuesByPath } from '@nocobase/utils/client';
import _, { isString } from 'lodash'; import _, { isString } from 'lodash';
import cloneDeep from 'lodash/cloneDeep'; import cloneDeep from 'lodash/cloneDeep';
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { useFormBlockContext } from '../../../block-provider';
import { mergeFilter } from '../../../block-provider/SharedFilterProvider'; import { mergeFilter } from '../../../block-provider/SharedFilterProvider';
import { useCollection, useCollectionManager } from '../../../collection-manager'; import { useCollection, useCollectionManager } from '../../../collection-manager';
import { isInFilterFormBlock } from '../../../filter-provider'; import { isInFilterFormBlock } from '../../../filter-provider';
@ -13,6 +12,7 @@ import { useRecord } from '../../../record-provider';
import { useParseDataScopeFilter } from '../../../schema-settings'; import { useParseDataScopeFilter } from '../../../schema-settings';
import { DEBOUNCE_WAIT } from '../../../variables'; import { DEBOUNCE_WAIT } from '../../../variables';
import { getPath } from '../../../variables/utils/getPath'; import { getPath } from '../../../variables/utils/getPath';
import { getVariableName } from '../../../variables/utils/getVariableName';
import { isVariable } from '../../../variables/utils/isVariable'; import { isVariable } from '../../../variables/utils/isVariable';
import { useDesignable } from '../../hooks'; import { useDesignable } from '../../hooks';
import { AssociationFieldContext } from './context'; import { AssociationFieldContext } from './context';
@ -48,17 +48,14 @@ export function useAssociationFieldContext<F extends GeneralField>() {
} }
export default function useServiceOptions(props) { export default function useServiceOptions(props) {
const { action = 'list', service, fieldNames } = props; const { action = 'list', service } = props;
const params = service?.params || {};
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const field = useField(); const field = useField();
const { getField } = useCollection(); const { getField } = useCollection();
const { getCollectionJoinField } = useCollectionManager(); const { getCollectionJoinField } = useCollectionManager();
const record = useRecord(); const record = useRecord();
const { parseFilter } = useParseDataScopeFilter(); const { parseFilter, findVariable } = useParseDataScopeFilter();
const [fieldServiceFilter, setFieldServiceFilter] = useState(null); const [fieldServiceFilter, setFieldServiceFilter] = useState(null);
const { form } = useFormBlockContext();
const { formValue: subFormValue } = useSubFormValue();
useEffect(() => { useEffect(() => {
const filterFromSchema = isString(fieldSchema?.['x-component-props']?.service?.params?.filter) const filterFromSchema = isString(fieldSchema?.['x-component-props']?.service?.params?.filter)
@ -83,10 +80,16 @@ export default function useServiceOptions(props) {
if (!isVariable(value)) { if (!isVariable(value)) {
return 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( const result = getValuesByPath(
{ {
$nForm: form?.values, [variableName]: variable?.ctx || {},
$iteration: subFormValue,
}, },
getPath(value), getPath(value),
); );
@ -100,39 +103,12 @@ export default function useServiceOptions(props) {
}, [ }, [
field.componentProps?.service?.params?.filter, field.componentProps?.service?.params?.filter,
fieldSchema, fieldSchema,
form?.values, findVariable,
parseFilter, parseFilter,
record, record,
service?.params?.filter, 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(() => { const collectionField = useMemo(() => {
return getField(fieldSchema.name) || getCollectionJoinField(fieldSchema?.['x-collection-field']); return getField(fieldSchema.name) || getCollectionJoinField(fieldSchema?.['x-collection-field']);
}, [fieldSchema]); }, [fieldSchema]);

View File

@ -3,7 +3,7 @@ import { useField, useFieldSchema } from '@formily/react';
import { reaction } from '@formily/reactive'; import { reaction } from '@formily/reactive';
import { getValuesByPath } from '@nocobase/utils/client'; import { getValuesByPath } from '@nocobase/utils/client';
import _ from 'lodash'; import _ from 'lodash';
import { useEffect } from 'react'; import { useCallback, useEffect } from 'react';
import { useRecord, useRecordIndex } from '../../../../../src/record-provider'; import { useRecord, useRecordIndex } from '../../../../../src/record-provider';
import { useFormBlockType } from '../../../../block-provider/FormBlockProvider'; import { useFormBlockType } from '../../../../block-provider/FormBlockProvider';
import { useCollection } from '../../../../collection-manager'; import { useCollection } from '../../../../collection-manager';
@ -31,6 +31,20 @@ const useParseDefaultValue = () => {
const index = useRecordIndex(); const index = useRecordIndex();
const { type: formBlockType } = useFormBlockType(); 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(() => { useEffect(() => {
if ( if (
fieldSchema.default == null || fieldSchema.default == null ||
@ -94,12 +108,16 @@ const useParseDefaultValue = () => {
if (isVariable(fieldSchema.default)) { if (isVariable(fieldSchema.default)) {
const variableName = getVariableName(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` 变量),重新解析默认值 // 实现联动的效果,当依赖的变量变化时(如 `$nForm` 变量),重新解析默认值
const dispose = reaction(() => { const dispose = reaction(() => {
const obj = { [variableName]: variableValue?.ctx }; const obj = { [variableName]: variable?.ctx || {} };
const path = getPath(fieldSchema.default); const path = getPath(fieldSchema.default);
// fix https://nocobase.height.app/T-2212 // fix https://nocobase.height.app/T-2212

View File

@ -22,7 +22,6 @@ import {
useTableSelectorContext, useTableSelectorContext,
} from '../../../'; } from '../../../';
import { useACLFieldWhitelist } from '../../../acl/ACLProvider'; import { useACLFieldWhitelist } from '../../../acl/ACLProvider';
import { LocalVariablesProvider } from '../../../variables/hooks/useLocalVariables';
import { useToken } from '../__builtins__'; import { useToken } from '../__builtins__';
import { SubFormProvider } from '../association-field/hooks'; import { SubFormProvider } from '../association-field/hooks';
import { ColumnFieldProvider } from './components/ColumnFieldProvider'; import { ColumnFieldProvider } from './components/ColumnFieldProvider';
@ -65,17 +64,15 @@ const useTableColumns = (props: { showDel?: boolean; isSubTable?: boolean }) =>
return ( return (
<SubFormProvider value={record}> <SubFormProvider value={record}>
<RecordIndexProvider index={record.__index || index}> <RecordIndexProvider index={record.__index || index}>
<LocalVariablesProvider iterationCtx={record}> <RecordProvider record={record}>
<RecordProvider record={record}> <ColumnFieldProvider schema={s} basePath={field.address.concat(record.__index || index)}>
<ColumnFieldProvider schema={s} basePath={field.address.concat(record.__index || index)}> <RecursionField
<RecursionField basePath={field.address.concat(record.__index || index)}
basePath={field.address.concat(record.__index || index)} schema={s}
schema={s} onlyRenderProperties
onlyRenderProperties />
/> </ColumnFieldProvider>
</ColumnFieldProvider> </RecordProvider>
</RecordProvider>
</LocalVariablesProvider>
</RecordIndexProvider> </RecordIndexProvider>
</SubFormProvider> </SubFormProvider>
); );

View File

@ -20,6 +20,20 @@ const useParseDataScopeFilter = ({ exclude = defaultExclude, currentRecord }: Pr
const localVariables = useLocalVariables({ currentRecord }); const localVariables = useLocalVariables({ currentRecord });
const variables = useVariables(); 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( const parseFilter = useCallback(
async (filterValue: any) => { async (filterValue: any) => {
const flat = flatten(filterValue, { const flat = flatten(filterValue, {
@ -52,7 +66,7 @@ const useParseDataScopeFilter = ({ exclude = defaultExclude, currentRecord }: Pr
[exclude, localVariables, variables?.parseVariable], [exclude, localVariables, variables?.parseVariable],
); );
return { parseFilter }; return { parseFilter, findVariable };
}; };
export default useParseDataScopeFilter; export default useParseDataScopeFilter;

View File

@ -1,8 +1,9 @@
import { Form } from '@formily/core'; import { Form } from '@formily/core';
import React, { useContext, useMemo } from 'react'; import { useMemo } from 'react';
import { useFormBlockContext } from '../../block-provider'; import { useFormBlockContext } from '../../block-provider';
import { useCollection } from '../../collection-manager'; import { useCollection } from '../../collection-manager';
import { useRecord } from '../../record-provider'; import { useRecord } from '../../record-provider';
import { useSubFormValue } from '../../schema-component/antd/association-field/hooks';
import { useBlockCollection } from '../../schema-settings/VariableInput/hooks/useBlockCollection'; import { useBlockCollection } from '../../schema-settings/VariableInput/hooks/useBlockCollection';
import { VariableOption } from '../types'; import { VariableOption } from '../types';
@ -12,21 +13,9 @@ interface Props {
currentForm?: Form; currentForm?: Form;
} }
const LocalVariablesContext = React.createContext<{ iterationCtx: Record<string, any> }>(null);
export const LocalVariablesProvider = ({
children,
iterationCtx,
}: {
iterationCtx: Record<string, any>;
children: React.ReactNode;
}) => {
return <LocalVariablesContext.Provider value={{ iterationCtx }}>{children}</LocalVariablesContext.Provider>;
};
const useLocalVariables = (props?: Props) => { const useLocalVariables = (props?: Props) => {
const { name: currentCollectionName } = useCollection(); const { name: currentCollectionName } = useCollection();
const { iterationCtx } = useContext(LocalVariablesContext) || {}; const { formValue: subFormValue } = useSubFormValue();
let { name } = useBlockCollection(); let { name } = useBlockCollection();
let currentRecord = useRecord(); let currentRecord = useRecord();
let { form } = useFormBlockContext(); let { form } = useFormBlockContext();
@ -86,10 +75,10 @@ const useLocalVariables = (props?: Props) => {
ctx: form?.values, ctx: form?.values,
collectionName: name, collectionName: name,
}, },
iterationCtx && { name: '$iteration', ctx: iterationCtx, collectionName: currentCollectionName }, subFormValue && { name: '$iteration', ctx: subFormValue, collectionName: currentCollectionName },
] as VariableOption[] ] as VariableOption[]
).filter(Boolean); ).filter(Boolean);
}, [currentRecord, name, form?.values, iterationCtx, currentCollectionName]); // 尽量保持返回的值不变,这样可以减少接口的请求次数,因为关系字段会缓存到变量的 ctx 中 }, [currentRecord, name, form?.values, subFormValue, currentCollectionName]); // 尽量保持返回的值不变,这样可以减少接口的请求次数,因为关系字段会缓存到变量的 ctx 中
}; };
export default useLocalVariables; export default useLocalVariables;