TinyRobot DragOverlay轻松实现AI对话中的拖拽上传

TinyRobot DragOverlay:轻松实现 AI 对话中的拖拽上传

在 AI 对话应用中,文件上传是与大模型交互的重要方式之一。用户经常需要上传文档、图片、代码文件等作为上下文输入,而传统的点击选择文件方式操作路径长、交互不够直观。拖拽上传作为一种更现代化的交互模式,让用户可以直接将文件拖入对话区域,操作自然流畅,大幅提升用户体验。

TinyRobot 的 v-dropzone 指令与 tr-drag-overlay 组件正是为此场景设计,二者协同工作:v-dropzone 负责监听拖拽事件、文件过滤与校验,tr-drag-overlay 负责展示拖拽过程中的视觉反馈。下面我们来详细了解它们的使用方式。

基础用法:v-dropzone + tr-drag-overlay 协作

v-dropzone 是一个自定义指令,绑定到目标容器元素上,使其成为拖拽区域。tr-drag-overlay 则是覆盖在拖拽区域上方的视觉反馈层。二者通过 is-dragging 状态变量联动:

vue 复制代码
<template>
  <div v-dropzone="dropzoneConfig" class="chat-area">
    <tr-drag-overlay
      :is-dragging="isDragging"
      :drag-target="dragTarget"
      overlay-title="上传文件"
      :overlay-description="['将文件拖到此处上传', '支持图片、文档等常见格式']"
    />
    <!-- 对话内容 -->
    <div class="messages">
      <p>对话消息区域...</p>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { TrDragOverlay, vDropzone } from '@opentiny/tiny-robot'

const isDragging = ref(false)
const dragTarget = ref(null)

const dropzoneConfig = {
  onDrop(files) {
    console.log('上传的文件:', files)
    // 处理文件上传逻辑
  },
  onDraggingChange(dragging, target) {
    isDragging.value = dragging
    dragTarget.value = target
  }
}
</script>

<style scoped>
.chat-area {
  position: relative;
  min-height: 500px;
}
</style>

当用户将文件拖入 chat-area 区域时,onDraggingChange 回调触发,将 isDragging 设为 truetr-drag-overlay 随即显示覆盖层;文件放下后,onDrop 回调接收文件列表,覆盖层自动消失。

文件类型过滤

通过 accept 属性可以限定可接受的文件类型,格式与 HTML <input type="file">accept 属性一致:

vue 复制代码
<template>
  <div v-dropzone="dropzoneConfig" class="chat-area">
    <tr-drag-overlay
      :is-dragging="isDragging"
      :drag-target="dragTarget"
      overlay-title="上传图片"
      :overlay-description="['仅支持 JPG、PNG、GIF 格式']"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { TrDragOverlay, vDropzone } from '@opentiny/tiny-robot'

const isDragging = ref(false)
const dragTarget = ref(null)

const dropzoneConfig = {
  accept: '.jpg,.jpeg,.png,.gif,image/jpeg,image/png,image/gif',
  onDrop(files) {
    console.log('图片文件:', files)
  },
  onDraggingChange(dragging, target) {
    isDragging.value = dragging
    dragTarget.value = target
  }
}
</script>

不符合类型要求的文件会在 onError 回调中被捕获,codeFILE_TYPE_NOT_ALLOWED

文件大小与数量限制

在 AI 对话场景中,通常需要对上传文件的大小和数量加以限制,maxSizemaxFiles 属性分别控制这两个维度:

vue 复制代码
<template>
  <div v-dropzone="dropzoneConfig" class="chat-area">
    <tr-drag-overlay
      :is-dragging="isDragging"
      :drag-target="dragTarget"
      overlay-title="上传文件"
      :overlay-description="['单个文件不超过 5MB', '每次最多上传 5 个文件']"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { TrDragOverlay, vDropzone } from '@opentiny/tiny-robot'

const isDragging = ref(false)
const dragTarget = ref(null)

const dropzoneConfig = {
  maxSize: 5 * 1024 * 1024, // 5MB
  maxFiles: 5,
  onDrop(files) {
    console.log('有效文件:', files)
  },
  onError(rejection) {
    console.warn('文件被拒绝:', rejection.code, rejection.message)
    // rejection.code 可能是 'FILE_SIZE_EXCEEDED' 或 'MAX_FILES_EXCEEDED'
  },
  onDraggingChange(dragging, target) {
    isDragging.value = dragging
    dragTarget.value = target
  }
}
</script>

当文件超出大小限制时,onError 回调接收 FileRejection 对象,codeFILE_SIZE_EXCEEDED;超过数量限制时,codeMAX_FILES_EXCEEDED

自定义覆盖层内容

默认的覆盖层提供了标题和描述文字,如果需要更丰富的视觉效果或自定义提示,可以使用 overlay 插槽:

vue 复制代码
<template>
  <div v-dropzone="dropzoneConfig" class="chat-area">
    <tr-drag-overlay :is-dragging="isDragging" :drag-target="dragTarget">
      <template #overlay>
        <div class="custom-overlay">
          <div class="overlay-icon">📎</div>
          <p class="overlay-title">松开即可上传</p>
          <p class="overlay-hint">支持 PDF、Word、Excel 等文档</p>
        </div>
      </template>
    </tr-drag-overlay>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { TrDragOverlay, vDropzone } from '@opentiny/tiny-robot'

const isDragging = ref(false)
const dragTarget = ref(null)

const dropzoneConfig = {
  onDrop(files) {
    console.log('上传文件:', files)
  },
  onDraggingChange(dragging, target) {
    isDragging.value = dragging
    dragTarget.value = target
  }
}
</script>

<style scoped>
.custom-overlay {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
.overlay-icon {
  font-size: 48px;
}
.overlay-title {
  font-size: 18px;
  font-weight: 600;
  color: #409eff;
}
.overlay-hint {
  font-size: 14px;
  color: #909399;
}
</style>

禁用状态

在某些场景下(如正在上传、对话已结束等),需要临时禁用拖拽上传功能。通过 disabled 属性即可控制:

vue 复制代码
<template>
  <div v-dropzone="dropzoneConfig" class="chat-area">
    <tr-drag-overlay
      :is-dragging="isDragging"
      :drag-target="dragTarget"
      overlay-title="上传文件"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { TrDragOverlay, vDropzone } from '@opentiny/tiny-robot'

const isDragging = ref(false)
const dragTarget = ref(null)
const uploading = ref(false)

const dropzoneConfig = {
  disabled: uploading,
  onDrop(files) {
    uploading.value = true
    // 模拟上传过程
    setTimeout(() => {
      uploading.value = false
      console.log('上传完成')
    }, 2000)
  },
  onDraggingChange(dragging, target) {
    isDragging.value = dragging
    dragTarget.value = target
  }
}
</script>

disabledtrue 时,拖拽区域不响应拖拽事件,onDraggingChange 不会被触发。

错误处理

onError 回调接收 FileRejection 类型的参数,包含错误码、错误信息和被拒绝的文件列表,便于在 UI 中给出精确提示:

vue 复制代码
<template>
  <div v-dropzone="dropzoneConfig" class="chat-area">
    <tr-drag-overlay
      :is-dragging="isDragging"
      :drag-target="dragTarget"
      overlay-title="上传文件"
    />
    <div v-if="errorMessage" class="error-toast">{{ errorMessage }}</div>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { TrDragOverlay, vDropzone } from '@opentiny/tiny-robot'

const isDragging = ref(false)
const dragTarget = ref(null)
const errorMessage = ref('')

const ERROR_MESSAGES = {
  FILE_TYPE_NOT_ALLOWED: '不支持的文件类型',
  FILE_SIZE_EXCEEDED: '文件大小超出限制',
  MAX_FILES_EXCEEDED: '文件数量超出限制'
}

const dropzoneConfig = {
  accept: '.pdf,.doc,.docx',
  maxSize: 10 * 1024 * 1024,
  maxFiles: 3,
  onDrop(files) {
    errorMessage.value = ''
    console.log('有效文件:', files)
  },
  onError(rejection) {
    errorMessage.value = ERROR_MESSAGES[rejection.code] || rejection.message
    console.warn('被拒绝的文件:', rejection.files)
    // 3 秒后自动清除提示
    setTimeout(() => {
      errorMessage.value = ''
    }, 3000)
  },
  onDraggingChange(dragging, target) {
    isDragging.value = dragging
    dragTarget.value = target
  }
}
</script>

<style scoped>
.error-toast {
  position: fixed;
  top: 20px;
  left: 50%;
  transform: translateX(-50%);
  padding: 10px 20px;
  background: #fef0f0;
  color: #f56c6c;
  border-radius: 4px;
  font-size: 14px;
}
</style>

全屏模式

当拖拽区域需要覆盖整个页面时(如全局文件拖入检测),可以启用 fullscreen 模式:

vue 复制代码
<template>
  <div v-dropzone="dropzoneConfig" class="chat-page">
    <tr-drag-overlay
      :is-dragging="isDragging"
      :drag-target="dragTarget"
      overlay-title="上传到当前对话"
      :overlay-description="['文件将添加到当前 AI 对话中']"
      fullscreen
    />
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { TrDragOverlay, vDropzone } from '@opentiny/tiny-robot'

const isDragging = ref(false)
const dragTarget = ref(null)

const dropzoneConfig = {
  onDrop(files) {
    console.log('全局拖入文件:', files)
  },
  onDraggingChange(dragging, target) {
    isDragging.value = dragging
    dragTarget.value = target
  }
}
</script>

fullscreentrue 时,覆盖层会以 position: fixed 定位到整个视口,无论用户拖拽到页面哪个位置都能触达。

API 参考

v-dropzone 指令属性

属性 类型 默认值 说明
accept string '' 可接受的文件类型,格式同 HTML accept 属性
multiple boolean true 是否允许多文件上传
maxSize number 10485760 单个文件最大字节数,默认 10MB
maxFiles number 3 最大文件数量
disabled boolean - 是否禁用拖拽功能
onDrop (files: File[]) => void - 文件放下时的回调
onError (rejection: FileRejection) => void - 文件校验不通过时的回调
onDraggingChange `(dragging: boolean, target: Element null) => void` -

tr-drag-overlay Props

属性 类型 默认值 说明
is-dragging boolean - 当前是否处于拖拽状态,与 v-dropzoneonDraggingChange 联动
drag-target `Element null` -
overlay-title string - 覆盖层标题文字
overlay-description string[] - 覆盖层描述文字数组
fullscreen boolean false 是否全屏显示覆盖层

tr-drag-overlay Slots

插槽名 说明
overlay 自定义覆盖层内容,替换默认的标题和描述

FileRejection 类型

typescript 复制代码
interface FileRejection {
  code: string       // 错误码:FILE_TYPE_NOT_ALLOWED | FILE_SIZE_EXCEEDED | MAX_FILES_EXCEEDED
  message: string    // 错误描述信息
  files: File[]      // 被拒绝的文件列表
}

OpenTiny NEXT 是一套企业智能前端开发解决方案,以生成式 UI 和 WebMCP 两大核心技术为基础,对现有传统的 TinyVue 组件库、TinyEngine 低代码引擎等产品进行智能化升级,构建出面向 Agent 应用的前端 NEXT-SDKs、AI Extension、TinyRobot智能助手、GenUI等新产品,加速企业应用的智能化改造,实现AI理解用户意图自主完成任务。

欢迎加入 OpenTiny 开源社区。添加微信小助手:opentiny-official 一起参与交流前端技术~

OpenTiny 官网:opentiny.design/ TinyRobot 代码仓库:github.com/opentiny/ti... (欢迎star ⭐) TinyRobot skill源码:github.com/opentiny/ag... (欢迎 Star ⭐)

相关推荐
elirlove11 小时前
打造属于自己的网页工匠台:HTML在线编辑器技术深度解析
前端·编辑器·html
wh_xmy1 小时前
从HTML5到AI,我的前端十年
前端·程序人生·十年程序员·ai 对前端的影响
程序员mine1 小时前
Web服务密码存储安全详解:从哈希到密钥派生的演进
前端·后端
如果超人不会飞1 小时前
TinyRobot Sender打造强大的AI聊天输入体验
前端·vue.js
爱吃生蚝的于勒1 小时前
QT开发第三章——常用控件
linux·服务器·开发语言·前端·javascript·c++·qt
xuankuxiaoyao1 小时前
Axios-图书列表案例
开发语言·前端·javascript
影寂ldy1 小时前
C# 多播委托
前端·javascript·c#
dy17171 小时前
Vue3 多文件上传
前端·javascript·vue.js
文阿花2 小时前
Echarts实现3D饼状图
前端·javascript·echarts·饼状图