From dde81eb93c88042f415cb5f6e0f55c75d6ef95fb Mon Sep 17 00:00:00 2001 From: Devosend Date: Wed, 16 Mar 2022 17:59:20 +0800 Subject: [PATCH] [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 +- .../modules/process-definition/index.ts | 2 +- .../definition/components/copy-modal.tsx | 115 ++++++++++++++++++ .../definition/components/start-modal.tsx | 7 +- .../definition/components/use-form.ts | 23 +++- .../definition/components/use-modal.ts | 23 ++++ .../workflow/definition/index.module.scss | 9 ++ .../projects/workflow/definition/index.tsx | 90 +++++++++++++- .../projects/workflow/definition/use-table.ts | 57 ++++++++- 10 files changed, 329 insertions(+), 11 deletions(-) create mode 100644 dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/copy-modal.tsx diff --git a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts index 7f35f22c90..1fb7cef939 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 cf69cfec85..efbd217920 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 477a61c450..b866f51d50 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 0000000000..487175f23c --- /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, + default: false + }, + codes: { + type: Array as PropType>, + 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([]) + const projectOptions = computed(() => { + return projects.value.map((t) => ({ + label: t.name, + value: t.code + })) + }) + + onMounted(() => { + queryProjectCreatedAndAuthorizedByUser().then( + (res: Array) => { + projects.value = res + } + ) + }) + + return { + hideModal, + handleCopy, + projectOptions, + ...toRefs(copyState) + } + }, + + render() { + const { t } = useI18n() + + return ( + + + + + + + + ) + } +}) 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 832b0d85ce..4201fe7de4 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({ - + 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 eacbf17f56..0d572614e5 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 e486b16fb6..2ef763ef2b 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) => { + 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 7eac171996..72e5e2f812 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 5aa6d50602..c16f862ec3 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({
- + {t('project.workflow.create_workflow')} (this.showRef = true)}> @@ -127,11 +150,13 @@ export default defineComponent({ 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' />
@@ -146,6 +171,60 @@ export default defineComponent({ onUpdatePageSize={this.handleChangePageSize} />
+
+ + {{ + default: () => t('project.workflow.delete'), + trigger: () => ( + + + {{ + default: () => t('project.workflow.delete_confirm'), + trigger: () => t('project.workflow.delete') + }} + + + ) + }} + + + {{ + default: () => t('project.workflow.export'), + trigger: () => ( + + {t('project.workflow.export')} + + ) + }} + + + {{ + default: () => t('project.workflow.batch_copy'), + trigger: () => ( + (this.copyShowRef = true)} + class='btn-delete-all' + > + {t('project.workflow.batch_copy')} + + ) + }} + +
+
) } 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 e05f9596f9..b8c8699c5d 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, row: {}, tableData: [], projectCode: ref(Number(router.currentRoute.value.params.projectCode)), @@ -50,11 +53,17 @@ 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', @@ -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 } }