hookehuyr

新增支付倒计时

<!--
* @Date: 2024-01-24 16:38:13
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2024-01-24 17:00:48
* @FilePath: /xysBooking/src/components/reserveCard.vue
* @Description: 文件描述
-->
<template>
<div class="booking-list-item" @click="goToDetail(props.data)">
<div class="booking-list-item-header">
<div>{{ props.data.booking_time }}</div>
<div :class="[formatStatus(props.data.status)?.key, 'status']">{{ formatStatus(props.data.status)?.value }}</div>
</div>
<div class="booking-list-item-body">
<div class="booking-num">
<div class="num-body">预约人数:<span>{{ props.data.total_qty }} 人</span></div>
<div><van-icon name="arrow" /></div>
</div>
<div class="booking-price">支付金额:<span>¥ {{ props.data.total_amt }}</span></div>
<div class="booking-time">下单时间:<span>{{ props.data.order_time }}</span></div>
</div>
<div class="booking-list-item-footer">
<div style="display: flex; justify-content: space-between; padding: 0 1rem 1rem 1rem; align-items: center;">
<div v-if="pay_show" style="font-size: 0.85rem; color: red;">
<span>支付剩余时间&nbsp;</span>
<span>{{ formatTime(remain_time) }}</span>
</div>
<div v-if="showBtn">
<van-button v-if="pay_show" @click="payOrder()" type="primary" color="#A67939" size="small">重新支付</van-button>
<div v-if="delay_pay_show" style="font-size: 23rpx; color: red; font-size: 0.85rem;">支付超时,请重新下单!</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { Cookies, $, _, axios, storeToRefs, mainStore, Toast, useTitle } from '@/utils/generatePackage.js'
//import { } from '@/utils/generateModules.js'
//import { } from '@/utils/generateIcons.js'
//import { } from '@/composables'
const $route = useRoute();
const $router = useRouter();
useTitle($route.meta.title);
const props = defineProps({
data: {
type: Object,
default: {},
},
});
/**
* 1=待支付(下单就立即支付,所以理论上不存在待支付的数据),
* 2=支付中(支付前先把状态打成2;支付回调后应立即变更状态,支付中的状态不会持续很久),
* 3=预约成功(已支付,且一个码都未被使用),
* 5=已取消(用户手动取消成功,退款成功回调后打成5,否则打成3),
* 7=已取消(支付超时或失败自动取消),
* 9=已使用(明细里有一个码被使用),
* 11=退款中(取消预约时先把状态打成11)
*/
const CodeStatus = {
APPLY: '1',
PAYING: '2',
SUCCESS: '3',
CANCEL: '5',
CANCELED: '7',
USED: '9',
REFUNDING: '11'
}
const formatStatus = (status) => {
switch (status) {
case CodeStatus.APPLY:
return {
key: 'cancel',
value: '待支付'
}
case CodeStatus.PAYING:
return {
key: 'success',
value: '支付中'
}
case CodeStatus.SUCCESS:
return {
key: 'success',
value: '预约成功'
}
case CodeStatus.CANCEL:
return {
key: 'cancel',
value: '已取消'
}
case CodeStatus.CANCELED:
return {
key: 'cancel',
value: '已取消'
}
case CodeStatus.USED:
return {
key: 'used',
value: '已使用'
}
case CodeStatus.REFUNDING:
return {
key: 'cancel',
value: '退款中'
}
}
}
const goToDetail = (item) => {
if (item.status === CodeStatus.SUCCESS || item.status === CodeStatus.USED || item.status === CodeStatus.CANCEL) {
go('/bookingDetail', { pay_id: item.pay_id })
}
}
/**
* 格式化时间
* @param {*} seconds
*/
function formatTime(seconds) {
const hours = Math.floor(seconds / 3600); // 计算小时数
const minutes = Math.floor((seconds % 3600) / 60); // 计算分钟数
const remainingSeconds = seconds % 60; // 计算剩余的秒数
const formattedHours = String(hours).padStart(2, "0"); // 格式化小时数,保证两位数
const formattedMinutes = String(minutes).padStart(2, "0"); // 格式化分钟数,保证两位数
const formattedSeconds = String(remainingSeconds).padStart(2, "0"); // 格式化剩余的秒数,保证两位数
return `${formattedHours}:${formattedMinutes}:${formattedSeconds}`;
}
// 显示操作按钮的条件判断
const showBtn = computed(() => {
return props.data.status === CodeStatus.APPLY;
});
const remain_time = ref(0); // 剩余时间秒数
// 控制待支付状态下的显示
const pay_show = computed(() => {
let flag = false;
if (props.data.status === CodeStatus.APPLY && remain_time.value) {
// 倒计时进行时
flag = true;
} else if (props.data.status === CodeStatus.APPLY && !remain_time.value) {
// 倒计时结束
flag = false;
}
return flag;
});
// 支付超时显示
const delay_pay_show = computed(() => {
// return props.data.status === CodeStatus.APPLY && !remain_time.value;
return props.data.status === CodeStatus.SUCCESS && !remain_time.value;
});
let timeId = null;
onMounted(() => {
remain_time.value = 5;
// 进入页面后,开始倒计时
timeId = setInterval(() => {
remain_time.value ? (remain_time.value -= 1) : 0;
}, 1000);
onUnmounted(() => {
timeId && clearInterval(timeId);
});
});
const payOrder = () => {
const pay_url = `/srv/?f=reserve&a=icbc_pay&pay_id=${props.data.pay_id}`;
location.href = pay_url; // 跳转支付页面
}
</script>
<style lang="less" scoped>
.booking-list-item {
background-color: #FFF;
border-radius: 8px;
margin-bottom: 1rem;
.booking-list-item-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
border-bottom: 1px dashed #f0f0f0;
.status {
font-size: 0.75rem;
padding: 5px 8px;
border-radius: 5px;
}
.success {
color: #A67939;
background-color: #FBEEDC;
}
.cancel {
color: #929292;
background-color: #E6E6E6;
}
.used {
color: #477F3D;
background-color: #E5EFE3;
}
}
.booking-list-item-body {
padding: 1rem;
line-height: 1.7;
.booking-num {
display: flex;
justify-content: space-between;
.num-body {
color: #959595;
span {
color: #1E1E1E;
}
}
}
.booking-price {
color: #959595;
span {
color: #1E1E1E;
}
}
.booking-time {
color: #959595;
span {
color: #1E1E1E;
}
}
}
}
.no-qrcode {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
img {
margin-top: 1rem;
margin-bottom: 1rem;
width: 10rem;
}
.no-qrcode-title {
color: #A67939;
font-size: 1.05rem;
}
}
</style>
<!--
* @Date: 2024-01-16 11:37:10
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2024-01-24 10:44:23
* @LastEditTime: 2024-01-24 16:49:34
* @FilePath: /xysBooking/src/views/bookingList.vue
* @Description: 文件描述
-->
......@@ -13,7 +13,7 @@
:finished-text="finishedTextStatus ? '没有更多了' : ''"
@load="onLoad"
>
<div @click="() => { go('/bookingDetail', { pay_id: item.pay_id }) }" v-for="(item, index) in bookingList" :key="index" class="booking-list-item">
<!-- <div @click="goToDetail(item)" v-for="(item, index) in bookingList" :key="index" class="booking-list-item">
<div class="booking-list-item-header">
<div>{{ item.booking_time }}</div>
<div :class="[formatStatus(item.status)?.key, 'status']">{{ formatStatus(item.status)?.value }}</div>
......@@ -26,7 +26,10 @@
<div class="booking-price">支付金额:<span>¥ {{ item.total_amt }}</span></div>
<div class="booking-time">下单时间:<span>{{ item.order_time }}</span></div>
</div>
</div>
</div> -->
<template v-for="(item, index) in bookingList" :key="index">
<reserveCard :data="item" />
</template>
</van-list>
<div v-if="!bookingList.length" class="no-qrcode">
<img src="https://cdn.ipadbiz.cn/xys/booking/%E6%9A%82%E6%97%A0@2x.png" style="width: 10rem;">
......@@ -44,71 +47,13 @@ import { Cookies, $, _, axios, storeToRefs, mainStore, Toast, useTitle } from '@
//import { } from '@/utils/generateIcons.js'
//import { } from '@/composables'
import { billListAPI } from '@/api/index'
import reserveCard from '@/components/reserveCard.vue'
const $route = useRoute();
const $router = useRouter();
useTitle($route.meta.title);
const go = useGo();
/**
* 1=待支付(下单就立即支付,所以理论上不存在待支付的数据),
* 2=支付中(支付前先把状态打成2;支付回调后应立即变更状态,支付中的状态不会持续很久),
* 3=预约成功(已支付,且一个码都未被使用),
* 5=已取消(用户手动取消成功,退款成功回调后打成5,否则打成3),
* 7=已取消(支付超时或失败自动取消),
* 9=已使用(明细里有一个码被使用),
* 11=退款中(取消预约时先把状态打成11)
*/
const CodeStatus = {
APPLY: '1',
PAYING: '2',
SUCCESS: '3',
CANCEL: '5',
CANCELED: '7',
USED: '9',
REFUNDING: '11'
}
const formatStatus = (status) => {
switch (status) {
case CodeStatus.APPLY:
return {
key: 'cancel',
value: '待支付'
}
case CodeStatus.PAYING:
return {
key: 'success',
value: '支付中'
}
case CodeStatus.SUCCESS:
return {
key: 'success',
value: '预约成功'
}
case CodeStatus.CANCEL:
return {
key: 'cancel',
value: '已取消'
}
case CodeStatus.CANCELED:
return {
key: 'cancel',
value: '已取消'
}
case CodeStatus.USED:
return {
key: 'used',
value: '已使用'
}
case CodeStatus.REFUNDING:
return {
key: 'cancel',
value: '退款中'
}
}
}
const page = ref(1); // 页码默认为1
const limit = ref(5); // 每页默认显示5条
const bookingList = ref([]);
......@@ -155,81 +100,12 @@ const onLoad = async () => {
}
}
}
</script>
<style lang="less" scoped>
.booking-list-page {
padding: 1rem;
.booking-list-item {
background-color: #FFF;
border-radius: 8px;
margin-bottom: 1rem;
.booking-list-item-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
border-bottom: 1px dashed #f0f0f0;
.status {
font-size: 0.75rem;
padding: 5px 8px;
border-radius: 5px;
}
.success {
color: #A67939;
background-color: #FBEEDC;
}
.cancel {
color: #929292;
background-color: #E6E6E6;
}
.used {
color: #477F3D;
background-color: #E5EFE3;
}
}
.booking-list-item-body {
padding: 1rem;
line-height: 1.7;
.booking-num {
display: flex;
justify-content: space-between;
.num-body {
color: #959595;
span {
color: #1E1E1E;
}
}
}
.booking-price {
color: #959595;
span {
color: #1E1E1E;
}
}
.booking-time {
color: #959595;
span {
color: #1E1E1E;
}
}
}
}
.no-qrcode {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
img {
margin-top: 1rem;
margin-bottom: 1rem;
width: 10rem;
}
.no-qrcode-title {
color: #A67939;
font-size: 1.05rem;
}
}
}
</style>
......