本來昨天就想分享封裝的這個upload組件,結果剛寫了兩句話,就被邊上的同事給偷窺上了,於是在我全神貫注地寫分享的時候,他就神不知鬼不覺地突然移動到我身邊,腆着臉問我在干啥呢。卧槽你媽,當場就把我嚇了一跳,我說我沒干嘛,在工作啊,然后我就把瀏覽器窗口切換了,他就來了一句:我看到了博客。我沒搭理他,但心里就不停地在罵“槽你媽”,我確實很生氣。其實我一直很反感那種坐在你邊上有事沒事就斜眼瞄你的電腦屏幕的同事,那種上班期間被偷窺的感覺很不爽。我這么說不是我特意去注意旁邊的同事有沒有在斜眼偷瞄我的電腦屏幕,因為我忙起來,我TM連我邊上有沒有人都不會有感覺的,之所以會經常發現被旁邊的同事偷瞄,是因為工作時間久了,我的頸椎會很疼,頭也疼,腰也很難受,所以我就會抬頭仰望天花板或“葛優躺”靠在椅背上,這時我的眼睛余光就能很明確地感覺到我的電腦屏幕正在被旁邊的同事偷瞄,我又不是那種直接回望過去:你TM的偷瞄什么呢?卧槽TM的,我就一直很奇怪,為什么我會經常跟那種喜歡偷瞄別人工作的人坐在一起,我也一直很奇怪,那種喜歡偷瞄別人工作或偷瞄別人電腦屏幕的人都是什么心理。但凡我發現我的電腦屏幕或我正在工作時被別人偷瞄,我就很不爽,渾身不自在,不知道大家有沒有碰到這樣的同事。
一直都很忙,寫分享基本都是在中午午休時趕出來的,然后趁着在工作中頸椎疼的難以忍受時稍微休息一下的過程中排版發出來的。不過我也發現一個現象,就是我分享的有關封裝的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',
word: 'application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document',
ppt: 'application/vnd.ms-powerpoint,application/vnd.openxmlformats-officedocument.presentationml.presentation',
},
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, regExp = /image\/[a-z]{2,4}$/i;
name = `${uid}${name.replace(SUFFIX, '$1')}`;
file.isImg = regExp.test(type) ? true : false;
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:剛才發現我邊上那個同事在用挖耳勺掏耳朵的時候還在偷瞄的電腦屏幕,冊那!