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/03/16 10:01:58 UTC

[dolphinscheduler] branch dev updated: [Fix][UI Next][V1.0.0-Alpha] Support workflow batch operation (#8930)

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 dde81eb  [Fix][UI Next][V1.0.0-Alpha] Support workflow batch operation (#8930)
dde81eb is described below

commit dde81eb93c88042f415cb5f6e0f55c75d6ef95fb
Author: Devosend <de...@gmail.com>
AuthorDate: Wed Mar 16 17:59:20 2022 +0800

    [Fix][UI Next][V1.0.0-Alpha] Support workflow batch operation (#8930)
    
    * workflow support batch delete
    
    * workflow support batch export
    
    * support batch copy
    
    * modify batch button styles
    
    * add blank after scss
---
 .../src/locales/modules/en_US.ts                   |   7 +-
 .../src/locales/modules/zh_CN.ts                   |   7 +-
 .../service/modules/process-definition/index.ts    |   2 +-
 .../workflow/definition/components/copy-modal.tsx  | 115 +++++++++++++++++++++
 .../workflow/definition/components/start-modal.tsx |   7 +-
 .../workflow/definition/components/use-form.ts     |  23 ++++-
 .../workflow/definition/components/use-modal.ts    |  23 +++++
 .../projects/workflow/definition/index.module.scss |   9 ++
 .../views/projects/workflow/definition/index.tsx   |  90 +++++++++++++++-
 .../projects/workflow/definition/use-table.ts      |  57 +++++++++-
 10 files changed, 329 insertions(+), 11 deletions(-)

diff --git a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts
index 7f35f22..1fb7cef 100644
--- a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts
+++ b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts
@@ -406,6 +406,7 @@ const project = {
     tree_view: 'Tree View',
     tree_limit: 'Limit Size',
     export: 'Export',
+    batch_copy: 'Batch Copy',
     version_info: 'Version Info',
     version: 'Version',
     file_upload: 'File Upload',
@@ -509,7 +510,11 @@ const project = {
     cancel_full_screen: 'Cancel full screen',
     task_state: 'Task status',
     mode_of_dependent: 'Mode of dependent',
-    open: 'Open'
+    open: 'Open',
+    project_name_required: 'Project name is required',
+    related_items: 'Related items',
+    project_name: 'Project Name',
+    project_tips: 'Please select project name'
   },
   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 cf69cfe..efbd217 100644
--- a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts
+++ b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts
@@ -403,6 +403,7 @@ const project = {
     tree_view: '工作流树形图',
     tree_limit: '限制大小',
     export: '导出',
+    batch_copy: '批量复制',
     version_info: '版本信息',
     version: '版本',
     file_upload: '文件上传',
@@ -506,7 +507,11 @@ const project = {
     cancel_full_screen: '取消全屏',
     task_state: '任务状态',
     mode_of_dependent: '依赖模式',
-    open: '打开'
+    open: '打开',
+    project_name_required: '项目名称必填',
+    related_items: '关联项目',
+    project_name: '项目名称',
+    project_tips: '请选择项目'
   },
   task: {
     task_name: '任务名称',
diff --git a/dolphinscheduler-ui-next/src/service/modules/process-definition/index.ts b/dolphinscheduler-ui-next/src/service/modules/process-definition/index.ts
index 477a61c..b866f51 100644
--- a/dolphinscheduler-ui-next/src/service/modules/process-definition/index.ts
+++ b/dolphinscheduler-ui-next/src/service/modules/process-definition/index.ts
@@ -65,7 +65,7 @@ export function batchCopyByCodes(
   })
 }
 
-export function batchDeleteByCodes(data: CodesReq, code: CodeReq): any {
+export function batchDeleteByCodes(data: CodesReq, code: number): any {
   return axios({
     url: `/projects/${code}/process-definition/batch-delete`,
     method: 'post',
diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/copy-modal.tsx b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/copy-modal.tsx
new file mode 100644
index 0000000..487175f
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/copy-modal.tsx
@@ -0,0 +1,115 @@
+/*
+ * 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,
+  onMounted,
+  ref,
+  computed
+} from 'vue'
+import { useI18n } from 'vue-i18n'
+import Modal from '@/components/modal'
+import { useForm } from './use-form'
+import { useModal } from './use-modal'
+import { NForm, NFormItem, NSelect } from 'naive-ui'
+import { ProjectList } from '@/service/modules/projects/types'
+import { queryProjectCreatedAndAuthorizedByUser } from '@/service/modules/projects'
+
+const props = {
+  show: {
+    type: Boolean as PropType<boolean>,
+    default: false
+  },
+  codes: {
+    type: Array as PropType<Array<string>>,
+    default: []
+  }
+}
+
+export default defineComponent({
+  name: 'workflowDefinitionCopy',
+  props,
+  emits: ['update:show', 'update:row', 'updateList'],
+  setup(props, ctx) {
+    const { copyState } = useForm()
+    const { handleBatchCopyDefinition } = useModal(copyState, ctx)
+    const hideModal = () => {
+      ctx.emit('update:show')
+    }
+
+    const handleCopy = () => {
+      handleBatchCopyDefinition(props.codes)
+    }
+
+    const projects = ref<ProjectList[]>([])
+    const projectOptions = computed(() => {
+      return projects.value.map((t) => ({
+        label: t.name,
+        value: t.code
+      }))
+    })
+
+    onMounted(() => {
+      queryProjectCreatedAndAuthorizedByUser().then(
+        (res: Array<ProjectList>) => {
+          projects.value = res
+        }
+      )
+    })
+
+    return {
+      hideModal,
+      handleCopy,
+      projectOptions,
+      ...toRefs(copyState)
+    }
+  },
+
+  render() {
+    const { t } = useI18n()
+
+    return (
+      <Modal
+        show={this.$props.show}
+        title={t('project.workflow.related_items')}
+        onCancel={this.hideModal}
+        onConfirm={this.handleCopy}
+        confirmLoading={this.saving}
+      >
+        <NForm
+          rules={this.copyRules}
+          ref='copyFormRef'
+          label-placement='left'
+          label-width='160'
+        >
+          <NFormItem
+            label={t('project.workflow.project_name')}
+            path='projectCode'
+          >
+            <NSelect
+              options={this.projectOptions}
+              v-model:value={this.copyForm.projectCode}
+              placeholder={t('project.workflow.project_tips')}
+            />
+          </NFormItem>
+        </NForm>
+      </Modal>
+    )
+  }
+})
diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/start-modal.tsx b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/start-modal.tsx
index 832b0d8..4201fe7 100644
--- a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/start-modal.tsx
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/start-modal.tsx
@@ -398,7 +398,12 @@ export default defineComponent({
                         <DeleteOutlined />
                       </NIcon>
                     </NButton>
-                    <NButton text type='primary' onClick={this.addStartParams} class='btn-create-custom-parameter'>
+                    <NButton
+                      text
+                      type='primary'
+                      onClick={this.addStartParams}
+                      class='btn-create-custom-parameter'
+                    >
                       <NIcon>
                         <PlusCircleOutlined />
                       </NIcon>
diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/use-form.ts b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/use-form.ts
index eacbf17..0d57261 100644
--- a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/use-form.ts
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/use-form.ts
@@ -88,9 +88,30 @@ export const useForm = () => {
     },
     saving: false
   })
+
+  const copyState = reactive({
+    copyFormRef: ref(),
+    copyForm: {
+      projectCode: null
+    },
+    saving: false,
+    copyRules: {
+      projectCode: {
+        required: true,
+        trigger: ['input', 'blur'],
+        validator() {
+          if (copyState.copyForm.projectCode === '') {
+            return new Error(t('project.workflow.project_name_required'))
+          }
+        }
+      }
+    } as FormRules
+  })
+
   return {
     importState,
     startState,
-    timingState
+    timingState,
+    copyState
   }
 }
diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/use-modal.ts b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/use-modal.ts
index e486b16..2ef763e 100644
--- a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/use-modal.ts
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/use-modal.ts
@@ -22,6 +22,7 @@ import { useRoute, useRouter } from 'vue-router'
 import type { Router } from 'vue-router'
 import { format } from 'date-fns'
 import {
+  batchCopyByCodes,
   importProcessDefinition,
   queryProcessDefinitionByCode
 } from '@/service/modules/process-definition'
@@ -155,6 +156,27 @@ export function useModal(
     }
   }
 
+  const handleBatchCopyDefinition = async (codes: Array<string>) => {
+    await state.copyFormRef.validate()
+
+    if (state.saving) return
+    state.saving = true
+    try {
+      const data = {
+        codes: _.join(codes, ','),
+        targetProjectCode: state.copyForm.projectCode
+      }
+      await batchCopyByCodes(data, variables.projectCode)
+      window.$message.success(t('project.workflow.success'))
+      state.saving = false
+      ctx.emit('updateList')
+      ctx.emit('update:show')
+      state.copyForm.projectCode = ''
+    } catch (err) {
+      state.saving = false
+    }
+  }
+
   const getTimingData = () => {
     const start = format(
       parseTime(state.timingForm.startEndTime[0]),
@@ -253,6 +275,7 @@ export function useModal(
     handleStartDefinition,
     handleCreateTiming,
     handleUpdateTiming,
+    handleBatchCopyDefinition,
     getWorkerGroups,
     getAlertGroups,
     getEnvironmentList,
diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/index.module.scss b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/index.module.scss
index 7eac171..72e5e2f 100644
--- a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/index.module.scss
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/index.module.scss
@@ -86,3 +86,12 @@
     width: 86%;
   }
 }
+
+.batch-button {
+  position: absolute;
+  bottom: 10px;
+  left: 10px;
+  > div {
+    margin-right: 5px;
+  }
+}
diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/index.tsx b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/index.tsx
index 5aa6d50..c16f862 100644
--- a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/index.tsx
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/index.tsx
@@ -23,7 +23,9 @@ import {
   NIcon,
   NInput,
   NPagination,
-  NSpace
+  NSpace,
+  NTooltip,
+  NPopconfirm
 } from 'naive-ui'
 import { defineComponent, onMounted, toRefs, watch } from 'vue'
 import { useI18n } from 'vue-i18n'
@@ -32,6 +34,7 @@ import ImportModal from './components/import-modal'
 import StartModal from './components/start-modal'
 import TimingModal from './components/timing-modal'
 import VersionModal from './components/version-modal'
+import CopyModal from './components/copy-modal'
 import { useRouter, useRoute } from 'vue-router'
 import type { Router } from 'vue-router'
 import styles from './index.module.scss'
@@ -43,7 +46,14 @@ export default defineComponent({
     const route = useRoute()
     const projectCode = Number(route.params.projectCode)
 
-    const { variables, createColumns, getTableData } = useTable()
+    const {
+      variables,
+      createColumns,
+      getTableData,
+      batchDeleteWorkflow,
+      batchExportWorkflow,
+      batchCopyWorkflow
+    } = useTable()
 
     const requestData = () => {
       getTableData({
@@ -57,6 +67,11 @@ export default defineComponent({
       requestData()
     }
 
+    const handleCopyUpdateList = () => {
+      variables.checkedRowKeys = []
+      requestData()
+    }
+
     const handleSearch = () => {
       variables.page = 1
       requestData()
@@ -88,6 +103,10 @@ export default defineComponent({
       handleUpdateList,
       createDefinition,
       handleChangePageSize,
+      batchDeleteWorkflow,
+      batchExportWorkflow,
+      batchCopyWorkflow,
+      handleCopyUpdateList,
       ...toRefs(variables)
     }
   },
@@ -99,7 +118,11 @@ export default defineComponent({
         <Card class={styles.card}>
           <div class={styles.header}>
             <NSpace>
-              <NButton type='primary' onClick={this.createDefinition} class='btn-create-process'>
+              <NButton
+                type='primary'
+                onClick={this.createDefinition}
+                class='btn-create-process'
+              >
                 {t('project.workflow.create_workflow')}
               </NButton>
               <NButton strong secondary onClick={() => (this.showRef = true)}>
@@ -127,11 +150,13 @@ export default defineComponent({
         </Card>
         <Card title={t('project.workflow.workflow_definition')}>
           <NDataTable
+            rowKey={(row) => row.code}
             columns={this.columns}
             data={this.tableData}
             striped
             size={'small'}
             class={styles.table}
+            v-model:checked-row-keys={this.checkedRowKeys}
             row-class-name='items'
           />
           <div class={styles.pagination}>
@@ -146,6 +171,60 @@ export default defineComponent({
               onUpdatePageSize={this.handleChangePageSize}
             />
           </div>
+          <div class={styles['batch-button']}>
+            <NTooltip>
+              {{
+                default: () => t('project.workflow.delete'),
+                trigger: () => (
+                  <NButton
+                    tag='div'
+                    type='primary'
+                    disabled={this.checkedRowKeys.length <= 0}
+                    class='btn-delete-all'
+                  >
+                    <NPopconfirm onPositiveClick={this.batchDeleteWorkflow}>
+                      {{
+                        default: () => t('project.workflow.delete_confirm'),
+                        trigger: () => t('project.workflow.delete')
+                      }}
+                    </NPopconfirm>
+                  </NButton>
+                )
+              }}
+            </NTooltip>
+            <NTooltip>
+              {{
+                default: () => t('project.workflow.export'),
+                trigger: () => (
+                  <NButton
+                    tag='div'
+                    type='primary'
+                    disabled={this.checkedRowKeys.length <= 0}
+                    onClick={this.batchExportWorkflow}
+                    class='btn-delete-all'
+                  >
+                    {t('project.workflow.export')}
+                  </NButton>
+                )
+              }}
+            </NTooltip>
+            <NTooltip>
+              {{
+                default: () => t('project.workflow.batch_copy'),
+                trigger: () => (
+                  <NButton
+                    tag='div'
+                    type='primary'
+                    disabled={this.checkedRowKeys.length <= 0}
+                    onClick={() => (this.copyShowRef = true)}
+                    class='btn-delete-all'
+                  >
+                    {t('project.workflow.batch_copy')}
+                  </NButton>
+                )
+              }}
+            </NTooltip>
+          </div>
         </Card>
         <ImportModal
           v-model:show={this.showRef}
@@ -166,6 +245,11 @@ export default defineComponent({
           v-model:show={this.versionShowRef}
           onUpdateList={this.handleUpdateList}
         />
+        <CopyModal
+          v-model:codes={this.checkedRowKeys}
+          v-model:show={this.copyShowRef}
+          onUpdateList={this.handleCopyUpdateList}
+        />
       </div>
     )
   }
diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/use-table.ts b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/use-table.ts
index e05f959..b8c8699 100644
--- a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/use-table.ts
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/use-table.ts
@@ -15,14 +15,16 @@
  * limitations under the License.
  */
 
+import _ from 'lodash'
 import { h, ref, reactive } from 'vue'
 import { useI18n } from 'vue-i18n'
 import { useRouter } from 'vue-router'
 import type { Router } from 'vue-router'
-import type { TableColumns } from 'naive-ui/es/data-table/src/interface'
+import type { TableColumns, RowKey } from 'naive-ui/es/data-table/src/interface'
 import { useAsyncState } from '@vueuse/core'
 import {
   batchCopyByCodes,
+  batchDeleteByCodes,
   batchExportByCodes,
   deleteByCode,
   queryListPaging,
@@ -40,6 +42,7 @@ export function useTable() {
 
   const variables = reactive({
     columns: [],
+    checkedRowKeys: [] as Array<RowKey>,
     row: {},
     tableData: [],
     projectCode: ref(Number(router.currentRoute.value.params.projectCode)),
@@ -50,12 +53,18 @@ export function useTable() {
     showRef: ref(false),
     startShowRef: ref(false),
     timingShowRef: ref(false),
-    versionShowRef: ref(false)
+    versionShowRef: ref(false),
+    copyShowRef: ref(false)
   })
 
   const createColumns = (variables: any) => {
     variables.columns = [
       {
+        type: 'selection',
+        disabled: (row) => row.releaseState === 'ONLINE',
+        className: 'btn-selected'
+      },
+      {
         title: '#',
         key: 'id',
         width: 50,
@@ -200,6 +209,45 @@ export function useTable() {
     })
   }
 
+  const batchDeleteWorkflow = () => {
+    const data = {
+      codes: _.join(variables.checkedRowKeys, ',')
+    }
+
+    batchDeleteByCodes(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({
+        pageSize: variables.pageSize,
+        pageNo: variables.page,
+        searchVal: variables.searchVal
+      })
+    })
+  }
+
+  const batchExportWorkflow = () => {
+    const fileName = 'workflow_' + new Date().getTime()
+    const data = {
+      codes: _.join(variables.checkedRowKeys, ',')
+    }
+
+    batchExportByCodes(data, variables.projectCode).then((res: any) => {
+      downloadBlob(res, fileName)
+      window.$message.success(t('project.workflow.success'))
+      variables.checkedRowKeys = []
+    })
+  }
+
+  const batchCopyWorkflow = () => {}
+
   const releaseWorkflow = (row: any) => {
     const data = {
       name: row.name,
@@ -298,6 +346,9 @@ export function useTable() {
   return {
     variables,
     createColumns,
-    getTableData
+    getTableData,
+    batchDeleteWorkflow,
+    batchExportWorkflow,
+    batchCopyWorkflow
   }
 }