背景:element-ui:2.15.13,平时写el-upload需要每次都写success、beforeupload、remove等函数很繁琐,而且遇到循环表单上传还要处理非常麻烦,所以我把上传组件进行封装,支持直接v-model双向绑定,不需要在考虑上传逻辑
一.新建上传组件
新建aLiYunUpload.vue文件,直接cv使用,需要注意的地方我都有注释
<template>
<div class="zzh_aLiYunUpload">
<el-upload
style="width: 100%;"
ref="upload"
class="zzh_aLiYunUpload"
:http-request="handleUpload"
action
:limit="limit"
:multiple="true"
:file-list="fileList"
:before-upload="onBeforeUpload.bind(null)"
:on-success="onFileSuccess.bind(null)"
:on-remove="onRemove.bind(null)"
:on-preview="handlePreview.bind(null)"
:on-exceed="onExceed.bind(null)"
size="small"
:list-type="listType"
:drag="listType == 'drag'"
:disabled="disabled"
:class="{ disabled: fileList.length >= limit, hideDefaultList: hideDefaultList }"
>
<slot name="button">
<el-button size="small" type="primary" v-if="listType == ''">
<i class="el-icon-upload2"></i>
点击上传
</el-button>
<i class="el-icon-plus" v-if="listType == 'picture-card'"></i>
<i v-if="listType == 'drag'" class="el-icon-upload"></i>
<div v-if="listType == 'drag'" class="el-upload__text">
将文件拖到此处,或
<em>点击上传</em>
</div>
</slot>
</el-upload>
<!-- <aLiYunUpload style="width: 90%;" v-model="ruleForm.files" :limit="20" fileType="file"></aLiYunUpload> -->
</div>
</template>
<script>
import upload from '@/utils/fileUpload.js'; //上传的函数或者接口,因为我这里走的前端oss,如果调后端接口上传同理
export default {
name: 'zzh_aLiYunUpload',
props: {
value: {
type: Array,
default: () => [],
},
//文件最大值 (单位M)
maxSize: {
type: Number,
default: 100,
},
//最多上传几个文件
limit: {
type: Number,
default: 1,
},
//同el-upload listType属性
listType: {
type: String,
default: '', //显示 drag:拖拽上传 picture-card:图片按钮 默认是文字上传按钮
},
props: {
type: Object,
default: () => {
return {
url: 'url', //自定义url
name: 'name', //自定义name
key: 'Id', //假如后端需要保存Id之类的 配一下key
};
},
},
disabled: {
type: Boolean,
default: false,
},
//自定义支持的校验类型
supportType: {
type: Array,
default: () => [],
},
//隐藏默认list,方便自定义
hideDefaultList: {
type: Boolean,
default: false,
},
},
data() {
return {
fileList: [], //文件
};
},
methods: {
handlePreview(file) {
let fileType = file.name
.split('.')
.pop()
.toLowerCase();
if (['txt'].includes(fileType)) {
const req = new XMLHttpRequest();
req.open('get', file.url, true);
req.responseType = 'blob';
req.setRequestHeader('Content-Type', 'application/json');
req.onload = function() {
// 这里因为没有对返回的数据处理编码格式,所以在预览txt时需要转换成utf-8
const data = new Blob([req.response], { type: 'application/json;charset=utf-8' });
let blobUrl = window.URL.createObjectURL(data);
window.open(blobUrl);
};
req.send();
return;
} else if (['zip', 'rar', 'ai', 'psd'].includes(fileType)) {
return this.download(file);
} else if (['jpg', 'jpeg', 'gif', 'png', 'tif', 'bmp'].includes(fileType)) {
return window.open(file.url);
} else if (['pdf', 'PDF'].includes(fileType)) {
return window.open(file.url);
} else if (['doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx'].includes(fileType)) {
return window.open(`https://view.officeapps.live.com/op/embed.aspx?src=${file.url}&wdAccPdf=1&wdDownloadButton=True'`);
} else {
return window.open(file.url);
}
},
download(file) {
// file = file.response;
var x = new XMLHttpRequest();
x.open('GET', file.url, true);
x.responseType = 'blob';
x.onload = function(e) {
var url = window.URL.createObjectURL(x.response);
const link = document.createElement('a');
link.target = '_blank';
link.href = url;
link.download = file.name;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
x.send();
},
onBeforeUpload(file) {
if (file.size / 1024 / 1024 > this.maxSize) {
this.$message.warning(`文件大小不超过${this.maxSize}M`);
return false;
}
let typeList;
// if (this.fileType == 'image') {
// typeList = ['jpg', 'jpeg', 'gif', 'bmp', 'png', 'tif'];
// if (
// !typeList.includes(
// file.name
// .split('.')
// .pop()
// .toLowerCase()
// )
// ) {
// this.$message.warning(`请检查上传的类型是否为${typeList.toString()}`);
// return false;
// }
// }
// else {
// typeList = ['jpg', 'jpeg', 'gif', 'bmp', 'png', 'tif', 'doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx', 'pdf', 'zip', 'rar', 'txt', 'ai', 'psd'];
// }
if (this.supportType && this.supportType.length) {
typeList = this.supportType;
if (
!typeList.includes(
file.name
.split('.')
.pop()
.toLowerCase()
)
) {
this.$message.warning(`请检查上传的类型是否为${typeList.toString()}`);
return false;
}
}
},
// 自定义上传操作
handleUpload(op) {
// this.uploadDisabled = true;
//************只需要修改这里的上传函数就行了 */
upload(
op.file,
res => {
//*******拿到接口返回的值,这里返回格式根据自己项目调整,然后拼接一个name和url的对象调用一下op.onSuccess */
let temp = {
name: res.attachment,
url: res.host + '/' + res.aliyunAddress,
};
this.host = res.host;
op.onSuccess(temp);
},
err => {
console.log(err);
// this.uploadDisabled = false;
},
res => {
this.uploadDisabled = false;
}
);
},
onFileSuccess(response, file, fileList) {
// console.log(fileList);
const statusColumn = fileList.filter(val => val && val.status !== 'success');
if (!statusColumn.length) {
this.fileList = fileList.map(item => {
return {
name: item.name,
url: item.response ? item.response.url : item.url,
[this.props.key]: item[this.props.key],
};
});
console.log('🚀this.fileList |', JSON.parse(JSON.stringify(this.fileList)));
this.$emit('input', this.getList(this.fileList));
this.$emit('change', this.getList(this.fileList));
}
},
onRemove(file, fileList) {
this.fileList.forEach((v, i) => {
if (v.url === file.url) {
this.fileList.splice(i, 1);
this.$emit('input', this.getList(this.fileList));
this.$emit('change', this.getList(this.fileList));
}
});
},
//超出文件上传数量
onExceed() {
this.$message.warning(`最多只能上传${this.limit}个文件`);
return false;
},
//#endregion
getList(arr) {
// 处理返回的文件格式
return arr.map(val => {
return {
[this.props.url]: val.url,
[this.props.name]: val.name,
[this.props.key]: val[this.props.key],
};
});
},
},
created() {},
mounted() {},
watch: {
value: {
handler(newVal) {
this.fileList = this.value.map(item => {
return {
url: item[this.props.url],
name: item[this.props.name],
[this.props.key]: item[this.props.key],
};
});
},
deep: true,
immediate: true,
},
},
};
</script>
<style lang="less">
.zzh_aLiYunUpload {
.disabled .el-upload--picture-card {
display: none !important;
}
.disabled .el-upload-- {
display: none !important;
}
.el-upload-list__item.is-ready {
display: none;
}
.el-upload-list__item {
transition: none !important;
-webkit-transition: none !important;
}
.el-upload-list__item-name {
transition: none !important;
-webkit-transition: none !important;
}
.hideDefaultList {
.el-upload-list,
.el-upload__tip {
display: none !important;
}
}
}
</style>
只需要关注一下自定义上传,调函数或者接口,然后拿到返回值调用一下op.onSuccess就行
二、使用组件
import 然后compoment注册我就不说了,如果项目用的地方多的话,建议直接全局注册,比较方便
1.常规使用
<aLiYunUpload style="width: 90%;" v-model="ruleForm.files" :limit="20" @input="input" @change="change"></aLiYunUpload>
直接v-model双向绑定就行,默认是[{url:xxx,name:xxx}]格式,另外内置了input和change函数根据需要使用
2.和默认el-upload或者这样相同样式,只需要传一个listType,同el-upload的listType,配置可以设置单个文件的最大值maxSize,上传个数limit单个就传1,多个就传数字,包括数组绑定的类型,如接口想要这个格式[{fileUrl:xxx,fileName}],都相应配置就行,可以在props自行参考扩展
<aLiYunUpload style="width: 90%;" v-model="ruleForm.files" :limit="20" :maxSize="1024" listType="picture-card" :props="{ url: 'fileUrl', name: 'fileName' }" ></aLiYunUpload>
3.完全自定义样式和上传按钮,这里的supportType默认不传不限制类型,传数组则只能上传指定类型
上传按钮可以传一个插槽slot=button自定义,传一个 hideDefaultList=true即可隐藏默认的list列表 自己根据需要去循环写样式,然后绑定函数可参考我的方式,就不需要重复写相应逻辑