青橙1.1.2

This commit is contained in:
2025-08-16 22:14:42 +08:00
parent a3ffe71ccb
commit 3dc4d64215
33 changed files with 430 additions and 289 deletions

View File

@ -4,104 +4,135 @@ const { notLogin } = require('../../../utils/util')
// pages/course/courseOrderList/courseOrderList.js
Page({
data: {
orderList: [], // 后端返回的订单列表
hasModalShown: false, // 弹框只显示一次
orderList: [], // 后端返回的订单列表
hasModalShown: false, // 本次停留页面只弹一次
isMaskVisible: false
},
onLoad(options) {
// —— 内部状态(不放 data避免多余 setData
_timer: null,
_isActive: false, // 页面是否处于“可见/激活”状态
_justEnteredAt: 0, // 进入页面时刻(可按需使用)
onLoad() {
this._isActive = true;
this._justEnteredAt = Date.now();
this.fetchOrders();
},
onShow() {
this._isActive = true;
this.fetchOrders(); // 如不想重复拉取可注释
},
onHide() {
clearInterval(this._timer);
this._isActive = false;
this._clearTimer();
},
onUnload() {
clearInterval(this._timer);
},
onShow() {
this.fetchOrders()
this._isActive = false;
this._clearTimer();
},
// 拉取后端接口
onPullDownRefresh() {
this.fetchOrders(); // stopPullDownRefresh 在 complete 里
},
// ========= 工具函数 =========
_clearTimer() {
if (this._timer) {
clearInterval(this._timer);
this._timer = null;
}
},
_pad2(n) {
return String(n).padStart(2, '0');
},
_fmtCountDownStr(totalSec) {
const sec = Math.max(0, totalSec | 0);
const m = Math.floor(sec / 60);
const s = sec % 60;
return `${this._pad2(m)}${this._pad2(s)}`;
},
// ========= 拉单 =========
fetchOrders() {
wx.request({
url: baseUrl + '/courseOrder/query/list', // 替换为真实接口
url: baseUrl + '/courseOrder/query/list',
method: 'POST',
header: {
Authorization: wx.getStorageSync('token')
},
header: { Authorization: wx.getStorageSync('token') },
success: res => {
console.log('课程订单列表---->',res.data.data);
if (res.data.code === 1) {
const now = Date.now();
let list = res.data.data.map(item => {
// 计算从 createTime 到 now 剩余秒数
const createMs = new Date(item.createTime.replace(/-/g,'/')).getTime();
let diff = Math.floor((createMs + 15*60*1000 - now) / 1000);
const list = (res.data.data || []).map(it => {
// 解析时间iOS 兼容 + NaN 兜底)
const ts = new Date(String(it.createTime).replace(/-/g, '/')).getTime();
const createMs = Number.isFinite(ts) ? ts : now;
// 只有“待支付”才需要倒计时、过期置“交易取消”
if (item.orderStatus === '待支付') {
if (diff <= 0) {
item.orderStatus = '交易取消';
diff = 0;
// 首次检测到过期就弹框
if (!this.data.hasModalShown) {
wx.showModal({
title: '提示',
content: '订单超时未支付,已取消',
showCancel: false
});
this.setData({ hasModalShown: true });
}
// 统一按 15 分钟有效期(若后端返回 expireTime建议直接用
const ttlMs = 15 * 60 * 1000;
let diff = Math.floor((createMs + ttlMs - now) / 1000);
diff = Math.max(0, diff);
if (it.orderStatus === '待支付') {
it.countDown = diff;
it.countDownStr = this._fmtCountDownStr(diff);
it._expiredNotified = false; // 每单的本地防重复标记
// 初始化阶段如果已经超时diff=0静默转关闭不弹窗
if (diff === 0) {
it.orderStatus = '交易关闭';
it.countDownStr = '';
}
item.countDown = diff;
const m = Math.floor(diff / 60);
const s = diff % 60;
item.countDownStr = `${m}${s < 10 ? '0'+s : s}`;
} else {
item.countDown = 0;
item.countDownStr = '';
it.countDown = 0;
it.countDownStr = '';
}
return item;
});
this.setData({ orderList: list }, () => {
// 初始处理完后启动全局定时器
this.startTimer();
return it;
});
// 渲染 + 启动定时器
this.setData({ orderList: list }, () => this._startTimer());
} else {
notLogin(res.data.message)
notLogin(res.data.message);
}
},
fail: () => {
wx.showToast({ title: '网络错误', icon: 'none' });
},
complete: () => {
wx.stopPullDownRefresh();
}
});
},
// 每秒更新所有待支付订单的倒计时
startTimer() {
// ← 新增:如果已有定时器,先清掉它
if (this._timer) {
clearInterval(this._timer);
}
// ========= 定时器刷新 =========
_startTimer() {
this._clearTimer();
// 如果已经没有“待支付”的订单,就不启动定时器
const hasPending = this.data.orderList.some(o => o.orderStatus === '待支付' && o.countDown > 0);
if (!hasPending) return;
this._timer = setInterval(() => {
// 不在激活页时不做任何 UI 相关动作(同时兜底不弹窗)
if (!this._isActive) return;
let needRerender = false;
const updated = this.data.orderList.map(item => {
if (item.orderStatus === '待支付' && item.countDown > 0) {
const cd = item.countDown - 1;
item.countDown = cd;
const m = Math.floor(cd / 60);
const s = cd % 60;
item.countDownStr = `${m}${s < 10 ? '0'+s : s}`;
const next = item.countDown - 1;
item.countDown = next;
item.countDownStr = this._fmtCountDownStr(next);
needRerender = true;
if (cd <= 0) {
item.orderStatus = '交易取消';
if (next <= 0) {
item.orderStatus = '交易关闭';
item.countDownStr = '';
if (!this.data.hasModalShown) {
// —— 仅在“当前页面可见”时弹一次,并且每单只弹一次;本页全局也只弹一次
if (this._isActive && !item._expiredNotified && !this.data.hasModalShown) {
item._expiredNotified = true;
wx.showModal({
title: '提示',
content: '订单超时未支付,已取消',
content: '订单超时未支付,已关闭',
showCancel: false
});
this.setData({ hasModalShown: true });
@ -110,17 +141,28 @@ Page({
}
return item;
});
this.setData({ orderList: updated });
if (needRerender) {
this.setData({ orderList: updated }, () => {
// 如果已经没有可继续倒计时的订单,关掉定时器
const stillPending = this.data.orderList.some(o => o.orderStatus === '待支付' && o.countDown > 0);
if (!stillPending) this._clearTimer();
});
} else {
// 没有需要更新的也关掉
this._clearTimer();
}
}, 1000);
},
// ========= 支付相关 =========
showIsPayModal(e) {
const orderId = e.currentTarget.dataset.orderId;
wx.showModal({
title: '下单成功',
content: '您确定要支付吗',
title: '确认支付',
content: '是否立即支付该订单',
cancelText: '取消',
confirmText: '确定',
confirmText: '去支付',
success: (res) => {
if (res.confirm) {
this.payOrder(orderId);
@ -130,73 +172,71 @@ Page({
},
fail: () => {
wx.hideLoading();
wx.showToast({
title: '网络错误,下单失败',
icon: 'none'
});
wx.showToast({ title: '网络错误', icon: 'none' });
}
});
},
payOrder(orderId) {
// 同样先显示遮罩
this.setData({ isMaskVisible: true });
wx.showLoading({ title: '支付中...'});
wx.request({
url: baseUrl + '/courseOrder/payment',
method: 'POST',
header: { Authorization: wx.getStorageSync('token') },
data: { id: orderId},
success: res => {
wx.hideLoading();
if (res.data.code === 1) {
// 支付成功,跳转详情页
wx.redirectTo({
url: `/pages/course/orderDetail/orderDetail?id=${orderId}`,
success: res => {
// 先把遮罩关掉
this.setData({ isMaskVisible: false });
}
});
} else {
this.setData({ isMaskVisible: false });
wx.showToast({ title: res.data.message || '支付失败', icon: 'none' });
}
},
fail: () => {
wx.hideLoading();
this.setData({ isMaskVisible: false });
wx.showToast({ title: '网络错误,支付失败', icon: 'none' });
}
});
},
// 跳转订单详情
payOrder(orderId) {
this.setData({ isMaskVisible: true });
wx.showLoading({ title: '支付中...' });
wx.request({
url: baseUrl + '/courseOrder/payment',
method: 'POST',
header: { Authorization: wx.getStorageSync('token') },
data: { id: orderId }, // 与后端约定:支付接口传 id若传 orderId 则改键名
success: res => {
wx.hideLoading();
if (res.data.code === 1) {
wx.redirectTo({
url: `/pages/course/orderDetail/orderDetail?id=${orderId}`,
complete: () => {
this.setData({ isMaskVisible: false });
}
});
} else {
this.setData({ isMaskVisible: false });
wx.showToast({ title: res.data.message || '支付失败', icon: 'none' });
}
},
fail: () => {
wx.hideLoading();
this.setData({ isMaskVisible: false });
wx.showToast({ title: '网络错误,支付失败', icon: 'none' });
}
});
},
// ========= 跳转 & 取消 =========
gotoOrderDetail(e) {
const orderId = e.currentTarget.dataset.id;
wx.navigateTo({
url: `/pages/course/orderDetail/orderDetail?id=${orderId}`,
})
});
},
// 取消订单
cancelOrder(e) {
// console.log(e);
const id = e.currentTarget.dataset.id;
wx.showModal({
title: '取消订单',
content: '是否要取消订单?',
content: '是否要取消订单?',
success: res => {
if (res.confirm) {
wx.request({
url: baseUrl + "/courseOrder/cancel",
method: 'POST',
data: { courseId: id },
header: { Authorization: wx.getStorageSync('token') },
success: () => this.fetchOrders()
data: { id }, // 如果后端需要 { orderId: id },改这里的键名即可
success: r => {
if (r.data && r.data.code !== 1) {
wx.showToast({ title: r.data.message || '取消失败', icon: 'none' });
}
this.fetchOrders();
},
fail: () => wx.showToast({ title: '网络错误', icon: 'none' })
});
}
}
});
}
});

View File

@ -1,3 +1,4 @@
{
"usingComponents": {}
"usingComponents": {},
"enablePullDownRefresh": true
}

View File

@ -14,7 +14,7 @@
<view wx:for="{{ orderList }}"
wx:for-item="item"
wx:for-index="index"
wx:key="item.id"
wx:key="id"
class="card order-item"
bind:tap="gotoOrderDetail"
data-id="{{ item.id }}">