element-ui实现证件照上传预览下载组件封装
效果:
参数说明
我只写了两个参数,后续有需求再对其组件进行丰富~
参数 | 说明 |
---|---|
fileListProp | 用来存储上传后后端返回的图片UR了 |
uploadUrl | 图片上传返回的URL后端接口地址 |
widthProp | 图片上传框的宽度 |
heightProp | 图片上传框的高度 |
方法说明
参数 | 说明 |
---|---|
beforeUpload(file) | 在文件上传前的钩子函数。用于检查文件的类型和大小,如果文件不符合条件,则显示相应的错误信息并停止上传。该方法也负责创建本地预览URL ,并通过直接调用directUpload 方法来处理文件的上传逻辑,同时避免使用el-upload 组件默认的上传行为。 |
handleUploadFailure(file) | 处理文件上传失败的逻辑。该方法从fileList中 移除当前文件,并显示上传失败的消息。 |
handleUploadResponse(responseData, file) | 处理文件上传成功的响应。该方法根据后端返回的数据更新fileList ,并确保上传的文件在列表中显示。 |
handleRemove(file, fileList, name) | 处理文件的删除操作。从fileList 中移除指定的文件,并清空临时存储的图片信息。 |
handleExceed(files, fileList, num) | 处理文件超出限制数量的情况。显示警告信息,告知用户已超出文件选择的限制。 |
handlePictureCardPreview(file) | 处理点击预览图片的逻辑。显示一个对话框来展示被点击的图片。 |
handleAvatarSuccess(file, fileList) | 处理文件上传成功后的回调。主要用于界面的更新,比如调整上传组件的大小以适应上传的文件。 |
handleDownload(file) | 处理下载图片的逻辑。根据文件对象中的URL 创建一个隐藏的链接,并触发点击事件来下载图片。 |
方法解析
此Vue组件是一个图片上传功能,包含了一系列的方法来处理文件的上传、预览、删除等操作。
-
beforeUpload(file)
在文件上传前的钩子函数。用于检查文件的类型和大小,如果文件不符合条件,则显示相应的错误信息并停止上传。该方法也负责创建本地预览URL
,并通过直接调用directUpload
方法来处理文件的上传逻辑,同时避免使用el-upload
组件默认的上传行为。
参数:
file
- 当前要上传的文件对象。
返回: 返回false
以阻止el-upload
组件的默认上传行为。 -
handleLocalPreview(localPreviewUrl, file)
处理文件的本地预览。该方法更新fileList
,将包含本地预览URL
的文件对象添加到列表中,以便在界面上显示。
参数:
localPreviewUrl
: 文件的本地预览URL
。
file
: 当前操作的文件对象。 -
directUpload(file)
直接上传文件到服务器。该方法创建一个FormData
对象,将文件添加到其中,并使用axios
发送POST
请求到指定的上传URL
。
参数:
file
- 要上传的文件对象。
操作:
成功上传后,调用handleUploadResponse
来处理响应数据。
如果上传失败,调用handleUploadFailure
来处理错误。 -
handleUploadFailure(file)
处理文件上传失败的逻辑。该方法从fileList中移除当前文件,并显示上传失败的消息。
参数:
file
- 上传失败的文件对象。 -
handleUploadResponse(responseData, file)
处理文件上传成功的响应。该方法根据后端返回的数据更新fileList
,并确保上传的文件在列表中显示。
参数:
responseData
: 后端返回的响应数据。
file
: 当前上传的文件对象。 -
handleRemove(file, fileList, name)
处理文件的删除操作。从fileList中移除指定的文件,并清空临时存储的图片信息。
参数:
file
: 要删除的文件对象。
fileList
: 当前的文件列表(此参数未在方法体中直接使用)。
name
: 文件的名称(此参数未在方法体中直接使用)。 -
handleExceed(files, fileList, num)
处理文件超出限制数量的情况。显示警告信息,告知用户已超出文件选择的限制。
参数:
files
: 当前选择的文件列表。
fileList
: 已经上传的文件列表。
num
: 允许上传的最大文件数量。 -
handleAvatarSuccess(file, fileList)
处理文件上传成功后的回调。主要用于界面的更新,比如调整上传组件的大小以适应上传的文件。
参数:
file
: 成功上传的文件对象。
fileList
: 当前的文件列表。 -
handlePictureCardPreview(file)
:处理点击预览图片的逻辑。显示一个对话框来展示被点击的图片。
参数:
file
- 被点击的文件对象,用于获取图片的URL
进行预览。 -
handleDownload(file)
处理下载图片的逻辑。根据文件对象中的URL
创建一个隐藏的链接,并触发点击事件来下载图片。
参数:
file
- 要下载的文件对象。
组件通过这些方法实现了一个具有上传前验证、文件预览、直接上传到后端、处理上传成功或失败响应、删除已上传文件等功能的图片上传器。
父组件调用:
<business-license-upload
:key="componentKey"
:widthProp="140"
:heightProp="140"
:uploadUrl="'http://192.168.60.27:8888/file-storage-center/object/uploadObjectByMultipartFile'"
:fileListProp.sync="fileList2">
</business-license-upload>
<p><span>*</span> <span class="idCardTip">身份证国徽面</span></p>
如果通过异步进行回显操作的话,在每次异步后执行componentKey++
操作,更新DOM 强制回显
组件源码:
<template>
<div>
<div
v-loading="loading"
:style="{ width: width + 'px', height: height + 'px' }"
>
<el-upload
:style="{ width: width + 'px', height: height + 'px' }"
class="avatar-uploader"
:class="noneBtnDealImg ? 'disUoloadSty' : ''"
ref="uploader"
:file-list="fileList"
:before-upload="beforeUpload"
:action="uploadUrl"
:on-exceed="(files, fileList) => handleExceed(files, fileList, 1)"
:on-change="
(file, fileList) => this.handleAvatarSuccess(file, fileList)
"
list-type="picture-card"
:auto-upload="true"
>
<i
slot="default"
class="el-icon-plus"
:style="{ lineHeight: height + 'px' }"
></i>
<div slot="file" slot-scope="{ file }">
<img class="el-upload-list__item-thumbnail" :src="file.url" alt="" />
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<i class="el-icon-zoom-in"></i>
</span>
<!--
<span v-if="!disabled" class="el-upload-list__item-delete" @click="handleDownload(file)"><i class="el-icon-download"></i></span>
-->
<span
v-if="!disabled"
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<i class="el-icon-delete"></i>
</span>
</span>
</div>
</el-upload>
</div>
<el-dialog :modal="false" width="60%" :visible.sync="dialogVisible">
<img width="100%" :src="dialogImageUrl" alt="" />
</el-dialog>
</div>
</template>
<script>
import { getToken } from '@/utils/auth'
import axios from 'axios' // 导入axios
export default {
props: {
widthProp: {
typeof: Number,
default: () => 200
},
heightProp: {
typeof: Number,
default: () => 140
},
fileListProp: {
typeof: Array,
default: () => []
},
uploadUrl: {
typeof: String,
default: () => ''
}
},
data() {
return {
width: this.widthProp,
height: this.heightProp,
fileList: this.fileListProp,
headerObj: {
authorization: getToken(),
tenant_id: 0
},
img: '',
noneBtnDealImg: false,
uploadfileurl: this.uploadFileURL,
dialogImageUrl: '',
dialogVisible: false,
loading: false,
disabled: false
}
},
watch: {
fileListProp: {
deep: true,
handler(oldVal, newVla) {
this.fileList = this.fileListProp
this.$nextTick(() => {
if (this.fileList.length >= 1) {
const uploadBox1 =
document.getElementsByClassName('avatar-uploader')
uploadBox1[0].style.height = this.height + 'px'
}
this.noneBtnDealImg = this.fileList.length >= 1
})
}
}
},
created() {},
mounted() {
this.noneBtnDealImg = this.fileList.length >= 1
},
methods: {
beforeUpload(file) {
// 检查文件类型、大小等
const isJPGorPNG = file.type === 'image/jpeg' || file.type === 'image/png'
const isLt2M = file.size / 1024 / 1024 < 2
if (!isJPGorPNG) {
this.$message.error('上传的图片只能是 JPG 或 PNG 格式!')
return false
}
if (!isLt2M) {
this.$message.error('上传的图片大小不能超过 2MB!')
return false
}
// 创建本地预览URL
const reader = new FileReader()
// 转换文件为base64字符串
reader.readAsDataURL(file)
reader.onload = (e) => {
// 假设后端需要的是base64字符串,则可以直接使用e.target.result
// 如果后端需要的是文件对象,则可以在此步骤中不处理
const localPreviewUrl = e.target.result
this.handleLocalPreview(localPreviewUrl, file)
}
// 发起上传请求
this.directUpload(file)
// 返回false阻止el-upload组件的默认上传行为
return false
},
handleLocalPreview(localPreviewUrl, file) {
// 根据需要更新的fileList,为了预览,临时添加一个带有本地URL的文件对象
const fileForPreview = {
...file,
url: localPreviewUrl // 使用本地预览URL
}
this.fileList = [fileForPreview] // 假设您只希望回显一个文件
// 注意:根据您的逻辑,您可能需要在上传成功或失败的回调中相应地更新或清除这个临时的预览文件
//当前只保留一张照片
this.$nextTick(() => {
if (this.fileList.length >= 1) {
const uploadBox1 = document.getElementsByClassName('avatar-uploader')
uploadBox1[0].style.height = this.height + 'px'
}
this.noneBtnDealImg = this.fileList.length >= 1
})
},
async directUpload(file) {
try {
this.loading = true
const formData = new FormData()
formData.append('file', file)
// this.fileList = file
const response = await axios.post(this.uploadUrl, formData, {
headers: this.headerObj
})
// 处理上传成功
this.handleUploadResponse(response.data, file)
} catch (error) {
this.handleUploadFailure(file)
// 处理上传错误
} finally {
this.loading = false
}
},
//上传出错
handleUploadFailure(file) {
//清除临时文件
this.fileList = this.fileList.filter((item) => item.uid !== file.uid)
// 向用户展示失败消息
this.$message.error('图片上传失败,请重试。')
// 检查fileList长度,更新noneBtnDealImg状态以正确显示上传按钮
this.noneBtnDealImg = this.fileList.length >= 1
// 因为文件上传组件可能依赖fileList的变化来更新UI,这里确保UI能够响应fileList的变化
this.$forceUpdate()
},
//上传成功
handleUploadResponse(responseData, file) {
// 根据后端返回的数据,更新fileList
const updatedFile = {
...file,
response: responseData,
url: responseData.data || '' //后端返回的URL在这里
}
// this.fileList = [updatedFile];
this.$emit('update:fileListProp', [updatedFile])
//当前只保留一张照片
this.$nextTick(() => {
if (this.fileList.length >= 1) {
const uploadBox1 = document.getElementsByClassName('avatar-uploader')
uploadBox1[0].style.height = this.height + 'px'
}
this.noneBtnDealImg = this.fileList.length >= 1
})
},
//图片删除
handleRemove(file, fileList, name) {
const index = this.fileList.indexOf(file)
if (index > -1) {
this.fileList.splice(index, 1)
}
this.img = ''
this.noneBtnDealImg = this.fileList.length >= 1
this.$refs['uploader'].clearFiles()
this.$forceUpdate()
},
handleExceed(files, fileList, num) {
this.$message.warning(
`当前限制选择 ${num} 个文件,本次选择了 ${
files.length
} 个文件,共选择了 ${files.length + fileList.length} 个文件`
)
},
handleAvatarSuccess(file, fileList) {
this.$nextTick(() => {
if (fileList.length >= 1) {
const uploadBox1 = document.getElementsByClassName('avatar-uploader')
uploadBox1[0].style.height = this.height + 'px'
}
this.noneBtnDealImg = fileList.length >= 1
})
},
handlePictureCardPreview(file) {
this.dialogImageUrl = file.url
this.dialogVisible = true
},
handleDownload(file) {
// 获取图片的真实URL
const imageUrl =
file.response && file.response.data ? file.response.data : file.url
// 创建一个隐藏的可下载链接
const link = document.createElement('a')
link.style.display = 'none'
link.href = imageUrl
link.download = file.name || 'image.png' // 设置下载的文件名,如果没有name属性则默认为'image.png'
// 触发点击事件以下载图片
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
}
}
</script>
<style lang="less" scoped>
.el-form-item__label::after {
content: '(最多1张)';
display: block;
font-size: 12px;
color: #999;
line-height: 12px;
}
/deep/ .allUpload .el-form-item__content {
display: flex;
}
/deep/ .el-upload-list__item {
transition: none !important;
}
/deep/ .disUoloadSty .el-upload--picture-card {
/* 上传按钮隐藏 */
display: none !important;
}
/deep/ .el-upload--picture-card {
width: 100%;
height: 100%;
}
/deep/ .el-upload-list--picture-card .el-upload-list__item {
margin-right: 0px !important;
margin-bottom: 0px !important;
}
/deep/ .el-upload-list__item {
width: 100% !important;
height: 100% !important;
}
/deep/ .el-upload-list__item div:nth-child(1) {
width: 100% !important;
height: 100% !important;
}
</style>