forked from angelo/web-retail-h5
				
			
		
			
	
	
		
			518 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Vue
		
	
	
	
		
		
			
		
	
	
			518 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Vue
		
	
	
	
|  | <template> | |||
|  |   <view class="success-page"> | |||
|  |     <!-- 状态栏占位 --> | |||
|  |     <view class="status-bar"></view> | |||
|  | 
 | |||
|  |     <!-- 成功状态区域 --> | |||
|  |     <view v-show="payStatus === 1" class="success-section"> | |||
|  |       <view class="success-icon-wrapper"> | |||
|  |         <view class="success-icon"> | |||
|  |           <text class="success-checkmark">✓</text> | |||
|  |         </view> | |||
|  |         <view class="success-rings"> | |||
|  |           <view class="ring ring1"></view> | |||
|  |           <view class="ring ring2"></view> | |||
|  |           <view class="ring ring3"></view> | |||
|  |         </view> | |||
|  |       </view> | |||
|  | 
 | |||
|  |       <view class="success-title">{{ paySuccessText }}</view> | |||
|  |       <view v-if="!isRecharge && payStatus === 1" class="success-subtitle" | |||
|  |         >您的订单已支付完成</view | |||
|  |       > | |||
|  |     </view> | |||
|  |     <!-- 查询中状态区域 --> | |||
|  |     <view v-show="payStatus === 0" class="loading-section"> | |||
|  |       <view class="loading-icon-wrapper"> | |||
|  |         <view class="loading-icon"> | |||
|  |           <view class="loading-spinner"> | |||
|  |             <view class="spinner-dot dot1"></view> | |||
|  |             <view class="spinner-dot dot2"></view> | |||
|  |             <view class="spinner-dot dot3"></view> | |||
|  |           </view> | |||
|  |         </view> | |||
|  |         <view class="loading-rings"> | |||
|  |           <view class="loading-ring ring1"></view> | |||
|  |           <view class="loading-ring ring2"></view> | |||
|  |         </view> | |||
|  |       </view> | |||
|  | 
 | |||
|  |       <view class="loading-title">支付查询中...</view> | |||
|  |       <view class="loading-subtitle">请稍等,正在确认您的支付状态</view> | |||
|  |     </view> | |||
|  |     <!-- 操作按钮区域 --> | |||
|  |     <view class="action-section"> | |||
|  |       <view class="button-group"> | |||
|  |         <button | |||
|  |           v-if="!isRecharge" | |||
|  |           class="btn btn-secondary" | |||
|  |           @click="goToOrderList" | |||
|  |         > | |||
|  |           查看订单 | |||
|  |         </button> | |||
|  |         <button class="btn btn-primary" @click="goToHome">返回首页</button> | |||
|  |       </view> | |||
|  |     </view> | |||
|  | 
 | |||
|  |     <successDialog @successClose="successClose" ref="successDialog" /> | |||
|  |   </view> | |||
|  | </template> | |||
|  | 
 | |||
|  | <script> | |||
|  | import { mapGetters } from 'vuex' | |||
|  | import { payStatus, registerInfo } from '@/config/pay.js' | |||
|  | import successDialog from '@/components/successDialog.vue' | |||
|  | 
 | |||
|  | let paySetTimeoutFlag = null | |||
|  | let getRegisterInfoTimeoutFlag = null | |||
|  | export default { | |||
|  |   name: 'PaySuccess', | |||
|  |   components: { | |||
|  |     successDialog, | |||
|  |   }, | |||
|  |   data() { | |||
|  |     return { | |||
|  |       orderCode: '', | |||
|  |       specialArea: null, | |||
|  |       isRecharge: undefined, | |||
|  |       payStatus: 0, | |||
|  |     } | |||
|  |   }, | |||
|  |   computed: { | |||
|  |     ...mapGetters(['userInfo']), | |||
|  |     paySuccessText() { | |||
|  |       if (this.isRecharge === undefined) { | |||
|  |         return '' | |||
|  |       } | |||
|  |       if (this.isRecharge === true) { | |||
|  |         return '充值成功' | |||
|  |       } | |||
|  |       return '支付成功' | |||
|  |     }, | |||
|  |   }, | |||
|  |   onLoad(options) { | |||
|  |     // 获取传递的参数
 | |||
|  |     const extParam = JSON.parse(atob(options.extParam || '{}')) | |||
|  |     console.log(extParam, '..extParam') | |||
|  |     this.specialArea = extParam.specialArea | |||
|  |     this.isRecharge = extParam.isRecharge | |||
|  |     this.orderCode = extParam.orderCode || '' | |||
|  |     if (this.orderCode) { | |||
|  |       setTimeout(() => { | |||
|  |         this.pollingPayStatus(this.orderCode) | |||
|  |       }, 500) | |||
|  |     } | |||
|  |     // 设置导航栏
 | |||
|  |     uni.setNavigationBarTitle({ | |||
|  |       title: '支付成功', | |||
|  |     }) | |||
|  |   }, | |||
|  |   onUnload() { | |||
|  |     clearTimeout(paySetTimeoutFlag) | |||
|  |     clearTimeout(getRegisterInfoTimeoutFlag) | |||
|  |   }, | |||
|  |   methods: { | |||
|  |     getRegisterInfo() { | |||
|  |       registerInfo(this.orderCode).then(res => { | |||
|  |         if (res.data) { | |||
|  |           uni.hideLoading() | |||
|  |           this.$refs.successDialog.showSuccess(res.data) | |||
|  |         } else { | |||
|  |           getRegisterInfoTimeoutFlag = setTimeout(() => { | |||
|  |             this.getRegisterInfo() | |||
|  |           }, 3000) | |||
|  |         } | |||
|  |       }) | |||
|  |     }, | |||
|  |     pollingPayStatus(orderCode) { | |||
|  |       payStatus({ businessCode: orderCode }).then(res => { | |||
|  |         if (res.data == 1) { | |||
|  |           this.payStatus = 1 | |||
|  |           clearTimeout(paySetTimeoutFlag) | |||
|  |           if ([1, 7, 24].includes(Number(this.specialArea))) { | |||
|  |             uni.showLoading({ | |||
|  |               title: '注册信息加载中...', | |||
|  |               mask: false, | |||
|  |             }) | |||
|  |             setTimeout(() => { | |||
|  |               this.getRegisterInfo() | |||
|  |             }, 2000) | |||
|  |           } | |||
|  |         } else { | |||
|  |           paySetTimeoutFlag = setTimeout(() => { | |||
|  |             this.pollingPayStatus(orderCode) | |||
|  |           }, 1000) | |||
|  |         } | |||
|  |       }) | |||
|  |     }, | |||
|  | 
 | |||
|  |     // 跳转到订单列表
 | |||
|  |     goToOrderList() { | |||
|  |       uni.redirectTo({ | |||
|  |         url: '/pages/mine/order/index', | |||
|  |       }) | |||
|  |     }, | |||
|  |     successClose() { | |||
|  |       uni.redirectTo({ | |||
|  |         url: '/pages/mine/order/index', | |||
|  |       }) | |||
|  |     }, | |||
|  | 
 | |||
|  |     // 返回首页
 | |||
|  |     goToHome() { | |||
|  |       uni.switchTab({ | |||
|  |         url: '/pages/index/index', | |||
|  |       }) | |||
|  |     }, | |||
|  |   }, | |||
|  | } | |||
|  | </script> | |||
|  | 
 | |||
|  | <style lang="scss" scoped> | |||
|  | .success-page { | |||
|  |   min-height: 100vh; | |||
|  |   background: linear-gradient(135deg, #f8fafc 0%, #e3f2fd 100%); | |||
|  |   position: relative; | |||
|  |   display: flex; | |||
|  |   flex-direction: column; | |||
|  | } | |||
|  | 
 | |||
|  | .status-bar { | |||
|  |   height: var(--status-bar-height); | |||
|  |   background: transparent; | |||
|  | } | |||
|  | 
 | |||
|  | .success-section { | |||
|  |   flex: 1; | |||
|  |   display: flex; | |||
|  |   flex-direction: column; | |||
|  |   align-items: center; | |||
|  |   justify-content: center; | |||
|  |   padding: 60rpx 40rpx 40rpx; | |||
|  |   position: relative; | |||
|  | } | |||
|  | 
 | |||
|  | .success-icon-wrapper { | |||
|  |   position: relative; | |||
|  |   margin-bottom: 40rpx; | |||
|  | } | |||
|  | 
 | |||
|  | .success-icon { | |||
|  |   width: 120rpx; | |||
|  |   height: 120rpx; | |||
|  |   background: #005bac; | |||
|  |   border-radius: 50%; | |||
|  |   display: flex; | |||
|  |   align-items: center; | |||
|  |   justify-content: center; | |||
|  |   position: relative; | |||
|  |   z-index: 2; | |||
|  |   box-shadow: 0 8rpx 24rpx rgba(0, 91, 172, 0.3); | |||
|  |   animation: successBounce 0.6s ease-out; | |||
|  | } | |||
|  | 
 | |||
|  | .success-checkmark { | |||
|  |   color: #ffffff; | |||
|  |   font-size: 60rpx; | |||
|  |   font-weight: bold; | |||
|  |   line-height: 1; | |||
|  | } | |||
|  | 
 | |||
|  | .success-rings { | |||
|  |   position: absolute; | |||
|  |   top: 50%; | |||
|  |   left: 50%; | |||
|  |   transform: translate(-50%, -50%); | |||
|  |   z-index: 1; | |||
|  | } | |||
|  | 
 | |||
|  | .ring { | |||
|  |   position: absolute; | |||
|  |   border: 2rpx solid #005bac; | |||
|  |   border-radius: 50%; | |||
|  |   opacity: 0; | |||
|  |   top: 50%; | |||
|  |   left: 50%; | |||
|  |   transform: translate(-50%, -50%); | |||
|  | } | |||
|  | 
 | |||
|  | .ring1 { | |||
|  |   width: 140rpx; | |||
|  |   height: 140rpx; | |||
|  |   animation: ripple 1.5s ease-out 0.2s infinite; | |||
|  | } | |||
|  | 
 | |||
|  | .ring2 { | |||
|  |   width: 180rpx; | |||
|  |   height: 180rpx; | |||
|  |   animation: ripple 1.5s ease-out 0.6s infinite; | |||
|  | } | |||
|  | 
 | |||
|  | .ring3 { | |||
|  |   width: 220rpx; | |||
|  |   height: 220rpx; | |||
|  |   animation: ripple 1.5s ease-out 1s infinite; | |||
|  | } | |||
|  | 
 | |||
|  | .success-title { | |||
|  |   font-size: 48rpx; | |||
|  |   font-weight: 600; | |||
|  |   color: #1a1a1a; | |||
|  |   margin-bottom: 16rpx; | |||
|  |   text-align: center; | |||
|  | } | |||
|  | 
 | |||
|  | .success-subtitle { | |||
|  |   font-size: 28rpx; | |||
|  |   color: #666666; | |||
|  |   text-align: center; | |||
|  |   line-height: 1.4; | |||
|  | } | |||
|  | 
 | |||
|  | .action-section { | |||
|  |   padding: 32rpx; | |||
|  | } | |||
|  | 
 | |||
|  | .button-group { | |||
|  |   display: flex; | |||
|  |   gap: 24rpx; | |||
|  | } | |||
|  | 
 | |||
|  | .btn { | |||
|  |   flex: 1; | |||
|  |   height: 88rpx; | |||
|  |   border-radius: 44rpx; | |||
|  |   font-size: 32rpx; | |||
|  |   font-weight: 500; | |||
|  |   border: none; | |||
|  |   transition: all 0.3s ease; | |||
|  |   position: relative; | |||
|  |   overflow: hidden; | |||
|  | 
 | |||
|  |   &::after { | |||
|  |     border: none; | |||
|  |   } | |||
|  | 
 | |||
|  |   &.btn-secondary { | |||
|  |     background: #ffffff; | |||
|  |     color: #005bac; | |||
|  |     border: 2rpx solid #005bac; | |||
|  | 
 | |||
|  |     &:active { | |||
|  |       background: #f8fafc; | |||
|  |       transform: scale(0.98); | |||
|  |     } | |||
|  |   } | |||
|  | 
 | |||
|  |   &.btn-primary { | |||
|  |     background: linear-gradient(135deg, #005bac 0%, #0066cc 100%); | |||
|  |     color: #ffffff; | |||
|  |     box-shadow: 0 4rpx 16rpx rgba(0, 91, 172, 0.3); | |||
|  | 
 | |||
|  |     &:active { | |||
|  |       background: linear-gradient(135deg, #004691 0%, #0052a3 100%); | |||
|  |       transform: scale(0.98); | |||
|  |     } | |||
|  |   } | |||
|  | } | |||
|  | 
 | |||
|  | // 动画效果
 | |||
|  | @keyframes successBounce { | |||
|  |   0% { | |||
|  |     transform: scale(0); | |||
|  |     opacity: 0; | |||
|  |   } | |||
|  |   50% { | |||
|  |     transform: scale(1.2); | |||
|  |     opacity: 1; | |||
|  |   } | |||
|  |   100% { | |||
|  |     transform: scale(1); | |||
|  |     opacity: 1; | |||
|  |   } | |||
|  | } | |||
|  | 
 | |||
|  | @keyframes ripple { | |||
|  |   0% { | |||
|  |     opacity: 0.6; | |||
|  |     transform: translate(-50%, -50%) scale(0.1); | |||
|  |   } | |||
|  |   100% { | |||
|  |     opacity: 0; | |||
|  |     transform: translate(-50%, -50%) scale(1); | |||
|  |   } | |||
|  | } | |||
|  | 
 | |||
|  | .spinner-dot { | |||
|  |   position: relative; | |||
|  |   width: 12rpx; | |||
|  |   height: 12rpx; | |||
|  |   background: #ffffff; | |||
|  |   border-radius: 50%; | |||
|  |   box-shadow: 0 2rpx 4rpx rgba(255, 255, 255, 0.3); | |||
|  | } | |||
|  | 
 | |||
|  | .spinner-dot.dot1 { | |||
|  |   animation: dotBlink 1.4s ease-in-out infinite; | |||
|  | } | |||
|  | 
 | |||
|  | .spinner-dot.dot2 { | |||
|  |   animation: dotBlink 1.4s ease-in-out 0.2s infinite; | |||
|  | } | |||
|  | 
 | |||
|  | .spinner-dot.dot3 { | |||
|  |   animation: dotBlink 1.4s ease-in-out 0.4s infinite; | |||
|  | } | |||
|  | 
 | |||
|  | .loading-section { | |||
|  |   flex: 1; | |||
|  |   display: flex; | |||
|  |   flex-direction: column; | |||
|  |   align-items: center; | |||
|  |   justify-content: center; | |||
|  |   padding: 60rpx 40rpx 40rpx; | |||
|  |   position: relative; | |||
|  | } | |||
|  | 
 | |||
|  | .loading-icon-wrapper { | |||
|  |   position: relative; | |||
|  |   margin-bottom: 40rpx; | |||
|  | } | |||
|  | 
 | |||
|  | .loading-icon { | |||
|  |   width: 120rpx; | |||
|  |   height: 120rpx; | |||
|  |   background: linear-gradient(135deg, #005bac 0%, #0066cc 100%); | |||
|  |   border-radius: 50%; | |||
|  |   display: flex; | |||
|  |   align-items: center; | |||
|  |   justify-content: center; | |||
|  |   position: relative; | |||
|  |   z-index: 2; | |||
|  |   box-shadow: 0 8rpx 32rpx rgba(0, 91, 172, 0.4); | |||
|  | } | |||
|  | 
 | |||
|  | .loading-spinner { | |||
|  |   position: relative; | |||
|  |   width: 80rpx; | |||
|  |   height: 20rpx; | |||
|  |   display: flex; | |||
|  |   align-items: center; | |||
|  |   justify-content: space-between; | |||
|  | } | |||
|  | 
 | |||
|  | .loading-rings { | |||
|  |   position: absolute; | |||
|  |   top: 50%; | |||
|  |   left: 50%; | |||
|  |   transform: translate(-50%, -50%); | |||
|  |   z-index: 1; | |||
|  | } | |||
|  | 
 | |||
|  | .loading-ring { | |||
|  |   position: absolute; | |||
|  |   border: 2rpx solid #005bac; | |||
|  |   border-radius: 50%; | |||
|  |   opacity: 0; | |||
|  |   top: 50%; | |||
|  |   left: 50%; | |||
|  |   transform: translate(-50%, -50%); | |||
|  | } | |||
|  | 
 | |||
|  | .loading-ring.ring1 { | |||
|  |   width: 140rpx; | |||
|  |   height: 140rpx; | |||
|  |   animation: loadingRipple 2s ease-out infinite; | |||
|  | } | |||
|  | 
 | |||
|  | .loading-ring.ring2 { | |||
|  |   width: 180rpx; | |||
|  |   height: 180rpx; | |||
|  |   animation: loadingRipple 2s ease-out 0.8s infinite; | |||
|  | } | |||
|  | 
 | |||
|  | .loading-title { | |||
|  |   font-size: 48rpx; | |||
|  |   font-weight: 600; | |||
|  |   color: #2c3e50; | |||
|  |   margin-bottom: 16rpx; | |||
|  |   text-align: center; | |||
|  |   text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1); | |||
|  | } | |||
|  | 
 | |||
|  | .loading-subtitle { | |||
|  |   font-size: 28rpx; | |||
|  |   color: #7f8c8d; | |||
|  |   text-align: center; | |||
|  |   line-height: 1.4; | |||
|  |   opacity: 0.9; | |||
|  | } | |||
|  | 
 | |||
|  | @keyframes dotBlink { | |||
|  |   0% { | |||
|  |     opacity: 0.4; | |||
|  |   } | |||
|  |   50% { | |||
|  |     opacity: 1; | |||
|  |   } | |||
|  |   100% { | |||
|  |     opacity: 0.4; | |||
|  |   } | |||
|  | } | |||
|  | 
 | |||
|  | @keyframes loadingRipple { | |||
|  |   0% { | |||
|  |     opacity: 0.8; | |||
|  |     transform: translate(-50%, -50%) scale(0.1); | |||
|  |   } | |||
|  |   100% { | |||
|  |     opacity: 0; | |||
|  |     transform: translate(-50%, -50%) scale(1); | |||
|  |   } | |||
|  | } | |||
|  | 
 | |||
|  | // 暗黑模式适配
 | |||
|  | @media (prefers-color-scheme: dark) { | |||
|  |   .success-page { | |||
|  |     background: linear-gradient(135deg, #1a1a1a 0%, #2d3748 100%); | |||
|  |   } | |||
|  | 
 | |||
|  |   .success-title { | |||
|  |     color: #ffffff; | |||
|  |   } | |||
|  | 
 | |||
|  |   .success-subtitle { | |||
|  |     color: #a0aec0; | |||
|  |   } | |||
|  | 
 | |||
|  |   .loading-title { | |||
|  |     color: #ffffff; | |||
|  |   } | |||
|  | 
 | |||
|  |   .loading-subtitle { | |||
|  |     color: #a0aec0; | |||
|  |   } | |||
|  | } | |||
|  | 
 | |||
|  | // 适配不同屏幕尺寸
 | |||
|  | @media screen and (max-width: 375px) { | |||
|  |   .success-icon { | |||
|  |     width: 100rpx; | |||
|  |     height: 100rpx; | |||
|  |   } | |||
|  | 
 | |||
|  |   .success-checkmark { | |||
|  |     font-size: 50rpx; | |||
|  |   } | |||
|  | 
 | |||
|  |   .success-title { | |||
|  |     font-size: 42rpx; | |||
|  |   } | |||
|  | 
 | |||
|  |   .button-group { | |||
|  |     flex-direction: column; | |||
|  |     gap: 16rpx; | |||
|  |   } | |||
|  | } | |||
|  | </style> |