diff --git a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts index 06f7509396..978d49864c 100644 --- a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts +++ b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts @@ -371,6 +371,7 @@ const project = { workflow_publish_status: 'Workflow Publish Status', schedule_publish_status: 'Schedule Publish Status', workflow_definition: 'Workflow Definition', + workflow_instance: 'Workflow Instance', id: '#', status: 'Status', create_time: 'Create Time', @@ -431,7 +432,45 @@ const project = { delete_confirm: 'Delete?', enter_name_tips: 'Please enter name', confirm_switch_version: 'Confirm Switch To This Version?', - current_version: 'Current Version' + current_version: 'Current Version', + run_type: 'Run Type', + scheduling_time: 'Scheduling Time', + duration: 'Duration', + run_times: 'Run Times', + fault_tolerant_sign: 'Fault-tolerant Sign', + dry_run_flag: 'Dry-run Flag', + executor: 'Executor', + host: 'Host', + start_process: 'Start Process', + execute_from_the_current_node: 'Execute from the current node', + recover_tolerance_fault_process: 'Recover tolerance fault process', + resume_the_suspension_process: 'Resume the suspension process', + execute_from_the_failed_nodes: 'Execute from the failed nodes', + scheduling_execution: 'Scheduling execution', + rerun: 'Rerun', + stop: 'Stop', + pause: 'Pause', + recovery_waiting_thread: 'Recovery waiting thread', + recover_serial_wait: 'Recover serial wait', + recovery_suspend: 'Recovery Suspend', + recovery_failed: 'Recovery Failed', + gantt: 'Gantt', + name: 'Name', + all_status: 'AllStatus', + submit_success: 'Submitted successfully', + running: 'Running', + ready_to_pause: 'Ready to pause', + ready_to_stop: 'Ready to stop', + failed: 'Failed', + need_fault_tolerance: 'Need fault tolerance', + kill: 'Kill', + waiting_for_thread: 'Waiting for thread', + waiting_for_dependence: 'Waiting for dependence', + waiting_for_dependency_to_complete: 'Waiting for dependency to complete', + delay_execution: 'Delay execution', + forced_success: 'Forced success', + serial_wait: 'Serial wait', + executing: 'Executing' }, task: { task_name: 'Task Name', diff --git a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts index 99d7b27ca8..fe1bd9c23b 100644 --- a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts +++ b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts @@ -366,6 +366,7 @@ const project = { workflow_publish_status: '工作流上线状态', schedule_publish_status: '定时状态', workflow_definition: '工作流定义', + workflow_instance: '工作流实例', id: '编号', status: '状态', create_time: '创建时间', @@ -430,7 +431,45 @@ const project = { enter_name_tips: '请输入名称', switch_version: '切换到该版本', confirm_switch_version: '确定切换到该版本吗?', - current_version: '当前版本' + current_version: '当前版本', + run_type: '运行类型', + scheduling_time: '调度时间', + duration: '运行时长', + run_times: '运行次数', + fault_tolerant_sign: '容错标识', + dry_run_flag: '空跑标识', + executor: '执行用户', + host: 'Host', + start_process: '启动工作流', + execute_from_the_current_node: '从当前节点开始执行', + recover_tolerance_fault_process: '恢复被容错的工作流', + resume_the_suspension_process: '恢复运行流程', + execute_from_the_failed_nodes: '从失败节点开始执行', + scheduling_execution: '调度执行', + rerun: '重跑', + stop: '停止', + pause: '暂停', + recovery_waiting_thread: '恢复等待线程', + recover_serial_wait: '串行恢复', + recovery_suspend: '恢复运行', + recovery_failed: '恢复失败', + gantt: '甘特图', + name: '名称', + all_status: '全部状态', + submit_success: '提交成功', + running: '正在运行', + ready_to_pause: '准备暂停', + ready_to_stop: '准备停止', + failed: '失败', + need_fault_tolerance: '需要容错', + kill: 'Kill', + waiting_for_thread: '等待线程', + waiting_for_dependence: '等待依赖', + waiting_for_dependency_to_complete: '等待依赖完成', + delay_execution: '延时执行', + forced_success: '强制成功', + serial_wait: '串行等待', + executing: '正在执行' }, task: { task_name: '任务名称', diff --git a/dolphinscheduler-ui-next/src/service/modules/executors/index.ts b/dolphinscheduler-ui-next/src/service/modules/executors/index.ts index 3cc956040f..d7356407de 100644 --- a/dolphinscheduler-ui-next/src/service/modules/executors/index.ts +++ b/dolphinscheduler-ui-next/src/service/modules/executors/index.ts @@ -23,7 +23,7 @@ import { ProcessInstanceReq } from './types' -export function execute(data: ExecuteReq, code: ProjectCodeReq): any { +export function execute(data: ExecuteReq, code: number): any { return axios({ url: `/projects/${code}/executors/execute`, method: 'post', diff --git a/dolphinscheduler-ui-next/src/service/modules/process-instances/index.ts b/dolphinscheduler-ui-next/src/service/modules/process-instances/index.ts index 3cfff4c5e5..bc403b92f7 100644 --- a/dolphinscheduler-ui-next/src/service/modules/process-instances/index.ts +++ b/dolphinscheduler-ui-next/src/service/modules/process-instances/index.ts @@ -29,7 +29,7 @@ import { export function queryProcessInstanceListPaging( params: ProcessInstanceListReq, - code: CodeReq + code: number ): any { return axios({ url: `/projects/${code}/process-instances`, @@ -40,7 +40,7 @@ export function queryProcessInstanceListPaging( export function batchDeleteProcessInstanceByIds( data: BatchDeleteReq, - code: CodeReq + code: number ): any { return axios({ url: `/projects/${code}/process-instances/batch-delete`, @@ -101,7 +101,7 @@ export function updateProcessInstance( }) } -export function deleteProcessInstanceById(id: IdReq, code: CodeReq): any { +export function deleteProcessInstanceById(id: number, code: number): any { return axios({ url: `/projects/${code}/process-instances/${id}`, method: 'delete' diff --git a/dolphinscheduler-ui-next/src/service/modules/process-instances/types.ts b/dolphinscheduler-ui-next/src/service/modules/process-instances/types.ts index 488a142b88..e5a021d0df 100644 --- a/dolphinscheduler-ui-next/src/service/modules/process-instances/types.ts +++ b/dolphinscheduler-ui-next/src/service/modules/process-instances/types.ts @@ -34,7 +34,7 @@ interface ProcessInstanceListReq { interface BatchDeleteReq { processInstanceIds: string - projectName: string + projectName?: string alertGroup?: string createTime?: string email?: string @@ -82,6 +82,26 @@ interface ProcessInstanceReq { timeout?: string } +interface IWorkflowInstance { + id: number + name: string + state: string + commandType: string + scheduleTime?: string + processDefinitionCode?: number + startTime: string + endTime: string + duration?: string + runTimes: number + recovery: string + dryRun: number + executorName: string + host: string + count?: number + disabled?: boolean + buttonType?: string +} + export { CodeReq, ProcessInstanceListReq, @@ -90,5 +110,6 @@ export { TaskReq, LongestReq, IdReq, - ProcessInstanceReq + ProcessInstanceReq, + IWorkflowInstance } diff --git a/dolphinscheduler-ui-next/src/utils/common.ts b/dolphinscheduler-ui-next/src/utils/common.ts index 6b8d1ef684..68766c9407 100644 --- a/dolphinscheduler-ui-next/src/utils/common.ts +++ b/dolphinscheduler-ui-next/src/utils/common.ts @@ -15,6 +15,25 @@ * limitations under the License. */ +import { + SettingFilled, + SettingOutlined, + CloseCircleOutlined, + PauseCircleOutlined, + CheckCircleOutlined, + EditOutlined, + MinusCircleOutlined, + CheckCircleFilled, + LoadingOutlined, + PauseCircleFilled, + ClockCircleOutlined, + StopFilled, + StopOutlined, + GlobalOutlined, + IssuesCloseOutlined +} from '@vicons/antd' +import { ITaskState } from './types' + /** * Intelligent display kb m */ @@ -46,3 +65,252 @@ export const fileTypeArr = [ 'ini', 'js' ] + +/** + * Operation type + * @desc tooltip + * @code identifier + */ +export const runningType = (t: any) => [ + { + desc: `${t('project.workflow.start_process')}`, + code: 'START_PROCESS' + }, + { + desc: `${t('project.workflow.execute_from_the_current_node')}`, + code: 'START_CURRENT_TASK_PROCESS' + }, + { + desc: `${t('project.workflow.recover_tolerance_fault_process')}`, + code: 'RECOVER_TOLERANCE_FAULT_PROCESS' + }, + { + desc: `${t('project.workflow.resume_the_suspension_process')}`, + code: 'RECOVER_SUSPENDED_PROCESS' + }, + { + desc: `${t('project.workflow.execute_from_the_failed_nodes')}`, + code: 'START_FAILURE_TASK_PROCESS' + }, + { + desc: `${t('project.workflow.complement_data')}`, + code: 'COMPLEMENT_DATA' + }, + { + desc: `${t('project.workflow.scheduling_execution')}`, + code: 'SCHEDULER' + }, + { + desc: `${t('project.workflow.rerun')}`, + code: 'REPEAT_RUNNING' + }, + { + desc: `${t('project.workflow.pause')}`, + code: 'PAUSE' + }, + { + desc: `${t('project.workflow.stop')}`, + code: 'STOP' + }, + { + desc: `${t('project.workflow.recovery_waiting_thread')}`, + code: 'RECOVER_WAITING_THREAD' + }, + { + desc: `${t('project.workflow.recover_serial_wait')}`, + code: 'RECOVER_SERIAL_WAIT' + } +] + +/** + * State code table + */ +export const stateType = (t: any) => [ + { + value: '', + label: `${t('project.workflow.all_status')}` + }, + { + value: 'SUBMITTED_SUCCESS', + label: `${t('project.workflow.submit_success')}` + }, + { + value: 'RUNNING_EXECUTION', + label: `${t('project.workflow.running')}` + }, + { + value: 'READY_PAUSE', + label: `${t('project.workflow.ready_to_pause')}` + }, + { + value: 'PAUSE', + label: `${t('project.workflow.pause')}` + }, + { + value: 'READY_STOP', + label: `${t('project.workflow.ready_to_stop')}` + }, + { + value: 'STOP', + label: `${t('project.workflow.stop')}` + }, + { + value: 'FAILURE', + label: `${t('project.workflow.failed')}` + }, + { + value: 'SUCCESS', + label: `${t('project.workflow.success')}` + }, + { + value: 'NEED_FAULT_TOLERANCE', + label: `${t('project.workflow.need_fault_tolerance')}` + }, + { + value: 'KILL', + label: `${t('project.workflow.kill')}` + }, + { + value: 'WAITING_THREAD', + label: `${t('project.workflow.waiting_for_thread')}` + }, + { + value: 'WAITING_DEPEND', + label: `${t('project.workflow.waiting_for_dependency_to_complete')}` + }, + { + value: 'DELAY_EXECUTION', + label: `${t('project.workflow.delay_execution')}` + }, + { + value: 'FORCED_SUCCESS', + label: `${t('project.workflow.forced_success')}` + }, + { + value: 'SERIAL_WAIT', + label: `${t('project.workflow.serial_wait')}` + } +] + +/** + * Task status + * @id id + * @desc tooltip + * @color color + * @icon icon + * @isSpin is loading (Need to execute the code block to write if judgment) + */ +// TODO: Looking for a more suitable icon +export const tasksState = (t: any): ITaskState => ({ + SUBMITTED_SUCCESS: { + id: 0, + desc: `${t('project.workflow.submit_success')}`, + color: '#A9A9A9', + icon: IssuesCloseOutlined, + isSpin: false, + classNames: 'submitted' + }, + RUNNING_EXECUTION: { + id: 1, + desc: `${t('project.workflow.executing')}`, + color: '#0097e0', + icon: SettingFilled, + isSpin: true, + classNames: 'executing' + }, + READY_PAUSE: { + id: 2, + desc: `${t('project.workflow.ready_to_pause')}`, + color: '#07b1a3', + icon: SettingOutlined, + isSpin: false, + classNames: 'submitted' + }, + PAUSE: { + id: 3, + desc: `${t('project.workflow.pause')}`, + color: '#057c72', + icon: PauseCircleOutlined, + isSpin: false, + classNames: 'pause' + }, + READY_STOP: { + id: 4, + desc: `${t('project.workflow.ready_to_stop')}`, + color: '#FE0402', + icon: StopFilled, + isSpin: false + }, + STOP: { + id: 5, + desc: `${t('project.workflow.stop')}`, + color: '#e90101', + icon: StopOutlined, + isSpin: false + }, + FAILURE: { + id: 6, + desc: `${t('project.workflow.failed')}`, + color: '#000000', + icon: CloseCircleOutlined, + isSpin: false, + classNames: 'failed' + }, + SUCCESS: { + id: 7, + desc: `${t('project.workflow.success')}`, + color: '#33cc00', + icon: CheckCircleOutlined, + isSpin: false, + classNames: 'success' + }, + NEED_FAULT_TOLERANCE: { + id: 8, + desc: `${t('project.workflow.need_fault_tolerance')}`, + color: '#FF8C00', + icon: EditOutlined, + isSpin: false + }, + KILL: { + id: 9, + desc: `${t('project.workflow.kill')}`, + color: '#a70202', + icon: MinusCircleOutlined, + isSpin: false + }, + WAITING_THREAD: { + id: 10, + desc: `${t('project.workflow.waiting_for_thread')}`, + color: '#912eed', + icon: ClockCircleOutlined, + isSpin: false + }, + WAITING_DEPEND: { + id: 11, + desc: `${t('project.workflow.waiting_for_dependence')}`, + color: '#5101be', + icon: GlobalOutlined, + isSpin: false + }, + DELAY_EXECUTION: { + id: 12, + desc: `${t('project.workflow.delay_execution')}`, + color: '#5102ce', + icon: PauseCircleFilled, + isSpin: false + }, + FORCED_SUCCESS: { + id: 13, + desc: `${t('project.workflow.forced_success')}`, + color: '#5102ce', + icon: CheckCircleFilled, + isSpin: false + }, + SERIAL_WAIT: { + id: 14, + desc: `${t('project.workflow.serial_wait')}`, + color: '#5102ce', + icon: LoadingOutlined, + isSpin: false + } +}) diff --git a/dolphinscheduler-ui-next/src/utils/types.ts b/dolphinscheduler-ui-next/src/utils/types.ts new file mode 100644 index 0000000000..194e1e5764 --- /dev/null +++ b/dolphinscheduler-ui-next/src/utils/types.ts @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +interface ITaskState { + [key: string]: any +} + +export { ITaskState } diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/instance/components/process-instance-condition.tsx b/dolphinscheduler-ui-next/src/views/projects/workflow/instance/components/process-instance-condition.tsx new file mode 100644 index 0000000000..09a3b0f53e --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/instance/components/process-instance-condition.tsx @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { SearchOutlined } from '@vicons/antd' +import { + NGrid, + NGridItem, + NInput, + NButton, + NDatePicker, + NSelect, + NIcon +} from 'naive-ui' +import { defineComponent, ref } from 'vue' +import { useI18n } from 'vue-i18n' +import { format } from 'date-fns' +import { stateType } from '@/utils/common' + +export default defineComponent({ + name: 'ProcessInstanceCondition', + emits: ['handleSearch'], + setup(props, ctx) { + const searchValRef = ref('') + const executorNameRef = ref('') + const hostRef = ref('') + const stateTypeRef = ref('') + const startEndTimeRef = ref() + + const handleSearch = () => { + let startDate = '' + let endDate = '' + if (startEndTimeRef.value) { + startDate = format( + new Date(startEndTimeRef.value[0]), + 'yyyy-MM-dd hh:mm:ss' + ) + endDate = format( + new Date(startEndTimeRef.value[1]), + 'yyyy-MM-dd hh:mm:ss' + ) + } + + ctx.emit('handleSearch', { + searchVal: searchValRef.value, + executorName: executorNameRef.value, + host: hostRef.value, + stateType: stateTypeRef.value, + startDate, + endDate + }) + } + + return { + searchValRef, + executorNameRef, + hostRef, + stateTypeRef, + startEndTimeRef, + handleSearch + } + }, + render() { + const { t } = useI18n() + const options = stateType(t) + return ( + + + + + + + + + + + + + + + + + + + + + + + + + ) + } +}) diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/instance/components/table-action.tsx b/dolphinscheduler-ui-next/src/views/projects/workflow/instance/components/table-action.tsx new file mode 100644 index 0000000000..a4800418f2 --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/instance/components/table-action.tsx @@ -0,0 +1,296 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { defineComponent, PropType, toRefs } from 'vue' +import { NSpace, NTooltip, NButton, NIcon, NPopconfirm } from 'naive-ui' +import { + DeleteOutlined, + FormOutlined, + InfoCircleFilled, + SyncOutlined, + CloseOutlined, + CloseCircleOutlined, + PauseCircleOutlined, + ControlOutlined, + PlayCircleOutlined +} from '@vicons/antd' +import { useI18n } from 'vue-i18n' +import { useRouter } from 'vue-router' +import type { Router } from 'vue-router' +import { IWorkflowInstance } from '@/service/modules/process-instances/types' + +const props = { + row: { + type: Object as PropType, + required: true + } +} + +export default defineComponent({ + name: 'TableAction', + props, + emits: [ + 'updateList', + 'reRun', + 'reStore', + 'stop', + 'suspend', + 'deleteInstance' + ], + setup(props, ctx) { + const router: Router = useRouter() + + const handleEdit = () => { + router.push({ + name: 'workflow-instance-detail', + params: { id: props.row!.id }, + query: { code: props.row!.processDefinitionCode } + }) + } + + const handleReRun = () => { + ctx.emit('reRun') + } + + const handleReStore = () => { + ctx.emit('reStore') + } + + const handleStop = () => { + ctx.emit('stop') + } + + const handleSuspend = () => { + ctx.emit('suspend') + } + + const handleDeleteInstance = () => { + ctx.emit('deleteInstance') + } + + return { + handleEdit, + handleReRun, + handleReStore, + handleStop, + handleSuspend, + handleDeleteInstance, + ...toRefs(props) + } + }, + render() { + const { t } = useI18n() + const state = this.row?.state + + return ( + + + {{ + default: () => t('project.workflow.edit'), + trigger: () => ( + + + + + + ) + }} + + + {{ + default: () => t('project.workflow.rerun'), + trigger: () => { + return ( + + {this.row?.buttonType === 'run' ? ( + {this.row?.count} + ) : ( + + + + )} + + ) + } + }} + + + {{ + default: () => t('project.workflow.recovery_failed'), + trigger: () => ( + + {this.row?.buttonType === 'store' ? ( + {this.row?.count} + ) : ( + + + + )} + + ) + }} + + + {{ + default: () => + state === 'PAUSE' + ? t('project.workflow.recovery_failed') + : t('project.workflow.stop'), + trigger: () => ( + + + {state === 'STOP' ? ( + + ) : ( + + )} + + + ) + }} + + + {{ + default: () => + state === 'PAUSE' + ? t('project.workflow.recovery_suspend') + : t('project.workflow.pause'), + trigger: () => ( + + + {state === 'PAUSE' ? ( + + ) : ( + + )} + + + ) + }} + + + {{ + default: () => t('project.workflow.delete'), + trigger: () => ( + + + {{ + default: () => t('project.workflow.delete_confirm'), + icon: () => ( + + + + ), + trigger: () => ( + + + + ) + }} + + + ) + }} + + + {{ + default: () => t('project.workflow.gantt'), + trigger: () => ( + + + + + + ) + }} + + + ) + } +}) diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/instance/index.module.scss b/dolphinscheduler-ui-next/src/views/projects/workflow/instance/index.module.scss new file mode 100644 index 0000000000..7622b8b36c --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/instance/index.module.scss @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + .content { + width: 100%; + + .card { + margin-bottom: 8px; + } + + .header { + display: flex; + justify-content: space-between; + align-items: center; + margin: 10px 0; + .right { + > .search { + .list { + float: right; + margin: 3px 0 3px 4px; + } + } + } + } +} + +.table { + table { + width: 100%; + tr { + height: 40px; + font-size: 12px; + th, + td { + &:nth-child(1) { + width: 50px; + text-align: center; + } + } + th { + &:nth-child(1) { + width: 60px; + text-align: center; + } + > span { + font-size: 12px; + color: #555; + } + } + } + } +} + +.pagination { + display: flex; + justify-content: center; + align-items: center; + margin-top: 20px; +} + +.operation { + > div { + > div { + margin-right: 5px !important; + } + } +} + +.startup { + align-items: center; + > div:first-child { + width: 86%; + } +} + +.links { + color: #2080f0; + text-decoration: none; + &:hover { + text-decoration: underline; + } +} diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/instance/index.tsx b/dolphinscheduler-ui-next/src/views/projects/workflow/instance/index.tsx index b79c035860..f4ae876274 100644 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/instance/index.tsx +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/instance/index.tsx @@ -15,11 +15,133 @@ * limitations under the License. */ -import { defineComponent } from 'vue' +import { defineComponent, onMounted, onUnmounted, toRefs, watch } from 'vue' +import { useI18n } from 'vue-i18n' +import Card from '@/components/card' +import { + NButton, + NDataTable, + NPagination, + NPopconfirm, + NTooltip +} from 'naive-ui' +import { useTable } from './use-table' +import ProcessInstanceCondition from './components/process-instance-condition' +import { IWorkflowInstanceSearch } from './types' +import styles from './index.module.scss' export default defineComponent({ name: 'WorkflowInstanceList', setup() { - return () =>
WorkflowInstanceList
+ let setIntervalP: number + const { variables, createColumns, getTableData, batchDeleteInstance } = + useTable() + + const requestData = () => { + getTableData() + } + + const handleSearch = (params: IWorkflowInstanceSearch) => { + variables.searchVal = params.searchVal + variables.executorName = params.executorName + variables.host = params.host + variables.stateType = params.stateType + variables.startDate = params.startDate + variables.endDate = params.endDate + variables.page = 1 + requestData() + } + + const handleChangePageSize = () => { + variables.page = 1 + requestData() + } + + const handleBatchDelete = () => { + batchDeleteInstance() + } + + onMounted(() => { + createColumns(variables) + requestData() + + // Update timing list data + setIntervalP = setInterval(() => { + requestData() + }, 9000) + }) + + watch(useI18n().locale, () => { + createColumns(variables) + }) + + onUnmounted(() => { + clearInterval(setIntervalP) + }) + + return { + requestData, + handleSearch, + handleChangePageSize, + handleBatchDelete, + ...toRefs(variables) + } + }, + render() { + const { t } = useI18n() + + return ( +
+ +
+ +
+
+ + row.id} + columns={this.columns} + data={this.tableData} + striped + size={'small'} + class={styles.table} + scrollX={1800} + v-model:checked-row-keys={this.checkedRowKeys} + /> +
+ +
+ + {{ + default: () => t('project.workflow.delete'), + trigger: () => ( + + + {{ + default: () => t('project.workflow.delete_confirm'), + trigger: () => t('project.workflow.delete') + }} + + + ) + }} + +
+
+ ) } }) diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/instance/types.ts b/dolphinscheduler-ui-next/src/views/projects/workflow/instance/types.ts new file mode 100644 index 0000000000..81894f92cf --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/instance/types.ts @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ExecuteReq } from '@/service/modules/executors/types' + +interface ICountDownParam extends ExecuteReq { + index: number + buttonType: 'run' | 'store' | 'suspend' +} + +interface IWorkflowInstanceSearch { + searchVal: string + executorName: string + host: string + stateType: string + startDate: string + endDate: string +} + +export { ICountDownParam, IWorkflowInstanceSearch } diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/instance/use-table.ts b/dolphinscheduler-ui-next/src/views/projects/workflow/instance/use-table.ts new file mode 100644 index 0000000000..158117583f --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/instance/use-table.ts @@ -0,0 +1,381 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import _ from 'lodash' +import { format } from 'date-fns' +import { reactive, h, ref } from 'vue' +import { useI18n } from 'vue-i18n' +import { useRouter } from 'vue-router' +import type { Router } from 'vue-router' +import { NTooltip, NIcon, NSpin } from 'naive-ui' +import { RowKey } from 'naive-ui/lib/data-table/src/interface' +import { + queryProcessInstanceListPaging, + deleteProcessInstanceById, + batchDeleteProcessInstanceByIds +} from '@/service/modules/process-instances' +import { execute } from '@/service/modules/executors' +import TableAction from './components/table-action' +import { runningType, tasksState } from '@/utils/common' +import { IWorkflowInstance } from '@/service/modules/process-instances/types' +import { ICountDownParam } from './types' +import { ExecuteReq } from '@/service/modules/executors/types' +import styles from './index.module.scss' + +export function useTable() { + const { t } = useI18n() + const router: Router = useRouter() + + const taskStateIcon = tasksState(t) + + const variables = reactive({ + columns: [], + checkedRowKeys: [] as Array, + tableData: [] as Array, + page: ref(1), + pageSize: ref(10), + totalPage: ref(1), + searchVal: ref(), + executorName: ref(), + host: ref(), + stateType: ref(), + startDate: ref(), + endDate: ref(), + projectCode: ref(Number(router.currentRoute.value.params.projectCode)) + }) + + const createColumns = (variables: any) => { + variables.columns = [ + { + type: 'selection' + }, + { + title: t('project.workflow.id'), + key: 'id', + width: 50 + }, + { + title: t('project.workflow.workflow_name'), + key: 'name', + width: 200, + render: (_row: IWorkflowInstance) => + h( + 'a', + { + href: 'javascript:', + class: styles.links, + onClick: () => + router.push({ + name: 'workflow-instance-detail', + params: { id: _row.id }, + query: { code: _row.processDefinitionCode } + }) + }, + { + default: () => { + return _row.name + } + } + ) + }, + { + title: t('project.workflow.status'), + key: 'state', + render: (_row: IWorkflowInstance) => { + const stateIcon = taskStateIcon[_row.state] + const iconElement = h( + NIcon, + { + size: '18px', + style: 'position: relative; top: 7.5px; left: 7.5px' + }, + { + default: () => + h(stateIcon.icon, { + color: stateIcon.color + }) + } + ) + return h( + NTooltip, + {}, + { + trigger: () => { + if (stateIcon.isSpin) { + return h( + NSpin, + { + small: 'small' + }, + { + icon: () => iconElement + } + ) + } else { + return iconElement + } + }, + default: () => stateIcon!.desc + } + ) + } + }, + { + title: t('project.workflow.run_type'), + key: 'commandType', + render: (_row: IWorkflowInstance) => + ( + _.filter(runningType(t), (v) => v.code === _row.commandType)[0] || + {} + ).desc + }, + { + title: t('project.workflow.scheduling_time'), + key: 'scheduleTime', + render: (_row: IWorkflowInstance) => + _row.scheduleTime + ? format(new Date(_row.scheduleTime), 'yyyy-MM-dd HH:mm:ss') + : '-' + }, + { + title: t('project.workflow.start_time'), + key: 'startTime', + render: (_row: IWorkflowInstance) => + _row.startTime + ? format(new Date(_row.startTime), 'yyyy-MM-dd HH:mm:ss') + : '-' + }, + { + title: t('project.workflow.end_time'), + key: 'endTime', + render: (_row: IWorkflowInstance) => + _row.endTime + ? format(new Date(_row.endTime), 'yyyy-MM-dd HH:mm:ss') + : '-' + }, + { + title: t('project.workflow.duration'), + key: 'duration', + render: (_row: IWorkflowInstance) => _row.duration || '-' + }, + { + title: t('project.workflow.run_times'), + key: 'runTimes' + }, + { + title: t('project.workflow.fault_tolerant_sign'), + key: 'recovery' + }, + { + title: t('project.workflow.dry_run_flag'), + key: 'dryRun', + render: (_row: IWorkflowInstance) => (_row.dryRun === 1 ? 'YES' : 'NO') + }, + { + title: t('project.workflow.executor'), + key: 'executorName' + }, + { + title: t('project.workflow.host'), + key: 'host' + }, + { + title: t('project.workflow.operation'), + key: 'operation', + width: 220, + fixed: 'right', + className: styles.operation, + render: (_row: IWorkflowInstance, index: number) => + h(TableAction, { + row: _row, + onReRun: () => + _countDownFn({ + index, + processInstanceId: _row.id, + executeType: 'REPEAT_RUNNING', + buttonType: 'run' + }), + onReStore: () => + _countDownFn({ + index, + processInstanceId: _row.id, + executeType: 'START_FAILURE_TASK_PROCESS', + buttonType: 'store' + }), + onStop: () => { + if (_row.state === 'STOP') { + _countDownFn({ + index, + processInstanceId: _row.id, + executeType: 'RECOVER_SUSPENDED_PROCESS', + buttonType: 'suspend' + }) + } else { + _upExecutorsState({ + processInstanceId: _row.id, + executeType: 'STOP' + }) + } + }, + onSuspend: () => { + if (_row.state === 'PAUSE') { + _countDownFn({ + index, + processInstanceId: _row.id, + executeType: 'RECOVER_SUSPENDED_PROCESS', + buttonType: 'suspend' + }) + } else { + _upExecutorsState({ + processInstanceId: _row.id, + executeType: 'PAUSE' + }) + } + }, + onDeleteInstance: () => deleteInstance(_row.id) + }) + } + ] + } + + const getTableData = () => { + const params = { + pageNo: variables.page, + pageSize: variables.pageSize, + searchVal: variables.searchVal, + executorName: variables.executorName, + host: variables.host, + stateType: variables.stateType, + startDate: variables.startDate, + endDate: variables.endDate + } + queryProcessInstanceListPaging({ ...params }, variables.projectCode).then( + (res: any) => { + variables.totalPage = res.totalPage + variables.tableData = res.totalList.map((item: any) => { + return { ...item } + }) + } + ) + } + + const deleteInstance = (id: number) => { + deleteProcessInstanceById(id, variables.projectCode) + .then(() => { + window.$message.success(t('project.workflow.success')) + if (variables.tableData.length === 1 && variables.page > 1) { + variables.page -= 1 + } + + getTableData() + }) + .catch((error: any) => { + window.$message.error(error.message || '') + getTableData() + }) + } + + const batchDeleteInstance = () => { + const data = { + processInstanceIds: _.join(variables.checkedRowKeys, ',') + } + + batchDeleteProcessInstanceByIds(data, variables.projectCode) + .then(() => { + window.$message.success(t('project.workflow.success')) + + if ( + variables.tableData.length === variables.checkedRowKeys.length && + variables.page > 1 + ) { + variables.page -= 1 + } + + variables.checkedRowKeys = [] + getTableData() + }) + .catch((error: any) => { + window.$message.error(error.message || '') + getTableData() + }) + } + + /** + * operating + */ + const _upExecutorsState = (param: ExecuteReq) => { + execute(param, variables.projectCode) + .then(() => { + window.$message.success(t('project.workflow.success')) + + getTableData() + }) + .catch((error: any) => { + window.$message.error(error.message || '') + getTableData() + }) + } + + /** + * Countdown + */ + const _countDown = (fn: any, index: number) => { + const TIME_COUNT = 10 + let timer: number | undefined + let $count: number + if (!timer) { + $count = TIME_COUNT + timer = setInterval(() => { + if ($count > 0 && $count <= TIME_COUNT) { + $count-- + variables.tableData[index].count = $count + } else { + fn() + clearInterval(timer) + timer = undefined + } + }, 1000) + } + } + + /** + * Countdown method refresh + */ + const _countDownFn = (param: ICountDownParam) => { + const { index } = param + variables.tableData[index].buttonType = param.buttonType + execute(param, variables.projectCode) + .then(() => { + variables.tableData[index].disabled = true + window.$message.success(t('project.workflow.success')) + _countDown(() => { + getTableData() + }, index) + }) + .catch((error: any) => { + window.$message.error(error.message) + getTableData() + }) + } + + return { + variables, + createColumns, + getTableData, + batchDeleteInstance + } +}