Browse Source

[Feature-6600][UI] Implement page about task-definition (#6762)

* fix api error in dependent task

* feat: This is a connecting test

* feat: second push test

* feat: email test

* [Feature]add taskdefinition

add new pages named taskDifinition
add new router
add new action in store/dag

* [Feature] add/edit/view task definition

* [DS-6600][feat][UI] Add page about task-definition list

* Add page about task-definition list
* Add create/edit/view/delete task-definition
* Add research task-definition

This closes #6600

* modify API_BASE

Co-authored-by: wangyizhi <wangyizhi1_yewu@cmss.chinamobile.com>
Co-authored-by: duchao <duchao@cmss.chinamobile.com>
DuChaoJiaYou 3 years ago
parent
commit
36c19748a6

+ 2 - 2
dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/_source/referenceFromTask.vue

@@ -81,7 +81,7 @@
       document.removeEventListener('click', this.outsideClick)
     },
     methods: {
-      ...mapActions('dag', ['getTaskDefinitions']),
+      ...mapActions('dag', ['getTaskDefinitionList']),
       outsideClick (e) {
         const elem = this.$refs.copyFrom
         if (!elem.contains(e.target) && this.dropdownVisible) {
@@ -99,7 +99,7 @@
         }
         if (this.noMore) return
         this.loading = true
-        this.getTaskDefinitions({
+        this.getTaskDefinitionsList({
           pageNo: this.pageNo,
           pageSize: this.pageSize,
           searchVal: this.searchVal,

+ 91 - 34
dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/formModel.vue

@@ -46,7 +46,6 @@
     </div>
     <div class="content-box" v-if="isContentBox">
       <div class="form-model">
-
         <!-- Reference from task -->
         <!-- <reference-from-task :taskType="nodeData.taskType" /> -->
 
@@ -61,12 +60,34 @@
               :disabled="isDetails"
               :placeholder="$t('Please enter name (required)')"
               maxlength="100"
-              @blur="_verifName()"
             >
             </el-input>
           </div>
         </m-list-box>
 
+        <m-list-box v-if="fromTaskDefinition">
+          <div slot="text">{{ $t("Task Type") }}</div>
+          <div slot="content">
+            <el-select
+              @change="changeTaskType"
+              :value="nodeData.taskType"
+              :placeholder="$t('Please select a task type (required)')"
+              size="small"
+              style="width: 100%"
+              :disabled="isDetails"
+            >
+              <el-option
+                v-for="type in tasksTypeList"
+                :key="type"
+                :label="type"
+                :value="type"
+                :disabled="type === 'CONDITIONS' || type === 'SWITCH'"
+              >
+              </el-option>
+            </el-select>
+          </div>
+        </m-list-box>
+
         <!-- Running sign -->
         <m-list-box>
           <div slot="text">{{ $t("Run flag") }}</div>
@@ -353,13 +374,14 @@
             :backfill-item="backfillItem"
           >
           </m-datax>
-        <m-pigeon
-          v-if="nodeData.taskType === 'PIGEON'"
-          @on-params="_onParams"
-          @on-cache-params="_onCacheParams"
-          :backfill-item="backfillItem"
-          ref="PIGEON">
-        </m-pigeon>
+          <m-pigeon
+            v-if="nodeData.taskType === 'PIGEON'"
+            @on-params="_onParams"
+            @on-cache-params="_onCacheParams"
+            :backfill-item="backfillItem"
+            ref="PIGEON"
+          >
+          </m-pigeon>
           <m-sqoop
             v-if="nodeData.taskType === 'SQOOP'"
             @on-params="_onParams"
@@ -398,7 +420,7 @@
         <!-- Pre-tasks in workflow -->
         <m-pre-tasks
           ref="preTasks"
-          v-if="['SHELL', 'SUB_PROCESS'].indexOf(nodeData.taskType) > -1"
+          v-if="['SHELL', 'SUB_PROCESS'].indexOf(nodeData.taskType) > -1 && !fromTaskDefinition"
           :code="code"
         />
       </div>
@@ -415,7 +437,7 @@
           :loading="spinnerLoading"
           @click="ok()"
           :disabled="isDetails"
-          >{{ spinnerLoading ? $t("Loading...") : $t("Confirm add") }}
+          >{{ spinnerLoading ? $t("Loading...") : $t("Confirm") }}
         </el-button>
       </div>
     </div>
@@ -453,6 +475,7 @@
   import disabledState from '@/module/mixin/disabledState'
   import mPriority from '@/module/components/priority/priority'
   import { findComponentDownward } from '@/module/util/'
+  import { tasksType } from '@/conf/home/pages/dag/_source/config.js'
   // import ReferenceFromTask from './_source/referenceFromTask.vue'
 
   export default {
@@ -524,7 +547,8 @@
         // refresh part of the formModel, after set backfillItem outside
         backfillRefresh: true,
         // whether this is a new Task
-        isNewCreate: true
+        isNewCreate: true,
+        tasksTypeList: Object.keys(tasksType)
       }
     },
     provide () {
@@ -542,7 +566,8 @@
       type: {
         type: String,
         default: ''
-      }
+      },
+      taskDefinition: Object
     },
     inject: ['dagChart'],
     methods: {
@@ -723,6 +748,16 @@
         }
         return true
       },
+      _verifTaskType () {
+        if (!this.fromTaskDefinition) return true
+        if (!this.nodeData.taskType) {
+          this.$message.warning(
+            `${i18n.$t('Please select a task type (required)')}`
+          )
+          return false
+        }
+        return true
+      },
       /**
        * Global verification procedure
        */
@@ -731,6 +766,12 @@
         if (!this._verifName()) {
           return
         }
+
+        // Verify task type
+        if (!this._verifTaskType()) {
+          return
+        }
+
         // verif workGroup
         if (!this._verifWorkGroup()) {
           return
@@ -777,14 +818,11 @@
             timeoutNotifyStrategy: this.timeout.strategy,
             timeout: this.timeout.interval || 0,
             delayTime: this.delayTime,
-            environmentCode: this.environmentCode || -1,
-            status: this.status,
-            branch: this.branch
+            environmentCode: this.environmentCode || -1
           },
           fromThis: this
         })
-        // set run flag
-        this._setRunFlag()
+
         // set edge label
         this._setEdgeLabel()
       },
@@ -795,10 +833,8 @@
         this.name = name
       },
       /**
-       *  set run flag
-       *  TODO
+       *  set edge label by successBranch && failedBranch
        */
-      _setRunFlag () {},
       _setEdgeLabel () {
         if (this.successBranch || this.failedBranch) {
           const canvas = findComponentDownward(this.dagChart, 'dag-canvas')
@@ -892,7 +928,8 @@
           } else {
             this.workerGroup = o.workerGroup
           }
-          this.environmentCode = o.environmentCode === -1 ? '' : o.environmentCode
+          this.environmentCode =
+            o.environmentCode === -1 ? '' : o.environmentCode
           this.params = o.params || {}
           this.dependence = o.dependence || {}
           this.cacheDependence = o.dependence || {}
@@ -901,23 +938,37 @@
         }
         this.cacheBackfillItem = JSON.parse(JSON.stringify(o))
         this.isContentBox = true
+      },
+      changeTaskType (value) {
+        this.$emit('changeTaskType', value)
       }
     },
     created () {
       // Backfill data
-      let taskList = this.store.state.dag.tasks
       let o = {}
-      if (taskList.length) {
-        taskList.forEach((task) => {
-          if (task.code === this.nodeData.id) {
-            const backfillItem = this.taskToBackfillItem(task)
-            o = backfillItem
-            this.backfillItem = backfillItem
-            this.isNewCreate = false
-          }
-        })
-      }
       this.code = this.nodeData.id
+
+      if (this.fromTaskDefinition) {
+        if (this.taskDefinition) {
+          const backfillItem = this.taskToBackfillItem(this.taskDefinition)
+          o = backfillItem
+          this.backfillItem = backfillItem
+          this.isNewCreate = false
+        }
+      } else {
+        let taskList = this.store.state.dag.tasks
+        if (taskList.length) {
+          taskList.forEach((task) => {
+            if (task.code === this.nodeData.id) {
+              const backfillItem = this.taskToBackfillItem(task)
+              o = backfillItem
+              this.backfillItem = backfillItem
+              this.isNewCreate = false
+            }
+          })
+        }
+      }
+
       this.backfill(o)
 
       if (this.dagChart) {
@@ -958,6 +1009,12 @@
           )
         }
         return null
+      },
+      /**
+       * Open the modal from task definition
+       */
+      fromTaskDefinition () {
+        return this.type === 'task-definition'
       }
     },
     components: {
@@ -986,7 +1043,7 @@
       mWorkerGroups,
       mRelatedEnvironment,
       mPreTasks
-      // ReferenceFromTask
+    // ReferenceFromTask
     }
   }
 </script>

+ 197 - 0
dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/taskDefinition/_source/list.vue

@@ -0,0 +1,197 @@
+/*
+ * 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 class="list-model" style="position: relative">
+    <div class="table-box">
+      <el-table
+        :data="list"
+        size="mini"
+        style="width: 100%"
+        @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">
+          <template v-slot="scope">
+            <el-popover trigger="hover" placement="top">
+              <p>{{ scope.row.name }}</p>
+              <div slot="reference" class="name-wrapper">
+                <a
+                  href="javascript:"
+                  class="links"
+                  @click="viewTaskDetail(scope.row)"
+                >
+                  {{ scope.row.name }}
+                </a>
+              </div>
+            </el-popover>
+          </template>
+        </el-table-column>
+        <el-table-column :label="$t('Task Type')" prop="taskType" min-width="135">
+        </el-table-column>
+        <el-table-column
+          :label="$t('User Name')"
+          prop="userName"
+          width="135"
+        ></el-table-column>
+        <el-table-column :label="$t('Version Info')" min-width="135">
+          <template v-slot="scope">
+            <span>
+              {{ 'V' + scope.row.version }}
+            </span>
+          </template>
+        </el-table-column>
+        <el-table-column :label="$t('Create Time')" min-width="135">
+          <template v-slot="scope">
+            <span>
+              {{ scope.row.createTime | formatDate }}
+            </span>
+          </template>
+        </el-table-column>
+        <el-table-column :label="$t('Update Time')" min-width="135">
+          <template v-slot="scope">
+            <span>
+              {{ scope.row.updateTime | formateDate }}
+            </span>
+          </template>
+        </el-table-column>
+        <el-table-column :label="$t('Description')" min-width="100">
+          <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">
+            <el-tooltip
+              :content="$t('Edit')"
+              placement="top"
+              :enterable="false"
+            >
+              <span>
+                <el-button
+                  type="primary"
+                  size="mini"
+                  icon="el-icon-edit-outline"
+                  circle
+                  :disabled="scope.row.taskType === 'CONDITIONS' || scope.row.taskType ==='SWITCH' "
+                  @click="editTask(scope.row)"
+                ></el-button>
+              </span>
+            </el-tooltip>
+            <el-tooltip
+              :content="$t('Delete')"
+              placement="top"
+              :enterable="false"
+            >
+              <el-popconfirm
+                :confirmButtonText="$t('Confirm')"
+                :cancelButtonText="$t('Cancel')"
+                icon="el-icon-info"
+                iconColor="red"
+                :title="$t('Delete?')"
+                @onConfirm="deleteTask(scope.row.code, scope.row.projectCode)"
+              >
+                <el-button
+                  type="danger"
+                  size="mini"
+                  icon="el-icon-delete"
+                  slot="reference"
+                  circle
+                >
+                </el-button>
+              </el-popconfirm>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+  </div>
+</template>
+
+<script>
+  import _ from 'lodash'
+  import { mapActions } from 'vuex'
+
+  export default {
+    name: 'task-list',
+    data () {
+      return {
+        list: []
+      }
+    },
+    props: {
+      tasksList: Array
+    },
+    watch: {
+      tasksList: {
+        handler (a) {
+          this.list = []
+          setTimeout(() => {
+            this.list = _.cloneDeep(a)
+          })
+        },
+        immediate: true,
+        deep: true
+      }
+    },
+    created () {
+    // this.list = this.tasksList
+    },
+    methods: {
+      ...mapActions('dag', ['deleteTaskDefinition']),
+      /**
+       * onUpdate
+       */
+      _onUpdate () {
+        this.$emit('on-update')
+      },
+      /**
+       * deleteTaskDefinition
+       */
+      deleteTask (code) {
+        this.deleteTaskDefinition({
+          code: code
+        })
+          .then((res) => {
+            this._onUpdate()
+            this.$message.success(res.msg)
+          })
+          .catch((e) => {
+            this.$message.error(e.msg || '')
+          })
+      },
+      /**
+       * taskdefinition detail
+       */
+      viewTaskDetail (task) {
+        this.$emit('viewTaskDetail', task)
+      },
+      /**
+       * task edit
+       */
+      editTask (task) {
+        this.$emit('editTask', task)
+      }
+    }
+  }
+</script>
+
+<style>
+</style>

+ 307 - 0
dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/taskDefinition/index.vue

@@ -0,0 +1,307 @@
+/*
+ * 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 class="task-definition" v-if="!isLoading">
+    <m-list-construction :title="$t('Task Definition')">
+      <template slot="conditions">
+        <m-conditions @on-conditions="_onConditions" :taskTypeShow="true">
+          <template v-slot:button-group>
+            <el-button size="mini" @click="createTask">
+              {{ $t("Create task") }}
+            </el-button>
+          </template>
+        </m-conditions>
+      </template>
+      <template v-slot:content>
+        <template v-if="tasksList.length || total > 0">
+          <m-list
+            :tasksList="tasksList"
+            @on-update="_onUpdate"
+            @editTask="editTask"
+            @viewTaskDetail="viewTaskDetail"
+          ></m-list>
+          <div class="page-box">
+            <el-pagination
+              background
+              @current-change="_page"
+              @size-change="_pageSize"
+              :page-size="searchParams.pageSize"
+              :current-page.sync="searchParams.pageNo"
+              :page-sizes="[10, 30, 50]"
+              :total="total"
+              layout="sizes, prev, pager, next, jumper"
+            >
+            </el-pagination>
+          </div>
+        </template>
+        <template v-if="!tasksList.length">
+          <m-no-data></m-no-data>
+        </template>
+        <m-spin :is-spin="isLoading"></m-spin>
+      </template>
+    </m-list-construction>
+    <el-drawer
+      :visible.sync="taskDrawer"
+      size=""
+      :with-header="false"
+      @close="closeTaskDrawer"
+    >
+      <!-- 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>
+      <m-form-model
+        v-if="taskDrawer"
+        :nodeData="nodeData"
+        type="task-definition"
+        @changeTaskType="changeTaskType"
+        @close="closeTaskDrawer"
+        @addTaskInfo="saveTask"
+        :taskDefinition="editingTask"
+      >
+      </m-form-model>
+    </el-drawer>
+  </div>
+</template>
+<script>
+  import mListConstruction from '@/module/components/listConstruction/listConstruction'
+  import mConditions from '@/module/components/conditions/conditions'
+  import mList from './_source/list'
+  import mNoData from '@/module/components/noData/noData'
+  import mSpin from '@/module/components/spin/spin'
+  import { mapActions, mapMutations } from 'vuex'
+  import listUrlParamHandle from '@/module/mixin/listUrlParamHandle'
+  import mFormModel from '@/conf/home/pages/dag/_source/formModel/formModel.vue'
+  /**
+   * tasksType
+   */
+  import { tasksType } from '@/conf/home/pages/dag/_source/config.js'
+
+  const DEFAULT_NODE_DATA = {
+    id: -1,
+    taskType: 'SHELL',
+    instanceId: -1
+  }
+  export default {
+    name: 'task-definition-index',
+    data () {
+      // tasksType
+      const tasksTypeList = Object.keys(tasksType)
+      return {
+        total: null,
+        tasksList: [],
+        isLoading: true,
+        searchParams: {
+          pageSize: 10,
+          pageNo: 1,
+          searchVal: '',
+          taskType: '',
+          userId: ''
+        },
+        // whether the task config drawer is visible
+        taskDrawer: false,
+        // nodeData
+        nodeData: { ...DEFAULT_NODE_DATA },
+        // tasksType
+        tasksTypeList,
+        // editing task definition
+        editingTask: null
+      }
+    },
+    mixins: [listUrlParamHandle],
+    methods: {
+      ...mapActions('dag', [
+        'getTaskDefinitionsList',
+        'genTaskCodeList',
+        'saveTaskDefinition',
+        'updateTaskDefinition'
+      ]),
+      ...mapActions('dag', [
+        'getProcessList',
+        'getProjectList',
+        'getResourcesList',
+        'getResourcesListJar',
+        'getResourcesListJar'
+      ]),
+      ...mapMutations('dag', ['resetParams', 'setIsDetails']),
+      ...mapActions('security', [
+        'getTenantList',
+        'getWorkerGroupsAll',
+        'getAlarmGroupsAll'
+      ]),
+      /**
+       * Toggle task drawer
+       */
+      showTaskDrawer () {
+        this.taskDrawer = true
+      },
+      closeTaskDrawer () {
+        this.setIsDetails(false)
+        this.taskDrawer = false
+      },
+      saveTask ({ item }) {
+        const isEditing = !!this.editingTask
+        if (isEditing) {
+          this.updateTaskDefinition(item)
+            .then((res) => {
+              this.$message.success(res.msg)
+              this._onUpdate()
+              this.closeTaskDrawer()
+            })
+            .catch((e) => {
+              this.$message.error(e.msg || '')
+            })
+        } else {
+          this.genTaskCodeList({
+            genNum: 1
+          })
+            .then((res) => {
+              const [code] = res
+              return code
+            })
+            .then((code) => {
+              return this.saveTaskDefinition({
+                taskDefinitionJson: [
+                  {
+                    ...item,
+                    code
+                  }
+                ]
+              })
+            })
+            .then((res) => {
+              this.$message.success(res.msg)
+              this._onUpdate()
+              this.closeTaskDrawer()
+            })
+            .catch((e) => {
+              this.$message.error(e.msg || '')
+            })
+        }
+      },
+      createTask () {
+        this.editingTask = null
+        this.nodeData.taskType = DEFAULT_NODE_DATA.taskType
+        this.showTaskDrawer()
+      },
+      editTask (task) {
+        this.editingTask = task
+        this.nodeData.id = task.code
+        this.nodeData.taskType = task.taskType
+        this.showTaskDrawer()
+      },
+      viewTaskDetail (task) {
+        this.setIsDetails(true)
+        this.editTask(task)
+      },
+      /**
+       * pageNo
+       */
+      _page (val) {
+        this.searchParams.pageNo = val
+      },
+      _pageSize (val) {
+        this.searchParams.pageSize = val
+      },
+      /**
+       * conditions
+       */
+      _onConditions (o) {
+        this.searchParams.searchVal = o.searchVal
+        this.searchParams.taskType = o.taskType
+        this.searchParams.pageNo = 1
+      },
+      /**
+       * get task definition list
+       */
+      _getList (flag) {
+        this.isLoading = !flag
+        this.getTaskDefinitionsList(this.searchParams)
+          .then((res) => {
+            if (this.searchParams.pageNo > 1 && res.totalList.length === 0) {
+              this.searchParams.pageNo = this.searchParams.pageNo - 1
+            } else {
+              this.tasksList = []
+              this.tasksList = res.totalList
+              this.total = res.total
+              this.isLoading = false
+            }
+          })
+          .catch((e) => {
+            this.isLoading = false
+          })
+      },
+      /**
+       * update task dataList
+       */
+      _onUpdate () {
+        this._debounceGET('false')
+      },
+      /**
+       * change form modal task type
+       */
+      changeTaskType (value) {
+        this.nodeData.taskType = value
+      }
+    },
+    created () {
+      this.isLoading = true
+      // Initialization parameters
+      this.resetParams()
+      // Promise Get node needs data
+      Promise.all([
+        // get process definition
+        this.getProcessList(),
+        // get project
+        this.getProjectList(),
+        // get jar
+        this.getResourcesListJar(),
+        // get resource
+        this.getResourcesList(),
+        // get jar
+        this.getResourcesListJar(),
+        // get worker group list
+        this.getWorkerGroupsAll(),
+        // get alarm group list
+        this.getAlarmGroupsAll(),
+        this.getTenantList()
+      ])
+        .then((data) => {
+          this.isLoading = false
+        })
+        .catch(() => {
+          this.isLoading = false
+        })
+    },
+    mounted () {},
+    components: {
+      mListConstruction,
+      mConditions,
+      mList,
+      mNoData,
+      mSpin,
+      mFormModel
+    }
+  }
+</script>
+<style  lang="scss" scoped>
+.task-definition {
+  .taskGroupBtn {
+    width: 300px;
+  }
+}
+</style>

+ 9 - 0
dolphinscheduler-ui/src/js/conf/home/router/index.js

@@ -207,6 +207,15 @@ const router = new Router({
           }
 
         },
+        {
+          path: '/projects/:projectCode/task-definition',
+          name: 'task-definition',
+          component: resolve => require(['../pages/projects/pages/taskDefinition/index'], resolve),
+          meta: {
+            title: `${i18n.$t('Task Definition')}`,
+            refreshInSwitchedTab: config.refreshInSwitchedTab
+          }
+        },
         {
           path: '/projects/:projectCode/task-record',
           name: 'task-record',

+ 41 - 1
dolphinscheduler-ui/src/js/conf/home/store/dag/actions.js

@@ -836,7 +836,10 @@ export default {
       })
     })
   },
-  getTaskDefinitions ({ state }, payload) {
+  /**
+   * Query Task Definitions List Paging
+   */
+  getTaskDefinitionsList ({ state }, payload) {
     return new Promise((resolve, reject) => {
       io.get(`projects/${state.projectCode}/task-definition`, payload, res => {
         resolve(res.data)
@@ -844,5 +847,42 @@ export default {
         reject(e)
       })
     })
+  },
+  /**
+   * Delete Task Definition by code
+   */
+  deleteTaskDefinition ({ state }, payload) {
+    return new Promise((resolve, reject) => {
+      io.delete(`projects/${state.projectCode}/task-definition/${payload.code}`, payload, res => {
+        resolve(res)
+      }).catch(e => {
+        reject(e)
+      })
+    })
+  },
+  /**
+   * Save Task Definition
+   */
+  saveTaskDefinition ({ state }, payload) {
+    return new Promise((resolve, reject) => {
+      io.post(`projects/${state.projectCode}/task-definition`, {
+        taskDefinitionJson: JSON.stringify(payload.taskDefinitionJson)
+      }, res => {
+        resolve(res)
+      }).catch(e => {
+        reject(e)
+      })
+    })
+  },
+  updateTaskDefinition ({ state }, taskDefinition) {
+    return new Promise((resolve, reject) => {
+      io.put(`projects/${state.projectCode}/task-definition/${taskDefinition.code}`, {
+        taskDefinitionJsonObj: JSON.stringify(taskDefinition)
+      }, res => {
+        resolve(res)
+      }).catch(e => {
+        reject(e)
+      })
+    })
   }
 }

+ 51 - 9
dolphinscheduler-ui/src/js/module/components/conditions/conditions.vue

@@ -24,17 +24,42 @@
         <slot name="search-group" v-if="isShow"></slot>
         <template v-if="!isShow">
           <div class="list">
-            <el-button size="mini" @click="_ckQuery" icon="el-icon-search"></el-button>
+            <el-button
+              size="mini"
+              @click="_ckQuery"
+              icon="el-icon-search"
+            ></el-button>
           </div>
           <div class="list">
-            <el-input v-model="searchVal"
-                     @keyup.enter="_ckQuery"
-                     size="mini"
-                     :placeholder="$t('Please enter keyword')"
-                     type="text"
-                     style="width:180px;">
+            <el-input
+              v-model="searchVal"
+              @keyup.enter="_ckQuery"
+              size="mini"
+              :placeholder="$t('Please enter keyword')"
+              type="text"
+              style="width: 180px"
+              clearable
+            >
             </el-input>
           </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>
       </div>
     </div>
@@ -42,24 +67,40 @@
 </template>
 <script>
   import _ from 'lodash'
+  /**
+   * taskType list
+   */
+  import { tasksType } from '@/conf/home/pages/dag/_source/config.js'
   export default {
     name: 'conditions',
     data () {
       return {
+        // taskType list
+        taskTypeList: tasksType,
         // search value
-        searchVal: ''
+        searchVal: '',
+        // taskType switch
+        taskType: ''
       }
     },
     props: {
+      taskTypeShow: Boolean,
       operation: Array
     },
     methods: {
+      /**
+       * switch taskType
+       */
+      _onChangeTaskType (val) {
+        this.taskType = val
+      },
       /**
        * emit Query parameter
        */
       _ckQuery () {
         this.$emit('on-conditions', {
-          searchVal: _.trim(this.searchVal)
+          searchVal: _.trim(this.searchVal),
+          taskType: this.taskType
         })
       }
     },
@@ -73,6 +114,7 @@
       // Routing parameter merging
       if (!_.isEmpty(this.$route.query)) {
         this.searchVal = this.$route.query.searchVal || ''
+        this.taskType = this.$route.query.taskType || ''
       }
     },
     components: {}

+ 6 - 0
dolphinscheduler-ui/src/js/module/components/secondaryMenu/_source/menu.js

@@ -76,6 +76,12 @@ const menu = {
           path: 'history-task-record',
           id: 4,
           enabled: config.recordSwitch
+        },
+        {
+          name: `${i18n.$t('Task Definition')}`,
+          path: 'task-definition',
+          id: 5,
+          enabled: true
         }
       ]
     }

+ 5 - 1
dolphinscheduler-ui/src/js/module/i18n/locale/en_US.js

@@ -711,6 +711,9 @@ export default {
   'The condition content cannot be empty': 'The condition content cannot be empty',
   'Reference from': 'Reference from',
   'No more...': 'No more...',
+  'Task Definition': 'Task Definition',
+  'Create task': 'Create task',
+  'Task Type': 'Task Type',
   'Process execute type': 'Process execute type',
   parallel: 'parallel',
   'Serial wait': 'Serial wait',
@@ -744,5 +747,6 @@ export default {
   agentId: 'AgentId',
   users: 'Users',
   Username: 'Username',
-  showType: 'Show Type'
+  showType: 'Show Type',
+  'Please select a task type (required)': 'Please select a task type (required)'
 }

+ 5 - 1
dolphinscheduler-ui/src/js/module/i18n/locale/zh_CN.js

@@ -711,6 +711,9 @@ export default {
   'The condition content cannot be empty': '条件内容不能为空',
   'Reference from': '使用已有任务',
   'No more...': '没有更多了...',
+  'Task Definition': '任务定义',
+  'Create task': '创建任务',
+  'Task Type': '任务类型',
   'Process execute type': '执行策略',
   parallel: '并行',
   'Serial wait': '串行等待',
@@ -745,5 +748,6 @@ export default {
   agentId: '应用ID',
   users: '群员',
   Username: '用户名',
-  showType: '内容展示类型'
+  showType: '内容展示类型',
+  'Please select a task type (required)': '请选择任务类型(必选)'
 }