一、示例
二、组件代码
<template>
<div
class="drop-area"
:class="{ dragging: isDragging }"
@dragover.prevent="handleDragOver"
@dragleave="handleDragLeave"
@drop.prevent="handleDrop"
>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<p>拖拽文件或文件夹到此处,开始上传</p>
<!-- <p v-if="fileList.length">已选择 {{ fileList.length }} 个文件</p> -->
</div>
</template>
<script setup>
import { ref, defineEmits } from 'vue'
const emits = defineEmits(['change'])
// 响应式状态
const isDragging = ref(false)
const fileList = ref([])
// 处理拖拽进入
const handleDragOver = (e) => {
isDragging.value = true
e.dataTransfer.dropEffect = 'copy'
}
// 处理拖拽离开
const handleDragLeave = () => {
isDragging.value = false
}
// 处理文件放置
const handleDrop = async (e) => {
isDragging.value = false
const droppedFiles = await extractFilesFromDropEvent(e)
fileList.value = [...droppedFiles]
console.log(droppedFiles, 123)
emits('change', droppedFiles)
}
// 从拖拽事件中提取所有文件
const extractFilesFromDropEvent = async (event) => {
const items = event.dataTransfer.items
console.log('拖拽项数量:', items?.length)
if (!items) return []
// 使用Promise.all并行处理所有文件项
const promises = Array.from(items).map(async (item) => {
if (item.kind === 'file') {
const entry = item.webkitGetAsEntry()
if (entry) {
console.log('正在处理文件:', entry)
// 递归处理文件条目
const fileResult = await traverseFileEntry(entry, '')
return fileResult
}
}
return null
})
// 等待所有处理完成并合并结果
const results = await Promise.all(promises)
// 扁平化结果数组并过滤掉null值
return results.flat().filter(Boolean)
}
// 递归遍历文件系统条目
const traverseFileEntry = (entry, relativePath) => {
return new Promise((resolve) => {
if (entry.isFile) {
// 处理文件
entry.file((file) => {
// 添加相对路径信息
file.relativePath = relativePath ? `${relativePath}/${file.name}` : ''
resolve(file)
})
} else if (entry.isDirectory) {
// 处理目录
const dirReader = entry.createReader()
dirReader.readEntries(async (entries) => {
const subPath = relativePath
? `${relativePath}/${entry.name}`
: entry.name
// 对目录项使用Promise.all并行处理
const promises = Array.from(entries).map((subEntry) =>
traverseFileEntry(subEntry, subPath)
)
const results = await Promise.all(promises)
// 扁平化目录下的所有文件
resolve(results.flat().filter(Boolean))
})
} else {
resolve(null)
}
})
}
</script>
<style scoped>
.drop-area {
width: 100%;
min-height: 100px;
border: 2px dashed #ccc;
border-radius: 8px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px;
transition: all 0.3s ease;
margin: 0px;
}
.drop-area.dragging {
border-color: #409eff;
background-color: rgba(64, 158, 255, 0.1);
}
.drop-area p {
margin: 5px 0;
color: #666;
}
.drop-area .el-icon--upload {
font-size: 50px;
color: #999;
}
</style>
@change获取上传file文件