From 97f84ae00d7a027109a737875b1d8d85663e8aa4 Mon Sep 17 00:00:00 2001 From: Devosend Date: Thu, 13 Jan 2022 16:03:29 +0800 Subject: [PATCH] [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 - .../components/conditions/index.module.scss | 31 +++ .../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 ++ .../src/service/service.ts | 52 +++- .../src/store/file/file.ts | 44 ++++ .../src/store/file/types.ts | 23 ++ .../src/store/menu/menu.ts | 2 +- dolphinscheduler-ui-next/src/utils/common.ts | 48 ++++ .../file/create/resource-file-create.tsx | 131 ++++++++++ .../views/resource/file/create/use-create.ts | 53 ++++ .../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 +++++ .../src/views/resource/file/edit/use-form.ts | 53 ++++ .../src/views/resource/file/folder/index.tsx | 86 +++++++ .../views/resource/file/folder/use-folder.ts | 59 +++++ .../views/resource/file/folder/use-form.ts | 57 +++++ .../src/views/resource/file/index.module.scss | 71 ++++++ .../src/views/resource/file/index.tsx | 228 ++++++++++++++++++ .../src/views/resource/file/rename/index.tsx | 102 ++++++++ .../views/resource/file/rename/use-form.ts | 56 +++++ .../views/resource/file/rename/use-rename.ts | 54 +++++ .../resource/file/table/index.module.scss | 25 ++ .../resource/file/table/table-action.tsx | 190 +++++++++++++++ .../views/resource/file/table/use-table.ts | 104 ++++++++ .../src/views/resource/file/types.ts | 66 +++++ .../src/views/resource/file/upload/index.tsx | 100 ++++++++ .../views/resource/file/upload/use-form.ts | 66 +++++ .../views/resource/file/upload/use-upload.ts | 64 +++++ .../src/views/resource/file/use-file.ts | 78 ++++++ 41 files changed, 2488 insertions(+), 34 deletions(-) create mode 100644 dolphinscheduler-ui-next/src/components/conditions/index.module.scss create mode 100644 dolphinscheduler-ui-next/src/components/conditions/index.tsx create mode 100644 dolphinscheduler-ui-next/src/components/monaco-editor/index.tsx create mode 100644 dolphinscheduler-ui-next/src/store/file/file.ts create mode 100644 dolphinscheduler-ui-next/src/store/file/types.ts create mode 100644 dolphinscheduler-ui-next/src/utils/common.ts create mode 100644 dolphinscheduler-ui-next/src/views/resource/file/create/resource-file-create.tsx create mode 100644 dolphinscheduler-ui-next/src/views/resource/file/create/use-create.ts create mode 100644 dolphinscheduler-ui-next/src/views/resource/file/create/use-form.ts create mode 100644 dolphinscheduler-ui-next/src/views/resource/file/edit/resource-file-edit.tsx create mode 100644 dolphinscheduler-ui-next/src/views/resource/file/edit/use-edit.ts create mode 100644 dolphinscheduler-ui-next/src/views/resource/file/edit/use-form.ts create mode 100644 dolphinscheduler-ui-next/src/views/resource/file/folder/index.tsx create mode 100644 dolphinscheduler-ui-next/src/views/resource/file/folder/use-folder.ts create mode 100644 dolphinscheduler-ui-next/src/views/resource/file/folder/use-form.ts create mode 100644 dolphinscheduler-ui-next/src/views/resource/file/index.module.scss create mode 100644 dolphinscheduler-ui-next/src/views/resource/file/index.tsx create mode 100644 dolphinscheduler-ui-next/src/views/resource/file/rename/index.tsx create mode 100644 dolphinscheduler-ui-next/src/views/resource/file/rename/use-form.ts create mode 100644 dolphinscheduler-ui-next/src/views/resource/file/rename/use-rename.ts create mode 100644 dolphinscheduler-ui-next/src/views/resource/file/table/index.module.scss create mode 100644 dolphinscheduler-ui-next/src/views/resource/file/table/table-action.tsx create mode 100644 dolphinscheduler-ui-next/src/views/resource/file/table/use-table.ts create mode 100644 dolphinscheduler-ui-next/src/views/resource/file/types.ts create mode 100644 dolphinscheduler-ui-next/src/views/resource/file/upload/index.tsx create mode 100644 dolphinscheduler-ui-next/src/views/resource/file/upload/use-form.ts create mode 100644 dolphinscheduler-ui-next/src/views/resource/file/upload/use-upload.ts create mode 100644 dolphinscheduler-ui-next/src/views/resource/file/use-file.ts diff --git a/dolphinscheduler-ui-next/package.json b/dolphinscheduler-ui-next/package.json index 65e89f5c18..54de5e4c6e 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 df3a27621e..33476945ce 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} > - + + {this.isRouterAlive ? : ''} + ) }, diff --git a/dolphinscheduler-ui-next/src/components/card/index.tsx b/dolphinscheduler-ui-next/src/components/card/index.tsx index 9ad5602ab9..25be283ffe 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, - required: true, }, } diff --git a/dolphinscheduler-ui-next/src/components/conditions/index.module.scss b/dolphinscheduler-ui-next/src/components/conditions/index.module.scss new file mode 100644 index 0000000000..2e59ac9260 --- /dev/null +++ b/dolphinscheduler-ui-next/src/components/conditions/index.module.scss @@ -0,0 +1,31 @@ +/* + * 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. + */ + + .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; + } + } + } +} 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 0000000000..d193f74bdc --- /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 ( + +
+ {$slots} +
+
+
+ + + + + +
+
+ +
+
+
+
+
+ ) + }, +}) + +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 0000000000..fb7100aa05 --- /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, + default: '', + }, + language: { + type: String as PropType, + default: 'shell', + }, + readOnly: { + type: Boolean as PropType, + 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 ( +
+ ) + }, +}) diff --git a/dolphinscheduler-ui-next/src/env.d.ts b/dolphinscheduler-ui-next/src/env.d.ts index 776e88b786..db96b93c11 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 3eb9a61eab..82da2a9ae2 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 7b0a725b34..3a37e6dbcf 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 be1fb01de6..a05a074408 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 41d83cca76..3372ccbdda 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 6ea940a746..24fc461b75 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 9fce316fdb..b1e1ce3910 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 79ec58bdf2..2102ba6516 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 6c3bfc198d..d357d9df43 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/file/file.ts b/dolphinscheduler-ui-next/src/store/file/file.ts new file mode 100644 index 0000000000..96fb868b3d --- /dev/null +++ b/dolphinscheduler-ui-next/src/store/file/file.ts @@ -0,0 +1,44 @@ +/* + * 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 { defineStore } from 'pinia' +import type { FileState } from '@/store/file/types' + +export const useFileStore = defineStore({ + id: 'file', + state: (): FileState => ({ + file: '', + currentDir: '/', + }), + persist: true, + getters: { + getFileInfo(): string { + return this.file + }, + getCurrentDir(): string { + return this.currentDir + }, + }, + actions: { + setFileInfo(file: string): void { + this.file = file + }, + setCurrentDir(currentDir: string): void { + this.currentDir = currentDir + }, + }, +}) diff --git a/dolphinscheduler-ui-next/src/store/file/types.ts b/dolphinscheduler-ui-next/src/store/file/types.ts new file mode 100644 index 0000000000..57ef1220c5 --- /dev/null +++ b/dolphinscheduler-ui-next/src/store/file/types.ts @@ -0,0 +1,23 @@ +/* + * 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. + */ + +interface FileState { + file: string + currentDir: string +} + +export { FileState } diff --git a/dolphinscheduler-ui-next/src/store/menu/menu.ts b/dolphinscheduler-ui-next/src/store/menu/menu.ts index b86cdc7057..b4c5c09aec 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/utils/common.ts b/dolphinscheduler-ui-next/src/utils/common.ts new file mode 100644 index 0000000000..15e7ca0b8e --- /dev/null +++ b/dolphinscheduler-ui-next/src/utils/common.ts @@ -0,0 +1,48 @@ +/* + * 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. + */ + +/** + * 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] +} + +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 0000000000..ccebfdc614 --- /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 ( + + + + + + + + + + + + +
+ +
+
+
+
+ + {t('resource.file.save')} + + + {t('resource.file.return')} + +
+
+
+
+ ) + }, +}) 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 0000000000..dc03a3d362 --- /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 0000000000..b0e7573d79 --- /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 0000000000..2cadb9302e --- /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 ( + +
+

+ {this.resourceViewRef?.value.alias} +

+ + +
+ +
+
+ {this.routeNameRef === 'resource-file-edit' && ( + + + {t('resource.file.return')} + + this.handleFileContent()} + > + {t('resource.file.save')} + + + )} +
+
+
+ ) + }, +}) 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 0000000000..f4cea46dad --- /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/views/resource/file/edit/use-form.ts b/dolphinscheduler-ui-next/src/views/resource/file/edit/use-form.ts new file mode 100644 index 0000000000..68dbf653dd --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/resource/file/edit/use-form.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 { reactive, ref, unref } from 'vue' +import { useI18n } from 'vue-i18n' +import type { FormRules } from 'naive-ui' + +const defaultValue = () => ({ + content: '', +}) + +export function useForm() { + const { t } = useI18n() + + const resetForm = () => { + state.fileForm = Object.assign(unref(state.fileForm), defaultValue()) + } + + 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, + }) + + 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 0000000000..bc2618a0be --- /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, + 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 ( + + + + + + + + + + + ) + }, +}) 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 0000000000..0b4599c049 --- /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/views/resource/file/folder/use-form.ts b/dolphinscheduler-ui-next/src/views/resource/file/folder/use-form.ts new file mode 100644 index 0000000000..be8e279a50 --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/resource/file/folder/use-form.ts @@ -0,0 +1,57 @@ +/* + * 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 = () => ({ + pid: -1, + type: 'FILE', + name: '', + description: '', + currentDir: '/', +}) + +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/views/resource/file/index.module.scss b/dolphinscheduler-ui-next/src/views/resource/file/index.module.scss new file mode 100644 index 0000000000..a6c089e897 --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/resource/file/index.module.scss @@ -0,0 +1,71 @@ +/* + * 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. + */ + +.file-edit-content { + width: 100%; + background: #fff; + padding-bottom: 20px; + >h2 { + line-height: 60px; + text-align: center; + padding-right: 170px; + position: relative; + } +} + +.form-content { + padding: 0 50px 0 50px; +} + +.submit { + text-align: left; + padding-top: 12px; + margin-left: 160px; +} + +.pagination { + display: flex; + justify-content: center; + align-items: center; + margin-top: 20px; +} + +.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 0000000000..f61aac2184 --- /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) => { + 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 ( +
+ + + + {t('resource.file.create_folder')} + + + {t('resource.file.create_file')} + + + {t('resource.file.upload_files')} + + + + + +
+ +
+ + + +
+
+ ) + }, +}) 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 0000000000..aaf04433cb --- /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, + default: false, + }, + id: { + type: Number as PropType, + default: -1, + }, + name: { + type: String as PropType, + default: '', + }, + description: { + type: String as PropType, + 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 ( + + + + + + + + + + + ) + }, +}) 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 0000000000..0ff73809e6 --- /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/views/resource/file/rename/use-rename.ts b/dolphinscheduler-ui-next/src/views/resource/file/rename/use-rename.ts new file mode 100644 index 0000000000..7006178f3e --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/resource/file/rename/use-rename.ts @@ -0,0 +1,54 @@ +/* + * 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 { updateResource } from '@/service/modules/resources' + +export function useRename(state: any) { + const { t } = useI18n() + + 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) + } + } + + hideModal() + resetForm() + }) + } + + return { + handleRenameFile, + } +} diff --git a/dolphinscheduler-ui-next/src/views/resource/file/table/index.module.scss b/dolphinscheduler-ui-next/src/views/resource/file/table/index.module.scss new file mode 100644 index 0000000000..4ec0732516 --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/resource/file/table/index.module.scss @@ -0,0 +1,25 @@ +/* + * 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. + */ + + +.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 0000000000..164c35b532 --- /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, + default: false, + }, + row: { + type: Object as PropType, + 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 ( + + + {{ + default: () => t('resource.file.edit'), + trigger: () => ( + { + this.handleEditFile(this.row) + }} + style={{ marginRight: '-5px' }} + circle + > + + + + + ), + }} + + + {{ + default: () => t('resource.file.rename'), + trigger: () => ( + + this.handleRenameFile( + this.row.id, + this.row.name, + this.row.description, + ) + } + style={{ marginRight: '-5px' }} + circle + > + + + + + ), + }} + + + {{ + default: () => t('resource.file.download'), + trigger: () => ( + downloadResource(this.row.id)} + > + + + + + ), + }} + + + {{ + default: () => t('resource.file.delete'), + trigger: () => ( + + { + this.handleDeleteFile(this.row.id) + }} + > + {{ + default: () => t('resource.file.delete_confirm'), + icon: () => ( + + + + ), + trigger: () => ( + + + + ), + }} + + + ), + }} + + + ) + }, +}) 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 0000000000..4df55f3348 --- /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 = [ + { + 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 0000000000..8bd5e52a14 --- /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 0000000000..b7f69eb588 --- /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, + 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 ( + + + + + + + + + + + {t('resource.file.upload_files')} + + + + + ) + }, +}) 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 0000000000..08de62d72e --- /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 0000000000..a5d7aab13a --- /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 0000000000..3e7c7a0209 --- /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 } +}