322 lines
6.0 KiB
Vue
322 lines
6.0 KiB
Vue
<template>
|
|
<view class="checkout-page">
|
|
<view class="checkout-sheet">
|
|
<view class="countdown-pill">
|
|
<view class="countdown-pill-inner">
|
|
<text class="countdown-label">支付剩余时间</text>
|
|
<text class="countdown-value">{{ countdownText }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="amount-block">
|
|
<text class="amount-currency">¥</text>
|
|
<text class="amount-value">{{ order.amount }}</text>
|
|
</view>
|
|
<view class="merchant-name">{{ order.merchantName }}</view>
|
|
|
|
<view class="info-card">
|
|
<view class="info-row">
|
|
<text class="info-label">备注信息</text>
|
|
<input
|
|
v-model="remark"
|
|
class="info-input"
|
|
placeholder="点击填写订单备注"
|
|
placeholder-class="info-placeholder"
|
|
:disabled="paying"
|
|
/>
|
|
</view>
|
|
<view class="info-row">
|
|
<text class="info-label">订单信息</text>
|
|
<text class="info-value">{{ order.orderDesc }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="pay-method-card">
|
|
<view class="pay-method-title">支付方式</view>
|
|
<view class="pay-method-item">
|
|
<view class="pay-method-left">
|
|
<view class="wx-icon">
|
|
<text class="ri-wechat-pay-fill"></text>
|
|
</view>
|
|
<text class="pay-method-name">微信支付</text>
|
|
</view>
|
|
<text class="ri-checkbox-circle-fill pay-method-check"></text>
|
|
</view>
|
|
<view class="pay-method-tip">支付完成后,您需手动切回原应用</view>
|
|
</view>
|
|
|
|
<button
|
|
class="pay-button"
|
|
hover-class="pay-button-hover"
|
|
:loading="paying"
|
|
:disabled="expired"
|
|
@tap="handlePay"
|
|
>
|
|
确认支付 ¥{{ order.amount }}
|
|
</button>
|
|
|
|
<view class="provider-footer">卓享汇收银台 提供服务</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, onUnmounted, ref } from 'vue';
|
|
import { onLoad } from '@dcloudio/uni-app';
|
|
import { buildMockOrderFromOptions, MOCK_PAY_CHECKOUT_ORDER, type PayCheckoutOrder } from '@/mock/pay-checkout';
|
|
|
|
const order = ref<PayCheckoutOrder>({ ...MOCK_PAY_CHECKOUT_ORDER });
|
|
const remark = ref('');
|
|
const paying = ref(false);
|
|
const remainSeconds = ref(0);
|
|
let countdownTimer: ReturnType<typeof setInterval> | null = null;
|
|
|
|
const expired = computed(() => remainSeconds.value <= 0);
|
|
const countdownText = computed(() => {
|
|
const minutes = String(Math.floor(remainSeconds.value / 60)).padStart(2, '0');
|
|
const seconds = String(remainSeconds.value % 60).padStart(2, '0');
|
|
return `${minutes}:${seconds}`;
|
|
});
|
|
|
|
const clearCountdown = () => {
|
|
if (countdownTimer) {
|
|
clearInterval(countdownTimer);
|
|
countdownTimer = null;
|
|
}
|
|
};
|
|
|
|
const startCountdown = (expireAt: number) => {
|
|
clearCountdown();
|
|
const tick = () => {
|
|
const next = Math.max(0, Math.floor((expireAt - Date.now()) / 1000));
|
|
remainSeconds.value = next;
|
|
if (next <= 0) {
|
|
clearCountdown();
|
|
}
|
|
};
|
|
tick();
|
|
countdownTimer = setInterval(tick, 1000);
|
|
};
|
|
|
|
const handlePay = () => {
|
|
if (paying.value || expired.value) return;
|
|
paying.value = true;
|
|
setTimeout(() => {
|
|
paying.value = false;
|
|
uni.showToast({
|
|
title: '支付接口待对接',
|
|
icon: 'none'
|
|
});
|
|
}, 500);
|
|
};
|
|
|
|
onLoad((options: any) => {
|
|
order.value = buildMockOrderFromOptions(options || {});
|
|
remark.value = order.value.remark;
|
|
startCountdown(order.value.expireAt);
|
|
});
|
|
|
|
onUnmounted(() => {
|
|
clearCountdown();
|
|
});
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.checkout-page {
|
|
box-sizing: border-box;
|
|
min-height: 100vh;
|
|
padding: 24rpx 0 calc(32rpx + env(safe-area-inset-bottom));
|
|
background: #f3f4f6;
|
|
}
|
|
|
|
.checkout-sheet {
|
|
padding: 16rpx 32rpx 0;
|
|
}
|
|
|
|
.countdown-pill {
|
|
display: flex;
|
|
justify-content: center;
|
|
margin-bottom: 40rpx;
|
|
}
|
|
|
|
.countdown-pill-inner {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 12rpx;
|
|
padding: 10rpx 24rpx;
|
|
border-radius: 999rpx;
|
|
background: #ffffff;
|
|
box-shadow: 0 4rpx 16rpx rgba(17, 24, 39, 0.06);
|
|
}
|
|
|
|
.countdown-label,
|
|
.countdown-value {
|
|
color: #6b7280;
|
|
font-size: 24rpx;
|
|
line-height: 1.2;
|
|
}
|
|
|
|
.countdown-value {
|
|
color: #111827;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.amount-block {
|
|
display: flex;
|
|
align-items: baseline;
|
|
justify-content: center;
|
|
}
|
|
|
|
.amount-currency {
|
|
margin-right: 8rpx;
|
|
color: #111827;
|
|
font-size: 44rpx;
|
|
font-weight: 700;
|
|
line-height: 1;
|
|
}
|
|
|
|
.amount-value {
|
|
color: #111827;
|
|
font-size: 72rpx;
|
|
font-weight: 800;
|
|
line-height: 1;
|
|
}
|
|
|
|
.merchant-name {
|
|
margin-top: 20rpx;
|
|
text-align: center;
|
|
color: #6b7280;
|
|
font-size: 28rpx;
|
|
}
|
|
|
|
.info-card,
|
|
.pay-method-card {
|
|
margin-top: 40rpx;
|
|
border-radius: 16rpx;
|
|
background: #ffffff;
|
|
}
|
|
|
|
.info-card {
|
|
padding: 4rpx 0;
|
|
}
|
|
|
|
.info-row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 24rpx;
|
|
padding: 28rpx 28rpx;
|
|
border-bottom: 1rpx solid #f0f2f5;
|
|
}
|
|
|
|
.info-row:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.info-label {
|
|
flex-shrink: 0;
|
|
color: #6b7280;
|
|
font-size: 28rpx;
|
|
}
|
|
|
|
.info-input,
|
|
.info-value {
|
|
flex: 1;
|
|
color: #111827;
|
|
font-size: 28rpx;
|
|
text-align: right;
|
|
}
|
|
|
|
.info-placeholder {
|
|
color: #c2c7d0;
|
|
}
|
|
|
|
.pay-method-card {
|
|
padding: 28rpx 28rpx 24rpx;
|
|
}
|
|
|
|
.pay-method-title {
|
|
margin-bottom: 20rpx;
|
|
color: #6b7280;
|
|
font-size: 28rpx;
|
|
}
|
|
|
|
.pay-method-item {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.pay-method-left {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 16rpx;
|
|
}
|
|
|
|
.wx-icon {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 48rpx;
|
|
height: 48rpx;
|
|
border-radius: 50%;
|
|
background: #07c160;
|
|
color: #ffffff;
|
|
font-size: 36rpx;
|
|
line-height: 1;
|
|
}
|
|
|
|
.pay-method-name {
|
|
color: #111827;
|
|
font-size: 30rpx;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.pay-method-check {
|
|
color: #07c160;
|
|
font-size: 36rpx;
|
|
line-height: 1;
|
|
}
|
|
|
|
.pay-method-tip {
|
|
margin-top: 20rpx;
|
|
color: #9ca3af;
|
|
font-size: 24rpx;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.pay-button {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 92rpx;
|
|
margin-top: 48rpx;
|
|
padding: 0;
|
|
border-radius: 12rpx;
|
|
background: #1f5bd8;
|
|
color: #ffffff;
|
|
font-size: 32rpx;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.pay-button::after {
|
|
border: none;
|
|
}
|
|
|
|
.pay-button-hover {
|
|
background: #174dc1;
|
|
}
|
|
|
|
.pay-button[disabled] {
|
|
background: #c8d3ee;
|
|
color: #ffffff;
|
|
}
|
|
|
|
.provider-footer {
|
|
margin-top: 28rpx;
|
|
text-align: center;
|
|
color: #9ca3af;
|
|
font-size: 24rpx;
|
|
}
|
|
</style>
|