新增h5qrcode依赖
This commit is contained in:
+201
-231
@@ -1,19 +1,23 @@
|
||||
<template>
|
||||
<view class="payment-container">
|
||||
<!-- 订单状态 -->
|
||||
<view class="status-card">
|
||||
<view class="status-icon-wrapper">
|
||||
<view class="status-icon" :class="orderStatus.class">
|
||||
<text class="icon-text">💳</text>
|
||||
</view>
|
||||
<!-- 地点信息卡片 -->
|
||||
<view class="location-card">
|
||||
<view class="location-header">
|
||||
<!-- <view class="location-icon">📍</view> -->
|
||||
<image src="@/static/device_location.png" mode="aspectFit" class="location-icon"></image>
|
||||
<text class="location-name">{{ locationName }}</text>
|
||||
<view class="status-badge">{{ $t('device.available') }}</view>
|
||||
</view>
|
||||
<view class="device-info">
|
||||
<text class="device-label">{{ $t('order.deviceNo') }}:</text>
|
||||
<text class="device-value">{{ orderInfo.deviceNo || '-' }}</text>
|
||||
</view>
|
||||
<view class="status-text">{{ orderStatus.text }}</view>
|
||||
<view class="status-desc">{{ orderStatus.desc }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单信息 -->
|
||||
<!-- 订单信息和费用信息 -->
|
||||
<view class="order-card">
|
||||
<view class="card-header">
|
||||
<view class="card-title-bar"></view>
|
||||
<view class="card-title">{{ $t('payment.orderInfo') }}</view>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
@@ -28,52 +32,35 @@
|
||||
<text class="label">{{ $t('payment.createTime') }}</text>
|
||||
<text class="value">{{ orderInfo.createTime || '-' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 费用信息 -->
|
||||
<view class="price-card">
|
||||
<view class="card-header">
|
||||
<!-- 费用信息部分 -->
|
||||
<view class="card-header" style="margin-top: 32rpx;">
|
||||
<view class="card-title-bar"></view>
|
||||
<view class="card-title">{{ $t('payment.feeInfo') }}</view>
|
||||
</view>
|
||||
<view class="price-item">
|
||||
<text class="label">{{ $t('payment.deposit') }}</text>
|
||||
<text class="value">¥{{ orderInfo.deposit || '99.00' }}</text>
|
||||
<text class="value">¥ {{ orderInfo.deposit || '90' }}</text>
|
||||
</view>
|
||||
<!-- <view class="price-divider"></view> -->
|
||||
<!-- <view class="price-item">
|
||||
<text class="label">{{ $t('payment.package') }}</text>
|
||||
<text class="value">{{ packageText }}</text>
|
||||
</view> -->
|
||||
<view class="price-item total">
|
||||
<text class="label">{{ $t('payment.total') }}</text>
|
||||
<text class="value">¥{{ totalAmount }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 支付方式选择 -->
|
||||
<view class="payment-methods" v-if="paymentMethods.length > 0">
|
||||
<view class="card-header">
|
||||
<view class="card-title">支付方式</view>
|
||||
</view>
|
||||
<view class="method-item" v-for="method in paymentMethods" :key="method?.paymentMethodType"
|
||||
:class="{ active: selectedPaymentMethod === method?.paymentMethodType }"
|
||||
@click="selectPaymentMethod(method?.paymentMethodType)">
|
||||
<view class="method-info">
|
||||
<view class="method-icon">
|
||||
<image :src="method?.logo?.logoUrl" mode="aspectFit" style="width: 48rpx; height: 48rpx;">
|
||||
</image>
|
||||
</view>
|
||||
<text class="method-name">{{ method?.logo?.logoName }}</text>
|
||||
</view>
|
||||
<view class="method-radio" :class="{ checked: selectedPaymentMethod === method?.paymentMethodType }">
|
||||
<view class="total-value">
|
||||
<text class="currency">¥</text>
|
||||
<text class="amount">{{ totalAmount }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<view class="bottom-bar">
|
||||
<view class="total-amount">
|
||||
<text class="label-text">{{ $t('payment.total') }}:</text>
|
||||
<text class="amount">¥{{ totalAmount }}</text>
|
||||
</view>
|
||||
<view class="pay-btn" @click="handlePayment">
|
||||
<text>{{ $t('payment.payNow') }}</text>
|
||||
<text class="currency-small">¥</text>
|
||||
<text class="amount-large">{{ totalAmount }}</text>
|
||||
<text class="pay-text">{{ $t('payment.payNow') }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -116,10 +103,23 @@
|
||||
const passedTotalAmount = ref(null)
|
||||
const passedDepositAmount = ref(null)
|
||||
|
||||
// 倒计时相关
|
||||
const countdown = ref(15 * 60) // 15分钟 = 900秒
|
||||
let countdownTimer = null
|
||||
|
||||
// 支付方式相关
|
||||
const paymentMethods = ref([])
|
||||
const selectedPaymentMethod = ref('ALIPAY') // 默认选择支付宝
|
||||
|
||||
// 地点名称(可以从设备信息中获取,这里先用默认值)
|
||||
const locationName = ref('澎创办公室')
|
||||
|
||||
// 套餐文本(可以从设备信息中获取,这里先用默认值)
|
||||
const packageText = computed(() => {
|
||||
// 这里可以根据实际的设备信息动态生成
|
||||
return '2元/小时'
|
||||
})
|
||||
|
||||
const orderStatus = reactive({
|
||||
get text() {
|
||||
return t('payment.waitingForPayment')
|
||||
@@ -191,11 +191,17 @@
|
||||
deviceNo: orderData.deviceNo,
|
||||
createTime: formattedTime,
|
||||
deposit: passedDepositAmount.value || orderData.depositAmount || '99.00',
|
||||
orderStatus:orderData.orderStatus
|
||||
}
|
||||
|
||||
deviceNo.value = orderData.deviceNo;
|
||||
await loadDeviceInfo();
|
||||
await loadPaymentMethods();
|
||||
// #ifdef H5
|
||||
if(orderInfo.value.orderStatus=='waiting_for_payment'){
|
||||
startPaymentStatusPolling();
|
||||
}
|
||||
// #endif
|
||||
} else {
|
||||
throw new Error(t('order.getOrderFailed'))
|
||||
}
|
||||
@@ -322,12 +328,12 @@
|
||||
uni.hideLoading();
|
||||
// #ifdef H5
|
||||
uni.setStorageSync('pendingPaymentNo', orderId.value);
|
||||
// #endif
|
||||
|
||||
// 跳转到支付页面
|
||||
uni.navigateTo({
|
||||
url: `/pages/webview/index?url=${encodeURIComponent(paymentUrl)}&title=支付`
|
||||
});
|
||||
// uni.navigateTo({
|
||||
// url: `/pages/webview/index?url=${encodeURIComponent(paymentUrl)}&title=支付`
|
||||
// });
|
||||
window.open(paymentUrl);
|
||||
// #endif
|
||||
|
||||
// 开始轮询支付状态
|
||||
startPaymentStatusPolling();
|
||||
@@ -409,12 +415,56 @@
|
||||
}, 5000); // 每5秒查询一次
|
||||
}
|
||||
|
||||
// 更新导航栏倒计时
|
||||
const updateNavBarCountdown = () => {
|
||||
const minutes = Math.floor(countdown.value / 60).toString().padStart(2, '0')
|
||||
const seconds = (countdown.value % 60).toString().padStart(2, '0')
|
||||
uni.setNavigationBarTitle({
|
||||
title: `待支付 ${minutes}:${seconds}`
|
||||
})
|
||||
}
|
||||
|
||||
// 开始倒计时
|
||||
const startCountdown = () => {
|
||||
// 清除之前的定时器
|
||||
if (countdownTimer) {
|
||||
clearInterval(countdownTimer)
|
||||
}
|
||||
|
||||
// 立即更新一次
|
||||
updateNavBarCountdown()
|
||||
|
||||
// 每秒更新
|
||||
countdownTimer = setInterval(() => {
|
||||
countdown.value--
|
||||
|
||||
if (countdown.value <= 0) {
|
||||
clearInterval(countdownTimer)
|
||||
uni.showToast({
|
||||
title: t('order.paymentFailedRetry'),
|
||||
icon: 'none'
|
||||
})
|
||||
setTimeout(() => {
|
||||
uni.switchTab({
|
||||
url: '/pages/index/index'
|
||||
})
|
||||
}, 1500)
|
||||
return
|
||||
}
|
||||
|
||||
updateNavBarCountdown()
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
// 页面卸载时清除定时器
|
||||
onMounted(() => {
|
||||
return () => {
|
||||
if (pollingTimer) {
|
||||
clearInterval(pollingTimer);
|
||||
}
|
||||
if (countdownTimer) {
|
||||
clearInterval(countdownTimer);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -430,9 +480,8 @@
|
||||
}
|
||||
|
||||
onLoad((options) => {
|
||||
uni.setNavigationBarTitle({
|
||||
title: t('payment.orderPayment')
|
||||
})
|
||||
// 启动倒计时
|
||||
startCountdown()
|
||||
|
||||
// 优先从 options 中获取 orderId
|
||||
if (options && options.orderId) {
|
||||
@@ -470,7 +519,7 @@
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
|
||||
loadOrderInfo()
|
||||
// 调用 loadOrderInfo,内部会再次检查 orderId 是否存在
|
||||
|
||||
})
|
||||
@@ -479,78 +528,81 @@
|
||||
<style lang="scss" scoped>
|
||||
.payment-container {
|
||||
min-height: 100vh;
|
||||
background: #f7f8fa;
|
||||
padding: 30rpx;
|
||||
padding-bottom: 180rpx;
|
||||
background: #F5F5F5;
|
||||
padding: 24rpx;
|
||||
padding-bottom: 200rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
.status-card {
|
||||
.location-card {
|
||||
background: #fff;
|
||||
border-radius: 20rpx;
|
||||
padding: 40rpx 30rpx;
|
||||
margin-bottom: 20rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
|
||||
border-radius: 24rpx;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.status-icon-wrapper {
|
||||
margin-bottom: 20rpx;
|
||||
.location-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.status-icon {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.location-icon {
|
||||
font-size: 40rpx;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.icon-text {
|
||||
font-size: 60rpx;
|
||||
}
|
||||
.location-name {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&.waiting {
|
||||
background: #FFF9C4;
|
||||
}
|
||||
|
||||
&.success {
|
||||
background: #E8F5E9;
|
||||
}
|
||||
|
||||
&.failed {
|
||||
background: #FFEBEE;
|
||||
}
|
||||
.status-badge {
|
||||
background: #D4F4DD;
|
||||
color: #52C41A;
|
||||
font-size: 24rpx;
|
||||
padding: 8rpx 20rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.status-desc {
|
||||
.device-info {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
color: #666;
|
||||
|
||||
.device-label {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.device-value {
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.order-card,
|
||||
.price-card,
|
||||
.payment-methods {
|
||||
.order-card {
|
||||
background: #fff;
|
||||
border-radius: 20rpx;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
|
||||
border-radius: 24rpx;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.card-header {
|
||||
margin-bottom: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 32rpx;
|
||||
|
||||
.card-title-bar {
|
||||
width: 8rpx;
|
||||
height: 32rpx;
|
||||
background: #52C41A;
|
||||
border-radius: 4rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
@@ -560,16 +612,11 @@
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 16rpx 0;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
padding: 24rpx 0;
|
||||
|
||||
.label {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.value {
|
||||
@@ -579,108 +626,34 @@
|
||||
}
|
||||
}
|
||||
|
||||
.price-divider {
|
||||
height: 1rpx;
|
||||
background: #f0f0f0;
|
||||
margin: 10rpx 0;
|
||||
}
|
||||
|
||||
.price-item.total {
|
||||
margin-top: 10rpx;
|
||||
padding-top: 20rpx;
|
||||
border-top: none;
|
||||
padding-top: 32rpx;
|
||||
justify-content: flex-end !important;
|
||||
// border-top: 1rpx solid #F0F0F0;
|
||||
margin-top: 16rpx;
|
||||
|
||||
.label {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
color: #666;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 48rpx;
|
||||
font-weight: bold;
|
||||
color: #ff6b6b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.payment-methods {
|
||||
.method-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 24rpx 20rpx;
|
||||
margin-bottom: 16rpx;
|
||||
background: #f7f8fa;
|
||||
border-radius: 12rpx;
|
||||
border: 2rpx solid transparent;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: #E8F5E9;
|
||||
border-color: #07c160;
|
||||
}
|
||||
|
||||
.method-info {
|
||||
.total-value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-items: baseline;
|
||||
|
||||
.method-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
margin-right: 16rpx;
|
||||
border-radius: 8rpx;
|
||||
|
||||
&.alipay {
|
||||
background: #1677FF;
|
||||
}
|
||||
|
||||
&.wechat {
|
||||
background: #07C160;
|
||||
}
|
||||
|
||||
&.default {
|
||||
background: #999;
|
||||
}
|
||||
.currency {
|
||||
font-size: 22rpx;
|
||||
color: #52C41A;
|
||||
margin-right: 4rpx;
|
||||
}
|
||||
|
||||
.method-name {
|
||||
font-size: 30rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
.amount {
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #52C41A;
|
||||
}
|
||||
}
|
||||
|
||||
.method-radio {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border: 2rpx solid #ddd;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
|
||||
&.checked {
|
||||
border-color: #07c160;
|
||||
background: #07c160;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
background: #fff;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -690,47 +663,44 @@
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: #fff;
|
||||
padding: 20rpx 30rpx;
|
||||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||
z-index: 10;
|
||||
gap: 20rpx;
|
||||
|
||||
.total-amount {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
|
||||
.label-text {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.amount {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #ff6b6b;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
}
|
||||
padding: 24rpx;
|
||||
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
|
||||
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.06);
|
||||
z-index: 100;
|
||||
|
||||
.pay-btn {
|
||||
height: 88rpx;
|
||||
width: 100%;
|
||||
padding: 20rpx 0;
|
||||
// height: 96rpx;
|
||||
background: linear-gradient(135deg, #52C41A 0%, #73D13D 100%);
|
||||
border-radius: 48rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #07c160;
|
||||
color: #fff;
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
padding: 0 60rpx;
|
||||
border-radius: 44rpx;
|
||||
border: none;
|
||||
box-shadow: 0 8rpx 24rpx rgba(82, 196, 26, 0.3);
|
||||
|
||||
.currency-small {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
margin-right: 4rpx;
|
||||
// text-align: ;
|
||||
}
|
||||
|
||||
.amount-large {
|
||||
font-size: 52rpx;
|
||||
font-weight: 700;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.pay-text {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&:active {
|
||||
opacity: 0.8;
|
||||
opacity: 0.9;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user