封装el-Upload
- 目前在编项目中,页面有用到el-Upload,所以对el-Upload做了二次封装, 组件在个人开发使用不错,但不确定能满足各种业务需求,所以这里主要和大家分享一下设计思路。用一次爽一次,越用越爽。
分析问题:
- el-Upload是element-ui库的表单组件,如果我们要将其进行二次封装,那么需要考虑几个问题:
- 上传的样式类型如(如:头像,照片墙 ,缩略图,列表缩略图,文件列表,拖拽上传,自定义等),
- 手动上传,自动上传
- 上传大小及类型校验
- 等等
组件内部template:
- 详情及组件使用方法:( 请查看《upload上传API》)。
- 这里特别注意(使用icon模式的时候,提前把icon图标封装进去,否则显示不出来)
<template>
<div>
<el-upload
ref="upload"
:drag="drag"
class="avatar-uploader"
:action="action"
:auto-upload="autoUpload"
:list-type="listType"
:show-file-list="showFileList"
:file-list="fileList"
:limit="limit"
:data="data"
:before-upload="beforeUploadFn"
:on-success="successFn"
:on-error="errorFn"
:on-remove="removeFn"
:on-preview="handlePictureCardPreview"
:on-exceed="exceedFn"
:on-change="handleAvatarSuccess">
<div v-if="listType === 'head' && !drag" v-loading="loading"
element-loading-text="上传中...">
<template >
<div slot="trigger">
<slot name="triggers">
<img v-if="imageUrl" :src="imageUrl" class="avatar" :style="styleOptions">
<i v-else class="el-icon-plus avatar-uploader-icon" :style="styleOptions"></i>
</slot>
</div>
</template>
</div>
<div v-if="listType === 'picture-card' && !drag">
<template >
<div slot="trigger">
<slot name="triggers">
<i class="el-icon-plus"></i>
</slot>
</div>
</template>
</div>
<div v-if="listType === 'text' && !drag || listType === 'picture' && !drag">
<template >
<div slot="trigger">
<slot name="triggers">
<el-button size="small" type="primary">点击上传</el-button>
</slot>
</div>
</template>
</div>
<div v-if="drag" style="width: 150px;height: 150px;line-height: 150px;overflow: hidden">
<template >
<div slot="trigger">
<slot name="triggers">
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
</slot>
</div>
</template>
</div>
<p v-if="tip" slot="tip" class="el-upload__tip">{{tip}}</p>
</el-upload>
<el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="dialogImageUrl" alt="">
</el-dialog>
</div>
</template>
<script>
export default {
name: 'wb-upload',
props: {
// 上传框样式控制
styleOptions: {
type: Object,
default: {
width: '100px',
height: '100px',
lineHeight: '100px',
}
},
// 是否开启icon图标模式(仅限picture模式使用)
isIcon: {
type:Boolean,
default:false
},
// 上传时附带的额外参数
data: {
type:Object
},
// 是否使用拖拽
drag: {
type: Boolean,
default: false
},
// 上传的类型
fileType: {
type: Array
},
// 要展示的列表数据
fileList: {
type: Array
},
// 提示语
tip: {
type: String
},
// 上传的大小
fileSize: {
type: Number
},
// 文件列表的类型
listType: {
type: String
},
// 是否显示已上传文件列表
showFileList: {
type:Boolean,
default: false
},
// 最大允许上传个数
limit: {
type: Number
},
// 上传地址
action: {
type: String
},
// 是否自动上传
autoUpload: {
type: Boolean,
default: false
}
},
data() {
return {
// 要显示的图片地址
imageUrl: '',
// 加载
loading: false,
// 查看图片
dialogVisible: false,
dialogImageUrl:''
};
},
methods: {
/**
* 统一处理上传文件类型,大小校验
* */
typeFn (file,fileList) {
//上传文件的后缀名称
const extension = file.name.split('.')[1];
// console.log(extension);
// 判断是否开启icon模式
if(this.isIcon){
// 仅限picture 模式使用
if(this.listType === 'picture'){
if(extension === 'txt'){
file.url = './src/static/img/txt.png'
}else if(extension === 'png' || extension === 'jpg' ){
file.url = './src/static/img/jpg.png'
}else if(extension === 'xls' || extension === 'xlsx'){
file.url = './src/static/img/xls.png'
}else if(extension === 'pdf'){
file.url = './src/static/img/pdf.png'
}else if(extension === 'docx' || extension === 'doc'){
file.url = './src/static/img/docx.png'
}
}
}
// 要上传的类型
let TypeArr = this.fileType;
let isType = TypeArr.some(item=>{
return item === extension
});
const isSize = file.size / 1024 < this.fileSize;
if (!isType) {
this.loading = false;
let str = TypeArr.join();
this.$message.error(`上传类型只能是${str}格式!`);
// 清除当前上传数据
if(fileList){
fileList.splice(fileList.length - 1,1);
}
return false
}
if (!isSize) {
this.loading = false;
this.$message.error(`上传图片大小不能超过 ${this.fileSize}KB!`);
// 清除当前上传数据
if(fileList){
fileList.splice(fileList.length - 1,1);
}
return false
}
return true
},
// 点击上传列表回调(点击查看图片)
handlePictureCardPreview(file) {
// console.log(file)
if(this.listType === 'picture' || this.listType === 'picture-card'){
this.dialogImageUrl = file.url;
this.dialogVisible = true;
}
},
/**
* 手动上传发生改变的时候触发事件
* */
handleAvatarSuccess(file, fileList) {
// console.log(file)
// 自动上传的时候不走这里
if(this.autoUpload){
return false
}
// console.log('手动')
this.loading = true;
// 上传校验
if(this.typeFn(file,fileList)){
// 反给父组件数据
let res = {
file: file.raw,
fileList
};
this.$emit('success',res);
// 根据不同的类型,赋值
if(this.listType === 'head'){
// 把文件流转换为blob展示出来
this.imageUrl = URL.createObjectURL(file.raw);
// console.log(this.imageUrl)
// 清除上传数据
this.$refs['upload'].clearFiles();
}
this.loading = false;
}else {
return false
}
},
/**
* 自动上传上传文件之前的钩子
* */
beforeUploadFn (file, fileList) {
// 开始加载
this.loading = true;
// 判断显示列表是否为true
if(this.listType === 'head'){
return this.typeFn(file,fileList)
}else if(this.showFileList){
// 上传校验
// console.log('自动');
return this.typeFn(file,fileList)
}else {
console.error('showFileList必须为:true')
return false
}
},
/**
* 文件上传失败时的钩子
* */
errorFn (error, file, fileList) {
this.loading = false;
// 返给父组件数据
let err = {
error,
file,
fileList
};
this.$emit('error',err);
},
/**
* 文件上传成功时的钩子
* */
successFn (response,file, fileList) {
this.loading = false;
// 根据不同的类型,赋值
if(this.listType === 'head'){
// 把文件流转换为blob展示出来
this.imageUrl = URL.createObjectURL(file.raw);
}
// 返给父组件数据
let res = {
response,
file,
fileList
};
if(this.listType === 'head'){
this.$emit('success',res);
// 清除上传数据
this.$refs['upload'].clearFiles();
}else {
this.$emit('success',res);
}
},
/**
* 文件列表移除文件时的钩子
* */
removeFn (file, fileList) {
// 返给父组件数据
let res = {
file,
fileList
};
this.$emit('remove',res);
},
/**
* 文件超出个数限制时的钩子
* */
exceedFn (files, fileList) {
this.$message.error(`上传文件不能超出${this.limit}个`)
}
},
mounted() {
}
}
</script>
<style scoped>
.avatar-uploader >>>.el-upload {
cursor: pointer;
position: relative;
}
.avatar-uploader >>>.el-upload:hover {
border-color: #409EFF;
}
.avatar-uploader ::v-deep.el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader ::v-deep.el-upload:hover {
border-color: #409EFF;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 120px;
height: 120px;
line-height: 120px;
text-align: center;
border: 1px dashed #d9d9d9;
border-radius: 6px;
}
.avatar {
width: 120px;
height: 120px;
display: block;
}
>>>.el-upload-list--picture-card .el-upload-list__item-actions span{
font-size:inherit;
}
</style>