diff --git a/eslint.config.js b/eslint.config.js index 00537e3f..4ff17dc8 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -15,7 +15,15 @@ export default defineConfig( 'PascalCase', { registeredComponentsOnly: false, - ignores: ['/^icon-/'] + ignores: [ + '/^icon-/', + 'uploader', + 'uploader-unsupport', + 'uploader-drop', + 'uploader-btn', + 'uploader-list', + 'uploader-file' + ] } ], 'unocss/order-attributify': 'off' diff --git a/package.json b/package.json index b5bc1f0e..d610b09d 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,8 @@ "vue": "3.5.13", "vue-draggable-plus": "0.6.0", "vue-i18n": "11.1.1", - "vue-router": "4.5.0" + "vue-router": "4.5.0", + "vue-simple-uploader": "^1.0.3" }, "devDependencies": { "@elegant-router/vue": "0.3.8", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 10793df9..7085d103 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -74,6 +74,9 @@ importers: vue-router: specifier: 4.5.0 version: 4.5.0(vue@3.5.13(typescript@5.7.3)) + vue-simple-uploader: + specifier: ^1.0.3 + version: 1.0.3(vue@3.5.13(typescript@5.7.3)) devDependencies: '@elegant-router/vue': specifier: 0.3.8 @@ -3798,6 +3801,9 @@ packages: resolution: {integrity: sha512-tgqwPUMDcNDhuf1Xf6KTUsyeqGdgKMhzaH4PAZZuzguOgTl5uuyeYe/8mWgAr6IBxB5V06uqEf6Dy37gIWDtDg==} hasBin: true + simple-uploader.js@0.6.0: + resolution: {integrity: sha512-EXN+o+LD6PVnfzTq/usP8k8yYrI6wKrAx8e+fPcPLVzzttonkyn1KT+Ycx5JnPBSnp6lpiVhNG4JhDJucdPnhA==} + simplebar-core@1.3.0: resolution: {integrity: sha512-LpWl3w0caz0bl322E68qsrRPpIn+rWBGAaEJ0lUJA7Xpr2sw92AkIhg6VWj988IefLXYh50ILatfAnbNoCFrlA==} @@ -4337,6 +4343,12 @@ packages: peerDependencies: vue: ^3.2.0 + vue-simple-uploader@1.0.3: + resolution: {integrity: sha512-RIghV5rG1CaA41R7VlQP0UG9xevs+cRaCN0k7gH4cFHdG9yIf4206fGKA90NRKzlPxGSBTwLm5dCajLHfqd2+w==} + engines: {node: '>= 4.0.0', npm: '>= 3.0.0'} + peerDependencies: + vue: '>=3.1' + vue-tsc@2.2.0: resolution: {integrity: sha512-gtmM1sUuJ8aSb0KoAFmK9yMxb8TxjewmxqTJ1aKphD5Cbu0rULFY6+UQT51zW7SpUcenfPUuflKyVwyx9Qdnxg==} hasBin: true @@ -8250,6 +8262,8 @@ snapshots: simple-git-hooks@2.11.1: {} + simple-uploader.js@0.6.0: {} + simplebar-core@1.3.0: dependencies: lodash: 4.17.21 @@ -8856,6 +8870,11 @@ snapshots: '@vue/devtools-api': 6.6.4 vue: 3.5.13(typescript@5.7.3) + vue-simple-uploader@1.0.3(vue@3.5.13(typescript@5.7.3)): + dependencies: + simple-uploader.js: 0.6.0 + vue: 3.5.13(typescript@5.7.3) + vue-tsc@2.2.0(typescript@5.7.3): dependencies: '@volar/typescript': 2.4.11 diff --git a/src/components/common/global-uploader.vue b/src/components/common/global-uploader.vue new file mode 100644 index 00000000..d60bf988 --- /dev/null +++ b/src/components/common/global-uploader.vue @@ -0,0 +1,199 @@ + + + + + diff --git a/src/main.ts b/src/main.ts index b97a0d93..3f9be276 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,6 @@ import { createApp } from 'vue'; import './plugins/assets'; +import uploader from 'vue-simple-uploader'; import { setupAppVersionNotification, setupDayjs, setupIconifyOffline, setupLoading, setupNProgress } from './plugins'; import { setupStore } from './store'; import { setupRouter } from './router'; @@ -25,6 +26,8 @@ async function setupApp() { setupAppVersionNotification(); + app.use(uploader); + app.mount('#app'); } diff --git a/src/plugins/assets.ts b/src/plugins/assets.ts index 904aa6a2..199197a2 100644 --- a/src/plugins/assets.ts +++ b/src/plugins/assets.ts @@ -1,3 +1,4 @@ import 'virtual:svg-icons-register'; import 'uno.css'; import '../styles/css/global.css'; +import 'vue-simple-uploader/dist/style.css'; diff --git a/src/typings/components.d.ts b/src/typings/components.d.ts index c3c7bfd6..c4f25ef6 100644 --- a/src/typings/components.d.ts +++ b/src/typings/components.d.ts @@ -14,10 +14,13 @@ declare module 'vue' { DarkModeContainer: typeof import('./../components/common/dark-mode-container.vue')['default'] ExceptionBase: typeof import('./../components/common/exception-base.vue')['default'] FullScreen: typeof import('./../components/common/full-screen.vue')['default'] + GlobalUploader: typeof import('./../components/common/global-uploader.vue')['default'] IconAntDesignEnterOutlined: typeof import('~icons/ant-design/enter-outlined')['default'] IconAntDesignReloadOutlined: typeof import('~icons/ant-design/reload-outlined')['default'] IconAntDesignSettingOutlined: typeof import('~icons/ant-design/setting-outlined')['default'] 'IconCuida:swapVerticalArrowsOutline': typeof import('~icons/cuida/swap-vertical-arrows-outline')['default'] + 'IconEp:circleClose': typeof import('~icons/ep/circle-close')['default'] + 'IconEp:position': typeof import('~icons/ep/position')['default'] 'IconFluent:multiselect20Filled': typeof import('~icons/fluent/multiselect20-filled')['default'] IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default'] IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default'] @@ -28,6 +31,7 @@ declare module 'vue' { IconIcRoundRefresh: typeof import('~icons/ic/round-refresh')['default'] IconIcRoundRemove: typeof import('~icons/ic/round-remove')['default'] IconIcRoundSearch: typeof import('~icons/ic/round-search')['default'] + 'IconLineicons:emptyFile': typeof import('~icons/lineicons/empty-file')['default'] IconLocalBanner: typeof import('~icons/local/banner')['default'] IconLocalGitee: typeof import('~icons/local/gitee')['default'] IconLocalLogo: typeof import('~icons/local/logo')['default'] diff --git a/src/typings/global.d.ts b/src/typings/global.d.ts index 081c8bde..aad65c56 100644 --- a/src/typings/global.d.ts +++ b/src/typings/global.d.ts @@ -12,6 +12,8 @@ declare global { $message?: import('naive-ui').MessageProviderInst; /** Notification instance */ $notification?: import('naive-ui').NotificationProviderInst; + /** Uploader instance */ + $uploader?: import('vue-simple-uploader').Uploader; } /** Build time of the project */ diff --git a/src/typings/storage.d.ts b/src/typings/storage.d.ts index f7134d4b..d7244033 100644 --- a/src/typings/storage.d.ts +++ b/src/typings/storage.d.ts @@ -38,5 +38,7 @@ declare namespace StorageType { siderCollapse: boolean; }; fileShowMode: UnionKey.FileListMode; + /** uploader chunk size */ + uploaderChunkSize: number; } } diff --git a/src/typings/uploader.d.ts b/src/typings/uploader.d.ts new file mode 100644 index 00000000..f29ca0c7 --- /dev/null +++ b/src/typings/uploader.d.ts @@ -0,0 +1,9 @@ +declare module 'vue-simple-uploader' { + import type { Plugin } from 'vue'; + const VueSimpleUploader: Plugin; + export default VueSimpleUploader; + export interface Uploader { + /** 文件列表 */ + fileList: File[]; + } +} diff --git a/src/views/pan/index.vue b/src/views/pan/index.vue index 5302c894..038a2148 100644 --- a/src/views/pan/index.vue +++ b/src/views/pan/index.vue @@ -14,7 +14,7 @@ const gap = computed(() => (appStore.isMobile ? 0 : 16));
- + diff --git a/src/views/pan/modules/file-main.vue b/src/views/pan/modules/file-main.vue index 6926b58a..cddee7eb 100644 --- a/src/views/pan/modules/file-main.vue +++ b/src/views/pan/modules/file-main.vue @@ -13,6 +13,8 @@ defineOptions({ const { SvgIconVNode } = useSvgIcon(); const panStore = usePanStore(); +const globalUploaderRef = ref(); + // 是否是批量操作 const isBatchMode = ref(false); // 是否是加载的状态 @@ -30,6 +32,21 @@ const toggleMode = () => { panStore.toggleFileShowMode(newMode); }; +// 点击上传文件按钮的回调 +const handleFileUpload = () => { + globalUploaderRef.value?.handleFileUpload(); +}; + +// 点击上传文件夹按钮的回调 +const handleFolderUpload = () => { + globalUploaderRef.value?.handleFolderUpload(); +}; + +// 打开传输列表面板 +const handleOpenPanel = () => { + globalUploaderRef.value?.openPanelShow(); +}; + const uploadOptions = [ { key: 'file', @@ -37,8 +54,7 @@ const uploadOptions = [ icon: SvgIconVNode({ localIcon: 'upload-file', fontSize: 25 }), props: { onClick: () => { - // handleFileUpload(); - window.$message?.info('上传文件功能'); + handleFileUpload(); } } }, @@ -48,8 +64,7 @@ const uploadOptions = [ icon: SvgIconVNode({ localIcon: 'upload-folder', fontSize: 20 }), props: { onClick: () => { - // handleFolderUpload(); - window.$message?.info('上传文件夹功能'); + handleFolderUpload(); } } } @@ -72,6 +87,7 @@ onMounted(async () => {