element-ui提供了上传文件的方法,我们先看一下个方法示例:
// 图片列表缩略图 <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">点击上传</el-button> <div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过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>
复制
这个是官方示例,使用的组件上action进行图片上传,这个图片上传有一些缺点:
- 无法自定义上传方法,必须指定完整的图片上传路径
- 一般后台都有设置token,如果需要加token还需要加
headers
参数,自定义token
目前发现的是在这两个缺点,针对此,决定使用自定义上传事件,el-upload
组件也提供了相应的方法:
http-request
此方法可以覆盖组件上的action事件,自定义上传方法,下面是自定义方法代码实例:
此示例是从项目里抽出来的,只列举了需要用到的参数
<el-upload action="" :http-request="uploadSectionFile" :on-preview="handlePreview" :on-remove="handleRemove" list-type="picture" :before-remove="handleBeforeRemove" > <el-button size="small" type="primary">点击上传</el-button> </el-upload> // http-request 自定义上传事件 // on-preview 点击文件列表中已上传的文件时的钩子,图片预览需要用 // on-remove 文件列表移除文件时的钩子,图片删除时要用 // list-type 文件列表的类型:展示为图片 // before-remove 删除文件之前的钩子,参数为上传的文件和文件列表,若返回 false 或者返回 Promise 且被 reject,则停止删除。 <!-- 图片预览 --> <el-dialog title="图片预览" :visible.sync="previewVisible" width="50%"> <img :src="previewPath" alt="" style="width:100%;height:100%" /> </el-dialog>
复制
方法实现:
export default { data() { return { editForm: { pics: [], // 上传的图片临时路径(对象) }, previewPath: '', // 预览路径 previewVisible: false //预览弹框 fileList: [], replyQueryForm: { content: '', images: [], complaintId: '', }, imagesList: [], } }, methods: { //方法一: // 覆盖默认的上传行为,自定义图片上传请求 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('上传图片的大小不能超过 2MB!') return Promise.reject() } if (!isImage) { // 文件格式 this.$message.error('请选择图片文件!') 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. 拼接得到一个图片信息对象 const pic = res.data.tmp_path // 2. 将图片信息对象,push到pics数组中 this.editForm.pics.push({ pic }) //* 3. 返回数据可以在组件on事件的response中捕获,比如:on-remove return res.data // }, // 处理图片预览效果 handlePreview(file) { this.previewPath = file.response.url this.previewVisible = true }, // 处理移除图片的操作 handleRemove(file) { //* 1. 获取将要删除的图片的临时路径 const { tmp_path } = file.response //* 2. 从 pics 数组中,找到这个图片对应的索引值 const i = this.editForm.pics.findIndex(item => item.pic == tmp_path) //* 3. 调用数组的splice方法,把图片信息对象,从pics数组中移除 this.editForm.pics.splice(i, 1) }, //方法二: // 覆盖默认的上传行为,自定义图片上传请求 uploadSectionFile(params) { console.log(params); //* 1. 图片处理 const { file } = params const fileType = file.type //获取文件类型 console.log(fileType); const isImage = fileType.indexOf('image') != -1 // 判断是否是图片类型 const isLt5M = file.size / 1024 / 1024 < 5 if (!isLt5M) { // 判断大小 this.$message.error('上传图片的大小不能超过 5MB!') return Promise.reject() } if (!isImage) { // 文件格式 this.$message.error('请选择图片文件!') return Promise.reject() } //* 2. 图片上传 const fromData = new FormData() fromData.append('file', file) console.log(fromData); uploadComplaints(this.complaints.sourceType, fromData).then(res => { if (res.code == 200) { console.log(res) this.$message.success(res.msg || '上传成功') this.imagesList.push(res.data) console.log(this.imagesList); } }) }, // 处理图片预览效果 handlePreview(file) { console.log(file); this.previewPath = file.url this.previewVisible = true }, // 处理移除图片的操作 handleBeforeRemove(file, fileList) { console.log(file); console.log(fileList); const index = fileList.findIndex(item => item.name === file.name); console.log(index); this.imagesList.splice(index,1) console.log(this.imagesList); return true }, handleExceed(files, fileList) { this.$message.warning(`当前限制选择 4个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`); }, }
复制
注意:自定义的上传方法,如果想要继续使用组件上的一些钩子,需要return获取到的数据,这样才能在钩子上的file.response
中接收到。
其中用到的封装的axios和定义的接口
// 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, // 设置超时时间 headers: { 'Content-Type': 'application/json; charset=utf-8' } }) // http请求拦截器 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 状态码下的异常map const erorrMap = { 200: '请求成功', 201: '创建成功', 204: '删除成功', 400: '请求的地址不存在或者包含不支持的参数', 401: '未授权', 403: '被禁止访问', 404: '请求的资源不存在', 422: '[POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误', 500: '内部错误' } // http响应拦截器 service.interceptors.response.use( res => { //可以根据后端的系统而相应的做调整 let status = res.data.meta.status var statusArr = [200, 201, 204] // 如果不包含此状态就是失败 if (statusArr.includes(status)) { return res.data } else { if (erorrMap[status]) { //erorrMap[code] if (status == 400 && res.data.meta.msg == '无效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) { //超时 } } else if (error.response) { if (error.response.status === 400) { //请求参数有问题 Message.error(error) } else if (error.response.status === 404) { //未找到资源 } else if (error.response.status === 401) { //请先登录 } else if (error.response.status === 500) { //服务器异常 } } 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: 临时路径 * url: 图片地址 */ export const uploadImg = formData => { return awaitWrap( request({ url: '/upload', method: 'POST', data: formData, contentType: 'multipart/form-data' }) ) }
复制