Files
rustfs-console/components/object/upload/picker.vue
T
2024-12-22 16:18:50 +08:00

149 lines
5.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<n-modal :show="show" @update:show="val => $emit('update:show', val)" size="huge">
<n-card class="max-w-screen-md">
<template #header>
<div style="display:flex; justify-content: space-between; align-items:center;">
<span>上传文件</span>
<n-button size="small" ghost @click="closeModal">关闭</n-button>
</div>
</template>
<div class="flex flex-col gap-4">
<div class="flex items-center gap-4">
<input type="file" ref="fileInput" multiple hidden @change="handleFileSelect" />
<input type="file" ref="folderInput" webkitdirectory directory hidden @change="handleFolderSelect" />
<n-button type="primary" @click="selectFile">选择文件</n-button>
<div></div>
<n-button @click="selectFolder">选择文件夹</n-button>
<div>上传至</div>
<div class="text-cyan-600">{{ bucketName }}{{ prefix || '/' }}</div>
</div>
<n-alert title="" type="info">
若上传路径中存在同名文件上传将覆盖原有文件
</n-alert>
<div @dragover.prevent @drop.prevent="handleDrop">
<div v-if="selectedItems.length === 0" class="flex flex-col gap-6 items-center justify-center border border-dashed border-muted rounded p-4 h-[40vh]">
<div class="text-gray-500 font-bold">未选择文件/文件夹</div>
<div class="text-muted-foreground text-center">
Chrome FireFox 支持拖拽到此区域上传支持选择多个文件/文件夹<br>
单个文件最大支持 512GB上传更大的文件请使用命令行工具
</div>
</div>
<!-- 展示文件与文件夹 -->
<n-list v-if="selectedItems.length" bordered class="h-[40vh] overflow-y-auto sticky-header relative">
<template #header>
<div class="flex items-center gap-2 font-bold">
<div class="flex-1">已选择文件/文件夹</div>
<div class="w-32 text-center">大小</div>
<div class="w-32 text-center">操作</div>
</div>
</template>
<n-list-item v-for="(item, index) in selectedItems" :key="index">
<div class="flex items-center gap-2 w-full">
<div class="flex-1">
<span v-if="item.type === 'folder'">{{ item.name }}/</span>
<span v-else>{{ item.name }}</span>
</div>
<div class="w-32 text-center">{{ formatBytes(item.size) }}</div>
<div class="w-32 text-center">
<n-button text type="info" @click="removeItem(index)">删除</n-button>
</div>
</div>
</n-list-item>
</n-list>
</div>
<div class="flex justify-center gap-4">
<n-button type="default" :disabled="!hasFiles">参数配置</n-button>
<n-button type="primary" :disabled="!hasFiles" @click="handleUpload">开始上传</n-button>
</div>
</div>
</n-card>
</n-modal>
</template>
<script setup lang="ts">
import { computed, defineEmits, defineProps, ref } from 'vue'
import { useUploadTaskManagerStore } from '~/store/upload-tasks'
const uploadTaskManagerStore = useUploadTaskManagerStore()
interface SelectedItem {
type: 'file' | 'folder'
name: string
size: number
files?: File[]
}
const emit = defineEmits(['update:show'])
const props = defineProps<{ show: boolean; bucketName: string; prefix: string }>()
const fileInput = ref<HTMLInputElement | null>(null)
const folderInput = ref<HTMLInputElement | null>(null)
// 待上传的文件/文件夹列表
const selectedItems = ref<SelectedItem[]>([])
const closeModal = () => emit('update:show', false)
const selectFile = () => fileInput.value?.click()
const selectFolder = () => folderInput.value?.click()
const handleFileSelect = (e: Event) => {
const input = e.target as HTMLInputElement
if (input.files) {
// 更新展示列表
Array.from(input.files).forEach(f => selectedItems.value.push({ type: 'file', name: f.name, size: f.size, files: [f] }))
}
}
const handleFolderSelect = (e: Event) => {
const input = e.target as HTMLInputElement
if (input.files) {
const folderFiles = Array.from(input.files)
// 找到每一个文件的 top-level 文件夹
const folderMap = new Map<string, File[]>()
for (const file of folderFiles) {
const parts = file.webkitRelativePath.split('/')
const folderName = parts[0] || '未知文件夹'
if (!folderMap.has(folderName)) {
folderMap.set(folderName, [])
}
folderMap.get(folderName)?.push(file)
}
// 只展示文件夹名称,实际上传依然包含 folderFiles
for (const [folderName, files] of folderMap.entries()) {
const totalSize = files.reduce((acc, file) => acc + file.size, 0)
selectedItems.value.push({ type: 'folder', name: folderName, size: totalSize, files })
}
}
}
const handleDrop = (e: DragEvent) => {
if (e.dataTransfer?.files) {
const droppedFiles = Array.from(e.dataTransfer.files)
droppedFiles.forEach(file => selectedItems.value.push({ type: 'file', name: file.name, size: file.size, files: [file] }))
}
}
const hasFiles = computed(() => selectedItems.value.length > 0)
const allFiles = computed(() => selectedItems.value.flatMap(item => item.files || []))
function removeItem(index: number) {
selectedItems.value.splice(index, 1)
}
function handleUpload() {
uploadTaskManagerStore.addFiles(allFiles.value, props.bucketName, props.prefix)
selectedItems.value = []
closeModal()
}
</script>