1. 程式人生 > 實用技巧 >封裝Vue Element的upload上傳元件

封裝Vue Element的upload上傳元件

本來昨天就想分享封裝的這個upload元件,結果剛寫了兩句話,就被邊上的同事給偷窺上了

一直都很忙,寫分享基本都是在中午午休時趕出來的,然後趁著在工作中頸椎疼的難以忍受時稍微休息一下的過程中排版發出來的。不過我也發現一個現象,就是我分享的有關封裝的react方面的元件的關注度沒有封裝的vue方面的元件的關注度來得高,有可能是百度對我分享的vue方面的元件做了收錄,所以大家也能在百度上搜索到,而我分享的react方面的元件卻基本沒有被百度收錄的原因,或許也有可能是用vue的人多過用react的人,但我無意去對比兩者,大家開心就好。

今天寫的這個upload元件也是採用的函式式元件,然後結合著Element ui的上傳元件來封裝,接下來就看具體實現吧。

還是先來一張效果圖:

1、封裝的上傳元件Upload.js

import { Message } from 'element-ui'
const OSS = require("ali-oss");

const SUFFIX = /.+(\.\w+)$/,
  TIMEOUT = 60000,
  BYTE = 1024,
  RETRY_COUNT_MAX = 3,
  ACCEPT = {
    zip: 'application/zip,application/x-zip,application/x-zip-compressed',
    pdf: 'application/pdf',
    excel: 'application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    image: 'image/jpeg,image/bmp,image/png,image/gif',
  },
  getAccepts = accept => (Array.isArray(accept) ? accept : [accept]).map(ac => ACCEPT[ac]).join(','),
  beforeCheck = (config, file) => {
    let {max = Number.MAX_VALUE, accept = []} = config || {}, { size, type } = file, accepts = getAccepts(accept).split(',');
    
    //大小限制(M)
    if(Math.pow(BYTE, 2) * max < size){
        Message.warning(`檔案不能超過${max}M`);
        return false;
    }
  };

export default {
  functional: true,
  props: {config: Object, limit: Number, percentage: Function},
  render: (h, ctx) => {
    let {props, data: attrs, parent: {$store: { dispatch}}, children} = ctx, { config, limit } = props, retryCount = 0, { accept } = config;
    if(!attrs.attrs.accept && accept){
      attrs.attrs.accept = getAccepts(accept);
    }

    Object.assign(props, {
      action: '',
      beforeUpload: file => beforeCheck(config, file),
      onExceed: () => Message.warning(`最多可以上傳${limit}個檔案`),
      httpRequest: opts => {
        let { file, onError, onProgress, onSuccess } = opts, { uid, name, type } = file;
        name = `${uid}${name.replace(SUFFIX, '$1')}`;
        if(getAccepts(accept).indexOf(type) > -1) file.isImg = true

        dispatch('getClient').then(res => {
          let aliClient = new OSS(res), { path } = res, url = path + '/' + name;
          //multipartUpload是阿里雲的分片(分段)大檔案上傳方法,這裡也可以用put方法來上傳單個小檔案(aliClient.put)
          aliClient.multipartUpload(url, file, {
            timeout: TIMEOUT,
            partSize: 500 * BYTE,
            progress: p => {
              onProgress(p * 100, file)
            }
          }).then(r => {
            let { res: { requestUrls } } = r;
            requestUrls = requestUrls.length < 1 ? '' : requestUrls[0]
            if (requestUrls.indexOf('?') > -1) requestUrls = requestUrls.split('?')[0]
            onSuccess({ res: file, url: requestUrls });
          }).catch(err => {
            let { name } = err;
            if (aliClient.isCancel()) {
              console.log('stop-upload!');
            } else {
              Message.error(err);
              onError(err);
              if (name.toLowerCase().indexOf('connectiontimeout') !== -1) {
                if (retryCount < RETRY_COUNT_MAX) {
                  retryCount++;
                  props.httpRequest(opt)
                }
              }
            }
          });;
        })
      }
    })
    return h('el-upload', {props, ...attrs}, children)
  }
}

本次封裝的upload上傳元件的封裝方式和實現的功能與之前我所分享的封裝React AntD的upload上傳元件類似,你可以移步到那篇文章去詳細瞭解,這裡不再贅述。

另外,我們公司所上傳的檔案都是上傳到了阿里雲,所以這裡順帶著將上傳到阿里雲的方法也給粗略地實現了。上傳到阿里雲所用到的API請自行查閱,本次分享也不再贅述。

2、使用方法:

<template>
  <div class="container">
    <p>第一種上傳形式:</p>
    <Upload :limit="limit" :on-success="onSuccess" :config="config" :on-progress="onProgress" :on-preview="onPreview" :on-remove="onRemove" list-type="picture-card">
      <i class="el-icon-plus"></i>
    </Upload>
    <br />
    <p>第二種上傳形式:</p>
    <Upload :limit="limit" :on-success="onSuccess" :config="config" :on-progress="onProgress" :on-preview="onPreview" :on-remove="onRemove">
      <el-button size="small" type="primary">點選上傳</el-button>
    </Upload>
  </div>
</template>

<script>
import Upload from '@/components/Upload'

export default {
  components: {
    Upload,
  },
  data() {
    return {
      limit: 1,
      config: {
        accept: "image", //接受上傳的檔案型別:zip、pdf、excel、image,也可以是檔案型別所組成的陣列型別如:['image', 'pdf'],則只可以上傳圖片或pdf型別的檔案,也可以為空,則任何型別的檔案都可以上傳
        max: 100, //檔案大小
      },
      fileList: [],
    }
  },
  methods: {
    //檔案上傳完成後的回撥
    onSuccess({res: {uid, isImg}, url}) {
      this.fileList.push({uid, url})
    },
    //檔案上傳進度條
    onProgress(percent, file){

    },
    //上傳檔案
    onRemove(file){
      this.fileList = this.fileList.filter(n => n.uid != file.uid)
    },
  },
}
</script>

以前去面試的時候,經常會有面試官問我在開發的過程中有沒有自己封裝過元件,我說封裝過,然後面試官就讓我舉例,那時候我的回答一般都不是基於函式式的封裝,都還是帶有狀態和生命週期函式的那種封裝,但面試官也沒說啥。後來接觸到函式式元件,才覺得這玩意兒是真好使。

PS:剛才發現我邊上那個同事在用挖耳勺掏耳朵的時候還在偷瞄的電腦螢幕,冊那!