Browse Source

[Feature-7018][UI] Add list edit view from task definition (#7852)

* Performance optimization of DEPENDENT task

* fix eslint

* [Feature] Improve task definition list

* feat: Task definition list

* change update interface
3.0.0/version-upgrade
wangyizhi 3 years ago committed by GitHub
parent
commit
984665a5ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 116
      dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/formModel.vue
  2. 33
      dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/tasks/pre_tasks.vue
  3. 198
      dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/taskDefinition/_source/list.vue
  4. 91
      dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/taskDefinition/_source/taskDeleteModal.vue
  5. 107
      dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/taskDefinition/_source/taskMoveModel.vue
  6. 253
      dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/taskDefinition/_source/versions.vue
  7. 309
      dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/taskDefinition/index.vue
  8. 136
      dolphinscheduler-ui/src/js/conf/home/store/dag/actions.js
  9. 39
      dolphinscheduler-ui/src/js/module/components/conditions/conditions.vue
  10. 13
      dolphinscheduler-ui/src/js/module/components/secondaryMenu/_source/menu.js
  11. 11
      dolphinscheduler-ui/src/js/module/i18n/locale/en_US.js
  12. 11
      dolphinscheduler-ui/src/js/module/i18n/locale/zh_CN.js

116
dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/formModel.vue

@ -17,11 +17,17 @@
<template> <template>
<div class="form-model-wrapper" v-clickoutside="_handleClose"> <div class="form-model-wrapper" v-clickoutside="_handleClose">
<div class="title-box"> <div class="title-box">
<span class="name">{{ $t("Current node settings") }} <span class="name"
<a v-if="helpUrlEnable(nodeData.taskType)" class="helper-link" target="_blank" >{{ $t("Current node settings") }}
:href="helpUrl(nodeData.taskType)"> <a
v-if="helpUrlEnable(nodeData.taskType)"
class="helper-link"
target="_blank"
:href="helpUrl(nodeData.taskType)"
>
<i class="el-icon-question" /> <i class="el-icon-question" />
{{nodeData.taskType}} {{ $t('Instructions') }}</a> {{ nodeData.taskType }} {{ $t("Instructions") }}</a
>
</span> </span>
<span class="go-subtask"> <span class="go-subtask">
<!-- Component can't pop up box to do component processing --> <!-- Component can't pop up box to do component processing -->
@ -94,6 +100,26 @@
</div> </div>
</m-list-box> </m-list-box>
<m-list-box v-if="fromTaskDefinition">
<div slot="text">{{ $t("Process Name") }}</div>
<div slot="content">
<el-select
@change="changeProcessCode"
:value="processCode"
size="small"
style="width: 100%"
:disabled="isDetails || taskDefinition"
>
<el-option
v-for="process in processListS"
:key="process.code"
:label="process.name"
:value="process.code"
/>
</el-select>
</div>
</m-list-box>
<!-- Running sign --> <!-- Running sign -->
<m-list-box> <m-list-box>
<div slot="text">{{ $t("Run flag") }}</div> <div slot="text">{{ $t("Run flag") }}</div>
@ -165,12 +191,13 @@
<span class="text-b">{{ $t("Task group queue priority") }}</span> <span class="text-b">{{ $t("Task group queue priority") }}</span>
<el-input <el-input
:disabled="taskGroupId === ''" :disabled="taskGroupId === ''"
style="width: 166px;" style="width: 166px"
type="input" type="input"
v-model="taskGroupPriority" v-model="taskGroupPriority"
maxlength="60" maxlength="60"
v-on:input="_onUpdateTaskGroupPriority" v-on:input="_onUpdateTaskGroupPriority"
size="small"> size="small"
>
</el-input> </el-input>
</div> </div>
</m-list-box> </m-list-box>
@ -448,11 +475,7 @@
</m-waterdrop> </m-waterdrop>
</div> </div>
<!-- Pre-tasks in workflow --> <!-- Pre-tasks in workflow -->
<m-pre-tasks <m-pre-tasks ref="preTasks" :code="code" :fromTaskDefinition="fromTaskDefinition" :prevTasks="prevTasks" :processDefinition="processDefinition"/>
ref="preTasks"
v-if="!fromTaskDefinition"
:code="code"
/>
</div> </div>
</div> </div>
<div class="bottom-box"> <div class="bottom-box">
@ -583,7 +606,10 @@
backfillRefresh: true, backfillRefresh: true,
// whether this is a new Task // whether this is a new Task
isNewCreate: true, isNewCreate: true,
tasksTypeList: Object.keys(tasksType) tasksTypeList: Object.keys(tasksType),
// processCode
processCode: undefined,
processDefinition: null
} }
}, },
provide () { provide () {
@ -609,7 +635,7 @@
}, },
inject: ['dagChart'], inject: ['dagChart'],
methods: { methods: {
...mapActions('dag', ['getTaskInstanceList']), ...mapActions('dag', ['getTaskInstanceList', 'getProcessDefinition']),
helpUrlEnable (typekey) { helpUrlEnable (typekey) {
const type = tasksType[typekey] const type = tasksType[typekey]
if (!type) return false if (!type) return false
@ -850,10 +876,16 @@
if (!this.$refs[this.nodeData.taskType]._verification()) { if (!this.$refs[this.nodeData.taskType]._verification()) {
return return
} }
// set preTask // set dag preTask
if (this.$refs.preTasks) { if (this.dagChart && this.$refs.preTasks) {
this.$refs.preTasks.setPreNodes() this.$refs.preTasks.setDagPreNodes()
}
// set edge label
if (this.dagChart) {
this._setEdgeLabel()
} }
this.successBranch && (this.conditionResult.successNode[0] = this.successBranch) this.successBranch && (this.conditionResult.successNode[0] = this.successBranch)
this.failedBranch && (this.conditionResult.failedNode[0] = this.failedBranch) this.failedBranch && (this.conditionResult.failedNode[0] = this.failedBranch)
this.$emit('addTaskInfo', { this.$emit('addTaskInfo', {
@ -882,11 +914,12 @@
taskGroupId: this.taskGroupId, taskGroupId: this.taskGroupId,
taskGroupPriority: this.taskGroupPriority taskGroupPriority: this.taskGroupPriority
}, },
fromThis: this fromThis: this,
...(this.fromTaskDefinition ? {
prevTasks: this.$refs.preTasks ? this.$refs.preTasks.preTasks : [],
processCode: this.processCode
} : {})
}) })
// set edge label
this._setEdgeLabel()
}, },
/** /**
* Sub-workflow selected node echo name * Sub-workflow selected node echo name
@ -1005,6 +1038,44 @@
}, },
changeTaskType (value) { changeTaskType (value) {
this.$emit('changeTaskType', value) this.$emit('changeTaskType', value)
},
calculateRelatedTasks () {
if (this.processDefinition && this.taskDefinition) {
const relations = this.processDefinition.processTaskRelationList || []
const tasks = this.processDefinition.taskDefinitionList || []
const tasksMap = {}
tasks.forEach(task => {
tasksMap[task.code] = task
})
const taskCode = this.taskDefinition.code
const buildTask = (task) => ({
code: task.code,
name: task.name,
type: task.taskType
})
// Downstream tasks
const postTasks = relations
.filter(relation => relation.preTaskCode === taskCode)
.map(relation => buildTask(tasksMap[relation.postTaskCode]))
// Upstream tasks
const prevTasks = relations
.filter(relation => relation.postTaskCode === taskCode && relation.preTaskCode !== 0)
.map(relation => buildTask(tasksMap[relation.preTaskCode]))
this.postTasks = postTasks
this.prevTasks = prevTasks
}
},
getProcessDetails () {
this.getProcessDefinition(this.processCode).then(res => {
this.processDefinition = res
this.calculateRelatedTasks()
})
},
changeProcessCode (code) {
this.processCode = code
this.getProcessDetails()
} }
}, },
created () { created () {
@ -1046,6 +1117,11 @@
this.postTasks = postNodes.map(buildTask) this.postTasks = postNodes.map(buildTask)
this.prevTasks = prevNodes.map(buildTask) this.prevTasks = prevNodes.map(buildTask)
} }
if (this.fromTaskDefinition && this.taskDefinition) {
this.processCode = this.taskDefinition.processCode
this.getProcessDetails()
}
}, },
mounted () { mounted () {
let self = this let self = this

33
dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/tasks/pre_tasks.vue

@ -53,7 +53,11 @@
code: { code: {
type: Number, type: Number,
default: 0 default: 0
} },
processDefinition: {
type: Object
},
prevTasks: Array
}, },
data () { data () {
return { return {
@ -61,7 +65,31 @@
preTasks: [] preTasks: []
} }
}, },
watch: {
processDefinition (def) {
if (def) {
this.preTasks = []
const relations = def.processTaskRelationList
this.options = def.taskDefinitionList.filter((task) => {
// The current node cannot be used as the prev node
if (task.code === this.code) return false
// The number of edges start with CONDITIONS task cannot be greater than 2
if (task.taskType === 'CONDITIONS') {
return relations.filter((e) => e.preTaskCode === task.code).length < 2
}
return true
})
}
},
prevTasks (prevTasks) {
if (prevTasks) {
this.preTasks = prevTasks.map(task => task.code)
}
}
},
mounted () { mounted () {
// Called by dag
if (this.dagChart) {
const canvas = this.getDagCanvasRef() const canvas = this.getDagCanvasRef()
const edges = canvas.getEdges() const edges = canvas.getEdges()
this.preTasks = canvas.getPrevNodes(this.code).map(node => node.id) this.preTasks = canvas.getPrevNodes(this.code).map(node => node.id)
@ -75,6 +103,7 @@
} }
return true return true
}) })
}
}, },
computed: { computed: {
...mapState('dag', ['tasks']) ...mapState('dag', ['tasks'])
@ -89,7 +118,7 @@
return canvas return canvas
} }
}, },
setPreNodes () { setDagPreNodes () {
const canvas = this.getDagCanvasRef() const canvas = this.getDagCanvasRef()
canvas.setPreNodes(this.code, this.preTasks, true) canvas.setPreNodes(this.code, this.preTasks, true)
} }

198
dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/taskDefinition/_source/list.vue

@ -23,61 +23,106 @@
style="width: 100%" style="width: 100%"
@selection-change="select" @selection-change="select"
> >
<el-table-column
prop="id"
:label="$t('#')"
min-width="50"
></el-table-column>
<el-table-column :label="$t('Task Name')" min-width="200"> <el-table-column :label="$t('Task Name')" min-width="200">
<template v-slot="scope"> <template v-slot="scope">
<el-popover trigger="hover" placement="top"> <el-popover trigger="hover" placement="top">
<p>{{ scope.row.name }}</p> <div>{{ scope.row.taskName }}</div>
<div slot="reference" class="name-wrapper"> <div slot="reference" class="name-wrapper">
<a <a
href="javascript:" href="javascript:"
class="links" class="links"
@click="viewTaskDetail(scope.row)" @click="viewTaskDetail(scope.row)"
> >
{{ scope.row.name }} <span class="ellipsis name">{{ scope.row.taskName }}</span>
</a> </a>
</div> </div>
</el-popover> </el-popover>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="$t('Task Type')" prop="taskType" min-width="135"> <el-table-column
:label="$t('Process Name')"
prop="processDefinitionName"
min-width="135"
>
</el-table-column>
<el-table-column
:label="$t('Process State')"
prop="processReleaseState"
min-width="135"
>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
:label="$t('User Name')" :label="$t('Task Type')"
prop="userName" prop="taskType"
width="135" min-width="135"
></el-table-column> >
</el-table-column>
<el-table-column :label="$t('Version Info')" min-width="135"> <el-table-column :label="$t('Version Info')" min-width="135">
<template v-slot="scope"> <template v-slot="scope">
<span> <span>
{{ 'V' + scope.row.version }} {{ "V" + scope.row.taskVersion }}
</span> </span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="$t('Upstream Tasks')" min-width="300">
<template v-slot="scope">
<div class="upstream-tasks">
<el-popover
trigger="hover"
placement="top"
v-for="task in scope.row.upstreamTasks.slice(0, 3)"
:key="task.taskCode"
>
<div>{{ task.taskName }}</div>
<el-tag class="pre-task-tag" size="mini" slot="reference">
{{ task.taskName }}
</el-tag>
</el-popover>
<!-- more popover -->
<el-popover
v-if="scope.row.upstreamTasks.length > 3"
trigger="hover"
:title="$t('Upstream Tasks')"
placement="top"
>
<div class="task-definition-upstreams-popover">
<el-tag
size="mini"
slot="reference"
class="popover-tag"
v-for="task in scope.row.upstreamTasks"
:key="task.taskCode"
>
{{ task.taskName }}
</el-tag>
</div>
<el-tag class="pre-task-tag" size="mini" slot="reference">
{{
$t("and {n} more", {
n: scope.row.upstreamTasks.length - 3,
})
}}
</el-tag>
</el-popover>
<span v-if="scope.row.upstreamTasks.length === 0">-</span>
</div>
</template>
</el-table-column>
<el-table-column :label="$t('Create Time')" min-width="135"> <el-table-column :label="$t('Create Time')" min-width="135">
<template v-slot="scope"> <template v-slot="scope">
<span> <span>
{{ scope.row.createTime | formatDate }} {{ scope.row.taskCreateTime | formatDate }}
</span> </span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="$t('Update Time')" min-width="135"> <el-table-column :label="$t('Update Time')" min-width="135">
<template v-slot="scope"> <template v-slot="scope">
<span> <span>
{{ scope.row.updateTime | formateDate }} {{ scope.row.taskUpdateTime | formateDate }}
</span> </span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="$t('Description')" min-width="100"> <el-table-column :label="$t('Operation')" width="150" fixed="right">
<template v-slot="scope">
<span>{{ scope.row.description | filterNull }} </span>
</template>
</el-table-column>
<el-table-column :label="$t('Operation')" width="100" fixed="right">
<template v-slot="scope"> <template v-slot="scope">
<el-tooltip <el-tooltip
:content="$t('Edit')" :content="$t('Edit')"
@ -90,33 +135,68 @@
size="mini" size="mini"
icon="el-icon-edit-outline" icon="el-icon-edit-outline"
circle circle
:disabled="scope.row.taskType === 'CONDITIONS' || scope.row.taskType ==='SWITCH' " :disabled="
['CONDITIONS', 'SWITCH'].includes(scope.row.taskType) ||
(scope.row.processDefinitionCode &&
scope.row.processReleaseState === 'ONLINE')
"
@click="editTask(scope.row)" @click="editTask(scope.row)"
></el-button> ></el-button>
</span> </span>
</el-tooltip> </el-tooltip>
<el-tooltip <el-tooltip
:content="$t('Delete')" :content="$t('Move task')"
placement="top" placement="top"
:enterable="false" :enterable="false"
> >
<el-popconfirm <span>
:confirmButtonText="$t('Confirm')" <el-button
:cancelButtonText="$t('Cancel')" type="primary"
icon="el-icon-info" size="mini"
iconColor="red" icon="el-icon-rank"
:title="$t('Delete?')" circle
@onConfirm="deleteTask(scope.row.code, scope.row.projectCode)" :disabled="
scope.row.processDefinitionCode &&
scope.row.processReleaseState === 'ONLINE'
"
@click="showMoveModal(scope.row)"
></el-button>
</span>
</el-tooltip>
<el-tooltip
:content="$t('Delete')"
placement="top"
:enterable="false"
> >
<span>
<el-button <el-button
type="danger" type="danger"
size="mini" size="mini"
icon="el-icon-delete" icon="el-icon-delete"
slot="reference" slot="reference"
circle circle
:disabled="
scope.row.processDefinitionCode &&
scope.row.processReleaseState === 'ONLINE'
"
@click="showDeleteModal(scope.row)"
></el-button>
</span>
</el-tooltip>
<el-tooltip
:content="$t('Version Info')"
placement="top"
:enterable="false"
> >
</el-button> <span
</el-popconfirm> ><el-button
type="primary"
size="mini"
icon="el-icon-info"
@click="viewTaskVersions(scope.row)"
circle
></el-button
></span>
</el-tooltip> </el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
@ -127,7 +207,6 @@
<script> <script>
import _ from 'lodash' import _ from 'lodash'
import { mapActions } from 'vuex'
export default { export default {
name: 'task-list', name: 'task-list',
@ -151,47 +230,48 @@
deep: true deep: true
} }
}, },
created () { created () {},
// this.list = this.tasksList
},
methods: { methods: {
...mapActions('dag', ['deleteTaskDefinition']),
/** /**
* onUpdate * Delete task
*/ */
_onUpdate () { showDeleteModal (taskRow) {
this.$emit('on-update') this.$emit('showDeleteModal', taskRow)
}, },
/** /**
* deleteTaskDefinition * View task detail
*/ */
deleteTask (code) { viewTaskDetail (taskRow) {
this.deleteTaskDefinition({ this.$emit('viewTaskDetail', taskRow)
code: code
})
.then((res) => {
this._onUpdate()
this.$message.success(res.msg)
})
.catch((e) => {
this.$message.error(e.msg || '')
})
}, },
/** /**
* taskdefinition detail * Edit task
*/ */
viewTaskDetail (task) { editTask (taskRow) {
this.$emit('viewTaskDetail', task) this.$emit('editTask', taskRow)
}, },
/** /**
* task edit * View task versions
*/ */
editTask (task) { viewTaskVersions (taskRow) {
this.$emit('editTask', task) this.$emit('viewTaskVersions', taskRow)
},
/**
* Move Task
*/
showMoveModal (taskRow) {
this.$emit('showMoveModal', taskRow)
} }
} }
} }
</script> </script>
<style> <style lang="scss">
.task-definition-upstreams-popover {
max-width: 500px;
.popover-tag {
margin-right: 10px;
margin-bottom: 10px;
}
}
</style> </style>

91
dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/taskDefinition/_source/taskDeleteModal.vue

@ -0,0 +1,91 @@
/*
* 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.
*/
<template>
<div>
<el-dialog :title="$t('Delete')" :visible.sync="visible" width="500px">
<div class="content" v-if="taskRow">
<template v-if="taskRow.processDefinitionCode">
<span>{{
$t("Delete task {taskName} from process {processName}?", {
processName: taskRow.processDefinitionName,
taskName: taskRow.taskName,
})
}}</span>
<el-checkbox class="remove-checkbox" v-model="removeCompletely">{{
$t("Delete task completely")
}}</el-checkbox>
</template>
<template v-else>
<span>{{
$t("Delete {taskName}?", {
taskName: taskRow.taskName,
})
}}</span>
</template>
</div>
<span slot="footer" class="dialog-footer">
<el-button size="small" @click="close">{{ $t("Cancel") }}</el-button>
<el-button size="small" type="primary" @click="submit">{{
$t("Confirm")
}}</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
export default {
props: {
taskRow: Object
},
data () {
return {
visible: false,
// Whether to delete the task completely
removeCompletely: false
}
},
methods: {
show () {
this.visible = true
},
close () {
this.visible = false
},
submit () {
this.$emit('deleteTask', {
completely: this.taskRow.processDefinitionCode ? this.removeCompletely : true,
taskCode: this.taskRow.taskCode,
processDefinitionCode: this.taskRow.processDefinitionCode
})
}
}
}
</script>
<style lang="scss" scoped>
.content {
margin: 20px;
color: #333;
display: flex;
flex-direction: column;
.remove-checkbox {
margin-top: 20px;
}
}
</style>

107
dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/taskDefinition/_source/taskMoveModel.vue

@ -0,0 +1,107 @@
/*
* 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.
*/
<template>
<div>
<el-dialog :title="$t('Move task')" :visible.sync="visible" width="500px">
<div class="content" v-if="taskRow">
<el-form ref="form" :model="form" label-width="100px" size="mini">
<el-form-item :label="$t('Process Name')">
<el-select v-model="form.processCode">
<el-option
:label="process.name"
:value="process.code"
v-for="process in processListS"
:key="process.code"
>{{ process.name }}</el-option
>
</el-select>
</el-form-item>
</el-form>
</div>
<span slot="footer" class="dialog-footer">
<el-button size="small" @click="close">{{ $t("Cancel") }}</el-button>
<el-button size="small" type="primary" @click="submit">{{
$t("Confirm")
}}</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
props: {
taskRow: Object
},
data () {
return {
visible: false,
form: {
processCode: -1
}
}
},
computed: {
...mapState('dag', ['processListS'])
},
methods: {
show () {
this.visible = true
},
close () {
this.visible = false
},
submit () {
if (this.taskRow.processDefinitionCode === this.form.processCode) {
this.visible = false
return
}
if (!this.form.processCode) {
this.$message.error(this.$t('Please select a process (required)'))
return
}
this.$emit('moveTask', {
taskCode: this.taskRow.taskCode,
processDefinitionCode: this.taskRow.processDefinitionCode,
targetProcessDefinitionCode: this.form.processCode
})
}
},
watch: {
taskRow (val) {
if (val) {
this.form.processCode = val.processDefinitionCode || ''
}
}
}
}
</script>
<style lang="scss" scoped>
.content {
margin: 20px;
color: #333;
display: flex;
flex-direction: column;
.remove-checkbox {
margin-top: 20px;
}
}
</style>

253
dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/taskDefinition/_source/versions.vue

@ -0,0 +1,253 @@
/*
* 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.
*/
<template>
<el-drawer
:title="$t('Version Info')"
:visible.sync="visible"
:with-header="false"
size=""
>
<!-- fix the bug that Element-ui(2.13.2) auto focus on the first input -->
<div style="width: 0px; height: 0px; overflow: hidden">
<el-input type="text" />
</div>
<div class="container">
<div class="versions-header">
<span class="name">{{ $t("Version Info") }}</span>
</div>
<div class="table-box" v-if="taskVersions.length > 0">
<el-table :data="taskVersions" size="mini" style="width: 100%">
<el-table-column
type="index"
:label="$t('#')"
width="50"
></el-table-column>
<el-table-column prop="userName" :label="$t('Version')">
<template slot-scope="scope">
<span v-if="scope.row.version">
<span
v-if="scope.row.version === taskRow.taskVersion"
style="color: green"
><strong
>V{{ scope.row.version }}
{{ $t("Current Version") }}</strong
></span
>
<span v-else>V{{ scope.row.version }}</span>
</span>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column
prop="description"
:label="$t('Description')"
></el-table-column>
<el-table-column :label="$t('Create Time')" min-width="120">
<template slot-scope="scope">
<span>{{ scope.row.updateTime | formatDate }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('Operation')" width="100">
<template slot-scope="scope">
<el-tooltip
:content="$t('Switch To This Version')"
placement="top"
>
<el-popconfirm
:confirmButtonText="$t('Confirm')"
:cancelButtonText="$t('Cancel')"
icon="el-icon-info"
iconColor="red"
:title="$t('Confirm Switch To This Version?')"
@onConfirm="swtichVersion(scope.row)"
>
<el-button
:disabled="
taskRow.processReleaseState === 'ONLINE' ||
scope.row.version === taskRow.taskVersion
"
type="primary"
size="mini"
icon="el-icon-warning"
circle
slot="reference"
></el-button>
</el-popconfirm>
</el-tooltip>
<el-tooltip :content="$t('Delete')" placement="top">
<el-popconfirm
:confirmButtonText="$t('Confirm')"
:cancelButtonText="$t('Cancel')"
icon="el-icon-info"
iconColor="red"
:title="$t('Delete?')"
@onConfirm="deleteVersion(scope.row)"
>
<el-button
:disabled="scope.row.version === taskRow.taskVersion"
type="danger"
size="mini"
icon="el-icon-delete"
circle
slot="reference"
></el-button>
</el-popconfirm>
</el-tooltip>
</template>
</el-table-column>
</el-table>
</div>
<div v-if="taskVersions.length === 0">
<m-no-data />
</div>
<div v-if="taskVersions.length > 0">
<div class="versions-footer">
<el-button size="mini" @click="close()">{{ $t("Cancel") }}</el-button>
<el-pagination
background
@current-change="changePageNo"
layout="prev, pager, next"
:total="total"
:page-size="pageSize"
>
</el-pagination>
</div>
</div>
</div>
</el-drawer>
</template>
<script>
import mNoData from '@/module/components/noData/noData'
import { mapActions } from 'vuex'
export default {
name: 'task-definition-versions',
data () {
return {
visible: false,
taskVersions: [],
pageNo: 1,
pageSize: 10,
total: 0
}
},
props: {
taskRow: Object
},
methods: {
...mapActions('dag', [
'getTaskVersions',
'switchTaskVersion',
'deleteTaskVersion'
]),
show () {
this.visible = true
},
close () {
this.visible = false
this.taskVersions = []
},
changePageNo (val) {
this.pageNo = val
this.reload()
},
reload () {
this.getTaskVersions({
taskCode: this.taskRow.taskCode,
pageNo: this.pageNo,
pageSize: this.pageSize
}).then((res) => {
this.taskVersions = res.totalList
this.total = res.total
})
},
swtichVersion (row) {
this.switchTaskVersion({ taskCode: row.code, version: row.version })
.then((res) => {
this.$message.success(res.msg)
this.$emit('reloadList')
this.close()
})
.catch((err) => {
this.$message.error(err.msg || '')
})
},
deleteVersion (row) {
this.deleteTaskVersion({ taskCode: row.code, version: row.version })
.then((res) => {
this.$message.success(res.msg)
this.$emit('reloadList')
this.close()
})
.catch((err) => {
this.$message.error(err.msg || '')
})
}
},
components: { mNoData },
watch: {
visible (bool, a, b) {
if (bool && this.taskRow) {
this.reload()
}
}
}
}
</script>
<style lang="scss" rel="stylesheet/scss">
.container {
width: 500px;
position: relative;
.versions-header {
height: 60px;
position: relative;
line-height: 60px;
.name {
font-size: 16px;
}
}
.versions-footer {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
border-top: 1px solid #dcdedc;
background: #fff;
display: flex;
padding: 20px;
align-items: center;
justify-content: flex-end;
.ans-page {
display: inline-block;
}
}
.table-box {
overflow-y: scroll;
height: calc(100vh - 61px);
padding-bottom: 60px;
}
}
</style>

309
dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/taskDefinition/index.vue

@ -18,12 +18,61 @@
<div class="task-definition" v-if="!isLoading"> <div class="task-definition" v-if="!isLoading">
<m-list-construction :title="$t('Task Definition')"> <m-list-construction :title="$t('Task Definition')">
<template slot="conditions"> <template slot="conditions">
<m-conditions @on-conditions="_onConditions" :taskTypeShow="true"> <m-conditions>
<template v-slot:button-group> <template v-slot:button-group>
<el-button size="mini" @click="createTask"> <el-button size="mini" @click="createTask">
{{ $t("Create task") }} {{ $t("Create task") }}
</el-button> </el-button>
</template> </template>
<template v-slot:search-group>
<div class="list">
<el-button
size="mini"
@click="_ckQuery"
icon="el-icon-search"
></el-button>
</div>
<div class="list">
<el-select
size="mini"
style="width: 140px"
:placeholder="$t('type')"
:value="tempParams.taskType"
@change="_onChangeTaskType"
clearable
>
<el-option
v-for="taskType in tasksTypeList"
:key="taskType"
:value="taskType"
:label="taskType"
>
</el-option>
</el-select>
</div>
<div class="list">
<el-input
v-model="tempParams.processName"
@keyup.enter.native="_ckQuery"
size="mini"
:placeholder="$t('Process Name')"
type="text"
style="width: 180px"
clearable
/>
</div>
<div class="list">
<el-input
v-model="tempParams.taskName"
@keyup.enter.native="_ckQuery"
size="mini"
:placeholder="$t('Task Name')"
type="text"
style="width: 180px"
clearable
/>
</div>
</template>
</m-conditions> </m-conditions>
</template> </template>
<template v-slot:content> <template v-slot:content>
@ -32,7 +81,10 @@
:tasksList="tasksList" :tasksList="tasksList"
@on-update="_onUpdate" @on-update="_onUpdate"
@editTask="editTask" @editTask="editTask"
@showDeleteModal="showDeleteModal"
@showMoveModal="showMoveModal"
@viewTaskDetail="viewTaskDetail" @viewTaskDetail="viewTaskDetail"
@viewTaskVersions="viewTaskVersions"
></m-list> ></m-list>
<div class="page-box"> <div class="page-box">
<el-pagination <el-pagination
@ -75,6 +127,21 @@
> >
</m-form-model> </m-form-model>
</el-drawer> </el-drawer>
<task-delete-modal
ref="taskDeleteModal"
:taskRow="deletingTaskRow"
@deleteTask="deleteTask"
/>
<task-move-modal
ref="taskMoveModal"
:taskRow="movingTaskRow"
@moveTask="moveTask"
/>
<version-drawer
ref="versionDrawer"
:taskRow="versionTaskRow"
@reloadList="_onUpdate"
/>
</div> </div>
</template> </template>
<script> <script>
@ -86,10 +153,11 @@
import { mapActions, mapMutations } from 'vuex' import { mapActions, mapMutations } from 'vuex'
import listUrlParamHandle from '@/module/mixin/listUrlParamHandle' import listUrlParamHandle from '@/module/mixin/listUrlParamHandle'
import mFormModel from '@/conf/home/pages/dag/_source/formModel/formModel.vue' import mFormModel from '@/conf/home/pages/dag/_source/formModel/formModel.vue'
/**
* tasksType
*/
import { tasksType } from '@/conf/home/pages/dag/_source/config.js' import { tasksType } from '@/conf/home/pages/dag/_source/config.js'
import TaskDeleteModal from './_source/taskDeleteModal.vue'
import TaskMoveModal from './_source/taskMoveModel.vue'
import VersionDrawer from './_source/versions.vue'
import _ from 'lodash'
const DEFAULT_NODE_DATA = { const DEFAULT_NODE_DATA = {
id: -1, id: -1,
@ -108,9 +176,14 @@
searchParams: { searchParams: {
pageSize: 10, pageSize: 10,
pageNo: 1, pageNo: 1,
searchVal: '', processName: '',
taskType: '', taskName: '',
userId: '' taskType: ''
},
tempParams: {
processName: '',
taskName: '',
taskType: ''
}, },
// whether the task config drawer is visible // whether the task config drawer is visible
taskDrawer: false, taskDrawer: false,
@ -119,7 +192,14 @@
// tasksType // tasksType
tasksTypeList, tasksTypeList,
// editing task definition // editing task definition
editingTask: null editingTask: null,
editingProcess: null,
// task to be deleted
deletingTaskRow: null,
// task ready to move
movingTaskRow: null,
// the current browse task
versionTaskRow: null
} }
}, },
mixins: [listUrlParamHandle], mixins: [listUrlParamHandle],
@ -127,8 +207,12 @@
...mapActions('dag', [ ...mapActions('dag', [
'getTaskDefinitionsList', 'getTaskDefinitionsList',
'genTaskCodeList', 'genTaskCodeList',
'saveTaskDefinition', 'saveTaskDefinitionWithUpstreams',
'updateTaskDefinition' 'updateTaskDefinition',
'deleteTaskDefinition',
'getTaskDefinition',
'moveTaskToProcess',
'deleteRelation'
]), ]),
...mapActions('dag', [ ...mapActions('dag', [
'getProcessList', 'getProcessList',
@ -142,7 +226,7 @@
'getAlarmGroupsAll' 'getAlarmGroupsAll'
]), ]),
/** /**
* Toggle task drawer * Toggle task form-model drawer
*/ */
showTaskDrawer () { showTaskDrawer () {
this.taskDrawer = true this.taskDrawer = true
@ -151,10 +235,16 @@
this.setIsDetails(false) this.setIsDetails(false)
this.taskDrawer = false this.taskDrawer = false
}, },
saveTask ({ item }) { /**
* Save task
*/
saveTask ({ item: taskDefinition, prevTasks, processCode }) {
const isEditing = !!this.editingTask const isEditing = !!this.editingTask
if (isEditing) { if (isEditing) {
this.updateTaskDefinition(item) this.updateTaskDefinition({
prevTasks: prevTasks,
taskDefinition: taskDefinition
})
.then((res) => { .then((res) => {
this.$message.success(res.msg) this.$message.success(res.msg)
this._onUpdate() this._onUpdate()
@ -172,13 +262,13 @@
return code return code
}) })
.then((code) => { .then((code) => {
return this.saveTaskDefinition({ return this.saveTaskDefinitionWithUpstreams({
taskDefinitionJson: [ taskDefinition: {
{ ...taskDefinition,
...item,
code code
} },
] prevTasks: prevTasks,
processDefinitionCode: processCode
}) })
}) })
.then((res) => { .then((res) => {
@ -191,20 +281,101 @@
}) })
} }
}, },
/**
* Show task creation modal
*/
createTask () { createTask () {
this.editingTask = null this.editingTask = null
this.nodeData.taskType = DEFAULT_NODE_DATA.taskType this.nodeData.taskType = DEFAULT_NODE_DATA.taskType
this.showTaskDrawer() this.showTaskDrawer()
}, },
editTask (task) { /**
this.editingTask = task * Show task edit modal
this.nodeData.id = task.code */
this.nodeData.taskType = task.taskType editTask (taskRow) {
this.getTaskDefinition(taskRow.taskCode).then((taskDefinition) => {
this.editingTask = {
...taskDefinition,
processCode: taskRow.processDefinitionCode
}
this.nodeData.id = taskDefinition.code
this.nodeData.taskType = taskDefinition.taskType
this.showTaskDrawer() this.showTaskDrawer()
})
}, },
viewTaskDetail (task) { /**
* Show task detail modal
*/
viewTaskDetail (taskRow) {
this.setIsDetails(true) this.setIsDetails(true)
this.editTask(task) this.editTask(taskRow)
},
/**
* Show delete task modal
*/
showDeleteModal (taskRow) {
this.deletingTaskRow = taskRow
if (this.$refs.taskDeleteModal) {
this.$refs.taskDeleteModal.show()
}
},
/**
* Show Move Modal
*/
showMoveModal (taskRow) {
this.movingTaskRow = taskRow
if (this.$refs.taskMoveModal) {
this.$refs.taskMoveModal.show()
}
},
/**
* Delete task
* @param {Boolean} completely Whether to delete the task completely
*/
deleteTask ({ completely, taskCode, processDefinitionCode }) {
const completelyDelete = this.deleteTaskDefinition
const deleteRelation = this.deleteRelation
const delRequest = completely ? completelyDelete : deleteRelation
const params = completely
? { taskCode }
: {
taskCode,
processDefinitionCode
}
delRequest(params)
.then((res) => {
this.$message.success(res.msg)
this.$refs.taskDeleteModal.close()
this.deletingTaskRow = null
this._onUpdate()
})
.catch((err) => {
this.$message.error(err.msg || '')
})
},
/**
* Move task to another workflow
*/
moveTask (params) {
this.moveTaskToProcess(params)
.then((res) => {
this.$message.success(res.msg)
this.$refs.taskMoveModal.close()
this.movingTaskRow = null
this._onUpdate()
})
.catch((err) => {
this.$message.error(err.msg || '')
})
},
/**
* ViewTaskVersion
*/
viewTaskVersions (taskRow) {
if (this.$refs.versionDrawer) {
this.versionTaskRow = taskRow
this.$refs.versionDrawer.show()
}
}, },
/** /**
* pageNo * pageNo
@ -216,25 +387,52 @@
this.searchParams.pageSize = val this.searchParams.pageSize = val
}, },
/** /**
* conditions * query tasks
*/ */
_onConditions (o) { _ckQuery (o) {
this.searchParams.searchVal = o.searchVal this.searchParams.processName = this.tempParams.processName
this.searchParams.taskType = o.taskType this.searchParams.taskType = this.tempParams.taskType
this.searchParams.taskName = this.tempParams.taskName
this.searchParams.pageNo = 1 this.searchParams.pageNo = 1
}, },
/**
* filter tasks by taskType
*/
_onChangeTaskType (val) {
this.tempParams.taskType = val
},
/** /**
* get task definition list * get task definition list
*/ */
_getList (flag) { _getList (flag) {
this.isLoading = !flag this.isLoading = !flag
this.getTaskDefinitionsList(this.searchParams) this.getTaskDefinitionsList({
pageNo: this.searchParams.pageNo,
pageSize: this.searchParams.pageSize,
taskType: this.searchParams.taskType,
searchTaskName: this.searchParams.taskName,
searchWorkflowName: this.searchParams.processName
})
.then((res) => { .then((res) => {
if (this.searchParams.pageNo > 1 && res.totalList.length === 0) { if (this.searchParams.pageNo > 1 && res.totalList.length === 0) {
this.searchParams.pageNo = this.searchParams.pageNo - 1 this.searchParams.pageNo = this.searchParams.pageNo - 1
} else { } else {
this.tasksList = [] this.tasksList = res.totalList.map((task) => {
this.tasksList = res.totalList const upstreamTaskMap = task.upstreamTaskMap || {}
const upstreamTasks = Object.keys(upstreamTaskMap).map((code) => {
return {
taskCode: code,
taskName: upstreamTaskMap[code]
}
})
return {
...task,
upstreamTasks,
upstreamTaskNames: upstreamTasks
.map((u) => u.taskName)
.join(',')
}
})
this.total = res.total this.total = res.total
this.isLoading = false this.isLoading = false
} }
@ -280,15 +478,23 @@
.catch(() => { .catch(() => {
this.isLoading = false this.isLoading = false
}) })
// Routing parameter merging
if (!_.isEmpty(this.$route.query)) {
this.tempParams.processName = this.$route.query.processName || ''
this.tempParams.taskType = this.$route.query.taskType || ''
this.tempParams.taskName = this.$route.query.taskName || ''
}
}, },
mounted () {},
components: { components: {
mListConstruction, mListConstruction,
mConditions, mConditions,
mList, mList,
mNoData, mNoData,
mSpin, mSpin,
mFormModel mFormModel,
TaskMoveModal,
TaskDeleteModal,
VersionDrawer
} }
} }
</script> </script>
@ -297,5 +503,40 @@
.taskGroupBtn { .taskGroupBtn {
width: 300px; width: 300px;
} }
::v-deep .table-box {
table {
tr {
th:first-child,
td:first-child {
text-align: left;
padding-left: 20px;
}
td:first-child span {
text-align: left;
}
}
td,
th.is-leaf {
padding-left: 10px;
}
}
.pre-task-tag {
margin-right: 10px;
max-width: 100px;
overflow: hidden;
text-overflow: ellipsis;
}
.upstream-tasks {
display: flex;
flex-wrap: wrap;
}
}
::v-deep .el-dialog__header {
.el-dialog__headerbtn {
right: 20px;
}
}
} }
</style> </style>

136
dolphinscheduler-ui/src/js/conf/home/store/dag/actions.js

@ -870,7 +870,7 @@ export default {
*/ */
deleteTaskDefinition ({ state }, payload) { deleteTaskDefinition ({ state }, payload) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
io.delete(`projects/${state.projectCode}/task-definition/${payload.code}`, payload, res => { io.delete(`projects/${state.projectCode}/task-definition/${payload.taskCode}`, res => {
resolve(res) resolve(res)
}).catch(e => { }).catch(e => {
reject(e) reject(e)
@ -891,15 +891,143 @@ export default {
}) })
}) })
}, },
updateTaskDefinition ({ state }, taskDefinition) { /**
* Save Task Definition with upstreams
* @param {Object} taskDefinition
* @param {number[]} prevTasks
* @param {number} processDefinitionCode
*/
saveTaskDefinitionWithUpstreams ({ state }, payload) {
return new Promise((resolve, reject) => {
io.post(`projects/${state.projectCode}/task-definition/save-single`, {
taskDefinitionJsonObj: JSON.stringify(payload.taskDefinition),
upstreamCodes: payload.prevTasks.join(','),
processDefinitionCode: payload.processDefinitionCode
}, res => {
resolve(res)
}).catch(e => {
reject(e)
})
})
},
/**
*
* @param {Object} taskDefinition
* @param {number[]} taskDefinition
* @returns
*/
updateTaskDefinition ({ state }, payload) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
io.put(`projects/${state.projectCode}/task-definition/${taskDefinition.code}`, { io.put(`projects/${state.projectCode}/task-definition/${payload.taskDefinition.code}/with-upstream`, {
taskDefinitionJsonObj: JSON.stringify(taskDefinition) taskDefinitionJsonObj: JSON.stringify(payload.taskDefinition),
upstreamCodes: payload.prevTasks.join(',')
}, res => { }, res => {
resolve(res) resolve(res)
}).catch(e => { }).catch(e => {
reject(e) reject(e)
}) })
}) })
},
/**
* Query taskDefinition by code
* @param {*} param0
*/
getTaskDefinition ({ state }, taskDefinitionCode) {
return new Promise((resolve, reject) => {
io.get(`projects/${state.projectCode}/task-definition/${taskDefinitionCode}`, res => {
resolve(res.data)
}).catch(e => {
reject(e)
})
})
},
/**
* Get process definition detail
* @param {numbetr} code
*/
getProcessDefinition ({ state }, code) {
return new Promise((resolve, reject) => {
io.get(`projects/${state.projectCode}/process-definition/${code}`, res => {
resolve(res.data)
}).catch(res => {
reject(res)
})
})
},
/**
* Move task
*/
moveTaskToProcess ({ state }, payload) {
return new Promise((resolve, reject) => {
io.post(`projects/${state.projectCode}/process-task-relation/move`, {
processDefinitionCode: payload.processDefinitionCode,
targetProcessDefinitionCode: payload.targetProcessDefinitionCode,
taskCode: payload.taskCode
}, res => {
resolve(res)
}).catch(e => {
reject(e)
})
})
},
/**
* Delete relation
*/
deleteRelation ({ state }, payload) {
return new Promise((resolve, reject) => {
io.delete(`projects/${state.projectCode}/process-task-relation/${payload.taskCode}`, {
processDefinitionCode: payload.processDefinitionCode
}, res => {
resolve(res)
}).catch(e => {
reject(e)
})
})
},
/**
* Query task versions
* @param {number} taskCode
* @param {number} pageNo
* @param {number} pageSize
*/
getTaskVersions ({ state }, payload) {
return new Promise((resolve, reject) => {
io.get(`projects/${state.projectCode}/task-definition/${payload.taskCode}/versions`, {
pageNo: payload.pageNo,
pageSize: payload.pageSize
}, res => {
resolve(res.data)
}).catch(res => {
reject(res)
})
})
},
/**
* Switch task version
* @param {number} taskCode
* @param {number} version
*/
switchTaskVersion ({ state }, payload) {
return new Promise((resolve, reject) => {
io.get(`projects/${state.projectCode}/task-definition/${payload.taskCode}/versions/${payload.version}`, res => {
resolve(res)
}).catch(res => {
reject(res)
})
})
},
/**
* Delete task version
* @param {number} taskCode
* @param {number} version
*/
deleteTaskVersion ({ state }, payload) {
return new Promise((resolve, reject) => {
io.delete(`projects/${state.projectCode}/task-definition/${payload.taskCode}/versions/${payload.version}`, res => {
resolve(res)
}).catch(e => {
reject(e)
})
})
} }
} }

39
dolphinscheduler-ui/src/js/module/components/conditions/conditions.vue

@ -42,24 +42,6 @@
> >
</el-input> </el-input>
</div> </div>
<div class="list" v-if="taskTypeShow">
<el-select
size="mini"
style="width: 140px"
:placeholder="$t('type')"
:value="taskType"
@change="_onChangeTaskType"
clearable
>
<el-option
v-for="(task, index) in taskTypeList"
:key="index"
:value="task.desc"
:label="index"
>
</el-option>
</el-select>
</div>
</template> </template>
</div> </div>
</div> </div>
@ -67,40 +49,24 @@
</template> </template>
<script> <script>
import _ from 'lodash' import _ from 'lodash'
/**
* taskType list
*/
import { tasksType } from '@/conf/home/pages/dag/_source/config.js'
export default { export default {
name: 'conditions', name: 'conditions',
data () { data () {
return { return {
// taskType list
taskTypeList: tasksType,
// search value // search value
searchVal: '', searchVal: ''
// taskType switch
taskType: ''
} }
}, },
props: { props: {
taskTypeShow: Boolean,
operation: Array operation: Array
}, },
methods: { methods: {
/**
* switch taskType
*/
_onChangeTaskType (val) {
this.taskType = val
},
/** /**
* emit Query parameter * emit Query parameter
*/ */
_ckQuery () { _ckQuery () {
this.$emit('on-conditions', { this.$emit('on-conditions', {
searchVal: _.trim(this.searchVal), searchVal: _.trim(this.searchVal)
taskType: this.taskType
}) })
} }
}, },
@ -114,7 +80,6 @@
// Routing parameter merging // Routing parameter merging
if (!_.isEmpty(this.$route.query)) { if (!_.isEmpty(this.$route.query)) {
this.searchVal = this.$route.query.searchVal || '' this.searchVal = this.$route.query.searchVal || ''
this.taskType = this.$route.query.taskType || ''
} }
}, },
components: {} components: {}

13
dolphinscheduler-ui/src/js/module/components/secondaryMenu/_source/menu.js

@ -54,6 +54,12 @@ const menu = {
enabled: true, enabled: true,
classNames: 'tab-process-definition' classNames: 'tab-process-definition'
}, },
{
name: `${i18n.$t('Task Definition')}`,
path: 'task-definition',
id: 5,
enabled: true
},
{ {
name: `${i18n.$t('Process Instance')}`, name: `${i18n.$t('Process Instance')}`,
path: 'instance', path: 'instance',
@ -78,17 +84,10 @@ const menu = {
path: 'history-task-record', path: 'history-task-record',
id: 4, id: 4,
enabled: config.recordSwitch enabled: config.recordSwitch
},
{
name: `${i18n.$t('Task Definition')}`,
path: 'task-definition',
id: 5,
enabled: true
} }
] ]
} }
], ],
security: [ security: [
{ {
name: `${i18n.$t('Tenant Manage')}`, name: `${i18n.$t('Tenant Manage')}`,

11
dolphinscheduler-ui/src/js/module/i18n/locale/en_US.js

@ -797,5 +797,14 @@ export default {
'Modify task group queue priority': 'Edit the priority of the task group queue', 'Modify task group queue priority': 'Edit the priority of the task group queue',
'Priority not empty': 'The value of priority can not be empty', 'Priority not empty': 'The value of priority can not be empty',
'Priority must be number': 'The value of priority should be number', 'Priority must be number': 'The value of priority should be number',
'Please select task name': 'Please select a task name' 'Please select task name': 'Please select a task name',
'Process State': 'Process State',
'Upstream Tasks': 'Upstream Tasks',
'and {n} more': ' and {n} more',
'Move task': 'Move task',
'Delete task {taskName} from process {processName}?': 'Delete task {taskName} from process {processName}?',
'Delete task completely': 'Delete task completely',
'Please select a process': 'Please select a process',
'Delete {taskName}?': 'Delete {taskName}?',
'Please select a process (required)': 'Please select a process (required)'
} }

11
dolphinscheduler-ui/src/js/module/i18n/locale/zh_CN.js

@ -799,5 +799,14 @@ export default {
'Force to start task': '强制启动', 'Force to start task': '强制启动',
'Priority not empty': '优先级不能为空', 'Priority not empty': '优先级不能为空',
'Priority must be number': '优先级必须是数值', 'Priority must be number': '优先级必须是数值',
'Please select task name': '请选择节点名称' 'Please select task name': '请选择节点名称',
'Process State': '工作流状态',
'Upstream Tasks': '上游任务',
'and {n} more': '{n}',
'Move task': '移动任务',
'Delete task {taskName} from process {processName}?': '将任务 {taskName} 从工作流 {processName} 中删除',
'Delete task completely': '彻底删除任务',
'Please select a process': '请选择工作流',
'Delete {taskName}?': '确定删除 {taskName} ?',
'Please select a process (required)': '请选择工作流必选'
} }

Loading…
Cancel
Save