从0搭建vue3组件库:实现Upload组件文件拖拽上传

简介: 从0搭建vue3组件库:实现Upload组件文件拖拽上传

image.png


上篇文章从0搭建vue3组件库:Upload文件上传组件已经实现基本的文件上传组件,本篇文章将为Upload组件加入拖拽上传(drag)的功能。


定义props



首先在types.ts中定义一个drag来控制是否使用拖拽上传

import { ExtractPropTypes } from 'vue'
export const uoloadType = {
    multiple: Boolean,
    accept: String,
    drag: Boolean
}
export type LinkProps = ExtractPropTypes<typeof uoloadType>


区域样式



upload.vue中通过判断用户是否传入drag来控制拖拽区域的显示与隐藏,并且为拖拽区域定义一些样式。部分代码省略,文章最后会贴最终完整代码

  • upload.vue部分代码
<div @click="fileUpload" v-if="!props.drag">
            <slot />
        </div>
        <div class="k-upload-dragger" v-else>
            <div class="k-upload-content">
                <Icon class="k-upload-icon" name="folder-close" />
                <div class="k-upload-dragger-text">将文件拖到此处或<em>点击上传</em></div>
            </div>
        </div>
  • style/index.less 部分代码
.k-upload-dragger {
    background-color: #fff;
    border: 1px dashed #d9d9d9;
    border-radius: 6px;
    box-sizing: border-box;
    width: 360px;
    height: 180px;
    display: flex;
    cursor: pointer;
    align-items: center;
    justify-content: center;
    &:hover {
      border: 1px dashed #409eff;
    }
    .k-upload-content {
      text-align: center;
      color: #606266;
      .k-upload-icon {
        font-size: 20px;
      }
      em {
        color: #409eff;
        font-style: normal;
      }
    }
  }

然后在本地测试项目examples下的app.vue中引入

<template>
    <div class="upload-demo">
        <k-upload @getFilesList="getFilesList" drag multiple accept="image/*"></k-upload>
    </div>
</template>
<script lang="ts" setup>
const getFilesList = (files: File[]) => {
    console.log(files)
}
</script>
<style lang="less">
.upload-demo {
    width: 400px;
}
</style>

注意这里为了调试方便,已经全局导入了kitty-ui库了。对应的main.ts

import { createApp } from 'vue'
import App from './app.vue'
const app = createApp(App)
import kittyui from "kitty-ui"
app.use(kittyui)
app.mount('#app')

启动项目,便可以看到下面的效果

image.png


拖拽实现



接下来要做的就是将文件拖进来获取到文件列表以及点击上传。其中点击上传很简单,只需要绑定和上面一样的事件即可

  • upload.vue部分代码
<div @click="fileUpload" v-if="!props.drag">
            <slot />
        </div>
        <div class="k-upload-dragger" @click="fileUpload" v-else>
            <div class="k-upload-content">
                <Icon class="k-upload-icon" name="folder-close" />
                <div class="k-upload-dragger-text">将文件拖到此处或<em>点击上传</em></div>
            </div>
        </div>

实现拖拽上传可以借助drop事件。在组件生命周期onMounted中获取到拖拽区域的dom,然后监听它的drop事件。

首先给拖拽区域一个ref属性

<div class="k-upload-dragger" ref="fileArea" @click="fileUpload" v-else>
            <div class="k-upload-content">
                <Icon class="k-upload-icon" name="folder-close" />
                <div class="k-upload-dragger-text">将文件拖到此处或<em>点击上传</em></div>
            </div>
        </div>

然后在组件创建完成后进行事件监听

const fileArea = ref()
onMounted(() => {
    fileArea.value.addEventListener('drop', (e: any) => {
        e.preventDefault();
        console.log(e)
    }, false)
    fileArea.value.addEventListener('dragover', (e: any) => {
        e.preventDefault();
    }, false)
})

注意 这里需要阻止dragover的默认事件,不然drop是不生效的.此时我们将文件拖拽到这个区域后我们可以看到控制台打印很多东西,而我们只需要e.dataTransfer.files即可(不知道为什么控制台上显示files的长度为0,但是实际代码中却可以获取到)

image.png

获取到文件之后把文件名字渲染到下面的列表中,并且将文件列表传给用户,同时我们做个限制,如果没有传入drag则不监听这两个事件

const fileArea = ref()
onMounted(() => {
    if (!props.drag) return
    fileArea.value.addEventListener('drop', (e: any) => {
        e.preventDefault();
        filesList.value.push(...Array.from(e.dataTransfer.files as FileList))
        emits('getFilesList', filesList.value)
    }, false)
    fileArea.value.addEventListener('dragover', (e: Event) => {
        e.preventDefault();
    }, false)
})

最终拖拽完毕的效果为

image.png

最后我们再加两个事件dragenterdragleave来判断文件是否拖到这个区域从而展示不同样式

  • template 部分代码
<div v-else class="k-upload-dragger" :class="{ ['k-upload-draggerenter']: isEnter }" ref="fileArea"
            @click="fileUpload">
            <div class="k-upload-content">
                <Icon class="k-upload-icon" :name="isEnter ? 'file-open' : 'folder-close'" />
                <div class="k-upload-dragger-text">将文件拖到此处或<em>点击上传</em></div>
            </div>
        </div>
  • script部分代码
const fileArea = ref()
const isEnter = ref(false)
onMounted(() => {
    if (!props.drag) return
    fileArea.value.addEventListener('drop', (e: any) => {
        e.preventDefault();
        filesList.value.push(...Array.from(e.dataTransfer.files as FileList))
        emits('getFilesList', filesList.value)
    }, false)
    fileArea.value.addEventListener('dragover', (e: Event) => {
        e.preventDefault();
    }, false)
    fileArea.value.addEventListener('dragenter', (e: Event) => {
        isEnter.value = true
        e.preventDefault();
    }, false)
    fileArea.value.addEventListener('dragleave', (e: Event) => {
        isEnter.value = false
        e.preventDefault();
    }, false)
})

此时文件进入的效果

image.png


写在最后



到这里Upload组件拖拽上传的功能基本已经实现,如果大家对vue3组件库搭建感兴趣的话,欢迎大家关注 从零搭建Vue3组件库专栏 将持续更新一些组件的实现。


完整代码



完整代码点击 kitty-ui 获取,最后希望大家给个👍



相关文章
|
3天前
|
JavaScript 前端开发 CDN
vue3速览
vue3速览
14 0
|
3天前
|
设计模式 JavaScript 前端开发
Vue3报错Property “xxx“ was accessed during render but is not defined on instance
Vue3报错Property “xxx“ was accessed during render but is not defined on instance
|
3天前
|
JavaScript API
Vue3 官方文档速通(中)
Vue3 官方文档速通(中)
20 0
|
3天前
|
缓存 JavaScript 前端开发
Vue3 官方文档速通(上)
Vue3 官方文档速通(上)
24 0
|
3天前
Vue3+Vite+Pinia+Naive后台管理系统搭建之五:Pinia 状态管理
Vue3+Vite+Pinia+Naive后台管理系统搭建之五:Pinia 状态管理
8 1
|
3天前
Vue3+Vite+Pinia+Naive后台管理系统搭建之三:vue-router 的安装和使用
Vue3+Vite+Pinia+Naive后台管理系统搭建之三:vue-router 的安装和使用
8 0
|
3天前
Vue3+Vite+Pinia+Naive后台管理系统搭建之二:scss 的安装和使用
Vue3+Vite+Pinia+Naive后台管理系统搭建之二:scss 的安装和使用
8 0
|
3天前
|
JavaScript 前端开发 API
Vue3 系列:从0开始学习vue3.0
Vue3 系列:从0开始学习vue3.0
9 1
|
3天前
|
网络架构
Vue3 系列:vue-router
Vue3 系列:vue-router
9 2
|
3天前
Vue3+Vite+Pinia+Naive后台管理系统搭建之一:基础项目构建
Vue3+Vite+Pinia+Naive后台管理系统搭建之一:基础项目构建
7 1