1、调整websocket的初始化

2、完善支付的倒计时
This commit is contained in:
ywyonui 2025-07-21 14:34:20 +08:00
parent 47bf0977cc
commit ee7de58ba4
5 changed files with 293 additions and 109 deletions

View File

@ -101,6 +101,12 @@ export const xsKscjApi = async (params: any) => {
export const jzXkQkjApi = async (params: any) => { export const jzXkQkjApi = async (params: any) => {
return await post("/mobile/jz/xk/qk", params); return await post("/mobile/jz/xk/qk", params);
}; };
/**
*
*/
export const jzGetQkExpiredTime = async (params: any) => {
return await get("/mobile/jz/xk/getQkExpiredTime", params);
};
/** /**
* *
*/ */

View File

@ -52,7 +52,7 @@
{ active: zc.djz === curZc.djz }, { active: zc.djz === curZc.djz },
]" @click="selectWeek(zc)"> ]" @click="selectWeek(zc)">
<text>{{ zc.mc }}</text> <text>{{ zc.mc }}</text>
<text v-if="zc.djz === curZc.djz" class="current-tag">当前周</text> <text v-if="zc.dnDjz === dnDjz" class="current-tag">当前周</text>
</view> </view>
</scroll-view> </scroll-view>
</view> </view>
@ -63,16 +63,27 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive, computed, onMounted, nextTick } from "vue"; import { ref, reactive, computed, onMounted, nextTick } from "vue";
import { dqpkApi, drpkkbApi, gzlGetDqXqAndZcApi } from "@/api/base/server"; import { dqpkApi, drpkkbApi } from "@/api/base/server";
import { useUserStore } from "@/store/modules/user"; import { useUserStore } from "@/store/modules/user";
const { getCurXs } = useUserStore(); const { getCurXs } = useUserStore();
import dayjs from "dayjs";
import "dayjs/locale/zh-cn";
import weekOfYear from "dayjs/plugin/weekOfYear";
import isoWeek from "dayjs/plugin/isoWeek";
dayjs.locale("zh-cn");
dayjs.extend(weekOfYear);
dayjs.extend(isoWeek);
let dqZc = 0; let dqZc = 0;
let xqId = ''; let xqId = '';
// //
const dnDjz = ref(dayjs().isoWeek());
//
const curZc = ref<any>({}); const curZc = ref<any>({});
// //
const curRqIndex = ref(0) const curRqIndex = ref(0)
// //
const zcList = ref<any>([]) const zcList = ref<any>([])

View File

@ -1,17 +1,40 @@
<template> <template>
<view class="wh-full"> <view class="payment-page">
<web-view :src="payUrl"></web-view> <!-- 倒计时区域 -->
<view class="countdown-section">
<view class="countdown-icon">
<u-icon name="clock" size="22" color="#fff"></u-icon>
</view>
<view class="countdown-text">待支付</view>
<view class="countdown-timer">
<text>剩余</text>
<text class="time-value">{{ countdownTime }}</text>
</view>
</view>
<view class="scrollable-content">
<web-view :src="payUrl" style="flex: 1;"></web-view>
</view>
<!-- 底部支付区域 -->
<view class="payment-footer">
<view class="action-buttons">
<view class="cancel-btn" @click="cancelRegistration">取消报名</view>
</view>
</view>
</view> </view>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onLoad } from "@dcloudio/uni-app"; import { onLoad } from "@dcloudio/uni-app";
import { useWebSocket } from '@/utils/webSocket/webSocket' import { jzGetQkExpiredTime, jzXkCancelApi } from "@/api/base/server";
import { useUserStore } from "@/store/modules/user"; import { useUserStore } from "@/store/modules/user";
const { getUser } = useUserStore(); import { useDataStore } from "@/store/modules/data";
const { getCurXs, getUser, setWsCallback } = useUserStore();
const { getData } = useDataStore();
const payUrl = ref(""); const payUrl = ref("");
const ws = useWebSocket(`/zhxy/webSocket/${getUser.userId}`, (type: string, res: any) => { setWsCallback((type: string, res: any) => {
console.log('收到WebSocket消息:', type, res.data); console.log('收到WebSocket消息:', type, res.data);
// data // data
const dataObj = JSON.parse(res.data); const dataObj = JSON.parse(res.data);
@ -20,7 +43,7 @@ const ws = useWebSocket(`/zhxy/webSocket/${getUser.userId}`, (type: string, res:
title: '支付成功', title: '支付成功',
icon: 'success', icon: 'success',
}); });
ws.closeConnect(); setWsCallback((type: string, res: any) => {})
// //
setTimeout(() => { setTimeout(() => {
uni.reLaunch({ uni.reLaunch({
@ -29,10 +52,70 @@ const ws = useWebSocket(`/zhxy/webSocket/${getUser.userId}`, (type: string, res:
}, 1000) }, 1000)
} }
}); });
onLoad((options: any) => {
//
const countdownTime = ref("1分20秒");
let timer: any = null;
let seconds = 1 * 60 + 20; // 120
//
const startCountdown = () => {
timer = setInterval(() => {
seconds--;
if (seconds <= 0) {
clearInterval(timer);
uni.showModal({
title: "支付超时",
content: "支付已超时,请重新选课",
showCancel: false,
success: () => {
cancelRegistration();
},
});
return;
}
const minutes = Math.floor(seconds / 60);
const remainSeconds = seconds % 60;
countdownTime.value = `${minutes}${remainSeconds}`;
}, 1000);
};
//
const goBack = () => {
uni.reLaunch({
url: getData.backUrl
});
};
//
const cancelRegistration = () => {
uni.showModal({
title: "取消报名",
content: "确定要取消报名吗?",
success: async (res) => {
if (res.confirm) {
await jzXkCancelApi({
xsId: getData.xsId,
xkId: getData.xkId
});
uni.showToast({
title: "已取消报名",
icon: "success",
});
goBack();
}
},
});
};
onLoad(async (options: any) => {
if (options.payUrl) { if (options.payUrl) {
payUrl.value = decodeURIComponent(options.payUrl); payUrl.value = decodeURIComponent(options.payUrl);
ws.reconnect(); const res = await jzGetQkExpiredTime({ xsId: getCurXs.id} );
seconds = res.result;
startCountdown();
} else { } else {
uni.showToast({ title: '缺少支付地址', icon: 'none' }) uni.showToast({ title: '缺少支付地址', icon: 'none' })
setTimeout(() => { setTimeout(() => {
@ -42,8 +125,92 @@ onLoad((options: any) => {
}); });
onBeforeUnmount(() => { onBeforeUnmount(() => {
ws.closeConnect(); if (timer) {
clearInterval(timer);
}
}); });
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped>
.payment-page {
min-height: 100%;
background-color: #f5f7fa;
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
}
//
.scrollable-content {
flex: 1;
display: flex;
position: relative;
}
.countdown-section {
display: flex;
align-items: center;
padding: 15px;
background-color: #2879ff;
.countdown-icon {
margin-right: 10px;
}
.countdown-text {
font-size: 16px;
font-weight: 500;
color: #fff;
margin-right: auto;
}
.countdown-timer {
font-size: 15px;
color: #fff;
.time-value {
color: #ff4d4f;
font-weight: 500;
}
}
}
.payment-footer {
position: sticky;
bottom: 0;
left: 0;
right: 0;
background-color: #fff;
padding: 15px;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
.total-amount {
display: flex;
align-items: center;
margin-bottom: 15px;
font-size: 15px;
color: #333;
.amount-value {
color: #ff6b00;
font-size: 20px;
font-weight: 500;
}
}
.action-buttons {
.cancel-btn {
height: 44px;
line-height: 44px;
text-align: center;
border-radius: 22px;
font-size: 16px;
background-color: #fff;
color: #333;
border: 1px solid #ddd;
}
}
}
</style>

View File

@ -6,17 +6,21 @@
<u-icon name="clock" size="22" color="#fff"></u-icon> <u-icon name="clock" size="22" color="#fff"></u-icon>
</view> </view>
<view class="countdown-text">待支付</view> <view class="countdown-text">待支付</view>
<!-- <view class="countdown-timer"> <view class="countdown-timer">
<text>剩余</text> <text>剩余</text>
<text class="time-value">{{ countdownTime }}</text> <text class="time-value">{{ countdownTime }}</text>
</view> --> </view>
</view> </view>
<!-- 学生信息卡片 --> <view class="scrollable-content">
<XkPayXs />
<!-- 课程信息卡片 --> <!-- 学生信息卡片 -->
<XkPayXkqd /> <XkPayXs />
<!-- 课程信息卡片 -->
<XkPayXkqd />
</view>
<!-- 底部支付区域 --> <!-- 底部支付区域 -->
<view class="payment-footer"> <view class="payment-footer">
@ -36,7 +40,7 @@
<script setup lang="ts"> <script setup lang="ts">
import XkPayXs from "@/pages/base/components/XkPayXs/index.vue" import XkPayXs from "@/pages/base/components/XkPayXs/index.vue"
import XkPayXkqd from "@/pages/base/components/XkPayXkqd/index.vue" import XkPayXkqd from "@/pages/base/components/XkPayXkqd/index.vue"
import { jzXkCancelApi, jzXkFqJfjApi } from "@/api/base/server"; import { jzGetQkExpiredTime, jzXkCancelApi, jzXkFqJfjApi } from "@/api/base/server";
import { useUserStore } from "@/store/modules/user"; import { useUserStore } from "@/store/modules/user";
import { useDataStore } from "@/store/modules/data"; import { useDataStore } from "@/store/modules/data";
const { getCurXs, getUser } = useUserStore(); const { getCurXs, getUser } = useUserStore();
@ -60,32 +64,32 @@ const totalJe = computed(() => {
}); });
// //
// const countdownTime = ref("120"); const countdownTime = ref("1分20秒");
// let timer: any = null; let timer: any = null;
// let seconds = 1 * 60 + 20; // 120 let seconds = 1 * 60 + 20; // 120
// //
// const startCountdown = () => { const startCountdown = () => {
// timer = setInterval(() => { timer = setInterval(() => {
// seconds--; seconds--;
// if (seconds <= 0) { if (seconds <= 0) {
// clearInterval(timer); clearInterval(timer);
// uni.showModal({ uni.showModal({
// title: "", title: "支付超时",
// content: "", content: "支付已超时,请重新选课",
// showCancel: false, showCancel: false,
// success: () => { success: () => {
// goBack(); cancelRegistration();
// }, },
// }); });
// return; return;
// } }
// const minutes = Math.floor(seconds / 60); const minutes = Math.floor(seconds / 60);
// const remainSeconds = seconds % 60; const remainSeconds = seconds % 60;
// countdownTime.value = `${minutes}${remainSeconds}`; countdownTime.value = `${minutes}${remainSeconds}`;
// }, 1000); }, 1000);
// }; };
// //
const goBack = () => { const goBack = () => {
@ -117,49 +121,40 @@ const cancelRegistration = () => {
// //
const payNow = async () => { const payNow = async () => {
const res = await jzXkFqJfjApi({ try {
xsId: getData.xsId, const res = await jzXkFqJfjApi({
xkId: getData.xkId, xsId: getData.xsId,
jffs: "四川农信", // TODO: xkId: getData.xkId,
jzId: getUser.jzId, jffs: "四川农信", // TODO:
userId: getUser.userId, jzId: getUser.jzId,
openId: getUser.openId, userId: getUser.userId,
}); openId: getUser.openId,
// const res = { });
// resultCode: 1, if (res.resultCode === 1 && res.result) {
// result: "https://pay.weixin.qq.com/wxpay/pay.action?prepay_id=wx20191018103005f5c0c0f5c0c" uni.redirectTo({
// } url: `/pages/base/course-selection/pay-wait?payUrl=${encodeURIComponent(res.result)}`
if (res.resultCode === 1 && res.result) { });
}
} catch (error) {
console.log(error);
const url = "https://www.baidu.com";
uni.redirectTo({ uni.redirectTo({
url: `/pages/base/course-selection/pay-wait?payUrl=${encodeURIComponent(res.result)}` url: `/pages/base/course-selection/pay-wait?payUrl=${encodeURIComponent(url)}`
}); });
} }
// uni.showLoading({
// title: "...",
// });
// //
// setTimeout(() => {
// uni.hideLoading();
// uni.redirectTo({
// url: "/pages/base/course-selection/payment-success",
// });
// // uni.redirectTo({
// // url: "/pages/base/course-selection/payment-fail",
// // });
// }, 2000);
}; };
onMounted(() => { onMounted(async() => {
// startCountdown(); const res = await jzGetQkExpiredTime({ xsId: getCurXs.id} );
console.log('获取倒计时', res);
seconds = res.result;
startCountdown();
}); });
onUnmounted(() => { onUnmounted(() => {
// if (timer) { if (timer) {
// clearInterval(timer); clearInterval(timer);
// } }
}); });
</script> </script>
@ -167,34 +162,17 @@ onUnmounted(() => {
.payment-page { .payment-page {
min-height: 100%; min-height: 100%;
background-color: #f5f7fa; background-color: #f5f7fa;
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
} }
.nav-bar { //
display: flex; .scrollable-content {
align-items: center; flex: 1;
justify-content: space-between; overflow-y: auto;
padding: 15px; -webkit-overflow-scrolling: touch; // iOS
height: 44px;
background-color: #2879ff;
.nav-left {
width: 40px;
height: 40px;
display: flex;
align-items: center;
}
.nav-title {
font-size: 18px;
font-weight: 500;
color: #fff;
}
.nav-right {
width: 40px;
display: flex;
justify-content: flex-end;
}
} }
.countdown-section { .countdown-section {
@ -226,10 +204,10 @@ onUnmounted(() => {
} }
.payment-footer { .payment-footer {
position: fixed; position: sticky;
bottom: 0;
left: 0; left: 0;
right: 0; right: 0;
bottom: 0;
background-color: #fff; background-color: #fff;
padding: 15px; padding: 15px;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05); box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);

View File

@ -2,12 +2,15 @@ import { defineStore } from "pinia";
import { authenticationApi, loginCode, loginPass, weChatLogin } from "@/api/system/login"; import { authenticationApi, loginCode, loginPass, weChatLogin } from "@/api/system/login";
import { AUTH_KEY } from "@/config"; import { AUTH_KEY } from "@/config";
import { imagUrl } from "@/utils"; import { imagUrl } from "@/utils";
import { useWebSocket } from '@/utils/webSocket/webSocket'
interface UserState { interface UserState {
userdata: any; userdata: any;
curXs: any; curXs: any;
token: string; token: string;
auth: string[] auth: string[],
ws: any,
wsCallback: any
} }
export const useUserStore = defineStore({ export const useUserStore = defineStore({
@ -20,7 +23,9 @@ export const useUserStore = defineStore({
// token // token
token: '', token: '',
//用户注册信息 //用户注册信息
auth: [] auth: [],
ws: null,
wsCallback: (type: string, res: any) => {}
}), }),
getters: { getters: {
getToken(): string { getToken(): string {
@ -49,6 +54,9 @@ export const useUserStore = defineStore({
setAuth(data: string[]) { setAuth(data: string[]) {
this.auth = data; this.auth = data;
}, },
setWsCallback(callback: any) {
this.wsCallback = callback;
},
/** /**
* @description: * @description:
*/ */
@ -99,6 +107,15 @@ export const useUserStore = defineStore({
if (value[AUTH_KEY]) { if (value[AUTH_KEY]) {
this.setToken(value[AUTH_KEY]) this.setToken(value[AUTH_KEY])
} }
if (!this.ws) {
this.ws = useWebSocket(`/zhxy/webSocket/${value.userId}`, (type: string, res: any) => {
// 判断this.wsCallback是函数调用
if (typeof this.wsCallback === "function") {
this.wsCallback(type, res);
}
});
this.ws.reconnect();
}
authenticationApi({ userId: value.userId }).then(({ result }) => { authenticationApi({ userId: value.userId }).then(({ result }) => {
if (result) { if (result) {
this.setAuth(result) this.setAuth(result)
@ -113,6 +130,11 @@ export const useUserStore = defineStore({
this.setUser('') this.setUser('')
this.setCurXs({}) this.setCurXs({})
this.setAuth([]) this.setAuth([])
if (this.ws) {
this.ws.closeConnect();
this.ws = null;
}
this.wsCallback((type: string, res: any) => {});
}, },
}, },
persist: { persist: {