diff --git a/packages/core/client/src/block-provider/hooks/index.ts b/packages/core/client/src/block-provider/hooks/index.ts index d048cd96c6..bd2c39d05a 100644 --- a/packages/core/client/src/block-provider/hooks/index.ts +++ b/packages/core/client/src/block-provider/hooks/index.ts @@ -227,8 +227,8 @@ export function useCollectValuesToSubmit() { ]); } -function interpolateVariables(str: string, scope: Record): string { - return str.replace(/\{\{\s*([a-zA-Z0-9_$-.]+?)\s*\}\}/g, (_, key) => { +export function interpolateVariables(str: string, scope: Record): string { + return str.replace(/\{\{\s*([a-zA-Z0-9_$.-]+?)\s*\}\}/g, (_, key) => { return scope[key] !== undefined ? String(scope[key]) : ''; }); } diff --git a/packages/core/client/src/schema-component/antd/action/hooks/useGetAfterSuccessVariablesOptions.ts b/packages/core/client/src/schema-component/antd/action/hooks/useGetAfterSuccessVariablesOptions.ts index c903752719..311c1b7be9 100644 --- a/packages/core/client/src/schema-component/antd/action/hooks/useGetAfterSuccessVariablesOptions.ts +++ b/packages/core/client/src/schema-component/antd/action/hooks/useGetAfterSuccessVariablesOptions.ts @@ -29,10 +29,9 @@ export const useAfterSuccessOptions = () => { }, [fieldsOptions, userFieldOptions]); const { settings: popupRecordSettings, shouldDisplayPopupRecord } = usePopupVariable(); const { currentRoleSettings } = useCurrentRoleVariable(); - const record = useCollectionRecordData(); return useMemo(() => { return [ - (record || form) && { + form && { value: '$record', label: t('Response record', { ns: 'client' }), children: [...fields], diff --git a/packages/core/client/src/schema-settings/index.ts b/packages/core/client/src/schema-settings/index.ts index 83b37a62f0..57453abc64 100644 --- a/packages/core/client/src/schema-settings/index.ts +++ b/packages/core/client/src/schema-settings/index.ts @@ -31,3 +31,4 @@ export { default as useParseDataScopeFilter } from './hooks/useParseDataScopeFil export * from './isPatternDisabled'; export { SchemaSettingsPlugin } from './SchemaSettingsPlugin'; export * from './VariableInput'; +export { replaceVariables } from './LinkageRules/bindLinkageRulesToFiled'; diff --git a/packages/plugins/@nocobase/plugin-action-custom-request/src/client/hooks/useCustomizeRequestActionProps.ts b/packages/plugins/@nocobase/plugin-action-custom-request/src/client/hooks/useCustomizeRequestActionProps.ts index 93739c9c74..deafba7833 100644 --- a/packages/plugins/@nocobase/plugin-action-custom-request/src/client/hooks/useCustomizeRequestActionProps.ts +++ b/packages/plugins/@nocobase/plugin-action-custom-request/src/client/hooks/useCustomizeRequestActionProps.ts @@ -18,6 +18,10 @@ import { useNavigateNoUpdate, useBlockRequestContext, useContextVariable, + useLocalVariables, + useVariables, + replaceVariables, + interpolateVariables, } from '@nocobase/client'; import { isURL } from '@nocobase/utils/client'; import { App } from 'antd'; @@ -38,12 +42,21 @@ export const useCustomizeRequestActionProps = () => { const { modal, message } = App.useApp(); const dataSourceKey = useDataSourceKey(); const { ctx } = useContextVariable(); + const localVariables = useLocalVariables(); + const variables = useVariables(); return { async onClick(e?, callBack?) { - const selectedRecord = field.data?.selectedRowData ? field.data?.selectedRowData : ctx; + const selectedRecord = field?.data?.selectedRowData ? field?.data?.selectedRowData : ctx; const { skipValidator, onSuccess } = actionSchema?.['x-action-settings'] ?? {}; - const { manualClose, redirecting, redirectTo, successMessage, actionAfterSuccess } = onSuccess || {}; + const { + manualClose, + redirecting, + redirectTo, + successMessage: rawSuccessMessage, + actionAfterSuccess, + } = onSuccess || {}; + let successMessage = rawSuccessMessage; const xAction = actionSchema?.['x-action']; if (skipValidator !== true && xAction === 'customize:form:request') { await form.submit(); @@ -71,6 +84,19 @@ export const useCustomizeRequestActionProps = () => { }, responseType: fieldSchema['x-response-type'] === 'stream' ? 'blob' : 'json', }); + try { + const { exp, scope: expScope } = await replaceVariables(successMessage, { + variables, + localVariables: [ + ...localVariables, + { name: '$nResponse', ctx: new Proxy({ ...res?.data, ...res?.data?.data }, {}) }, + ], + }); + successMessage = interpolateVariables(exp, expScope); + } catch (error) { + console.log(error); + } + if (res.headers['content-disposition']) { const contentDisposition = res.headers['content-disposition']; const utf8Match = contentDisposition.match(/filename\*=utf-8''([^;]+)/i); diff --git a/packages/plugins/@nocobase/plugin-action-custom-request/src/client/schemaSettings.ts b/packages/plugins/@nocobase/plugin-action-custom-request/src/client/schemaSettings.ts deleted file mode 100644 index 2a872ac122..0000000000 --- a/packages/plugins/@nocobase/plugin-action-custom-request/src/client/schemaSettings.ts +++ /dev/null @@ -1,92 +0,0 @@ -/** - * This file is part of the NocoBase (R) project. - * Copyright (c) 2020-2024 NocoBase Co., Ltd. - * Authors: NocoBase Team. - * - * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. - * For more information, please refer to: https://www.nocobase.com/agreement. - */ - -import { useFieldSchema } from '@formily/react'; -import { - AfterSuccess, - ButtonEditor, - RefreshDataBlockRequest, - RemoveButton, - SchemaSettings, - SchemaSettingsLinkageRules, - SecondConFirm, - useCollection, - useCollectionRecord, - useSchemaToolbar, - SchemaSettingAccessControl, - useDataBlockProps, - useCollectionManager_deprecated, -} from '@nocobase/client'; -import { CustomRequestSettingsItem } from './components/CustomRequestActionDesigner'; - -export const customizeCustomRequestActionSettings = new SchemaSettings({ - name: 'actionSettings:customRequest', - items: [ - { - name: 'editButton', - Component: ButtonEditor, - useComponentProps() { - const fieldSchema = useFieldSchema(); - return { - isLink: fieldSchema['x-action'] === 'customize:table:request', - }; - }, - }, - { - name: 'linkageRules', - Component: SchemaSettingsLinkageRules, - useComponentProps() { - const { linkageRulesProps } = useSchemaToolbar(); - return { - ...linkageRulesProps, - }; - }, - }, - { - name: 'secondConFirm', - Component: SecondConFirm, - }, - { - name: 'afterSuccessfulSubmission', - Component: AfterSuccess, - }, - { - name: 'request settings', - Component: CustomRequestSettingsItem, - }, - { - ...SchemaSettingAccessControl, - useVisible() { - return true; - }, - }, - { - name: 'refreshDataBlockRequest', - Component: RefreshDataBlockRequest, - useComponentProps() { - return { - isPopupAction: false, - }; - }, - useVisible() { - const collection = useCollection(); - return !!collection; - }, - }, - { - name: 'delete', - sort: 100, - Component: RemoveButton as any, - useComponentProps() { - const { removeButtonProps } = useSchemaToolbar(); - return removeButtonProps; - }, - }, - ], -}); diff --git a/packages/plugins/@nocobase/plugin-action-custom-request/src/client/schemaSettings.tsx b/packages/plugins/@nocobase/plugin-action-custom-request/src/client/schemaSettings.tsx new file mode 100644 index 0000000000..9f2e680968 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-custom-request/src/client/schemaSettings.tsx @@ -0,0 +1,242 @@ +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. + * For more information, please refer to: https://www.nocobase.com/agreement. + */ + +import { + ButtonEditor, + RefreshDataBlockRequest, + RemoveButton, + SchemaSettings, + SchemaSettingsLinkageRules, + SecondConFirm, + useCollection, + useSchemaToolbar, + SchemaSettingAccessControl, + useDesignable, + useGlobalVariable, + usePlugin, + SchemaSettingsModalItem, + useAfterSuccessOptions, + BlocksSelector, +} from '@nocobase/client'; +import React from 'react'; +import { ISchema, useFieldSchema } from '@formily/react'; +import { useTranslation } from 'react-i18next'; +import { CustomRequestSettingsItem } from './components/CustomRequestActionDesigner'; + +const useVariableOptions = () => { + const scopes = useAfterSuccessOptions(); + const { t } = useTranslation(); + return [ + { + value: '$nResponse', + label: t('Response', { ns: 'client' }), + children: null, + }, + ...scopes.filter((v: any) => ['currentUser', 'currentTime', '$nRole'].includes(v.value)), + ].filter(Boolean); +}; + +const useLinkVariableOptions = () => { + const scopes = useAfterSuccessOptions(); + const environmentVariables = useGlobalVariable('$env'); + return [...scopes.filter((v: any) => v.value !== '$record'), environmentVariables].filter(Boolean); +}; +const useLinkVariableProps = () => { + const scope = useLinkVariableOptions(); + return { + scope, + useTypedConstant: true, + }; +}; +export function AfterSuccess() { + const { dn } = useDesignable(); + const { t } = useTranslation(); + const fieldSchema = useFieldSchema(); + const { onSuccess } = fieldSchema?.['x-action-settings'] || {}; + const templatePlugin: any = usePlugin('@nocobase/plugin-block-template'); + const isInBlockTemplateConfigPage = templatePlugin?.isInBlockTemplateConfigPage?.(); + return ( + { + return { + tooltip: t('After successful submission, the selected data blocks will be automatically refreshed.'), + }; + }, + 'x-component': BlocksSelector, + 'x-hidden': isInBlockTemplateConfigPage, // 模板配置页面暂不支持该配置 + }, + }, + } as ISchema + } + onSubmit={(onSuccess) => { + fieldSchema['x-action-settings']['onSuccess'] = onSuccess; + dn.emit('patch', { + schema: { + ['x-uid']: fieldSchema['x-uid'], + 'x-action-settings': fieldSchema['x-action-settings'], + }, + }); + }} + /> + ); +} + +export const customizeCustomRequestActionSettings = new SchemaSettings({ + name: 'actionSettings:customRequest', + items: [ + { + name: 'editButton', + Component: ButtonEditor, + useComponentProps() { + const fieldSchema = useFieldSchema(); + return { + isLink: fieldSchema['x-action'] === 'customize:table:request', + }; + }, + }, + { + name: 'linkageRules', + Component: SchemaSettingsLinkageRules, + useComponentProps() { + const { linkageRulesProps } = useSchemaToolbar(); + return { + ...linkageRulesProps, + }; + }, + }, + { + name: 'secondConFirm', + Component: SecondConFirm, + }, + { + name: 'afterSuccessfulSubmission', + Component: AfterSuccess, + }, + { + name: 'request settings', + Component: CustomRequestSettingsItem, + }, + { + ...SchemaSettingAccessControl, + useVisible() { + return true; + }, + }, + { + name: 'refreshDataBlockRequest', + Component: RefreshDataBlockRequest, + useComponentProps() { + return { + isPopupAction: false, + }; + }, + useVisible() { + const collection = useCollection(); + return !!collection; + }, + }, + { + name: 'delete', + sort: 100, + Component: RemoveButton as any, + useComponentProps() { + const { removeButtonProps } = useSchemaToolbar(); + return removeButtonProps; + }, + }, + ], +});