开发背景:
首先项目是用uniapp写的h5项目,要求能上传、预览、和进度条展示,还要求总览的时候用缩略图,点开预览要原图,(不得不吐槽一下)
开发环境:
uniapp +阿里云存储
先看截图效果:
好了直接上代码
// photo-picker.vue
<template>
<view class="imgs">
<view class="image" v-for="(item, index) in localPhotos" :key="index">
<image class="img" :src="item.path" mode="aspectFill" @click="previewImage(index)"></image>
<view class="del" mode="widthFix" @click="delImg(index)">
<uni-icons color='white' custom-prefix="custom-icon" type="closeempty" size="16"></uni-icons>
</view>
<view class="file-picker__progress" v-if="(item.progress && item.progress !== 100) || item.progress === 0">
<progress class="file-picker__progress-item" :activeColor="'#FF7300'" :percent="item.progress === -1?0:item.progress" stroke-width="12" :backgroundColor="item.errMsg?'#ff5a5f':'#EBEBEB'"/>
</view>
</view>
<!-- 实例用的插槽 -->
<slot name="instance" v-if="localPhotos.length===0"></slot>
<view class="icon_box" @click="uploadImg()" v-show="isShow && photos.length < astrict">
<image class="icon-add" src="/static/images/img_photo_1.png"></image>
<view class="icon-title">上传图片</view>
</view>
</view>
</template>
<script>
import api from '@/static/api.js';
import { recursionCompressH5 } from '@/static/util.js';
export default {
props:{
//原图
photos:{
type: [Array,String],
default: () => [],
// required: true // 必填
},
//缩略图
thumbnailPhotos:{
type: [Array],
default: () => [],
required: true // 必填
},
//限制传多少张
astrict:{
type: Number,
default: 1
}
},
data() {
return {
localPhotos: [],//保存本地图片
isShow:true
}
},
methods: {
//预览图片
previewImage(current = 0) {
uni.previewImage({
current,
urls: this.photos,
loop: true,
indicator: "default",
longPressActions: {
itemList: ['发送给朋友', '保存图片', '收藏'],
success: function (data) {
},
fail: function (err) {
}
}
})
},
//执行回调
callback(file,i){
uni.uploadFile({
url: api.upLoadImg,
filePath: file.path,
name: 'file_img',
success: (uploadFileRes) => {
let data = JSON.parse(uploadFileRes.data);
this.photos[i]=data.data.url;//图片链接放到原图数组里
this.thumbnailPhotos[i] = { path:data.data.thumbnail, progress: 100 }
// this.$emit("onSaveThumbnail",this.thumbnailPhotos)//图片链接放到缩略图数组里
this.localPhotos[i].progress = 100
this.localPhotos[i].errMsg = false
this.$set(this.localPhotos,i,this.localPhotos[i])//本地图片放到存放本地图片的数组里
if (++this.num === this.len) {
this.isShow = true
uni.hideLoading()
this.num = 0
this.len = 0
}
},
fail:(err) => {
this.localPhotos[i].errMsg = true
this.localPhotos[i].progress = -1
this.$set(this.localPhotos,i,this.localPhotos[i])//本地图片放到存放本地图片的数组里
if (++this.num === this.len) {
this.isShow = true
uni.hideLoading()
this.num = 0
this.len = 0
}
}
})
},
//进度条自动前进
autoStep(v,i){
let timer = null
timer = setInterval(() => {
if (v.progress >= 90) {
clearInterval(timer);
}else if(v.progress === -1){
clearInterval(timer);
}else{
v.progress += 5
this.$set(this.localPhotos,i,v)
}
}, 500);
},
//获取图片
uploadImg() {
let sun = this.astrict - this.photos.length;
uni.chooseImage({
count: sun,
sizeType: ['compressed'],
success: (res) => {
this.isShow = false
uni.showLoading({
title: '上传中...',
mask:true
})
this.len =res.tempFilePaths.length
this.num = 0
res.tempFiles.forEach((v, i) => {
v.progress = 0
this.localPhotos.push(v)
})
//走模拟进度条
this.localPhotos.forEach((item,index)=>{
if (item.progress === 0) {
//调用模拟进度条
this.autoStep(item, index)
//调用压缩方法
recursionCompressH5(item,index,this.callback,2)
}
})
},
fail: (err) => {
}
});
},
//删除图片
delImg(index) {
this.photos.splice(index, 1)
this.localPhotos.splice(index, 1)
this.thumbnailPhotos.splice(index, 1)
},
},
watch: {
thumbnailPhotos() {
this.localPhotos = JSON.parse(JSON.stringify(this.thumbnailPhotos))
}
},
}
</script>
<style lang="scss" scoped>
.imgs{
margin-top:10upx;
width: 100%;
display: flex;
align-items: center;
flex-wrap: wrap;
.image{
width: 202upx;
height: 202upx;
border-radius: 16upx;
overflow: hidden;
margin: 0 10upx 10upx 0;
position: relative;
.img{
width: 100%;
height: 100%;
display: block;
}
.del{
background-color: #000;
opacity: 0.5;
width: 44upx;
height: 44upx;
border-radius: 50%;
position: absolute;
top: 4upx;
right: 4upx;
text-align: center;
}
.file-picker__progress {
position: absolute;
bottom: 0;
left: 0;
right: 0;
/* border: 1px red solid; */
z-index: 99;
}
.file-picker__progress-item {
width: 100%;
}
}
.icon_box{
width: 202upx;
height: 202upx;
border-radius: 16upx;
background-color:#F4F4F4;
text-align: center;
// margin-top: -10upx;
.icon-add {
width: 80upx;
height: 80upx;
margin: 40upx 0 5upx;
}
.icon-title {
font-size: 24upx;
font-family: 10pxPingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #999999;
}
}
}
</style>
如何使用呢?
<upload-file class="img_picker" :astrict="9" :photos.sync="formInfo.medical_treatment_photos" :thumbnailPhotos.sync="formInfo.thumbnail_medical_treatment_photos"/>
//photo是原图的数组 如:["http://draftimg.zhongaihuzhu.com/uploads/20220914/6VaNfQGheXlgsWyyoSLWhxWZoUp6Je4jpS5IS0og.jpg","http://draftimg.zhongaihuzhu.com/uploads/20220914/6VaNfQGheXlgsWyyoSLWhxWZoUp6Je4jpS5IS0og.jpg"]
//thumbnailPhotos是缩略图的数组
//astrict是上传图片张数,不传默认一张
//此处使用.sync传参可实现子组件修改父组件的数据,
thumbnailPhotos 缩略图的数组,要特别提醒一下,由于要有进度条效果,所以,该数组格式为
[
{
path:"http://draftimg.zhongaihuzhu.com/uploads/20220914/6VaNfQGheXlgsWyyoSLWhxWZoUp6Je4jpS5IS0og.jpg",
progress:0
}
]