You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dolphinscheduler.apache.org by so...@apache.org on 2022/02/12 06:08:34 UTC

[dolphinscheduler] branch dev updated: [Feature][UI Next] Add Workflow Instance (#8356)

This is an automated email from the ASF dual-hosted git repository.

songjian pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/dolphinscheduler.git


The following commit(s) were added to refs/heads/dev by this push:
     new 80d2ee7  [Feature][UI Next] Add Workflow Instance (#8356)
80d2ee7 is described below

commit 80d2ee7b11a247d99d17ca6cee78d02200639f45
Author: Devosend <de...@gmail.com>
AuthorDate: Sat Feb 12 14:08:22 2022 +0800

    [Feature][UI Next] Add Workflow Instance (#8356)
    
    * add workflow list manage
    
    * support batch delete
    
    * add condition search
    
    * add interval to update data
    
    * fix table column I18n error
    
    * add icon for table state column
    
    * del redundant comment
    
    * fix delete data paging jump
---
 .../src/locales/modules/en_US.ts                   |  41 ++-
 .../src/locales/modules/zh_CN.ts                   |  41 ++-
 .../src/service/modules/executors/index.ts         |   2 +-
 .../src/service/modules/process-instances/index.ts |   6 +-
 .../src/service/modules/process-instances/types.ts |  25 +-
 dolphinscheduler-ui-next/src/utils/common.ts       | 268 +++++++++++++++
 .../workflow/instance/index.tsx => utils/types.ts} |  11 +-
 .../components/process-instance-condition.tsx      | 123 +++++++
 .../workflow/instance/components/table-action.tsx  | 296 ++++++++++++++++
 .../projects/workflow/instance/index.module.scss   |  96 ++++++
 .../src/views/projects/workflow/instance/index.tsx | 126 ++++++-
 .../workflow/instance/{index.tsx => types.ts}      |  23 +-
 .../views/projects/workflow/instance/use-table.ts  | 381 +++++++++++++++++++++
 13 files changed, 1415 insertions(+), 24 deletions(-)

diff --git a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts
index 06f7509..978d498 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 99d7b27..fe1bd9c 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 3cc9560..d735640 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 3cfff4c..bc403b9 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 488a142..e5a021d 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 6b8d1ef..68766c9 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/views/projects/workflow/instance/index.tsx b/dolphinscheduler-ui-next/src/utils/types.ts
similarity index 82%
copy from dolphinscheduler-ui-next/src/views/projects/workflow/instance/index.tsx
copy to dolphinscheduler-ui-next/src/utils/types.ts
index b79c035..194e1e5 100644
--- a/dolphinscheduler-ui-next/src/views/projects/workflow/instance/index.tsx
+++ b/dolphinscheduler-ui-next/src/utils/types.ts
@@ -15,11 +15,8 @@
  * limitations under the License.
  */
 
-import { defineComponent } from 'vue'
+interface ITaskState {
+  [key: string]: any
+}
 
-export default defineComponent({
-  name: 'WorkflowInstanceList',
-  setup() {
-    return () => <div>WorkflowInstanceList</div>
-  }
-})
+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 0000000..09a3b0f
--- /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 (
+      <NGrid xGap={6} cols={24}>
+        <NGridItem offset={5} span={3}>
+          <NInput
+            v-model:value={this.searchValRef}
+            placeholder={t('project.workflow.name')}
+          />
+        </NGridItem>
+        <NGridItem span={3}>
+          <NInput
+            v-model:value={this.executorNameRef}
+            placeholder={t('project.workflow.executor')}
+          />
+        </NGridItem>
+        <NGridItem span={3}>
+          <NInput
+            v-model:value={this.hostRef}
+            placeholder={t('project.workflow.host')}
+          />
+        </NGridItem>
+        <NGridItem span={3}>
+          <NSelect
+            options={options}
+            defaultValue={''}
+            v-model:value={this.stateTypeRef}
+          />
+        </NGridItem>
+        <NGridItem span={6}>
+          <NDatePicker
+            type='datetimerange'
+            clearable
+            v-model:value={this.startEndTimeRef}
+          />
+        </NGridItem>
+        <NGridItem span={1}>
+          <NButton type='primary' onClick={this.handleSearch}>
+            <NIcon>
+              <SearchOutlined />
+            </NIcon>
+          </NButton>
+        </NGridItem>
+      </NGrid>
+    )
+  }
+})
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 0000000..a480041
--- /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<IWorkflowInstance>,
+    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 (
+      <NSpace>
+        <NTooltip trigger={'hover'}>
+          {{
+            default: () => t('project.workflow.edit'),
+            trigger: () => (
+              <NButton
+                tag='div'
+                size='tiny'
+                type='info'
+                circle
+                disabled={
+                  (state !== 'SUCCESS' &&
+                    state !== 'PAUSE' &&
+                    state !== 'FAILURE' &&
+                    state !== 'STOP') ||
+                  this.row?.disabled
+                }
+                onClick={this.handleEdit}
+              >
+                <NIcon>
+                  <FormOutlined />
+                </NIcon>
+              </NButton>
+            )
+          }}
+        </NTooltip>
+        <NTooltip trigger={'hover'}>
+          {{
+            default: () => t('project.workflow.rerun'),
+            trigger: () => {
+              return (
+                <NButton
+                  tag='div'
+                  size='tiny'
+                  type='info'
+                  circle
+                  onClick={this.handleReRun}
+                  disabled={
+                    (state !== 'SUCCESS' &&
+                      state !== 'PAUSE' &&
+                      state !== 'FAILURE' &&
+                      state !== 'STOP') ||
+                    this.row?.disabled
+                  }
+                >
+                  {this.row?.buttonType === 'run' ? (
+                    <span>{this.row?.count}</span>
+                  ) : (
+                    <NIcon>
+                      <SyncOutlined />
+                    </NIcon>
+                  )}
+                </NButton>
+              )
+            }
+          }}
+        </NTooltip>
+        <NTooltip trigger={'hover'}>
+          {{
+            default: () => t('project.workflow.recovery_failed'),
+            trigger: () => (
+              <NButton
+                tag='div'
+                size='tiny'
+                type='primary'
+                circle
+                onClick={this.handleReStore}
+                disabled={state !== 'FAILURE' || this.row?.disabled}
+              >
+                {this.row?.buttonType === 'store' ? (
+                  <span>{this.row?.count}</span>
+                ) : (
+                  <NIcon>
+                    <CloseCircleOutlined />
+                  </NIcon>
+                )}
+              </NButton>
+            )
+          }}
+        </NTooltip>
+        <NTooltip trigger={'hover'}>
+          {{
+            default: () =>
+              state === 'PAUSE'
+                ? t('project.workflow.recovery_failed')
+                : t('project.workflow.stop'),
+            trigger: () => (
+              <NButton
+                tag='div'
+                size='tiny'
+                type='error'
+                circle
+                onClick={this.handleStop}
+                disabled={
+                  (state !== 'RUNNING_EXECUTION' && state !== 'PAUSE') ||
+                  this.row?.disabled
+                }
+              >
+                <NIcon>
+                  {state === 'STOP' ? (
+                    <PlayCircleOutlined />
+                  ) : (
+                    <CloseOutlined />
+                  )}
+                </NIcon>
+              </NButton>
+            )
+          }}
+        </NTooltip>
+        <NTooltip trigger={'hover'}>
+          {{
+            default: () =>
+              state === 'PAUSE'
+                ? t('project.workflow.recovery_suspend')
+                : t('project.workflow.pause'),
+            trigger: () => (
+              <NButton
+                tag='div'
+                size='tiny'
+                type='warning'
+                circle
+                disabled={
+                  (state !== 'RUNNING_EXECUTION' && state !== 'PAUSE') ||
+                  this.row?.disabled
+                }
+                onClick={this.handleSuspend}
+              >
+                <NIcon>
+                  {state === 'PAUSE' ? (
+                    <PlayCircleOutlined />
+                  ) : (
+                    <PauseCircleOutlined />
+                  )}
+                </NIcon>
+              </NButton>
+            )
+          }}
+        </NTooltip>
+        <NTooltip trigger={'hover'}>
+          {{
+            default: () => t('project.workflow.delete'),
+            trigger: () => (
+              <NButton
+                tag='div'
+                size='tiny'
+                type='error'
+                circle
+                disabled={
+                  (state !== 'SUCCESS' &&
+                    state !== 'FAILURE' &&
+                    state !== 'STOP' &&
+                    state !== 'PAUSE') ||
+                  this.row?.disabled
+                }
+              >
+                <NPopconfirm onPositiveClick={this.handleDeleteInstance}>
+                  {{
+                    default: () => t('project.workflow.delete_confirm'),
+                    icon: () => (
+                      <NIcon>
+                        <InfoCircleFilled />
+                      </NIcon>
+                    ),
+                    trigger: () => (
+                      <NIcon>
+                        <DeleteOutlined />
+                      </NIcon>
+                    )
+                  }}
+                </NPopconfirm>
+              </NButton>
+            )
+          }}
+        </NTooltip>
+        <NTooltip trigger={'hover'}>
+          {{
+            default: () => t('project.workflow.gantt'),
+            trigger: () => (
+              <NButton
+                tag='div'
+                size='tiny'
+                type='info'
+                circle
+                /* TODO: Goto gantt*/
+                disabled={this.row?.disabled}
+              >
+                <NIcon>
+                  <ControlOutlined />
+                </NIcon>
+              </NButton>
+            )
+          }}
+        </NTooltip>
+      </NSpace>
+    )
+  }
+})
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 0000000..7622b8b
--- /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 b79c035..f4ae876 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 () => <div>WorkflowInstanceList</div>
+    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 (
+      <div class={styles.content}>
+        <Card class={styles.card}>
+          <div class={styles.header}>
+            <ProcessInstanceCondition onHandleSearch={this.handleSearch} />
+          </div>
+        </Card>
+        <Card title={t('project.workflow.workflow_instance')}>
+          <NDataTable
+            rowKey={(row) => row.id}
+            columns={this.columns}
+            data={this.tableData}
+            striped
+            size={'small'}
+            class={styles.table}
+            scrollX={1800}
+            v-model:checked-row-keys={this.checkedRowKeys}
+          />
+          <div class={styles.pagination}>
+            <NPagination
+              v-model:page={this.page}
+              v-model:page-size={this.pageSize}
+              page-count={this.totalPage}
+              show-size-picker
+              page-sizes={[10, 30, 50]}
+              show-quick-jumper
+              onUpdatePage={this.requestData}
+              onUpdatePageSize={this.handleChangePageSize}
+            />
+          </div>
+          <NTooltip>
+            {{
+              default: () => t('project.workflow.delete'),
+              trigger: () => (
+                <NButton
+                  tag='div'
+                  type='primary'
+                  disabled={this.checkedRowKeys.length <= 0}
+                  style='position: absolute; bottom: 10px; left: 10px;'
+                >
+                  <NPopconfirm onPositiveClick={this.handleBatchDelete}>
+                    {{
+                      default: () => t('project.workflow.delete_confirm'),
+                      trigger: () => t('project.workflow.delete')
+                    }}
+                  </NPopconfirm>
+                </NButton>
+              )
+            }}
+          </NTooltip>
+        </Card>
+      </div>
+    )
   }
 })
diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/instance/index.tsx b/dolphinscheduler-ui-next/src/views/projects/workflow/instance/types.ts
similarity index 67%
copy from dolphinscheduler-ui-next/src/views/projects/workflow/instance/index.tsx
copy to dolphinscheduler-ui-next/src/views/projects/workflow/instance/types.ts
index b79c035..81894f9 100644
--- a/dolphinscheduler-ui-next/src/views/projects/workflow/instance/index.tsx
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/instance/types.ts
@@ -15,11 +15,20 @@
  * limitations under the License.
  */
 
-import { defineComponent } from 'vue'
+import { ExecuteReq } from '@/service/modules/executors/types'
 
-export default defineComponent({
-  name: 'WorkflowInstanceList',
-  setup() {
-    return () => <div>WorkflowInstanceList</div>
-  }
-})
+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 0000000..1581175
--- /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<RowKey>,
+    tableData: [] as Array<IWorkflowInstance>,
+    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
+  }
+}