This is an automated email from the ASF dual-hosted git repository.

lgcareer pushed a commit to branch dev-resource-tree
in repository https://gitbox.apache.org/repos/asf/incubator-dolphinscheduler.git


The following commit(s) were added to refs/heads/dev-resource-tree by this push:
     new 840dde3  UDF resource tree and change DAG style (#2019)
840dde3 is described below

commit 840dde3daaa9c6534a9184bbe8146941ee4d3641
Author: break60 <[email protected]>
AuthorDate: Wed Feb 26 13:44:42 2020 +0800

    UDF resource tree and change DAG style (#2019)
    
    * File management list modification
    
    * Shell task resources and authorization resources
    
    * Spark task resource changes
    
    * UDF resource tree and change DAG style
---
 .../src/js/conf/home/pages/dag/_source/dag.vue     |   6 +-
 .../pages/dag/_source/formModel/tasks/shell.vue    |  12 +-
 .../pages/dag/_source/formModel/tasks/spark.vue    |  12 +-
 .../home/pages/dag/_source/plugIn/jsPlumbHandle.js |   4 +-
 .../pages/file/pages/createUdfFolder/index.vue     | 131 +++++++++
 .../pages/file/pages/subdirectory/index.vue        |   1 +
 .../pages/udf/pages/createUdfFolder/index.vue      | 128 +++++++++
 .../pages/udf/pages/function/_source/createUdf.vue |  49 ++--
 .../resource/pages/udf/pages/function/index.vue    |   5 +-
 .../pages/udf/pages/resource/_source/list.vue      |  17 +-
 .../resource/pages/udf/pages/resource/index.vue    |   6 +-
 .../{resource => subUdfDirectory}/_source/list.vue |  17 +-
 .../udf/pages/subUdfDirectory/_source/rename.vue   | 119 ++++++++
 .../pages/{resource => subUdfDirectory}/index.vue  |  12 +-
 .../pages/udf/pages/subUdfFolder/index.vue         | 128 +++++++++
 .../pages/security/pages/users/_source/list.vue    |  39 +--
 .../src/js/conf/home/router/index.js               |  32 ++-
 .../components/fileUpdate/resourceChildUpdate.vue  | 318 +++++++++++++++++++++
 .../src/js/module/components/nav/nav.vue           |  41 +++
 .../components/secondaryMenu/_source/menu.js       |   4 +-
 .../src/js/module/components/transfer/resource.vue |  23 +-
 21 files changed, 1042 insertions(+), 62 deletions(-)

diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/dag.vue 
b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/dag.vue
index 40b6d85..34f423d 100644
--- a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/dag.vue
+++ b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/dag.vue
@@ -177,7 +177,7 @@
           Endpoint: [
             'Dot', { radius: 1, cssClass: 'dot-style' }
           ],
-          Connector: 'Straight',
+          Connector: 'Bezier',
           PaintStyle: { lineWidth: 2, stroke: '#456' }, // Connection style
           ConnectionOverlays: [
             [
@@ -185,7 +185,7 @@
               {
                 location: 1,
                 id: 'arrow',
-                length: 12,
+                length: 1,
                 foldback: 0.8
               }
             ]
@@ -553,7 +553,7 @@
           Endpoint: [
             'Dot', { radius: 1, cssClass: 'dot-style' }
           ],
-          Connector: 'Straight',
+          Connector: 'Bezier',
           PaintStyle: { lineWidth: 2, stroke: '#456' }, // Connection style
           ConnectionOverlays: [
             [
diff --git 
a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/tasks/shell.vue
 
b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/tasks/shell.vue
index c490405..7bc78e4 100644
--- 
a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/tasks/shell.vue
+++ 
b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/tasks/shell.vue
@@ -34,7 +34,7 @@
     <m-list-box>
       <div slot="text">{{$t('Resources')}}</div>
       <div slot="content">
-        <treeselect v-model="resourceList" :multiple="true" :options="options" 
:normalizer="normalizer">
+        <treeselect v-model="resourceList" :multiple="true" :options="options" 
:normalizer="normalizer" :placeholder="$t('Please select resources')">
           <div slot="value-label" slot-scope="{ node }">{{ node.raw.fullName 
}}</div>
         </treeselect>
       </div>
@@ -205,6 +205,12 @@
         editor.setValue(this.rawScript)
 
         return editor
+      },
+      diGuiTree(item) {  // Recursive convenience tree structure
+        item.forEach(item => {
+          item.children === '' || item.children === undefined || item.children 
=== null || item.children.length === 0?        
+            delete item.children : this.diGuiTree(item.children);
+        })
       }
     },
     watch: {
@@ -223,7 +229,9 @@
       }
     },
     created () {
-      this.options = this.store.state.dag.resourcesListS
+      let item = this.store.state.dag.resourcesListS
+      this.diGuiTree(item)
+      this.options = item
       let o = this.backfillItem
       // Non-null objects represent backfill
       if (!_.isEmpty(o)) {
diff --git 
a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/tasks/spark.vue
 
b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/tasks/spark.vue
index 451c15e..915f64d 100644
--- 
a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/tasks/spark.vue
+++ 
b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/tasks/spark.vue
@@ -179,7 +179,7 @@
     <m-list-box>
       <div slot="text">{{$t('Resources')}}</div>
       <div slot="content">
-        <treeselect v-model="resourceList" :multiple="true" 
:options="mainJarList" :normalizer="normalizer">
+        <treeselect v-model="resourceList" :multiple="true" 
:options="mainJarList" :normalizer="normalizer" :placeholder="$t('Please select 
resources')">
           <div slot="value-label" slot-scope="{ node }">{{ node.raw.fullName 
}}</div>
         </treeselect>
       </div>
@@ -287,6 +287,12 @@
       _onCacheResourcesData (a) {
         this.cacheResourceList = a
       },
+      diGuiTree(item) {  // Recursive convenience tree structure
+        item.forEach(item => {
+          item.children === '' || item.children === undefined || item.children 
=== null || item.children.length === 0?        
+            delete item.children : this.diGuiTree(item.children);
+        })
+      },
       /**
        * verification
        */
@@ -408,7 +414,9 @@
       }
     },
     created () {
-        this.mainJarList = this.store.state.dag.resourcesListS
+        let item = this.store.state.dag.resourcesListS
+        this.diGuiTree(item)
+        this.mainJarList = item
         let o = this.backfillItem
 
         // Non-null objects represent backfill
diff --git 
a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/jsPlumbHandle.js
 
b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/jsPlumbHandle.js
index 6523a1c..bcc05f1 100644
--- 
a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/jsPlumbHandle.js
+++ 
b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/jsPlumbHandle.js
@@ -66,7 +66,7 @@ JSP.prototype.init = function ({ dag, instance }) {
   // Register jsplumb connection type and configuration
   this.JspInstance.registerConnectionType('basic', {
     anchor: 'Continuous',
-    connector: 'Straight' // Line type
+    connector: 'Bezier' // Line type
   })
 
   // Initial configuration
@@ -232,7 +232,7 @@ JSP.prototype.initNode = function (el) {
     filter: '.ep',
     anchor: 'Continuous',
     connectorStyle: {
-      stroke: '#555',
+      stroke: '#2d8cf0',
       strokeWidth: 2,
       outlineStroke: 'transparent',
       outlineWidth: 4
diff --git 
a/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/file/pages/createUdfFolder/index.vue
 
b/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/file/pages/createUdfFolder/index.vue
new file mode 100755
index 0000000..2511452
--- /dev/null
+++ 
b/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/file/pages/createUdfFolder/index.vue
@@ -0,0 +1,131 @@
+/*
+ * 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>
+  <m-list-construction :title="$t('Create folder')">
+    <template slot="content">
+      <div class="resource-create-model">
+        <m-list-box-f>
+          <template slot="name"><strong>*</strong>{{$t('Folder 
Name')}}</template>
+          <template slot="content">
+            <x-input
+                    type="input"
+                    v-model="name"
+                    maxlength="60"
+                    style="width: 300px;"
+                    :placeholder="$t('Please enter name')"
+                    autocomplete="off">
+            </x-input>
+          </template>
+        </m-list-box-f>
+        <m-list-box-f>
+          <template slot="name">{{$t('Description')}}</template>
+          <template slot="content">
+            <x-input
+                    type="textarea"
+                    v-model="description"
+                    style="width: 430px;"
+                    :placeholder="$t('Please enter description')"
+                    autocomplete="off">
+            </x-input>
+          </template>
+        </m-list-box-f>
+        <m-list-box-f>
+          <template slot="name">&nbsp;</template>
+          <template slot="content">
+            <div class="submit">
+              <x-button type="primary" shape="circle" 
:loading="spinnerLoading" @click="ok()">{{spinnerLoading ? 'Loading...' : 
$t('Create')}} </x-button>
+              <x-button type="text" @click="() => $router.push({name: 
'resource-udf'})"> {{$t('Cancel')}} </x-button>
+            </div>
+          </template>
+        </m-list-box-f>
+      </div>
+    </template>
+  </m-list-construction>
+</template>
+<script>
+  import i18n from '@/module/i18n'
+  import { mapActions } from 'vuex'
+  import { folderList } from '../_source/common'
+  import { handlerSuffix } from '../details/_source/utils'
+  import mListBoxF from '@/module/components/listBoxF/listBoxF'
+  import mSpin from '@/module/components/spin/spin'
+  import mConditions from '@/module/components/conditions/conditions'
+  import localStore from '@/module/util/localStorage'
+  import mListConstruction from 
'@/module/components/listConstruction/listConstruction'
+
+  export default {
+    name: 'resource-list-create-udf',
+    data () {
+      return {
+        type: '',
+        name: '',
+        description: '',
+        folderList: folderList,
+        spinnerLoading: false
+      }
+    },
+    props: {},
+    methods: {
+      ...mapActions('resource', ['createResourceFolder']),
+      ok () {
+        if (this._validation()) {
+          this.spinnerLoading = true
+          this.createResourceFolder({
+            type: 'UDF',
+            name: this.name,
+            currentDir: '/',
+            pid: -1,
+            description: this.description
+          }).then(res => {
+            this.$message.success(res.msg)
+            setTimeout(() => {
+              this.spinnerLoading = false
+              this.$router.push({ path: `/resource/udf/resource`})
+            }, 800)
+          }).catch(e => {
+            this.$message.error(e.msg || '')
+            this.spinnerLoading = false
+          })
+        }
+      },
+      _validation () {
+        if (!this.name) {
+          this.$message.warning(`${i18n.$t('Please enter resource folder 
name')}`)
+          return false
+        }
+
+        return true
+      },
+    },
+    watch: {},
+    created () {
+    },
+    mounted () {
+      this.$modal.destroy()
+    },
+    destroyed () {
+    },
+    computed: {},
+    components: { mListConstruction, mConditions, mSpin, mListBoxF }
+  }
+</script>
+
+<style lang="scss" rel="stylesheet/scss">
+  .resource-create-model {
+    padding: 30px;
+  }
+</style>
diff --git 
a/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/file/pages/subdirectory/index.vue
 
b/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/file/pages/subdirectory/index.vue
index 0cf7528..86d1e87 100755
--- 
a/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/file/pages/subdirectory/index.vue
+++ 
b/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/file/pages/subdirectory/index.vue
@@ -23,6 +23,7 @@
             <x-button type="ghost" @click="() => $router.push({path: 
`/resource/file/subFileFolder/${searchParams.id}`})">{{$t('Create 
folder')}}</x-button>
             <x-button type="ghost" @click="() => $router.push({path: 
`/resource/file/subFile/${searchParams.id}`})">{{$t('Create File')}}</x-button>
             <x-button type="ghost" @click="_uploading">{{$t('Upload 
Files')}}</x-button>
+            <a style="padding: 7px 0;line-height: 14px;position: 
relative;float: left;cursor: pointer;" @click="() => $router.push({path: 
`/resource/file`})">全部文件</a>
           </x-button-group>
         </template>
       </m-conditions>
diff --git 
a/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/createUdfFolder/index.vue
 
b/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/createUdfFolder/index.vue
new file mode 100755
index 0000000..fe7e077
--- /dev/null
+++ 
b/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/createUdfFolder/index.vue
@@ -0,0 +1,128 @@
+/*
+ * 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>
+  <m-list-construction :title="$t('Create folder')">
+    <template slot="content">
+      <div class="resource-create-model">
+        <m-list-box-f>
+          <template slot="name"><strong>*</strong>{{$t('Folder 
Name')}}</template>
+          <template slot="content">
+            <x-input
+                    type="input"
+                    v-model="name"
+                    maxlength="60"
+                    style="width: 300px;"
+                    :placeholder="$t('Please enter name')"
+                    autocomplete="off">
+            </x-input>
+          </template>
+        </m-list-box-f>
+        <m-list-box-f>
+          <template slot="name">{{$t('Description')}}</template>
+          <template slot="content">
+            <x-input
+                    type="textarea"
+                    v-model="description"
+                    style="width: 430px;"
+                    :placeholder="$t('Please enter description')"
+                    autocomplete="off">
+            </x-input>
+          </template>
+        </m-list-box-f>
+        <m-list-box-f>
+          <template slot="name">&nbsp;</template>
+          <template slot="content">
+            <div class="submit">
+              <x-button type="primary" shape="circle" 
:loading="spinnerLoading" @click="ok()">{{spinnerLoading ? 'Loading...' : 
$t('Create')}} </x-button>
+              <x-button type="text" @click="() => $router.push({name: 
'resource-udf'})"> {{$t('Cancel')}} </x-button>
+            </div>
+          </template>
+        </m-list-box-f>
+      </div>
+    </template>
+  </m-list-construction>
+</template>
+<script>
+  import i18n from '@/module/i18n'
+  import { mapActions } from 'vuex'
+  import mListBoxF from '@/module/components/listBoxF/listBoxF'
+  import mSpin from '@/module/components/spin/spin'
+  import mConditions from '@/module/components/conditions/conditions'
+  import localStore from '@/module/util/localStorage'
+  import mListConstruction from 
'@/module/components/listConstruction/listConstruction'
+
+  export default {
+    name: 'resource-list-create-udf',
+    data () {
+      return {
+        type: '',
+        name: '',
+        description: '',
+        spinnerLoading: false
+      }
+    },
+    props: {},
+    methods: {
+      ...mapActions('resource', ['createResourceFolder']),
+      ok () {
+        if (this._validation()) {
+          this.spinnerLoading = true
+          this.createResourceFolder({
+            type: 'UDF',
+            name: this.name,
+            currentDir: '/',
+            pid: -1,
+            description: this.description
+          }).then(res => {
+            this.$message.success(res.msg)
+            setTimeout(() => {
+              this.spinnerLoading = false
+              this.$router.push({ path: `/resource/udf/resource`})
+            }, 800)
+          }).catch(e => {
+            this.$message.error(e.msg || '')
+            this.spinnerLoading = false
+          })
+        }
+      },
+      _validation () {
+        if (!this.name) {
+          this.$message.warning(`${i18n.$t('Please enter resource folder 
name')}`)
+          return false
+        }
+
+        return true
+      },
+    },
+    watch: {},
+    created () {
+    },
+    mounted () {
+      this.$modal.destroy()
+    },
+    destroyed () {
+    },
+    computed: {},
+    components: { mListConstruction, mConditions, mSpin, mListBoxF }
+  }
+</script>
+
+<style lang="scss" rel="stylesheet/scss">
+  .resource-create-model {
+    padding: 30px;
+  }
+</style>
diff --git 
a/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/function/_source/createUdf.vue
 
b/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/function/_source/createUdf.vue
index 01d8d22..9805434 100644
--- 
a/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/function/_source/createUdf.vue
+++ 
b/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/function/_source/createUdf.vue
@@ -72,19 +72,9 @@
         <m-list-box-f>
           <template slot="name"><strong>*</strong>{{$t('UDF 
Resources')}}</template>
           <template slot="content">
-            <x-select
-                    filterable
-                    v-model="resourceId"
-                    :disabled="isUpdate"
-                    :add-title="true"
-                    style="width: 261px">
-              <x-option
-                      v-for="city in udfResourceList"
-                      :key="city.id"
-                      :value="city.id"
-                      :label="city.alias">
-              </x-option>
-            </x-select>
+            <treeselect style="width:260px;float:left;" v-model="resourceId" 
:disable-branch-nodes="true" :options="udfResourceList" :disabled="isUpdate" 
:normalizer="normalizer" :placeholder="$t('Please select resources')">
+              <div slot="value-label" slot-scope="{ node }">{{ 
node.raw.fullName }}</div>
+            </treeselect>
             <x-button type="primary" @click="_toggleUpdate" 
:disabled="upDisabled">{{$t('Upload Resources')}}</x-button>
           </template>
         </m-list-box-f>
@@ -115,6 +105,8 @@
   import _ from 'lodash'
   import i18n from '@/module/i18n'
   import store from '@/conf/home/store'
+  import Treeselect from '@riophae/vue-treeselect'
+  import '@riophae/vue-treeselect/dist/vue-treeselect.css'
   import mPopup from '@/module/components/popup/popup'
   import mListBoxF from '@/module/components/listBoxF/listBoxF'
   import mUdfUpdate from '@/module/components/fileUpdate/udfUpdate'
@@ -130,10 +122,15 @@
         argTypes: '',
         database: '',
         description: '',
-        resourceId: '',
+        resourceId: null,
         udfResourceList: [],
         isUpdate: false,
-        upDisabled: false
+        upDisabled: false,
+        normalizer(node) {
+          return {
+            label: node.name
+          }
+        },
       }
     },
     props: {
@@ -198,11 +195,19 @@
       _getUdfList () {
         return new Promise((resolve, reject) => {
           this.store.dispatch('resource/getResourcesList', { type: 'UDF' 
}).then(res => {
-            this.udfResourceList = res.data
+            let item = res.data
+            this.diGuiTree(item)
+            this.udfResourceList = item
             resolve()
           })
         })
       },
+      diGuiTree(item) {  // Recursive convenience tree structure
+        item.forEach(item => {
+          item.children === '' || item.children === undefined || item.children 
=== null || item.children.length === 0?        
+            delete item.children : this.diGuiTree(item.children);
+        })
+      },
       /**
        * Upload udf resources
        */
@@ -257,8 +262,7 @@
         })
       }
     },
-    watch: {
-    },
+    watch: {},
     created () {
       this._getUdfList().then(res => {
         // edit
@@ -271,13 +275,18 @@
           this.description = this.item.description || ''
           this.resourceId = this.item.resourceId
         } else {
-          this.resourceId = this.udfResourceList.length && 
this.udfResourceList[0].id || ''
+          this.resourceId = null
         }
       })
     },
     mounted () {
 
     },
-    components: { mPopup, mListBoxF, mUdfUpdate }
+    components: { mPopup, mListBoxF, mUdfUpdate, Treeselect }
   }
 </script>
+<style lang="scss" rel="stylesheet/scss">
+  .vue-treeselect__control {
+    height: 32px;
+  }
+</style>
diff --git 
a/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/function/index.vue
 
b/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/function/index.vue
index df46d7f..d6c79bd 100644
--- 
a/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/function/index.vue
+++ 
b/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/function/index.vue
@@ -19,7 +19,9 @@
     <template slot="conditions">
       <m-conditions @on-conditions="_onConditions">
         <template slot="button-group">
-          <x-button type="ghost" @click="_create"  size="small" >{{$t('Create 
UDF Function')}}</x-button>
+          <x-button-group size="small">
+            <x-button type="ghost" @click="_create">{{$t('Create UDF 
Function')}}</x-button>
+          </x-button-group>
         </template>
       </m-conditions>
     </template>
@@ -58,6 +60,7 @@
         isLoading: false,
         udfFuncList: [],
         searchParams: {
+          id: -1,
           pageSize: 10,
           pageNo: 1,
           searchVal: ''
diff --git 
a/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/resource/_source/list.vue
 
b/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/resource/_source/list.vue
index 1f72e51..ddb097e 100644
--- 
a/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/resource/_source/list.vue
+++ 
b/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/resource/_source/list.vue
@@ -26,6 +26,9 @@
             <span>{{$t('UDF Resource Name')}}</span>
           </th>
           <th scope="col">
+            <span>{{$t('Whether directory')}}</span>
+          </th>
+          <th scope="col">
             <span>{{$t('File Name')}}</span>
           </th>
           <th scope="col" width="80">
@@ -50,9 +53,12 @@
           </td>
           <td>
             <span class="ellipsis" v-tooltip.large.top.start.light="{text: 
item.alias, maxWidth: '500px'}">
-              <a href="javascript:" class="links" >{{item.alias}}</a>
+              <a href="javascript:" class="links" 
@click="_go(item)">{{item.alias}}</a>
             </span>
           </td>
+          <td>
+            <span>{{item.directory? $t('Yes') : $t('No')}}</span>
+          </td>
           <td><span class="ellipsis" v-tooltip.large.top.start.light="{text: 
item.fileName, maxWidth: '500px'}">{{item.fileName}}</span></td>
           <td>
             <span>{{_rtSize(item.size)}}</span>
@@ -85,6 +91,7 @@
                     size="xsmall"
                     data-toggle="tooltip"
                     :title="$t('Download')"
+                    :disabled="item.directory? true: false"
                     icon="ans-icon-download"
                     @click="_downloadFile(item)">
             </x-button>
@@ -120,6 +127,7 @@
   import mRename from './rename'
   import { downloadFile } from '@/module/download'
   import { bytesToSize } from '@/module/util/util'
+  import localStore from '@/module/util/localStorage'
 
   export default {
     name: 'udf-manage-list',
@@ -140,6 +148,13 @@
           id: item.id
         })
       },
+      _go (item) {
+        localStore.setItem('file', `${item.alias}|${item.size}`)
+        if(item.directory) {
+          localStore.setItem('currentDir', `${item.fullName}`)
+          this.$router.push({ path: `/resource/udf/subUdfDirectory/${item.id}` 
})
+        }
+      },
       _rtSize (val) {
         return bytesToSize(parseInt(val))
       },
diff --git 
a/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/resource/index.vue
 
b/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/resource/index.vue
index 732c5a2..b87b178 100644
--- 
a/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/resource/index.vue
+++ 
b/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/resource/index.vue
@@ -19,7 +19,10 @@
     <template slot="conditions">
       <m-conditions @on-conditions="_onConditions">
         <template slot="button-group">
-          <x-button type="ghost" size="small"  
@click="_uploading">{{$t('Upload UDF Resources')}}</x-button>
+          <x-button-group size="small">
+            <x-button type="ghost"  @click="() => this.$router.push({name: 
'resource-udf-createUdfFolder'})">{{$t('Create folder')}}</x-button>
+            <x-button type="ghost" size="small"  
@click="_uploading">{{$t('Upload UDF Resources')}}</x-button>
+          </x-button-group>
         </template>
       </m-conditions>
     </template>
@@ -58,6 +61,7 @@
         isLoading: false,
         udfResourcesList: [],
         searchParams: {
+          id: -1,
           pageSize: 10,
           pageNo: 1,
           searchVal: '',
diff --git 
a/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/resource/_source/list.vue
 
b/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/subUdfDirectory/_source/list.vue
similarity index 90%
copy from 
dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/resource/_source/list.vue
copy to 
dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/subUdfDirectory/_source/list.vue
index 1f72e51..ddb097e 100644
--- 
a/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/resource/_source/list.vue
+++ 
b/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/subUdfDirectory/_source/list.vue
@@ -26,6 +26,9 @@
             <span>{{$t('UDF Resource Name')}}</span>
           </th>
           <th scope="col">
+            <span>{{$t('Whether directory')}}</span>
+          </th>
+          <th scope="col">
             <span>{{$t('File Name')}}</span>
           </th>
           <th scope="col" width="80">
@@ -50,9 +53,12 @@
           </td>
           <td>
             <span class="ellipsis" v-tooltip.large.top.start.light="{text: 
item.alias, maxWidth: '500px'}">
-              <a href="javascript:" class="links" >{{item.alias}}</a>
+              <a href="javascript:" class="links" 
@click="_go(item)">{{item.alias}}</a>
             </span>
           </td>
+          <td>
+            <span>{{item.directory? $t('Yes') : $t('No')}}</span>
+          </td>
           <td><span class="ellipsis" v-tooltip.large.top.start.light="{text: 
item.fileName, maxWidth: '500px'}">{{item.fileName}}</span></td>
           <td>
             <span>{{_rtSize(item.size)}}</span>
@@ -85,6 +91,7 @@
                     size="xsmall"
                     data-toggle="tooltip"
                     :title="$t('Download')"
+                    :disabled="item.directory? true: false"
                     icon="ans-icon-download"
                     @click="_downloadFile(item)">
             </x-button>
@@ -120,6 +127,7 @@
   import mRename from './rename'
   import { downloadFile } from '@/module/download'
   import { bytesToSize } from '@/module/util/util'
+  import localStore from '@/module/util/localStorage'
 
   export default {
     name: 'udf-manage-list',
@@ -140,6 +148,13 @@
           id: item.id
         })
       },
+      _go (item) {
+        localStore.setItem('file', `${item.alias}|${item.size}`)
+        if(item.directory) {
+          localStore.setItem('currentDir', `${item.fullName}`)
+          this.$router.push({ path: `/resource/udf/subUdfDirectory/${item.id}` 
})
+        }
+      },
       _rtSize (val) {
         return bytesToSize(parseInt(val))
       },
diff --git 
a/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/subUdfDirectory/_source/rename.vue
 
b/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/subUdfDirectory/_source/rename.vue
new file mode 100644
index 0000000..69acdef
--- /dev/null
+++ 
b/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/subUdfDirectory/_source/rename.vue
@@ -0,0 +1,119 @@
+/*
+ * 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>
+  <m-popup :ok-text="$t('Rename')" :nameText="$t('Rename')" @ok="_ok" 
:asyn-loading="true">
+    <template slot="content">
+      <div class="resource-rename-model">
+        <m-list-box-f>
+          <template slot="name"><strong>*</strong>{{$t('Name')}}</template>
+          <template slot="content">
+            <x-input
+                    type="input"
+                    v-model="name"
+                    maxlength="60"
+                    :placeholder="$t('Please enter name')"
+                    autocomplete="off">
+            </x-input>
+          </template>
+        </m-list-box-f>
+        <m-list-box-f>
+          <template slot="name">{{$t('Description')}}</template>
+          <template slot="content">
+            <x-input
+                    type="textarea"
+                    v-model="description"
+                    :placeholder="$t('Please enter description')"
+                    autocomplete="off">
+            </x-input>
+          </template>
+        </m-list-box-f>
+      </div>
+    </template>
+  </m-popup>
+</template>
+<script>
+  import i18n from '@/module/i18n'
+  import store from '@/conf/home/store'
+  import mPopup from '@/module/components/popup/popup'
+  import mListBoxF from '@/module/components/listBoxF/listBoxF'
+
+  export default {
+    name: 'resource-udf-rename',
+    data () {
+      return {
+        store,
+        description: '',
+        name: ''
+      }
+    },
+    props: {
+      item: Object
+    },
+    methods: {
+      _ok (fn) {
+        this._verification().then(res => {
+          if (this.name === this.item.alias) {
+            return new Promise((resolve,reject) => {
+              this.description === this.item.description ? 
reject({msg:'内容未修改'}) : resolve()
+            })
+          }else{
+            return this.store.dispatch('resource/resourceVerifyName', {
+              name: this.name,
+              type: 'UDF'
+            })
+          }
+        }).then(res => {
+          return this.store.dispatch('resource/resourceRename', {
+            name: this.name,
+            description: this.description,
+            id: this.item.id,
+            type: 'UDF'
+          })
+        }).then(res => {
+          this.$message.success(res.msg)
+          this.$emit('onUpDate', res.data)
+          fn()
+        }).catch(e => {
+          fn()
+          this.$message.error(e.msg || '')
+        })
+      },
+      _verification () {
+        return new Promise((resolve, reject) => {
+          if (!this.name) {
+            reject({ // eslint-disable-line
+              msg: `${i18n.$t('Please enter resource name')}`
+            })
+          } else {
+            resolve()
+          }
+        })
+      }
+    },
+    watch: {},
+    created () {
+      let item = this.item || {}
+      if (item) {
+        this.name = item.alias
+        this.description = item.description
+      }
+    },
+    mounted () {
+    },
+    components: { mPopup, mListBoxF }
+  }
+</script>
diff --git 
a/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/resource/index.vue
 
b/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/subUdfDirectory/index.vue
similarity index 87%
copy from 
dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/resource/index.vue
copy to 
dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/subUdfDirectory/index.vue
index 732c5a2..fb52856 100644
--- 
a/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/resource/index.vue
+++ 
b/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/subUdfDirectory/index.vue
@@ -19,7 +19,10 @@
     <template slot="conditions">
       <m-conditions @on-conditions="_onConditions">
         <template slot="button-group">
-          <x-button type="ghost" size="small"  
@click="_uploading">{{$t('Upload UDF Resources')}}</x-button>
+          <x-button-group size="small">
+            <x-button type="ghost" @click="() => $router.push({name: 
'resource-udf-subCreateUdfFolder'})">{{$t('Create folder')}}</x-button>
+            <x-button type="ghost" size="small"  
@click="_uploading">{{$t('Upload UDF Resources')}}</x-button>
+          </x-button-group>
         </template>
       </m-conditions>
     </template>
@@ -58,6 +61,7 @@
         isLoading: false,
         udfResourcesList: [],
         searchParams: {
+          id: this.$route.params.id,
           pageSize: 10,
           pageNo: 1,
           searchVal: '',
@@ -73,7 +77,7 @@
        * File Upload
        */
       _uploading () {
-        findComponentDownward(this.$root, 'roof-nav')._fileUpdate('UDF')
+        findComponentDownward(this.$root, 
'roof-nav')._resourceChildUpdate('UDF',this.searchParams.id)
       },
       _onConditions (o) {
         this.searchParams = _.assign(this.searchParams, o)
@@ -88,7 +92,8 @@
       _onUpdate () {
         this._debounceGET()
       },
-      _updateList () {
+      _updateList (data) {
+        this.searchParams.id = data
         this.searchParams.pageNo = 1
         this.searchParams.searchVal = ''
         this._debounceGET()
@@ -114,6 +119,7 @@
       '$route' (a) {
         // url no params get instance list
         this.searchParams.pageNo = _.isEmpty(a.query) ? 1 : a.query.pageNo
+        this.searchParams.id = a.params.id
       }
     },
     created () {
diff --git 
a/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/subUdfFolder/index.vue
 
b/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/subUdfFolder/index.vue
new file mode 100755
index 0000000..c3cddc3
--- /dev/null
+++ 
b/dolphinscheduler-ui/src/js/conf/home/pages/resource/pages/udf/pages/subUdfFolder/index.vue
@@ -0,0 +1,128 @@
+/*
+ * 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>
+  <m-list-construction :title="$t('Create folder')">
+    <template slot="content">
+      <div class="resource-create-model">
+        <m-list-box-f>
+          <template slot="name"><strong>*</strong>{{$t('Folder 
Name')}}</template>
+          <template slot="content">
+            <x-input
+                    type="input"
+                    v-model="name"
+                    maxlength="60"
+                    style="width: 300px;"
+                    :placeholder="$t('Please enter name')"
+                    autocomplete="off">
+            </x-input>
+          </template>
+        </m-list-box-f>
+        <m-list-box-f>
+          <template slot="name">{{$t('Description')}}</template>
+          <template slot="content">
+            <x-input
+                    type="textarea"
+                    v-model="description"
+                    style="width: 430px;"
+                    :placeholder="$t('Please enter description')"
+                    autocomplete="off">
+            </x-input>
+          </template>
+        </m-list-box-f>
+        <m-list-box-f>
+          <template slot="name">&nbsp;</template>
+          <template slot="content">
+            <div class="submit">
+              <x-button type="primary" shape="circle" 
:loading="spinnerLoading" @click="ok()">{{spinnerLoading ? 'Loading...' : 
$t('Create')}} </x-button>
+              <x-button type="text" @click="() => $router.push({name: 
'resource-udf-subUdfDirectory'})"> {{$t('Cancel')}} </x-button>
+            </div>
+          </template>
+        </m-list-box-f>
+      </div>
+    </template>
+  </m-list-construction>
+</template>
+<script>
+  import i18n from '@/module/i18n'
+  import { mapActions } from 'vuex'
+  import mListBoxF from '@/module/components/listBoxF/listBoxF'
+  import mSpin from '@/module/components/spin/spin'
+  import mConditions from '@/module/components/conditions/conditions'
+  import localStore from '@/module/util/localStorage'
+  import mListConstruction from 
'@/module/components/listConstruction/listConstruction'
+
+  export default {
+    name: 'resource-list-create-FILE',
+    data () {
+      return {
+        type: '',
+        name: '',
+        description: '',
+        spinnerLoading: false
+      }
+    },
+    props: {},
+    methods: {
+      ...mapActions('resource', ['createResourceFolder']),
+      ok () {
+        if (this._validation()) {
+          this.spinnerLoading = true
+          this.createResourceFolder({
+            type: 'UDF',
+            name: this.name,
+            currentDir: localStore.getItem('currentDir'),
+            pid: this.$route.params.id,
+            description: this.description
+          }).then(res => {
+            this.$message.success(res.msg)
+            setTimeout(() => {
+              this.spinnerLoading = false
+              this.$router.push({ path: 
`/resource/udf/subUdfDirectory/${this.$route.params.id}`})
+            }, 800)
+          }).catch(e => {
+            this.$message.error(e.msg || '')
+            this.spinnerLoading = false
+          })
+        }
+      },
+      _validation () {
+        if (!this.name) {
+          this.$message.warning(`${i18n.$t('Please enter resource folder 
name')}`)
+          return false
+        }
+
+        return true
+      },
+    },
+    watch: {},
+    created () {
+    },
+    mounted () {
+      this.$modal.destroy()
+    },
+    destroyed () {
+    },
+    computed: {},
+    components: { mListConstruction, mConditions, mSpin, mListBoxF }
+  }
+</script>
+
+<style lang="scss" rel="stylesheet/scss">
+  .resource-create-model {
+    padding: 30px;
+  }
+</style>
diff --git 
a/dolphinscheduler-ui/src/js/conf/home/pages/security/pages/users/_source/list.vue
 
b/dolphinscheduler-ui/src/js/conf/home/pages/security/pages/users/_source/list.vue
index ecd6d97..59d9199 100644
--- 
a/dolphinscheduler-ui/src/js/conf/home/pages/security/pages/users/_source/list.vue
+++ 
b/dolphinscheduler-ui/src/js/conf/home/pages/security/pages/users/_source/list.vue
@@ -231,27 +231,30 @@
           // })
           let fileSourceList = []
           let udfSourceList = []
-          fileSourceList = data[1].concat(data[0])
-          // sourceListPrs.forEach((value,index,array)=>{
-          //   if(value.type =='FILE'){
-          //     fileSourceList.push(value)
-          //   } else{
-          //     udfSourceList.push(value)
-          //   }
-          // })
-          let targetListPrs = _.map(data[1], v => {
-            return v.id
+          data[0].forEach((value,index,array)=>{
+            if(value.type =='FILE'){
+              fileSourceList.push(value)
+            } else{
+              udfSourceList.push(value)
+            }
           })
           let fileTargetList = []
           let udfTargetList = []
-          fileTargetList = targetListPrs
-          // targetListPrs.forEach((value,index,array)=>{
-          //   if(value.type =='FILE'){
-          //     fileTargetList.push(value)
-          //   } else{
-          //     udfTargetList.push(value)
-          //   }
-          // })
+          data[1].forEach((value,index,array)=>{
+            if(value.type =='FILE'){
+              fileTargetList.push(value)
+            } else{
+              udfTargetList.push(value)
+            }
+          })
+          fileSourceList = fileSourceList.concat(fileTargetList)
+          udfSourceList  = udfSourceList.concat(udfTargetList)
+          fileTargetList = _.map(fileTargetList, v => {
+            return v.id
+          })
+          udfTargetList = _.map(udfTargetList, v => {
+            return v.id
+          })
           let self = this
           let modal = this.$modal.dialog({
             closable: false,
diff --git a/dolphinscheduler-ui/src/js/conf/home/router/index.js 
b/dolphinscheduler-ui/src/js/conf/home/router/index.js
index 4e7d169..8151f72 100644
--- a/dolphinscheduler-ui/src/js/conf/home/router/index.js
+++ b/dolphinscheduler-ui/src/js/conf/home/router/index.js
@@ -267,16 +267,40 @@ const router = new Router({
           },
           children: [
             {
-              path: '/resource/udf/resource',
-              name: 'resource-udf-resource',
+              path: '/resource/udf',
+              name: 'resource-udf',
               component: resolve => 
require(['../pages/resource/pages/udf/pages/resource/index'], resolve),
               meta: {
                 title: `${i18n.$t('UDF Resources')}`
               }
             },
             {
-              path: '/resource/udf/function',
-              name: 'resource-udf-function',
+              path: '/resource/udf/subUdfDirectory/:id',
+              name: 'resource-udf-subUdfDirectory',
+              component: resolve => 
require(['../pages/resource/pages/udf/pages/subUdfDirectory/index'], resolve),
+              meta: {
+                title: `${i18n.$t('File Subdirectory')}`
+              }
+            },
+            {
+              path: '/resource/udf/createUdfFolder',
+              name: 'resource-udf-createUdfFolder',
+              component: resolve => 
require(['../pages/resource/pages/udf/pages/createUdfFolder/index'], resolve),
+              meta: {
+                title: `${i18n.$t('Create Resource')}`
+              }
+            },
+            {
+              path: '/resource/udf/subCreateUdfFolder/:id',
+              name: 'resource-udf-subCreateUdfFolder',
+              component: resolve => 
require(['../pages/resource/pages/udf/pages/subUdfFolder/index'], resolve),
+              meta: {
+                title: `${i18n.$t('Create Resource')}`
+              }
+            },
+            {
+              path: '/resource/func',
+              name: 'resource-func',
               component: resolve => 
require(['../pages/resource/pages/udf/pages/function/index'], resolve),
               meta: {
                 title: `${i18n.$t('UDF Function')}`
diff --git 
a/dolphinscheduler-ui/src/js/module/components/fileUpdate/resourceChildUpdate.vue
 
b/dolphinscheduler-ui/src/js/module/components/fileUpdate/resourceChildUpdate.vue
new file mode 100644
index 0000000..4052c44
--- /dev/null
+++ 
b/dolphinscheduler-ui/src/js/module/components/fileUpdate/resourceChildUpdate.vue
@@ -0,0 +1,318 @@
+/*
+ * 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>
+  <m-popup
+          ref="popup"
+          :ok-text="$t('Upload')"
+          :nameText="$t('File Upload')"
+          @ok="_ok"
+          :disabled="progress === 0 ? false : true">
+    <template slot="content">
+      <form name="files" enctype="multipart/form-data" method="post">
+        <div class="file-update-model"
+             @drop.prevent="_onDrop"
+             @dragover.prevent="dragOver = true"
+             @dragleave.prevent="dragOver = false"
+             id="file-update-model">
+          <div class="tooltip-info">
+            <em class="ans ans-icon-warn-solid"></em>
+            <span>{{$t('Drag the file into the current upload window')}}</span>
+          </div>
+          <!--<div class="hide-archive" v-if="progress !== 0" 
@click="_ckArchive">
+            <em class="fa fa-minus" data-toggle="tooltip" title="关闭窗口 继续上传" 
data-container="body" ></em>
+          </div>-->
+          <div class="update-popup" v-if="dragOver">
+            <div class="icon-box">
+              <em class="ans ans-icon-upload"></em>
+            </div>
+            <p class="p1">
+              <span>{{$t('Drag area upload')}}</span>
+            </p>
+          </div>
+          <m-list-box-f>
+            <template slot="name"><strong>*</strong>{{$t('File 
Name')}}</template>
+            <template slot="content">
+              <x-input
+                      type="input"
+                      v-model="name"
+                      :disabled="progress !== 0"
+                      :placeholder="$t('Please enter name')"
+                      autocomplete="off">
+              </x-input>
+            </template>
+          </m-list-box-f>
+          <m-list-box-f>
+            <template slot="name">{{$t('Description')}}</template>
+            <template slot="content">
+              <x-input
+                      type="textarea"
+                      v-model="description"
+                      :disabled="progress !== 0"
+                      :placeholder="$t('Please enter description')"
+                      autocomplete="off">
+              </x-input>
+            </template>
+          </m-list-box-f>
+          <m-list-box-f>
+            <template slot="name"><strong>*</strong>{{$t('Upload 
Files')}}</template>
+            <template slot="content">
+              <div class="file-update-box">
+                <template v-if="progress === 0">
+                  <input name="file" id="file" type="file" class="file-update">
+                  <x-button type="dashed" size="xsmall"> {{$t('Upload')}} 
</x-button>
+                </template>
+                <div class="progress-box" v-if="progress !== 0">
+                  <m-progress-bar :value="progress" 
text-placement="left-right"></m-progress-bar>
+                </div>
+              </div>
+            </template>
+          </m-list-box-f>
+        </div>
+      </form>
+    </template>
+  </m-popup>
+</template>
+<script>
+  import io from '@/module/io'
+  import i18n from '@/module/i18n'
+  import store from '@/conf/home/store'
+  import mPopup from '@/module/components/popup/popup'
+  import mListBoxF from '@/module/components/listBoxF/listBoxF'
+  import localStore from '@/module/util/localStorage'
+  import mProgressBar from '@/module/components/progressBar/progressBar'
+
+  export default {
+    name: 'file-update',
+    data () {
+      return {
+        store,
+        // name
+        name: '',
+        // description
+        description: '',
+        // progress
+        progress: 0,
+        // file
+        file: '',
+        currentDir: localStore.getItem('currentDir'),
+        pid: this.id,
+        // Whether to drag upload
+        dragOver: false
+      }
+    },
+    watch: {
+    },
+    props: {
+      type: String,
+      id: Number
+    },
+    methods: {
+      /**
+       * submit
+       */
+      _ok () {
+        this.$refs['popup'].spinnerLoading = true
+        if (this._validation()) {
+          this.store.dispatch('resource/resourceVerifyName', {
+            name: this.name,
+            type: this.type
+          }).then(res => {
+            const isLt1024M = this.file.size / 1024 / 1024 < 1024
+            if(isLt1024M) {
+              this._formDataUpdate().then(res => {
+                setTimeout(() => {
+                  this.$refs['popup'].spinnerLoading = false
+                }, 800)
+              }).catch(e => {
+                this.$refs['popup'].spinnerLoading = false
+              })
+            } else {
+              this.$message.warning(`${i18n.$t('Upload File Size')}`)
+              this.$refs['popup'].spinnerLoading = false
+            }
+          }).catch(e => {
+            this.$message.error(e.msg || '')
+            this.$refs['popup'].spinnerLoading = false
+          })
+        } else {
+          this.$refs['popup'].spinnerLoading = false
+        }
+      },
+      /**
+       * validation
+       */
+      _validation () {
+        if (!this.name) {
+          this.$message.warning(`${i18n.$t('Please enter file name')}`)
+          return false
+        }
+        if (!this.file) {
+          this.$message.warning(`${i18n.$t('Please select the file to 
upload')}`)
+          return false
+        }
+        return true
+      },
+      /**
+       * update file
+       */
+      _formDataUpdate () {
+        return new Promise((resolve, reject) => {
+          let self = this
+          let formData = new FormData()
+          formData.append('file', this.file)
+          formData.append('type', this.type)
+          formData.append('name', this.name)
+          formData.append('pid', this.pid)
+          formData.append('currentDir', this.currentDir)
+          formData.append('description', this.description)
+          io.post(`resources/create`, res => {
+            this.$message.success(res.msg)
+            resolve()
+            self.$emit('onUpdate')
+          }, e => {
+            reject(e)
+            self.$emit('close')
+            this.$message.error(e.msg || '')
+          }, {
+            data: formData,
+            emulateJSON: false,
+            onUploadProgress (progressEvent) {
+              // Size has been uploaded
+              let loaded = progressEvent.loaded
+              // Total attachment size
+              let total = progressEvent.total
+              self.progress = Math.floor(100 * loaded / total)
+              self.$emit('onProgress', self.progress)
+            }
+          })
+        })
+      },
+      /**
+       * Archive to the top right corner Continue uploading
+       */
+      _ckArchive () {
+        $('.update-file-modal').hide()
+        this.$emit('onArchive')
+      },
+      /**
+       * Drag and drop upload
+       */
+      _onDrop (e) {
+        let file = e.dataTransfer.files[0]
+        this.file = file
+        this.name = file.name
+        this.dragOver = false
+      }
+    },
+    mounted () {
+      $('#file').change(() => {
+        let file = $('#file')[0].files[0]
+        this.file = file
+        this.name = file.name
+      })
+    },
+    components: { mPopup, mListBoxF, mProgressBar }
+  }
+</script>
+
+<style lang="scss" rel="stylesheet/scss">
+  .file-update-model {
+    .tooltip-info {
+      position: absolute;
+      left: 20px;
+      bottom: 26px;
+      span {
+        font-size: 12px;
+        color: #666;
+        vertical-align: middle;
+      }
+      .fa,.ans {
+        color: #0097e0;
+        font-size: 14px;
+        vertical-align: middle;
+      }
+    }
+    .hide-archive {
+      position: absolute;
+      right: 22px;
+      top: 17px;
+      .fa,.ans{
+        font-size: 16px;
+        color: #333;
+        font-weight: normal;
+        cursor: pointer;
+        &:hover {
+          color: #0097e0;
+        }
+      }
+    }
+    .file-update-box {
+      padding-top: 4px;
+      position: relative;
+      .file-update {
+        width: 70px;
+        height: 40px;
+        position: absolute;
+        left: 0;
+        top: 0;
+        cursor: pointer;
+        filter: alpha(opacity=0);
+        -moz-opacity: 0;
+        opacity: 0;
+      }
+      &:hover {
+        .v-btn-dashed {
+          background-color: transparent;
+          border-color: #47c3ff;
+          color: #47c3ff;
+          cursor: pointer;
+        }
+      }
+      .progress-box {
+        width: 200px;
+        position: absolute;
+        left: 70px;
+        top: 14px;
+      }
+    }
+    .update-popup {
+      width: calc(100% - 20px);
+      height: calc(100% - 20px);
+      background: rgba(255,253,239,.7);
+      position: absolute;
+      top: 10px;
+      left: 10px;
+      border-radius: 3px;
+      z-index: 1;
+      border: .18rem dashed #cccccc;
+      .icon-box {
+        text-align: center;
+        margin-top: 96px;
+        .fa,.ans {
+          font-size: 50px;
+          color: #2d8cf0;
+        }
+      }
+      .p1 {
+        text-align: center;
+        font-size: 16px;
+        color: #333;
+        padding-top: 8px;
+      }
+    }
+  }
+</style>
diff --git a/dolphinscheduler-ui/src/js/module/components/nav/nav.vue 
b/dolphinscheduler-ui/src/js/module/components/nav/nav.vue
index 91fd182..a46ff6f 100644
--- a/dolphinscheduler-ui/src/js/module/components/nav/nav.vue
+++ b/dolphinscheduler-ui/src/js/module/components/nav/nav.vue
@@ -156,6 +156,7 @@
   import { findComponentDownward } from '@/module/util/'
   import mFileUpdate from '@/module/components/fileUpdate/fileUpdate'
   import mFileChildUpdate from '@/module/components/fileUpdate/fileChildUpdate'
+  import mResourceChildUpdate from 
'@/module/components/fileUpdate/resourceChildUpdate'
   import mDefinitionUpdate from 
'@/module/components/fileUpdate/definitionUpdate'
   import mProgressBar from '@/module/components/progressBar/progressBar'
 
@@ -301,6 +302,46 @@
           }
         })
       },
+      _resourceChildUpdate (type,data) {
+        if (this.progress) {
+          this._toggleArchive()
+          return
+        }
+        let self = this
+        let modal = this.$modal.dialog({
+          closable: false,
+          showMask: true,
+          escClose: true,
+          className: 'update-file-modal',
+          transitionName: 'opacityp',
+          render (h) {
+            return h(mResourceChildUpdate, {
+              on: {
+                onProgress (val) {
+                  self.progress = val
+                },
+                onUpdate () {
+                  findComponentDownward(self.$root, 
`resource-list-index-${type}`)._updateList(data)
+                  self.isUpdate = false
+                  self.progress = 0
+                  modal.remove()
+                },
+                onArchive () {
+                  self.isUpdate = true
+                },
+                close () {
+                  self.progress = 0
+                  modal.remove()
+                }
+              },
+              props: {
+                type: type,
+                id: data
+              }
+            })
+          }
+        })
+      },
       /**
        * Upload popup layer display
        */
diff --git 
a/dolphinscheduler-ui/src/js/module/components/secondaryMenu/_source/menu.js 
b/dolphinscheduler-ui/src/js/module/components/secondaryMenu/_source/menu.js
index 2ed0fc1..baf2cf7 100644
--- a/dolphinscheduler-ui/src/js/module/components/secondaryMenu/_source/menu.js
+++ b/dolphinscheduler-ui/src/js/module/components/secondaryMenu/_source/menu.js
@@ -148,13 +148,13 @@ let menu = {
       children: [
         {
           name: `${i18n.$t('Resource manage')}`,
-          path: 'resource-udf-resource',
+          path: 'resource-udf',
           id: 0,
           disabled: true
         },
         {
           name: `${i18n.$t('Function manage')}`,
-          path: 'resource-udf-function',
+          path: 'resource-func',
           id: 1,
           disabled: true
         }
diff --git a/dolphinscheduler-ui/src/js/module/components/transfer/resource.vue 
b/dolphinscheduler-ui/src/js/module/components/transfer/resource.vue
index e39bd67..91bcbf5 100644
--- a/dolphinscheduler-ui/src/js/module/components/transfer/resource.vue
+++ b/dolphinscheduler-ui/src/js/module/components/transfer/resource.vue
@@ -24,7 +24,10 @@
                 <x-button type="ghost" value="udfResource" 
@click="_ckUDf">{{$t('UDF resources')}}</x-button>
             </x-button-group>
         </div>
-        <treeselect v-model="selectFileSource" :multiple="true" 
:options="fileSourceList" :normalizer="normalizer">
+        <treeselect v-show="checkedValue=='fileResource'" 
v-model="selectFileSource" :multiple="true" :options="fileList" 
:normalizer="normalizer" :placeholder="$t('Please select resources')">
+          <div slot="value-label" slot-scope="{ node }">{{ node.raw.fullName 
}}</div>
+        </treeselect>
+        <treeselect v-show="checkedValue=='udfResource'" 
v-model="selectUdfSource" :disable-branch-nodes="true" :options="udfList" 
:normalizer="normalizer" :placeholder="$t('Please select resources')">
           <div slot="value-label" slot-scope="{ node }">{{ node.raw.fullName 
}}</div>
         </treeselect>
         <!-- <div class="select-list-box">
@@ -76,7 +79,10 @@
         cacheTargetList: this.fileTargetList,
 
         fileSource: this.fileSourceList,
+        fileList: [],
+        udfList: [],
         selectFileSource: [],
+        selectUdfSource: [],
         fileTarget: this.fileTargetList,
         udfSource: this.udfSourceList,
         udfTarget: this.udfTargetList,
@@ -99,14 +105,21 @@
       udfTargetList: Array,
     },
     created() {
+      let file = this.fileSourceList
+      let udf = this.udfSourceList
+      this.diGuiTree(file)
+      this.diGuiTree(udf)
+      this.fileList = file
+      this.udfList = udf
       this.selectFileSource = this.fileTargetList
+      this.selectUdfSource = this.udfTargetList
     },
     methods: {
       _ok () {
         this.$refs['popup'].spinnerLoading = true
         setTimeout(() => {
           this.$refs['popup'].spinnerLoading = false
-          this.$emit('onUpdate', _.map(this.selectFileSource, v => 
v).join(','))
+          this.$emit('onUpdate', 
_.map(this.selectFileSource.concat(this.selectUdfSource), v => v).join(','))
         }, 800)
       },
       _ckFile() {
@@ -164,6 +177,12 @@
             this.udfSource = this.sourceList
             this.udfTarget = this.targetList
         }
+      },
+      diGuiTree(item) {  // Recursive convenience tree structure
+        item.forEach(item => {
+          item.children === '' || item.children === undefined || item.children 
=== null || item.children.length === 0?        
+            delete item.children : this.diGuiTree(item.children);
+        })
       }
     },
     watch: {

Reply via email to