170 lines
6.6 KiB
Vue
170 lines
6.6 KiB
Vue
<template>
|
||
<view class="contentStyle">
|
||
<scroll-view class="page" scroll-y="true">
|
||
<view class="container">
|
||
<view v-if="currentStep === 2" class="container">
|
||
<view class="success-icon"><text class="icon-check">✓</text></view>
|
||
<view class="title">缴费成功</view>
|
||
<view class="desc">成功缴纳{{ paymentItem.type || '' }}费用</view>
|
||
</view>
|
||
<view class="container" v-if="currentStep === 3">
|
||
<view class="fail-icon"><text class="icon-fail">✕</text></view>
|
||
<view class="title">缴费失败</view>
|
||
<view class="desc">未能完成缴费,请重新尝试</view>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
|
||
<view class="btn-section" v-if="currentStep === 1">
|
||
<button class="pay-btn" @click="handlePay">立即缴费</button>
|
||
</view>
|
||
<view class="btn-section" v-if="currentStep === 2">
|
||
<button class="pay-btn" open-type="launchApp" app-parameter="payResult=success" @click="handleBackToApp" @error="handleLaunchAppError">
|
||
返回
|
||
</button>
|
||
</view>
|
||
<view class="btn-section" v-if="currentStep === 3">
|
||
<!-- <button class="retry-btn" @click="handleRetry">重新缴费</button> -->
|
||
<button class="pay-btn" open-type="launchApp" app-parameter="payResult=fail" @click="handleBackToApp" @error="handleLaunchAppError">
|
||
返回
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, reactive } from 'vue'
|
||
import { onShow, onLoad } from '@dcloudio/uni-app'
|
||
import { getAppPaymentData, getAppQueryParams, clearAppPaymentData, buildPaymentItemFromApp, getPaymentReturnResult, clearPaymentReturnResult, resolvePaymentResult, clearPaymentItem, clearPaymentCache } from '/pageSubPack/api/appLaunch.js'
|
||
import { getBankMiniProgramParams } from '/pageSubPack/api/apiSub.js'
|
||
import { showApiError } from '/pageSubPack/api/request.js'
|
||
|
||
const paymentItem = reactive({})
|
||
const currentStep = ref(1)
|
||
const navigatedToPayment = ref(false)
|
||
|
||
onLoad(() => {
|
||
loadPaymentItem()
|
||
})
|
||
|
||
onShow(() => {
|
||
// 从日照银行支付小程序返回后,根据返回结果显示成功/失败页。
|
||
// 必须放在最前面判断:本页发起过支付跳转(navigatedToPayment)时,缓存里可能
|
||
// 残留兜底写入的 App 参数,若先走下面的 App 入口分支会被重置回待缴费状态,
|
||
// 成功/失败页就永远不显示了。
|
||
if (navigatedToPayment.value) {
|
||
navigatedToPayment.value = false
|
||
const result = resolvePaymentResult(getPaymentReturnResult())
|
||
clearPaymentReturnResult()
|
||
clearAppPaymentData()
|
||
currentStep.value = result === 'success' ? 2 : 3
|
||
return
|
||
}
|
||
// 再次从 App 跳转进入(携带新的缴费参数):小程序热启动时 onLoad 不会重复执行,
|
||
// 页面会残留上一次的金额/状态,这里必须重置后重新加载。
|
||
if (getAppPaymentData()) {
|
||
resetPaymentState()
|
||
loadPaymentItem()
|
||
}
|
||
})
|
||
|
||
/** 重置为待缴费状态,并清掉上一次缴费残留的页面状态与缓存条目 */
|
||
const resetPaymentState = () => {
|
||
navigatedToPayment.value = false
|
||
currentStep.value = 1
|
||
clearPaymentReturnResult()
|
||
clearPaymentItem()
|
||
Object.keys(paymentItem).forEach(key => delete paymentItem[key])
|
||
}
|
||
|
||
const loadPaymentItem = () => {
|
||
Object.keys(paymentItem).forEach(key => delete paymentItem[key])
|
||
const appData = getAppPaymentData()
|
||
if (appData) {
|
||
console.log('-----检测到 App 跳转参数:', appData)
|
||
clearAppPaymentData()
|
||
const item = buildPaymentItemFromApp(appData)
|
||
uni.setStorageSync('arrearsPayment', item)
|
||
Object.assign(paymentItem, item)
|
||
return item
|
||
}
|
||
const stored = uni.getStorageSync('arrearsPayment')
|
||
const data = (typeof stored === 'object' && stored !== null) ? stored : {}
|
||
Object.assign(paymentItem, data)
|
||
return data
|
||
}
|
||
|
||
const handlePay = async () => {
|
||
const apiParams = getAppQueryParams()
|
||
if (!apiParams) {
|
||
uni.showToast({ title: '支付参数缺失', icon: 'none' })
|
||
return
|
||
}
|
||
delete apiParams.token
|
||
delete apiParams.billId // billId 已归一化为 billIds 数组,删除冗余字段
|
||
console.log('-----传递小程序参数2222------:', apiParams)
|
||
uni.showLoading({ title: '加载中...', mask: true })
|
||
try {
|
||
const res = await getBankMiniProgramParams(apiParams)
|
||
uni.hideLoading()
|
||
if (res.code !== 200 || !res.data) {
|
||
uni.showToast({ title: res.msg || '获取支付参数失败', icon: 'none' })
|
||
return
|
||
}
|
||
navigatedToPayment.value = true
|
||
clearPaymentReturnResult()
|
||
wx.navigateToMiniProgram({
|
||
appId: 'wxc0189436c58b59cd',
|
||
path: 'pages/pay/pay',
|
||
extraData: res.data.extraData,
|
||
envVersion: 'trial',
|
||
success: () => {
|
||
console.log('已跳转到日照银行支付小程序')
|
||
},
|
||
fail: (err) => {
|
||
navigatedToPayment.value = false
|
||
console.error('跳转日照银行支付失败', err)
|
||
uni.showToast({ title: '拉起支付失败', icon: 'none' })
|
||
}
|
||
})
|
||
} catch (err) {
|
||
uni.hideLoading()
|
||
showApiError(err)
|
||
console.error('支付失败:', err)
|
||
}
|
||
}
|
||
|
||
/** 返回 App 前清掉本次缴费缓存,下次从 App 进入时不会残留上一次的金额 */
|
||
const handleBackToApp = () => {
|
||
navigatedToPayment.value = false
|
||
clearPaymentCache()
|
||
}
|
||
|
||
const handleLaunchAppError = () => {
|
||
uni.showToast({ title: '请点击左上角关闭返回', icon: 'none', duration: 2000 })
|
||
}
|
||
|
||
// const handleRetry = () => {
|
||
// currentStep.value = 1
|
||
// }
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
page { background-color: #f9f9f9; }
|
||
</style>
|
||
|
||
<style scoped>
|
||
.contentStyle { display: flex; flex-direction: column; height: 100vh; background-color: #f9f9f9; }
|
||
.page { flex: 1; overflow-y: auto; padding: 0 40rpx; box-sizing: border-box; }
|
||
.btn-section { padding: 20rpx 30rpx 40rpx; }
|
||
.pay-btn { width: 100%; height: 100rpx; line-height: 100rpx; background-color: #007aff; color: #fff; border-radius: 16rpx; font-size: 32rpx; border: none; }
|
||
.container { display: flex; flex-direction: column; align-items: center; }
|
||
.success-icon { width: 220rpx; height: 220rpx; border-radius: 50%; background-color: #1677ff; display: flex; align-items: center; justify-content: center; margin-top: 200rpx; margin-bottom: 60rpx; }
|
||
.icon-check { color: #fff; font-size: 130rpx; font-weight: bold; }
|
||
.fail-icon { width: 220rpx; height: 220rpx; border-radius: 50%; background-color: #fa5151; display: flex; align-items: center; justify-content: center; margin-top: 200rpx; margin-bottom: 60rpx; }
|
||
.icon-fail { color: #fff; font-size: 130rpx; font-weight: bold; }
|
||
.retry-btn { width: 100%; height: 100rpx; line-height: 100rpx; background-color: #fff; color: #007aff; border: 2rpx solid #007aff; border-radius: 16rpx; font-size: 32rpx; margin-bottom: 20rpx; }
|
||
.title { font-size: 40rpx; font-weight: bold; color: #000; margin-bottom: 20rpx; }
|
||
.desc { font-size: 32rpx; color: #666; text-align: center; margin-bottom: 150rpx; }
|
||
</style>
|