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 设为 true,tr-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 回调中被捕获,code 为 FILE_TYPE_NOT_ALLOWED。
文件大小与数量限制
在 AI 对话场景中,通常需要对上传文件的大小和数量加以限制,maxSize 和 maxFiles 属性分别控制这两个维度:
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 对象,code 为 FILE_SIZE_EXCEEDED;超过数量限制时,code 为 MAX_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>
当 disabled 为 true 时,拖拽区域不响应拖拽事件,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>
fullscreen 为 true 时,覆盖层会以 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-dropzone 的 onDraggingChange 联动 |
| 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 ⭐)