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文件

相关推荐
kyriewen7 分钟前
CSS Container Queries:彻底告别 @media 写到手软,附 5 个真实布局案例
前端·css·面试
Patrick_Wilson1 小时前
router.replace 之后紧跟 reload,页面为什么无限刷新?
javascript·react.js·浏览器
小小小小宇2 小时前
OpenMemory MCP
前端
和平宇宙2 小时前
AI笔记005. hermes-DeepSeek V4 Pro, 128K上下文引发的探索
前端·人工智能·笔记
IT_陈寒2 小时前
Redis持久化这个坑,我爬了一整天才出来
前端·人工智能·后端
naildingding3 小时前
3-ts接口 Interface
前端·typescript
mONESY3 小时前
JavaScript 栈、队列、数组与链表核心知识点总结
javascript·面试
小小前端仔LC3 小时前
Node.js + LangChain + React:搭建个人知识库(六)- “吃什么”项目实战:从700+菜谱入库到Taro H5端JSON渲染
前端·后端
ZengLiangYi3 小时前
TypeScript 项目配置:tsconfig、ESM、路径别名
javascript·typescript·aigc
晓13133 小时前
【Cocos Creator 3.x】篇——第二章 入门
前端·javascript·游戏引擎