Browse Source

[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
3.0.0/version-upgrade
Devosend 3 years ago committed by GitHub
parent
commit
dde81eb93c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      dolphinscheduler-ui-next/src/locales/modules/en_US.ts
  2. 7
      dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts
  3. 2
      dolphinscheduler-ui-next/src/service/modules/process-definition/index.ts
  4. 115
      dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/copy-modal.tsx
  5. 7
      dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/start-modal.tsx
  6. 23
      dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/use-form.ts
  7. 23
      dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/use-modal.ts
  8. 9
      dolphinscheduler-ui-next/src/views/projects/workflow/definition/index.module.scss
  9. 90
      dolphinscheduler-ui-next/src/views/projects/workflow/definition/index.tsx
  10. 57
      dolphinscheduler-ui-next/src/views/projects/workflow/definition/use-table.ts

7
dolphinscheduler-ui-next/src/locales/modules/en_US.ts

@ -406,6 +406,7 @@ const project = {
tree_view: 'Tree View', tree_view: 'Tree View',
tree_limit: 'Limit Size', tree_limit: 'Limit Size',
export: 'Export', export: 'Export',
batch_copy: 'Batch Copy',
version_info: 'Version Info', version_info: 'Version Info',
version: 'Version', version: 'Version',
file_upload: 'File Upload', file_upload: 'File Upload',
@ -509,7 +510,11 @@ const project = {
cancel_full_screen: 'Cancel full screen', cancel_full_screen: 'Cancel full screen',
task_state: 'Task status', task_state: 'Task status',
mode_of_dependent: 'Mode of dependent', 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: {
task_name: 'Task Name', task_name: 'Task Name',

7
dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts

@ -403,6 +403,7 @@ const project = {
tree_view: '工作流树形图', tree_view: '工作流树形图',
tree_limit: '限制大小', tree_limit: '限制大小',
export: '导出', export: '导出',
batch_copy: '批量复制',
version_info: '版本信息', version_info: '版本信息',
version: '版本', version: '版本',
file_upload: '文件上传', file_upload: '文件上传',
@ -506,7 +507,11 @@ const project = {
cancel_full_screen: '取消全屏', cancel_full_screen: '取消全屏',
task_state: '任务状态', task_state: '任务状态',
mode_of_dependent: '依赖模式', mode_of_dependent: '依赖模式',
open: '打开' open: '打开',
project_name_required: '项目名称必填',
related_items: '关联项目',
project_name: '项目名称',
project_tips: '请选择项目'
}, },
task: { task: {
task_name: '任务名称', task_name: '任务名称',

2
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({ return axios({
url: `/projects/${code}/process-definition/batch-delete`, url: `/projects/${code}/process-definition/batch-delete`,
method: 'post', method: 'post',

115
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>
)
}
})

7
dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/start-modal.tsx

@ -398,7 +398,12 @@ export default defineComponent({
<DeleteOutlined /> <DeleteOutlined />
</NIcon> </NIcon>
</NButton> </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> <NIcon>
<PlusCircleOutlined /> <PlusCircleOutlined />
</NIcon> </NIcon>

23
dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/use-form.ts

@ -88,9 +88,30 @@ export const useForm = () => {
}, },
saving: false 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 { return {
importState, importState,
startState, startState,
timingState timingState,
copyState
} }
} }

23
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 type { Router } from 'vue-router'
import { format } from 'date-fns' import { format } from 'date-fns'
import { import {
batchCopyByCodes,
importProcessDefinition, importProcessDefinition,
queryProcessDefinitionByCode queryProcessDefinitionByCode
} from '@/service/modules/process-definition' } 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 getTimingData = () => {
const start = format( const start = format(
parseTime(state.timingForm.startEndTime[0]), parseTime(state.timingForm.startEndTime[0]),
@ -253,6 +275,7 @@ export function useModal(
handleStartDefinition, handleStartDefinition,
handleCreateTiming, handleCreateTiming,
handleUpdateTiming, handleUpdateTiming,
handleBatchCopyDefinition,
getWorkerGroups, getWorkerGroups,
getAlertGroups, getAlertGroups,
getEnvironmentList, getEnvironmentList,

9
dolphinscheduler-ui-next/src/views/projects/workflow/definition/index.module.scss

@ -86,3 +86,12 @@
width: 86%; width: 86%;
} }
} }
.batch-button {
position: absolute;
bottom: 10px;
left: 10px;
> div {
margin-right: 5px;
}
}

90
dolphinscheduler-ui-next/src/views/projects/workflow/definition/index.tsx

@ -23,7 +23,9 @@ import {
NIcon, NIcon,
NInput, NInput,
NPagination, NPagination,
NSpace NSpace,
NTooltip,
NPopconfirm
} from 'naive-ui' } from 'naive-ui'
import { defineComponent, onMounted, toRefs, watch } from 'vue' import { defineComponent, onMounted, toRefs, watch } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
@ -32,6 +34,7 @@ import ImportModal from './components/import-modal'
import StartModal from './components/start-modal' import StartModal from './components/start-modal'
import TimingModal from './components/timing-modal' import TimingModal from './components/timing-modal'
import VersionModal from './components/version-modal' import VersionModal from './components/version-modal'
import CopyModal from './components/copy-modal'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
import type { Router } from 'vue-router' import type { Router } from 'vue-router'
import styles from './index.module.scss' import styles from './index.module.scss'
@ -43,7 +46,14 @@ export default defineComponent({
const route = useRoute() const route = useRoute()
const projectCode = Number(route.params.projectCode) const projectCode = Number(route.params.projectCode)
const { variables, createColumns, getTableData } = useTable() const {
variables,
createColumns,
getTableData,
batchDeleteWorkflow,
batchExportWorkflow,
batchCopyWorkflow
} = useTable()
const requestData = () => { const requestData = () => {
getTableData({ getTableData({
@ -57,6 +67,11 @@ export default defineComponent({
requestData() requestData()
} }
const handleCopyUpdateList = () => {
variables.checkedRowKeys = []
requestData()
}
const handleSearch = () => { const handleSearch = () => {
variables.page = 1 variables.page = 1
requestData() requestData()
@ -88,6 +103,10 @@ export default defineComponent({
handleUpdateList, handleUpdateList,
createDefinition, createDefinition,
handleChangePageSize, handleChangePageSize,
batchDeleteWorkflow,
batchExportWorkflow,
batchCopyWorkflow,
handleCopyUpdateList,
...toRefs(variables) ...toRefs(variables)
} }
}, },
@ -99,7 +118,11 @@ export default defineComponent({
<Card class={styles.card}> <Card class={styles.card}>
<div class={styles.header}> <div class={styles.header}>
<NSpace> <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')} {t('project.workflow.create_workflow')}
</NButton> </NButton>
<NButton strong secondary onClick={() => (this.showRef = true)}> <NButton strong secondary onClick={() => (this.showRef = true)}>
@ -127,11 +150,13 @@ export default defineComponent({
</Card> </Card>
<Card title={t('project.workflow.workflow_definition')}> <Card title={t('project.workflow.workflow_definition')}>
<NDataTable <NDataTable
rowKey={(row) => row.code}
columns={this.columns} columns={this.columns}
data={this.tableData} data={this.tableData}
striped striped
size={'small'} size={'small'}
class={styles.table} class={styles.table}
v-model:checked-row-keys={this.checkedRowKeys}
row-class-name='items' row-class-name='items'
/> />
<div class={styles.pagination}> <div class={styles.pagination}>
@ -146,6 +171,60 @@ export default defineComponent({
onUpdatePageSize={this.handleChangePageSize} onUpdatePageSize={this.handleChangePageSize}
/> />
</div> </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> </Card>
<ImportModal <ImportModal
v-model:show={this.showRef} v-model:show={this.showRef}
@ -166,6 +245,11 @@ export default defineComponent({
v-model:show={this.versionShowRef} v-model:show={this.versionShowRef}
onUpdateList={this.handleUpdateList} onUpdateList={this.handleUpdateList}
/> />
<CopyModal
v-model:codes={this.checkedRowKeys}
v-model:show={this.copyShowRef}
onUpdateList={this.handleCopyUpdateList}
/>
</div> </div>
) )
} }

57
dolphinscheduler-ui-next/src/views/projects/workflow/definition/use-table.ts

@ -15,14 +15,16 @@
* limitations under the License. * limitations under the License.
*/ */
import _ from 'lodash'
import { h, ref, reactive } from 'vue' import { h, ref, reactive } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import type { Router } 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 { useAsyncState } from '@vueuse/core'
import { import {
batchCopyByCodes, batchCopyByCodes,
batchDeleteByCodes,
batchExportByCodes, batchExportByCodes,
deleteByCode, deleteByCode,
queryListPaging, queryListPaging,
@ -40,6 +42,7 @@ export function useTable() {
const variables = reactive({ const variables = reactive({
columns: [], columns: [],
checkedRowKeys: [] as Array<RowKey>,
row: {}, row: {},
tableData: [], tableData: [],
projectCode: ref(Number(router.currentRoute.value.params.projectCode)), projectCode: ref(Number(router.currentRoute.value.params.projectCode)),
@ -50,11 +53,17 @@ export function useTable() {
showRef: ref(false), showRef: ref(false),
startShowRef: ref(false), startShowRef: ref(false),
timingShowRef: ref(false), timingShowRef: ref(false),
versionShowRef: ref(false) versionShowRef: ref(false),
copyShowRef: ref(false)
}) })
const createColumns = (variables: any) => { const createColumns = (variables: any) => {
variables.columns = [ variables.columns = [
{
type: 'selection',
disabled: (row) => row.releaseState === 'ONLINE',
className: 'btn-selected'
},
{ {
title: '#', title: '#',
key: 'id', 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 releaseWorkflow = (row: any) => {
const data = { const data = {
name: row.name, name: row.name,
@ -298,6 +346,9 @@ export function useTable() {
return { return {
variables, variables,
createColumns, createColumns,
getTableData getTableData,
batchDeleteWorkflow,
batchExportWorkflow,
batchCopyWorkflow
} }
} }

Loading…
Cancel
Save