You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dolphinscheduler.apache.org by zh...@apache.org on 2022/01/13 08:03:35 UTC

[dolphinscheduler] branch dev updated: [Feature][UI Next] Add file manage (#7943)

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

zhongjiajie pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/dolphinscheduler.git


The following commit(s) were added to refs/heads/dev by this push:
     new 97f84ae  [Feature][UI Next] Add file manage (#7943)
97f84ae is described below

commit 97f84ae00d7a027109a737875b1d8d85663e8aa4
Author: Devosend <de...@gmail.com>
AuthorDate: Thu Jan 13 16:03:29 2022 +0800

    [Feature][UI Next] Add file manage (#7943)
    
    * add file
    * fix form style
    * fix menu key not init bug
    * add license header
    * fix review question
    * delete jquery
    * modify conditions place
    * use pagination components instead of pagination attr of tables.
    * fixed table column width
    * delete card component title required attr
    * add table style
---
 dolphinscheduler-ui-next/package.json              |   1 +
 dolphinscheduler-ui-next/src/App.tsx               |  34 ++-
 .../src/components/card/index.tsx                  |   1 -
 .../conditions/index.module.scss}                  |  22 +-
 .../src/components/conditions/index.tsx            |  67 ++++++
 .../src/components/monaco-editor/index.tsx         | 121 +++++++++++
 dolphinscheduler-ui-next/src/env.d.ts              |  12 +-
 .../src/layouts/content/index.tsx                  |   4 +-
 .../src/layouts/content/use-dataList.ts            |   3 +-
 .../src/locales/modules/en_US.ts                   |  39 ++++
 .../src/locales/modules/zh_CN.ts                   |  40 ++++
 .../src/router/modules/resources.ts                |  38 +++-
 .../src/service/modules/resources/index.ts         |  37 ++--
 .../src/service/modules/resources/types.ts         |  23 +++
 dolphinscheduler-ui-next/src/service/service.ts    |  52 ++++-
 .../src/store/{menu/menu.ts => file/file.ts}       |  25 ++-
 .../src/{env.d.ts => store/file/types.ts}          |  12 +-
 dolphinscheduler-ui-next/src/store/menu/menu.ts    |   2 +-
 .../src/{env.d.ts => utils/common.ts}              |  37 +++-
 .../resource/file/create/resource-file-create.tsx  | 131 ++++++++++++
 .../src/views/resource/file/create/use-create.ts   |  53 +++++
 .../src/views/resource/file/create/use-form.ts     |  77 +++++++
 .../resource/file/edit/resource-file-edit.tsx      | 118 +++++++++++
 .../src/views/resource/file/edit/use-edit.ts       |  64 ++++++
 .../resource/file/edit/use-form.ts}                |  63 +++---
 .../src/views/resource/file/folder/index.tsx       |  86 ++++++++
 .../src/views/resource/file/folder/use-folder.ts   |  59 ++++++
 .../resource/file/folder/use-form.ts}              |  64 +++---
 .../resource/file/index.module.scss}               |  77 ++++---
 .../src/views/resource/file/index.tsx              | 228 +++++++++++++++++++++
 .../src/views/resource/file/rename/index.tsx       | 102 +++++++++
 .../src/views/resource/file/rename/use-form.ts     |  56 +++++
 .../resource/file/rename/use-rename.ts}            |  66 +++---
 .../resource/file/table/index.module.scss}         |  16 +-
 .../src/views/resource/file/table/table-action.tsx | 190 +++++++++++++++++
 .../src/views/resource/file/table/use-table.ts     | 104 ++++++++++
 .../src/views/resource/file/types.ts               |  66 ++++++
 .../src/views/resource/file/upload/index.tsx       | 100 +++++++++
 .../src/views/resource/file/upload/use-form.ts     |  66 ++++++
 .../src/views/resource/file/upload/use-upload.ts   |  64 ++++++
 .../src/views/resource/file/use-file.ts            |  78 +++++++
 41 files changed, 2298 insertions(+), 200 deletions(-)

diff --git a/dolphinscheduler-ui-next/package.json b/dolphinscheduler-ui-next/package.json
index 65e89f5..54de5e4 100644
--- a/dolphinscheduler-ui-next/package.json
+++ b/dolphinscheduler-ui-next/package.json
@@ -15,6 +15,7 @@
     "date-fns": "^2.28.0",
     "echarts": "^5.2.2",
     "lodash": "^4.17.21",
+    "monaco-editor": "^0.31.1",
     "naive-ui": "2.23.2",
     "nprogress": "^0.2.0",
     "pinia": "^2.0.9",
diff --git a/dolphinscheduler-ui-next/src/App.tsx b/dolphinscheduler-ui-next/src/App.tsx
index df3a276..3347694 100644
--- a/dolphinscheduler-ui-next/src/App.tsx
+++ b/dolphinscheduler-ui-next/src/App.tsx
@@ -15,20 +15,43 @@
  * limitations under the License.
  */
 
-import { defineComponent, computed } from 'vue'
-import { NConfigProvider, darkTheme, GlobalThemeOverrides } from 'naive-ui'
+import { defineComponent, computed, ref, nextTick, provide } from 'vue'
+import {
+  zhCN,
+  enUS,
+  NConfigProvider,
+  darkTheme,
+  GlobalThemeOverrides,
+  NMessageProvider,
+} from 'naive-ui'
 import { useThemeStore } from '@/store/theme/theme'
+import { useLocalesStore } from '@/store/locales/locales'
 import themeList from '@/themes'
 
 const App = defineComponent({
   name: 'App',
   setup() {
+    const isRouterAlive = ref(true)
     const themeStore = useThemeStore()
     const currentTheme = computed(() =>
-      themeStore.darkTheme ? darkTheme : undefined
+      themeStore.darkTheme ? darkTheme : undefined,
     )
+    const localesStore = useLocalesStore()
+    /*refresh page when router params change*/
+    const reload = () => {
+      isRouterAlive.value = false
+      nextTick(() => {
+        isRouterAlive.value = true
+      })
+    }
+
+    provide('reload', reload)
+
     return {
+      reload,
+      isRouterAlive,
       currentTheme,
+      localesStore,
     }
   },
   render() {
@@ -40,8 +63,11 @@ const App = defineComponent({
         theme={this.currentTheme}
         themeOverrides={themeOverrides}
         style={{ width: '100%', height: '100vh' }}
+        locale={String(this.localesStore.getLocales) === 'zh_CN' ? zhCN : enUS}
       >
-        <router-view />
+        <NMessageProvider>
+          {this.isRouterAlive ? <router-view /> : ''}
+        </NMessageProvider>
       </NConfigProvider>
     )
   },
diff --git a/dolphinscheduler-ui-next/src/components/card/index.tsx b/dolphinscheduler-ui-next/src/components/card/index.tsx
index 9ad5602..25be283 100644
--- a/dolphinscheduler-ui-next/src/components/card/index.tsx
+++ b/dolphinscheduler-ui-next/src/components/card/index.tsx
@@ -29,7 +29,6 @@ const contentStyle = {
 const props = {
   title: {
     type: String as PropType<string>,
-    required: true,
   },
 }
 
diff --git a/dolphinscheduler-ui-next/src/env.d.ts b/dolphinscheduler-ui-next/src/components/conditions/index.module.scss
similarity index 72%
copy from dolphinscheduler-ui-next/src/env.d.ts
copy to dolphinscheduler-ui-next/src/components/conditions/index.module.scss
index 776e88b..2e59ac9 100644
--- a/dolphinscheduler-ui-next/src/env.d.ts
+++ b/dolphinscheduler-ui-next/src/components/conditions/index.module.scss
@@ -15,13 +15,17 @@
  * limitations under the License.
  */
 
-declare module '*.vue' {
-  import { DefineComponent } from 'vue'
-  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
-  const component: DefineComponent<{}, {}, any>
-  export default component
+ .conditions-model {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin: 10px 0;
+  .right {
+    > .form-box {
+      .list {
+        float: right;
+        margin: 3px 0 3px 4px;
+      }
+    }
+  }
 }
-
-declare module '*.png'
-declare module '*.jpg'
-declare module '*.jpeg'
diff --git a/dolphinscheduler-ui-next/src/components/conditions/index.tsx b/dolphinscheduler-ui-next/src/components/conditions/index.tsx
new file mode 100644
index 0000000..d193f74
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/components/conditions/index.tsx
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import _ from 'lodash'
+import { useI18n } from 'vue-i18n'
+import { defineComponent, ref } from 'vue'
+import { SearchOutlined } from '@vicons/antd'
+import { NButton, NIcon, NInput, NSpace } from 'naive-ui'
+import Card from '@/components/card'
+import styles from './index.module.scss'
+
+const Conditions = defineComponent({
+  name: 'Conditions',
+  emits: ['conditions'],
+  setup(props, ctx) {
+    const searchVal = ref()
+    const handleConditions = () => {
+      ctx.emit('conditions', _.trim(searchVal.value))
+    }
+
+    return { searchVal, handleConditions }
+  },
+  render() {
+    const { t } = useI18n()
+    const { $slots, handleConditions } = this
+    return (
+      <Card style={{ marginBottom: '5px' }}>
+        <div class={styles['conditions-model']}>
+          <NSpace>{$slots}</NSpace>
+          <div class={styles.right}>
+            <div class={styles['form-box']}>
+              <div class={styles.list}>
+                <NButton onClick={handleConditions}>
+                  <NIcon>
+                    <SearchOutlined />
+                  </NIcon>
+                </NButton>
+              </div>
+              <div class={styles.list}>
+                <NInput
+                  placeholder={t('resource.file.enter_keyword_tips')}
+                  v-model={[this.searchVal, 'value']}
+                />
+              </div>
+            </div>
+          </div>
+        </div>
+      </Card>
+    )
+  },
+})
+
+export default Conditions
diff --git a/dolphinscheduler-ui-next/src/components/monaco-editor/index.tsx b/dolphinscheduler-ui-next/src/components/monaco-editor/index.tsx
new file mode 100644
index 0000000..fb7100a
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/components/monaco-editor/index.tsx
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {
+  defineComponent,
+  onMounted,
+  onUnmounted,
+  PropType,
+  nextTick,
+  ref,
+  watch,
+} from 'vue'
+import * as monaco from 'monaco-editor'
+import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
+import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'
+import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker'
+import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker'
+import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'
+
+const props = {
+  modelValue: {
+    type: String as PropType<string>,
+    default: '',
+  },
+  language: {
+    type: String as PropType<string>,
+    default: 'shell',
+  },
+  readOnly: {
+    type: Boolean as PropType<boolean>,
+    default: false,
+  },
+  options: {
+    type: Object,
+    default: () => {},
+  },
+}
+
+// @ts-ignore
+window.MonacoEnvironment = {
+  getWorker(_: any, label: string) {
+    if (label === 'json') {
+      return new jsonWorker()
+    }
+    if (['css', 'scss', 'less'].includes(label)) {
+      return new cssWorker()
+    }
+    if (['html', 'handlebars', 'razor'].includes(label)) {
+      return new htmlWorker()
+    }
+    if (['typescript', 'javascript'].includes(label)) {
+      return new tsWorker()
+    }
+    return new editorWorker()
+  },
+}
+
+export default defineComponent({
+  name: 'MonacoEditor',
+  props,
+  setup(props) {
+    let editor = null as monaco.editor.IStandaloneCodeEditor | null
+    const content = ref()
+    const getValue = () => editor?.getValue()
+
+    watch(
+      () => props.modelValue,
+      (val) => {
+        if (val !== getValue()) {
+          editor?.setValue(val)
+        }
+      },
+    )
+
+    onMounted(async () => {
+      content.value = props.modelValue
+
+      await nextTick()
+      const dom = document.getElementById('monaco-container')
+      if (dom) {
+        editor = monaco.editor.create(dom, props.options, {
+          value: props.modelValue,
+          language: props.language,
+          readOnly: props.readOnly,
+          automaticLayout: true,
+        })
+      }
+    })
+
+    onUnmounted(() => {
+      editor?.dispose()
+    })
+
+    return { getValue }
+  },
+  render() {
+    return (
+      <div
+        id='monaco-container'
+        style={{
+          height: '300px',
+          border: '1px solid #eee',
+        }}
+      ></div>
+    )
+  },
+})
diff --git a/dolphinscheduler-ui-next/src/env.d.ts b/dolphinscheduler-ui-next/src/env.d.ts
index 776e88b..db96b93 100644
--- a/dolphinscheduler-ui-next/src/env.d.ts
+++ b/dolphinscheduler-ui-next/src/env.d.ts
@@ -15,13 +15,23 @@
  * limitations under the License.
  */
 
+import { DefineComponent } from 'vue'
+// import * as $ from 'jquery'
+
 declare module '*.vue' {
-  import { DefineComponent } from 'vue'
   // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
   const component: DefineComponent<{}, {}, any>
   export default component
 }
 
+declare global {
+  interface Window {
+    $message: any
+  }
+}
+
+declare namespace jquery {}
+
 declare module '*.png'
 declare module '*.jpg'
 declare module '*.jpeg'
diff --git a/dolphinscheduler-ui-next/src/layouts/content/index.tsx b/dolphinscheduler-ui-next/src/layouts/content/index.tsx
index 3eb9a61..82da2a9 100644
--- a/dolphinscheduler-ui-next/src/layouts/content/index.tsx
+++ b/dolphinscheduler-ui-next/src/layouts/content/index.tsx
@@ -16,7 +16,7 @@
  */
 
 import { defineComponent, onMounted, watch, toRefs, ref } from 'vue'
-import { NLayout, NLayoutContent, NLayoutHeader } from 'naive-ui'
+import { NLayout, NLayoutContent, NLayoutHeader, useMessage } from 'naive-ui'
 import NavBar from './components/navbar'
 import SideBar from './components/sidebar'
 import { useDataList } from './use-dataList'
@@ -27,6 +27,8 @@ import { useI18n } from 'vue-i18n'
 const Content = defineComponent({
   name: 'Content',
   setup() {
+    window.$message = useMessage()
+
     const menuStore = useMenuStore()
     const { locale } = useI18n()
     const localesStore = useLocalesStore()
diff --git a/dolphinscheduler-ui-next/src/layouts/content/use-dataList.ts b/dolphinscheduler-ui-next/src/layouts/content/use-dataList.ts
index 7b0a725..3a37e6d 100644
--- a/dolphinscheduler-ui-next/src/layouts/content/use-dataList.ts
+++ b/dolphinscheduler-ui-next/src/layouts/content/use-dataList.ts
@@ -122,7 +122,7 @@ export function useDataList() {
       },
       {
         label: t('menu.resources'),
-        key: 'resources',
+        key: 'resource',
         icon: renderIcon(FolderOutlined),
         isShowSide: true,
         children: [
@@ -153,6 +153,7 @@ export function useDataList() {
         key: 'datasource',
         icon: renderIcon(DatabaseOutlined),
         isShowSide: false,
+        children: [],
       },
       {
         label: t('menu.monitor'),
diff --git a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts
index be1fb01..a05a074 100644
--- a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts
+++ b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts
@@ -151,6 +151,44 @@ const monitor = {
   },
 }
 
+const resource = {
+  file: {
+    file_manage: 'File Manage',
+    create_folder: 'Create Folder',
+    create_file: 'Create File',
+    upload_files: 'Upload Files',
+    enter_keyword_tips: 'Please enter keyword',
+    id: '#',
+    name: 'Name',
+    user_name: 'Resource userName',
+    whether_directory: 'Whether directory',
+    file_name: 'File Name',
+    description: 'Description',
+    size: 'Size',
+    update_time: 'Update Time',
+    operation: 'Operation',
+    edit: 'Edit',
+    rename: 'Rename',
+    download: 'Download',
+    delete: 'Delete',
+    yes: 'Yes',
+    no: 'No',
+    folder_name: 'Folder Name',
+    enter_name_tips: 'Please enter name',
+    enter_description_tips: 'Please enter description',
+    enter_content_tips: 'Please enter the resource content',
+    file_format: 'File Format',
+    file_content: 'File Content',
+    delete_confirm: 'Delete?',
+    confirm: 'Confirm',
+    cancel: 'Cancel',
+    success: 'Success',
+    file_details: 'File Details',
+    return: 'Return',
+    save: 'Save',
+  }
+}
+
 const project = {
   list: {
     create_project: 'Create Project',
@@ -185,5 +223,6 @@ export default {
   password,
   profile,
   monitor,
+  resource,
   project,
 }
diff --git a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts
index 41d83cc..3372ccb 100644
--- a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts
+++ b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts
@@ -149,6 +149,45 @@ const monitor = {
   },
 }
 
+const resource = {
+  file: {
+    file_manage: '文件管理',
+    create_folder: '创建文件夹',
+    create_file: '创建文件',
+    upload_files: '上传文件',
+    enter_keyword_tips: '请输入关键词',
+    id: '编号',
+    name: '名称',
+    user_name: '所属用户',
+    whether_directory: '是否文件夹',
+    file_name: '文件名称',
+    description: '描述',
+    size: '大小',
+    update_time: '更新时间',
+    operation: '操作',
+    edit: '编辑',
+    rename: '重命名',
+    download: '下载',
+    delete: '删除',
+    yes: '是',
+    no: '否',
+    folder_name: '文件夹名称',
+    enter_name_tips: '请输入名称',
+    enter_description_tips: '请输入描述',
+    enter_content_tips: '请输入资源内容',
+    enter_suffix_tips: '请输入文件后缀',
+    file_format: '文件格式',
+    file_content: '文件内容',
+    delete_confirm: '确定删除吗?',
+    confirm: '确定',
+    cancel: '取消',
+    success: '成功',
+    file_details: '文件详情',
+    return: '返回',
+    save: '保存',
+  }
+}
+
 const project = {
   list: {
     create_project: '创建项目',
@@ -183,5 +222,6 @@ export default {
   password,
   profile,
   monitor,
+  resource,
   project,
 }
diff --git a/dolphinscheduler-ui-next/src/router/modules/resources.ts b/dolphinscheduler-ui-next/src/router/modules/resources.ts
index 6ea940a..24fc461 100644
--- a/dolphinscheduler-ui-next/src/router/modules/resources.ts
+++ b/dolphinscheduler-ui-next/src/router/modules/resources.ts
@@ -32,7 +32,7 @@ export default {
     {
       path: '/resource/file',
       name: 'file',
-      component: components['home'],
+      component: components['file'],
       meta: {
         title: '文件管理',
       },
@@ -40,9 +40,41 @@ export default {
     {
       path: '/resource/file/create',
       name: 'resource-file-create',
-      component: components['home'],
+      component: components['resource-file-create'],
       meta: {
-        title: '创建资源',
+        title: '文件创建',
+      },
+    },
+    {
+      path: '/resource/file/edit/:id',
+      name: 'resource-file-edit',
+      component: components['resource-file-edit'],
+      meta: {
+        title: '文件编辑',
+      },
+    },
+    {
+      path: '/resource/file/subdirectory/:id',
+      name: 'resource-file-subdirectory',
+      component: components['file'],
+      meta: {
+        title: '文件管理',
+      },
+    },
+    {
+      path: '/resource/file/list/:id',
+      name: 'resource-file-list',
+      component: components['resource-file-edit'],
+      meta: {
+        title: '文件详情',
+      },
+    },
+    {
+      path: '/resource/file/create/:id',
+      name: 'resource-subfile-create',
+      component: components['resource-file-create'],
+      meta: {
+        title: '文件创建',
       },
     },
   ],
diff --git a/dolphinscheduler-ui-next/src/service/modules/resources/index.ts b/dolphinscheduler-ui-next/src/service/modules/resources/index.ts
index 9fce316..b1e1ce3 100644
--- a/dolphinscheduler-ui-next/src/service/modules/resources/index.ts
+++ b/dolphinscheduler-ui-next/src/service/modules/resources/index.ts
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-import { axios } from '@/service/service'
+import { axios, downloadFile } from '@/service/service'
 import {
   FileReq,
   ResourceTypeReq,
@@ -37,7 +37,7 @@ import {
 } from './types'
 
 export function queryResourceListPaging(
-  params: ListReq & IdReq & ResourceTypeReq
+  params: ListReq & IdReq & ResourceTypeReq,
 ): any {
   return axios({
     url: '/resources',
@@ -47,7 +47,7 @@ export function queryResourceListPaging(
 }
 
 export function createResource(
-  data: CreateReq & FileNameReq & NameReq & ResourceTypeReq
+  data: CreateReq & FileNameReq & NameReq & ResourceTypeReq,
 ): any {
   return axios({
     url: '/resources',
@@ -81,7 +81,7 @@ export function authUDFFunc(params: UserIdReq): any {
 }
 
 export function createDirectory(
-  data: CreateReq & NameReq & ResourceTypeReq
+  data: CreateReq & NameReq & ResourceTypeReq,
 ): any {
   return axios({
     url: '/resources/directory',
@@ -99,7 +99,7 @@ export function queryResourceList(params: ResourceTypeReq): any {
 }
 
 export function onlineCreateResource(
-  data: OnlineCreateReq & FileNameReq & ResourceTypeReq
+  data: OnlineCreateReq & FileNameReq & ResourceTypeReq,
 ): any {
   return axios({
     url: '/resources/online-create',
@@ -109,7 +109,7 @@ export function onlineCreateResource(
 }
 
 export function queryResourceByProgramType(
-  params: ResourceTypeReq & ProgramTypeReq
+  params: ResourceTypeReq & ProgramTypeReq,
 ): any {
   return axios({
     url: '/resources/query-by-type',
@@ -151,7 +151,7 @@ export function deleteUdfFunc(id: IdReq): any {
 
 export function unAuthUDFFunc(params: UserIdReq): any {
   return axios({
-    url: `/resources/unauth-udf-func`,
+    url: '/resources/unauth-udf-func',
     method: 'get',
     params,
   })
@@ -167,7 +167,7 @@ export function verifyResourceName(params: FullNameReq & ResourceTypeReq): any {
 
 export function queryResource(
   params: FullNameReq & ResourceTypeReq,
-  id: IdReq
+  id: IdReq,
 ): any {
   return axios({
     url: `/resources/verify-name/${id}`,
@@ -177,8 +177,8 @@ export function queryResource(
 }
 
 export function updateResource(
-  data: NameReq & ResourceTypeReq,
-  id: IdReq
+  data: NameReq & ResourceTypeReq & IdReq & DescriptionReq,
+  id: number,
 ): any {
   return axios({
     url: `/resources/${id}`,
@@ -187,18 +187,15 @@ export function updateResource(
   })
 }
 
-export function deleteResource(id: IdReq): any {
+export function deleteResource(id: number): any {
   return axios({
     url: `/resources/${id}`,
     method: 'delete',
   })
 }
 
-export function downloadResource(id: IdReq): any {
-  return axios({
-    url: `/resources/${id}/download`,
-    method: 'get',
-  })
+export function downloadResource(id: number): void {
+  downloadFile(`resources/${id}/download`)
 }
 
 export function viewUIUdfFunction(id: IdReq): any {
@@ -208,7 +205,7 @@ export function viewUIUdfFunction(id: IdReq): any {
   })
 }
 
-export function updateResourceContent(data: ContentReq, id: IdReq): any {
+export function updateResourceContent(data: ContentReq, id: number): any {
   return axios({
     url: `/resources/${id}/update-content`,
     method: 'put',
@@ -216,7 +213,7 @@ export function updateResourceContent(data: ContentReq, id: IdReq): any {
   })
 }
 
-export function viewResource(params: ViewResourceReq, id: IdReq): any {
+export function viewResource(params: ViewResourceReq, id: number): any {
   return axios({
     url: `/resources/${id}/view`,
     method: 'get',
@@ -226,7 +223,7 @@ export function viewResource(params: ViewResourceReq, id: IdReq): any {
 
 export function createUdfFunc(
   data: UdfFuncReq,
-  resourceId: ResourceIdReq
+  resourceId: ResourceIdReq,
 ): any {
   return axios({
     url: `/resources/${resourceId}/udf-func`,
@@ -238,7 +235,7 @@ export function createUdfFunc(
 export function updateUdfFunc(
   data: UdfFuncReq,
   resourceId: ResourceIdReq,
-  id: IdReq
+  id: IdReq,
 ): any {
   return axios({
     url: `/resources/${resourceId}/udf-func/${id}`,
diff --git a/dolphinscheduler-ui-next/src/service/modules/resources/types.ts b/dolphinscheduler-ui-next/src/service/modules/resources/types.ts
index 79ec58b..2102ba6 100644
--- a/dolphinscheduler-ui-next/src/service/modules/resources/types.ts
+++ b/dolphinscheduler-ui-next/src/service/modules/resources/types.ts
@@ -90,6 +90,28 @@ interface UdfFuncReq extends UdfTypeReq, DescriptionReq {
   database?: string
 }
 
+interface ResourceFile {
+  id: number
+  pid: number
+  alias: string
+  userId: number
+  type: string
+  directory: boolean
+  fileName: string
+  fullName: string
+  description: string
+  size: number
+  updateTime: string
+}
+
+interface ResourceListRes {
+  currentPage: number
+  pageSize: number
+  start: number
+  total: number
+  totalList: ResourceFile[]
+}
+
 export {
   FileReq,
   ResourceTypeReq,
@@ -108,4 +130,5 @@ export {
   ViewResourceReq,
   ResourceIdReq,
   UdfFuncReq,
+  ResourceListRes,
 }
diff --git a/dolphinscheduler-ui-next/src/service/service.ts b/dolphinscheduler-ui-next/src/service/service.ts
index 6c3bfc1..d357d9d 100644
--- a/dolphinscheduler-ui-next/src/service/service.ts
+++ b/dolphinscheduler-ui-next/src/service/service.ts
@@ -22,6 +22,7 @@ import axios, {
   AxiosRequestHeaders,
 } from 'axios'
 import qs from 'qs'
+import _ from 'lodash'
 import { useUserStore } from '@/store/user/user'
 
 const userStore = useUserStore()
@@ -30,7 +31,12 @@ const baseRequestConfig: AxiosRequestConfig = {
   baseURL: import.meta.env.VITE_APP_WEB_URL + '/dolphinscheduler',
   timeout: 10000,
   transformRequest: (params) => {
-    return qs.stringify(params, { arrayFormat: 'repeat' })
+    if (_.isPlainObject(params)) {
+      console.log(params)
+      return qs.stringify(params, { arrayFormat: 'repeat' })
+    } else {
+      return params
+    }
   },
   paramsSerializer: (params) => {
     return qs.stringify(params, { arrayFormat: 'repeat' })
@@ -66,4 +72,46 @@ service.interceptors.response.use((res: AxiosResponse) => {
   }
 }, err)
 
-export { service as axios }
+const apiPrefix = '/dolphinscheduler'
+const reSlashPrefix = /^\/+/
+
+const resolveURL = (url: string) => {
+  if (url.indexOf('http') === 0) {
+    return url
+  }
+  if (url.charAt(0) !== '/') {
+    return `${apiPrefix}/${url.replace(reSlashPrefix, '')}`
+  }
+
+  return url
+}
+
+/**
+ * download file
+ */
+const downloadFile = (url: string, obj?: any) => {
+  const param: any = {
+    url: resolveURL(url),
+    obj: obj || {},
+  }
+
+  const form = document.createElement('form')
+  form.action = param.url
+  form.method = 'get'
+  form.style.display = 'none'
+  Object.keys(param.obj).forEach((key) => {
+    const input = document.createElement('input')
+    input.type = 'hidden'
+    input.name = key
+    input.value = param.obj[key]
+    form.appendChild(input)
+  })
+  const button = document.createElement('input')
+  button.type = 'submit'
+  form.appendChild(button)
+  document.body.appendChild(form)
+  form.submit()
+  document.body.removeChild(form)
+}
+
+export { service as axios, downloadFile }
diff --git a/dolphinscheduler-ui-next/src/store/menu/menu.ts b/dolphinscheduler-ui-next/src/store/file/file.ts
similarity index 67%
copy from dolphinscheduler-ui-next/src/store/menu/menu.ts
copy to dolphinscheduler-ui-next/src/store/file/file.ts
index b86cdc7..96fb868 100644
--- a/dolphinscheduler-ui-next/src/store/menu/menu.ts
+++ b/dolphinscheduler-ui-next/src/store/file/file.ts
@@ -16,22 +16,29 @@
  */
 
 import { defineStore } from 'pinia'
-import MenuState from './types'
+import type { FileState } from '@/store/file/types'
 
-export const useMenuStore = defineStore({
-  id: 'menu',
-  state: (): MenuState => ({
-    menuKey: 'home',
+export const useFileStore = defineStore({
+  id: 'file',
+  state: (): FileState => ({
+    file: '',
+    currentDir: '/',
   }),
   persist: true,
   getters: {
-    getMenuKey(): string {
-      return this.menuKey
+    getFileInfo(): string {
+      return this.file
+    },
+    getCurrentDir(): string {
+      return this.currentDir
     },
   },
   actions: {
-    setMenuKey(menuKey: string): void {
-      this.menuKey = menuKey
+    setFileInfo(file: string): void {
+      this.file = file
+    },
+    setCurrentDir(currentDir: string): void {
+      this.currentDir = currentDir
     },
   },
 })
diff --git a/dolphinscheduler-ui-next/src/env.d.ts b/dolphinscheduler-ui-next/src/store/file/types.ts
similarity index 72%
copy from dolphinscheduler-ui-next/src/env.d.ts
copy to dolphinscheduler-ui-next/src/store/file/types.ts
index 776e88b..57ef122 100644
--- a/dolphinscheduler-ui-next/src/env.d.ts
+++ b/dolphinscheduler-ui-next/src/store/file/types.ts
@@ -15,13 +15,9 @@
  * limitations under the License.
  */
 
-declare module '*.vue' {
-  import { DefineComponent } from 'vue'
-  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
-  const component: DefineComponent<{}, {}, any>
-  export default component
+interface FileState {
+  file: string
+  currentDir: string
 }
 
-declare module '*.png'
-declare module '*.jpg'
-declare module '*.jpeg'
+export { FileState }
diff --git a/dolphinscheduler-ui-next/src/store/menu/menu.ts b/dolphinscheduler-ui-next/src/store/menu/menu.ts
index b86cdc7..b4c5c09 100644
--- a/dolphinscheduler-ui-next/src/store/menu/menu.ts
+++ b/dolphinscheduler-ui-next/src/store/menu/menu.ts
@@ -26,7 +26,7 @@ export const useMenuStore = defineStore({
   persist: true,
   getters: {
     getMenuKey(): string {
-      return this.menuKey
+      return this.menuKey || 'home'
     },
   },
   actions: {
diff --git a/dolphinscheduler-ui-next/src/env.d.ts b/dolphinscheduler-ui-next/src/utils/common.ts
similarity index 59%
copy from dolphinscheduler-ui-next/src/env.d.ts
copy to dolphinscheduler-ui-next/src/utils/common.ts
index 776e88b..15e7ca0 100644
--- a/dolphinscheduler-ui-next/src/env.d.ts
+++ b/dolphinscheduler-ui-next/src/utils/common.ts
@@ -15,13 +15,34 @@
  * limitations under the License.
  */
 
-declare module '*.vue' {
-  import { DefineComponent } from 'vue'
-  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
-  const component: DefineComponent<{}, {}, any>
-  export default component
+/**
+ * Intelligent display kb m
+ */
+export const bytesToSize = (bytes: number) => {
+  if (bytes === 0) return '0 B'
+  const k = 1024 // or 1024
+  const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
+  const i = Math.floor(Math.log(bytes) / Math.log(k))
+
+  return parseInt((bytes / Math.pow(k, i)).toPrecision(3)) + ' ' + sizes[i]
 }
 
-declare module '*.png'
-declare module '*.jpg'
-declare module '*.jpeg'
+export const fileTypeArr = [
+  'txt',
+  'log',
+  'sh',
+  'bat',
+  'conf',
+  'cfg',
+  'py',
+  'java',
+  'sql',
+  'xml',
+  'hql',
+  'properties',
+  'json',
+  'yml',
+  'yaml',
+  'ini',
+  'js',
+]
diff --git a/dolphinscheduler-ui-next/src/views/resource/file/create/resource-file-create.tsx b/dolphinscheduler-ui-next/src/views/resource/file/create/resource-file-create.tsx
new file mode 100644
index 0000000..ccebfdc
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/resource/file/create/resource-file-create.tsx
@@ -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.
+ */
+
+import { defineComponent, ref, toRefs } from 'vue'
+import { useRouter } from 'vue-router'
+import { NForm, NFormItem, NInput, NSelect, NButton } from 'naive-ui'
+import { useI18n } from 'vue-i18n'
+
+import Card from '@/components/card'
+import MonacoEditor from '@/components/monaco-editor'
+import { useCreate } from './use-create'
+import { useForm } from './use-form'
+import { fileTypeArr } from '@/utils/common'
+
+import styles from '../index.module.scss'
+
+import type { Router } from 'vue-router'
+
+export default defineComponent({
+  name: 'ResourceFileCreate',
+  setup() {
+    const router: Router = useRouter()
+
+    const codeEditorRef = ref()
+    const { state } = useForm()
+    const { handleCreateFile } = useCreate(state)
+
+    const fileSuffixOptions = fileTypeArr.map((suffix) => ({
+      key: suffix,
+      label: suffix,
+      value: suffix,
+    }))
+
+    const handleFile = () => {
+      state.fileForm.content = codeEditorRef.value?.getValue()
+      handleCreateFile()
+    }
+
+    const handleReturn = () => {
+      const { id } = router.currentRoute.value.params
+      const name = id ? 'resource-file-subdirectory' : 'file'
+      router.push({ name, params: { id } })
+    }
+
+    return {
+      codeEditorRef,
+      fileSuffixOptions,
+      handleFile,
+      handleReturn,
+      ...toRefs(state),
+    }
+  },
+  render() {
+    const { t } = useI18n()
+    return (
+      <Card title={t('resource.file.file_details')}>
+        <NForm
+          rules={this.rules}
+          ref='fileFormRef'
+          label-placement='left'
+          label-width='160'
+          class={styles['form-content']}
+        >
+          <NFormItem label={t('resource.file.file_name')} path='fileName'>
+            <NInput
+              v-model={[this.fileForm.fileName, 'value']}
+              placeholder={t('resource.file.enter_name_tips')}
+              style={{ width: '300px' }}
+            />
+          </NFormItem>
+          <NFormItem label={t('resource.file.file_format')} path='suffix'>
+            <NSelect
+              defaultValue={[this.fileForm.suffix]}
+              v-model={[this.fileForm.suffix, 'value']}
+              options={this.fileSuffixOptions}
+              style={{ width: '100px' }}
+            />
+          </NFormItem>
+          <NFormItem label={t('resource.file.description')} path='description'>
+            <NInput
+              type='textarea'
+              v-model={[this.fileForm.description, 'value']}
+              placeholder={t('resource.file.enter_description_tips')}
+              style={{ width: '430px' }}
+            />
+          </NFormItem>
+          <NFormItem label={t('resource.file.file_content')} path='content'>
+            <div
+              class={styles.cont}
+              style={{
+                width: '90%',
+              }}
+            >
+              <MonacoEditor ref='codeEditorRef' />
+            </div>
+          </NFormItem>
+          <div class={styles['file-edit-content']}>
+            <div class={styles.submit}>
+              <NButton type='info' size='small' round onClick={this.handleFile}>
+                {t('resource.file.save')}
+              </NButton>
+              <NButton
+                type='info'
+                size='small'
+                text
+                style={{ marginLeft: '15px' }}
+                onClick={this.handleReturn}
+              >
+                {t('resource.file.return')}
+              </NButton>
+            </div>
+          </div>
+        </NForm>
+      </Card>
+    )
+  },
+})
diff --git a/dolphinscheduler-ui-next/src/views/resource/file/create/use-create.ts b/dolphinscheduler-ui-next/src/views/resource/file/create/use-create.ts
new file mode 100644
index 0000000..dc03a3d
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/resource/file/create/use-create.ts
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { useI18n } from 'vue-i18n'
+import { useRouter } from 'vue-router'
+import type { Router } from 'vue-router'
+import { useFileStore } from '@/store/file/file'
+import { onlineCreateResource } from '@/service/modules/resources'
+
+export function useCreate(state: any) {
+  const { t } = useI18n()
+  const router: Router = useRouter()
+  const fileStore = useFileStore()
+
+  const handleCreateFile = () => {
+    const pid = router.currentRoute.value.params.id || -1
+    const currentDir = fileStore.getCurrentDir || '/'
+    state.fileFormRef.validate(async (valid: any) => {
+      if (!valid) {
+        try {
+          await onlineCreateResource({
+            ...state.fileForm,
+            ...{ pid, currentDir },
+          })
+
+          window.$message.success(t('resource.file.success'))
+          const name = pid ? 'resource-file-subdirectory' : 'file'
+          router.push({ name, params: { id: pid } })
+        } catch (error: any) {
+          window.$message.error(error.message)
+        }
+      }
+    })
+  }
+
+  return {
+    handleCreateFile,
+  }
+}
diff --git a/dolphinscheduler-ui-next/src/views/resource/file/create/use-form.ts b/dolphinscheduler-ui-next/src/views/resource/file/create/use-form.ts
new file mode 100644
index 0000000..b0e7573
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/resource/file/create/use-form.ts
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { useI18n } from 'vue-i18n'
+import { reactive, ref, unref } from 'vue'
+import type { FormRules } from 'naive-ui'
+
+const defaultValue = () => ({
+  pid: -1,
+  type: 'FILE',
+  suffix: 'sh',
+  fileName: '',
+  description: '',
+  content: '',
+  currentDir: '/',
+})
+
+export function useForm() {
+  const { t } = useI18n()
+
+  const resetForm = () => {
+    state.fileForm = Object.assign(unref(state.fileForm), defaultValue())
+  }
+
+  const state = reactive({
+    fileFormRef: ref(),
+    fileForm: defaultValue(),
+    rules: {
+      fileName: {
+        required: true,
+        trigger: ['input', 'blur'],
+        validator() {
+          if (state.fileForm.fileName === '') {
+            return new Error(t('resource.file.enter_name_tips'))
+          }
+        },
+      },
+      suffix: {
+        required: true,
+        trigger: ['input', 'blur'],
+        validator() {
+          if (state.fileForm.suffix === '') {
+            return new Error(t('resource.file.enter_suffix_tips'))
+          }
+        },
+      },
+      content: {
+        required: true,
+        trigger: ['input', 'blur'],
+        validator() {
+          if (state.fileForm.content === '') {
+            return new Error(t('resource.file.enter_content_tips'))
+          }
+        },
+      },
+    } as FormRules,
+  })
+
+  return {
+    state,
+    resetForm,
+  }
+}
diff --git a/dolphinscheduler-ui-next/src/views/resource/file/edit/resource-file-edit.tsx b/dolphinscheduler-ui-next/src/views/resource/file/edit/resource-file-edit.tsx
new file mode 100644
index 0000000..2cadb93
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/resource/file/edit/resource-file-edit.tsx
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { useRouter } from 'vue-router'
+import { defineComponent, onMounted, ref, toRefs } from 'vue'
+import { NButton, NForm, NFormItem, NSpace } from 'naive-ui'
+import { useI18n } from 'vue-i18n'
+import Card from '@/components/card'
+import MonacoEditor from '@/components/monaco-editor'
+import { useForm } from './use-form'
+import { useEdit } from './use-edit'
+
+import styles from '../index.module.scss'
+import type { Router } from 'vue-router'
+
+export default defineComponent({
+  name: 'ResourceFileEdit',
+  setup() {
+    const router: Router = useRouter()
+
+    const resourceViewRef = ref()
+    const codeEditorRef = ref()
+    const routeNameRef = ref(router.currentRoute.value.name)
+    const idRef = ref(Number(router.currentRoute.value.params.id))
+
+    const { state } = useForm()
+    const { getResourceView, handleUpdateContent } = useEdit(state)
+
+    const handleFileContent = () => {
+      state.fileForm.content = codeEditorRef.value?.getValue()
+      handleUpdateContent(idRef.value)
+    }
+
+    const handleReturn = () => {
+      router.go(-1)
+    }
+
+    onMounted(() => {
+      resourceViewRef.value = getResourceView(idRef.value)
+    })
+
+    return {
+      idRef,
+      routeNameRef,
+      codeEditorRef,
+      resourceViewRef,
+      handleReturn,
+      handleFileContent,
+      ...toRefs(state),
+    }
+  },
+  render() {
+    const { t } = useI18n()
+    return (
+      <Card title={t('resource.file.file_details')}>
+        <div class={styles['file-edit-content']}>
+          <h2>
+            <span>{this.resourceViewRef?.value.alias}</span>
+          </h2>
+          <NForm
+            rules={this.rules}
+            ref='fileFormRef'
+            class={styles['form-content']}
+          >
+            <NFormItem path='content'>
+              <div
+                class={styles.cont}
+                style={{
+                  width: '90%',
+                }}
+              >
+                <MonacoEditor
+                  ref='codeEditorRef'
+                  modelValue={this.resourceViewRef?.value.content}
+                />
+              </div>
+            </NFormItem>
+            {this.routeNameRef === 'resource-file-edit' && (
+              <NSpace>
+                <NButton
+                  type='info'
+                  size='small'
+                  text
+                  style={{ marginRight: '15px' }}
+                  onClick={this.handleReturn}
+                >
+                  {t('resource.file.return')}
+                </NButton>
+                <NButton
+                  type='info'
+                  size='small'
+                  round
+                  onClick={() => this.handleFileContent()}
+                >
+                  {t('resource.file.save')}
+                </NButton>
+              </NSpace>
+            )}
+          </NForm>
+        </div>
+      </Card>
+    )
+  },
+})
diff --git a/dolphinscheduler-ui-next/src/views/resource/file/edit/use-edit.ts b/dolphinscheduler-ui-next/src/views/resource/file/edit/use-edit.ts
new file mode 100644
index 0000000..f4cea46
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/resource/file/edit/use-edit.ts
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { useI18n } from 'vue-i18n'
+import { useRouter } from 'vue-router'
+import type { Router } from 'vue-router'
+import { useAsyncState } from '@vueuse/core'
+import {
+  viewResource,
+  updateResourceContent,
+} from '@/service/modules/resources'
+
+export function useEdit(state: any) {
+  const { t } = useI18n()
+  const router: Router = useRouter()
+
+  const getResourceView = (id: number) => {
+    const params = {
+      skipLineNum: 0,
+      limit: 3000,
+    }
+    const { state } = useAsyncState(viewResource(params, id), {})
+    return state
+  }
+
+  const handleUpdateContent = (id: number) => {
+    state.fileFormRef.validate(async (valid: any) => {
+      if (!valid) {
+        try {
+          await updateResourceContent(
+            {
+              ...state.fileForm,
+            },
+            id,
+          )
+
+          window.$message.success(t('resource.file.success'))
+          router.go(-1)
+        } catch (error: any) {
+          window.$message.error(error.message)
+        }
+      }
+    })
+  }
+
+  return {
+    getResourceView,
+    handleUpdateContent,
+  }
+}
diff --git a/dolphinscheduler-ui-next/src/components/card/index.tsx b/dolphinscheduler-ui-next/src/views/resource/file/edit/use-form.ts
similarity index 52%
copy from dolphinscheduler-ui-next/src/components/card/index.tsx
copy to dolphinscheduler-ui-next/src/views/resource/file/edit/use-form.ts
index 9ad5602..68dbf65 100644
--- a/dolphinscheduler-ui-next/src/components/card/index.tsx
+++ b/dolphinscheduler-ui-next/src/views/resource/file/edit/use-form.ts
@@ -15,40 +15,39 @@
  * limitations under the License.
  */
 
-import { defineComponent, PropType } from 'vue'
-import { NCard } from 'naive-ui'
+import { reactive, ref, unref } from 'vue'
+import { useI18n } from 'vue-i18n'
+import type { FormRules } from 'naive-ui'
 
-const headerStyle = {
-  borderBottom: '1px solid var(--n-border-color)',
-}
+const defaultValue = () => ({
+  content: '',
+})
 
-const contentStyle = {
-  padding: '8px 10px',
-}
+export function useForm() {
+  const { t } = useI18n()
 
-const props = {
-  title: {
-    type: String as PropType<string>,
-    required: true,
-  },
-}
+  const resetForm = () => {
+    state.fileForm = Object.assign(unref(state.fileForm), defaultValue())
+  }
 
-const Card = defineComponent({
-  name: 'Card',
-  props,
-  render() {
-    const { title, $slots } = this
-    return (
-      <NCard
-        title={title}
-        size='small'
-        headerStyle={headerStyle}
-        contentStyle={contentStyle}
-      >
-        {$slots}
-      </NCard>
-    )
-  },
-})
+  const state = reactive({
+    fileFormRef: ref(),
+    fileForm: defaultValue(),
+    rules: {
+      content: {
+        required: true,
+        trigger: ['input', 'blur'],
+        validator() {
+          if (state.fileForm.content === '') {
+            return new Error(t('resource.file.enter_content_tips'))
+          }
+        },
+      },
+    } as FormRules,
+  })
 
-export default Card
+  return {
+    state,
+    resetForm,
+  }
+}
diff --git a/dolphinscheduler-ui-next/src/views/resource/file/folder/index.tsx b/dolphinscheduler-ui-next/src/views/resource/file/folder/index.tsx
new file mode 100644
index 0000000..bc2618a
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/resource/file/folder/index.tsx
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { defineComponent, toRefs, PropType } from 'vue'
+import { NForm, NFormItem, NInput } from 'naive-ui'
+import { useI18n } from 'vue-i18n'
+import Modal from '@/components/modal'
+import { useForm } from './use-form'
+import { useFolder } from './use-folder'
+
+const props = {
+  show: {
+    type: Boolean as PropType<boolean>,
+    default: false,
+  },
+}
+
+export default defineComponent({
+  name: 'ResourceFileFolder',
+  props,
+  emits: ['updateList', 'update:show'],
+  setup(props, ctx) {
+    const { state, resetForm } = useForm()
+    const { handleCreateFolder } = useFolder(state)
+
+    const hideModal = () => {
+      ctx.emit('update:show')
+    }
+
+    const handleFolder = () => {
+      handleCreateFolder(ctx.emit, hideModal, resetForm)
+    }
+
+    return {
+      hideModal,
+      handleFolder,
+      ...toRefs(state),
+    }
+  },
+  render() {
+    const { t } = useI18n()
+    return (
+      <Modal
+        show={this.$props.show}
+        title={t('resource.file.create_folder')}
+        onCancel={this.hideModal}
+        onConfirm={this.handleFolder}
+      >
+        <NForm
+          rules={this.rules}
+          ref='folderFormRef'
+          label-placement='left'
+          label-width='160'
+        >
+          <NFormItem label={t('resource.file.folder_name')} path='name'>
+            <NInput
+              v-model={[this.folderForm.name, 'value']}
+              placeholder={t('resource.file.enter_name_tips')}
+            />
+          </NFormItem>
+          <NFormItem label={t('resource.file.description')} path='description'>
+            <NInput
+              type='textarea'
+              v-model={[this.folderForm.description, 'value']}
+              placeholder={t('resource.file.enter_description_tips')}
+            />
+          </NFormItem>
+        </NForm>
+      </Modal>
+    )
+  },
+})
diff --git a/dolphinscheduler-ui-next/src/views/resource/file/folder/use-folder.ts b/dolphinscheduler-ui-next/src/views/resource/file/folder/use-folder.ts
new file mode 100644
index 0000000..0b4599c
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/resource/file/folder/use-folder.ts
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { useI18n } from 'vue-i18n'
+import { IEmit } from '../types'
+import { useRouter } from 'vue-router'
+import type { Router } from 'vue-router'
+import { useFileStore } from '@/store/file/file'
+import { createDirectory } from '@/service/modules/resources'
+
+export function useFolder(state: any) {
+  const { t } = useI18n()
+  const router: Router = useRouter()
+  const fileStore = useFileStore()
+
+  const handleCreateFolder = (
+    emit: IEmit,
+    hideModal: () => void,
+    resetForm: () => void,
+  ) => {
+    const pid = router.currentRoute.value.params.id || -1
+    const currentDir = fileStore.getCurrentDir || '/'
+    state.folderFormRef.validate(async (valid: any) => {
+      if (!valid) {
+        try {
+          await createDirectory({
+            ...state.folderForm,
+            ...{ pid, currentDir },
+          })
+
+          window.$message.success(t('resource.file.success'))
+          emit('updateList')
+        } catch (error: any) {
+          window.$message.error(error.message)
+        }
+        hideModal()
+        resetForm()
+      }
+    })
+  }
+
+  return {
+    handleCreateFolder,
+  }
+}
diff --git a/dolphinscheduler-ui-next/src/router/modules/resources.ts b/dolphinscheduler-ui-next/src/views/resource/file/folder/use-form.ts
similarity index 50%
copy from dolphinscheduler-ui-next/src/router/modules/resources.ts
copy to dolphinscheduler-ui-next/src/views/resource/file/folder/use-form.ts
index 6ea940a..be8e279 100644
--- a/dolphinscheduler-ui-next/src/router/modules/resources.ts
+++ b/dolphinscheduler-ui-next/src/views/resource/file/folder/use-form.ts
@@ -15,35 +15,43 @@
  * limitations under the License.
  */
 
-import type { Component } from 'vue'
-import utils from '@/utils'
+import { reactive, ref, unref } from 'vue'
+import { useI18n } from 'vue-i18n'
+import type { FormRules } from 'naive-ui'
 
-// All TSX files under the views folder automatically generate mapping relationship
-const modules = import.meta.glob('/src/views/**/**.tsx')
-const components: { [key: string]: Component } = utils.mapping(modules)
+const defaultValue = () => ({
+  pid: -1,
+  type: 'FILE',
+  name: '',
+  description: '',
+  currentDir: '/',
+})
 
-export default {
-  path: '/resource',
-  name: 'resource',
-  redirect: { name: 'file' },
-  meta: { title: '资源中心' },
-  component: () => import('@/layouts/content'),
-  children: [
-    {
-      path: '/resource/file',
-      name: 'file',
-      component: components['home'],
-      meta: {
-        title: '文件管理',
-      },
-    },
-    {
-      path: '/resource/file/create',
-      name: 'resource-file-create',
-      component: components['home'],
-      meta: {
-        title: '创建资源',
+export function useForm() {
+  const { t } = useI18n()
+
+  const resetForm = () => {
+    state.folderForm = Object.assign(unref(state.folderForm), defaultValue())
+  }
+
+  const state = reactive({
+    folderFormRef: ref(),
+    folderForm: defaultValue(),
+    rules: {
+      name: {
+        required: true,
+        trigger: ['input', 'blur'],
+        validator() {
+          if (state.folderForm.name === '') {
+            return new Error(t('resource.file.enter_name_tips'))
+          }
+        },
       },
-    },
-  ],
+    } as FormRules,
+  })
+
+  return {
+    state,
+    resetForm,
+  }
 }
diff --git a/dolphinscheduler-ui-next/src/components/card/index.tsx b/dolphinscheduler-ui-next/src/views/resource/file/index.module.scss
similarity index 50%
copy from dolphinscheduler-ui-next/src/components/card/index.tsx
copy to dolphinscheduler-ui-next/src/views/resource/file/index.module.scss
index 9ad5602..a6c089e 100644
--- a/dolphinscheduler-ui-next/src/components/card/index.tsx
+++ b/dolphinscheduler-ui-next/src/views/resource/file/index.module.scss
@@ -15,40 +15,57 @@
  * limitations under the License.
  */
 
-import { defineComponent, PropType } from 'vue'
-import { NCard } from 'naive-ui'
-
-const headerStyle = {
-  borderBottom: '1px solid var(--n-border-color)',
+.file-edit-content {
+  width: 100%;
+  background: #fff;
+  padding-bottom: 20px;
+  >h2 {
+    line-height: 60px;
+    text-align: center;
+    padding-right: 170px;
+    position: relative;
+  }
 }
 
-const contentStyle = {
-  padding: '8px 10px',
+.form-content {
+  padding: 0 50px 0 50px;
 }
 
-const props = {
-  title: {
-    type: String as PropType<string>,
-    required: true,
-  },
+.submit {
+  text-align: left;
+  padding-top: 12px;
+  margin-left: 160px;
 }
 
-const Card = defineComponent({
-  name: 'Card',
-  props,
-  render() {
-    const { title, $slots } = this
-    return (
-      <NCard
-        title={title}
-        size='small'
-        headerStyle={headerStyle}
-        contentStyle={contentStyle}
-      >
-        {$slots}
-      </NCard>
-    )
-  },
-})
+.pagination {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  margin-top: 20px;
+}
 
-export default Card
+.table-box {
+  table {
+    width: 100%;
+    tr {
+      height: 40px;
+      font-size: 12px;
+      th,td{
+        &:nth-child(1) {
+          width: 50px;
+          text-align: center;
+        }
+      }
+      th {
+        &:nth-child(1) {
+          width: 60px;
+          text-align: center;
+        }
+        >span {
+          font-size: 12px;
+          color: #555;
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/dolphinscheduler-ui-next/src/views/resource/file/index.tsx b/dolphinscheduler-ui-next/src/views/resource/file/index.tsx
new file mode 100644
index 0000000..f61aac2
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/resource/file/index.tsx
@@ -0,0 +1,228 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { useRouter } from 'vue-router'
+import {
+  defineComponent,
+  onMounted,
+  ref,
+  reactive,
+  Ref,
+  watch,
+  inject,
+} from 'vue'
+import { NDataTable, NButtonGroup, NButton, NPagination } from 'naive-ui'
+import { useI18n } from 'vue-i18n'
+import Card from '@/components/card'
+import Conditions from '@/components/conditions'
+import { useTable } from './table/use-table'
+import { useFileState } from './use-file'
+import ResourceFolderModal from './folder'
+import ResourceUploadModal from './upload'
+import ResourceRenameModal from './rename'
+import { IRenameFile } from './types'
+import type { Router } from 'vue-router'
+import styles from './index.module.scss'
+
+export default defineComponent({
+  name: 'File',
+  inject: ['reload'],
+  setup() {
+    const router: Router = useRouter()
+    const fileId = ref(Number(router.currentRoute.value.params.id) || -1)
+
+    const reload = inject('reload')
+    const resourceListRef = ref()
+    const folderShowRef = ref(false)
+    const uploadShowRef = ref(false)
+    const renameShowRef = ref(false)
+    const serachRef = ref()
+
+    const renameInfo = reactive({
+      id: -1,
+      name: '',
+      description: '',
+    })
+
+    const paginationReactive = reactive({
+      page: 1,
+      pageSize: 10,
+      itemCount: 0,
+      pageSizes: [10, 30, 50],
+    })
+
+    const handleUpdatePage = (page: number) => {
+      paginationReactive.page = page
+      resourceListRef.value = getResourceListState(
+        fileId.value,
+        serachRef.value,
+        paginationReactive.page,
+        paginationReactive.pageSize,
+      )
+    }
+
+    const handleUpdatePageSize = (pageSize: number) => {
+      paginationReactive.page = 1
+      paginationReactive.pageSize = pageSize
+      resourceListRef.value = getResourceListState(
+        fileId.value,
+        serachRef.value,
+        paginationReactive.page,
+        paginationReactive.pageSize,
+      )
+    }
+
+    const handleShowModal = (showRef: Ref<Boolean>) => {
+      showRef.value = true
+    }
+
+    const setPagination = (count: number) => {
+      paginationReactive.itemCount = count
+    }
+
+    const { getResourceListState } = useFileState(setPagination)
+
+    const handleConditions = (val: string) => {
+      serachRef.value = val
+      resourceListRef.value = getResourceListState(fileId.value, val)
+    }
+
+    const handleCreateFolder = () => {
+      handleShowModal(folderShowRef)
+    }
+
+    const handleCreateFile = () => {
+      const name = fileId.value
+        ? 'resource-subfile-create'
+        : 'resource-file-create'
+      router.push({
+        name,
+        params: { id: fileId.value },
+      })
+    }
+
+    const handleUploadFile = () => {
+      handleShowModal(uploadShowRef)
+    }
+
+    const handleRenameFile: IRenameFile = (id, name, description) => {
+      renameInfo.id = id
+      renameInfo.name = name
+      renameInfo.description = description
+      handleShowModal(renameShowRef)
+    }
+
+    const updateList = () => {
+      resourceListRef.value = getResourceListState(
+        fileId.value,
+        serachRef.value,
+      )
+    }
+
+    onMounted(() => {
+      resourceListRef.value = getResourceListState(fileId.value)
+    })
+
+    watch(
+      () => router.currentRoute.value.params.id,
+      () => reload(),
+    )
+
+    return {
+      fileId,
+      folderShowRef,
+      uploadShowRef,
+      renameShowRef,
+      handleShowModal,
+      resourceListRef,
+      updateList,
+      handleConditions,
+      handleCreateFolder,
+      handleCreateFile,
+      handleUploadFile,
+      handleRenameFile,
+      handleUpdatePage,
+      handleUpdatePageSize,
+      pagination: paginationReactive,
+      renameInfo,
+    }
+  },
+  render() {
+    const { t } = useI18n()
+    const { columnsRef } = useTable(this.handleRenameFile, this.updateList)
+    const {
+      handleConditions,
+      handleCreateFolder,
+      handleCreateFile,
+      handleUploadFile,
+    } = this
+    return (
+      <div>
+        <Conditions onConditions={handleConditions}>
+          <NButtonGroup>
+            <NButton onClick={handleCreateFolder}>
+              {t('resource.file.create_folder')}
+            </NButton>
+            <NButton onClick={handleCreateFile}>
+              {t('resource.file.create_file')}
+            </NButton>
+            <NButton onClick={handleUploadFile}>
+              {t('resource.file.upload_files')}
+            </NButton>
+          </NButtonGroup>
+        </Conditions>
+        <Card title={t('resource.file.file_manage')}>
+          <NDataTable
+            remote
+            columns={columnsRef}
+            data={this.resourceListRef?.value.table}
+            striped
+            size={'small'}
+            class={styles['table-box']}
+          />
+          <div class={styles.pagination}>
+            <NPagination
+              v-model:page={this.pagination.page}
+              v-model:pageSize={this.pagination.pageSize}
+              pageSizes={this.pagination.pageSizes}
+              item-count={this.pagination.itemCount}
+              onUpdatePage={this.handleUpdatePage}
+              onUpdatePageSize={this.handleUpdatePageSize}
+              show-quick-jumper
+              show-size-picker
+            />
+          </div>
+          <ResourceFolderModal
+            v-model:show={this.folderShowRef}
+            onUpdateList={this.updateList}
+          />
+          <ResourceUploadModal
+            v-model:show={this.uploadShowRef}
+            onUpdateList={this.updateList}
+          />
+          <ResourceRenameModal
+            v-model:show={this.renameShowRef}
+            id={this.renameInfo.id}
+            name={this.renameInfo.name}
+            description={this.renameInfo.description}
+            onUpdateList={this.updateList}
+          />
+        </Card>
+      </div>
+    )
+  },
+})
diff --git a/dolphinscheduler-ui-next/src/views/resource/file/rename/index.tsx b/dolphinscheduler-ui-next/src/views/resource/file/rename/index.tsx
new file mode 100644
index 0000000..aaf0443
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/resource/file/rename/index.tsx
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { defineComponent, toRefs, PropType, watch } from 'vue'
+import { NForm, NFormItem, NInput } from 'naive-ui'
+import { useI18n } from 'vue-i18n'
+import Modal from '@/components/modal'
+import { useForm } from './use-form'
+import { useRename } from './use-rename'
+
+const props = {
+  show: {
+    type: Boolean as PropType<boolean>,
+    default: false,
+  },
+  id: {
+    type: Number as PropType<number>,
+    default: -1,
+  },
+  name: {
+    type: String as PropType<string>,
+    default: '',
+  },
+  description: {
+    type: String as PropType<string>,
+    default: '',
+  },
+}
+
+export default defineComponent({
+  name: 'ResourceFileRename',
+  props,
+  emits: ['updateList', 'update:show'],
+  setup(props, ctx) {
+    const { state, resetForm } = useForm(props.name, props.description)
+    const { handleRenameFile } = useRename(state)
+
+    const hideModal = () => {
+      ctx.emit('update:show', false)
+    }
+
+    const handleFile = () => {
+      handleRenameFile(ctx.emit, hideModal, resetForm)
+    }
+
+    watch(
+      () => props.name,
+      () => {
+        state.renameForm.id = props.id
+        state.renameForm.name = props.name
+        state.renameForm.description = props.description
+      },
+    )
+
+    return { hideModal, handleFile, ...toRefs(state) }
+  },
+  render() {
+    const { t } = useI18n()
+    return (
+      <Modal
+        show={this.$props.show}
+        title={t('resource.file.rename')}
+        onCancel={this.hideModal}
+        onConfirm={this.handleFile}
+      >
+        <NForm
+          rules={this.rules}
+          ref='renameFormRef'
+          label-placement='left'
+          label-width='160'
+        >
+          <NFormItem label={t('resource.file.name')} path='name'>
+            <NInput
+              v-model={[this.renameForm.name, 'value']}
+              placeholder={t('resource.file.enter_name_tips')}
+            />
+          </NFormItem>
+          <NFormItem label={t('resource.file.description')} path='description'>
+            <NInput
+              type='textarea'
+              v-model={[this.renameForm.description, 'value']}
+              placeholder={t('resource.file.enter_description_tips')}
+            />
+          </NFormItem>
+        </NForm>
+      </Modal>
+    )
+  },
+})
diff --git a/dolphinscheduler-ui-next/src/views/resource/file/rename/use-form.ts b/dolphinscheduler-ui-next/src/views/resource/file/rename/use-form.ts
new file mode 100644
index 0000000..0ff7380
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/resource/file/rename/use-form.ts
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { reactive, ref, unref } from 'vue'
+import { useI18n } from 'vue-i18n'
+import type { FormRules } from 'naive-ui'
+
+const defaultValue = (name = '', description = '') => ({
+  id: -1,
+  name,
+  type: 'FILE',
+  description,
+})
+
+export function useForm(name: string, description: string) {
+  const { t } = useI18n()
+
+  const resetForm = () => {
+    state.renameForm = Object.assign(unref(state.renameForm), defaultValue())
+  }
+
+  const state = reactive({
+    renameFormRef: ref(),
+    renameForm: defaultValue(name, description),
+    rules: {
+      name: {
+        required: true,
+        trigger: ['input', 'blur'],
+        validator() {
+          if (state.renameForm.name === '') {
+            return new Error(t('resource.file.enter_name_tips'))
+          }
+        },
+      },
+    } as FormRules,
+  })
+
+  return {
+    state,
+    resetForm,
+  }
+}
diff --git a/dolphinscheduler-ui-next/src/components/card/index.tsx b/dolphinscheduler-ui-next/src/views/resource/file/rename/use-rename.ts
similarity index 50%
copy from dolphinscheduler-ui-next/src/components/card/index.tsx
copy to dolphinscheduler-ui-next/src/views/resource/file/rename/use-rename.ts
index 9ad5602..7006178 100644
--- a/dolphinscheduler-ui-next/src/components/card/index.tsx
+++ b/dolphinscheduler-ui-next/src/views/resource/file/rename/use-rename.ts
@@ -15,40 +15,40 @@
  * limitations under the License.
  */
 
-import { defineComponent, PropType } from 'vue'
-import { NCard } from 'naive-ui'
+import { useI18n } from 'vue-i18n'
+import { IEmit } from '../types'
+import { updateResource } from '@/service/modules/resources'
 
-const headerStyle = {
-  borderBottom: '1px solid var(--n-border-color)',
-}
+export function useRename(state: any) {
+  const { t } = useI18n()
 
-const contentStyle = {
-  padding: '8px 10px',
-}
+  const handleRenameFile = (
+    emit: IEmit,
+    hideModal: () => void,
+    resetForm: () => void,
+  ) => {
+    state.renameFormRef.validate(async (valid: any) => {
+      if (!valid) {
+        try {
+          await updateResource(
+            {
+              ...state.renameForm,
+            },
+            state.renameForm.id,
+          )
+          window.$message.success(t('resource.file.success'))
+          emit('updateList')
+        } catch (error: any) {
+          window.$message.error(error.message)
+        }
+      }
 
-const props = {
-  title: {
-    type: String as PropType<string>,
-    required: true,
-  },
-}
+      hideModal()
+      resetForm()
+    })
+  }
 
-const Card = defineComponent({
-  name: 'Card',
-  props,
-  render() {
-    const { title, $slots } = this
-    return (
-      <NCard
-        title={title}
-        size='small'
-        headerStyle={headerStyle}
-        contentStyle={contentStyle}
-      >
-        {$slots}
-      </NCard>
-    )
-  },
-})
-
-export default Card
+  return {
+    handleRenameFile,
+  }
+}
diff --git a/dolphinscheduler-ui-next/src/env.d.ts b/dolphinscheduler-ui-next/src/views/resource/file/table/index.module.scss
similarity index 72%
copy from dolphinscheduler-ui-next/src/env.d.ts
copy to dolphinscheduler-ui-next/src/views/resource/file/table/index.module.scss
index 776e88b..4ec0732 100644
--- a/dolphinscheduler-ui-next/src/env.d.ts
+++ b/dolphinscheduler-ui-next/src/views/resource/file/table/index.module.scss
@@ -15,13 +15,11 @@
  * limitations under the License.
  */
 
-declare module '*.vue' {
-  import { DefineComponent } from 'vue'
-  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
-  const component: DefineComponent<{}, {}, any>
-  export default component
-}
 
-declare module '*.png'
-declare module '*.jpg'
-declare module '*.jpeg'
+.links {
+  color: #2080f0;
+  text-decoration: none;
+  &:hover {
+    text-decoration: underline;
+  }
+}
\ No newline at end of file
diff --git a/dolphinscheduler-ui-next/src/views/resource/file/table/table-action.tsx b/dolphinscheduler-ui-next/src/views/resource/file/table/table-action.tsx
new file mode 100644
index 0000000..164c35b
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/resource/file/table/table-action.tsx
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { useRouter } from 'vue-router'
+import { defineComponent, PropType } from 'vue'
+import { NSpace, NTooltip, NButton, NIcon, NPopconfirm } from 'naive-ui'
+import {
+  DeleteOutlined,
+  DownloadOutlined,
+  FormOutlined,
+  EditOutlined,
+  InfoCircleFilled,
+} from '@vicons/antd'
+import _ from 'lodash'
+import { useI18n } from 'vue-i18n'
+import { ResourceFileTableData } from '../types'
+import { fileTypeArr } from '@/utils/common'
+import { downloadResource, deleteResource } from '@/service/modules/resources'
+import { IRenameFile, IRtDisb } from '../types'
+import type { Router } from 'vue-router'
+
+const props = {
+  show: {
+    type: Boolean as PropType<boolean>,
+    default: false,
+  },
+  row: {
+    type: Object as PropType<ResourceFileTableData>,
+    default: {
+      id: -1,
+      name: '',
+      description: '',
+    },
+  },
+}
+
+export default defineComponent({
+  name: 'TableAction',
+  props,
+  emits: ['updateList', 'renameResource'],
+  setup(props, { emit }) {
+    const { t } = useI18n()
+    const router: Router = useRouter()
+
+    const rtDisb: IRtDisb = (name, size) => {
+      const i = name.lastIndexOf('.')
+      const a = name.substring(i, name.length)
+      const flag = _.includes(fileTypeArr, _.trimStart(a, '.'))
+      return !(flag && size < 1000000)
+    }
+
+    const handleEditFile = (item: { id: number }) => {
+      router.push({ name: 'resource-file-edit', params: { id: item.id } })
+    }
+
+    const handleDeleteFile = (id: number) => {
+      deleteResource(id).then(() => emit('updateList'))
+    }
+
+    const handleRenameFile: IRenameFile = (id, name, description) => {
+      emit('renameResource', id, name, description)
+    }
+
+    return {
+      t,
+      rtDisb,
+      handleEditFile,
+      handleDeleteFile,
+      handleRenameFile,
+      ...props,
+    }
+  },
+  render() {
+    const { t } = useI18n()
+    return (
+      <NSpace>
+        <NTooltip trigger={'hover'}>
+          {{
+            default: () => t('resource.file.edit'),
+            trigger: () => (
+              <NButton
+                size='tiny'
+                type='info'
+                disabled={this.rtDisb(this.row.name, this.row.size)}
+                tag='div'
+                onClick={() => {
+                  this.handleEditFile(this.row)
+                }}
+                style={{ marginRight: '-5px' }}
+                circle
+              >
+                <NIcon>
+                  <FormOutlined />
+                </NIcon>
+              </NButton>
+            ),
+          }}
+        </NTooltip>
+        <NTooltip trigger={'hover'}>
+          {{
+            default: () => t('resource.file.rename'),
+            trigger: () => (
+              <NButton
+                size='tiny'
+                type='info'
+                onClick={() =>
+                  this.handleRenameFile(
+                    this.row.id,
+                    this.row.name,
+                    this.row.description,
+                  )
+                }
+                style={{ marginRight: '-5px' }}
+                circle
+              >
+                <NIcon>
+                  <EditOutlined />
+                </NIcon>
+              </NButton>
+            ),
+          }}
+        </NTooltip>
+        <NTooltip trigger={'hover'}>
+          {{
+            default: () => t('resource.file.download'),
+            trigger: () => (
+              <NButton
+                size='tiny'
+                type='info'
+                disabled={this.row?.directory ? true : false}
+                tag='div'
+                circle
+                style={{ marginRight: '-5px' }}
+                onClick={() => downloadResource(this.row.id)}
+              >
+                <NIcon>
+                  <DownloadOutlined />
+                </NIcon>
+              </NButton>
+            ),
+          }}
+        </NTooltip>
+        <NTooltip trigger={'hover'}>
+          {{
+            default: () => t('resource.file.delete'),
+            trigger: () => (
+              <NButton size='tiny' type='error' circle>
+                <NPopconfirm
+                  positive-text={t('resource.file.confirm')}
+                  negative-text={t('resource.file.cancel')}
+                  onPositiveClick={() => {
+                    this.handleDeleteFile(this.row.id)
+                  }}
+                >
+                  {{
+                    default: () => t('resource.file.delete_confirm'),
+                    icon: () => (
+                      <NIcon>
+                        <InfoCircleFilled />
+                      </NIcon>
+                    ),
+                    trigger: () => (
+                      <NIcon>
+                        <DeleteOutlined />
+                      </NIcon>
+                    ),
+                  }}
+                </NPopconfirm>
+              </NButton>
+            ),
+          }}
+        </NTooltip>
+      </NSpace>
+    )
+  },
+})
diff --git a/dolphinscheduler-ui-next/src/views/resource/file/table/use-table.ts b/dolphinscheduler-ui-next/src/views/resource/file/table/use-table.ts
new file mode 100644
index 0000000..4df55f3
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/resource/file/table/use-table.ts
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { h } from 'vue'
+import { useI18n } from 'vue-i18n'
+import { useRouter } from 'vue-router'
+import { bytesToSize } from '@/utils/common'
+import { useFileStore } from '@/store/file/file'
+import TableAction from './table-action'
+import { IRenameFile } from '../types'
+import type { Router } from 'vue-router'
+import type { TableColumns } from 'naive-ui/es/data-table/src/interface'
+import styles from './index.module.scss'
+
+const goSubFolder = (router: Router, item: any) => {
+  const fileStore = useFileStore()
+  fileStore.setFileInfo(`${item.alias}|${item.size}`)
+
+  if (item.directory) {
+    fileStore.setCurrentDir(`${item.fullName}`)
+    router.push({ name: 'resource-file-subdirectory', params: { id: item.id } })
+  } else {
+    router.push({ name: 'resource-file-list', params: { id: item.id } })
+  }
+}
+
+export function useTable(renameResource: IRenameFile, updateList: () => void) {
+  const { t } = useI18n()
+  const router: Router = useRouter()
+
+  const columnsRef: TableColumns<any> = [
+    {
+      title: t('resource.file.id'),
+      key: 'id',
+      width: 50,
+      render: (_row, index) => index + 1,
+    },
+    {
+      title: t('resource.file.name'),
+      key: 'name',
+      width: 120,
+      render: (row) =>
+        h(
+          'a',
+          {
+            href: 'javascript:',
+            class: styles.links,
+            onClick: () => goSubFolder(router, row),
+          },
+          {
+            default: () => {
+              return row.name
+            },
+          },
+        ),
+    },
+    { title: t('resource.file.user_name'), width: 100, key: 'user_name' },
+    {
+      title: t('resource.file.whether_directory'),
+      key: 'whether_directory',
+      width: 100,
+      render: (row) =>
+        row.directory ? t('resource.file.yes') : t('resource.file.no'),
+    },
+    { title: t('resource.file.file_name'), key: 'file_name' },
+    { title: t('resource.file.description'), width: 150, key: 'description' },
+    {
+      title: t('resource.file.size'),
+      key: 'size',
+      render: (row) => bytesToSize(row.size),
+    },
+    { title: t('resource.file.update_time'), width: 150, key: 'update_time' },
+    {
+      title: t('resource.file.operation'),
+      key: 'operation',
+      width: 150,
+      render: (row) =>
+        h(TableAction, {
+          row,
+          onRenameResource: (id, name, description) =>
+            renameResource(id, name, description),
+          onUpdateList: () => updateList(),
+        }),
+    },
+  ]
+
+  return {
+    columnsRef,
+  }
+}
diff --git a/dolphinscheduler-ui-next/src/views/resource/file/types.ts b/dolphinscheduler-ui-next/src/views/resource/file/types.ts
new file mode 100644
index 0000000..8bd5e52
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/resource/file/types.ts
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+export interface ResourceFileTableData {
+  id: number
+  name: string
+  user_name: string
+  directory: string
+  file_name: string
+  description: string
+  size: number
+  update_time: string
+}
+
+export interface IEmit {
+  (event: any, ...args: any[]): void
+}
+
+export interface IRenameFile {
+  (id: number, name: string, description: string): void
+}
+export interface IRtDisb {
+  (name: string, size: number): boolean
+}
+
+export interface IResourceListState {
+  (id?: number, searchVal?: string, pageNo?: number, pageSize?: number): any
+}
+
+export interface BasicTableProps {
+  title?: string
+  dataSource: Function
+  columns: any[]
+  pagination: object
+  showPagination: boolean
+  actionColumn: any[]
+  canResize: boolean
+  resizeHeightOffset: number
+}
+
+export interface PaginationProps {
+  page?: number
+  pageCount?: number
+  pageSize?: number
+  pageSizes?: number[]
+  showSizePicker?: boolean
+  showQuickJumper?: boolean
+}
+
+export interface ISetPagination {
+  (itemCount: number): void
+}
diff --git a/dolphinscheduler-ui-next/src/views/resource/file/upload/index.tsx b/dolphinscheduler-ui-next/src/views/resource/file/upload/index.tsx
new file mode 100644
index 0000000..b7f69eb
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/resource/file/upload/index.tsx
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { defineComponent, toRefs, PropType } from 'vue'
+import { NButton, NForm, NFormItem, NInput, NUpload } from 'naive-ui'
+
+import { useI18n } from 'vue-i18n'
+import Modal from '@/components/modal'
+import { useForm } from './use-form'
+import { useUpload } from './use-upload'
+
+const props = {
+  show: {
+    type: Boolean as PropType<boolean>,
+    default: false,
+  },
+}
+
+export default defineComponent({
+  name: 'ResourceFileUpload',
+  props,
+  emits: ['updateList', 'update:show'],
+  setup(props, ctx) {
+    const { state, resetForm } = useForm()
+    const { handleUploadFile } = useUpload(state)
+
+    const hideModal = () => {
+      ctx.emit('update:show')
+    }
+
+    const customRequest = ({ file }: any) => {
+      state.uploadForm.name = file.name
+      state.uploadForm.file = file.file
+    }
+
+    const handleFile = () => {
+      handleUploadFile(ctx.emit, hideModal, resetForm)
+    }
+
+    return {
+      hideModal,
+      customRequest,
+      handleFile,
+      ...toRefs(state),
+    }
+  },
+  render() {
+    const { t } = useI18n()
+    return (
+      <Modal
+        show={this.$props.show}
+        title={t('resource.file.upload_files')}
+        onCancel={this.hideModal}
+        onConfirm={this.handleFile}
+      >
+        <NForm
+          rules={this.rules}
+          ref='uploadFormRef'
+          label-placement='left'
+          label-width='160'
+        >
+          <NFormItem label={t('resource.file.file_name')} path='name'>
+            <NInput
+              v-model={[this.uploadForm.name, 'value']}
+              placeholder={t('resource.file.enter_name_tips')}
+            />
+          </NFormItem>
+          <NFormItem label={t('resource.file.description')} path='description'>
+            <NInput
+              type='textarea'
+              v-model={[this.uploadForm.description, 'value']}
+              placeholder={t('resource.file.enter_description_tips')}
+            />
+          </NFormItem>
+          <NFormItem label={t('resource.file.upload_files')} path='file'>
+            <NUpload
+              v-model={[this.uploadForm.file, 'value']}
+              customRequest={this.customRequest}
+            >
+              <NButton>{t('resource.file.upload_files')}</NButton>
+            </NUpload>
+          </NFormItem>
+        </NForm>
+      </Modal>
+    )
+  },
+})
diff --git a/dolphinscheduler-ui-next/src/views/resource/file/upload/use-form.ts b/dolphinscheduler-ui-next/src/views/resource/file/upload/use-form.ts
new file mode 100644
index 0000000..08de62d
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/resource/file/upload/use-form.ts
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { reactive, ref, unref } from 'vue'
+import { useI18n } from 'vue-i18n'
+import type { FormRules } from 'naive-ui'
+
+const defaultValue = () => ({
+  name: '',
+  file: '',
+  description: '',
+  pid: -1,
+  currentDir: '/',
+})
+
+export function useForm() {
+  const { t } = useI18n()
+
+  const resetForm = () => {
+    state.uploadForm = Object.assign(unref(state.uploadForm), defaultValue())
+  }
+
+  const state = reactive({
+    uploadFormRef: ref(),
+    uploadForm: defaultValue(),
+    rules: {
+      name: {
+        required: true,
+        trigger: ['input', 'blur'],
+        validator() {
+          if (state.uploadForm.name === '') {
+            return new Error(t('resource.file.enter_name_tips'))
+          }
+        },
+      },
+      file: {
+        required: true,
+        trigger: ['input', 'blur'],
+        validator() {
+          if (state.uploadForm.file === '') {
+            return new Error(t('resource.file.enter_content_tips'))
+          }
+        },
+      },
+    } as FormRules,
+  })
+
+  return {
+    state,
+    resetForm,
+  }
+}
diff --git a/dolphinscheduler-ui-next/src/views/resource/file/upload/use-upload.ts b/dolphinscheduler-ui-next/src/views/resource/file/upload/use-upload.ts
new file mode 100644
index 0000000..a5d7aab
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/resource/file/upload/use-upload.ts
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { useI18n } from 'vue-i18n'
+import { IEmit } from '../types'
+import { useRouter } from 'vue-router'
+import type { Router } from 'vue-router'
+import { useFileStore } from '@/store/file/file'
+import { createResource } from '@/service/modules/resources'
+
+export function useUpload(state: any) {
+  const { t } = useI18n()
+  const router: Router = useRouter()
+  const fileStore = useFileStore()
+
+  const handleUploadFile = (
+    emit: IEmit,
+    hideModal: () => void,
+    resetForm: () => void,
+  ) => {
+    state.uploadFormRef.validate(async (valid: any) => {
+      const pid = router.currentRoute.value.params.id || -1
+      const currentDir = fileStore.getCurrentDir || '/'
+      if (!valid) {
+        const formData = new FormData()
+        formData.append('file', state.uploadForm.file)
+        formData.append('type', 'FILE')
+        formData.append('name', state.uploadForm.name)
+        formData.append('pid', String(pid))
+        formData.append('currentDir', currentDir)
+        formData.append('description', state.uploadForm.description)
+
+        try {
+          await createResource(formData as any)
+          window.$message.success(t('resource.file.success'))
+          emit('updateList')
+        } catch (error: any) {
+          window.$message.error(error.message)
+        }
+
+        hideModal()
+        resetForm()
+      }
+    })
+  }
+
+  return {
+    handleUploadFile,
+  }
+}
diff --git a/dolphinscheduler-ui-next/src/views/resource/file/use-file.ts b/dolphinscheduler-ui-next/src/views/resource/file/use-file.ts
new file mode 100644
index 0000000..3e7c7a0
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/resource/file/use-file.ts
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { useAsyncState } from '@vueuse/core'
+import {
+  queryResourceListPaging,
+  viewResource,
+} from '@/service/modules/resources'
+import type { ResourceListRes } from '@/service/modules/resources/types'
+import { IResourceListState, ISetPagination } from './types'
+
+export function useFileState(
+  setPagination: ISetPagination = {} as ISetPagination,
+) {
+  const getResourceListState: IResourceListState = (
+    id = -1,
+    searchVal = '',
+    pageNo = 1,
+    pageSize = 10,
+  ) => {
+    const { state } = useAsyncState(
+      queryResourceListPaging({
+        id,
+        type: 'FILE',
+        searchVal,
+        pageNo,
+        pageSize,
+      }).then((res: ResourceListRes): any => {
+        const { total } = res
+        setPagination(total)
+        const table = res.totalList.map((item) => {
+          return {
+            id: item.id,
+            name: item.alias,
+            alias: item.alias,
+            fullName: item.fullName,
+            type: item.type,
+            directory: item.directory,
+            file_name: item.fileName,
+            description: item.description,
+            size: item.size,
+            update_time: item.updateTime,
+          }
+        })
+
+        return { total, table }
+      }),
+      { total: 0, table: [] },
+    )
+
+    return state
+  }
+
+  const getResourceView = (id: number) => {
+    const params = {
+      skipLineNum: 0,
+      limit: 3000,
+    }
+    const { state } = useAsyncState(viewResource(params, id), {})
+    return state
+  }
+
+  return { getResourceListState, getResourceView }
+}