<template> <view class="upload-progress"> <view id="_drag_button" :class="[ 'u-flex', 'suspension', { error: uploadStatus === 3, 'no-active': clicked <= 0 }, ]" @touchstart.capture="touchstart" @touchend.capture="touchend" @touchmove.capture="touchmove" :style="{ left: left + 'px', top: top + 'px', }" @click="openpRrogress" > <view class="progress" v-if="taskCount > 0">{{ uploadPercentage }}%</view> <view class="upload-img" v-else> <image class="icon" :src="icon"></image> </view> </view> <u-popup v-model="open" border-radius="12" mode="center" :animation="false" @close="closePopup" width="690rpx" > <view class="slot-content"> <view class="upload-title"> <text class="status-txt">图片上传进度</text> </view> <view v-if="taskCount > 0"> <view class="progress-circle"> <u-loading class="loading" v-if="!progressStatus" mode="circle" ></u-loading> <u-circle-progress v-if="progressStatus" :width="320" :border-width="20" inactive-color="#F4F5F7" :active-color="progressColor" :percent="uploadPercentage" > <view :class="['u-progress-content', { error: uploadStatus === 3 }]" > {{ uploadPercentage }}%</view > </u-circle-progress> </view> <view class="u-flex upload-data"> <view class="u-flex"> <view class="title">需上传</view> <view class="pending">{{ taskCount }}</view> </view> <view class="u-flex"> <view class="title">已上传</view> <view class="resolve">{{ uploadSuccess }}</view> </view> <view class="u-flex"> <view class="title">状态</view> <view :class="['status', { error: uploadStatus === 3 }]">{{ uploadStatusName }}</view> </view> </view> <view class="upload-bottom" v-if="uploadStatus === 3"> <view class="button confirm" @click="$u.throttle(retry, 500)" >重试</view > </view> </view> <view class="u-flex default" v-if="taskCount === 0"> <view><image class="default-img" :src="defaultImg"></image></view> <view class="text">当前暂无上传图片</view> </view> </view> </u-popup> </view> </template> <script> /** * upload-progress 图片上传进度 */ import upload from '@/components/upload/task.js' const sys = uni.getSystemInfoSync() export default { name: 'upload-progress', props: { // 是否启用自动停靠 isDock: { type: Boolean, default: false, }, // 当前页面是否包含tabbar existTabBar: { type: Boolean, default: false, }, }, data() { return { open: false, progressStatus: false, clicked: 1, top: sys.windowHeight - 80, left: sys.windowWidth - 50, width: 0, height: 0, offsetWidth: 0, offsetHeight: 0, windowWidth: 0, windowHeight: 0, isMove: true, edge: 10, } }, computed: { icon() { console.log(process.uniEnv.qn_base_url + 'upload-photo.png') return process.uniEnv.qn_base_url + 'upload-photo.png' }, defaultImg() { return process.uniEnv.qn_base_url + 'upload-default.png' }, taskCount() { return this.uploadTotal }, uploadStatusName() { let status switch (this.uploadStatus) { case 1: status = '等待上传' break case 2: status = '上传中' break case 3: status = '上传中断' break } return status }, progressColor() { return this.uploadStatus === 3 ? '#FA5A49' : '#2272FF' }, uploadPercentage() { const num = this.uploadSuccess const total = this.uploadTotal return total > 0 ? Math.floor(Math.round((num / total) * 10000) / 100.0) : 0 }, }, watch: { uploadStatus(newValue) { this.progressStatus = false this.$nextTick(() => { this.progressStatus = true }) }, open(val) { if (val == false) { this.changeActive() } }, }, created() { this.changeActive() }, mounted() { this.windowWidth = sys.windowWidth this.windowHeight = sys.windowHeight // #ifdef APP-PLUS this.existTabBar && (this.windowHeight -= 50) // #endif if (sys.windowTop) { this.windowHeight += sys.windowTop } const query = uni.createSelectorQuery().in(this) query .select('#_drag_button') .boundingClientRect((data) => { this.width = data.width this.height = data.height this.offsetWidth = data.width / 2 this.offsetHeight = data.height / 2 // this.left = this.windowWidth - this.width - this.edge // this.top = this.windowHeight - this.height - this.edge }) .exec() }, methods: { closePopup() { this.progressStatus = false }, openpRrogress() { this.clicked += 1 this.open = true this.progressStatus = false setTimeout(() => { this.progressStatus = true }, 300) }, retry() { if (this.uploadStatus !== 2) upload.uploadImageTask(this) }, changeActive() { setTimeout(() => { // 三秒不点击,算失去活跃 this.clicked = Math.max(0, this.clicked - 1) }, 3000) }, touchstart(e) {}, touchend(e) { if (this.isDock) { let edgeRigth = this.windowWidth - this.width - this.edge if (this.left < this.windowWidth / 2 - this.offsetWidth) { this.left = this.edge } else { this.left = edgeRigth } } this.isMove = false }, touchmove(e) { // 单指触摸 if (e.touches.length !== 1) { return false } this.isMove = true let clientY = e.touches[0].clientY - this.offsetHeight // #ifdef H5 clientY += this.height // #endif let edgeBottom = this.windowHeight - this.height - this.edge // 上下触及边界 if (clientY < this.edge) { this.top = this.edge } else if (clientY > edgeBottom) { this.top = edgeBottom } else { this.top = clientY } let clientX = e.touches[0].clientX - this.offsetWidth // #ifdef H5 clientX += this.width // #endif let edgeLeft = this.windowWidth - this.width - this.edge // 左右触及边界 if (clientX < this.edge) { this.left = this.edge } else if (clientX > edgeLeft) { this.left = edgeLeft } else { this.left = clientX } }, }, } </script> <style lang="scss"> .upload-progress { width: 100%; height: 100%; .suspension { position: fixed; width: 80rpx; height: 80rpx; border-radius: 50%; background: #2272ff; z-index: 999; opacity: 1; &.error { background: #fa5a49; } &.no-active { opacity: 0.6; } .progress { font-size: 24rpx; color: #fff; width: 100%; text-align: center; } .upload-img { width: 100%; text-align: center; padding-top: 8rpx; .icon { width: 36rpx; height: 32rpx; } } } .slot-content { padding: 40rpx 30rpx 30rpx 30rpx; .upload-title { margin: 20rpx 0 50rpx 0; display: flex; flex-direction: column; justify-content: center; align-items: center; .status-txt { height: 46rpx; font-size: 40rpx; font-weight: bold; color: #333; line-height: 46rpx; } } .progress-circle { width: 100%; height: 320rpx; text-align: center; .loading { line-height: 320rpx; } .u-progress-content { color: #2272ff; font-size: 60rpx; &.error { color: #fa5a49; } } } .upload-data { margin: 50rpx 0 30rpx 0; .u-flex { flex-flow: column; font-size: 48rpx; width: 210rpx; height: 120rpx; .title { color: #666; font-size: 28rpx; padding-bottom: 20rpx; } .pending { color: #333; } .resolve { color: #2272ff; } .status { color: #2272ff; font-size: 36rpx; &.error { color: #fa5a49; } } } } .upload-bottom { display: flex; justify-content: center; align-items: center; .button { text-align: center; height: 104rpx; width: 300rpx; border-radius: 52rpx; font-size: 32rpx; font-weight: 600; line-height: 104rpx; } .confirm { margin: 36rpx 30rpx 52rpx 15rpx; background: #2272ff; color: #ffffff; } } .default { flex-flow: column; margin: 150rpx 0; .default-img { width: 360rpx; height: 240rpx; } .text { color: #666; font-size: 28rpx; margin-top: 50rpx; } } } } </style>