feat(variable): add current parent record (#2857)
* feat(variable): add currentParentRecord * chore: resolve table actions * chore: get collection name of parent * chore: table block and details block * chore: list block * chore: grid card block * chore: calendar * fix: bulk button * refactor: use useMemo * fix: fix form block
This commit is contained in:
parent
010c286f7c
commit
e05a30380a
@ -5,7 +5,7 @@ import { useRequest } from 'ahooks';
|
||||
import { Col, Row } from 'antd';
|
||||
import merge from 'deepmerge';
|
||||
import template from 'lodash/template';
|
||||
import React, { createContext, useContext, useEffect, useRef, useState } from 'react';
|
||||
import React, { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import {
|
||||
TableFieldResource,
|
||||
@ -285,7 +285,7 @@ export const RenderChildrenWithAssociationFilter: React.FC<any> = (props) => {
|
||||
export const BlockProvider = (props) => {
|
||||
const { collection, association } = props;
|
||||
const resource = useResource(props);
|
||||
const params = { ...props.params };
|
||||
const params = useMemo(() => ({ ...props.params }), [props.params]);
|
||||
const { appends, updateAssociationValues } = useAssociationNames();
|
||||
if (!Object.keys(params).includes('appends')) {
|
||||
params['appends'] = appends;
|
||||
|
@ -1,8 +1,11 @@
|
||||
import { ArrayField } from '@formily/core';
|
||||
import { useField } from '@formily/react';
|
||||
import _ from 'lodash';
|
||||
import React, { createContext, useContext, useEffect } from 'react';
|
||||
import { useRecord } from '../record-provider';
|
||||
import { FixedBlockWrapper } from '../schema-component';
|
||||
import { BlockProvider, useBlockRequestContext } from './BlockProvider';
|
||||
import { useParsedFilter } from './hooks';
|
||||
|
||||
export const CalendarBlockContext = createContext<any>({});
|
||||
|
||||
@ -10,6 +13,18 @@ const InternalCalendarBlockProvider = (props) => {
|
||||
const { fieldNames, showLunar } = props;
|
||||
const field = useField();
|
||||
const { resource, service } = useBlockRequestContext();
|
||||
const record = useRecord();
|
||||
|
||||
const { filter } = useParsedFilter({
|
||||
filterOption: service?.params?.[0]?.filter,
|
||||
currentRecord: { __parent: record, __collectionName: props.collection },
|
||||
});
|
||||
useEffect(() => {
|
||||
if (!_.isEmpty(filter)) {
|
||||
service?.run({ ...service?.params?.[0], filter });
|
||||
}
|
||||
}, [JSON.stringify(filter)]);
|
||||
|
||||
// if (service.loading) {
|
||||
// return <Spin />;
|
||||
// }
|
||||
|
@ -1,9 +1,11 @@
|
||||
import { createForm } from '@formily/core';
|
||||
import { useField } from '@formily/react';
|
||||
import { Spin } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import React, { createContext, useContext, useEffect, useMemo } from 'react';
|
||||
import { RecordProvider } from '../record-provider';
|
||||
import { RecordProvider, useRecord } from '../record-provider';
|
||||
import { BlockProvider, useBlockRequestContext } from './BlockProvider';
|
||||
import { useParsedFilter } from './hooks';
|
||||
|
||||
export const DetailsBlockContext = createContext<any>({});
|
||||
|
||||
@ -18,21 +20,36 @@ const InternalDetailsBlockProvider = (props) => {
|
||||
[],
|
||||
);
|
||||
const { resource, service } = useBlockRequestContext();
|
||||
if (service.loading && !field.loaded) {
|
||||
return <Spin />;
|
||||
}
|
||||
field.loaded = true;
|
||||
return (
|
||||
<DetailsBlockContext.Provider
|
||||
value={{
|
||||
const record = useRecord();
|
||||
const currentRecord = service?.data?.data?.[0] || {};
|
||||
const detailsBLockValue = useMemo(() => {
|
||||
return {
|
||||
action,
|
||||
form,
|
||||
field,
|
||||
service,
|
||||
resource,
|
||||
}}
|
||||
>
|
||||
<RecordProvider record={service?.data?.data?.[0] || {}}>{props.children}</RecordProvider>
|
||||
};
|
||||
}, [action, field, form, resource, service]);
|
||||
|
||||
const { filter } = useParsedFilter({
|
||||
filterOption: service?.params?.[0]?.filter,
|
||||
currentRecord: { ...currentRecord, __parent: record, __collectionName: props.collection },
|
||||
});
|
||||
useEffect(() => {
|
||||
if (!_.isEmpty(filter)) {
|
||||
service?.run({ ...service?.params?.[0], filter });
|
||||
}
|
||||
}, [JSON.stringify(filter)]);
|
||||
|
||||
if (service.loading && !field.loaded) {
|
||||
return <Spin />;
|
||||
}
|
||||
field.loaded = true;
|
||||
|
||||
return (
|
||||
<DetailsBlockContext.Provider value={detailsBLockValue}>
|
||||
<RecordProvider record={currentRecord}>{props.children}</RecordProvider>
|
||||
</DetailsBlockContext.Provider>
|
||||
);
|
||||
};
|
||||
@ -53,9 +70,12 @@ export const useDetailsBlockProps = () => {
|
||||
const ctx = useDetailsBlockContext();
|
||||
useEffect(() => {
|
||||
if (!ctx.service.loading) {
|
||||
ctx.form.reset().then(() => {
|
||||
ctx.form
|
||||
.reset()
|
||||
.then(() => {
|
||||
ctx.form.setValues(ctx.service?.data?.data?.[0] || {});
|
||||
});
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
}, [ctx.service.loading]);
|
||||
return {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { createForm } from '@formily/core';
|
||||
import { RecursionField, Schema, useField, useFieldSchema } from '@formily/react';
|
||||
import { Spin } from 'antd';
|
||||
import { isEmpty } from 'lodash';
|
||||
import _, { isEmpty } from 'lodash';
|
||||
import React, { createContext, useContext, useEffect, useMemo, useRef } from 'react';
|
||||
import { useCollection } from '../collection-manager';
|
||||
import { RecordProvider, useRecord } from '../record-provider';
|
||||
@ -13,7 +13,7 @@ import { FormActiveFieldsProvider } from './hooks';
|
||||
export const FormBlockContext = createContext<any>({});
|
||||
|
||||
const InternalFormBlockProvider = (props) => {
|
||||
const { action, readPretty, params } = props;
|
||||
const { action, readPretty, params, association } = props;
|
||||
const field = useField();
|
||||
const form = useMemo(
|
||||
() =>
|
||||
@ -25,13 +25,8 @@ const InternalFormBlockProvider = (props) => {
|
||||
const { resource, service, updateAssociationValues } = useBlockRequestContext();
|
||||
const formBlockRef = useRef();
|
||||
const record = useRecord();
|
||||
if (service.loading && Object.keys(form?.initialValues)?.length === 0 && action) {
|
||||
return <Spin />;
|
||||
}
|
||||
|
||||
return (
|
||||
<FormBlockContext.Provider
|
||||
value={{
|
||||
const formBlockValue = useMemo(() => {
|
||||
return {
|
||||
params,
|
||||
action,
|
||||
form,
|
||||
@ -42,21 +37,39 @@ const InternalFormBlockProvider = (props) => {
|
||||
resource,
|
||||
updateAssociationValues,
|
||||
formBlockRef,
|
||||
}}
|
||||
>
|
||||
{readPretty ? (
|
||||
<RecordProvider parent={isEmpty(record?.__parent) ? record : record?.__parent} record={service?.data?.data}>
|
||||
};
|
||||
}, [action, field, form, params, resource, service, updateAssociationValues]);
|
||||
|
||||
if (service.loading && Object.keys(form?.initialValues)?.length === 0 && action) {
|
||||
return <Spin />;
|
||||
}
|
||||
|
||||
let content = (
|
||||
<div ref={formBlockRef}>
|
||||
<RenderChildrenWithDataTemplates form={form} />
|
||||
</div>
|
||||
</RecordProvider>
|
||||
) : (
|
||||
<div ref={formBlockRef}>
|
||||
<RenderChildrenWithDataTemplates form={form} />
|
||||
</div>
|
||||
)}
|
||||
</FormBlockContext.Provider>
|
||||
);
|
||||
if (readPretty) {
|
||||
content = (
|
||||
<RecordProvider parent={isEmpty(record?.__parent) ? record : record?.__parent} record={service?.data?.data}>
|
||||
{content}
|
||||
</RecordProvider>
|
||||
);
|
||||
} else if (
|
||||
formBlockValue.type === 'create' &&
|
||||
// 点击关系表格区块的 Add new 按钮,在弹窗中新增的表单区块,是不需要重置 record 的。在这里用 record 是否为空来判断
|
||||
!_.isEmpty(_.omit(record, ['__parent', '__collectionName'])) &&
|
||||
// association 不为空,说明是关系区块
|
||||
association
|
||||
) {
|
||||
content = (
|
||||
<RecordProvider parent={record} record={{}}>
|
||||
{content}
|
||||
</RecordProvider>
|
||||
);
|
||||
}
|
||||
|
||||
return <FormBlockContext.Provider value={formBlockValue}>{content}</FormBlockContext.Provider>;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -3,10 +3,11 @@ import { FormContext, useField, useFieldSchema } from '@formily/react';
|
||||
import React, { createContext, useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { useCollectionManager } from '../collection-manager';
|
||||
import { useFilterBlock } from '../filter-provider/FilterProvider';
|
||||
import { useRecord } from '../record-provider';
|
||||
import { FixedBlockWrapper, SchemaComponentOptions, removeNullCondition } from '../schema-component';
|
||||
import { BlockProvider, RenderChildrenWithAssociationFilter, useBlockRequestContext } from './BlockProvider';
|
||||
import { mergeFilter } from './SharedFilterProvider';
|
||||
import { findFilterTargets } from './hooks';
|
||||
import { findFilterTargets, useParsedFilter } from './hooks';
|
||||
|
||||
export const TableBlockContext = createContext<any>({});
|
||||
export function getIdsWithChildren(nodes) {
|
||||
@ -74,9 +75,11 @@ const InternalTableBlockProvider = (props: Props) => {
|
||||
|
||||
export const TableBlockProvider = (props) => {
|
||||
const resourceName = props.resource;
|
||||
const params = { ...props.params };
|
||||
const params = useMemo(() => ({ ...props.params }), [props.params]);
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { getCollection, getCollectionField } = useCollectionManager();
|
||||
const record = useRecord();
|
||||
|
||||
const collection = getCollection(props.collection);
|
||||
const { treeTable } = fieldSchema?.['x-decorator-props'] || {};
|
||||
if (props.dragSort) {
|
||||
@ -99,12 +102,22 @@ export const TableBlockProvider = (props) => {
|
||||
}
|
||||
}
|
||||
const form = useMemo(() => createForm(), [treeTable]);
|
||||
const { filter: parsedFilter } = useParsedFilter({
|
||||
filterOption: params?.filter,
|
||||
currentRecord: { __parent: record, __collectionName: props.collection },
|
||||
});
|
||||
const paramsWithFilter = useMemo(() => {
|
||||
return {
|
||||
...params,
|
||||
filter: parsedFilter,
|
||||
};
|
||||
}, [parsedFilter, params]);
|
||||
|
||||
return (
|
||||
<SchemaComponentOptions scope={{ treeTable }}>
|
||||
<FormContext.Provider value={form}>
|
||||
<BlockProvider data-testid="table-block" {...props} params={params} runWhenParamsChanged>
|
||||
<InternalTableBlockProvider {...props} childrenColumnName={childrenColumnName} params={params} />
|
||||
<BlockProvider data-testid="table-block" {...props} params={paramsWithFilter} runWhenParamsChanged>
|
||||
<InternalTableBlockProvider {...props} childrenColumnName={childrenColumnName} params={paramsWithFilter} />
|
||||
</BlockProvider>
|
||||
</FormContext.Provider>
|
||||
</SchemaComponentOptions>
|
||||
|
@ -26,6 +26,7 @@ import { mergeFilter } from '../SharedFilterProvider';
|
||||
import { TableFieldResource } from '../TableFieldProvider';
|
||||
|
||||
export * from './useFormActiveFields';
|
||||
export * from './useParsedFilter';
|
||||
|
||||
export const usePickActionProps = () => {
|
||||
const form = useForm();
|
||||
@ -587,8 +588,9 @@ export const useCustomizeBulkUpdateActionProps = () => {
|
||||
const actionField = useField();
|
||||
const { modal } = App.useApp();
|
||||
const variables = useVariables();
|
||||
const localVariables = useLocalVariables();
|
||||
const record = useRecord();
|
||||
const { name, getField } = useCollection();
|
||||
const localVariables = useLocalVariables({ currentRecord: { __parent: record, __collectionName: name } });
|
||||
|
||||
return {
|
||||
async onClick() {
|
||||
|
@ -0,0 +1,45 @@
|
||||
import { reaction } from '@formily/reactive';
|
||||
import { flatten } 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 { isVariable } from '../../variables/utils/isVariable';
|
||||
import { useFormBlockContext } from '../FormBlockProvider';
|
||||
|
||||
export function useParsedFilter({ filterOption, currentRecord }: { filterOption: any; currentRecord?: any }) {
|
||||
const { parseFilter } = useParseDataScopeFilter({ currentRecord });
|
||||
const [filter, setFilter] = useState({});
|
||||
const { form } = useFormBlockContext();
|
||||
|
||||
useEffect(() => {
|
||||
if (!filterOption) return;
|
||||
|
||||
const _run = async () => {
|
||||
const result = await parseFilter(filterOption);
|
||||
setFilter(result);
|
||||
};
|
||||
_run();
|
||||
const run = _.debounce(_run, DEBOUNCE_WAIT);
|
||||
|
||||
reaction(() => {
|
||||
// 这一步主要是为了使 reaction 能够收集到依赖
|
||||
const flat = flatten(filterOption, {
|
||||
breakOn({ key }) {
|
||||
return key.startsWith('$') && key !== '$and' && key !== '$or';
|
||||
},
|
||||
transformValue(value) {
|
||||
if (!isVariable(value)) {
|
||||
return value;
|
||||
}
|
||||
const result = _.get({ $nForm: form?.values }, getPath(value));
|
||||
return result;
|
||||
},
|
||||
});
|
||||
return flat;
|
||||
}, run);
|
||||
}, [JSON.stringify(filterOption)]);
|
||||
|
||||
return { filter };
|
||||
}
|
@ -79,7 +79,10 @@ const getSchema = (schema: IField, record: any, compile, getContainer): ISchema
|
||||
export const useValuesFromRecord = (options) => {
|
||||
const record = useRecord();
|
||||
const result = useRequest(
|
||||
() => Promise.resolve({ data: { ...omit(record, ['__parent']), category: record?.category.map((v) => v.id) } }),
|
||||
() =>
|
||||
Promise.resolve({
|
||||
data: { ...omit(record, ['__parent', '__collectionName']), category: record?.category.map((v) => v.id) },
|
||||
}),
|
||||
{
|
||||
...options,
|
||||
manual: true,
|
||||
|
@ -23,7 +23,7 @@ export const useCancelAction = () => {
|
||||
|
||||
export const useValuesFromRecord = (options) => {
|
||||
const record = useRecord();
|
||||
const result = useRequest(() => Promise.resolve({ data: omit(record, ['__parent']) }), {
|
||||
const result = useRequest(() => Promise.resolve({ data: omit(record, ['__parent', '__collectionName']) }), {
|
||||
...options,
|
||||
manual: true,
|
||||
});
|
||||
|
@ -61,6 +61,11 @@ export const FilterBlockProvider: React.FC = ({ children }) => {
|
||||
return <FilterContext.Provider value={{ dataBlocks, setDataBlocks }}>{children}</FilterContext.Provider>;
|
||||
};
|
||||
|
||||
/**
|
||||
* 用于收集记录当前页面中的数据区块的信息
|
||||
* @param param0
|
||||
* @returns
|
||||
*/
|
||||
export const FilterBlockRecord = ({
|
||||
children,
|
||||
params,
|
||||
|
@ -619,6 +619,7 @@ export default {
|
||||
"Dynamic value": "Dynamic value",
|
||||
"Current user": "Current user",
|
||||
"Current record": "Current record",
|
||||
"Current parent record": "Current parent record",
|
||||
"Current time": "Current time",
|
||||
"System variables": "System variables",
|
||||
"Date variables": "Date variables",
|
||||
|
@ -603,6 +603,7 @@ export default {
|
||||
"Dynamic value": "Valor dinámico",
|
||||
"Current user": "Usuario actual",
|
||||
"Current record": "Registro actual",
|
||||
"Current parent record": "Registro padre actual",
|
||||
"Current time": "Hora actual",
|
||||
"System variables": "Variables del sistema",
|
||||
"Date variables": "Variables de fecha",
|
||||
|
@ -600,6 +600,7 @@ export default {
|
||||
"Dynamic value": "Valeur dynamique",
|
||||
"Current user": "Utilisateur actuel",
|
||||
"Current record": "Enregistrement actuel",
|
||||
"Current parent record": "Enregistrement parent actuel",
|
||||
"Current time": "Heure actuelle",
|
||||
"System variables": "Variables système",
|
||||
"Date variables": "Variables de date",
|
||||
|
@ -502,6 +502,7 @@ export default {
|
||||
"Dynamic value": "動的値",
|
||||
"Current user": "現在のユーザー",
|
||||
"Current record": "現在のレコード",
|
||||
"Current parent record": "現在の親レコード",
|
||||
"Popup close method": "ポップアップを閉じる方法",
|
||||
"Automatic close": "自動で閉じる",
|
||||
"Manually close": "手動で閉じる",
|
||||
|
@ -508,6 +508,7 @@ export default {
|
||||
"Single select and radio fields can be used as the grouping field": "Campos de seleção única e de rádio podem ser usados como o campo de agrupamento",
|
||||
"Tab name": "Nome da aba",
|
||||
"Current record blocks": "Blocos de registro atual",
|
||||
"Current parent record": "Registro pai atual",
|
||||
"Popup message": "Mensagem pop-up",
|
||||
"Delete role": "Excluir função",
|
||||
"Role display name": "Nome de exibição da função",
|
||||
|
@ -439,6 +439,7 @@ export default {
|
||||
"Dynamic value": "Динамическое значение",
|
||||
"Current user": "Текущий пользователь",
|
||||
"Current record": "Текущая запись",
|
||||
"Current parent record": "Текущая родительская запись",
|
||||
"Popup close method": "Метод закрытия всплывающего окна",
|
||||
"Automatic close": "Автоматическое закрытие",
|
||||
"Manually close": "Ручное закрытие",
|
||||
|
@ -438,6 +438,7 @@ export default {
|
||||
"Dynamic value": "Dinamik değer",
|
||||
"Current user": "Seçili kullanıcı",
|
||||
"Current record": "Seçili kayıt",
|
||||
"Current parent record": "Seçili üst kayıt",
|
||||
"Popup close method": "Açılır pencere kapatma metodu",
|
||||
"Automatic close": "Otomatik kapat",
|
||||
"Manually close": "Manuel kapat",
|
||||
|
@ -620,6 +620,7 @@ export default {
|
||||
"Dynamic value": "Динамічне значення",
|
||||
"Current user": "Поточний користувач",
|
||||
"Current record": "Поточний запис",
|
||||
"Current parent record": "Поточний батьківський запис",
|
||||
"Current time": "Поточний час",
|
||||
"System variables": "Системні змінні",
|
||||
"Date variables": "Змінні дати",
|
||||
|
@ -661,6 +661,7 @@ export default {
|
||||
'Dynamic value': '动态值',
|
||||
'Current user': '当前用户',
|
||||
'Current record': '当前记录',
|
||||
"Current parent record": "当前父记录",
|
||||
'Current time': '当前时间',
|
||||
Now: '现在',
|
||||
'Popup close method': '弹窗关闭方式',
|
||||
|
@ -1,14 +1,17 @@
|
||||
import React, { createContext, useContext } from 'react';
|
||||
import { useCollection } from '../collection-manager';
|
||||
import { useCurrentUserContext } from '../user';
|
||||
|
||||
export const RecordContext = createContext({});
|
||||
export const RecordIndexContext = createContext(null);
|
||||
|
||||
export const RecordProvider: React.FC<{ record: any; parent?: any }> = (props) => {
|
||||
const { record, children, parent = false } = props;
|
||||
export const RecordProvider: React.FC<{ record: any; parent?: any; collectionName?: string }> = (props) => {
|
||||
const { record, children, collectionName, parent = false } = props;
|
||||
const { name: __collectionName } = useCollection();
|
||||
const __parent = useContext(RecordContext);
|
||||
const value = { ...record };
|
||||
value['__parent'] = parent ? parent : __parent;
|
||||
value['__collectionName'] = collectionName || __collectionName;
|
||||
return <RecordContext.Provider value={value}>{children}</RecordContext.Provider>;
|
||||
};
|
||||
|
||||
|
@ -8,7 +8,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { useActionContext } from '../..';
|
||||
import { useDesignable } from '../../';
|
||||
import { Icon } from '../../../icon';
|
||||
import { useRecord } from '../../../record-provider';
|
||||
import { RecordProvider, useRecord } from '../../../record-provider';
|
||||
import { useLocalVariables, useVariables } from '../../../variables';
|
||||
import { SortableItem } from '../../common';
|
||||
import { useCompile, useComponent, useDesigner } from '../../hooks';
|
||||
@ -52,7 +52,7 @@ export const Action: ComposedAction = observer(
|
||||
const fieldSchema = useFieldSchema();
|
||||
const compile = useCompile();
|
||||
const form = useForm();
|
||||
const values = useRecord();
|
||||
const record = useRecord();
|
||||
const designerProps = fieldSchema['x-designer-props'];
|
||||
const openMode = fieldSchema?.['x-component-props']?.['openMode'];
|
||||
const disabled = form.disabled || field.disabled || field.data?.disabled || props.disabled;
|
||||
@ -61,7 +61,12 @@ export const Action: ComposedAction = observer(
|
||||
const tarComponent = useComponent(component) || component;
|
||||
const { modal } = App.useApp();
|
||||
const variables = useVariables();
|
||||
const localVariables = useLocalVariables({ currentForm: { values } as any });
|
||||
const localVariables = useLocalVariables({ currentForm: { values: record } as any });
|
||||
|
||||
// fix https://nocobase.height.app/T-2259
|
||||
const shouldResetRecord = ['create', 'customize:bulkUpdate', 'customize:bulkEdit', 'customize:create'].includes(
|
||||
fieldSchema['x-action'],
|
||||
);
|
||||
|
||||
let actionTitle = title || compile(fieldSchema.title);
|
||||
actionTitle = lodash.isString(actionTitle) ? t(actionTitle) : actionTitle;
|
||||
@ -75,13 +80,13 @@ export const Action: ComposedAction = observer(
|
||||
operator: h.operator,
|
||||
field,
|
||||
condition: v.condition,
|
||||
values,
|
||||
values: record,
|
||||
variables,
|
||||
localVariables,
|
||||
});
|
||||
});
|
||||
});
|
||||
}, [JSON.stringify(linkageRules), values, designable, field]);
|
||||
}, [JSON.stringify(linkageRules), record, designable, field]);
|
||||
|
||||
const handleButtonClick = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
@ -142,7 +147,7 @@ export const Action: ComposedAction = observer(
|
||||
);
|
||||
};
|
||||
|
||||
return wrapSSR(
|
||||
const result = (
|
||||
<ActionContextProvider
|
||||
button={renderButton()}
|
||||
visible={visible}
|
||||
@ -158,7 +163,17 @@ export const Action: ComposedAction = observer(
|
||||
{!popover && renderButton()}
|
||||
{!popover && props.children}
|
||||
{element}
|
||||
</ActionContextProvider>,
|
||||
</ActionContextProvider>
|
||||
);
|
||||
|
||||
return wrapSSR(
|
||||
shouldResetRecord ? (
|
||||
<RecordProvider parent={record} record={{}}>
|
||||
{result}
|
||||
</RecordProvider>
|
||||
) : (
|
||||
result
|
||||
),
|
||||
);
|
||||
},
|
||||
{ displayName: 'Action' },
|
||||
|
@ -15,7 +15,6 @@ import { DEBOUNCE_WAIT } from '../../../variables';
|
||||
import { getPath } from '../../../variables/utils/getPath';
|
||||
import { isVariable } from '../../../variables/utils/isVariable';
|
||||
import { useDesignable } from '../../hooks';
|
||||
import { removeNullCondition } from '../filter';
|
||||
import { AssociationFieldContext } from './context';
|
||||
|
||||
export const useInsertSchema = (component) => {
|
||||
|
@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { FixedBlockDesignerItem, removeNullCondition, useDesignable } from '../..';
|
||||
import { useCalendarBlockContext, useFormBlockContext } from '../../../block-provider';
|
||||
import { useCollection, useCollectionManager } from '../../../collection-manager';
|
||||
import { RecordProvider, useRecord } from '../../../record-provider';
|
||||
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
|
||||
import { useSchemaTemplate } from '../../../schema-templates';
|
||||
|
||||
@ -17,10 +18,12 @@ export const CalendarDesigner = () => {
|
||||
const { dn } = useDesignable();
|
||||
const { t } = useTranslation();
|
||||
const template = useSchemaTemplate();
|
||||
const record = useRecord();
|
||||
const fieldNames = fieldSchema?.['x-decorator-props']?.['fieldNames'] || {};
|
||||
const defaultResource = fieldSchema?.['x-decorator-props']?.resource;
|
||||
|
||||
return (
|
||||
<RecordProvider parent={record} record={{}}>
|
||||
<GeneralSchemaDesigner template={template} title={title || name}>
|
||||
<SchemaSettings.BlockTitleItem />
|
||||
<SchemaSettings.SelectItem
|
||||
@ -131,5 +134,6 @@ export const CalendarDesigner = () => {
|
||||
}}
|
||||
/>
|
||||
</GeneralSchemaDesigner>
|
||||
</RecordProvider>
|
||||
);
|
||||
};
|
||||
|
@ -735,7 +735,7 @@ FormItem.FilterFormDesigner = FilterFormDesigner;
|
||||
|
||||
function useIsAddNewForm() {
|
||||
const record = useRecord();
|
||||
const isAddNewForm = _.isEmpty(_.omit(record, '__parent'));
|
||||
const isAddNewForm = _.isEmpty(_.omit(record, ['__parent', '__collectionName']));
|
||||
|
||||
return isAddNewForm;
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ const useLazyLoadAssociationFieldOfForm = () => {
|
||||
|
||||
const cloneRecord = { ...record };
|
||||
delete cloneRecord['__parent'];
|
||||
delete cloneRecord['__collectionName'];
|
||||
|
||||
if (_.isEmpty(cloneRecord) || !variables || record[schemaName] != null) {
|
||||
return;
|
||||
|
@ -2,7 +2,8 @@ import { FormLayout } from '@formily/antd-v5';
|
||||
import { createForm } from '@formily/core';
|
||||
import { FormContext, useField } from '@formily/react';
|
||||
import React, { createContext, useContext, useEffect, useMemo } from 'react';
|
||||
import { BlockProvider, useBlockRequestContext } from '../../../block-provider';
|
||||
import { BlockProvider, useBlockRequestContext, useParsedFilter } from '../../../block-provider';
|
||||
import { useRecord } from '../../../record-provider';
|
||||
import useStyles from './GridCard.Decorator.style';
|
||||
export const GridCardBlockContext = createContext<any>({});
|
||||
|
||||
@ -41,8 +42,22 @@ const InternalGridCardBlockProvider = (props) => {
|
||||
};
|
||||
|
||||
export const GridCardBlockProvider = (props) => {
|
||||
const { params } = props;
|
||||
const record = useRecord();
|
||||
|
||||
const { filter: parsedFilter } = useParsedFilter({
|
||||
filterOption: params?.filter,
|
||||
currentRecord: { __parent: record, __collectionName: props.collection },
|
||||
});
|
||||
const paramsWithFilter = useMemo(() => {
|
||||
return {
|
||||
...params,
|
||||
filter: parsedFilter,
|
||||
};
|
||||
}, [parsedFilter, params]);
|
||||
|
||||
return (
|
||||
<BlockProvider data-testid="grid-card-block" {...props}>
|
||||
<BlockProvider data-testid="grid-card-block" {...props} params={paramsWithFilter}>
|
||||
<InternalGridCardBlockProvider {...props} />
|
||||
</BlockProvider>
|
||||
);
|
||||
|
@ -6,6 +6,7 @@ import React, { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useFormBlockContext } from '../../../block-provider';
|
||||
import { useCollection, useSortFields } from '../../../collection-manager';
|
||||
import { RecordProvider, useRecord } from '../../../record-provider';
|
||||
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
|
||||
import { useSchemaTemplate } from '../../../schema-templates';
|
||||
import { SchemaComponentOptions } from '../../core';
|
||||
@ -27,6 +28,7 @@ export const GridCardDesigner = () => {
|
||||
const field = useField();
|
||||
const { dn } = useDesignable();
|
||||
const sortFields = useSortFields(name);
|
||||
const record = useRecord();
|
||||
const defaultSort = fieldSchema?.['x-decorator-props']?.params?.sort || [];
|
||||
const defaultResource = fieldSchema?.['x-decorator-props']?.resource;
|
||||
const columnCount = field.decoratorProps.columnCount || defaultColumnCount;
|
||||
@ -70,6 +72,8 @@ export const GridCardDesigner = () => {
|
||||
};
|
||||
});
|
||||
return (
|
||||
// fix https://nocobase.height.app/T-2259
|
||||
<RecordProvider parent={record} record={{}}>
|
||||
<GeneralSchemaDesigner template={template} title={title || name}>
|
||||
<SchemaComponentOptions components={{ Slider }}>
|
||||
<SchemaSettings.ModalItem
|
||||
@ -224,5 +228,6 @@ export const GridCardDesigner = () => {
|
||||
/>
|
||||
</SchemaComponentOptions>
|
||||
</GeneralSchemaDesigner>
|
||||
</RecordProvider>
|
||||
);
|
||||
};
|
||||
|
@ -3,7 +3,8 @@ import { FormLayout } from '@formily/antd-v5';
|
||||
import { createForm } from '@formily/core';
|
||||
import { FormContext, useField } from '@formily/react';
|
||||
import React, { createContext, useContext, useEffect, useMemo } from 'react';
|
||||
import { BlockProvider, useBlockRequestContext } from '../../../block-provider';
|
||||
import { BlockProvider, useBlockRequestContext, useParsedFilter } from '../../../block-provider';
|
||||
import { useRecord } from '../../../record-provider';
|
||||
|
||||
export const ListBlockContext = createContext<any>({});
|
||||
|
||||
@ -51,8 +52,22 @@ const InternalListBlockProvider = (props) => {
|
||||
};
|
||||
|
||||
export const ListBlockProvider = (props) => {
|
||||
const { params } = props;
|
||||
const record = useRecord();
|
||||
|
||||
const { filter: parsedFilter } = useParsedFilter({
|
||||
filterOption: params?.filter,
|
||||
currentRecord: { __parent: record, __collectionName: props.collection },
|
||||
});
|
||||
const paramsWithFilter = useMemo(() => {
|
||||
return {
|
||||
...params,
|
||||
filter: parsedFilter,
|
||||
};
|
||||
}, [parsedFilter, params]);
|
||||
|
||||
return (
|
||||
<BlockProvider data-testid="list-block" {...props}>
|
||||
<BlockProvider data-testid="list-block" {...props} params={paramsWithFilter}>
|
||||
<InternalListBlockProvider {...props} />
|
||||
</BlockProvider>
|
||||
);
|
||||
|
@ -5,6 +5,7 @@ import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useFormBlockContext } from '../../../block-provider';
|
||||
import { useCollection, useSortFields } from '../../../collection-manager';
|
||||
import { RecordProvider, useRecord } from '../../../record-provider';
|
||||
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
|
||||
import { useSchemaTemplate } from '../../../schema-templates';
|
||||
import { useDesignable } from '../../hooks';
|
||||
@ -19,6 +20,7 @@ export const ListDesigner = () => {
|
||||
const field = useField();
|
||||
const { dn } = useDesignable();
|
||||
const sortFields = useSortFields(name);
|
||||
const record = useRecord();
|
||||
const defaultSort = fieldSchema?.['x-decorator-props']?.params?.sort || [];
|
||||
const defaultResource = fieldSchema?.['x-decorator-props']?.resource;
|
||||
const sort = defaultSort?.map((item: string) => {
|
||||
@ -33,6 +35,8 @@ export const ListDesigner = () => {
|
||||
};
|
||||
});
|
||||
return (
|
||||
// fix https://nocobase.height.app/T-2259
|
||||
<RecordProvider parent={record} record={{}}>
|
||||
<GeneralSchemaDesigner template={template} title={title || name}>
|
||||
<SchemaSettings.BlockTitleItem />
|
||||
<SchemaSettings.DataScope
|
||||
@ -170,5 +174,6 @@ export const ListDesigner = () => {
|
||||
}}
|
||||
/>
|
||||
</GeneralSchemaDesigner>
|
||||
</RecordProvider>
|
||||
);
|
||||
};
|
||||
|
@ -7,6 +7,7 @@ import { mergeFilter } from '../../../block-provider/SharedFilterProvider';
|
||||
import { useCollection, useCollectionManager } from '../../../collection-manager';
|
||||
import { useSortFields } from '../../../collection-manager/action-hooks';
|
||||
import { FilterBlockType } from '../../../filter-provider/utils';
|
||||
import { RecordProvider, useRecord } from '../../../record-provider';
|
||||
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
|
||||
import { useSchemaTemplate } from '../../../schema-templates';
|
||||
import { useDesignable } from '../../hooks';
|
||||
@ -23,6 +24,8 @@ export const TableBlockDesigner = () => {
|
||||
const { service } = useTableBlockContext();
|
||||
const { t } = useTranslation();
|
||||
const { dn } = useDesignable();
|
||||
const record = useRecord();
|
||||
|
||||
const defaultSort = fieldSchema?.['x-decorator-props']?.params?.sort || [];
|
||||
const defaultResource = fieldSchema?.['x-decorator-props']?.resource;
|
||||
const supportTemplate = !fieldSchema?.['x-decorator-props']?.disableTemplate;
|
||||
@ -64,6 +67,8 @@ export const TableBlockDesigner = () => {
|
||||
[dn, field.decoratorProps, fieldSchema, service],
|
||||
);
|
||||
return (
|
||||
// fix https://nocobase.height.app/T-2259
|
||||
<RecordProvider parent={record} record={{}}>
|
||||
<GeneralSchemaDesigner template={template} title={title || name}>
|
||||
<SchemaSettings.BlockTitleItem />
|
||||
{collection?.tree && collectionField?.collectionName === collectionField?.target && (
|
||||
@ -241,5 +246,6 @@ export const TableBlockDesigner = () => {
|
||||
}}
|
||||
/>
|
||||
</GeneralSchemaDesigner>
|
||||
</RecordProvider>
|
||||
);
|
||||
};
|
||||
|
@ -0,0 +1,29 @@
|
||||
import { Schema } from '@formily/json-schema';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { CollectionFieldOptions } from '../../../collection-manager';
|
||||
import { useBaseVariable } from './useBaseVariable';
|
||||
|
||||
interface Props {
|
||||
collectionField: CollectionFieldOptions;
|
||||
schema: any;
|
||||
collectionName: string;
|
||||
noDisabled?: boolean;
|
||||
/** 消费变量值的字段 */
|
||||
targetFieldSchema?: Schema;
|
||||
}
|
||||
|
||||
export const useParentRecordVariable = (props: Props) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const currentRecordVariable = useBaseVariable({
|
||||
collectionField: props.collectionField,
|
||||
uiSchema: props.schema,
|
||||
name: '$nParentRecord',
|
||||
title: t('Current parent record'),
|
||||
collectionName: props.collectionName,
|
||||
noDisabled: props.noDisabled,
|
||||
targetFieldSchema: props.targetFieldSchema,
|
||||
});
|
||||
|
||||
return currentRecordVariable;
|
||||
};
|
@ -1,13 +1,14 @@
|
||||
import { Form } from '@formily/core';
|
||||
import { ISchema, Schema } from '@formily/react';
|
||||
import _ from 'lodash';
|
||||
import { useMemo } from 'react';
|
||||
import { useFormBlockType } from '../../../block-provider/FormBlockProvider';
|
||||
import { CollectionFieldOptions, useCollection } from '../../../collection-manager';
|
||||
import { useFlag } from '../../../flag-provider';
|
||||
import { useBlockCollection } from './useBlockCollection';
|
||||
import { useDateVariable } from './useDateVariable';
|
||||
import { useFormVariable } from './useFormVariable';
|
||||
import { useIterationVariable } from './useIterationVariable';
|
||||
import { useParentRecordVariable } from './useParentRecordVariable';
|
||||
import { useRecordVariable } from './useRecordVariable';
|
||||
import { useUserVariable } from './useUserVariable';
|
||||
|
||||
@ -44,11 +45,12 @@ export const useVariableOptions = ({
|
||||
operator,
|
||||
noDisabled,
|
||||
targetFieldSchema,
|
||||
record,
|
||||
}: Props) => {
|
||||
const { name: blockCollectionName } = useBlockCollection();
|
||||
const { name: blockCollectionName = record?.__collectionName } = useBlockCollection();
|
||||
const { isInSubForm, isInSubTable } = useFlag() || {};
|
||||
const { type: formBlockType } = useFormBlockType();
|
||||
const { name } = useCollection();
|
||||
const blockParentCollectionName = record?.__parent?.__collectionName;
|
||||
const userVariable = useUserVariable({
|
||||
maxDepth: 3,
|
||||
uiSchema: uiSchema,
|
||||
@ -78,6 +80,13 @@ export const useVariableOptions = ({
|
||||
noDisabled,
|
||||
targetFieldSchema,
|
||||
});
|
||||
const currentParentRecordVariable = useParentRecordVariable({
|
||||
schema: uiSchema,
|
||||
collectionName: blockParentCollectionName,
|
||||
collectionField,
|
||||
noDisabled,
|
||||
targetFieldSchema,
|
||||
});
|
||||
|
||||
return useMemo(() => {
|
||||
return [
|
||||
@ -85,7 +94,10 @@ export const useVariableOptions = ({
|
||||
dateVariable,
|
||||
form && !form.readPretty && formVariable,
|
||||
(isInSubForm || isInSubTable) && iterationVariable,
|
||||
formBlockType === 'update' && currentRecordVariable,
|
||||
blockCollectionName && !_.isEmpty(_.omit(record, ['__parent', '__collectionName'])) && currentRecordVariable,
|
||||
blockParentCollectionName &&
|
||||
!_.isEmpty(_.omit(record?.__parent, ['__parent', '__collectionName'])) &&
|
||||
currentParentRecordVariable,
|
||||
].filter(Boolean);
|
||||
}, [
|
||||
userVariable,
|
||||
@ -95,6 +107,10 @@ export const useVariableOptions = ({
|
||||
isInSubForm,
|
||||
isInSubTable,
|
||||
iterationVariable,
|
||||
blockCollectionName,
|
||||
record,
|
||||
currentRecordVariable,
|
||||
blockParentCollectionName,
|
||||
currentParentRecordVariable,
|
||||
]);
|
||||
};
|
||||
|
@ -9,14 +9,15 @@ interface Props {
|
||||
* 需要排除的变量名称,例如:['$user', '$date']
|
||||
* 被排除的变量不会被解析,会按原值返回
|
||||
*/
|
||||
exclude: string[];
|
||||
exclude?: string[];
|
||||
currentRecord?: any;
|
||||
}
|
||||
|
||||
// TODO: 建议变量名统一命名为 `$n` 开头,以防止与 formily 内置变量冲突
|
||||
const defaultExclude = ['$user', '$date', '$nDate'];
|
||||
|
||||
const useParseDataScopeFilter = ({ exclude }: Props = { exclude: defaultExclude }) => {
|
||||
const localVariables = useLocalVariables();
|
||||
const useParseDataScopeFilter = ({ exclude = defaultExclude, currentRecord }: Props = {}) => {
|
||||
const localVariables = useLocalVariables({ currentRecord });
|
||||
const variables = useVariables();
|
||||
|
||||
const parseFilter = useCallback(
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { Form } from '@formily/core';
|
||||
import _ from 'lodash';
|
||||
import React, { useContext, useMemo } from 'react';
|
||||
import { useFormBlockContext } from '../../block-provider';
|
||||
import { useCollection } from '../../collection-manager';
|
||||
@ -42,12 +41,6 @@ const useLocalVariables = (props?: Props) => {
|
||||
name = props.collectionName;
|
||||
}
|
||||
|
||||
let blockRecord = currentRecord;
|
||||
// 获取到最顶层的 record,即当前区块所在的 record
|
||||
while (!_.isEmpty(blockRecord.__parent)) {
|
||||
blockRecord = blockRecord.__parent;
|
||||
}
|
||||
|
||||
return useMemo(() => {
|
||||
return (
|
||||
[
|
||||
@ -57,7 +50,7 @@ const useLocalVariables = (props?: Props) => {
|
||||
*/
|
||||
{
|
||||
name: 'currentRecord',
|
||||
ctx: blockRecord,
|
||||
ctx: currentRecord,
|
||||
collectionName: name,
|
||||
},
|
||||
/**
|
||||
@ -66,7 +59,7 @@ const useLocalVariables = (props?: Props) => {
|
||||
*/
|
||||
{
|
||||
name,
|
||||
ctx: form?.values || blockRecord,
|
||||
ctx: form?.values || currentRecord,
|
||||
collectionName: name,
|
||||
},
|
||||
/**
|
||||
@ -80,8 +73,13 @@ const useLocalVariables = (props?: Props) => {
|
||||
},
|
||||
{
|
||||
name: '$nRecord',
|
||||
ctx: blockRecord,
|
||||
collectionName: name,
|
||||
ctx: currentRecord,
|
||||
collectionName: currentRecord?.__collectionName,
|
||||
},
|
||||
{
|
||||
name: '$nParentRecord',
|
||||
ctx: currentRecord?.__parent,
|
||||
collectionName: currentRecord?.__parent?.__collectionName,
|
||||
},
|
||||
{
|
||||
name: '$nForm',
|
||||
@ -91,7 +89,7 @@ const useLocalVariables = (props?: Props) => {
|
||||
iterationCtx && { name: '$iteration', ctx: iterationCtx, collectionName: currentCollectionName },
|
||||
] as VariableOption[]
|
||||
).filter(Boolean);
|
||||
}, [blockRecord, name, form?.values, iterationCtx, currentCollectionName]); // 尽量保持返回的值不变,这样可以减少接口的请求次数,因为关系字段会缓存到变量的 ctx 中
|
||||
}, [currentRecord, name, form?.values, iterationCtx, currentCollectionName]); // 尽量保持返回的值不变,这样可以减少接口的请求次数,因为关系字段会缓存到变量的 ctx 中
|
||||
};
|
||||
|
||||
export default useLocalVariables;
|
||||
|
@ -148,7 +148,6 @@ export function useTriggerWorkflowsActionProps() {
|
||||
|
||||
return {
|
||||
async onClick() {
|
||||
const fieldNames = fields.map((field) => field.name);
|
||||
const {
|
||||
assignedValues: originalAssignedValues = {},
|
||||
onSuccess,
|
||||
|
Loading…
Reference in New Issue
Block a user