1. 程式人生 > 程式設計 >基於Vue3的全屏拖拽上傳元件

基於Vue3的全屏拖拽上傳元件

本文主要介紹了基於3的全屏拖拽上傳元件,分享給大家,具體如下:

知識點

  • 瀏覽器拖拽 api
  • fetch 請求
  • vue3

說來話長,長話短說,關於 html5 的拖拽 api 也只是做過一些拖拽排序的例子.其實思路上與其他拖拽上傳元件基本一樣,都是指定一個區域可拖拽,然後讀取檔案在上傳
先說說拖拽 api,這個是 html5 新增的一個 api,給一個元素設定 draggable = true 屬性時,該元素就會支援拖拽
拖拽元素事件如下

1. ondrag 當拖動元素的時候執行
2. ondragstart 當拖動操作開始時候執行指令碼
3. ondragend 當拖動操作結束的時候執行指令碼

目標元素的事件如下:
1. ondragover 當元素被拖動至有效拖放目標上方時執行指令碼
2. ondragenter 當元素被拖動至有效拖動目標時執行指令碼
3. ondragleave 當元素離開至有效拖放目標是執行指令碼
4. ondrop 當被拖動元素正在被放下的時候執行指令碼

比如我們想監聽 body 的拖拽:

const ele = document.querySelector('body')
ele.addEventListener('dragenter',(e) => {
  // do something
})

而當我們想要阻止預設事件的時候我們可以用 e.preventDefault()

元件

先看一下效果,此時我這裡是設定的僅能上傳 png 與 jpg

基於Vue3的全屏拖拽上傳元件

使用:

    <upload
      accept=".jpg,.png,.ico" // 設定檔案型別
      @onChange="change" // 檔案上傳事件
      action="http://localhost:3001/upload" // 上傳地址
      :header="header" // 上傳的header
      autoUpload // 是否自動上傳
      name="file"// 上傳的欄位名
      @onSuccess="onSuccess"  // 上傳成功回撥
    ></upload>

最開始的時候我想獲取拖拽元素的時候莫名發現儘管加了監聽事件,可還是會開啟新的視窗去預覽檔案,所以我們第一步就是先把預設事件都給禁用掉

// 禁用預設拖拽事件
function disableDefaultEvents() {
  const doc = document.documentElement
  doc.addEventListener('dragleave', (e) => e.preventDefault()) //拖離
  doc.addEventListener('drop',(e) => e.preventDefault()) //拖後放
  doc.addEventListener('dragenter',(e) => e.preventDefault()) //拖進
  doc.addEventListener('dragover',(e) => e.preventDefault()) //拖來拖去
}

直接獲取根元素,阻止拖拽的預設事件

第二步就是我們給 body 或是其他元素加上我們想要監聽的事件,這裡有一個注意的是 body 的高度一定是視窗的高度,這樣才會全屏拖拽,在拖離的時候我們還要判斷一下檔案是否被拖出區域

這裡一共有這麼判斷,

e.target.nodeName === 'HTML',這個用來判斷根元素是不是 html
e.target === e.explicitOriginalTarget 這個是火狐特有的一個 api,判斷這兩個觸發事件的目標是否一致
(!e.fromElement &&
        (e.clientX <= 0 ||
          e.clientY <= 0 ||
          e.clientX >= window.innerWidth ||
e.clientY >= window.innerHeight))

這個是用來判斷滑鼠當前的位置的,是否還在區域內

// 初始化拖入事件
function init() {
    // 獲取body元素
  const ele = document.querySelector('body')
  // 新增事件
  //拖後放
  ele.addEventListener('dragenter',() => {
    show.value = true
  })
  // 這裡判斷滑鼠拖離
  ele.addEventListener('dragleave',(e) => {
    if (
      e.target.nodeName === 'HTML' ||
      e.target === e.explicitOriginalTarget ||
      (!e.fromElement &&
        (e.clientX <= 0 ||
          e.clientY <= 0 ||
          e.clientX >= window.innerWidth ||
          e.clientY >= window.innerHeight))
    ) {
      show.value = false
    }
  })
  //拖進
  ele.addEventListener('drop',(e) => {
    show.value = false
    e.preventDefault()
    onDrop(e) // 拖入處理檔案的方法
  })
}

第三步是處理拖入的檔案,此時 accept 是我們定義的檔案型別,此時我們用e.dataTransfer.files這個屬性可以獲得拖入的檔案,
然後我們把拖入的檔案用 filter 做一個過濾,只保留我們需要的檔案型別

checkType(file,accept)就是用來判斷檔案型別的,這一個函式是借鑑了 element ui 裡面的上傳元件的篩選,當時我也是寫蒙了我 😂

// 檢查檔案型別
function checkType(file,accept = '') {
  const { type,name } = file
  if (accept.length === 0) return true
  chttp://www.cppcns.comonst extension = name.indexOf('.') > -1 ? `.${name.split('.').pop()}` : ''
  const baseType = type.replace(/\/.*$/,'')
  return accept
    .split(',')
    .map((type) => type.trim())
    .filter((type) => type)
    .some((acceptedType) => {
      if (/\..+$/.test(acceptedType)) {
        return extension === acceptedType
      }
      if (/\/\*$/.test(acceptedType)) {
        return baseType === acceptedType.replace(/\/\*$/,'')
      }
      if (/^[^/]+\/[^/]+$/.test(acceptedType)) {
        return type === acceptedType
      }
    })
}

這個方法是檔案拖入之後的處理,當我們獲得需要的檔案之後就是根據autoUpload來判斷一下是否上傳

function onDrop(e) {
  const accept = props.accept
  const list = [].slice.call(e.dataTransfer.files).filter((file) => {
    if (accept) {
      return checkType(file,accept)
    }
    return true
  })
  fileList = list.map((p) => {
    return handleStart(p)
  })
  // 觸發事件
  onChange()
  if (props.autwww.cppcns.comoUpload) {
    if (props.action === '') {
      onError()
      throw 'need action'
      return
    }
    list.forEach((file) => {
      post(file) // 上傳檔案
    })
  }
}

原始碼如下:

<template>
  <div class="mask" v-show="show" id="mask">
    <h3>拖拽到這裡上傳</h3>
  </div>
</template>
<script setup>
import { ref,reactive,onMounted } from 'vue'
// import ajax from './ajax'
const props = defineProps({
  name: String,// 上傳的欄位名
  header: { Object,Number,String },// 上傳的檔案頭
  // 驗證的檔案型別,有值的時候只會拖入所有的檔案只會保留設定過濾後的檔案
  accept: {
    type: String,default: '',},// 是否開啟自動上傳
  autoUpload: {
    type: Boolean,default: false,// 上傳地址
  action: {
    type: String,default: '#',})

const emit = defineEmits(['onError','onProgress','onSuccess','onChange']) // 預設emit事件
let show = ref(false) // 是否展示遮罩
let fileList = reactive([]) // 檔案列表
let tempIndex = 0 // 做一個標記
onMounted(() => {
  disableDefaultEvents()
  init()
})
// 初始化拖入事件
function init() {
  const ele = document.querySelector('body')
  ele.addEventListener('dragenter',() => {
    show.value = true
  }) //拖後放
  ele.addEventListener('dragleave',(e) => {
    if (
      e.target.nodeName === 'HTML' ||
      e.target === e.explicitOriginalTarget ||
      (!e.fromElement &&
        (e.clientX <= 0 ||
          e.clientY <= 0 ||
          e.clientX >= window.innerWidth ||
          e.clientY >= window.innerHeight))
    ) {
      show.value = false
    }
  }) //拖離
  ele.addEventListener('drop',(e) => {
    show.value = false
    e.preventDefault()
    onDrop(e)
  }) //拖進
}
// 禁用預設拖拽事件
function disableDefaultEvents() {
  const doc = document.documentElement
  doc.addEventListener('dragleave',(e) => e.preventDefault()) //拖離
  doc.addEventListener('drop',(e) => e.preventDefault()) //拖來拖去
}
// 拖入時的事件
function onDrop(e) {
  const accept = props.accept
  const list = [].slice.call(e.dataTransfer.files).filter((file) => {
    if (accept) {
      return checkType(file,accept)
    }
    return true
  })
  fileList = list.map((p) => {
    return handleStart(p)
  })
  onChange()
  if (props.autoUpload) {
    if (props.action === '') {
      onError()
      throw 'need action'
      return
    }
    list.forEach((file) => {
      post(file)
    })
  }
}
// 檢查檔案型別
function checkType(file,name } = file
  if (accept.length === 0) return true
  const extension = name.indexOf('.') > -1 ? `.${name.split('.').pop()}` : ''
  const baseType = type.replace(/\/.*$/,'')
      }
      if (/^[^/]+\/[^/]+$/.test(acceptedType)) {
        return type === acceptedType
      }
    })
}
// 處理檔案列表返回值
function handleStart(rawFile) {
  rawFile.uid = Date.now() + tempIndex++
  return {
    status: 'ready',name: rawFile.name,size: rawFile.size,percentage: 0,uid: rawFile.uid,raw: rawFile,}
}
// 上傳的事件
function post(rawFile) {
  const options = {
    headers: props.header,file: rawFile,data: props.data || '',filename: props.name || 'file',action: props.action,}
  upload(options)
    .then((res) => {
      res.on()
    })
    .then((json) => {
      onSuccess(json,rawFile)
    })
    .catch((err) => {
      onError(err,rawFile)
    })
}
// 檔案上傳方法
function upload(option) {
  const action = option.action

  const formData = new FormData()

  if (option.data) {
    Object.keys(option.data).forEach((key) => {
      formData.append(key,option.data[key])
    })
  }
  formData.append(option.filename,option.file,option.file.name)

  const headers = new Headers()
  for (let item in headers) {
    if (headers.hasOwnProperty(item) && headers[item] !== null) {
      headers.append(i,option.headers[i])
    }
  }
  return fetch(action,{
    mode: 'no-cors',body: formData,headers: headers,method: 'post',})
}

// 拖拽進去獲取檔案列表的事件
function onChange() {
  emit('onChange',fileList)
}
// 上傳中的事件
function onProgress(e,file) {
  emit('onProgress',e,file,fileList)
}
// 上傳成功事件
function onSuccess(res,res,fileList)
}
// 上傳失敗事件
function onError() {
  emit('onError')
}
</script>
<style scoped>
.mask {
  top: 0;
  bottom: 0;
  right: 0;
  left: 0;
  position: fixed;
  z-index: 9999;
  opacity: 0.6;
  text-align: center;
  background: #000;
}
h3 {
  margin: -0.5em 0 0;
  position: http://www.cppcns.comabsolute;
  top: 50%;
  left: 0;
  right: 0;
  -webkit-transform: translateY(-50%);
  -ms-transform: translateY(-50%);
  transform: translateY(-50%);
  font-size: 40px;
  color: #fff;
  padding: 0;
}
</style>

到此這篇關於基於Vue3的全屏拖拽上傳元件的文章就介紹到這了,更多相關Vue3 全屏拖拽上傳內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!