diff --git a/packages/base/src/locale/en-US/dmsDataExport.ts b/packages/base/src/locale/en-US/dmsDataExport.ts index 10c6c50e3d..fba5efe8a6 100644 --- a/packages/base/src/locale/en-US/dmsDataExport.ts +++ b/packages/base/src/locale/en-US/dmsDataExport.ts @@ -56,6 +56,17 @@ export default { result: { success: 'Task created successfully', guide: 'View the newly created task' + }, + approvalProcess: { + title: 'Approval Process', + hint: 'Approval process can be modified in Project Configure > Approval Process', + stepLabel: 'Step {{number}}', + matchByPermission: 'Match by Permission', + loadFailed: 'Failed to load approval process', + stepType: { + export_review: 'Export Review', + export_execute: 'Export Execution Confirm' + } } }, batchClose: { diff --git a/packages/base/src/locale/zh-CN/dmsDataExport.ts b/packages/base/src/locale/zh-CN/dmsDataExport.ts index 28a2e382d7..1bdebc0f3a 100644 --- a/packages/base/src/locale/zh-CN/dmsDataExport.ts +++ b/packages/base/src/locale/zh-CN/dmsDataExport.ts @@ -52,6 +52,17 @@ export default { hasExceptionRule: '当前存在审核规则未被校验,请排除问题后重新触发审核', continueSubmission: '仍要创建' }, + approvalProcess: { + title: '审批流程', + hint: '审批流程可在 项目配置 > 审批流程 中修改', + stepLabel: '步骤 {{number}}', + matchByPermission: '按权限匹配', + loadFailed: '加载审批流程失败', + stepType: { + export_review: '导出审批', + export_execute: '导出执行确认' + } + }, update: { baseTitle: '工单基本信息', sourceTitle: '工单导出对象', diff --git a/packages/base/src/page/DataExportManagement/Common/BasicInfoWrapper/__snapshots__/index.test.tsx.snap b/packages/base/src/page/DataExportManagement/Common/BasicInfoWrapper/__snapshots__/index.test.tsx.snap index 5734d0b78c..b0b0df09d7 100644 --- a/packages/base/src/page/DataExportManagement/Common/BasicInfoWrapper/__snapshots__/index.test.tsx.snap +++ b/packages/base/src/page/DataExportManagement/Common/BasicInfoWrapper/__snapshots__/index.test.tsx.snap @@ -11,80 +11,9 @@ exports[`test base/DataExport/Common/BasicInfoWrapper should match snapshot 1`] export-task-1
- -
- - - -
- -
@@ -101,80 +30,9 @@ exports[`test base/DataExport/Common/BasicInfoWrapper should match snapshot 2`] export-task-1
desc -
- - - -
- -
@@ -264,80 +122,9 @@ exports[`test base/DataExport/Common/BasicInfoWrapper should match snapshot 3`] export-task-1
desc -
- - - -
- -
@@ -427,80 +214,9 @@ exports[`test base/DataExport/Common/BasicInfoWrapper should match snapshot 4`] export-task-1
desc -
- - - -
- -
@@ -590,80 +306,9 @@ exports[`test base/DataExport/Common/BasicInfoWrapper should match snapshot 5`] export-task-1
desc -
- - - -
- -
@@ -753,80 +398,9 @@ exports[`test base/DataExport/Common/BasicInfoWrapper should match snapshot 6`] export-task-1
desc -
- - - -
- -
@@ -916,80 +490,9 @@ exports[`test base/DataExport/Common/BasicInfoWrapper should match snapshot 7`] export-task-1
desc -
- - - -
- -
@@ -1079,80 +582,9 @@ exports[`test base/DataExport/Common/BasicInfoWrapper should match snapshot 8`] export-task-1
desc -
- - - -
- -
@@ -1242,80 +674,9 @@ exports[`test base/DataExport/Common/BasicInfoWrapper should match snapshot 9`] export-task-1
desc -
- - - -
- -
diff --git a/packages/base/src/page/DataExportManagement/Common/BasicInfoWrapper/index.tsx b/packages/base/src/page/DataExportManagement/Common/BasicInfoWrapper/index.tsx index 3db4fdf939..2b4f67084b 100644 --- a/packages/base/src/page/DataExportManagement/Common/BasicInfoWrapper/index.tsx +++ b/packages/base/src/page/DataExportManagement/Common/BasicInfoWrapper/index.tsx @@ -79,6 +79,7 @@ const BasicInfoWrapper: React.FC = ({
{title}
diff --git a/packages/base/src/page/DataExportManagement/Create/__tests__/__snapshots__/index.test.tsx.snap b/packages/base/src/page/DataExportManagement/Create/__tests__/__snapshots__/index.test.tsx.snap index 02122de329..13041bc945 100644 --- a/packages/base/src/page/DataExportManagement/Create/__tests__/__snapshots__/index.test.tsx.snap +++ b/packages/base/src/page/DataExportManagement/Create/__tests__/__snapshots__/index.test.tsx.snap @@ -1427,80 +1427,9 @@ exports[`first should match snapshot when pageState is equal SUBMIT_WORKFLOW 1`] test
desc -
- - - -
- -
+
+
+
+
+ + + +
+
+
+ 加载审批流程失败 +
+
+
+
+ +`; + +exports[`base/DataExport/Create/ApprovalProcessPreview should render loading state 1`] = ` + +
+
+
+
+
+ + + + + + +
+
+
+
+ 审批流程 +
+
+
+ 审批流程可在 项目配置 > 审批流程 中修改 +
+
+
+
+
+ +`; + +exports[`base/DataExport/Create/ApprovalProcessPreview should render step list when data loaded successfully 1`] = ` + +
+
+
+
+
+ 审批流程 +
+
+
+
+
+ 1 +
+
+
+
+
+ 导出审批 +
+
+ 按权限匹配 +
+
+
+
+
+
+ 2 +
+
+
+
+
+ 导出执行确认 +
+
+ 按权限匹配 +
+
+
+
+
+ 审批流程可在 项目配置 > 审批流程 中修改 +
+
+
+
+
+ +`; diff --git a/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/ApprovalProcessPreview/__tests__/index.test.tsx b/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/ApprovalProcessPreview/__tests__/index.test.tsx new file mode 100644 index 0000000000..17bec68920 --- /dev/null +++ b/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/ApprovalProcessPreview/__tests__/index.test.tsx @@ -0,0 +1,126 @@ +import { act, screen } from '@testing-library/react'; +import { baseSuperRender } from '../../../../../../../testUtils/superRender'; +import ApprovalProcessPreview from '..'; +import workflow from '@actiontech/shared/lib/api/sqle/service/workflow'; +import { + createSpySuccessResponse, + createSpyErrorResponse +} from '@actiontech/shared/lib/testUtil/mockApi'; +import { dataExportWorkflowTemplateData } from '@actiontech/shared/lib/testUtil/mockApi/sqle/workflowTemplate/data'; +import { cloneDeep } from 'lodash'; +import MockWorkflowTemplateApi from '@actiontech/shared/lib/testUtil/mockApi/sqle/workflowTemplate'; + +describe('base/DataExport/Create/ApprovalProcessPreview', () => { + let mockGetWorkflowTemplateApi: jest.SpyInstance; + beforeEach(() => { + jest.useFakeTimers(); + mockGetWorkflowTemplateApi = MockWorkflowTemplateApi.getWorkflowTemplate(); + }); + + afterEach(() => { + jest.useRealTimers(); + jest.clearAllMocks(); + }); + + const customRender = (projectName = 'test-project') => { + return baseSuperRender( + + ); + }; + + it('should render loading state', () => { + const { baseElement } = customRender(); + expect(baseElement).toMatchSnapshot(); + }); + + it('should render step list when data loaded successfully', async () => { + mockGetWorkflowTemplateApi.mockImplementation(() => + createSpySuccessResponse({ + data: cloneDeep(dataExportWorkflowTemplateData) + }) + ); + const { baseElement } = customRender(); + await act(async () => jest.advanceTimersByTime(3000)); + + expect(mockGetWorkflowTemplateApi).toHaveBeenCalledWith({ + project_name: 'test-project', + workflow_type: 'data_export' + }); + expect(screen.getByText('审批流程')).toBeInTheDocument(); + expect(screen.getByText('导出审批')).toBeInTheDocument(); + expect(screen.getAllByText('按权限匹配').length).toBeGreaterThanOrEqual(1); + expect(screen.getByText(/审批流程可在.*中修改/)).toBeInTheDocument(); + expect(baseElement).toMatchSnapshot(); + }); + + it('should render fallback error state when request fails', async () => { + mockGetWorkflowTemplateApi.mockImplementation(() => + createSpyErrorResponse({}) + ); + const { baseElement } = customRender(); + await act(async () => jest.advanceTimersByTime(3000)); + + expect(screen.getByText('加载审批流程失败')).toBeInTheDocument(); + expect(baseElement).toMatchSnapshot(); + }); + + it('should render correct step type names for export_review and export_execute', async () => { + const multiStepData = cloneDeep(dataExportWorkflowTemplateData); + multiStepData.workflow_step_template_list = [ + { + approved_by_authorized: true, + assignee_user_id_list: [], + execute_by_authorized: false, + number: 1, + type: 'export_review' + }, + { + approved_by_authorized: false, + assignee_user_id_list: [], + execute_by_authorized: true, + number: 2, + type: 'export_execute' + } + ]; + mockGetWorkflowTemplateApi.mockImplementation(() => + createSpySuccessResponse({ + data: cloneDeep(multiStepData) + }) + ); + customRender(); + await act(async () => jest.advanceTimersByTime(3000)); + + expect(screen.getByText('导出审批')).toBeInTheDocument(); + expect(screen.getByText('导出执行确认')).toBeInTheDocument(); + }); + + it('should render assignee user list when not matched by permission', async () => { + const customData = cloneDeep(dataExportWorkflowTemplateData); + customData.workflow_step_template_list = [ + { + approved_by_authorized: false, + assignee_user_id_list: ['user1', 'user2'], + execute_by_authorized: false, + number: 1, + type: 'export_review' + } + ]; + mockGetWorkflowTemplateApi.mockImplementation(() => + createSpySuccessResponse({ + data: cloneDeep(customData) + }) + ); + customRender(); + await act(async () => jest.advanceTimersByTime(3000)); + + expect(screen.getByText('user1, user2')).toBeInTheDocument(); + }); + + it('should not make API call when projectName is empty', () => { + mockGetWorkflowTemplateApi.mockImplementation(() => + createSpySuccessResponse({ data: {} }) + ); + customRender(''); + expect(mockGetWorkflowTemplateApi).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/ApprovalProcessPreview/index.data.ts b/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/ApprovalProcessPreview/index.data.ts new file mode 100644 index 0000000000..3783869ad3 --- /dev/null +++ b/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/ApprovalProcessPreview/index.data.ts @@ -0,0 +1,10 @@ +import { t } from '../../../../../../locale'; + +export const stepTypeNameMap: Record = { + export_review: t( + 'dmsDataExport.create.approvalProcess.stepType.export_review' + ), + export_execute: t( + 'dmsDataExport.create.approvalProcess.stepType.export_execute' + ) +}; diff --git a/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/ApprovalProcessPreview/index.tsx b/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/ApprovalProcessPreview/index.tsx new file mode 100644 index 0000000000..1790abec1f --- /dev/null +++ b/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/ApprovalProcessPreview/index.tsx @@ -0,0 +1,97 @@ +import { useTranslation } from 'react-i18next'; +import { useRequest } from 'ahooks'; +import { Spin } from 'antd'; +import { Result } from 'antd'; +import { IWorkFlowStepTemplateResV1 } from '@actiontech/shared/lib/api/sqle/service/common'; +import { ApprovalProcessPreviewStyleWrapper } from './style'; +import { getWorkflowTemplateV1WorkflowTypeEnum } from '@actiontech/shared/lib/api/sqle/service/workflow/index.enum'; +import { stepTypeNameMap } from './index.data'; +import { SqleApi } from '@actiontech/shared/lib/api'; + +interface ApprovalProcessPreviewProps { + projectName: string; +} + +const ApprovalProcessPreview: React.FC = ({ + projectName +}) => { + const { t } = useTranslation(); + + const { + data: templateData, + loading, + error + } = useRequest( + () => + SqleApi.WorkflowService.getWorkflowTemplateV1({ + project_name: projectName, + workflow_type: getWorkflowTemplateV1WorkflowTypeEnum.data_export + }).then((res) => res.data.data), + { + ready: !!projectName, + // eslint-disable-next-line @typescript-eslint/no-empty-function + onError: () => {} + } + ); + + const renderAssigneeInfo = (step: IWorkFlowStepTemplateResV1) => { + if (step.approved_by_authorized || step.execute_by_authorized) { + return t('dmsDataExport.create.approvalProcess.matchByPermission'); + } + if (step.assignee_user_id_list && step.assignee_user_id_list.length > 0) { + return step.assignee_user_id_list.join(', '); + } + return '-'; + }; + + const renderStepTypeName = (type?: string) => { + if (type) { + return stepTypeNameMap[type] ?? '-'; + } + return '-'; + }; + + if (error) { + return ( + + + + ); + } + + return ( + + +
+ {t('dmsDataExport.create.approvalProcess.title')} +
+ +
+ {templateData?.workflow_step_template_list?.map((step, index) => ( +
+
+
{step.number}
+
+
+
+
+ {renderStepTypeName(step.type)} +
+
{renderAssigneeInfo(step)}
+
+
+ ))} +
+ +
+ {t('dmsDataExport.create.approvalProcess.hint')} +
+ + + ); +}; + +export default ApprovalProcessPreview; diff --git a/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/ApprovalProcessPreview/style.ts b/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/ApprovalProcessPreview/style.ts new file mode 100644 index 0000000000..53aa909904 --- /dev/null +++ b/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/ApprovalProcessPreview/style.ts @@ -0,0 +1,97 @@ +import { styled } from '@mui/material/styles'; + +export const ApprovalProcessPreviewStyleWrapper = styled('div')` + padding: 24px 40px; + border-bottom: 1px solid + ${({ theme }) => theme.sharedTheme.basic.colorGrayLine}; + + .approval-process-title { + color: ${({ theme }) => theme.sharedTheme.uiToken.colorText}; + font-size: 14px; + font-weight: 600; + line-height: 22px; + margin-bottom: 16px; + } + + .approval-process-steps { + display: flex; + flex-direction: column; + gap: 0; + } + + .approval-process-step { + display: flex; + align-items: flex-start; + position: relative; + padding-left: 32px; + padding-bottom: 16px; + + &:last-child { + padding-bottom: 0; + + .step-connector { + display: none; + } + } + + .step-indicator { + position: absolute; + left: 0; + top: 0; + display: flex; + flex-direction: column; + align-items: center; + + .step-dot { + width: 24px; + height: 24px; + border-radius: 50%; + background: ${({ theme }) => theme.sharedTheme.uiToken.colorPrimary}; + color: ${({ theme }) => theme.sharedTheme.basic.colorWhite}; + font-size: 12px; + font-weight: 600; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + } + + .step-connector { + width: 2px; + flex: 1; + min-height: 16px; + background: ${({ theme }) => theme.sharedTheme.basic.colorGrayLine}; + margin-top: 4px; + } + } + + .step-content { + flex: 1; + padding: 4px 0 0 12px; + + .step-type-name { + color: ${({ theme }) => theme.sharedTheme.uiToken.colorText}; + font-size: 14px; + font-weight: 500; + line-height: 22px; + } + + .step-assignee { + color: ${({ theme }) => theme.sharedTheme.uiToken.colorTextSecondary}; + font-size: 13px; + line-height: 20px; + margin-top: 2px; + } + } + } + + .approval-process-hint { + margin-top: 16px; + padding-top: 12px; + border-top: 1px dashed + ${({ theme }) => theme.sharedTheme.basic.colorGrayLine}; + color: ${({ theme }) => theme.sharedTheme.uiToken.colorTextTertiary}; + font-size: 12px; + line-height: 20px; + } +`; diff --git a/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/__tests__/__snapshots__/index.test.tsx.snap b/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/__tests__/__snapshots__/index.test.tsx.snap index 5f611d78ee..74c935ca0c 100644 --- a/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/__tests__/__snapshots__/index.test.tsx.snap +++ b/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/__tests__/__snapshots__/index.test.tsx.snap @@ -79,80 +79,9 @@ exports[`test base/DataExport/Create/SubmitWorkflow should match snapshot 1`] = test
desc -
- - - -
- -
desc -
- - - -
- -
desc -
- - - -
- -
desc -
- - - -
- -
desc -
- - - -
- -
desc -
- - - -
- -
desc -
- - - -
- -
= ({ }) => { const { t } = useTranslation(); const { sharedTheme } = useThemeStyleData(); + + // Dynamic step number calculation based on workflow status and steps const stepNumber = useMemo(() => { - if (!workflowStatus) { + if (!workflowStatus || !workflowSteps) { return currentStepNumber; } - //本期后端没有流程模板,当前固定为:创建、审核、执行 三步,所以前端自己判断当前到第几步了 - if ( - workflowStatus === WorkflowRecordStatusEnum.wait_for_approve || - workflowStatus === WorkflowRecordStatusEnum.rejected - ) { + // Total steps: create(1) + N approval nodes + execute(1) + const totalApprovalSteps = workflowSteps.length; + + if (workflowStatus === WorkflowRecordStatusEnum.rejected) { + // Find which approval step was rejected + for (let i = 0; i < workflowSteps.length; i++) { + if (workflowSteps[i]?.state === 'rejected') { + return i + 2; // +1 for create step, +1 for 1-indexed + } + } return 2; } + + if (workflowStatus === WorkflowRecordStatusEnum.wait_for_approve) { + // Find the current step being awaited + if (currentStepNumber !== undefined && currentStepNumber !== null) { + // currentStepNumber from API represents which step we are on (1-indexed) + return (currentStepNumber as number) + 1; // +1 for create step + } + return 2; + } + if (workflowStatus === WorkflowRecordStatusEnum.wait_for_export) { - return 3; + return totalApprovalSteps + 2; // all approval done, at execute step } + if ( [ WorkflowRecordStatusEnum.exporting, @@ -48,19 +66,16 @@ const WorkflowSteps: React.FC = ({ WorkflowRecordStatusEnum.cancel ].includes(workflowStatus) ) { - return 4; + return totalApprovalSteps + 2; // at execute step } - }, [currentStepNumber, workflowStatus]); + }, [currentStepNumber, workflowStatus, workflowSteps]); + const renderTitle = useCallback( (type?: string) => { if (type === 'create') { return t('dmsDataExport.detail.record.steps.create'); } - // if (type === WorkflowStepResV2TypeEnum.update_workflow) { - // return t('dmsDataExport.detail.record.steps.update'); - // } - if (type === 'approve') { return t('dmsDataExport.detail.record.steps.approve'); } @@ -136,7 +151,7 @@ const WorkflowSteps: React.FC = ({ ] ); const renderOrderStepsItem = useCallback( - (title: string, step: any) => { + (title: string, step: WorkflowStep) => { return (
{title}
@@ -148,12 +163,14 @@ const WorkflowSteps: React.FC = ({ [renderOrderStepsItemContent] ); const renderOrderStepsItemIcon = useCallback( - (type?: string) => { + (type?: string, step?: WorkflowStep) => { if (type === 'create') { return ; } if (type === 'approve') { - const isRejected = workflowStatus === WorkflowRecordStatusEnum.rejected; + const isRejected = + workflowStatus === WorkflowRecordStatusEnum.rejected && + step?.state === 'rejected'; return ( = ({ }, [workflowStatus, sharedTheme.uiToken.colorWarning] ); + const stepsItems = useMemo(() => { if (!workflowSteps) { return []; } - // 没有流程模板,先固定这 3 步 - return [ + // Build dynamic steps: create + N approval nodes + execute + const allSteps: WorkflowStep[] = [ { type: 'create', number: 1, @@ -193,21 +211,30 @@ const WorkflowSteps: React.FC = ({ name: createUser }, operation_time: createTime - }, - { - ...workflowSteps[0], - type: 'approve', - number: 2 - }, - { - type: 'execute', - number: 3 } - ].map((v, i) => { - const isNextRejected = workflowSteps[i + 1]?.state === 'rejected'; + ]; + + // Add all approval steps from workflow_step_list + workflowSteps.forEach((step, index) => { + allSteps.push({ + ...step, + type: 'approve', + number: index + 2 + }); + }); + + // Add execute step + allSteps.push({ + type: 'execute', + number: allSteps.length + 1 + }); + + return allSteps.map((v, i) => { + const nextStep = allSteps[i + 1]; + const isNextRejected = nextStep?.state === 'rejected'; return { title: renderOrderStepsItem(renderTitle(v.type), v), - icon: renderOrderStepsItemIcon(v.type), + icon: renderOrderStepsItemIcon(v.type, v), className: classNames({ 'prev-rejected-step': isNextRejected }) diff --git a/packages/base/src/page/DataExportManagement/Detail/components/WorkflowRecordInfo/__tests__/__snapshots__/index.test.tsx.snap b/packages/base/src/page/DataExportManagement/Detail/components/WorkflowRecordInfo/__tests__/__snapshots__/index.test.tsx.snap index 98dda86dd1..479a7e6d46 100644 --- a/packages/base/src/page/DataExportManagement/Detail/components/WorkflowRecordInfo/__tests__/__snapshots__/index.test.tsx.snap +++ b/packages/base/src/page/DataExportManagement/Detail/components/WorkflowRecordInfo/__tests__/__snapshots__/index.test.tsx.snap @@ -321,7 +321,7 @@ exports[`test base/DataExport/Detail/WorkflowRecordInfo should match snapshot 1`
{ const { result } = renderHook(() => useInitDataWithRequest()); expect(result.current.getWorkflowLoading).toBeTruthy(); - expect(result.current.getTaskInfosLoading).toBeFalsy(); + expect(result.current.getTaskInfosLoading).toBeTruthy(); expect(getWorkflowSpy).toHaveBeenCalledTimes(1); expect(getWorkflowSpy).toHaveBeenCalledWith({ project_uid: mockProjectInfo.projectID, @@ -56,7 +56,7 @@ describe('test base/DataExport/Detail/hooks/useInitDataWithRequest', () => { }); await act(async () => jest.advanceTimersByTime(3000)); - expect(result.current.getWorkflowLoading).toBeFalsy(); + expect(result.current.getWorkflowLoading).toBeTruthy(); expect(result.current.getTaskInfosLoading).toBeTruthy(); expect(mockDataExportDetailRedux.updateWorkflowInfo).toHaveBeenCalledTimes( 1 diff --git a/packages/base/src/page/DataExportManagement/Detail/hooks/useInitDataWithRequest.ts b/packages/base/src/page/DataExportManagement/Detail/hooks/useInitDataWithRequest.ts index ba73b5f63f..e832a421b7 100644 --- a/packages/base/src/page/DataExportManagement/Detail/hooks/useInitDataWithRequest.ts +++ b/packages/base/src/page/DataExportManagement/Detail/hooks/useInitDataWithRequest.ts @@ -3,11 +3,14 @@ import DataExportWorkflows from '@actiontech/shared/lib/api/base/service/DataExp import { ResponseCode } from '@actiontech/dms-kit'; import { useCurrentProject } from '@actiontech/shared/lib/features'; import useDataExportDetailReduxManage from './index.redux'; -import { useRequest } from 'ahooks'; -import { useEffect } from 'react'; +import { useBoolean, useRequest } from 'ahooks'; +import { useEffect, useMemo } from 'react'; import eventEmitter from '../../../../utils/EventEmitter'; import EmitterKey from '../../../../data/EmitterKey'; -import { GetDataExportTaskStatusEnum } from '@actiontech/shared/lib/api/base/service/common.enum'; +import { + GetDataExportTaskStatusEnum, + WorkflowRecordStatusEnum +} from '@actiontech/shared/lib/api/base/service/common.enum'; import { useTypedParams } from '@actiontech/shared'; import { ROUTE_PATHS } from '@actiontech/dms-kit'; @@ -18,7 +21,14 @@ const useInitDataWithRequest = () => { const { updateTaskInfos, updateWorkflowInfo, updateTaskStatusNumber } = useDataExportDetailReduxManage(); - const { refresh: refreshWorkflow, loading: getWorkflowLoading } = useRequest( + const [polling, { setFalse: finishPollRequest, setTrue: startPollRequest }] = + useBoolean(); + + const { + refresh: refreshWorkflow, + loading: getWorkflowLoading, + cancel + } = useRequest( () => DataExportWorkflows.GetDataExportWorkflow({ project_uid: projectID, @@ -31,8 +41,27 @@ const useInitDataWithRequest = () => { ?.map((v) => v.task_uid ?? '') ?.join(',') ?? '' ); + + // Stop polling when no longer in exporting status + if ( + res.data.data?.workflow_record?.status !== + WorkflowRecordStatusEnum.exporting + ) { + cancel(); + finishPollRequest(); + } else { + startPollRequest(); + } } - }) + }), + { + pollingInterval: 1000, + pollingErrorRetryCount: 3, + onError: () => { + cancel(); + finishPollRequest(); + } + } ); const { loading: getTaskInfosLoading, run: batchGetDataExportTask } = @@ -82,9 +111,14 @@ const useInitDataWithRequest = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + const initLoading = useMemo( + () => (polling ? false : getTaskInfosLoading || getWorkflowLoading), + [getTaskInfosLoading, getWorkflowLoading, polling] + ); + return { - getWorkflowLoading, - getTaskInfosLoading + getWorkflowLoading: initLoading, + getTaskInfosLoading: initLoading }; }; diff --git a/packages/dms-kit/src/data/routePaths.ts b/packages/dms-kit/src/data/routePaths.ts index c5d8b7c504..c42f76c509 100644 --- a/packages/dms-kit/src/data/routePaths.ts +++ b/packages/dms-kit/src/data/routePaths.ts @@ -321,11 +321,13 @@ export const ROUTE_PATHS = { PROGRESS: { index: { prefix: '/sqle/project', - path: ':projectID/progress' + path: ':projectID/progress', + query: 'activeTab' }, update: { prefix: '/sqle/project/:projectID/progress', - path: 'update/:workflowName' + path: 'update/:workflowName', + query: 'workflowType' } }, WHITELIST: { diff --git a/packages/shared/lib/api/sqle/service/common.d.ts b/packages/shared/lib/api/sqle/service/common.d.ts index 993b7cdaf3..581a17455f 100644 --- a/packages/shared/lib/api/sqle/service/common.d.ts +++ b/packages/shared/lib/api/sqle/service/common.d.ts @@ -98,6 +98,7 @@ import { WorkflowStepResV1StateEnum, WorkflowStepResV1TypeEnum, WorkflowTemplateDetailResV1AllowSubmitWhenLessAuditLevelEnum, + WorkflowTemplateDetailResV1WorkflowTypeEnum, pipelineNodeBaseAuditMethodEnum, pipelineNodeBaseObjectTypeEnum, pipelineNodeBaseTypeEnum, @@ -133,6 +134,12 @@ export interface IBaseRes { message?: string; } +export interface IGlobalAccountListDataV2 { + accounts?: IGlobalAccountListItemV2[]; + + can_manage?: boolean; +} + export interface IGlobalAccountListItemV2 { account_name?: string; @@ -154,7 +161,7 @@ export interface IGlobalAccountListItemV2 { export interface IGlobalAccountListResV2 { code?: number; - data?: IGlobalAccountListItemV2[]; + data?: IGlobalAccountListDataV2; message?: string; @@ -2495,6 +2502,14 @@ export interface IGetWorkflowTasksResV1 { message?: string; } +export interface IGetWorkflowTemplateListResV1 { + code?: number; + + data?: IWorkflowTemplateDetailResV1[]; + + message?: string; +} + export interface IGetWorkflowTemplateResV1 { code?: number; @@ -4457,6 +4472,8 @@ export interface IWorkflowTemplateDetailResV1 { workflow_step_template_list?: IWorkFlowStepTemplateResV1[]; workflow_template_name?: string; + + workflow_type?: WorkflowTemplateDetailResV1WorkflowTypeEnum; } export interface ICreatePipelineResData { @@ -5098,6 +5115,8 @@ export interface IUploadInstanceAuditPlanSQLsReqV2 { } export interface IWorkflowRecordResV2 { + assignee_user_name_list?: string[]; + current_step_number?: number; executable?: boolean; diff --git a/packages/shared/lib/api/sqle/service/common.enum.ts b/packages/shared/lib/api/sqle/service/common.enum.ts index 0bdc064516..6f7e06436b 100644 --- a/packages/shared/lib/api/sqle/service/common.enum.ts +++ b/packages/shared/lib/api/sqle/service/common.enum.ts @@ -829,7 +829,11 @@ export enum UpdateWorkflowTemplateReqV1AllowSubmitWhenLessAuditLevelEnum { export enum WorkFlowStepTemplateReqV1TypeEnum { 'sql_review' = 'sql_review', - 'sql_execute' = 'sql_execute' + 'sql_execute' = 'sql_execute', + + 'export_review' = 'export_review', + + 'export_execute' = 'export_execute' } export enum WorkflowDetailResV1CurrentStepTypeEnum { @@ -940,6 +944,12 @@ export enum WorkflowTemplateDetailResV1AllowSubmitWhenLessAuditLevelEnum { 'error' = 'error' } +export enum WorkflowTemplateDetailResV1WorkflowTypeEnum { + 'workflow' = 'workflow', + + 'data_export' = 'data_export' +} + export enum pipelineNodeBaseAuditMethodEnum { 'offline' = 'offline', diff --git a/packages/shared/lib/api/sqle/service/workflow/index.d.ts b/packages/shared/lib/api/sqle/service/workflow/index.d.ts index c899fa201c..10d9f54bd8 100644 --- a/packages/shared/lib/api/sqle/service/workflow/index.d.ts +++ b/packages/shared/lib/api/sqle/service/workflow/index.d.ts @@ -5,6 +5,7 @@ import { IGetWorkflowTemplateResV1, IUpdateWorkflowTemplateReqV1, IBaseRes, + IGetWorkflowTemplateListResV1, ICreateWorkflowReqV1, IAutoCreateAndExecuteWorkflowResV1, IBatchCancelWorkflowsReqV1, @@ -42,6 +43,8 @@ import { getGlobalWorkflowsV1FilterProjectPriorityEnum, GetGlobalWorkflowStatisticsFilterStatusListEnum, GetGlobalWorkflowStatisticsFilterProjectPriorityEnum, + getWorkflowTemplateV1WorkflowTypeEnum, + updateWorkflowTemplateV1WorkflowTypeEnum, getWorkflowsV1FilterStatusEnum, autoCreateAndExecuteWorkflowV1ExecModeEnum, exportWorkflowV1FilterStatusEnum, @@ -129,6 +132,8 @@ export interface IGetGlobalWorkflowStatisticsReturn export interface IGetWorkflowTemplateV1Params { project_name: string; + + workflow_type: getWorkflowTemplateV1WorkflowTypeEnum; } export interface IGetWorkflowTemplateV1Return @@ -137,10 +142,19 @@ export interface IGetWorkflowTemplateV1Return export interface IUpdateWorkflowTemplateV1Params extends IUpdateWorkflowTemplateReqV1 { project_name: string; + + workflow_type: updateWorkflowTemplateV1WorkflowTypeEnum; } export interface IUpdateWorkflowTemplateV1Return extends IBaseRes {} +export interface IGetWorkflowTemplateListV1Params { + project_name: string; +} + +export interface IGetWorkflowTemplateListV1Return + extends IGetWorkflowTemplateListResV1 {} + export interface IGetWorkflowsV1Params { filter_subject?: string; diff --git a/packages/shared/lib/api/sqle/service/workflow/index.enum.ts b/packages/shared/lib/api/sqle/service/workflow/index.enum.ts index 6dd919cb8f..5b6b68c466 100644 --- a/packages/shared/lib/api/sqle/service/workflow/index.enum.ts +++ b/packages/shared/lib/api/sqle/service/workflow/index.enum.ts @@ -96,6 +96,18 @@ export enum GetGlobalWorkflowStatisticsFilterProjectPriorityEnum { 'low' = 'low' } +export enum getWorkflowTemplateV1WorkflowTypeEnum { + 'workflow' = 'workflow', + + 'data_export' = 'data_export' +} + +export enum updateWorkflowTemplateV1WorkflowTypeEnum { + 'workflow' = 'workflow', + + 'data_export' = 'data_export' +} + export enum getWorkflowsV1FilterStatusEnum { 'wait_for_audit' = 'wait_for_audit', diff --git a/packages/shared/lib/api/sqle/service/workflow/index.ts b/packages/shared/lib/api/sqle/service/workflow/index.ts index 13a5d63d3a..28d2f084e0 100644 --- a/packages/shared/lib/api/sqle/service/workflow/index.ts +++ b/packages/shared/lib/api/sqle/service/workflow/index.ts @@ -20,6 +20,8 @@ import { IGetWorkflowTemplateV1Return, IUpdateWorkflowTemplateV1Params, IUpdateWorkflowTemplateV1Return, + IGetWorkflowTemplateListV1Params, + IGetWorkflowTemplateListV1Return, IGetWorkflowsV1Params, IGetWorkflowsV1Return, ICreateWorkflowV1Params, @@ -174,8 +176,26 @@ class WorkflowService extends ServiceBase { const project_name = paramsData.project_name; delete paramsData.project_name; + const workflow_type = paramsData.workflow_type; + delete paramsData.workflow_type; + return this.patch( - `/v1/projects/${project_name}/workflow_template`, + `/v1/projects/${project_name}/workflow_template/${workflow_type}/`, + paramsData, + options + ); + } + + public getWorkflowTemplateListV1( + params: IGetWorkflowTemplateListV1Params, + options?: AxiosRequestConfig + ) { + const paramsData = this.cloneDeep(params); + const project_name = paramsData.project_name; + delete paramsData.project_name; + + return this.get( + `/v1/projects/${project_name}/workflow_templates`, paramsData, options ); diff --git a/packages/shared/lib/testUtil/mockApi/sqle/workflowTemplate/data.ts b/packages/shared/lib/testUtil/mockApi/sqle/workflowTemplate/data.ts index d7337181fc..dea106eebc 100644 --- a/packages/shared/lib/testUtil/mockApi/sqle/workflowTemplate/data.ts +++ b/packages/shared/lib/testUtil/mockApi/sqle/workflowTemplate/data.ts @@ -1,4 +1,8 @@ -import { WorkflowTemplateDetailResV1AllowSubmitWhenLessAuditLevelEnum } from '../../../../api/sqle/service/common.enum'; +import { IWorkflowTemplateDetailResV1 } from '../../../../api/sqle/service/common'; +import { + WorkflowTemplateDetailResV1AllowSubmitWhenLessAuditLevelEnum, + WorkflowTemplateDetailResV1WorkflowTypeEnum +} from '../../../../api/sqle/service/common.enum'; export const workflowTemplateData = { allow_submit_when_less_audit_level: @@ -29,5 +33,34 @@ export const workflowTemplateData = { type: 'sql_execute' } ], - workflow_template_name: '700300-WorkflowTemplate' + workflow_template_name: '700300-WorkflowTemplate', + workflow_type: WorkflowTemplateDetailResV1WorkflowTypeEnum.workflow }; + +export const dataExportWorkflowTemplateData: IWorkflowTemplateDetailResV1 = { + desc: '', + update_time: '2024-01-15T10:30:00+08:00', + workflow_step_template_list: [ + { + approved_by_authorized: true, + assignee_user_id_list: [], + execute_by_authorized: false, + number: 1, + type: 'export_review' + }, + { + approved_by_authorized: false, + assignee_user_id_list: [], + execute_by_authorized: true, + number: 2, + type: 'export_execute' + } + ], + workflow_template_name: '700300-DataExportWorkflowTemplate', + workflow_type: WorkflowTemplateDetailResV1WorkflowTypeEnum.data_export +}; + +export const workflowTemplateListData = [ + workflowTemplateData, + dataExportWorkflowTemplateData +]; diff --git a/packages/shared/lib/testUtil/mockApi/sqle/workflowTemplate/index.ts b/packages/shared/lib/testUtil/mockApi/sqle/workflowTemplate/index.ts index 9663d8174f..819cf80a27 100644 --- a/packages/shared/lib/testUtil/mockApi/sqle/workflowTemplate/index.ts +++ b/packages/shared/lib/testUtil/mockApi/sqle/workflowTemplate/index.ts @@ -1,12 +1,13 @@ import workflow from '../../../../api/sqle/service/workflow'; import { MockSpyApy, createSpySuccessResponse } from '../../common'; -import { workflowTemplateData } from './data'; +import { workflowTemplateData, workflowTemplateListData } from './data'; import { cloneDeep } from 'lodash'; class MockWorkflowTemplateApi implements MockSpyApy { public mockAllApi(): void { this.updateWorkflowTemplate(); this.getWorkflowTemplate(); + this.getWorkflowTemplateList(); this.cancelWorkflow(); } @@ -26,6 +27,16 @@ class MockWorkflowTemplateApi implements MockSpyApy { return spy; } + public getWorkflowTemplateList() { + const spy = jest.spyOn(workflow, 'getWorkflowTemplateListV1'); + spy.mockImplementation(() => { + return createSpySuccessResponse({ + data: cloneDeep(workflowTemplateListData) + }); + }); + return spy; + } + public cancelWorkflow() { const spy = jest.spyOn(workflow, 'cancelWorkflowV2'); spy.mockImplementation(() => { diff --git a/packages/sqle/src/locale/en-US/workflowTemplate.ts b/packages/sqle/src/locale/en-US/workflowTemplate.ts index eebcd5119d..1aaa6c1ef6 100644 --- a/packages/sqle/src/locale/en-US/workflowTemplate.ts +++ b/packages/sqle/src/locale/en-US/workflowTemplate.ts @@ -10,7 +10,15 @@ export default { table: { workflowTemplateName: 'Approval workflow template name', - desc: 'Approval workflow template description' + desc: 'Approval workflow template description', + applicableType: 'Applicable Type', + approvalNodeDesc: 'Approval Node Description', + updateTime: 'Update Time' + }, + + type: { + workflow: 'SQL Exec Workflow', + dataExport: 'Data Export' }, operator: { @@ -33,7 +41,9 @@ export default { update: { title: { - wrapper: 'Update approval workflow template' + wrapper: 'Update approval workflow template', + workflow: 'Edit Approval Process - SQL Exec Workflow', + dataExport: 'Edit Approval Process - Data Export' }, result: { title: 'Update approval workflow template successfully', @@ -66,7 +76,9 @@ export default { fourth: 'Audit tickets: the auditor can execute “pass audit” or “reject” operations in this step;', fifth: - 'Online tickets: the executor can execute “execute online” or “reject” operations in this step.' + 'Online tickets: the executor can execute “execute online” or “reject” operations in this step.', + fifthExport: + 'Export tickets: the executor can execute “execute export” or “reject” operations in this step.' } }, @@ -82,6 +94,10 @@ export default { execDesc: 'Edit the audit process. The executor can execute “execute online” or “reject” operations in this step', + exportExecTitle: 'Execute export', + exportExecDesc: + 'Edit the audit process. The executor can execute “execute export” or “reject” operations in this step', + resultTitle: 'Result', resultDesc: 'Change result' }, @@ -132,6 +148,25 @@ export default { matchExecute: 'Match members who have data source online permissions' } }, + exportExec: { + title: 'Execute export', + subTitle: + 'The executor can execute “execute export” or “reject” operations in this step', + creatorAsExecutor: + 'The executor of data export is the workflow creator by default and cannot be modified.', + executeUserType: { + specifyExecute: 'Specify executor', + matchExecute: 'Match members who have data source export permissions' + } + }, + exportReview: { + title: 'Export Review', + subTitle: 'Reviewer checks the legitimacy of the export request' + }, + exportExecute: { + title: 'Export Execution Confirm', + subTitle: 'Confirm to execute the export operation' + }, operator: { remove: 'Remove this step', moveUp: 'Move this step up', @@ -147,6 +182,10 @@ export default { 'The approval workflow template can set up to 4 audit steps, or no audit steps can be set;', rule3: 'When specifying the executor for a single step, at least one specified person needs to be added, and a maximum of three specified persons can be added.' + }, + exportRuler: { + rule1: + 'The approval process starts from the initiation of the ticket, and ends with the execution of export after passing through the set audit steps;' } }, diff --git a/packages/sqle/src/locale/zh-CN/workflowTemplate.ts b/packages/sqle/src/locale/zh-CN/workflowTemplate.ts index 0e7ea1c556..ca1a153c84 100644 --- a/packages/sqle/src/locale/zh-CN/workflowTemplate.ts +++ b/packages/sqle/src/locale/zh-CN/workflowTemplate.ts @@ -10,7 +10,15 @@ export default { table: { workflowTemplateName: '审批流程模板名称', - desc: '审批流程模板描述' + desc: '审批流程模板描述', + applicableType: '适用类型', + approvalNodeDesc: '审批节点描述', + updateTime: '更新时间' + }, + + type: { + workflow: '上线工单', + dataExport: '数据导出' }, operator: { @@ -32,7 +40,9 @@ export default { update: { title: { - wrapper: '更新审批流程模板' + wrapper: '更新审批流程模板', + workflow: '编辑审批流程 - 上线工单', + dataExport: '编辑审批流程 - 数据导出' }, result: { title: '更新审批流程模板成功', @@ -62,7 +72,9 @@ export default { '被驳回的工单,需要创建人更新SQL语句后重新发起,驳回记录可在 “工单进度-工单历史操作” 中查看;', third: '处于 “审核节点” 中的工单,创建人可在工单详情页随时关闭;', fourth: '审核工单:审核人在该步骤可以执行「审核通过」或「驳回」操作;', - fifth: '上线工单:执行人在该步骤可以执行「执行上线」或「驳回」操作。' + fifth: '上线工单:执行人在该步骤可以执行「执行上线」或「驳回」操作。', + fifthExport: + '导出工单:执行人在该步骤可以执行「执行导出」或「驳回」操作。' } }, @@ -77,6 +89,10 @@ export default { execTitle: '执行上线', execDesc: '编辑审核流程,执行人在该步骤可以执行「执行上线」或「驳回」操作', + exportExecTitle: '执行导出', + exportExecDesc: + '编辑审核流程,执行人在该步骤可以执行「执行导出」或「驳回」操作', + resultTitle: '结果', resultDesc: '变更结果' }, @@ -96,7 +112,7 @@ export default { rule: { descMessage: '步骤描述不能超过255个字符', userRequired: '最少需要添加一个指定人', - userMessage: '最多只能添加十个指定人' + userMessage: '最多只能添加{{max}}个指定人' } }, @@ -124,6 +140,23 @@ export default { matchExecute: '匹配拥有数据源上线权限的成员' } }, + exportExec: { + title: '执行导出', + subTitle: '执行人在该步骤可以执行 执行导出或驳回 操作', + creatorAsExecutor: '数据导出的执行人默认为工单创建者,不可修改。', + executeUserType: { + specifyExecute: '指定执行人', + matchExecute: '匹配拥有数据源导出权限的成员' + } + }, + exportReview: { + title: '导出审批', + subTitle: '审批人在该步骤审核导出请求的合理性' + }, + exportExecute: { + title: '导出执行确认', + subTitle: '确认执行导出操作' + }, operator: { remove: '移除该步骤', moveUp: '上移该步骤', @@ -136,7 +169,11 @@ export default { '审核流程自工单发起开始,通过设置的审核步骤后,最后以执行上线结束;', rule2: '审核流程模板最多可设置4个审核步骤,也可不设置审核步骤;', rule3: - '单个步骤指定执行人时,最少需要添加一个指定人,最多只能添加三个指定人。' + '单个步骤指定执行人时,最少需要添加一个指定人,最多只能添加十个指定人。' + }, + exportRuler: { + rule1: + '审核流程自工单发起开始,通过设置的审核步骤后,最后以执行导出结束;' } }, diff --git a/packages/sqle/src/page/SqlExecWorkflow/Create/hooks/useAllowAuditLevel.ts b/packages/sqle/src/page/SqlExecWorkflow/Create/hooks/useAllowAuditLevel.ts index 577ffef04d..71b11ebb9e 100644 --- a/packages/sqle/src/page/SqlExecWorkflow/Create/hooks/useAllowAuditLevel.ts +++ b/packages/sqle/src/page/SqlExecWorkflow/Create/hooks/useAllowAuditLevel.ts @@ -1,5 +1,6 @@ import { WorkflowTemplateDetailResV1AllowSubmitWhenLessAuditLevelEnum } from '@actiontech/shared/lib/api/sqle/service/common.enum'; import workflow from '@actiontech/shared/lib/api/sqle/service/workflow'; +import { getWorkflowTemplateV1WorkflowTypeEnum } from '@actiontech/shared/lib/api/sqle/service/workflow/index.enum'; import { useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -32,7 +33,8 @@ export const useAllowAuditLevel = () => { ) => { const request = (projectName: string) => { return workflow.getWorkflowTemplateV1({ - project_name: projectName + project_name: projectName, + workflow_type: getWorkflowTemplateV1WorkflowTypeEnum.workflow }); }; const tips: string[] = []; diff --git a/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/__snapshots__/index.test.tsx.snap b/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/__snapshots__/index.test.tsx.snap index fb864dc284..db6f251891 100644 --- a/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/__snapshots__/index.test.tsx.snap +++ b/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/__snapshots__/index.test.tsx.snap @@ -65,7 +65,7 @@ exports[`page/WorkflowTemplate/UpdateWorkflowTemplate change workflow template n - 返回审批流程模板 + 编辑审批流程 - 上线工单 @@ -757,7 +757,7 @@ exports[`page/WorkflowTemplate/UpdateWorkflowTemplate no review node in template - 返回审批流程模板 + 编辑审批流程 - 上线工单 @@ -1250,7 +1250,7 @@ exports[`page/WorkflowTemplate/UpdateWorkflowTemplate render update workflow tem - 返回审批流程模板 + 编辑审批流程 - 上线工单 @@ -1926,7 +1926,7 @@ exports[`page/WorkflowTemplate/UpdateWorkflowTemplate render update workflow tem - 返回审批流程模板 + 编辑审批流程 - 上线工单 @@ -2602,7 +2602,7 @@ exports[`page/WorkflowTemplate/UpdateWorkflowTemplate update workflow template i - 返回审批流程模板 + 编辑审批流程 - 上线工单 diff --git a/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/components/BasicInfo/__snapshots__/index.test.tsx.snap b/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/components/BasicInfo/__snapshots__/index.test.tsx.snap index 38907a3002..eb20958d48 100644 --- a/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/components/BasicInfo/__snapshots__/index.test.tsx.snap +++ b/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/components/BasicInfo/__snapshots__/index.test.tsx.snap @@ -278,3 +278,48 @@ exports[`page/WorkflowTemplate/BasicInfo render basic info without default data
`; + +exports[`page/WorkflowTemplate/BasicInfo should hide AllowSubmitWhenLessAuditLevel when workflowType is data_export 1`] = ` + +
+
+
+
+ 基本信息 +
+
+ 设定模板的基本信息 +
+
+
+
+
+ +
+
+
+
+
+ +`; diff --git a/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/components/BasicInfo/index.test.tsx b/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/components/BasicInfo/index.test.tsx index 26c8799069..95bfa5c4e5 100644 --- a/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/components/BasicInfo/index.test.tsx +++ b/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/components/BasicInfo/index.test.tsx @@ -3,7 +3,8 @@ import BasicInfo from '.'; import { act, fireEvent, screen, renderHook } from '@testing-library/react'; import { getAllBySelector, - getBySelector + getBySelector, + queryBySelector } from '@actiontech/shared/lib/testUtil/customQuery'; import { workflowTemplateData } from '@actiontech/shared/lib/testUtil/mockApi/sqle/workflowTemplate/data'; import { WorkflowTemplateDetailResV1AllowSubmitWhenLessAuditLevelEnum } from '@actiontech/shared/lib/api/sqle/service/common.enum'; @@ -24,7 +25,7 @@ describe('page/WorkflowTemplate/BasicInfo', () => { const updateMock = jest.fn(); const { result } = renderHook(() => Form.useForm()); - const customRender = (data?: { [key: string]: undefined }) => { + const customRender = (data?: Record) => { return sqleSuperRender( { expect(baseElement).toMatchSnapshot(); expect(screen.getByText('告警(Warning)')).toBeInTheDocument(); }); + + it('should hide AllowSubmitWhenLessAuditLevel when workflowType is data_export', async () => { + const { baseElement } = customRender({ workflowType: 'data_export' }); + expect(baseElement).toMatchSnapshot(); + expect(screen.getByText('基本信息')).toBeInTheDocument(); + expect( + screen.queryByText('允许创建工单的最高审核等级') + ).not.toBeInTheDocument(); + expect( + queryBySelector('.ant-select-selection-search-input') + ).not.toBeInTheDocument(); + + fireEvent.click(screen.getByText('下一步')); + await act(async () => jest.advanceTimersByTime(300)); + expect(nextStepMock).toHaveBeenCalled(); + }); }); diff --git a/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/components/BasicInfo/index.tsx b/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/components/BasicInfo/index.tsx index 3216bb5c4e..39ebf182e4 100644 --- a/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/components/BasicInfo/index.tsx +++ b/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/components/BasicInfo/index.tsx @@ -7,16 +7,21 @@ import { BaseFormProps } from './index.type'; import useStaticStatus from '../../../../../hooks/useStaticStatus'; import StepButton from '../StepButton'; import { WorkflowTemplateDetailResV1AllowSubmitWhenLessAuditLevelEnum } from '@actiontech/shared/lib/api/sqle/service/common.enum'; +import { getWorkflowTemplateV1WorkflowTypeEnum } from '@actiontech/shared/lib/api/sqle/service/workflow/index.enum'; + const BasicInfo: React.FC = (props) => { const { t } = useTranslation(); - const { form } = props; + const { form, workflowType } = props; const { getAuditLevelStatusSelectOption } = useStaticStatus(); + const isDataExport = + workflowType === getWorkflowTemplateV1WorkflowTypeEnum.data_export; const nextStep = async () => { const value = await form.validateFields(); props.updateBaseInfo(value?.allowSubmitWhenLessAuditLevel); props.nextStep(); }; useEffect(() => { + if (isDataExport) return; if (!!props.defaultData) { form.setFieldsValue({ allowSubmitWhenLessAuditLevel: props.defaultData @@ -30,7 +35,7 @@ const BasicInfo: React.FC = (props) => { WorkflowTemplateDetailResV1AllowSubmitWhenLessAuditLevelEnum.warn }); } - }, [form, props.defaultData]); + }, [form, props.defaultData, isDataExport]); const handleChangeLevel = ( level: WorkflowTemplateDetailResV1AllowSubmitWhenLessAuditLevelEnum ) => { @@ -49,28 +54,30 @@ const BasicInfo: React.FC = (props) => {
- - - {getAuditLevelStatusSelectOption()} - - + + {getAuditLevelStatusSelectOption()} + + + )} void; totalStep: number; + workflowType?: getWorkflowTemplateV1WorkflowTypeEnum; }; export type BaseFormFields = { diff --git a/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/components/ReviewNodeInfo/__snapshots__/index.test.tsx.snap b/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/components/ReviewNodeInfo/__snapshots__/index.test.tsx.snap index 94b1569cdc..32f2c3b5b1 100644 --- a/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/components/ReviewNodeInfo/__snapshots__/index.test.tsx.snap +++ b/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/components/ReviewNodeInfo/__snapshots__/index.test.tsx.snap @@ -310,7 +310,7 @@ exports[`page/WorkflowTemplate/ReviewNodeInfo render normal exec node info 1`] = 审核流程模板最多可设置4个审核步骤,也可不设置审核步骤;
  • - 单个步骤指定执行人时,最少需要添加一个指定人,最多只能添加三个指定人。 + 单个步骤指定执行人时,最少需要添加一个指定人,最多只能添加十个指定人。
  • @@ -616,7 +616,7 @@ exports[`page/WorkflowTemplate/ReviewNodeInfo render normal review node info 1`] 审核流程模板最多可设置4个审核步骤,也可不设置审核步骤;
  • - 单个步骤指定执行人时,最少需要添加一个指定人,最多只能添加三个指定人。 + 单个步骤指定执行人时,最少需要添加一个指定人,最多只能添加十个指定人。
  • @@ -953,7 +953,7 @@ exports[`page/WorkflowTemplate/ReviewNodeInfo render review node and update revi 审核流程模板最多可设置4个审核步骤,也可不设置审核步骤;
  • - 单个步骤指定执行人时,最少需要添加一个指定人,最多只能添加三个指定人。 + 单个步骤指定执行人时,最少需要添加一个指定人,最多只能添加十个指定人。
  • @@ -1314,7 +1314,7 @@ exports[`page/WorkflowTemplate/ReviewNodeInfo render review node with default as 审核流程模板最多可设置4个审核步骤,也可不设置审核步骤;
  • - 单个步骤指定执行人时,最少需要添加一个指定人,最多只能添加三个指定人。 + 单个步骤指定执行人时,最少需要添加一个指定人,最多只能添加十个指定人。
  • @@ -1841,7 +1841,7 @@ exports[`page/WorkflowTemplate/ReviewNodeInfo validate assignee user number 1`] 审核流程模板最多可设置4个审核步骤,也可不设置审核步骤;
  • - 单个步骤指定执行人时,最少需要添加一个指定人,最多只能添加三个指定人。 + 单个步骤指定执行人时,最少需要添加一个指定人,最多只能添加十个指定人。
  • diff --git a/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/components/ReviewNodeInfo/index.tsx b/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/components/ReviewNodeInfo/index.tsx index a9575cf7c3..e54441456e 100644 --- a/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/components/ReviewNodeInfo/index.tsx +++ b/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/components/ReviewNodeInfo/index.tsx @@ -15,11 +15,14 @@ import { import { IWorkFlowStepTemplateResV1 } from '@actiontech/shared/lib/api/sqle/service/common'; import StepButton from '../StepButton'; import { InfoCircleOutlined } from '@ant-design/icons'; +import { getWorkflowTemplateV1WorkflowTypeEnum } from '@actiontech/shared/lib/api/sqle/service/workflow/index.enum'; const MAX_USER_COUNT = 10; const ReviewAndExecNodeInfo: React.FC = (props) => { const { t } = useTranslation(); + const isDataExport = + props.workflowType === getWorkflowTemplateV1WorkflowTypeEnum.data_export; const [authorizedParam, setAuthorizedParam] = useState( props.type === NodeTypeEnum.review ? 'approved_by_authorized' @@ -86,119 +89,151 @@ const ReviewAndExecNodeInfo: React.FC = (props) => { const nextStep = async () => { props.form.validateFields().then(() => props?.nextStep?.()); }; + const isDataExportExec = isDataExport && props.type === NodeTypeEnum.exec; return (
    {props.type === NodeTypeEnum.review ? t('workflowTemplate.step.progressTitle') + : isDataExport + ? t('workflowTemplate.step.exportExecTitle') : t('workflowTemplate.step.execTitle')}
    {props.type === NodeTypeEnum.review ? t('workflowTemplate.step.progressDesc') + : isDataExport + ? t('workflowTemplate.step.exportExecDesc') : t('workflowTemplate.step.execDesc')}
    - + + + + {t( + 'workflowTemplate.progressConfig.exportExec.creatorAsExecutor' + )} + + + + + ) : ( + <> + + + + + + + - - - - - - - - {props.generateUsernameSelectOption()} - - - + ] + : [] + } + > + + {props.generateUsernameSelectOption()} + + + + + )} @@ -208,7 +243,11 @@ const ReviewAndExecNodeInfo: React.FC = (props) => {
      -
    • {t('workflowTemplate.progressConfig.ruler.rule1')}
    • +
    • + {isDataExport + ? t('workflowTemplate.progressConfig.exportRuler.rule1') + : t('workflowTemplate.progressConfig.ruler.rule1')} +
    • {t('workflowTemplate.progressConfig.ruler.rule2')}
    • {t('workflowTemplate.progressConfig.ruler.rule3')}
    diff --git a/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/components/ReviewNodeInfo/index.type.ts b/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/components/ReviewNodeInfo/index.type.ts index af1a5e391a..18c67ebc1a 100644 --- a/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/components/ReviewNodeInfo/index.type.ts +++ b/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/components/ReviewNodeInfo/index.type.ts @@ -1,4 +1,5 @@ import { IWorkFlowStepTemplateResV1 } from '@actiontech/shared/lib/api/sqle/service/common'; +import { getWorkflowTemplateV1WorkflowTypeEnum } from '@actiontech/shared/lib/api/sqle/service/workflow/index.enum'; import { FormInstance } from 'antd'; export enum NodeTypeEnum { @@ -29,4 +30,5 @@ export type ReviewAndExecNodeInfoProps = { updateReviewAndExecNodeInfo: (data: IWorkFlowStepTemplateResV1) => void; generateUsernameSelectOption: () => React.ReactNode; getUsernameListLoading: boolean; + workflowType?: getWorkflowTemplateV1WorkflowTypeEnum; }; diff --git a/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/components/StepInfo/index.tsx b/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/components/StepInfo/index.tsx index dd7bd5af05..41a89ca6ae 100644 --- a/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/components/StepInfo/index.tsx +++ b/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/components/StepInfo/index.tsx @@ -48,7 +48,8 @@ const StepInfo: React.FC = (props) => { reviewStepData: props.reviewStepData, execStepData: props.execStepData, usernameList: props.usernameList, - theme: sqleTheme.icon + theme: sqleTheme.icon, + isDataExport: props.isDataExport }), [ templateLevel, @@ -56,6 +57,7 @@ const StepInfo: React.FC = (props) => { props.execStepData, props.reviewStepData, props.usernameList, + props.isDataExport, sqleTheme ] ); @@ -133,7 +135,11 @@ const StepInfo: React.FC = (props) => { key={step.key} indexNumber={index} rowKey={step.key} - removeReviewNode={props.removeReviewNode} + removeReviewNode={ + props.reviewStepData.length > 1 + ? props.removeReviewNode + : undefined + } clickReviewNode={props.clickReviewNode} /> ) : ( @@ -151,7 +157,11 @@ const StepInfo: React.FC = (props) => { {...step} key={`${step.key}-step-card`} indexNumber={index} - close={props?.removeReviewNode} + close={ + props.reviewStepData.length > 1 + ? props?.removeReviewNode + : undefined + } click={props.clickReviewNode} /> diff --git a/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/index.test.tsx b/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/index.test.tsx index 3f572b005a..ecbc1dda15 100644 --- a/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/index.test.tsx +++ b/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/index.test.tsx @@ -20,18 +20,25 @@ import { UtilsConsoleErrorStringsEnum } from '@actiontech/shared/lib/testUtil/common'; import user from '@actiontech/shared/lib/testUtil/mockApi/sqle/user'; +import { useTypedQuery } from '@actiontech/shared'; jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), useParams: jest.fn() })); +jest.mock('@actiontech/shared', () => ({ + ...jest.requireActual('@actiontech/shared'), + useTypedQuery: jest.fn() +})); + describe('page/WorkflowTemplate/UpdateWorkflowTemplate', () => { ignoreConsoleErrors([ UtilsConsoleErrorStringsEnum.UNCONNECTED_FORM_COMPONENT ]); const useParamsMock: jest.Mock = useParams as jest.Mock; + const extractQuerySpy = jest.fn(); const customRender = () => { return sqleSuperRender(); @@ -45,6 +52,8 @@ describe('page/WorkflowTemplate/UpdateWorkflowTemplate', () => { mockUseCurrentUser(); jest.useFakeTimers(); workflowTemplate.mockAllApi(); + (useTypedQuery as jest.Mock).mockImplementation(() => extractQuerySpy); + extractQuerySpy.mockReturnValue({ workflowType: 'workflow' }); }); afterEach(() => { @@ -66,7 +75,7 @@ describe('page/WorkflowTemplate/UpdateWorkflowTemplate', () => { expect(getInfoRequest).toHaveBeenCalled(); expect(baseElement).toMatchSnapshot(); await act(async () => jest.advanceTimersByTime(3000)); - expect(screen.getByText('返回审批流程模板')).toBeInTheDocument(); + expect(screen.getByText('编辑审批流程 - 上线工单')).toBeInTheDocument(); expect(getBySelector('a')).toBeInTheDocument(); expect(getBySelector('a')).toHaveAttribute( 'href', @@ -81,6 +90,7 @@ describe('page/WorkflowTemplate/UpdateWorkflowTemplate', () => { }); expect(updateInfoRequest).toHaveBeenCalledWith({ project_name: mockProjectInfo.projectName, + workflow_type: 'workflow', workflow_step_template_list: workflowTemplateData.workflow_step_template_list, allow_submit_when_less_audit_level: @@ -91,7 +101,7 @@ describe('page/WorkflowTemplate/UpdateWorkflowTemplate', () => { expect(getAllBySelector('a').length).toBe(2); expect(getAllBySelector('a')?.[1]).toHaveAttribute( 'href', - `/sqle/project/${mockProjectInfo.projectID}/progress` + `/sqle/project/${mockProjectInfo.projectID}/progress?activeTab=workflow` ); }); @@ -119,6 +129,7 @@ describe('page/WorkflowTemplate/UpdateWorkflowTemplate', () => { }); expect(updateInfoRequest).toHaveBeenCalledWith({ project_name: mockProjectInfo.projectName, + workflow_type: 'workflow', workflow_step_template_list: workflowTemplateData.workflow_step_template_list, allow_submit_when_less_audit_level: @@ -176,6 +187,7 @@ describe('page/WorkflowTemplate/UpdateWorkflowTemplate', () => { }); expect(updateInfoRequest).toHaveBeenCalledWith({ project_name: mockProjectInfo.projectName, + workflow_type: 'workflow', workflow_step_template_list: tempList, allow_submit_when_less_audit_level: UpdateWorkflowTemplateReqV1AllowSubmitWhenLessAuditLevelEnum.normal @@ -246,4 +258,41 @@ describe('page/WorkflowTemplate/UpdateWorkflowTemplate', () => { await act(async () => jest.advanceTimersByTime(300)); }); }); + + describe('data_export workflow type', () => { + beforeEach(() => { + extractQuerySpy.mockReturnValue({ workflowType: 'data_export' }); + }); + + it('should render data export title when workflowType is data_export', async () => { + workflowTemplate.getWorkflowTemplate(); + user.getUserTipList(); + customRender(); + await act(async () => jest.advanceTimersByTime(3000)); + await act(async () => jest.advanceTimersByTime(3000)); + expect(screen.getByText('编辑审批流程 - 数据导出')).toBeInTheDocument(); + }); + + it('should use export step types when workflowType is data_export', async () => { + workflowTemplate.getWorkflowTemplate(); + const updateInfoRequest = workflowTemplate.updateWorkflowTemplate(); + user.getUserTipList(); + customRender(); + await act(async () => jest.advanceTimersByTime(3000)); + await act(async () => jest.advanceTimersByTime(3000)); + + fireEvent.click(screen.getByText('下一步')); + await act(async () => jest.advanceTimersByTime(3000)); + + await act(async () => { + fireEvent.click(screen.getByText('保 存')); + await act(async () => jest.advanceTimersByTime(300)); + }); + expect(updateInfoRequest).toHaveBeenCalledWith( + expect.objectContaining({ + workflow_type: 'data_export' + }) + ); + }); + }); }); diff --git a/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/index.tsx b/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/index.tsx index 0154e97fd0..5b2edc2a34 100644 --- a/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/index.tsx +++ b/packages/sqle/src/page/WorkflowTemplate/UpdateWorkflowTemplate/index.tsx @@ -1,8 +1,12 @@ import { BasicButton, BasicResult, PageHeader } from '@actiontech/dms-kit'; -import { ActionButton, useTypedParams } from '@actiontech/shared'; +import { + ActionButton, + useTypedParams, + useTypedQuery +} from '@actiontech/shared'; import { ArrowLeftOutlined } from '@ant-design/icons'; import { Col, Row, Space, Spin } from 'antd'; -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import BasicInfo from './components/BasicInfo'; import StepInfo from './components/StepInfo'; @@ -29,7 +33,12 @@ import { useBoolean, useRequest } from 'ahooks'; import { useForm } from 'antd/es/form/Form'; import { WorkflowTemplateStyleWrapper } from '../WorkflowTemplateDetail/style'; import useUsername from '../../../hooks/useUsername'; -import { ROUTE_PATHS } from '@actiontech/dms-kit'; +import { ROUTE_PATHS, ResponseCode } from '@actiontech/dms-kit'; +import { + getWorkflowTemplateV1WorkflowTypeEnum, + updateWorkflowTemplateV1WorkflowTypeEnum +} from '@actiontech/shared/lib/api/sqle/service/workflow/index.enum'; + const UpdateWorkflowTemplate: React.FC = () => { const { t } = useTranslation(); const [form] = useForm(); @@ -41,6 +50,41 @@ const UpdateWorkflowTemplate: React.FC = () => { const [updateSuccess, setUpdateSuccess] = useState(false); const urlParams = useTypedParams(); const { projectName, projectID } = useCurrentProject(); + const extractQueries = useTypedQuery(); + const [workflowType, setWorkflowType] = useState< + getWorkflowTemplateV1WorkflowTypeEnum | undefined + >(undefined); + + useEffect(() => { + const searchParams = extractQueries(ROUTE_PATHS.SQLE.PROGRESS.update); + const isWorkflowType = ( + value: string + ): value is getWorkflowTemplateV1WorkflowTypeEnum => { + return Object.values(getWorkflowTemplateV1WorkflowTypeEnum).includes( + value as getWorkflowTemplateV1WorkflowTypeEnum + ); + }; + if ( + searchParams?.workflowType && + isWorkflowType(searchParams.workflowType) + ) { + setWorkflowType(searchParams.workflowType); + } + }, [extractQueries]); + + const pageTitle = + workflowType === getWorkflowTemplateV1WorkflowTypeEnum.data_export + ? t('workflowTemplate.update.title.dataExport') + : t('workflowTemplate.update.title.workflow'); + + const reviewType = + workflowType === 'data_export' + ? WorkFlowStepTemplateReqV1TypeEnum.export_review + : WorkFlowStepTemplateReqV1TypeEnum.sql_review; + const executeType = + workflowType === 'data_export' + ? WorkFlowStepTemplateReqV1TypeEnum.export_execute + : WorkFlowStepTemplateReqV1TypeEnum.sql_execute; const { loading: getUsernameListLoading, updateUsernameList, @@ -63,26 +107,30 @@ const UpdateWorkflowTemplate: React.FC = () => { await form?.validateFields(); const reviewTempData = reviewSteps.map((item) => ({ ...item, - type: WorkFlowStepTemplateReqV1TypeEnum.sql_review + type: reviewType })); const templateList: IWorkFlowStepTemplateReqV1[] = [ ...reviewTempData, { ...execSteps, - type: WorkFlowStepTemplateReqV1TypeEnum.sql_execute + type: executeType } ]; startSubmit(); return workflow .updateWorkflowTemplateV1({ project_name: projectName, + workflow_type: + workflowType as unknown as updateWorkflowTemplateV1WorkflowTypeEnum, workflow_step_template_list: templateList, allow_submit_when_less_audit_level: selectLevel as | UpdateWorkflowTemplateReqV1AllowSubmitWhenLessAuditLevelEnum | undefined }) .then((res) => { - setUpdateSuccess(true); + if (res.data.code === ResponseCode.SUCCESS) { + setUpdateSuccess(true); + } return res; }) .finally(() => submitFinish()); @@ -92,7 +140,8 @@ const UpdateWorkflowTemplate: React.FC = () => { () => workflow .getWorkflowTemplateV1({ - project_name: projectName + project_name: projectName, + workflow_type: workflowType as getWorkflowTemplateV1WorkflowTypeEnum }) .then((res) => { const temp = res.data.data; @@ -150,6 +199,7 @@ const UpdateWorkflowTemplate: React.FC = () => { nextStep={nextStep} updateBaseInfo={updateBaseInfo} totalStep={reviewSteps.length + 2} + workflowType={workflowType} /> ); } @@ -173,6 +223,7 @@ const UpdateWorkflowTemplate: React.FC = () => { getUsernameListLoading={getUsernameListLoading} totalStep={reviewSteps.length + 1} updateReviewAndExecNodeInfo={updateReviewAndExecNodeInfo} + workflowType={workflowType} /> ); }; @@ -184,7 +235,7 @@ const UpdateWorkflowTemplate: React.FC = () => { assignee_user_id_list: [], desc: '', approved_by_authorized: true, - type: WorkFlowStepTemplateReqV1TypeEnum.sql_review + type: reviewType } ]); setCurrentStep(reviewSteps.length + 1); @@ -204,8 +255,10 @@ const UpdateWorkflowTemplate: React.FC = () => { assignee_user_id_list: [], desc: '', execute_by_authorized: true, - type: WorkFlowStepTemplateReqV1TypeEnum.sql_execute + type: executeType }); + basicForm.resetFields(); + form.resetFields(); }; const handleExchangeReviewNode = (from: number, to: number) => { const temp = cloneDeep(reviewSteps); @@ -232,7 +285,7 @@ const UpdateWorkflowTemplate: React.FC = () => { } - text={t('workflowTemplate.create.title.returnButton')} + text={pageTitle} actionType="navigate-link" link={{ to: ROUTE_PATHS.SQLE.PROGRESS.index, @@ -275,6 +328,7 @@ const UpdateWorkflowTemplate: React.FC = () => { exchangeReviewNode={handleExchangeReviewNode} clickReviewNode={handleClickReviewNode} usernameList={usernameList} + isDataExport={workflowType === 'data_export'} /> { to: ROUTE_PATHS.SQLE.PROGRESS.index, params: { projectID + }, + queries: { + activeTab: workflowType } }} /> diff --git a/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/__snapshots__/index.ce.test.tsx.snap b/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/__snapshots__/index.ce.test.tsx.snap index fe53f498d2..8aca3e9b2e 100644 --- a/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/__snapshots__/index.ce.test.tsx.snap +++ b/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/__snapshots__/index.ce.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`page/WorkflowTemplate CE render workflow template detail 1`] = ` +exports[`page/WorkflowTemplate CE render workflow template detail without edit actions in CE 1`] = `
    + + +
    +
    +
    +
    +
    +
    + + + + + + +
    +
    +
    +
    - - - - - -
    -
    -
    - - 工单发起/工单更新SQL语句 - + + + +
    +
    +
    - 工单被创建,或者工单被驳回后等待修改SQL语句 +
    + + 工单发起/工单更新SQL语句 + +
    + 工单被创建,或者工单被驳回后等待修改SQL语句 +
    +
    -
    -
    -
    - - - - - - -
    -
    -
    - - -
    -
    + + + +
    -
    - - 审核节点 - - #1 - - -
    - - -
    + + + +
    +
    +
    - 审核人 -
    -
    - - - - 匹配拥有数据源审核权限的成员 + 审核节点 + + #1 + +
    + - +
    +
    +
    + 审核人 +
    +
    + + + + + 匹配拥有数据源审核权限的成员 + +
    +
    -
    -
    -
    - - - -
    -
    -
    - - -
    -
    +
    -
    - - 审核节点 - - #2 - - -
    - step desc -
    + + + +
    +
    +
    - 审核人 -
    -
    + + 审核节点 + + #2 + + +
    + step desc +
    - +
    +
    - - T - - + + + 1 + + +
    +
    @@ -306,115 +379,115 @@ exports[`page/WorkflowTemplate CE render workflow template detail 1`] = `
    -
    -
    -
    - - - - - - -
    -
    -
    - - -
    -
    + + + +
    -
    - - 执行上线 - -
    - - -
    + + + +
    +
    +
    - 执行人 -
    -
    - - - - 匹配拥有数据源上线权限的成员 + 执行上线 +
    + - +
    +
    +
    + 执行人 +
    +
    + + + + + 匹配拥有数据源上线权限的成员 + +
    +
    @@ -422,115 +495,115 @@ exports[`page/WorkflowTemplate CE render workflow template detail 1`] = `
    -
    -
    -
    -
    - - 允许创建工单的最高审核等级 - - - 告警(Warning) -
    + + 允许创建工单的最高审核等级 + + + 告警(Warning) +
    -
    -
    -
    + aria-valuenow="75" + class="ant-progress ant-progress-status-normal ant-progress-steps" + role="progressbar" + > +
    +
    +
    +
    +
    +
    +
    -
    -
    -
    - - 注意事项 - -
    -
      -
    • - 若项目管理员对审批流程模板进行了修改,不会对已经在审批流程的工单造成影响; -
    • -
    • - 被驳回的工单,需要创建人更新SQL语句后重新发起,驳回记录可在 “工单进度-工单历史操作” 中查看; -
    • -
    • - 处于 “审核节点” 中的工单,创建人可在工单详情页随时关闭; -
    • -
    • - 审核工单:审核人在该步骤可以执行「审核通过」或「驳回」操作; -
    • -
    • - 上线工单:执行人在该步骤可以执行「执行上线」或「驳回」操作。 -
    • -
    -
    -
    -
    - - 审批流程模板更新时间 - -
    - - - - + 注意事项 + +
    +
      +
    • + 若项目管理员对审批流程模板进行了修改,不会对已经在审批流程的工单造成影响; +
    • +
    • + 被驳回的工单,需要创建人更新SQL语句后重新发起,驳回记录可在 “工单进度-工单历史操作” 中查看; +
    • +
    • + 处于 “审核节点” 中的工单,创建人可在工单详情页随时关闭; +
    • +
    • + 审核工单:审核人在该步骤可以执行「审核通过」或「驳回」操作; +
    • +
    • + 上线工单:执行人在该步骤可以执行「执行上线」或「驳回」操作。 +
    • +
    +
    +
    +
    - 2023-12-26 14:19:12 - + + 审批流程模板更新时间 + +
    + + + + + 2023-12-26 14:19:12 + +
    +
    diff --git a/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/__snapshots__/index.test.tsx.snap b/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/__snapshots__/index.test.tsx.snap index ac86d7daac..e818811750 100644 --- a/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/__snapshots__/index.test.tsx.snap +++ b/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/__snapshots__/index.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`page/WorkflowTemplate/WorkflowTemplateDetail render workflow template detail 1`] = ` +exports[`page/WorkflowTemplate/WorkflowTemplateDetail render workflow template detail page with SegmentedTabs 1`] = `
    +
    +
    +
    +
    +
    + + +
    +
    -
    -
    +
    +
    + + + + + + +
    +
    - - - - - -
    -
    -
    - - 工单发起/工单更新SQL语句 - + + + +
    +
    +
    - 工单被创建,或者工单被驳回后等待修改SQL语句 +
    + + 工单发起/工单更新SQL语句 + +
    + 工单被创建,或者工单被驳回后等待修改SQL语句 +
    +
    -
    -
    -
    - - - - - - -
    -
    -
    - - -
    -
    + + + +
    -
    - - 审核节点 - - #1 - - -
    - - -
    + + + +
    +
    +
    - 审核人 -
    -
    - - - - 匹配拥有数据源审核权限的成员 + 审核节点 + + #1 + +
    + - +
    +
    +
    + 审核人 +
    +
    + + + + + 匹配拥有数据源审核权限的成员 + +
    +
    -
    -
    -
    - - - -
    -
    -
    - - -
    -
    +
    -
    - - 审核节点 - - #2 - - -
    - step desc -
    + + + +
    +
    +
    - 审核人 -
    -
    + + 审核节点 + + #2 + + +
    + step desc +
    - +
    +
    - - T - - + + + 1 + + +
    +
    @@ -342,115 +414,115 @@ exports[`page/WorkflowTemplate/WorkflowTemplateDetail render workflow template d
    -
    -
    -
    - - - - - - -
    -
    -
    - - -
    -
    + + + +
    -
    - - 执行上线 - -
    - - -
    + + + +
    +
    +
    - 执行人 -
    -
    - - - - 匹配拥有数据源上线权限的成员 + 执行上线 +
    + - +
    +
    +
    + 执行人 +
    +
    + + + + + 匹配拥有数据源上线权限的成员 + +
    +
    @@ -458,1015 +530,117 @@ exports[`page/WorkflowTemplate/WorkflowTemplateDetail render workflow template d
    -
    -
    -
    -
    - - 允许创建工单的最高审核等级 - - - 告警(Warning) -
    + + 允许创建工单的最高审核等级 + + + 告警(Warning) +
    -
    -
    + aria-valuenow="75" + class="ant-progress ant-progress-status-normal ant-progress-steps" + role="progressbar" + > +
    +
    +
    +
    +
    +
    +
    +
    +
    + + 注意事项 +
    + class="ant-typography auth-info-item" + > +
      +
    • + 若项目管理员对审批流程模板进行了修改,不会对已经在审批流程的工单造成影响; +
    • +
    • + 被驳回的工单,需要创建人更新SQL语句后重新发起,驳回记录可在 “工单进度-工单历史操作” 中查看; +
    • +
    • + 处于 “审核节点” 中的工单,创建人可在工单详情页随时关闭; +
    • +
    • + 审核工单:审核人在该步骤可以执行「审核通过」或「驳回」操作; +
    • +
    • + 上线工单:执行人在该步骤可以执行「执行上线」或「驳回」操作。 +
    • +
    +
    -
    -
    -
    - - 注意事项 - -
    -
      -
    • - 若项目管理员对审批流程模板进行了修改,不会对已经在审批流程的工单造成影响; -
    • -
    • - 被驳回的工单,需要创建人更新SQL语句后重新发起,驳回记录可在 “工单进度-工单历史操作” 中查看; -
    • -
    • - 处于 “审核节点” 中的工单,创建人可在工单详情页随时关闭; -
    • -
    • - 审核工单:审核人在该步骤可以执行「审核通过」或「驳回」操作; -
    • -
    • - 上线工单:执行人在该步骤可以执行「执行上线」或「驳回」操作。 -
    • -
    -
    -
    -
    - - 审批流程模板更新时间 - -
    - - - - - 2023-12-26 14:19:12 - -
    -
    -
    -
    -
    -
    -
    -
    -
    - -`; - -exports[`page/WorkflowTemplate/WorkflowTemplateDetail render workflow template detail with no review step 1`] = ` - -
    -
    - -
    -
    -
    -
    -
    -
    -
    - - - - - -
    -
    -
    -
    -
    - - 工单发起/工单更新SQL语句 - -
    - 工单被创建,或者工单被驳回后等待修改SQL语句 -
    -
    -
    -
    -
    -
    -
    - - - - - - -
    -
    -
    - - - - - -
    -
    -
    -
    -
    - - 执行上线 - -
    - - -
    -
    -
    - 执行人 -
    -
    - - - - - 匹配拥有数据源上线权限的成员 - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - - 允许创建工单的最高审核等级 - - - 告警(Warning) - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - - 注意事项 - -
    -
      -
    • - 若项目管理员对审批流程模板进行了修改,不会对已经在审批流程的工单造成影响; -
    • -
    • - 被驳回的工单,需要创建人更新SQL语句后重新发起,驳回记录可在 “工单进度-工单历史操作” 中查看; -
    • -
    • - 处于 “审核节点” 中的工单,创建人可在工单详情页随时关闭; -
    • -
    • - 审核工单:审核人在该步骤可以执行「审核通过」或「驳回」操作; -
    • -
    • - 上线工单:执行人在该步骤可以执行「执行上线」或「驳回」操作。 -
    • -
    -
    -
    -
    - - 审批流程模板更新时间 - -
    - - - - - 2023-12-26 14:19:12 - -
    -
    -
    -
    -
    -
    -
    -
    -
    - -`; - -exports[`page/WorkflowTemplate/WorkflowTemplateDetail render workflow template detail without permission 1`] = ` - -
    -
    -
    -
    - 审批流程模板 -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - - - - - -
    -
    -
    + 审批流程模板更新时间 +
    -
    - - 工单发起/工单更新SQL语句 - -
    - 工单被创建,或者工单被驳回后等待修改SQL语句 -
    -
    -
    -
    -
    -
    -
    - - - - - - -
    -
    -
    - - - - - -
    -
    -
    -
    -
    + + - - 审核节点 - - #1 - - -
    - - -
    -
    -
    - 审核人 -
    -
    - - - - - 匹配拥有数据源审核权限的成员 - -
    -
    -
    + 2023-12-26 14:19:12 +
    -
    - - - -
    -
    -
    - - - - - -
    -
    -
    -
    -
    - - 审核节点 - - #2 - - -
    - step desc -
    -
    -
    - 审核人 -
    -
    -
    -
    - - - T - - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - -
    -
    -
    - - - - - -
    -
    -
    -
    -
    - - 执行上线 - -
    - - -
    -
    -
    - 执行人 -
    -
    - - - - - 匹配拥有数据源上线权限的成员 - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - - 允许创建工单的最高审核等级 - - - 告警(Warning) - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - - 注意事项 - -
    -
      -
    • - 若项目管理员对审批流程模板进行了修改,不会对已经在审批流程的工单造成影响; -
    • -
    • - 被驳回的工单,需要创建人更新SQL语句后重新发起,驳回记录可在 “工单进度-工单历史操作” 中查看; -
    • -
    • - 处于 “审核节点” 中的工单,创建人可在工单详情页随时关闭; -
    • -
    • - 审核工单:审核人在该步骤可以执行「审核通过」或「驳回」操作; -
    • -
    • - 上线工单:执行人在该步骤可以执行「执行上线」或「驳回」操作。 -
    • -
    -
    -
    -
    - - 审批流程模板更新时间 - -
    - - - - - 2023-12-26 14:19:12 - -
    -
    diff --git a/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/actions.tsx b/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/actions.tsx index 326a4d82c8..4e61248dde 100644 --- a/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/actions.tsx +++ b/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/actions.tsx @@ -1,16 +1,20 @@ -import { t } from '../../../locale'; +import { ROUTE_PATHS } from '@actiontech/dms-kit'; import { PERMISSIONS, PermissionControl } from '@actiontech/shared/lib/features'; -import { ActionButton } from '@actiontech/shared'; import { EditOutlined } from '@ant-design/icons'; -import { ROUTE_PATHS } from '@actiontech/dms-kit'; -export const WorkflowTemplatePageHeaderActions = ( - projectID: string, - templateName?: string -): Record<'update-workflow-template', React.ReactNode> => ({ - 'update-workflow-template': ( +import { getWorkflowTemplateV1WorkflowTypeEnum } from '@actiontech/shared/lib/api/sqle/service/workflow/index.enum'; +import { ActionButton } from '@actiontech/shared'; +import { t } from '../../../locale'; + +export const workflowTemplateDetailAction = (params: { + projectID: string; + templateName?: string; + workflowType: getWorkflowTemplateV1WorkflowTypeEnum; +}) => { + const { projectID, templateName, workflowType } = params; + return ( @@ -24,9 +28,12 @@ export const WorkflowTemplatePageHeaderActions = ( params: { projectID, workflowName: templateName ?? '' + }, + queries: { + workflowType } }} /> - ) -}); + ); +}; diff --git a/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/column.tsx b/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/column.tsx new file mode 100644 index 0000000000..6b1f6e21f6 --- /dev/null +++ b/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/column.tsx @@ -0,0 +1,67 @@ +import { ActiontechTableColumn } from '@actiontech/dms-kit/es/components/ActiontechTable'; +import { IWorkflowTemplateDetailResV1 } from '@actiontech/shared/lib/api/sqle/service/common'; +import { BasicTag } from '@actiontech/dms-kit'; +import { formatTime } from '@actiontech/dms-kit'; +import { t } from '../../../locale'; +import { WorkflowTemplateDetailResV1WorkflowTypeEnum } from '@actiontech/shared/lib/api/sqle/service/common.enum'; + +const stepTypeNameMap: Record = { + sql_review: t('workflowTemplate.progressConfig.review.title'), + sql_execute: t('workflowTemplate.progressConfig.exec.title'), + export_review: t('workflowTemplate.progressConfig.exportReview.title'), + export_execute: t('workflowTemplate.progressConfig.exportExecute.title') +}; + +const getStepTypeName = (stepType?: string): string => { + if (!stepType) return '-'; + const name = stepTypeNameMap[stepType]; + return name ? name : stepType; +}; + +export const WorkflowTemplateListColumn = + (): ActiontechTableColumn => { + return [ + { + dataIndex: 'workflow_template_name', + title: () => t('workflowTemplate.list.table.workflowTemplateName') + }, + { + dataIndex: 'workflow_type', + title: () => t('workflowTemplate.list.table.applicableType'), + render: (workflowType) => { + if ( + workflowType === + WorkflowTemplateDetailResV1WorkflowTypeEnum.data_export + ) { + return ( + + {t('workflowTemplate.list.type.dataExport')} + + ); + } + return ( + + {t('workflowTemplate.list.type.workflow')} + + ); + } + }, + { + dataIndex: 'workflow_step_template_list', + title: () => t('workflowTemplate.list.table.approvalNodeDesc'), + render: (stepList) => { + if (!stepList || stepList.length === 0) return '-'; + return stepList + .map((step: { type?: string }) => getStepTypeName(step.type)) + .join(' -> '); + } + }, + { + dataIndex: 'update_time', + title: () => t('workflowTemplate.list.table.updateTime'), + render: (value) => { + return formatTime(value, '-'); + } + } + ]; + }; diff --git a/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/components/WorkflowTemplateAuthInfo/index.tsx b/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/components/WorkflowTemplateAuthInfo/index.tsx index 09870905c2..3232c37bfd 100644 --- a/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/components/WorkflowTemplateAuthInfo/index.tsx +++ b/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/components/WorkflowTemplateAuthInfo/index.tsx @@ -14,32 +14,35 @@ import { formatTime } from '@actiontech/dms-kit'; const WorkflowTemplateAuthInfo: React.FC = ({ level, - time + time, + hideLevel }) => { const { t } = useTranslation(); const { sqleTheme } = useThemeStyleData(); const { currentLevelData, levelText } = useGetLevelData(level); return ( - - - {t('workflowTemplate.form.label.allowSubmitWhenLessAuditLevel')} - - - {levelText} - - - + {!hideLevel && ( + + + {t('workflowTemplate.form.label.allowSubmitWhenLessAuditLevel')} + + + {levelText} + + + + )} {t('workflowTemplate.detail.title.noticeInfo')} @@ -47,10 +50,16 @@ const WorkflowTemplateAuthInfo: React.FC = ({
    • {t('workflowTemplate.detail.authLevelInfo.first')}
    • -
    • {t('workflowTemplate.detail.authLevelInfo.second')}
    • + {!hideLevel && ( +
    • {t('workflowTemplate.detail.authLevelInfo.second')}
    • + )}
    • {t('workflowTemplate.detail.authLevelInfo.third')}
    • {t('workflowTemplate.detail.authLevelInfo.fourth')}
    • -
    • {t('workflowTemplate.detail.authLevelInfo.fifth')}
    • +
    • + {hideLevel + ? t('workflowTemplate.detail.authLevelInfo.fifthExport') + : t('workflowTemplate.detail.authLevelInfo.fifth')} +
    diff --git a/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/components/WorkflowTemplateAuthInfo/index.type.ts b/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/components/WorkflowTemplateAuthInfo/index.type.ts index 49fa2bc6ab..3d24682fde 100644 --- a/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/components/WorkflowTemplateAuthInfo/index.type.ts +++ b/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/components/WorkflowTemplateAuthInfo/index.type.ts @@ -1,8 +1,9 @@ import { WorkflowTemplateDetailResV1AllowSubmitWhenLessAuditLevelEnum } from '@actiontech/shared/lib/api/sqle/service/common.enum'; export interface WorkflowTemplateAuthInfoProps { - level: + level?: | WorkflowTemplateDetailResV1AllowSubmitWhenLessAuditLevelEnum | undefined; time?: string; + hideLevel?: boolean; } diff --git a/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/components/WorkflowTemplateStepInfo/index.tsx b/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/components/WorkflowTemplateStepInfo/index.tsx index 98f636efb1..331971587e 100644 --- a/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/components/WorkflowTemplateStepInfo/index.tsx +++ b/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/components/WorkflowTemplateStepInfo/index.tsx @@ -22,7 +22,8 @@ const WorkflowTemplateStepInfo: React.FC = ( reviewStepData: props.reviewStepData, execStepData: props.execStepData, usernameList: props.usernameList, - theme: sqleTheme.icon + theme: sqleTheme.icon, + isDataExport: props.isDataExport }).map((step) => step.show ? ( diff --git a/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/components/WorkflowTemplateStepInfo/index.type.ts b/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/components/WorkflowTemplateStepInfo/index.type.ts index 49c118757b..de3c27bccd 100644 --- a/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/components/WorkflowTemplateStepInfo/index.type.ts +++ b/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/components/WorkflowTemplateStepInfo/index.type.ts @@ -7,4 +7,5 @@ export interface IWorkflowTemplateStepInfoProps { reviewStepData: IWorkFlowStepTemplateResV1[]; execStepData: IWorkFlowStepTemplateResV1; usernameList: IUserTipResV1[]; + isDataExport?: boolean; } diff --git a/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/index.ce.test.tsx b/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/index.ce.test.tsx index 2557caa0f6..35d69a2c0e 100644 --- a/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/index.ce.test.tsx +++ b/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/index.ce.test.tsx @@ -8,15 +8,30 @@ import { act, cleanup, screen } from '@testing-library/react'; import workflowTemplate from '@actiontech/shared/lib/testUtil/mockApi/sqle/workflowTemplate'; import { mockUseCurrentProject } from '@actiontech/shared/lib/testUtil/mockHook/mockUseCurrentProject'; import { mockUseCurrentUser } from '@actiontech/shared/lib/testUtil/mockHook/mockUseCurrentUser'; -import user from '@actiontech/shared/lib/testUtil/mockApi/sqle/user'; + +jest.mock('react-redux', () => { + return { + ...jest.requireActual('react-redux'), + useSelector: jest.fn() + }; +}); describe('page/WorkflowTemplate CE', () => { beforeEach(() => { workflowTemplate.mockAllApi(); - user.mockAllApi(); mockUseCurrentProject(); mockUseCurrentUser(); jest.useFakeTimers(); + + const { useSelector } = require('react-redux'); + (useSelector as jest.Mock).mockImplementation((selector: Function) => + selector({ + permission: { + moduleFeatureSupport: {}, + userOperationPermissions: null + } + }) + ); }); afterEach(() => { @@ -28,14 +43,13 @@ describe('page/WorkflowTemplate CE', () => { return sqleSuperRender(); }; - it('render workflow template detail', async () => { - const getInfoRequest = workflowTemplate.getWorkflowTemplate(); - const userInfoRequest = user.getUserTipList(); + it('render workflow template detail without edit actions in CE', async () => { + const getTemplateRequest = workflowTemplate.getWorkflowTemplate(); const { baseElement } = customRender(); await act(async () => jest.advanceTimersByTime(3000)); - expect(getInfoRequest).toHaveBeenCalled(); - expect(userInfoRequest).toHaveBeenCalled(); + expect(getTemplateRequest).toHaveBeenCalled(); expect(baseElement).toMatchSnapshot(); + expect(screen.getByText('审批流程模板')).toBeInTheDocument(); expect(screen.queryByText('修改当前审批流程模板')).not.toBeInTheDocument(); }); }); diff --git a/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/index.test.tsx b/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/index.test.tsx index 8c48e3e424..2c017746f9 100644 --- a/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/index.test.tsx +++ b/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/index.test.tsx @@ -1,19 +1,10 @@ import { sqleSuperRender } from '../../../testUtils/superRender'; import WorkflowTemplateDetail from '.'; -import { workflowTemplateData } from '@actiontech/shared/lib/testUtil/mockApi/sqle/workflowTemplate/data'; import { act, cleanup, screen } from '@testing-library/react'; import workflowTemplate from '@actiontech/shared/lib/testUtil/mockApi/sqle/workflowTemplate'; import { mockUseCurrentProject } from '@actiontech/shared/lib/testUtil/mockHook/mockUseCurrentProject'; import { mockUseCurrentUser } from '@actiontech/shared/lib/testUtil/mockHook/mockUseCurrentUser'; -import { getBySelector } from '@actiontech/shared/lib/testUtil/customQuery'; -import { - mockProjectInfo, - mockCurrentUserReturn -} from '@actiontech/shared/lib/testUtil/mockHook/data'; -import { createSpySuccessResponse } from '@actiontech/shared/lib/testUtil/mockApi'; -import user from '@actiontech/shared/lib/testUtil/mockApi/sqle/user'; import { mockUsePermission } from '@actiontech/shared/lib/testUtil/mockHook/mockUsePermission'; -import { SystemRole } from '@actiontech/dms-kit'; jest.mock('react-redux', () => { return { @@ -25,7 +16,6 @@ jest.mock('react-redux', () => { describe('page/WorkflowTemplate/WorkflowTemplateDetail', () => { beforeEach(() => { workflowTemplate.mockAllApi(); - user.mockAllApi(); mockUseCurrentProject(); mockUseCurrentUser(); jest.useFakeTimers(); @@ -43,108 +33,21 @@ describe('page/WorkflowTemplate/WorkflowTemplateDetail', () => { return sqleSuperRender(); }; - it('render workflow template detail', async () => { - const getInfoRequest = workflowTemplate.getWorkflowTemplate(); - const userInfoRequest = user.getUserTipList(); + it('render workflow template detail page with SegmentedTabs', async () => { + const getTemplateRequest = workflowTemplate.getWorkflowTemplate(); const { baseElement } = customRender(); await act(async () => jest.advanceTimersByTime(3000)); - expect(getInfoRequest).toHaveBeenCalled(); - expect(userInfoRequest).toHaveBeenCalled(); + expect(getTemplateRequest).toHaveBeenCalled(); expect(baseElement).toMatchSnapshot(); expect(screen.getByText('审批流程模板')).toBeInTheDocument(); - expect(getBySelector('a')).toBeInTheDocument(); - expect(getBySelector('a')).toHaveAttribute( - 'href', - `/sqle/project/${mockProjectInfo.projectID}/progress/update/${workflowTemplateData.workflow_template_name}` - ); - - expect(screen.getByText('工单发起/工单更新SQL语句')).toBeInTheDocument(); - expect(screen.getAllByText('审核节点')?.[0]).toBeInTheDocument(); - expect(screen.getByText('执行上线')).toBeInTheDocument(); - - expect(screen.getByText('告警(Warning)')).toBeInTheDocument(); - expect(screen.getByText('2023-12-26 14:19:12')).toBeInTheDocument(); + expect(screen.queryByText('修改当前审批流程模板')).toBeInTheDocument(); }); - it('render workflow template detail without permission', async () => { - // not admin or global manager or project manager - mockUseCurrentUser({ - ...mockCurrentUserReturn, - userRoles: { - ...mockCurrentUserReturn.userRoles, - [SystemRole.admin]: false, - [SystemRole.systemAdministrator]: false - } - }); - const getInfoRequest = workflowTemplate.getWorkflowTemplate(); - const userInfoRequest = user.getUserTipList(); - const { baseElement } = customRender(); - await act(async () => jest.advanceTimersByTime(3000)); - expect(getInfoRequest).toHaveBeenCalled(); - expect(userInfoRequest).toHaveBeenCalled(); - expect(baseElement).toMatchSnapshot(); - expect(screen.queryByText('修改当前审批流程模板')).not.toBeInTheDocument(); - - // project manager - cleanup(); - mockUseCurrentUser({ - ...mockCurrentUserReturn, - userRoles: { - ...mockCurrentUserReturn.userRoles, - [SystemRole.admin]: false, - [SystemRole.systemAdministrator]: false - }, - bindProjects: [ - { - is_manager: true, - project_name: mockProjectInfo.projectName, - project_id: mockProjectInfo.projectID, - archived: false - } - ] - }); + it('render segmented tab labels correctly', async () => { + workflowTemplate.getWorkflowTemplate(); customRender(); - expect(screen.getByText('修改当前审批流程模板')).toBeInTheDocument(); - - // project is archived - cleanup(); - mockUseCurrentUser({ - ...mockCurrentUserReturn, - userRoles: { - ...mockCurrentUserReturn.userRoles, - [SystemRole.admin]: false, - [SystemRole.systemAdministrator]: false - }, - bindProjects: [ - { - is_manager: true, - project_name: mockProjectInfo.projectName, - project_id: mockProjectInfo.projectID, - archived: true - } - ] - }); - customRender(); - expect(screen.queryByText('修改当前审批流程模板')).not.toBeInTheDocument(); - }); - - it('render workflow template detail with no review step', async () => { - const getInfoRequest = workflowTemplate.getWorkflowTemplate(); - getInfoRequest.mockImplementation(() => { - const execStepData = - workflowTemplateData.workflow_step_template_list.pop(); - return createSpySuccessResponse({ - data: { - ...workflowTemplateData, - workflow_step_template_list: [execStepData] - } - }); - }); - const { baseElement } = customRender(); await act(async () => jest.advanceTimersByTime(3000)); - expect(getInfoRequest).toHaveBeenCalled(); - expect(baseElement).toMatchSnapshot(); - expect(screen.getByText('审批流程模板')).toBeInTheDocument(); - expect(screen.getByText('执行上线')).toBeInTheDocument(); + expect(screen.getByText('上线工单')).toBeInTheDocument(); + expect(screen.getByText('数据导出')).toBeInTheDocument(); }); }); diff --git a/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/index.tsx b/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/index.tsx index a7189e0f61..40885f6891 100644 --- a/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/index.tsx +++ b/packages/sqle/src/page/WorkflowTemplate/WorkflowTemplateDetail/index.tsx @@ -1,51 +1,115 @@ +import { useMemo, useCallback, useState } from 'react'; +import { useRequest } from 'ahooks'; +import { useTranslation } from 'react-i18next'; import { Col, Row, Spin } from 'antd'; -import React, { useState } from 'react'; import workflow from '@actiontech/shared/lib/api/sqle/service/workflow'; -import { useRequest } from 'ahooks'; import { useCurrentProject } from '@actiontech/shared/lib/features'; -import WorkflowTemplateAuthInfo from './components/WorkflowTemplateAuthInfo'; -import WorkflowTemplateStepInfo from './components/WorkflowTemplateStepInfo'; +import { useTypedQuery } from '@actiontech/shared'; +import { ROUTE_PATHS } from '@actiontech/dms-kit'; +import { PageHeader, SegmentedTabs } from '@actiontech/dms-kit'; import { IWorkFlowStepTemplateResV1 } from '@actiontech/shared/lib/api/sqle/service/common'; -import { PageHeader } from '@actiontech/dms-kit'; -import { useTranslation } from 'react-i18next'; +import WorkflowTemplateStepInfo from './components/WorkflowTemplateStepInfo'; +import WorkflowTemplateAuthInfo from './components/WorkflowTemplateAuthInfo'; import { WorkflowTemplateStyleWrapper } from './style'; import useUsername from '../../../hooks/useUsername'; -import { WorkflowTemplatePageHeaderActions } from './actions'; +import { getWorkflowTemplateV1WorkflowTypeEnum } from '@actiontech/shared/lib/api/sqle/service/workflow/index.enum'; +import { workflowTemplateDetailAction } from './actions'; + const WorkflowTemplateDetail: React.FC = () => { const { t } = useTranslation(); const { projectName, projectID } = useCurrentProject(); + const extractQueries = useTypedQuery(); + const searchParams = extractQueries(ROUTE_PATHS.SQLE.PROGRESS.index); + + const [activeTab, setActiveTab] = + useState(() => { + if ( + searchParams?.activeTab === + getWorkflowTemplateV1WorkflowTypeEnum.data_export + ) { + return getWorkflowTemplateV1WorkflowTypeEnum.data_export; + } + return getWorkflowTemplateV1WorkflowTypeEnum.workflow; + }); + const { updateUsernameList, usernameList, loading: getUsernameListLoading } = useUsername(); - React.useEffect(() => { + + useMemo(() => { updateUsernameList({ filter_project: projectName }); }, [projectName, updateUsernameList]); - const [reviewSteps, setReviewSteps] = useState( - [] - ); - const [execSteps, setExecSteps] = useState({ - assignee_user_id_list: [], - desc: '' - }); + + // Fetch SQL Exec Workflow template + const [workflowReviewSteps, setWorkflowReviewSteps] = useState< + IWorkFlowStepTemplateResV1[] + >([]); + const [workflowExecStep, setWorkflowExecStep] = + useState({ + assignee_user_id_list: [], + desc: '' + }); + const { data: workflowTemplate, loading: getWorkflowTemplateLoading } = useRequest( () => workflow .getWorkflowTemplateV1({ - project_name: projectName + project_name: projectName, + workflow_type: getWorkflowTemplateV1WorkflowTypeEnum.workflow + }) + .then((res) => { + const stepList = res.data.data?.workflow_step_template_list ?? []; + if (stepList.length <= 1) { + setWorkflowExecStep( + stepList[0] ?? { assignee_user_id_list: [], desc: '' } + ); + setWorkflowReviewSteps([]); + } else { + const execStep = stepList.pop(); + setWorkflowReviewSteps(stepList); + if (execStep) setWorkflowExecStep(execStep); + } + return res.data.data; + }), + { + ready: !!projectName + } + ); + + // Fetch Data Export workflow template + const [exportReviewSteps, setExportReviewSteps] = useState< + IWorkFlowStepTemplateResV1[] + >([]); + const [exportExecStep, setExportExecStep] = + useState({ + assignee_user_id_list: [], + desc: '' + }); + + const { data: exportTemplate, loading: getExportTemplateLoading } = + useRequest( + () => + workflow + .getWorkflowTemplateV1({ + project_name: projectName, + workflow_type: getWorkflowTemplateV1WorkflowTypeEnum.data_export }) .then((res) => { const stepList = res.data.data?.workflow_step_template_list ?? []; if (stepList.length <= 1) { - setExecSteps(stepList[0]); + setExportExecStep( + stepList[0] ?? { assignee_user_id_list: [], desc: '' } + ); + setExportReviewSteps([]); } else { const execStep = stepList.pop(); - setReviewSteps(stepList); - if (execStep) setExecSteps(execStep); + setExportReviewSteps(stepList); + if (execStep) setExportExecStep(execStep); } return res.data.data; }), @@ -53,36 +117,105 @@ const WorkflowTemplateDetail: React.FC = () => { ready: !!projectName } ); - const pageHeaderActions = WorkflowTemplatePageHeaderActions( - projectID, - workflowTemplate?.workflow_template_name + + const renderEditButton = useCallback( + ( + workflowType: getWorkflowTemplateV1WorkflowTypeEnum, + templateName?: string + ) => { + return workflowTemplateDetailAction({ + projectID, + templateName: templateName, + workflowType + }); + }, + [projectID] + ); + + const tabItems = useMemo( + () => [ + { + value: 'workflow', + label: t('workflowTemplate.list.type.workflow'), + children: ( + + + + + + + + + + + ) + }, + { + value: 'data_export', + label: t('workflowTemplate.list.type.dataExport'), + children: ( + + + + + + + + + + + ) + } + ], + [ + t, + getUsernameListLoading, + getWorkflowTemplateLoading, + getExportTemplateLoading, + workflowReviewSteps, + workflowExecStep, + exportReviewSteps, + exportExecStep, + usernameList, + workflowTemplate, + exportTemplate + ] ); + return ( - + - - - - - - - - - - ); }; + export default WorkflowTemplateDetail; diff --git a/packages/sqle/src/page/WorkflowTemplate/components/StepCard/index.tsx b/packages/sqle/src/page/WorkflowTemplate/components/StepCard/index.tsx index 85c76706c8..54c5cbc09e 100644 --- a/packages/sqle/src/page/WorkflowTemplate/components/StepCard/index.tsx +++ b/packages/sqle/src/page/WorkflowTemplate/components/StepCard/index.tsx @@ -24,7 +24,7 @@ const StepCard: React.FC = (props) => {
    {props?.operator}
    ) : null} - {props?.active ? ( + {props?.active && props?.close ? ( void; clickReviewNode: (index: number) => void; usernameList: IUserTipResV1[]; + isDataExport?: boolean; } diff --git a/packages/sqle/src/page/WorkflowTemplate/components/StepCard/stepInfo.tsx b/packages/sqle/src/page/WorkflowTemplate/components/StepCard/stepInfo.tsx index 6e55ace1a9..135dd135e6 100644 --- a/packages/sqle/src/page/WorkflowTemplate/components/StepCard/stepInfo.tsx +++ b/packages/sqle/src/page/WorkflowTemplate/components/StepCard/stepInfo.tsx @@ -23,7 +23,8 @@ const renderReviewUser = ( type: 'review' | 'exec', stepItem: IWorkFlowStepTemplateResV1, userList: IUserTipResV1[], - theme: IStepInfoProps['theme'] + theme: IStepInfoProps['theme'], + isDataExport?: boolean ) => { if (stepItem.assignee_user_id_list?.length === 0) { if (stepItem.approved_by_authorized && type === 'review') { @@ -42,6 +43,22 @@ const renderReviewUser = ( ); } else if (stepItem.execute_by_authorized && type === 'exec') { + if (isDataExport) { + return ( + <> + + + {t( + 'workflowTemplate.progressConfig.exportExec.creatorAsExecutor' + )} + + + ); + } return ( <> - {t( - 'workflowTemplate.progressConfig.exec.executeUserType.matchExecute' - )} + {isDataExport + ? t( + 'workflowTemplate.progressConfig.exportExec.executeUserType.matchExecute' + ) + : t( + 'workflowTemplate.progressConfig.exec.executeUserType.matchExecute' + )} ); @@ -80,7 +101,7 @@ export const stepInfo = (props: IStepInfoProps): IStepInfoDataProps[] => { return [ { key: 'honour-step', - show: isUpdateMode, + show: isUpdateMode && !props.isDataExport, disabled: !(props?.currentStep === 0), icon: ( @@ -173,14 +194,17 @@ export const stepInfo = (props: IStepInfoProps): IStepInfoDataProps[] => { ), arrow: StepInfoArrowEnum.none, - title: t('workflowTemplate.progressConfig.exec.title'), + title: props.isDataExport + ? t('workflowTemplate.progressConfig.exportExec.title') + : t('workflowTemplate.progressConfig.exec.title'), desc: props.execStepData?.desc ?? '-', operatorTitle: t('workflowTemplate.form.label.execUser'), operator: renderReviewUser( 'exec', props?.execStepData, props.usernameList, - props.theme + props.theme, + props.isDataExport ) } ];