组件说明、预览、功能
在Element UI的上传组件基础上,根据业务特点进行二次封装;使用腾讯云COS作为图片服务器的平替
- 添加本地图片,并上传到图片服务器
- 本地预览上传到图片服务器的图片,并可以放大预览
- 删除本地预览的图片
- 自定义限制图片的格式和大小
- 预览旧头像



一、新建组件
使用“照片墙”模式的el-upload,并设定limit的属性值为1
原因:利用该组件自带的预览放大图片和删除图片的功能;虽然是照片墙意味着要上传很多图片,但是可以给< el-upload >设定limit属性,限制上传数量为1,这样既限制了上传数量,又可以使用照片墙模式的预览放大图片和删除图片的功能
<template>
<div>
<el-upload
action="#"
list-type="picture-card"
:limit="1"
:file-list="fileListArr"
:on-preview="avatarPreview"
:on-remove="removeHandler"
:class="{disabled:uploadedOnePic}"
:on-change="fileStateChangeHandler"
:before-upload="beforeUploadHandler"
:http-request="upload"
>
<!-- 虚线框中间的加号 -->
<i class="el-icon-plus" />
</el-upload>
<!-- 放大图片查看时的对话框 -->
<el-dialog :visible="showDialog" @close="closeDialog">
<img width="100%" :src="avatarUrl" alt="">
</el-dialog>
<!-- el-progress 进度条, 与'选择图片的虚线框'同宽 -->
<el-progress v-if="showProgress" :percentage="percent" style="width:148px" />
</div>
</template>
属性说明:
- action 属性:必须项,默认上传接口
- list-type 属性 :文件列表的类型(字符串),可以是:text / picture / picture-card
- limit 属性:控制上传文件的数量
- file-list 属性:它是已经上传完成的文件的列表,属性值是一个数组,成员是对象。象必须有url属性,即完成上传的图片的在线地址;还可以添加自定义属性,例如文件名name等。完成上传的图片,如何在页面中显示? 将fileListArr数组绑定给 file-list 属性
- on-preview 属性:点击“ 放大镜 ”时能触发它的回调函数
- on-remove 属性:点击“ 垃圾桶图标 ”时能触发它的回调函数
- class 属性:vue的动态类名
- on-change 属性:添加文件、上传成功和上传失败时都会触发它的回调函数
- before-upload 属性:上传操作执行前,回调被自动触发
- http-request 属性:属性值是函数,函数的参数是事件对象e,事件对象e有很对属性, 包括原生JS的File文件对象属性
二、配置el-upload组件的核心属性
file-list 属性
- 它是已经上传完成的文件的列表,属性值是数组fileListArr,成员是对象,对象必须有url属性,即上传完成的图片的在线地址
- 将fileListArr数组绑定给 file-list 属性,已经上传完成的图片就能在页面中显示
- fileListArr数组中先放入一个图片在线地址,方便调试。此时,左侧删除完成图片 和 右侧'选择图片的虚线框'都显示在页面上
data() {
return {
/* 已经完成上传的文件的列表, 是一个数组
例如: [{name: 'food.jpg', url: 'https://xxx.cdn.com/xxx.jpg'}] */
// fileListArr: [{ url: 'https://tse2-mm.cn.bing.net/th/id/OIP-C.myc8QUm7MwMHeC2FLR4snAAAAA?pid=ImgDet&rs=1' }],
fileListArr: [],
// 对话框中的<img>元素的src属性值
avatarUrl: '',
showDialog: false,
// 当前选择的确定上传的图片的uid,即图片的唯一标识
currentFileUid: null,
showProgress: false,
// 上传进度条的百分比,属性值来自于COS
percent: 0
}
}
三、点击放大镜图标,放大查看图片
使用的钩子:on-preview
实现方式:将当前图片的在线地址赋值给对话框中的图片,再显示对话框
注意:没有预览本地图片的功能,预览的只是上传到图片服务器后,拿到的图片在线地址,点击“保存更新”后,才将在线地址存到我们自己的服务器;或者预览后端传来的旧头像
/* 函数来源:on-preview 属性的回调,组件的钩子函数 */
/* 函数作用:放大预览已经完成上传的头像 */
/* 触发方式:点击“ 放大镜 ”时触发该函数 */
avatarPreview(file) {
// console.log(file)
/*
打印 file 参数, 它是一个对象,有三个属性:status uid url
status 属性:上传的完成的状态;
url 属性:图片完成上传后的在线地址, 属性值来自 fileListArr 数组中的url属性;
uid 属性:文件的唯一标识
*/
// 1. 将file.url(图片的在线地址)存入变量avatarUrl,作为对话框中<img>标签的src属性值,实现大图预览
this.avatarUrl = file.url
// 2. 显示对话框
this.showDialog = true
},
closeDialog() {
this.showDialog = false
},
四、控制'选择图片的虚线框'的显示或隐藏
实现方式:'选择图片的虚线框'就是有加号的大上传按钮,通过判断数组fileListArr的长度是否为1,再结合计算属性计算出布尔值,最后使用vue的动态类名实现这个盒子的显示或隐藏
// 计算属性控制'选择图片的虚线框'的显示或隐藏
computed: {
/* 判断 已经完成上传的文件的列表fileListArr数组中是否存在图片
1. 判断的目的:如果已经上传,则不应该显示'选择图片的虚线框', 表示不允许继续上传;如果没有上传,则显示'选择图片的虚线框',表示允许上传
2. 如何显示'选择图片的虚线框'?
2.1 打开调试面板,观察发现'选择图片的虚线框'的类名是'el-upload--picture-card',在该类名下添加display:none 可隐藏盒子
2.2 但是,该盒子是编译出来的,在<template>中看不到。怎么办?给<el-upload>添加类名disabled,通过叠加类选择器的形式,可以给'el-upload--picture-card'盒子添加属性' display:none '。注意,必须使用全局的CSS,<style>上不能加scoped
2.3 所以, 根据计算属性uploadedOnePic,给<el-upload>动态添加或移除类名disabled,disabled类名控制 '选择图片的虚线框' 的显示或隐藏
2.4 vue中,给元素动态添加/移除类名的语法: v-on:class="{ 类名 : 布尔值 }"
*/
uploadedOnePic() {
if (this.fileListArr.length === 1) {
return true
} else {
return false
}
}
},
<style>
/* //! 错误点/BUG/注意:不能加scoped,否则样式失效。因为编译后,要保证该样式在全局有效 */
.disabled .el-upload--picture-card {
display: none;
}
</style>
五、点击垃圾桶图标,删除当前图片
使用的钩子:on-remove
实现方式:清空数组fileListArr
注意:
- 删除当前图片后,'选择图片的虚线框'自动显示
- on-remove的钩子函数只是删除了页面上能看到的上传成功的图片(前端视图层与数据层), 后端数据库中的图片并未删除(后端数据库层)。如果需要删除数据库,必须在该函数中调用删除用户头像的接口
/* 函数来源:on-remove 属性的回调,组件的钩子函数 */
/* 函数作用:删除当前页面正显示的上传成功的图片,同时让'选择图片的虚线框'显示 */
/* 触发方式:点击“ 垃圾桶图标 ”时触发 */
//! 注意:该函数只是删除了页面上能看到的上传成功的图片(前端视图层与数据层), 后端数据库中的图片并未删除(后端数据库层)。如果需要删除数据库,必须在该函数中调用删除用户头像的接口
/* 实现原理:
1. 点击“ 垃圾桶图标 ”时,<el-upload>会自动移除页面上的图片,但这只是视图层的变化,数据层也要同步。所以将回调的fileList参数(空数组)赋值给fileListArr数组,清空该数组
2.因为fileListArr数组为空,所以计算属性uploadedOnePic能让'选择图片的虚线框'显示,它显示后,用户,可上传其他图片,或不再上传(如果调用了删除后端头像结果的话,相当于删除了当前的头像)
*/
removeHandler(file, fileList) {
// 打印查看
// console.log(file) // 打印 file 参数, 它是一个对象,有三个属性:status uid url
// console.log(fileList) // 打印 fileList 参数,它是一个删除当前文件后的空数组
this.fileListArr = fileList
},
六、删除当前图片后,可添加其他图片
使用的钩子:on-change
实现方式:清空数组fileListArr
/* 函数来源:on-change 属性的回调,组件的钩子函数 */
/* 函数作用:删除当前头像,再点击'选择图片的虚线框'的加号选择图片后,显示已经完成上传的图片,同时让'选择图片的虚线框'隐藏 */
/* 触发方式:添加文件、上传成功和上传失败时都会触发。因此添加图片时,不能向fileListArr数组push成员,因为会被重复添加多次*/
fileStateChangeHandler(file, fileList) {
// 打印 file 参数, 它是一个对象,有多个属性(name percentage raw size uid url等),但它可不是原生JS的文件对象File
// console.log(file)
// 打印 fileList 参数, 它是一个数组,成员就是上一行的对象
// console.log(fileList)
// 将fileList数组赋值给fileListArr数组,就能显示已经完成上传的图片
this.fileListArr = fileList
},
七、校验上传图片的格式和大小
使用的钩子:before-upload
实现方式:准备图片格式的数组,some方法遍历,不符合时返回false,终止上传操作;准备图片大小,if判断,不符合时返回false,终止上传操作;
/* 函数来源:before-upload 属性的回调,组件的钩子函数 */
/* 函数作用:上传前校验图片的文件类型和大小 */
/* 触发方式:在上传这一操作发生前 */
/* 实现原理:
1. beforeUploadHandler函数的file参数为原生JS的文件对象File,有很多属性,例如文件大小size、文件类型type、文件的唯一标识uid
2. 如果beforeUploadHandler函数 返回 false ,则停止上传
3. 如果beforeUploadHandler函数 返回 true ,则可以上传
*/
beforeUploadHandler(file) {
console.log(file)
/* 1. 图片格式校验 */
// 允许的上传的图片格式
const types = ['image/jpeg', 'image/gif', 'image/bmp', 'image/png']
// 根据根据当前选择文件的类型,在允许的图片格式中查找,如果找到说明格式支持;如果找不到说明格式不支持
const isAllow = types.some((item) => {
return item === file.type
})
if (isAllow === false) {
this.$message.error('图片格式只支持jpeg gif bmp png')
return false // 函数返回false,终止上传操作
}
/* 2. 图片大小校验 */
// 限制为3M
const maxFileSize = 3 * 1024 * 1024
if (file.size > maxFileSize) {
this.$message.error('图片大小不能超过3M')
return false // 函数返回false,终止上传操作
}
/* 3. 校验全部通过 */
// 提取当前文件的标识,存入变量currentFileUid,表示当前确定的要上传的图片就是此uid的图片
this.currentFileUid = file.uid
// 如果校验全部通过,一定要返回true,上传操作才能进行
return true
},
八、上传到腾讯云COS
- 利用http-request 属性,自定义上传的实现
- 配置el-progress,显示和隐藏上传进度条
- 将腾讯云COS响应回的在线地址保存到数组fileListArr中,实现图片的回显,然后将在线地址传给父组件
- 最后,在父组件中发请求,将在线地址存到我们的服务器中
/* 函数来源:http-request 属性的回调, 自定义上传的实现 */
/* 函数作用:将用户选择的图片上传到腾讯云COS,并将响应回的图片在线地址存入fileListArr数组 */
/* 触发方式:上传条件具备时,自动触发 */
upload(e) {
// console.log(e)
// 如果文件对象不存在,退出函数
if (e.file === false) return
this.showProgress = true // 显示上传进度条
// 在腾讯云控制台一顿配置后,再安装腾讯云COS的SDK依赖: npm i cos-js-sdk-v5 --save
// 使用node的方式引入
var COS = require('cos-js-sdk-v5')
// new一个实例对象
var cos = new COS({
SecretId: 'AKIDhEUUDfspVv60dz7yZuxkGVTBcmfIekZ8',
SecretKey: 'MSHp78sLXS6TZOxFG2XliPpxoOimY4KJ'
})
// 调用cos实例的 putObject( {配置对象},上传的回调 ) 方法,实现文件的上传
cos.putObject({
Bucket: 'yoyo-1309207666', /* 存储桶名称 */
Region: 'ap-nanjing', /* 存储桶所在地域,必须字段 */
Key: e.file.name, /* 上传文件的名字 */
StorageClass: 'STANDARD',
Body: e.file, // 上传的文件对象File
// onProgress 表示上传的进度信息,在上传时,cos支持配置onProgress回调,在这个回调函数中可以拿到当前上传的进度,然后配合el-progress在页面显示上传进度。
// {"loaded":0,"total":3080,"speed":0,"percent":0}
onProgress: (progressData) => {
// 防坑指南:匿名函数统一换成箭头函数,否则“ this.percent ”无法指向data()函数中的percent变量,导致进度条不变化
// console.log(JSON.stringify(progressData)) // 官网的打印项
// 转换为整数, el-progress 要求percent的属性值为整数
this.percent = progressData.percent * 100
}
}, (err, data) => {
// 防坑指南:匿名函数统一换成箭头函数
this.showProgress = false // 隐藏上传进度条
this.percent = 0 // 重置进度条的百分比
console.log(err || data)
if (data.statusCode === 200) {
// 上传成功就会得到data对象,访问data对象的Location属性就能拿到图片的非完整在线地址(没有协议名),手动拼接协议名后,得到完整可用的在线地址
const completeUrl = 'https://' + data.Location
// 注意:页面显示完成上传的图片的依赖数据是fileListArr数组,数组中能存多个包含url属性的对象,所以不能将图片在线地址简单的push到数组中,也不能将地址直接赋值给fileListArr[0],这样不利于以后上传多张图片。
// 应该map遍历fileListArr数组,
this.fileListArr = this.fileListArr.map((item) => {
if (item.uid === this.currentFileUid) {
// 如果fileListArr数组的成员中,有uid与当前上传图片的uid相同的(说明此时上传成功的正是刚刚选择的那张图片,而不是别的图片),就将该成员替换为有完整url的新成员,这样才能让完成上传的图片在页面显示
/*
同时,要追加一个uploaded属性,这是一个标识,表示这张图片已经完成上传。true表示完成上传,false表示没有完成上传。
在后期,根据uploaded的属性值来决定是否可以保存用户信息,因为如果没有上传成功不允许保存保存用户信息
*/
return { url: completeUrl, uploaded: true }
} else {
// 如果没有,则不做任何事情
return item
}
})
/* 将 fileListArr数组 传给父组件。因为保存更新的按钮在父组件中,只有保存更新了,在线图片地址才会存到我们的服务器中*/
this.$emit('urlFromSon', this.fileListArr)
}
})
}