首先我们来了解下可能需要使用到的拖拽事件:

dragenter事件】将元素拖拽到目标区域时触发

dragover事件  将元素拖拽到目标区域时(每几百毫秒)触发一次。

dragleave事件】 进入目标区域后将拖拽的元素移出目标区域时触发。

dragend 事件】 在拖放操作结束时触发(通过释放鼠标按钮或单击 escape 键)。

drop 事件】将拖拽的元素放在目标区域时触发。

tips:这里要注意为确保 drop 事件始终按预期触发,应当在处理 dragover 事件的代码部分始终包含 preventDefault() 调用。

 

要实现deepseek的拖拽上传效果,首先我们得有一个外层区域去绑定dragenter事件来获取我们将元素拖拽到目标元素的动作,在

  1. 在外层区域去绑定dragenter事件来获取我们将元素拖拽到目标区域的动作
  2. 拖拽入目标区域后将之前位置的元素背景设置模糊【filter:blur(3px)】同时显示上传结构
  3. 给上传结构绑定【dragleave事件】来获取拖拽元素离开目标区域的动作
  4. 给上传结构绑定【drop.prevent】来获取用户在目标区域放下元素的动作,这里必须调用preventDefault()来阻止浏览器弹出选择文件的弹窗
  5. 为了让drop事件正常触发,必须在上传结构上绑定一个dragover.prevent 
  6. 使用e.dataTransfer.files来获取拖拽操作中的数据

tips:

bug:由于我们目标区域的内部还有一些文字和图片结构,当将拖拽的元素移入我们目标元素内部的图片/文字这些子元素上时,会触发该目标元素绑定的dragleave事件

原因:从技术上讲,拖动元素确实暂时离开了外层div的边界,即使它仍然在外层div的可视区域内。这是因为事件冒泡和拖放目标的边界检测是基于元素的边框进行的。

解决方案:可以检查event.relatedTarget属性来确定离开的目标。如果离开的目标是div的一个子元素,则可能不需要执行任何操作。

具体实现: 

<template>
  <div>
    <div
      class="questionArea"
      @dragenter.prevent.stop="isDrag = true"
      :style="{ filter: isDrag ? 'blur(3px)' : 'none' }"
    >
      <div class="questionInput">
        <el-input
          v-model="sendValue"
          resize="none"
          maxlength="5000"
          type="textarea"
        />
      </div>
    </div>
    <div class="sendGroup flex">
      <!-- 附件上传按钮 -->
      <div class="connectContainer" @click="handleClickUpload">
        <el-tooltip placement="top" :show-after="200" :hide-after="0"
          ><template #content>
            上传附件(仅识别文字)<br />
            最多50个,每个100MB,支持各类文档和图片</template
          >
          <svg-icon icon-class="attachment"
        /></el-tooltip>
      </div>
      <!-- 发送 -->
      <el-button type="primary" color="#006eff" round size="large"
        >发送
      </el-button>
    </div>
    <!-- 拖拽上传区域 -->
    <div
      v-show="isDrag"
      class="drag-upload-demo flex"
      @dragleave.prevent.stop="handleDragLeave"
      @dragover.prevent.stop
      @drop.prevent.stop="handleDragUpload"
    >
      <el-icon class="el-icon--upload" size="50px" color="#a8abb2">
        <upload-filled />
      </el-icon>
      <div class="text">文件拖拽到此处即可上传</div>
    </div>
    <!-- 点击上传file -->
    <input
      class="deepseeek-upload"
      name="file"
      multiple
      :accept="accept"
      type="file"
      @change="
        ($event) => {
          filesChange(Array.from($event.target.files)); //获取点击的文件信息
        }
      "
      style="display: none"
    />
  </div>
</template>

<script setup>
const isDrag = ref(false);
const sendValue = ref("");
const accept =
  ".doc,.docx,xls,xlsx,.pdf,.ppt,.pptx,.txt,.html,.js,.css,.png,.jpg,.jpeg,.bmp,.tiff,.tif";

// 移出拖拽区域
const handleDragLeave = (e) => {
  let demoDom = document.querySelector(".drag-upload-demo");
  // 这里要单独判断 鼠标移出的区域是否属于我们外层绑定drag的dom之外
  !demoDom.contains(e.relatedTarget) && (isDrag.value = false);
};

// 通过拖拽上传文件
const handleDragUpload = (e) => {
  isDrag.value = false;
  // 这里可以通过e.dataTransfer.files来获取到拖拽的文件列表
  // filesChange(Array.from(e.dataTransfer.files));
};

// 通过点击按钮上传文件
const handleClickUpload = () => {
  document.querySelector(".deepseeek-upload").click();
};

const filesChange = (files) => {
  // 这里写你的上传方法,files就是拖拽上传/点击按钮选中的文件
};
</script>

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐