<template>
<div id="vue-img-uploader">
<input type="file"
accept="image/*"
:id="componentId"
@change="getFile">
<template v-if="files.length>0">
<div class="vue-thumbnail-wrapper" v-for="(img,index) in files" :key="index">
<div class="del-button" @click="deleteImg(img.name,index)">
<img src="@/assets/img/delete.png" alt="">
</div>
<img class="vue-img-thumbnail" :data-index="index" :src="img.imageUrl">
</div>
</template>
<div class="loading" v-show="loading">
<div class="donut"></div>
</div>
<label :for="componentId" v-if="files.length<max && !loading">
<div class="default" v-if="!$slots.addButton">
<img src="@/assets/img/add_img.png" alt="">
</div>
<p v-if="files.length==0" class="default_p">请上传清晰的问题照片,以方便快速解决问题(图片大小2M以内)</p>
</label>
</div>
</template>
<script>
import ImageCompressor from 'image-compressor.js'
export default {
data() {
return {
files: [], // 展示用列表
formData: new FormData(), // 上传用列表
loading: false,
max:9,
maxSize: 2 * 1024 * 1024,//限制上传图片大小为2M
compressQuality:0.6,
autoUpload:true,
initialImg:[],
qiniuDomain: "",
QiniuData: {
token: "",
},
}
},
props: {
componentId:{
type:String,
default:'cid' + Math.floor(Math.random() * 10000),
validator: function (value) {
let hasNotWord = /\W/.test(value)
return !hasNotWord
}
}
},
mounted() {
this.$qiniuUtil.Init(this);
},
watch: {
},
methods: {
async getFile(evt) {
var _this = this;
let file = evt.target.files[0];
let compreeBolb = await _this.imgCompress(file);
let fileName = compreeBolb.name;
if(compreeBolb.size>this.maxSize){
this.$toast({
message:"请上传2M以内图片"
})
return
}
this.loading = true
document.querySelector(`#${this.componentId}`).value = null;
this.$qiniuUtil.uploadFile(compreeBolb, this, function(url){
_this.files.push({"imageUrl": url })
});
// 新增图片加入展示列表
this.$emit('updateImg', this.files);
this.loading = false
},
deleteImg(name, index) {
this.files.splice(index, 1);
this.formData.delete(name);
this.$emit('updateImg', this.files);
},
getDataURL(file) {
return new Promise((res, rej) => {
let reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = evt => res(evt.target.result)
reader.onerror = err => rej(err)
})
},
imgCompress(file) {
return new Promise((res, rej) => {
new ImageCompressor(file, {
quality: this.compressQuality,
success(result) {
res(result)
},
error(err) {
rej(err)
}
})
})
},
}
}
</script>
<style scoped lang="scss">
#vue-img-uploader {
display: flex;
flex-wrap: wrap;
}
.vue-thumbnail-wrapper {
position: relative;
margin-right:0.34rem;
margin-bottom:0.34rem;
border-radius: 8px;
}
.vue-thumbnail-wrapper> img{
display: block;
height: 1.52rem;
width: 1.52rem;
object-fit: cover;
border-radius: 8px;
}
.del-button {
position: absolute;
margin: 5px;
top: -0.06rem;
right:-0.05rem;
border-radius: 50%;
width: 0.5rem;
height: 0.5rem;
line-height: 15px;
text-align: center;
img{
width: 0.5rem;
height: 0.5rem;
}
}
.loading {
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid #ccc;
height: 100px;
width: 100px;
margin-right: 10px;
}
.vue-thumbnail-wrapper:nth-child(4n+1){
margin-right:0;
}
@keyframes donut-spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.donut {
display: inline-block;
border: 4px solid rgba(0, 0, 0, 0.1);
border-left-color: #888;
border-radius: 50%;
width: 30px;
height: 30px;
animation: donut-spin 1.2s linear infinite;
}
label>.default {
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
font-size: 0.3rem;
height: 1.52rem;
width: 1.52rem;
img{
width:100%;
height: 100%;
vertical-align:middle;
}
}
input {
position: absolute;
top: -10rem;
left: 0;
}
.default_p{
font-family: PingFangSC-Regular;
font-size:0.3rem;
width: 4.5rem;
text-align: left;
color: #999999;
display: inline-block;
margin-left:0.3rem;
position: absolute;
top:0.5rem;
left:1.6rem;
}
</style>