二次封装el-upload组件,以便使用。
包含两个部分:创建、引用。
说明:
1. 包含:组件本身有的各种属性控制、是否拖拽、扩展按钮、上传前的各种校验(图片的话需要压缩,压缩方法在之前的文章有,这里不再重复写)、预览附件(这里支持pdf和图片预览,支持Excel下载,word没写,原理应该和Excel一样,其余的暂不支持)等。
2. 各个属性灵活运用,也可自己添加、更改相关属性配置。
3. 这里设置从父组件中传输过来的对象和数组都用JSON串接收,目的是为了方便监听数值变化。
4. 代码为手写,已检查,但不保证没有单词写错,如报错,请检查一下单词的拼写。
<template>
<div>
<el-upload
ref="comUpload"
:class="{ 'is-drag': drag }"
:drag="drag"
:accept="accept"
:action="action"
:multiple="multiple"
:disabled="disabled"
:limit="limit"
:file-list="fileList"
:on-preview="onPreview"
:on-remove="onRemove"
:on-success="onSuccess"
:before-upload="beforeUpload"
:before-remove="beforeRemove"
:on-exceed="onExceed"
:on-progress="onProgress"
:on-error="onError"
:data="data"
>
<i v-if="drag" class="el-icon-upload"></i>
<div v-if="drag" class="el-upload_text">
将文件拖到此处,或<em>点击上传</em>
</div>
<el-button v-if="!drag" slot="trigger" size="small" type="primary">点击上传</el-button>
<!-- 扩展按钮 -->
<template v-if="buttonList">
<el-button
v-for="itemB in buttonList"
:key="itemB.id"
style="margin-left: 10px;"
size="small"
:type="itemB.type"
@click="buttonClick(itemB)"
>{{ itemB.label }}</el-button>
</template>
<!-- 提示语 -->
<div slot="tip" class="el-upload_tip">
<span v-if="acceptText">只能上传{{ acceptText }}文件</span>
<span v-if="acceptText && limit">,</span>
<span v-if="limit">最多上传{{ limit }}个</span>
<span v-if="(limit && size) || (acceptText && size)">,</span>
<span v-if="size">每个文件不超过{{ size }}MB</span>
<p v-if="tips" class="el-upload__tip_tips">{{ tips }}</p>
</div>
</el-upload>
<!-- pdf预览组件,前篇文章已有介绍 -->
<com-pdf-show ref="comPdfShowRef"></com-pdf-show>
<!-- 图片预览组件,由el-dialog全屏展示,img标签展示,没什么技术含量,这里就不写了 -->
<com-img-show ref="comImgShowRef"></com-img-show>
</div>
</template>
<script>
import comImgShow from './comImgShow';
import comPdfShow from './comPdfShow';
export default {
name: 'ComUpload',
components: { comImgShow, comPdfShow },
props: {
multiple: { // 是否可以上传多个,默认true,非必传
type: Boolean,
default: true,
},
drag: { // 是否可拖拽,默认false,非必传
type: Boolean,
default: false,
},
accept: { // 上传文件的格式限制,默认不限制,非必传
type: String,
default: '',
},
accepts: { // 当drag为true时,accept设置的话拖拽无效,故加此属性限制文件格式,在上传前校验;默认不限制,非必传
type: String,
default: '',
},
acceptText: String, // 上传文件格式限制文字说明,放入提示语中用来展示;默认空,非必传。
action: { // 上传文件地址,默认'/upload';非必传
type: String,
default: '/upload',
},
disabled: { // 是否禁用,默认false;非必传
type: Boolean,
default: false,
},
limit: Number, // 最大允许上传个数,默认不限制,非必传
size: Number, // 最大允许上传文件大小,单位为MB,默认不限制,非必传
initFiles: String, // 初始化附件数据数组JSON串,默认空,非必传
paramsData: String, // 上传文件时附带的额外参数对象JSON串,默认空,非必传
buttons: String, // 额外按钮数组JSON串,默认空,非必传
tips: String, // 额外需添加的提示语,默认空,非必传
},
data() {
return {
fileList: [],
data: {},
buttonList: [],
};
},
watch: {
initFiles() {
if (this.initFiles) this.fileList = JSON.parse(this.initFiles)
},
buttons() {
this.buttonList = this.buttons ? JSON.parse(this.buttons) : [];
},
paramsData() {
this.data = this.paramsData ? JSON.parse(this.paramsData) : {}
},
},
created() {
this.data = this.paramsData ? JSON.parse(this.paramsData) : {}
if (this.initFiles) this.fileList = JSON.parse(this.initFiles);
this.buttonList = this.buttons ? JSON.parse(this.buttons) : [];
},
methods: {
/**
* 点击额外按钮触发
* @buttonInfo {Object} 按钮信息
**/
buttonClick(buttonInfo) {
this.$emit('buttonClick', buttonInfo);
},
/**
* 预览文件,暂时只支持预览图片和pdf,预览Excel是以下载的方式,word没有写,应该和Excel原理一样,其他不支持
* @file {File} 附件信息
**/
onPreview(file) {
let acceptType = this.accepts || this.accept;
let isImg = acceptType.match('image/');
let isPdf = acceptType.match('application/pdf');
let excelAccept = 'application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
let isExcel = excelAccept.match(acceptType)
if (isImg) {
// 预览图片,调用图片预览组件展示图片;如果有本地文件,则直接展示本地文件,否则,根据fileId获取服务器上的文件后进行展示
if (file.raw) {
this.$refs.comImgShowRef.getmgUrl(file.raw, file.name)
} else {
this.axios.post('/getFile', { fileId: file.fileId }).then(res => {
this.$refs.comImgShowRef.previewPDF(res, file.name)
})
}
} else if (isPdf) {
// 预览pdf,如果有本地文件,则直接展示本地文件,否则,根据fileId获取服务器上的文件后进行展示
if (file.raw) {
this.$refs.comPdfShowRef.previewPDF(file.raw, '02')
} else {
this.axios.post('/getFile', { fileId: file.fileId }).then(res => {
this.$refs.comPdfShowRef.previewPDF(res)
})
}
} else if (isExcel) {
// 预览Excel,仅支持查看非当前上传行为的Excel预览(因预览是以下载的模式预览的,这里认为当前上传的文件再次下载没有意义)。
if (file.fileId) {
this.downloadExcel(file.fileId, file.name)
}
}
},
/**
* 下载Excel表
* @fileId {String} 文件id
* @name {String} 文件名
**/
downloadExcel(fileId, name) {
// 根据fileId请求服务获取Excel流,将流进行整理后下载下来
this.axios.post('/getxcel', { fileId }).then( res => {
// 调用公共方法,下载文件;【此公共方法是写在全局的,通用的,现将公共方法写在下面】
this.downloadFile(res, name)
})
},
/**
* 通过文件流下载文件
* @file 文件流,必输
* @name 下载的文件名,必输
* @type 下载的文件类型,默认xls,非必输
**/
downloadFile(file, name, type) {
if (!file) {
return
}
const url = window.URL.createObjectURL(
new Blob([file], {
type: type || 'application/vnd.ms-excel'
})
);
const link = document.createElement('a');
link.style.display = 'none';
link.href = url;
// 设置文件名;download属性定义了下载链接的地址而不是跳转路径
link.setAttribute('download', name);
document.body.appendChild(link);
link.click();
document.body.removeChild(link); // 下载完成后移除元素
window.URL.revokeObjectURL(url); // 释放掉blob对象
},
/**
* 删除文件的钩子
* @file {File} 删除的文件信息
* @fileList {Array} 删除后的文件列表
**/
onRemove(file, fileList) {
this.fileList = fileList;
this.$emit('fileChange', fileList)
this.$emit('fileRemove', file)
},
/**
* 上传成功的钩子
* @response {Object} 上传成功后服务返回的数据
* @file {File} 上传的文件信息
* @fileList {Array} 上传后的文件列表
**/
onSuccess(response, file, fileList) {
if (response.code != 'SUCCESS') {
console.log(response.message || '文件上传报错')
// 删除报错文件
this.$refs.comUpload.abort(file.raw);
fileList.splice(fileList.indexOf(file), 1);
this.onRemove(file, fileList);
return;
}
this.fileList = fileList
this.$emit('fileChange', fileList)
this.$emit('fileAdd', file);
},
/**
* 上传文件之前的钩子,用于做校验文件等一系列操作,压缩图片操作在以前的文章中讲过了,这里就略过不写了
* @file {File} 文件本身
**/
beforeUpload(file) {
let _this = this
return new Promise((resolve, reject) => {
if (file.size === 0) { // 如果检测不到文件大小,报错
console.log('文件错误,请上传正确的文件')
reject()
}
// 判断文件是否超过限制大小
let isLtSize = _this.size
? file.size / 1024 / 1024 < _this.size
? true
: false
:true
if (!isLtSize) {
console.log(`文件大小超过限制,最大不超过${_this.size}MB`)
reject()
}
// 判断文件是否符合要求的格式
let acceptType = _this.accepts || _this.accept;
let extension = acceptType
? acceptType.match(file.type)
? true
: false
: true
if (!extension) {
console.log(`文件格式错误,仅限${_this.acceptText}格式`)
reject();
}
// 判断是否是图片,如果是图片文件,则进行压缩
if (acceptType.match('image') || (!acceptType && file.type.match('image'))) {
// 此处做压缩图片操作,方法已在之前的文章中写过(https://blog.csdn.net/weixin_44134899/article/details/95046994),这里不做赘述
} else {
resolve(file)
}
});
},
/**
* 删除文件之前的钩子
* @file {File} 删除的文件
* @fileList {Array} 文件列表
**/
beforeRemove(file, fileList) {
this.$emit('fileBeforeRemove', file, fileList)
},
/**
* 文件超出个数限制时的钩子
* @files {File} 当前上传的文件
* @fileList {Array} 当前的文件列表
**/
onExceed(files, fileList) {
console.log(`文件${files[0].name}上传失败,最多只能上传${this.limit}个文件`)
},
/**
* 文件上传失败时的钩子
**/
onError(err, file, fileList) {
console.log(`文件【${file.name}】上传失败,请刷新重试或联系系统管理员!`)
},
},
};
</script>
<style lang="less">
.com-upload {
.el-upload {
display: inline-block;
text-align: left;
}
&.is-drag {
.el-upload {
width: 100%;
}
}
.el-upload__tip {
line-height: 20px;
.el-upload__tip_tips {
margin: 0;
color: #777;
}
}
}
</style>
html部分(这里只列举少数属性的配置例子):
<com-upload
v-model="model"
accept="application/pdf"
:drag="false"
accept-text="pdf"
:buttons="JSON.stringify([{label: '额外按钮1', type: '', id: '01'},{label: '额外按钮2', type: 'primary', id: '02'}])"
:init-files="JSON.stringify([{name: '01.pdf', url: 'https://www.baidu.com', fileId: '01'},{name: '02.pdf', url: 'https://www.baidu.com', fileId: '02'}])"
:size="5"
:limit="3"
tips="这是额外提:请在上传前检查附件是否正确!"
@fileChange="fileChange"
@fileAdd="fileAdd"
></com-upload>
js部分:
fileChange(fileList) {
console.log("fileList=>",fileList)
},
fileAdd(file) {
console.log("file=>",file)
},