1、使用van-uploader
使用van-uploader组件上传图片,并将其封装成组件,接收传入的参数imglist。图片地址为服务器返回的。
完整代码
<template>
<div class="image-uploader">
<div class="list-img" v-for="(src, srcKey) in list" :key="srcKey">
<van-image
:src="src"
width="2.133rem"
height="2.133rem"
@click="previewImage(src, srcKey)"
>
<template v-slot:loading>
<van-loading type="spinner" size="20" />
</template>
<van-icon
name="cross"
size="6"
class="del-icon"
color="#fff"
@click="onDelImage(srcKey)"
/>
</van-image>
</div>
<div class="loading" v-if="isShowLoading">
<div class="loading-icon">
</div>
<div class="loading-text">正在上传</div>
</div>
<van-uploader
class="list-upload"
:after-read="afterRead"
accept="image/*"
:preview-image="false"
multiple
/>
</div>
</template>
<script>
import Compressor from 'compressorjs';
import { ImagePreview, Toast } from 'vant';
import { mapGetters } from "vuex";
import { multipleFiles } from '@/api/work';
export default {
props: {
imglist: Array,
},
data() {
return {
list: [],
isShowLoading: false,
}
},
computed: {
...mapGetters([
"HOST"
]),
},
watch: {
imglist() {
this.list = this.imglist
},
},
mounted() {
this.list = this.imglist;
},
methods: {
beforeRead(file) {
return new Promise((resolve) => {
// compressorjs 默认开启 checkOrientation 选项
// 会将图片修正为正确方向
new Compressor(file, {
success: resolve,
error(err) {
console.log(err.message);
},
});
});
},
afterRead(file) {
console.log("afterRead", file)
this.isShowLoading = true;
// 多文件上传
// let _this = this;
var formData = new FormData();
if (file.constructor != Array) {
formData.append("file", file.file);
} else {
file.forEach(element => {
formData.append("file", element.file);
});
}
multipleFiles(formData).then((res) => {
if (res.data.code == 20000) {
file.status = 'done';
file.message = '';
let list = res.data.data;
list.forEach(element => {
let url = this.HOST + element.afterName;
this.list.push(url);
});
console.log("上传后", this.list)
}
}).finally(() => {
this.isShowLoading = false;
});
},
previewImage(url, urlKey) {
ImagePreview({
// 真实数据
images: this.list,
showIndex: true,
loop: true,
startPosition: urlKey
});
},
//删除图片
onDelImage(index) {
this.list.splice(index, 1);
},
}
}
</script>
<style lang="scss" scoped>
.image-uploader {
width: 100%;
border: 1px dashed rgb(220 223 230);
display: flex;
flex-wrap: wrap;
.list-img {
position: relative;
margin: 5px;
}
.del-icon {
position: absolute;
top: 0;
right: 0;
width: 16px;
height: 16px;
text-align: right;
background-color: rgb(0 0 0 / 70%);
border-radius: 0 0 0 12px;
}
.list-upload {
margin: 5px;
}
}
.loading {
margin: 5px;
width: 80px;
height: 80px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: #c6c7c9;
background: rgb(247 248 250);
margin-right: 5px;
animation-duration: 2s;
.loading-text {
margin-top: 6px;
}
}
.loading-icon {
width: 50px;
height: 50px;
margin: 0 auto;
background-color: #f7f8fa;
}
</style>
主要代码
1、添加正在上传的提示
在图片上传和image显示上传的图片间会存在等待时间,因此加了一个下图所示的正在上传,在调用上传的方法是显示样式,图片接收成功后隐藏样式。(本来还想加一个loading加载的动态效果,奈何太菜没有搞出来)
<div class="loading" v-if="isShowLoading">
<div class="loading-icon">
</div>
<div class="loading-text">正在上传</div>
</div>
2、实现多文件上传(multiple)
官方文档:Vant 2 - Mobile UI Components built on Vue
afterRead(file) {
console.log("afterRead", file)
this.isShowLoading = true;
let _this = this;
var formData = new FormData();
if (file.constructor != Array) {
formData.append("file", file.file);
} else {
file.forEach(element => {
formData.append("file", element.file);
});
}
multipleFiles(formData).then((res) => {
if (res.data.code == 20000) {
let list = res.data.data;
list.forEach(element => {
let url = this.HOST + element.afterName;//根据返回值拼接成需要的图片地址
_this.list.push(url);
});
console.log("上传后", this.list)
}
}).finally(() => {
this.isShowLoading = false;
});
},
开启图片多选功能后,打印一下file值
选择一张图片上传,file值如下图所示,返回值是一个对象
选择两个图片上传,file值是一个数组
因为上传一个图片和多个图片,file的值类型不同,所以在转FormData前先判断一下file是不是数组,如果是数组就用foreach循环
if (file.constructor != Array) {
formData.append("file", file.file);
} else {
file.forEach(element => {
formData.append("file", element.file);
});
}
2、使用原生input
1、用input代替van-uploader
<div class="uploader_box list-upload">
<input
type="file"
id="logimg"
accept="image/*"
multiple="multiple"
@change="change($event)"
/>
<van-icon class="camera-icon" name="photograph" />
</div>
2、上传文件的方法
change(e) {
if (e.target.files.length > 0) {
this.getUrl(e.target.files); //多文件上传
}
},
getUrl(file) {
console.log("getUrl", file)
this.isShowLoading = true;
// 多文件上传
let _this = this;
var formData = new FormData();
// file.forEach无效,使用for( ; ; ;)循环
for (let i = 0; i < file.length; i++) {
formData.append("file", file[i]);
}
let config = {
headers: {
"Content-Type": "multipart/form-data"
}
};
multipleFiles(formData, config).then((res) => {
if (res.data.code == 20000) {
file.status = 'done';
file.message = '';
let list = res.data.data;
list.forEach(element => {
let url = this.HOST + element.afterName;
_this.list.push(url);
});
}
}).catch(error => {
Toast.fail("上传文件接口调用失败")
}).finally(() => {
this.isShowLoading = false;
});
},
上传一个文件时,file的返回值
上传两个文件值,file的返回值
返回值都是一个object对象,就直接使用foreach将文件转成FormData。
3、实现一个跟van-uploader相同的样式
.uploader_box {
width: 80px;
height: 80px;
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
box-sizing: border-box;
margin: 0 8px 8px 0;
// border: 1px dashed rgb(220 223 230);
background-color: rgb(247 248 250);
#logimg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
cursor: pointer;
}
.camera-icon {
color: rgb(220 222 224);
font-size: 24px;
}
}
样式实现效果:
3、在文件中引入组件
<div style="padding: 5px 10px 0 10px">
<p style="color: #646566">答案补充:</p>
<uploader :imglist="data.topic.topicResult.imgSrc" />
</div>
提示:如果出现部分图片能上传,部分图片不能上传,可以看看是不是服务器上设置了图片的大小。