feat(custom-request): support selected table records in custom request (#6647)

* feat: support selected table records in custom request

* fix: test

* fix: bug

* fix: bug

* fix: bug

* fix: bug

* fix: merge bug

* fix: e2e test

* fix: e2e test
This commit is contained in:
Katherine 2025-04-17 12:35:03 +08:00 committed by GitHub
parent 1c343cad66
commit f5fb2844da
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 96 additions and 48 deletions

View File

@ -57,6 +57,7 @@ test.describe('add blocks to the popup', () => {
await page.getByRole('menuitem', { name: 'Details right' }).hover();
await page.getByRole('menuitem', { name: 'Associated records' }).last().hover();
await page.getByRole('menuitem', { name: 'Roles' }).click();
await page.mouse.move(300, 0);
await page.getByLabel('schema-initializer-Grid-details:configureFields-roles').hover();
await page.getByRole('menuitem', { name: 'Role UID' }).click();
await page.mouse.move(300, 0);

View File

@ -71,7 +71,7 @@ test.describe('sub page', () => {
expect(page.url()).not.toContain('/popups/');
// 确认是否回到了主页面
await page.getByText('Users单层子页面Configure').hover();
// await page.getByText('Users单层子页面Configure').hover();
await expect(
page.getByRole('button', { name: 'designer-schema-settings-CardItem-blockSettings:table-users' }),
).toBeVisible();

View File

@ -139,11 +139,13 @@ export const Action: ComposedAction = withDynamicSchemaProps(
);
const handleClick = useMemo(() => {
return onClick && (async (e, callback) => {
return (
onClick &&
(async (e, callback) => {
await onClick?.(e, callback);
// 执行完 onClick 之后,刷新数据区块
const blocksToRefresh = fieldSchema['x-action-settings']?.onSuccess?.blocksToRefresh || []
const blocksToRefresh = fieldSchema['x-action-settings']?.onSuccess?.blocksToRefresh || [];
if (blocksToRefresh.length > 0) {
getAllDataBlocks().forEach((block) => {
if (blocksToRefresh.includes(block.uid)) {
@ -155,7 +157,8 @@ export const Action: ComposedAction = withDynamicSchemaProps(
}
});
}
});
})
);
}, [onClick, fieldSchema, getAllDataBlocks]);
return (

View File

@ -900,13 +900,19 @@ export const SchemaSettingsModalItem: FC<SchemaSettingsModalItemProps> = (props)
>
<LocationSearchContext.Provider value={locationSearch}>
<BlockRequestContext_deprecated.Provider value={ctx}>
<DataSourceApplicationProvider dataSourceManager={dm} dataSource={dataSourceKey}>
<DataSourceApplicationProvider
dataSourceManager={dm}
dataSource={dataSourceKey}
>
<AssociationOrCollectionProvider
allowNull
collection={collection?.name}
association={association}
>
<SchemaComponentOptions scope={options.scope} components={options.components}>
<SchemaComponentOptions
scope={options.scope}
components={options.components}
>
<FormLayout
layout={'vertical'}
className={css`

View File

@ -15,3 +15,4 @@ export * from './useURLSearchParamsVariable';
export * from './useUserVariable';
export * from './useVariableOptions';
export * from './usePopupVariable';
export * from './useContextAssociationFields';

View File

@ -46,7 +46,9 @@ const getChildren = (
return {
key: option.name,
value: option.name,
name: option.name,
label: compile(option.title),
title: compile(option.title),
disabled: disabled,
isLeaf: true,
depth,
@ -59,7 +61,9 @@ const getChildren = (
return {
key: option.name,
value: option.name,
name: option.name,
label: compile(option.title),
title: compile(option.title),
disabled: disabled,
isLeaf: true,
field: option,
@ -77,10 +81,10 @@ export const useContextAssociationFields = ({
contextCollectionName,
collectionField,
}: {
schema: any;
schema?: any;
maxDepth?: number;
contextCollectionName: string;
collectionField: CollectionFieldOptions_deprecated;
collectionField?: CollectionFieldOptions_deprecated;
}) => {
const { t } = useTranslation();
const compile = useCompile();
@ -101,10 +105,17 @@ export const useContextAssociationFields = ({
const children =
getChildren(
getFilterOptions(collectionName).filter((v) => {
const isAssociationField = ['hasOne', 'hasMany', 'belongsTo', 'belongsToMany', 'belongsToArray'].includes(
v.type,
);
if (collectionField) {
const isAssociationField = [
'hasOne',
'hasMany',
'belongsTo',
'belongsToMany',
'belongsToArray',
].includes(v.type);
return isAssociationField;
}
return true;
}),
{
schema,

View File

@ -78,7 +78,7 @@ function AfterSuccess() {
return (
<SchemaSettingsModalItem
dialogRootClassName='dialog-after-successful-submission'
dialogRootClassName="dialog-after-successful-submission"
width={700}
title={t('After successful submission')}
initialValues={fieldSchema?.['x-action-settings']?.['onSuccess']}

View File

@ -15,11 +15,28 @@ import {
useCollectionRecordData,
useCompile,
useGlobalVariable,
useContextAssociationFields,
useTableBlockContext,
useCurrentPopupContext,
getStoredPopupContext,
useFormBlockContext,
} from '@nocobase/client';
import { useMemo } from 'react';
import { isEmpty } from 'lodash';
import { useTranslation } from '../locale';
const useIsShowTableSelectRecord = () => {
const { params } = useCurrentPopupContext();
const recordData = useCollectionRecordData();
const tableBlockContextBasicValue = useTableBlockContext();
if (recordData) {
return false;
}
const popupTableBlockContext = getStoredPopupContext(params?.popupuid)?.tableBlockContext;
return !isEmpty(popupTableBlockContext) || !isEmpty(tableBlockContextBasicValue);
};
export const useCustomRequestVariableOptions = () => {
const collection = useCollection_deprecated();
const { t } = useTranslation();
@ -33,6 +50,8 @@ export const useCustomRequestVariableOptions = () => {
return [compile(fieldsOptions), compile(userFieldOptions)];
}, [fieldsOptions, userFieldOptions]);
const environmentVariables = useGlobalVariable('$env');
const contextVariable = useContextAssociationFields({ maxDepth: 2, contextCollectionName: collection.name });
const shouldShowTableSelectVariable = useIsShowTableSelectRecord();
return useMemo(() => {
return [
environmentVariables,
@ -61,6 +80,7 @@ export const useCustomRequestVariableOptions = () => {
title: 'API token',
children: null,
},
shouldShowTableSelectVariable && { ...contextVariable, name: '$nSelectedRecord', title: contextVariable.label },
].filter(Boolean);
}, [recordData, t, fields, blockType, userFields]);
}, [recordData, t, fields, blockType, userFields, shouldShowTableSelectVariable]);
};

View File

@ -16,6 +16,8 @@ import {
useCompile,
useDataSourceKey,
useNavigateNoUpdate,
useBlockRequestContext,
useContextVariable,
} from '@nocobase/client';
import { isURL } from '@nocobase/utils/client';
import { App } from 'antd';
@ -25,18 +27,21 @@ export const useCustomizeRequestActionProps = () => {
const apiClient = useAPIClient();
const navigate = useNavigateNoUpdate();
const actionSchema = useFieldSchema();
const { field } = useBlockRequestContext();
const compile = useCompile();
const form = useForm();
const { name: blockType } = useBlockContext() || {};
// const { getPrimaryKey } = useCollection_deprecated();
const recordData = useCollectionRecordData();
const fieldSchema = useFieldSchema();
const actionField = useField();
const { setVisible } = useActionContext();
const { modal, message } = App.useApp();
const dataSourceKey = useDataSourceKey();
const { ctx } = useContextVariable();
return {
async onClick(e?, callBack?) {
const selectedRecord = field.data?.selectedRowData ? field.data?.selectedRowData : ctx;
const { skipValidator, onSuccess } = actionSchema?.['x-action-settings'] ?? {};
const { manualClose, redirecting, redirectTo, successMessage, actionAfterSuccess } = onSuccess || {};
const xAction = actionSchema?.['x-action'];
@ -58,12 +63,11 @@ export const useCustomizeRequestActionProps = () => {
method: 'POST',
data: {
currentRecord: {
// id: record[getPrimaryKey()],
// appends: result.params[0]?.appends,
dataSourceKey,
data: currentRecordData,
},
$nForm: blockType === 'form' ? form.values : undefined,
$nSelectedRecord: selectedRecord,
},
responseType: fieldSchema['x-response-type'] === 'stream' ? 'blob' : 'json',
});

View File

@ -73,6 +73,7 @@ export async function send(this: CustomRequestPlugin, ctx: Context, next: Next)
data: {},
},
$nForm,
$nSelectedRecord,
} = values;
// root role has all permissions
@ -154,6 +155,7 @@ export async function send(this: CustomRequestPlugin, ctx: Context, next: Next)
$nToken: ctx.getBearerToken(),
$nForm,
$env: ctx.app.environment.getVariables(),
$nSelectedRecord,
};
const axiosRequestConfig = {