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

3,827 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第8天,点击查看活动详情

上篇文章从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') 

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

1661270770334.jpg

拖拽实现

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

  • 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,但是实际代码中却可以获取到)

1661273011890.jpg

获取到文件之后把文件名字渲染到下面的列表中,并且将文件列表传给用户,同时我们做个限制,如果没有传入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)
})

最终拖拽完毕的效果为

1661273923190.jpg

最后我们再加两个事件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)
})

此时文件进入的效果

1661274444549.jpg

写在最后

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

完整代码

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