mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-09-27 05:36:43 +08:00
feat(projects): 网盘全局上传组件静态封装完毕
This commit is contained in:
parent
a6cb692b9a
commit
75313878f5
@ -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'
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
199
src/components/common/global-uploader.vue
Normal file
199
src/components/common/global-uploader.vue
Normal file
@ -0,0 +1,199 @@
|
||||
<script setup lang="ts">
|
||||
import type { ComponentPublicInstance } from 'vue';
|
||||
import { nextTick, onMounted, ref } from 'vue';
|
||||
import { simpleUploadURL } from '@/service/api/pan';
|
||||
import { localStg } from '@/utils/storage';
|
||||
|
||||
defineOptions({
|
||||
name: 'GlobalUploader'
|
||||
});
|
||||
|
||||
/** 数据定义 */
|
||||
const uploaderRef = ref();
|
||||
const showUploader = ref(true);
|
||||
const dragover = ref(false);
|
||||
const enableDragUpload = ref(false);
|
||||
const isPanelShow = ref(false);
|
||||
const fileListLength = ref(0);
|
||||
const process = ref(-10);
|
||||
const uploadBtnRef = ref<ComponentPublicInstance | null>(null);
|
||||
const folderBtnRef = ref<ComponentPublicInstance | null>(null);
|
||||
|
||||
/** 上传组件的配置 */
|
||||
const options = {
|
||||
target: simpleUploadURL, // 上传地址
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStg.get('token')}` // 上传请求头
|
||||
},
|
||||
chunkSize: localStg.get('uploaderChunkSize') || 1024 * 1024, // 分片大小
|
||||
forceChunkSize: true, // 是否强制所有的块都是小于等于 chunkSize 的值。默认是 false。
|
||||
// fileParameterName: 'file', // 上传文件时文件的参数名,默认file
|
||||
maxChunkRetries: 3, // 最大自动失败重试上传次数
|
||||
simultaneousUploads: 3, // 并发上传数
|
||||
testChunks: true, // 是否开启服务器分片校验
|
||||
// 服务器分配校验函数,秒传及断点续传基础函数
|
||||
checkChunkUploadedByResponse(chunk: any, message: any) {
|
||||
const objMessage = JSON.parse(message);
|
||||
const res = objMessage.data;
|
||||
if (!res) {
|
||||
return [];
|
||||
}
|
||||
if (res.pass) {
|
||||
// 秒传
|
||||
return true;
|
||||
}
|
||||
// 断点续传
|
||||
return (res.resume || []).includes(chunk.offset + 1);
|
||||
},
|
||||
// 格式化你想要剩余时间
|
||||
parseTimeRemaining(parsedTimeRemaining: string) {
|
||||
return parsedTimeRemaining
|
||||
.replace(/\syears?/, '年')
|
||||
.replace(/\days?/, '天')
|
||||
.replace(/\shours?/, '小时')
|
||||
.replace(/\sminutes?/, '分钟')
|
||||
.replace(/\sseconds?/, '秒');
|
||||
},
|
||||
query() {}
|
||||
};
|
||||
|
||||
/** 上传状态文本 */
|
||||
const statusText = {
|
||||
success: '上传成功',
|
||||
error: '上传失败',
|
||||
uploading: '正在上传',
|
||||
paused: '已暂停',
|
||||
waiting: '等待中'
|
||||
};
|
||||
|
||||
// 限制上传文件的类型
|
||||
const attrs = {
|
||||
accept: '*'
|
||||
};
|
||||
|
||||
// 点击上传文件的方法
|
||||
const handleFileUpload = () => {
|
||||
if (uploadBtnRef.value) {
|
||||
uploadBtnRef.value?.$el.click();
|
||||
}
|
||||
};
|
||||
|
||||
// 点击上传文件夹的方法
|
||||
const handleFolderUpload = () => {
|
||||
if (folderBtnRef.value) {
|
||||
folderBtnRef.value?.$el.click();
|
||||
}
|
||||
};
|
||||
|
||||
// 关闭上传框的方法
|
||||
const closePanel = () => {
|
||||
if (process.value === -10 || process.value === 100 || fileListLength.value === 0) {
|
||||
isPanelShow.value = false;
|
||||
} else {
|
||||
window.$dialog?.warning({
|
||||
title: '提示',
|
||||
content: '文件正在上传中,是否关闭上传面板?',
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: () => {
|
||||
isPanelShow.value = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 打开上传框的方法
|
||||
const openPanelShow = () => {
|
||||
if (isPanelShow.value) return;
|
||||
isPanelShow.value = true;
|
||||
};
|
||||
|
||||
// 初始化上传器
|
||||
const initUploader = () => {
|
||||
nextTick(() => {
|
||||
if (uploaderRef.value) {
|
||||
window.$uploader = uploaderRef.value.uploader;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({ handleFileUpload, handleFolderUpload, openPanelShow });
|
||||
|
||||
onMounted(() => {
|
||||
initUploader();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="pos-fixed bottom-15px right-15px z-1002">
|
||||
<uploader
|
||||
v-if="showUploader"
|
||||
ref="uploaderRef"
|
||||
:options="options"
|
||||
:auto-start="false"
|
||||
:file-status-text="statusText"
|
||||
class="w-720px"
|
||||
>
|
||||
<uploader-unsupport>您的浏览器不支持上传组件</uploader-unsupport>
|
||||
|
||||
<uploader-drop
|
||||
v-show="dragover && enableDragUpload"
|
||||
class="pos-fixed left-0 top-0 size-full bg-[#ffffff99] text-center"
|
||||
>
|
||||
<span class="pos-relative top-48% text-34px text-[#616161] font-700">上传文件到当前目录下</span>
|
||||
</uploader-drop>
|
||||
|
||||
<uploader-btn id="global-uploader-btn" ref="uploadBtnRef" :attrs="attrs">选择文件</uploader-btn>
|
||||
<uploader-btn id="folder-uploader-btn" ref="folderBtnRef" :directory="true">选择文件夹</uploader-btn>
|
||||
<uploader-list v-show="isPanelShow">
|
||||
<template #default="props">
|
||||
<div
|
||||
class="pos-fixed bottom-[4%] right-[2%] m-auto h-300px w-720px overflow-hidden border-1 border-[#e2e2e2] rounded-7px border-solid bg-[length:100%_100%] bg-white shadow-[rgba(0,0,0,0.2)] transition-all duration-500 ease-in-out"
|
||||
>
|
||||
<div class="h-[2.8rem] flex border-b-1 border-primary border-solid p-[0px,10px]">
|
||||
<h2 class="ml-[3%] text-18px/[2.8rem]">传输列表</h2>
|
||||
<div class="flex-[1] text-align-right">
|
||||
<NButton text class="ml-0 px-5px py-16px">
|
||||
<template #icon>
|
||||
<icon-ep:position />
|
||||
</template>
|
||||
</NButton>
|
||||
<NButton text class="ml-0 mr-10px px-5px py-16px" @click="closePanel">
|
||||
<template #icon>
|
||||
<icon-ep:circle-close />
|
||||
</template>
|
||||
</NButton>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="pos-relative m-0 max-h-300px overflow-x-hidden overflow-y-auto bg-white p-0">
|
||||
<li v-for="file in props.fileList" :key="file.id" class="bg-white">
|
||||
<uploader-file :class="'file_' + file.id" :file="file" :list="true" />
|
||||
</li>
|
||||
<div
|
||||
v-if="!props.fileList.length"
|
||||
class="pos-relative left-50% top-50% mt-100px transform-translate-x-[-50%] translate-y-[-50%] text-center text-18px"
|
||||
>
|
||||
<icon-lineicons:empty-file />
|
||||
暂无待上传文件
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
</uploader-list>
|
||||
</uploader>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 隐藏上传按钮 */
|
||||
#global-uploader-btn {
|
||||
position: absolute;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
/* 隐藏上传按钮 */
|
||||
#folder-uploader-btn {
|
||||
position: absolute;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
}
|
||||
</style>
|
@ -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');
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'virtual:svg-icons-register';
|
||||
import 'uno.css';
|
||||
import '../styles/css/global.css';
|
||||
import 'vue-simple-uploader/dist/style.css';
|
||||
|
4
src/typings/components.d.ts
vendored
4
src/typings/components.d.ts
vendored
@ -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']
|
||||
|
2
src/typings/global.d.ts
vendored
2
src/typings/global.d.ts
vendored
@ -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 */
|
||||
|
2
src/typings/storage.d.ts
vendored
2
src/typings/storage.d.ts
vendored
@ -38,5 +38,7 @@ declare namespace StorageType {
|
||||
siderCollapse: boolean;
|
||||
};
|
||||
fileShowMode: UnionKey.FileListMode;
|
||||
/** uploader chunk size */
|
||||
uploaderChunkSize: number;
|
||||
}
|
||||
}
|
||||
|
9
src/typings/uploader.d.ts
vendored
Normal file
9
src/typings/uploader.d.ts
vendored
Normal file
@ -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[];
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ const gap = computed(() => (appStore.isMobile ? 0 : 16));
|
||||
<div class="h-full flex pb[-28px]">
|
||||
<NGrid :x-gap="gap" responsive="screen" item-responsive>
|
||||
<NGridItem span="0 s:8 m:6 l:5 xl:4">
|
||||
<FileAside></FileAside>
|
||||
<FileAside />
|
||||
</NGridItem>
|
||||
<NGridItem span="24 s:16 m:18 l:19 xl:20">
|
||||
<FileMain />
|
||||
|
@ -13,6 +13,8 @@ defineOptions({
|
||||
const { SvgIconVNode } = useSvgIcon();
|
||||
const panStore = usePanStore();
|
||||
|
||||
const globalUploaderRef = ref();
|
||||
|
||||
// 是否是批量操作
|
||||
const isBatchMode = ref<boolean>(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 () => {
|
||||
<template>
|
||||
<div class="h-full flex-col overflow-hidden">
|
||||
<NCard class="h-full">
|
||||
<GlobalUploader ref="globalUploaderRef" />
|
||||
<FilePath />
|
||||
<NSpace justify="space-between" class="mt-10px">
|
||||
<NSpace>
|
||||
@ -113,7 +129,7 @@ onMounted(async () => {
|
||||
<NButtonGroup>
|
||||
<NTooltip placement="bottom" trigger="hover">
|
||||
<template #trigger>
|
||||
<NButton>
|
||||
<NButton @click="handleOpenPanel">
|
||||
<template #icon>
|
||||
<icon-cuida:swap-vertical-arrows-outline />
|
||||
</template>
|
||||
|
Loading…
Reference in New Issue
Block a user