mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 05:29:26 +08:00
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:
parent
1c343cad66
commit
f5fb2844da
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -57,7 +57,7 @@ import { useAllDataBlocks } from '../page/AllDataBlocksProvider';
|
||||
|
||||
const useA = () => {
|
||||
return {
|
||||
async run() { },
|
||||
async run() {},
|
||||
};
|
||||
};
|
||||
|
||||
@ -139,23 +139,26 @@ export const Action: ComposedAction = withDynamicSchemaProps(
|
||||
);
|
||||
|
||||
const handleClick = useMemo(() => {
|
||||
return onClick && (async (e, callback) => {
|
||||
await onClick?.(e, callback);
|
||||
return (
|
||||
onClick &&
|
||||
(async (e, callback) => {
|
||||
await onClick?.(e, callback);
|
||||
|
||||
// 执行完 onClick 之后,刷新数据区块
|
||||
const blocksToRefresh = fieldSchema['x-action-settings']?.onSuccess?.blocksToRefresh || []
|
||||
if (blocksToRefresh.length > 0) {
|
||||
getAllDataBlocks().forEach((block) => {
|
||||
if (blocksToRefresh.includes(block.uid)) {
|
||||
try {
|
||||
block.service?.refresh();
|
||||
} catch (error) {
|
||||
console.error('Failed to refresh block:', block.uid, error);
|
||||
// 执行完 onClick 之后,刷新数据区块
|
||||
const blocksToRefresh = fieldSchema['x-action-settings']?.onSuccess?.blocksToRefresh || [];
|
||||
if (blocksToRefresh.length > 0) {
|
||||
getAllDataBlocks().forEach((block) => {
|
||||
if (blocksToRefresh.includes(block.uid)) {
|
||||
try {
|
||||
block.service?.refresh();
|
||||
} catch (error) {
|
||||
console.error('Failed to refresh block:', block.uid, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
}, [onClick, fieldSchema, getAllDataBlocks]);
|
||||
|
||||
return (
|
||||
|
@ -365,8 +365,8 @@ export const SchemaSettingsFormItemTemplate = function FormItemTemplate(props) {
|
||||
required: true,
|
||||
default: collection
|
||||
? `${compile(collection?.title || collection?.name)}_${t(
|
||||
componentTitle[componentName] || componentName,
|
||||
)}`
|
||||
componentTitle[componentName] || componentName,
|
||||
)}`
|
||||
: t(componentTitle[componentName] || componentName),
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Input',
|
||||
@ -567,7 +567,7 @@ export const SchemaSettingsRemove: FC<SchemaSettingsRemoveProps> = (props) => {
|
||||
|
||||
export interface SchemaSettingsSelectItemProps
|
||||
extends Omit<SchemaSettingsItemProps, 'onChange' | 'onClick'>,
|
||||
Omit<SelectWithTitleProps, 'title' | 'defaultValue'> {
|
||||
Omit<SelectWithTitleProps, 'title' | 'defaultValue'> {
|
||||
value?: SelectWithTitleProps['defaultValue'];
|
||||
optionRender?: (option: any, info: { index: number }) => React.ReactNode;
|
||||
}
|
||||
@ -900,26 +900,32 @@ 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`
|
||||
// screen > 576px
|
||||
@media (min-width: 576px) {
|
||||
min-width: 520px;
|
||||
}
|
||||
// screen > 576px
|
||||
@media (min-width: 576px) {
|
||||
min-width: 520px;
|
||||
}
|
||||
|
||||
// screen <= 576px
|
||||
@media (max-width: 576px) {
|
||||
min-width: 320px;
|
||||
}
|
||||
`}
|
||||
// screen <= 576px
|
||||
@media (max-width: 576px) {
|
||||
min-width: 320px;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<ApplicationContext.Provider value={app}>
|
||||
<APIClientProvider apiClient={apiClient}>
|
||||
@ -984,13 +990,13 @@ export const SchemaSettingsDefaultSortingRules = function DefaultSortingRules(pr
|
||||
const sort = defaultSort?.map((item: string) => {
|
||||
return item.startsWith('-')
|
||||
? {
|
||||
field: item.substring(1),
|
||||
direction: 'desc',
|
||||
}
|
||||
field: item.substring(1),
|
||||
direction: 'desc',
|
||||
}
|
||||
: {
|
||||
field: item,
|
||||
direction: 'asc',
|
||||
};
|
||||
field: item,
|
||||
direction: 'asc',
|
||||
};
|
||||
});
|
||||
const sortFields = useSortFields(props.name || collection?.name);
|
||||
|
||||
|
@ -15,3 +15,4 @@ export * from './useURLSearchParamsVariable';
|
||||
export * from './useUserVariable';
|
||||
export * from './useVariableOptions';
|
||||
export * from './usePopupVariable';
|
||||
export * from './useContextAssociationFields';
|
||||
|
@ -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,
|
||||
);
|
||||
return isAssociationField;
|
||||
if (collectionField) {
|
||||
const isAssociationField = [
|
||||
'hasOne',
|
||||
'hasMany',
|
||||
'belongsTo',
|
||||
'belongsToMany',
|
||||
'belongsToArray',
|
||||
].includes(v.type);
|
||||
return isAssociationField;
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
{
|
||||
schema,
|
||||
|
@ -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']}
|
||||
|
@ -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]);
|
||||
};
|
||||
|
@ -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',
|
||||
});
|
||||
|
@ -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 = {
|
||||
|
Loading…
x
Reference in New Issue
Block a user