diff --git a/packages/base/src/page/Transit/index.test.tsx b/packages/base/src/page/Transit/index.test.tsx index e405dc92eb..c74b400dbe 100644 --- a/packages/base/src/page/Transit/index.test.tsx +++ b/packages/base/src/page/Transit/index.test.tsx @@ -38,6 +38,21 @@ describe('Transit Component', () => { console.error = originOutputError; }); + it('should wait until user info is fetched before resolving project', () => { + mockUseCurrentUser({ + isUserInfoFetched: false, + bindProjects: [] + }); + mockExtractQueries.mockReturnValue({ + from: 'cloudbeaver', + to: 'create_workflow', + project_name: 'default', + compression_data: 'data' + }); + superRender(); + expect(mockNavigate).not.toHaveBeenCalled(); + }); + it('should navigate to / when required parameters are missing', () => { mockExtractQueries.mockReturnValue({ from: 'cloudbeaver' diff --git a/packages/base/src/page/Transit/index.tsx b/packages/base/src/page/Transit/index.tsx index 2b9e70bcb8..4b280d0b35 100644 --- a/packages/base/src/page/Transit/index.tsx +++ b/packages/base/src/page/Transit/index.tsx @@ -42,7 +42,7 @@ const TARGET_DATA = ( const Transit: React.FC = () => { const navigate = useTypedNavigate(); - const { bindProjects } = useCurrentUser(); + const { bindProjects, isUserInfoFetched } = useCurrentUser(); const extractQueries = useTypedQuery(); useEffect(() => { const searchParams = extractQueries(ROUTE_PATHS.BASE.TRANSIT.index); @@ -51,6 +51,9 @@ const Transit: React.FC = () => { const compressionData = searchParams?.compression_data; const projectName = searchParams?.project_name; const workflowId = searchParams?.workflow_id; + if (!isUserInfoFetched) { + return; + } if (!from || !to) { console.error(`Missing required parameters!\n from=${from}\n to=${to}\n`); navigate(ROUTE_PATHS.BASE.HOME, { @@ -85,9 +88,15 @@ const Transit: React.FC = () => { return; } if (compressionData) { - navigate(`${path}?from=${from}&compression_data=${compressionData}`, { - replace: true - }); + // useTypedQuery / URLSearchParams 已解码;再拼 URL 必须重新编码,否则 + 变空格破坏预填 + navigate( + `${path}?from=${encodeURIComponent( + from + )}&compression_data=${encodeURIComponent(compressionData)}`, + { + replace: true + } + ); } else { navigate(path, { replace: true @@ -99,7 +108,7 @@ const Transit: React.FC = () => { replace: true }); } - }, [bindProjects, navigate, extractQueries]); + }, [bindProjects, isUserInfoFetched, navigate, extractQueries]); return ; }; export default Transit; diff --git a/packages/dms-kit/src/utils/Common.ts b/packages/dms-kit/src/utils/Common.ts index f5e737b7c2..51592817ae 100644 --- a/packages/dms-kit/src/utils/Common.ts +++ b/packages/dms-kit/src/utils/Common.ts @@ -84,26 +84,64 @@ export type GetErrorMessageParams = | string | AxiosResponse | ISwaggerLoseRequireParamsRes; + +const BUSINESS_ERROR_I18N_KEYS: Record = { + 'privilege_apply.no_assignee': 'privilegeApply.create.errors.noAssignee', + 'privilege_apply.account_mismatch': + 'privilegeApply.create.errors.accountMismatch', + 'privilege_apply.invalid_argument': + 'privilegeApply.create.errors.invalidArgument', + 'privilege_apply.duplicate_open': + 'privilegeApply.create.errors.duplicateOpen', + 'privilege_apply.invalid_account_name': + 'privilegeApply.create.errors.invalidAccountName', + 'privilege_apply.forbidden': 'privilegeApply.detail.errors.forbidden' +}; + +const BUSINESS_ERROR_FALLBACK_ZH: Record = { + 'privilege_apply.no_assignee': '请先配置提权审批人后再提交(未落单)', + 'privilege_apply.account_mismatch': + '当前连库账号已变更,请回到工作台刷新后重新申请', + 'privilege_apply.invalid_argument': '请完善必填项后重试', + 'privilege_apply.duplicate_open': + '该数据源已有进行中的提权申请,请等待处理完成后再提交', + 'privilege_apply.invalid_account_name': + '新账号名不合法:仅支持字母开头,字母、数字、下划线、连字符与点,最长 32 字符', + 'privilege_apply.forbidden': '没有账号管理权限,无法审批该提权申请' +}; + +const mapBusinessErrorMessage = (message: string): string => { + const i18nKey = BUSINESS_ERROR_I18N_KEYS[message]; + if (!i18nKey) { + return message; + } + const translated = i18n.t(i18nKey); + if (translated && translated !== i18nKey) { + return translated; + } + return BUSINESS_ERROR_FALLBACK_ZH[message] || message; +}; + export const getErrorMessage = (error: GetErrorMessageParams): string => { if (typeof error === 'string') { - return error; + return mapBusinessErrorMessage(error); } if (error instanceof Error || 'code' in error) { - return error.message; + return mapBusinessErrorMessage(error.message); } if (error.data) { if (typeof error.data?.message === 'string') { - return error.data.message; + return mapBusinessErrorMessage(error.data.message); } if (typeof error.data?.msg === 'string') { - return error.data.msg; + return mapBusinessErrorMessage(error.data.msg); } if (typeof error.data === 'string') { - return error.data; + return mapBusinessErrorMessage(error.data); } return JSON.stringify(error.data);