element-ui/el-upload上传oss,支持截屏后粘贴上传

upload上传是前端开发很常用的一个功能,在Vue开发中常用的Element组件库也提供了非常好用的upload组件。本文采用scoped-slot 去设置缩略图模版,并且实现了oss粘贴上传功能。

一、html代码

使用插槽scoped-slot获取当前上传的状态file。通过file.status的状态判断是否上传成功。file.status为uploading代表正在上传中,为success代表上传成功。上传中hover到文件显示上传的文件大小、进度、速度。

二、js代码

1.首先声明data参数

data() {
    return {
      thumbnailMaxSize: 1048576 * 20, // 缩略图(最大20M)
      id: `baseUpload_${new Date().getTime()}`,
      uploadStartTimeList: [], // 存储开始上传的事件
      OSS_URL: defaultSettings.OSS_URL, // oss上传地址
      myValue: this.value,
      tokens: {},
      dialogImageUrl: '',
      dialogVisible: false,
      headers: {
        Authorization: getToken()
      }
    }
  },

2.计算属性设置,这里有一个需要注意的点,oss缩略图限制图片最大为20M。如果上传图片过大,可以直接显示图片,另外判断大于20M显示原图片。

  computed: {
    // 缩略图地址
    getThumbnailUrl(file) {
      return (file) => {
        return validURL(file.url) && file.size < this.thumbnailMaxSize ? file.url + `?x-oss-process=image/resize,w_${this.width}` : file.url
      }
    }
  },

3.文件上传前钩子。前面说到的oss缩略图最大20M,在这里进行判断。之后通过后台接口设置签名,钩子返回Promise reject(false)则回调结束,

    generateTokens(key, file) {
      return new Promise((resolve, reject) => {
        getOssLiuParamsApi().then((res) => {
          file.startTime = new Date().getTime()
          this.uploadStartTimeList.push(file)

          this.tokens.key = res.dir + key
          this.tokens.policy = res.policy
          this.tokens.OSSAccessKeyId = res.accessid
          this.tokens.success_action_status = 200
          this.tokens.callback = res.callback
          this.tokens.signature = res.signature
          resolve(true)
        })
      }, () => {
        // eslint-disable-next-line no-undef
        reject(false)
      })
    },
    // 上传文件之前的钩子 {Object} file 文件信息
    beforeUpload(file) {
      const isBeyond = file.size < this.maxSize
      if (!isBeyond) {
        const beyondTip = '文件大小超过' + showSize(this.maxSize) + ',请重新上传。'
        this.$message.error(beyondTip)
        return isBeyond
      } else {
        const arr = file.name.split('.')
        const un_name = `${file.uid}.${arr[arr.length - 1]}`
        return this.generateTokens(un_name, file)
      }
    },

4.文件上传时钩子。

    handleProgress(event, file, fileList) {
      this.$store.dispatch('tool/toggleBeUploading', true)
      const currFile = this.uploadStartTimeList.filter(v => v.uid === file.uid)
      if (currFile && currFile[0]) {
        file.speed = (new Date().getTime() - currFile[0].startTime) / 1000
      }
      this.myValue = fileList
    },

5.文件上传成功钩子。设置图片路径,为避免前面设置上传进度的时候数据错乱,这里重新遍历设置,有其他更好的方法可以自行修改。

    handleUploadSuccess(res, file, fileList) {
      fileList.forEach(fileListItem => {
        const obj = this.setImg(file)
        const index = this.myValue.findIndex(item => item.uid === file.uid)
        this.myValue.splice(index, 1, obj)
      })
      console.log(this.myValue, 'myValue')
      this.$store.dispatch('tool/toggleBeUploading', false)
      this.$message({ type: 'success', message: '上传成功' })
    },

6.最后来说说截屏上传

原理:借助QQ、微信等截屏工具截屏,同时监听浏览器粘贴和鼠标移动事件, ctrl+v调用后台接口进行上传并且同步到upload组件

核心代码

mixins: [pasteUploadMixin],
    onMousemove(e) {
      const el = e.target
      const elWrappend = el.closest(`div#${this.id}`)
      const elCurrent = elWrappend.parentNode.parentNode.parentNode.firstElementChild
      if (elCurrent.getAttributeNode('for') && elCurrent.getAttributeNode('for').value) {
        this.pasteUploadKey = elCurrent.getAttributeNode('for').value
      } else {
        this.pasteUploadKey = ''
      }
    },
    // 初始化粘贴
    initPaste() {
      var handleAction = e => {
        var cbd = e.clipboardData
        var ua = window.navigator.userAgent
        // 如果是 Safari 直接 return
        if (!(e.clipboardData && e.clipboardData.items)) {
          return
        }

        // Mac平台下Chrome49版本以下 复制Finder中的文件的Bug Hack掉
        if (cbd.items && cbd.items.length === 2 && cbd.items[0].kind === 'string' && cbd.items[1].kind === 'file' && cbd.types && cbd.types.length === 2 && cbd.types[0] === 'text/plain' && cbd.types[1] === 'Files' && ua.match(/Macintosh/i) && Number(ua.match(/Chrome\/(\d{2})/i)[1]) < 49) {
          return
        }
        for (var i = 0; i < cbd.items.length; i++) {
          var item = cbd.items[i]
          if (item.kind === 'file') {
            var blob = item.getAsFile()
            if (blob.size === 0) {
              return
            }
            var reader = new FileReader()
            reader.readAsDataURL(blob)
            reader.onload = readerE => {
              this.submitUpload(readerE.target.result)
            }
          }
        }
      }

      this.newHandle = event => {
        handleAction(event)
      }

      document.getElementById(this.id).removeEventListener('paste', this.newHandle, false)
      document.getElementById(this.id).addEventListener('paste', this.newHandle, false)
    },

三、调用



完整代码

代码存放目录

BaseUpload
    mixin
        paste-upload.js
    index.vue

 对应的JS方法

import { uploadingPicture } from '@/utils/dict'
// 上传默认图片
export const uploadingPicture = {
  zip: 'http://xxxx.aliyuncs.com/1546836641143_4273.png',
  xls: 'http://xxxx.aliyuncs.com/1550486132435_7782.png',
  doc: 'http://xxxx.aliyuncs.com/1550486135954_2036.png',
  txt: 'http://xxxx.aliyuncs.com/1550486134063_8220.png',
  mp4: 'http://xxxx.aliyuncs.com/1550653591085_6150.png',
  other: 'http://xxxx.aliyuncs.com/1550653593890_4662.png'
}

import { showSize } from '@/utils'
/**
 * @description 将字节转化为MB GB T
 * @param {Number} size 字节
 * @return {string} data 转化后的大小
 */
export const showSize = size => {
  let data = ''
  if (size < 1048576) {
    data = Number((size / 1024).toFixed(2)) + 'KB'
  } else if (size === 1048576) {
    data = '1MB'
  } else if (size > 1048576 && size < 1073741824) {
    data = Number((size / (1024 * 1024)).toFixed(2)) + 'MB'
  } else if (size > 1048576 && size === 1073741824) {
    data = '1GB'
  } else if (size > 1073741824 && size < 1099511627776) {
    data = Number((size / (1024 * 1024 * 1024)).toFixed(2)) + 'GB'
  } else {
    data = '文件超过1TB'
  }
  return data
}

import { validURL } from '@/utils/validate'
/**
 * @param {string} url
 * @returns {Boolean}
 */
export function validURL(url) {
  const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
  return reg.test(url)
}

import { getToken } from '@/utils/auth'
export function getToken() {
  return Cookies.get(TokenKey)
}

import { pasteUploadMixin } from './mixin/paste-upload'

import { getOssLiuParamsApi } from '@/api/utils'
// 前端直传参数接口
export function getOssLiuParamsApi(data) {
  return request({
    url: 'order/ossLiu',
    method: 'post',
    data
  })
}

import defaultSettings from '@/settings'
module.exports = {
  OSS_URL: 'https://xxxx/', // oss下载地址(测试)
  BASE_API: 'https://xxxx/' // 测试环境
}


/**
 * @param {Array} str
 * @returns {Boolean}
 */
export function isEmpty(str) {
  if (str === '' || str === null || str === undefined || str.length === 0) {
    return true
  }
  return false
}

// 上传粘贴的图片到oss
export function uploadingUpFile(data) {
  return request({
    url: 'order/screenshotImg',
    method: 'post',
    data
  })
}





// paste-upload.js
import { uploadingUpFile } from '@/api/utils'
import { mapGetters } from 'vuex'
import { isEmpty } from '@/utils/validate'
export const pasteUploadMixin = {
  data() {
    return {
      newHandle: null,
      pasteUploadKey: ''
    }
  },
  mounted() {
    this.$nextTick(() => {
      this.initPaste()
    })
  },
  computed: {
    ...mapGetters(['pastPanel']),
    // 获取激活项的地址(默认粘贴板的地址)
    getBase64() {
      return function(base64) {
        let result = base64
        if (!isEmpty(this.pastPanel.active)) {
          const newResult = this.pastPanel.list.filter(v => v.id === this.pastPanel.active)
          result = newResult[0].src
        }
        return result
      }
    },
    // 返回粘贴限制key对应的type
    getPasteUploadType() {
      return function(key) {
        const keyMap = {
          model_img: 1,
          reference_img: 2,
          material_img: 3
        }
        return keyMap[key]
      }
    }
  },
  methods: {
    onMousemove(e) {
      const el = e.target
      const elWrappend = el.closest(`div#${this.id}`)
      const elCurrent = elWrappend.parentNode.parentNode.parentNode.firstElementChild
      if (elCurrent.getAttributeNode('for') && elCurrent.getAttributeNode('for').value) {
        this.pasteUploadKey = elCurrent.getAttributeNode('for').value
      } else {
        this.pasteUploadKey = ''
      }
    },
    // 初始化粘贴
    initPaste() {
      var handleAction = e => {
        var cbd = e.clipboardData
        var ua = window.navigator.userAgent
        // 如果是 Safari 直接 return
        if (!(e.clipboardData && e.clipboardData.items)) {
          return
        }

        // Mac平台下Chrome49版本以下 复制Finder中的文件的Bug Hack掉
        if (cbd.items && cbd.items.length === 2 && cbd.items[0].kind === 'string' && cbd.items[1].kind === 'file' && cbd.types && cbd.types.length === 2 && cbd.types[0] === 'text/plain' && cbd.types[1] === 'Files' && ua.match(/Macintosh/i) && Number(ua.match(/Chrome\/(\d{2})/i)[1]) < 49) {
          return
        }
        for (var i = 0; i < cbd.items.length; i++) {
          var item = cbd.items[i]
          if (item.kind === 'file') {
            var blob = item.getAsFile()
            if (blob.size === 0) {
              return
            }
            var reader = new FileReader()
            reader.readAsDataURL(blob)
            reader.onload = readerE => {
              this.submitUpload(readerE.target.result)
            }
          }
        }
      }

      this.newHandle = event => {
        handleAction(event)
      }

      document.getElementById(this.id).removeEventListener('paste', this.newHandle, false)
      document.getElementById(this.id).addEventListener('paste', this.newHandle, false)
    },
    submitUpload(base64) {
      const params = {
        type: this.getPasteUploadType(this.pasteUploadKey),
        data: this.getBase64(base64)
      }
      this.$message.info('正在上传中,勿刷新页面')
      this.$store.dispatch('tool/toggleBeUploading', true)
      uploadingUpFile(params)
        .then(res => {
          this.myValue.push({ url: res.data, name: new Date().getTime() + '.jpg' })
          this.$message({ type: 'success', message: '上传成功' })
          this.$store.dispatch('tool/toggleBeUploading', false)
        })
        .catch(() => {
          this.$message({ type: 'error', message: '上传失败' })
          this.$store.dispatch('tool/toggleBeUploading', false)
        })
    }
  },
  destroyed() {
    document.getElementById(this.id) && document.getElementById(this.id).removeEventListener('paste', this.newHandle, false)
  }
}

喜欢的给个哦,欢迎留言

你可能感兴趣的:(css,vue,upload)