实现功能:
addOrUpdate.vue新增或修改时,修改需要可以回显图片,上传图片后要可以预览以及删除。
当页面有多个字段涉及上传单图的,也可以使用子组件作为公共的组件引入。
这里前后端关于图片的交互都是base64字符串,所以回显和上传提交的也都是base64字符串。
图片:
el-upload照片墙只上传一张,隐藏另一张参考的是如下链接:
https://blog.csdn.net/weixin_59395272/article/details/128649347
上传预览实现效果图如下:
父组件
多个字段需要用到这个公共的上传组件,加type字段进行区分(示例是父组件页面多字段上传图片)。
如果只有一个字段上传图片,也是可以用子组件的,一个字段的话就不用加判断
getChildParam 和 getUploadChildData修改为更简单的直接赋值。
注意点:
<div v-if="showUpload"> showUpload默认设置为true,保证每次子组件都进一次mounted方法
关键示例代码如下:
<el-form-item label="正面照" >
<div v-if="showUpload">
<upload-child ref="imgUpload1" :data="getChildParam('1','正面照')" @getUploadChildData="getUploadChildData"></upload-child>
</div>
</el-form-item>
<el-form-item label="背面照" >
<div v-if="showUpload">
<upload-child ref="imgUpload1" :data="getChildParam('2','背面照')" @getUploadChildData="getUploadChildData"></upload-child>
</div>
</el-form-item>
<el-form-item label="侧面照" >
<div v-if="showUpload">
<upload-child ref="imgUpload1" :data="getChildParam('3','侧面照')" @getUploadChildData="getUploadChildData"></upload-child>
</div>
</el-form-item>
// 传给子组件的数据 ,用于子组件回显和区分
getChildParam(type,title){
var obj = new Object();
obj.type = type;
obj.title = title;
obj.fileList =[];
switch(type){
case '1':
obj.fileList = this.frontImgList; // 获取详细信息的时候赋值frontImgList 主要是为了回显
break;
case '2':
obj.fileList = this.backImgList;
break;
case '3':
obj.fileList = this.sideImgList;
break;
}
return obj;
},
//接收子组件上传的base64格式的图片url,赋值给想传给后端的对象
getUploadChildData(obj){
switch(obj.type){
case '1':
this.frontImgUrl = obj.url;
break;
case '2':
this.backImgUrl = obj.url;
break;
case '3':
this.sideImgUrl = obj.url;
break;
}
}
子组件
el-upload照片墙只上传一张,隐藏另一张参考如下链接:
https://blog.csdn.net/weixin_59395272/article/details/128649347
这是抽取一个公共的上传组件,同一个页面或不同页面(有一个或多个字段需要上传图片,但是字段都只需要上传一张图片)需要上传的时候可以引入这个公共的上传组件。
注意点:
mounted 和hideAnother 解决的是页面正常回显了后端返回的图片,然后隐藏另一个多余的上传框,如果页面不涉及回显的话可以忽略这两个方法的处理。
setTimeOut要加,避免还获取不到页面元素。
<template>
<div>
<el-upload action="#"
accept="image/**" :limit="1" :multiple="false" :auto-upload="false"
list-type="picture-card"
:on-preview="(file)=>handleImgPreview(file,data.type)"
:on-change="(file)=>changeUpload(file,data.type)"
:file-list="data.fileList"
:before-upload="(file)=>handleBeforeUpload(file,data.type)"
:on-exceed="handleExceed"
:on-remove="(file)=>imgRemove(file,data.type)"
:show-file-list="true"
:class="type">
<i class="el-icon-plus"></i>
</el-upload>
<el-dialog :visible.sync="imgVisible" :append-to-body="true" style="width:80%;height:100%;" :title="data.title">
<div style="width:80%;height:80%;justify-content:center;align-items:center;text-align:center;display:flex">
<div v-viewer>
<img :src="imgUrl" style="height: 200px; width: 200px; " />
</div>
</div>
</el-dialog>
</div>
</template>
<script>
import {getBase64Url} from '@/api/utils.js'
export default {
name:"upload",
props:{
data:{
type: Object,
default:()=>{return {} }
},
},
data(){
return {
fileList:[],
imageList:[],
hideUpload:false,
imgVisible:false,
imgUrl:'',
onChangeImgUrl:'',
type:'',
}
},
mounted(){
//有数据 回显隐藏 多余upload 如果只是新增可以不考虑
setTimeout(()=>{
//通过动态加样式,使上传组件唯一,方便隐藏多余的上传框
this.type="type"+this.data.type;//动态设置class值
var length = this.data.fileList.length;
if(length>=1){
this.hideAnother();
}
},20)
},
//监听事件
watch: {
data:{
handler(newData) {
//通过动态加样式,使上传组件唯一,方便隐藏多余的上传框
this.type="type"+newData.type;//动态设置class值
var length = newData.fileList.length;
if(length>=1){
this.hideAnother();
}
},deep:true
},
//只上传一张图片,照片墙隐藏多余的上传框
onChangeImgUrl: {
handler(newName) {
//这个type上传后,隐藏这个多余的上传框,
//这个type代表上传的该字段标识,该字段上传隐藏该字段多余框
var aa = document.querySelector('.'+this.type+' .el-upload--picture-card');
//document.querySelector('.el-upload--picture-card')
if (newName) {
aa.style.display = 'none'
} else {
setTimeout(() => {
aa.style.display = 'inline-block'
}, 1100)
}
}
}
},
methods:{
hideAnother(){
//要加setTimeOut避免此时元素还找不到 为null
setTimeout(()=>{
var aa = document.querySelector('.'+this.type+' .el-upload--picture-card');
aa.style.display = 'none'
},20)
},
//上传基本校验
handleBeforeUpload(file,type){
var img = file.name.substring(file.name.lastIndexOf('.') + 1)
const suffix = img === 'jpg'
const suffix2 = img === 'png'
const suffix3 = img === 'jpeg'
const isLt1M = file.size / 1024 / 1024 < 1;
if (!suffix && !suffix2 && !suffix3) {
this.$message.error("只能上传图片!");
return false
}
// 可以限制图片的大小
if (!isLt1M) {
this.$message.error('上传图片大小不能超过 1MB!');
}
return suffix || suffix2 || suffix3
},
// 弹窗 图片预览
handleImgPreview(file,type){
this.imgUrl = file.url;
this.imageList.push(file.url);
this.imgVisible = true;
// this.$refs.preview.dispatchEvent(new MouseEvent('click'));
},
//上传后 删除
imgRemove(file ,type){
this.onChangeImgUrl = '';
var parentObj = new Object ();
parentObj.type = type ;
parentObj.url = '';// 传给父组件方法
this.$emit("getUploadchildData", parentObj);
},
//上传超出提示
handleExceed(files,filelist){
this.$message.error("上传图片不能超过1张!");
},
//上传控件的改变事件 提交到父组件
async changeUpload(file, type){
this.onChangeImgUrl = file.url;
//图片转base64
const response = await getBase64Url(file)
var res = response.result
var parentObj = new Object ();
parentObj.type = type ;
parentObj.url = res;// 传给父组件方法
this.$emit("getUploadchildData", parentObj);
}
}
}
</script>
<style >
.img{
width: 60%;
height: 80;
margin: 0 auto;
display: flex;
justify-content: center;
align-items: center;
}
</style>
util.js图片转base64
// 转base64 el-upload上传的file 不能直接用,要用file.raw
// 文件对象转base64
export function getBase64Url (file) {
return new Promise ((resolve,reject) =>{
const reader = new FileReader(); //实例化文件读取对象
reader.readAsDataURL(file.raw); //将文件读取为 DataURL,也就是base64编码
reader.onload = function () {
resolve(reader)
}
reader.onerror = function (error) {
reject(error)
}
})
}