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
+ }
+}