demo

12 阅读2分钟

我理解您的需求了 - 您希望初始状态下上传区域保持原始大小,但拖拽文件时感应区域变大,以便有更大的区域可以接收拖拽的文件。这是一个巧妙的用户体验优化。

我们可以通过以下方式实现:

  1. 保持原始上传组件的尺寸不变
  2. 添加一个覆盖在页面上的隐藏区域,当用户开始拖拽文件时显示这个更大的区域
  3. 监听全局的拖拽事件来控制这个扩展区域的显示与隐藏

以下是实现代码:

<template>
  <div class="upload-container">
    <!-- 原始大小的上传组件 -->
    <el-upload
      ref="uploadRef"
      class="original-uploader"
      drag
      action="https://your-upload-url.com/upload"
      multiple
      :on-success="handleSuccess"
      :before-upload="beforeUpload"
    >
      <el-icon class="el-icon--upload"><upload-filled /></el-icon>
      <div class="el-upload__text">
        拖拽文件到此处 或 <em>点击上传</em>
      </div>
      <template #tip>
        <div class="el-upload__tip">
          支持上传jpg/png文件,且不超过500kb
        </div>
      </template>
    </el-upload>

    <!-- 拖拽时显示的扩展区域 -->
    <div 
      v-show="isDragging" 
      class="extended-drop-area"
      @dragover.prevent
      @drop.prevent="handleExtendedDrop"
    >
      <div class="drop-area-content">
        <el-icon class="el-icon--upload"><upload-filled /></el-icon>
        <div class="drop-text">释放鼠标上传文件</div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { UploadFilled } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'

const uploadRef = ref(null)
const isDragging = ref(false)
let dragCounter = 0

// 处理上传成功
const handleSuccess = (response, file, fileList) => {
  ElMessage.success('上传成功!')
}

// 上传前的验证
const beforeUpload = (file) => {
  // 文件验证逻辑
  return true
}

// 处理扩展区域的文件放置
const handleExtendedDrop = (e) => {
  // 获取Element Plus上传组件的内部上传方法
  const uploadComponent = uploadRef.value
  if (uploadComponent && e.dataTransfer.files) {
    // 使用上传组件的方法处理文件
    Array.from(e.dataTransfer.files).forEach(file => {
      uploadComponent.uploadFiles([file])
    })
  }
  isDragging.value = false
  dragCounter = 0
}

// 添加全局拖拽事件监听
const handleDragEnter = (e) => {
  e.preventDefault()
  dragCounter++
  if (e.dataTransfer.items && e.dataTransfer.items.length > 0) {
    isDragging.value = true
  }
}

const handleDragLeave = (e) => {
  e.preventDefault()
  dragCounter--
  if (dragCounter === 0) {
    isDragging.value = false
  }
}

const handleDragOver = (e) => {
  e.preventDefault()
}

const handleDrop = (e) => {
  e.preventDefault()
  isDragging.value = false
  dragCounter = 0
}

onMounted(() => {
  // 添加全局事件监听
  document.addEventListener('dragenter', handleDragEnter)
  document.addEventListener('dragleave', handleDragLeave)
  document.addEventListener('dragover', handleDragOver)
  document.addEventListener('drop', handleDrop)
})

onUnmounted(() => {
  // 移除全局事件监听
  document.removeEventListener('dragenter', handleDragEnter)
  document.removeEventListener('dragleave', handleDragLeave)
  document.removeEventListener('dragover', handleDragOver)
  document.removeEventListener('drop', handleDrop)
})
</script>

<style scoped>
.upload-container {
  position: relative;
  width: 360px; /* 或者您需要的宽度 */
}

/* 原始上传组件样式保持不变 */
.original-uploader {
  width: 100%;
}

/* 拖拽时显示的扩展区域 */
.extended-drop-area {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(64, 158, 255, 0.1);
  border: 3px dashed #409EFF;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: all;
}

.drop-area-content {
  background-color: white;
  padding: 40px;
  border-radius: 8px;
  text-align: center;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

.drop-text {
  margin-top: 16px;
  font-size: 18px;
  color: #409EFF;
  font-weight: bold;
}

:deep(.el-icon--upload) {
  font-size: 48px;
  color: #409EFF;
}
</style>

这个实现的工作原理:

  1. 保持原始的 El-Upload 组件尺寸不变

  2. 创建一个扩展的拖拽区域,默认隐藏(v-show="isDragging"

  3. 使用全局事件监听器检测文件拖拽:

    • dragenter:当文件开始被拖入页面时,显示扩展区域
    • dragleave:当文件拖出页面时,隐藏扩展区域
    • drop:当文件被放置时,关闭扩展区域
  4. 使用计数器 dragCounter 解决拖拽事件冒泡问题,确保精确控制扩展区域的显示和隐藏

  5. 当文件放置在扩展区域时,调用原始上传组件的上传方法处理文件

这样实现后,初始状态下上传区域保持原始大小,但一旦用户开始拖拽文件,整个页面都变成可接收拖拽的区域,提供了更好的用户体验。

您可以根据需要调整扩展区域的样式,比如背景颜色、透明度、边框等,使其与您的应用设计风格一致。​​​​​​​​​​​​​​​​