Files
uni-fans-score/pages/waiting/index.vue
T
2025-10-18 09:24:35 +08:00

263 lines
5.5 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="waiting-container">
<view class="title">设备弹出中请稍候</view>
<view class="progress-wrapper">
<view class="progress-circle">
<view class="progress-left">
<view class="progress-bar" :style="{ transform: 'rotate(' + leftRotateDeg + 'deg)' }"></view>
</view>
<view class="progress-right">
<view class="progress-bar" :style="{ transform: 'rotate(' + rightRotateDeg + 'deg)' }"></view>
</view>
<view class="progress-inner">
<text class="percent">{{ progress }}%</text>
<text class="hint">正在为您弹出设备</text>
</view>
</view>
</view>
<view class="desc">若长时间未弹出请联系现场工作人员或稍后重试</view>
</view>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { onLoad, onUnload } from '@dcloudio/uni-app'
import { getOrderByOrderNoScorePayStatus, cancelOrder } from '@/config/api/order.js'
const progress = ref(0)
const leftRotateDeg = ref(0)
const rightRotateDeg = ref(0)
const orderNo = ref('')
let progressTimer = null
let pollTimer = null
let timeoutTimer = null
const updateRotate = (val) => {
const safeVal = Math.max(0, Math.min(100, val))
if (safeVal <= 50) {
rightRotateDeg.value = safeVal * 3.6
leftRotateDeg.value = 0
} else {
rightRotateDeg.value = 180
leftRotateDeg.value = (safeVal - 50) * 3.6
}
}
const startProgress = () => {
// 视觉进度:平滑增长到 97%,等待真实状态成功后拉到 100%
progressTimer = setInterval(() => {
if (progress.value < 97) {
progress.value += 1
updateRotate(progress.value)
}
}, 120)
}
const stopAllTimers = () => {
if (progressTimer) {
clearInterval(progressTimer)
progressTimer = null
}
if (pollTimer) {
clearInterval(pollTimer)
pollTimer = null
}
if (timeoutTimer) {
clearTimeout(timeoutTimer)
timeoutTimer = null
}
}
const handleSuccess = () => {
progress.value = 100
updateRotate(progress.value)
setTimeout(() => {
uni.redirectTo({ url: '/pages/order/index' })
}, 400)
}
const handleFailure = async () => {
try {
if (orderNo.value) {
await cancelOrder({ orderId: orderNo.value })
}
} catch (e) {}
uni.showToast({ title: '设备租借失败,订单已取消', icon: 'none' })
setTimeout(() => {
uni.switchTab({ url: '/pages/index/index' })
}, 800)
}
const startPolling = () => {
pollTimer = setInterval(async () => {
try {
if (!orderNo.value) return
const res = await getOrderByOrderNoScorePayStatus(orderNo.value)
if (res && res.data) {
if (res.data.orderStatus == 'in_used') {
stopAllTimers()
handleSuccess()
} else if (res.data.orderStatus == 'waiting_for_payment') {
stopAllTimers()
handleFailure()
}
}
} catch (e) {
// 网络错误不立即失败,继续轮询
}
}, 1200)
// 超时保护:例如 60 秒
timeoutTimer = setTimeout(() => {
stopAllTimers()
uni.showToast({ title: '等待超时,请稍后重试', icon: 'none' })
setTimeout(() => {
uni.switchTab({ url: '/pages/index/index' })
}, 800)
}, 60000)
}
onLoad((query) => {
if (query && query.orderNo) {
orderNo.value = query.orderNo
}
startProgress()
startPolling()
})
onUnload(() => {
stopAllTimers()
})
onMounted(() => {
// 进入页面时初始化进度显示
updateRotate(progress.value)
})
onUnmounted(() => {
stopAllTimers()
})
</script>
<style lang="scss" scoped>
.waiting-container {
min-height: 100vh;
padding: 60rpx 40rpx;
background: #f5f7fa;
display: flex;
flex-direction: column;
align-items: center;
animation: fade-in 0.25s ease;
}
.title {
font-size: 36rpx;
font-weight: 600;
color: #333;
margin-bottom: 40rpx;
}
.progress-wrapper {
display: flex;
align-items: center;
justify-content: center;
margin: 40rpx 0;
}
.progress-circle {
position: relative;
width: 240rpx;
height: 240rpx;
}
/* 外层灰色轨道 */
.progress-circle::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 50%;
border: 16rpx solid #eaecef;
box-sizing: border-box;
}
/* 两个半圆容器,分别裁剪一半区域 */
.progress-left,
.progress-right {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.progress-right {
clip: rect(0, 240rpx, 240rpx, 120rpx);
}
.progress-left {
clip: rect(0, 120rpx, 240rpx, 0);
}
/* 进度条(整圆),通过旋转配合父容器裁剪形成半圈填充 */
.progress-bar {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 50%;
border: 16rpx solid #07c160;
clip: rect(0, 120rpx, 240rpx, 0);
box-sizing: border-box;
transform: rotate(0deg);
transition: transform 0.12s linear;
}
/* 内层文案区域 */
.progress-inner {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 180rpx;
height: 180rpx;
border-radius: 50%;
background: #ffffff;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
box-shadow: 0 6rpx 24rpx rgba(0, 0, 0, 0.08);
}
.percent {
font-size: 44rpx;
font-weight: 700;
color: #07c160;
}
.hint {
margin-top: 8rpx;
font-size: 22rpx;
color: #999;
}
.desc {
margin-top: 20rpx;
font-size: 26rpx;
color: #666;
text-align: center;
line-height: 1.6;
}
@keyframes fade-in {
from { opacity: 0; transform: translateY(10rpx); }
to { opacity: 1; transform: translateY(0); }
}
</style>