vue文件或文件夹拖拽上传

一、示例

二、组件代码

javascript 复制代码
<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文件

相关推荐
一点一木12 小时前
从60首歌到1个网站:输入你的故事,还你一首歌
前端·github
IT_陈寒14 小时前
Vue这个特性差点让我加班到凌晨,谁懂啊
前端·人工智能·后端
kyriewen14 小时前
我让AI改一个bug——它偷偷动了5个我没让它碰的地方
前端·javascript·ai编程
遇乐的果园14 小时前
前端学习笔记-vue加载渲染优化
前端·笔记·学习
用户9850335932815 小时前
OpenLayers 热力图从原理到 Vue 组件一键接入
前端
颜酱15 小时前
01 | 骨架搭建:FastAPI + Vue 跑通第一个 SSE 流式问答
前端·人工智能·后端
hoLzwEge15 小时前
解码 IDE 智能提示:jsconfig.json 辅助开发全攻略
前端·前端框架
leoZ23115 小时前
记忆系统与 Agent 定制完全指南(四):自定义 Agent 开发(一)
前端·chrome
慧一居士15 小时前
Element Plus 按需引入的配置使用说明和完整示例
前端·vue.js
程序员黑豆15 小时前
鸿蒙应用开发实战:从零学会自定义组件
前端·华为·harmonyos