element-ui提供了上傳文件的方法,我們先看一下個(gè)方法示例:
// 圖片列表縮略圖
<el-upload
class="upload-demo"
action="https://jsonplaceholder.typicode.com/posts/"
:on-preview="handlePreview"
:on-remove="handleRemove"
:file-list="fileList"
list-type="picture">
<el-button size="small" type="primary">點(diǎn)擊上傳</el-button>
<div slot="tip" class="el-upload__tip">只能上傳jpg/png文件,且不超過(guò)500kb</div>
</el-upload>
<script>
export default {
data() {
return {
fileList: [{name: 'food.jpeg', url: 'https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100'}, {name: 'food2.jpeg', url: 'https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100'}]
};
},
methods: {
handleRemove(file, fileList) {
console.log(file, fileList);
},
handlePreview(file) {
console.log(file);
}
}
}
</script>
這個(gè)是官方示例,使用的組件上action進(jìn)行圖片上傳,這個(gè)圖片上傳有一些缺點(diǎn):
- 無(wú)法自定義上傳方法,必須指定完整的圖片上傳路徑
- 一般后臺(tái)都有設(shè)置token,如果需要加token還需要加
headers參數(shù),自定義token
目前發(fā)現(xiàn)的是在這兩個(gè)缺點(diǎn),針對(duì)此,決定使用自定義上傳事件,el-upload組件也提供了相應(yīng)的方法:<br />

image.png
http-request此方法可以覆蓋組件上的action事件,自定義上傳方法,下面是自定義方法代碼實(shí)例:<br />此示例是從項(xiàng)目里抽出來(lái)的,只列舉了需要用到的參數(shù)
<el-upload
action=""
:http-request="uploadSectionFile"
:on-preview="handlePreview"
:on-remove="handleRemove"
list-type="picture"
>
<el-button size="small" type="primary">點(diǎn)擊上傳</el-button>
</el-upload>
// http-request 自定義上傳事件
// on-preview 點(diǎn)擊文件列表中已上傳的文件時(shí)的鉤子,圖片預(yù)覽需要用
// on-remove 文件列表移除文件時(shí)的鉤子,圖片刪除時(shí)要用
// list-type 文件列表的類型:展示為圖片
<!-- 圖片預(yù)覽 -->
<el-dialog title="圖片預(yù)覽" :visible.sync="previewVisible" width="50%">
<img :src="previewPath" alt="" style="width:100%;height:100%" />
</el-dialog>
方法實(shí)現(xiàn):
export default {
data() {
return {
editForm: {
pics: [], // 上傳的圖片臨時(shí)路徑(對(duì)象)
},
previewPath: '', // 預(yù)覽路徑
previewVisible: false //預(yù)覽彈框
}
},
methods: {
// 覆蓋默認(rèn)的上傳行為,自定義圖片上傳請(qǐng)求
async uploadSectionFile(params) {
//* 1. 圖片處理
const { file } = params
const fileType = file.type //獲取文件類型
const isImage = fileType.indexOf('image') != -1 // 判斷是否是圖片類型
const isLt2M = file.size / 1024 / 1024 < 2
if (!isLt2M) {
// 判斷大小
this.$message.error('上傳圖片的大小不能超過(guò) 2MB!')
return Promise.reject()
}
if (!isImage) {
// 文件格式
this.$message.error('請(qǐng)選擇圖片文件!')
return Promise.reject()
}
//* 2. 圖片上傳
const fromData = new FormData()
fromData.append('file', file)
const [err, res] = await uploadImg(fromData)
if (err) {
console.log(err)
return this.$message.error(err.meta.msg || '上傳失敗')
}
// console.log(res)
this.$message.success(res.meta.msg || '上傳成功')
// 1. 拼接得到一個(gè)圖片信息對(duì)象
const pic = res.data.tmp_path
// 2. 將圖片信息對(duì)象,push到pics數(shù)組中
this.editForm.pics.push({ pic })
//* 3. 返回?cái)?shù)據(jù)可以在組件on事件的response中捕獲,比如:on-remove
return res.data //
},
// 處理圖片預(yù)覽效果
handlePreview(file) {
this.previewPath = file.response.url
this.previewVisible = true
},
// 處理移除圖片的操作
handleRemove(file) {
//* 1. 獲取將要?jiǎng)h除的圖片的臨時(shí)路徑
const { tmp_path } = file.response
//* 2. 從 pics 數(shù)組中,找到這個(gè)圖片對(duì)應(yīng)的索引值
const i = this.editForm.pics.findIndex(item => item.pic == tmp_path)
//* 3. 調(diào)用數(shù)組的splice方法,把圖片信息對(duì)象,從pics數(shù)組中移除
this.editForm.pics.splice(i, 1)
}
}
注意:自定義的上傳方法,如果想要繼續(xù)使用組件上的一些鉤子,需要return獲取到的數(shù)據(jù),這樣才能在鉤子上的file.response中接收到。<br />其中用到的封裝的axios和定義的接口<br />
// axios
import axios from 'axios'
import { storage, sessionStorage } from '@/utils/storage'
import { Message } from 'element-ui'
import router from '../router/index'
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_URL,
timeout: 5000, // 設(shè)置超時(shí)時(shí)間
headers: { 'Content-Type': 'application/json; charset=utf-8' }
})
// http請(qǐng)求攔截器
service.interceptors.request.use(
config => {
config.headers['Authorization'] = sessionStorage.get('token')
config.contentType && (config.headers['Content-Type'] = config.contentType)
return config
},
error => {
return Promise.reject(error)
}
)
//http 200 狀態(tài)碼下的異常map
const erorrMap = {
200: '請(qǐng)求成功',
201: '創(chuàng)建成功',
204: '刪除成功',
400: '請(qǐng)求的地址不存在或者包含不支持的參數(shù)',
401: '未授權(quán)',
403: '被禁止訪問(wèn)',
404: '請(qǐng)求的資源不存在',
422: '[POST/PUT/PATCH] 當(dāng)創(chuàng)建一個(gè)對(duì)象時(shí),發(fā)生一個(gè)驗(yàn)證錯(cuò)誤',
500: '內(nèi)部錯(cuò)誤'
}
// http響應(yīng)攔截器
service.interceptors.response.use(
res => {
//可以根據(jù)后端的系統(tǒng)而相應(yīng)的做調(diào)整
let status = res.data.meta.status
var statusArr = [200, 201, 204]
// 如果不包含此狀態(tài)就是失敗
if (statusArr.includes(status)) {
return res.data
} else {
if (erorrMap[status]) {
//erorrMap[code]
if (status == 400 && res.data.meta.msg == '無(wú)效token') {
// token失效
Message.error(res.data.meta.msg)
sessionStorage.remove('token')
router.replace('/login')
}
return Promise.reject(res.data)
}
}
return res.data
},
async error => {
if (error.request) {
if (error.request.status === 0) {
//超時(shí)
}
} else if (error.response) {
if (error.response.status === 400) {
//請(qǐng)求參數(shù)有問(wèn)題
Message.error(error)
} else if (error.response.status === 404) {
//未找到資源
} else if (error.response.status === 401) {
//請(qǐng)先登錄
} else if (error.response.status === 500) {
//服務(wù)器異常
}
}
return Promise.reject(error)
}
)
export default service
// 接口
import request from '@/utils/http'
/**
* 處理await成功失敗信息
* @param {*} promise
*/
const awaitWrap = (promise) => {
return promise
.then(data => [null, data])
.catch(err => [err, null])
}
/**
* 圖片上傳
* @param {*} file 上傳的文件
* @param {contentType} 'form-data'
* @returns
* tmp_path: 臨時(shí)路徑
* url: 圖片地址
*/
export const uploadImg = formData => {
return awaitWrap(
request({
url: '/upload',
method: 'POST',
data: formData,
contentType: 'multipart/form-data'
})
)
}