hookehuyr

新增支付倒计时

1 +<!--
2 + * @Date: 2024-01-24 16:38:13
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2024-01-24 17:00:48
5 + * @FilePath: /xysBooking/src/components/reserveCard.vue
6 + * @Description: 文件描述
7 +-->
8 +<template>
9 + <div class="booking-list-item" @click="goToDetail(props.data)">
10 + <div class="booking-list-item-header">
11 + <div>{{ props.data.booking_time }}</div>
12 + <div :class="[formatStatus(props.data.status)?.key, 'status']">{{ formatStatus(props.data.status)?.value }}</div>
13 + </div>
14 + <div class="booking-list-item-body">
15 + <div class="booking-num">
16 + <div class="num-body">预约人数:<span>{{ props.data.total_qty }} 人</span></div>
17 + <div><van-icon name="arrow" /></div>
18 + </div>
19 + <div class="booking-price">支付金额:<span>¥ {{ props.data.total_amt }}</span></div>
20 + <div class="booking-time">下单时间:<span>{{ props.data.order_time }}</span></div>
21 + </div>
22 + <div class="booking-list-item-footer">
23 + <div style="display: flex; justify-content: space-between; padding: 0 1rem 1rem 1rem; align-items: center;">
24 + <div v-if="pay_show" style="font-size: 0.85rem; color: red;">
25 + <span>支付剩余时间&nbsp;</span>
26 + <span>{{ formatTime(remain_time) }}</span>
27 + </div>
28 + <div v-if="showBtn">
29 + <van-button v-if="pay_show" @click="payOrder()" type="primary" color="#A67939" size="small">重新支付</van-button>
30 + <div v-if="delay_pay_show" style="font-size: 23rpx; color: red; font-size: 0.85rem;">支付超时,请重新下单!</div>
31 + </div>
32 + </div>
33 + </div>
34 + </div>
35 +</template>
36 +
37 +<script setup>
38 +import { ref } from 'vue'
39 +import { useRoute, useRouter } from 'vue-router'
40 +
41 +import { Cookies, $, _, axios, storeToRefs, mainStore, Toast, useTitle } from '@/utils/generatePackage.js'
42 +//import { } from '@/utils/generateModules.js'
43 +//import { } from '@/utils/generateIcons.js'
44 +//import { } from '@/composables'
45 +const $route = useRoute();
46 +const $router = useRouter();
47 +useTitle($route.meta.title);
48 +
49 +const props = defineProps({
50 + data: {
51 + type: Object,
52 + default: {},
53 + },
54 +});
55 +
56 +
57 +/**
58 + * 1=待支付(下单就立即支付,所以理论上不存在待支付的数据),
59 + * 2=支付中(支付前先把状态打成2;支付回调后应立即变更状态,支付中的状态不会持续很久),
60 + * 3=预约成功(已支付,且一个码都未被使用),
61 + * 5=已取消(用户手动取消成功,退款成功回调后打成5,否则打成3),
62 + * 7=已取消(支付超时或失败自动取消),
63 + * 9=已使用(明细里有一个码被使用),
64 + * 11=退款中(取消预约时先把状态打成11)
65 + */
66 +const CodeStatus = {
67 + APPLY: '1',
68 + PAYING: '2',
69 + SUCCESS: '3',
70 + CANCEL: '5',
71 + CANCELED: '7',
72 + USED: '9',
73 + REFUNDING: '11'
74 +}
75 +
76 +const formatStatus = (status) => {
77 + switch (status) {
78 + case CodeStatus.APPLY:
79 + return {
80 + key: 'cancel',
81 + value: '待支付'
82 + }
83 + case CodeStatus.PAYING:
84 + return {
85 + key: 'success',
86 + value: '支付中'
87 + }
88 + case CodeStatus.SUCCESS:
89 + return {
90 + key: 'success',
91 + value: '预约成功'
92 + }
93 + case CodeStatus.CANCEL:
94 + return {
95 + key: 'cancel',
96 + value: '已取消'
97 + }
98 + case CodeStatus.CANCELED:
99 + return {
100 + key: 'cancel',
101 + value: '已取消'
102 + }
103 + case CodeStatus.USED:
104 + return {
105 + key: 'used',
106 + value: '已使用'
107 + }
108 + case CodeStatus.REFUNDING:
109 + return {
110 + key: 'cancel',
111 + value: '退款中'
112 + }
113 + }
114 +}
115 +
116 +
117 +const goToDetail = (item) => {
118 + if (item.status === CodeStatus.SUCCESS || item.status === CodeStatus.USED || item.status === CodeStatus.CANCEL) {
119 + go('/bookingDetail', { pay_id: item.pay_id })
120 + }
121 +}
122 +
123 +/**
124 + * 格式化时间
125 + * @param {*} seconds
126 + */
127 +function formatTime(seconds) {
128 + const hours = Math.floor(seconds / 3600); // 计算小时数
129 + const minutes = Math.floor((seconds % 3600) / 60); // 计算分钟数
130 + const remainingSeconds = seconds % 60; // 计算剩余的秒数
131 +
132 + const formattedHours = String(hours).padStart(2, "0"); // 格式化小时数,保证两位数
133 + const formattedMinutes = String(minutes).padStart(2, "0"); // 格式化分钟数,保证两位数
134 + const formattedSeconds = String(remainingSeconds).padStart(2, "0"); // 格式化剩余的秒数,保证两位数
135 +
136 + return `${formattedHours}:${formattedMinutes}:${formattedSeconds}`;
137 +}
138 +
139 +// 显示操作按钮的条件判断
140 +const showBtn = computed(() => {
141 + return props.data.status === CodeStatus.APPLY;
142 +});
143 +
144 +const remain_time = ref(0); // 剩余时间秒数
145 +
146 +// 控制待支付状态下的显示
147 +const pay_show = computed(() => {
148 + let flag = false;
149 + if (props.data.status === CodeStatus.APPLY && remain_time.value) {
150 + // 倒计时进行时
151 + flag = true;
152 + } else if (props.data.status === CodeStatus.APPLY && !remain_time.value) {
153 + // 倒计时结束
154 + flag = false;
155 + }
156 + return flag;
157 +});
158 +
159 +// 支付超时显示
160 +const delay_pay_show = computed(() => {
161 + // return props.data.status === CodeStatus.APPLY && !remain_time.value;
162 + return props.data.status === CodeStatus.SUCCESS && !remain_time.value;
163 +});
164 +
165 +let timeId = null;
166 +
167 +onMounted(() => {
168 + remain_time.value = 5;
169 + // 进入页面后,开始倒计时
170 + timeId = setInterval(() => {
171 + remain_time.value ? (remain_time.value -= 1) : 0;
172 + }, 1000);
173 + onUnmounted(() => {
174 + timeId && clearInterval(timeId);
175 + });
176 +});
177 +
178 +const payOrder = () => {
179 + const pay_url = `/srv/?f=reserve&a=icbc_pay&pay_id=${props.data.pay_id}`;
180 + location.href = pay_url; // 跳转支付页面
181 +}
182 +</script>
183 +
184 +<style lang="less" scoped>
185 + .booking-list-item {
186 + background-color: #FFF;
187 + border-radius: 8px;
188 + margin-bottom: 1rem;
189 + .booking-list-item-header {
190 + display: flex;
191 + justify-content: space-between;
192 + align-items: center;
193 + padding: 1rem;
194 + border-bottom: 1px dashed #f0f0f0;
195 + .status {
196 + font-size: 0.75rem;
197 + padding: 5px 8px;
198 + border-radius: 5px;
199 + }
200 + .success {
201 + color: #A67939;
202 + background-color: #FBEEDC;
203 + }
204 + .cancel {
205 + color: #929292;
206 + background-color: #E6E6E6;
207 + }
208 + .used {
209 + color: #477F3D;
210 + background-color: #E5EFE3;
211 + }
212 + }
213 + .booking-list-item-body {
214 + padding: 1rem;
215 + line-height: 1.7;
216 + .booking-num {
217 + display: flex;
218 + justify-content: space-between;
219 + .num-body {
220 + color: #959595;
221 + span {
222 + color: #1E1E1E;
223 + }
224 + }
225 + }
226 + .booking-price {
227 + color: #959595;
228 + span {
229 + color: #1E1E1E;
230 + }
231 + }
232 + .booking-time {
233 + color: #959595;
234 + span {
235 + color: #1E1E1E;
236 + }
237 + }
238 + }
239 + }
240 +
241 + .no-qrcode {
242 + display: flex;
243 + justify-content: center;
244 + align-items: center;
245 + flex-direction: column;
246 + img {
247 + margin-top: 1rem;
248 + margin-bottom: 1rem;
249 + width: 10rem;
250 + }
251 + .no-qrcode-title {
252 + color: #A67939;
253 + font-size: 1.05rem;
254 + }
255 + }
256 +</style>
1 <!-- 1 <!--
2 * @Date: 2024-01-16 11:37:10 2 * @Date: 2024-01-16 11:37:10
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2024-01-24 10:44:23 4 + * @LastEditTime: 2024-01-24 16:49:34
5 * @FilePath: /xysBooking/src/views/bookingList.vue 5 * @FilePath: /xysBooking/src/views/bookingList.vue
6 * @Description: 文件描述 6 * @Description: 文件描述
7 --> 7 -->
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
13 :finished-text="finishedTextStatus ? '没有更多了' : ''" 13 :finished-text="finishedTextStatus ? '没有更多了' : ''"
14 @load="onLoad" 14 @load="onLoad"
15 > 15 >
16 - <div @click="() => { go('/bookingDetail', { pay_id: item.pay_id }) }" v-for="(item, index) in bookingList" :key="index" class="booking-list-item"> 16 + <!-- <div @click="goToDetail(item)" v-for="(item, index) in bookingList" :key="index" class="booking-list-item">
17 <div class="booking-list-item-header"> 17 <div class="booking-list-item-header">
18 <div>{{ item.booking_time }}</div> 18 <div>{{ item.booking_time }}</div>
19 <div :class="[formatStatus(item.status)?.key, 'status']">{{ formatStatus(item.status)?.value }}</div> 19 <div :class="[formatStatus(item.status)?.key, 'status']">{{ formatStatus(item.status)?.value }}</div>
...@@ -26,7 +26,10 @@ ...@@ -26,7 +26,10 @@
26 <div class="booking-price">支付金额:<span>¥ {{ item.total_amt }}</span></div> 26 <div class="booking-price">支付金额:<span>¥ {{ item.total_amt }}</span></div>
27 <div class="booking-time">下单时间:<span>{{ item.order_time }}</span></div> 27 <div class="booking-time">下单时间:<span>{{ item.order_time }}</span></div>
28 </div> 28 </div>
29 - </div> 29 + </div> -->
30 + <template v-for="(item, index) in bookingList" :key="index">
31 + <reserveCard :data="item" />
32 + </template>
30 </van-list> 33 </van-list>
31 <div v-if="!bookingList.length" class="no-qrcode"> 34 <div v-if="!bookingList.length" class="no-qrcode">
32 <img src="https://cdn.ipadbiz.cn/xys/booking/%E6%9A%82%E6%97%A0@2x.png" style="width: 10rem;"> 35 <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 '@ ...@@ -44,71 +47,13 @@ import { Cookies, $, _, axios, storeToRefs, mainStore, Toast, useTitle } from '@
44 //import { } from '@/utils/generateIcons.js' 47 //import { } from '@/utils/generateIcons.js'
45 //import { } from '@/composables' 48 //import { } from '@/composables'
46 import { billListAPI } from '@/api/index' 49 import { billListAPI } from '@/api/index'
50 +import reserveCard from '@/components/reserveCard.vue'
47 const $route = useRoute(); 51 const $route = useRoute();
48 const $router = useRouter(); 52 const $router = useRouter();
49 useTitle($route.meta.title); 53 useTitle($route.meta.title);
50 54
51 const go = useGo(); 55 const go = useGo();
52 56
53 -/**
54 - * 1=待支付(下单就立即支付,所以理论上不存在待支付的数据),
55 - * 2=支付中(支付前先把状态打成2;支付回调后应立即变更状态,支付中的状态不会持续很久),
56 - * 3=预约成功(已支付,且一个码都未被使用),
57 - * 5=已取消(用户手动取消成功,退款成功回调后打成5,否则打成3),
58 - * 7=已取消(支付超时或失败自动取消),
59 - * 9=已使用(明细里有一个码被使用),
60 - * 11=退款中(取消预约时先把状态打成11)
61 - */
62 -const CodeStatus = {
63 - APPLY: '1',
64 - PAYING: '2',
65 - SUCCESS: '3',
66 - CANCEL: '5',
67 - CANCELED: '7',
68 - USED: '9',
69 - REFUNDING: '11'
70 -}
71 -
72 -const formatStatus = (status) => {
73 - switch (status) {
74 - case CodeStatus.APPLY:
75 - return {
76 - key: 'cancel',
77 - value: '待支付'
78 - }
79 - case CodeStatus.PAYING:
80 - return {
81 - key: 'success',
82 - value: '支付中'
83 - }
84 - case CodeStatus.SUCCESS:
85 - return {
86 - key: 'success',
87 - value: '预约成功'
88 - }
89 - case CodeStatus.CANCEL:
90 - return {
91 - key: 'cancel',
92 - value: '已取消'
93 - }
94 - case CodeStatus.CANCELED:
95 - return {
96 - key: 'cancel',
97 - value: '已取消'
98 - }
99 - case CodeStatus.USED:
100 - return {
101 - key: 'used',
102 - value: '已使用'
103 - }
104 - case CodeStatus.REFUNDING:
105 - return {
106 - key: 'cancel',
107 - value: '退款中'
108 - }
109 - }
110 -}
111 -
112 const page = ref(1); // 页码默认为1 57 const page = ref(1); // 页码默认为1
113 const limit = ref(5); // 每页默认显示5条 58 const limit = ref(5); // 每页默认显示5条
114 const bookingList = ref([]); 59 const bookingList = ref([]);
...@@ -155,81 +100,12 @@ const onLoad = async () => { ...@@ -155,81 +100,12 @@ const onLoad = async () => {
155 } 100 }
156 } 101 }
157 } 102 }
103 +
158 </script> 104 </script>
159 105
160 <style lang="less" scoped> 106 <style lang="less" scoped>
161 .booking-list-page { 107 .booking-list-page {
162 padding: 1rem; 108 padding: 1rem;
163 - .booking-list-item {
164 - background-color: #FFF;
165 - border-radius: 8px;
166 - margin-bottom: 1rem;
167 - .booking-list-item-header {
168 - display: flex;
169 - justify-content: space-between;
170 - align-items: center;
171 - padding: 1rem;
172 - border-bottom: 1px dashed #f0f0f0;
173 - .status {
174 - font-size: 0.75rem;
175 - padding: 5px 8px;
176 - border-radius: 5px;
177 - }
178 - .success {
179 - color: #A67939;
180 - background-color: #FBEEDC;
181 - }
182 - .cancel {
183 - color: #929292;
184 - background-color: #E6E6E6;
185 - }
186 - .used {
187 - color: #477F3D;
188 - background-color: #E5EFE3;
189 - }
190 - }
191 - .booking-list-item-body {
192 - padding: 1rem;
193 - line-height: 1.7;
194 - .booking-num {
195 - display: flex;
196 - justify-content: space-between;
197 - .num-body {
198 - color: #959595;
199 - span {
200 - color: #1E1E1E;
201 - }
202 - }
203 - }
204 - .booking-price {
205 - color: #959595;
206 - span {
207 - color: #1E1E1E;
208 - }
209 - }
210 - .booking-time {
211 - color: #959595;
212 - span {
213 - color: #1E1E1E;
214 - }
215 - }
216 - }
217 - }
218 109
219 - .no-qrcode {
220 - display: flex;
221 - justify-content: center;
222 - align-items: center;
223 - flex-direction: column;
224 - img {
225 - margin-top: 1rem;
226 - margin-bottom: 1rem;
227 - width: 10rem;
228 - }
229 - .no-qrcode-title {
230 - color: #A67939;
231 - font-size: 1.05rem;
232 - }
233 - }
234 } 110 }
235 </style> 111 </style>
......