2025-07-20 18:22:59 +08:00
|
|
|
|
import { baseUrl } from "../../../request";
|
2025-08-15 00:36:04 +08:00
|
|
|
|
const { notLogin } = require('../../../utils/util')
|
2025-07-20 18:22:59 +08:00
|
|
|
|
|
2025-06-25 15:45:25 +08:00
|
|
|
|
// pages/course/courseOrderList/courseOrderList.js
|
|
|
|
|
Page({
|
|
|
|
|
data: {
|
2025-08-16 22:14:42 +08:00
|
|
|
|
orderList: [], // 后端返回的订单列表
|
|
|
|
|
hasModalShown: false, // 本次停留页面只弹一次
|
2025-08-08 19:21:04 +08:00
|
|
|
|
isMaskVisible: false
|
2025-06-25 15:45:25 +08:00
|
|
|
|
},
|
|
|
|
|
|
2025-08-16 22:14:42 +08:00
|
|
|
|
// —— 内部状态(不放 data,避免多余 setData)
|
|
|
|
|
_timer: null,
|
|
|
|
|
_isActive: false, // 页面是否处于“可见/激活”状态
|
|
|
|
|
_justEnteredAt: 0, // 进入页面时刻(可按需使用)
|
|
|
|
|
|
|
|
|
|
onLoad() {
|
|
|
|
|
this._isActive = true;
|
|
|
|
|
this._justEnteredAt = Date.now();
|
2025-07-20 18:22:59 +08:00
|
|
|
|
this.fetchOrders();
|
2025-06-25 15:45:25 +08:00
|
|
|
|
},
|
2025-08-16 22:14:42 +08:00
|
|
|
|
onShow() {
|
|
|
|
|
this._isActive = true;
|
|
|
|
|
this.fetchOrders(); // 如不想重复拉取可注释
|
|
|
|
|
},
|
2025-06-25 15:45:25 +08:00
|
|
|
|
onHide() {
|
2025-08-16 22:14:42 +08:00
|
|
|
|
this._isActive = false;
|
|
|
|
|
this._clearTimer();
|
2025-06-25 15:45:25 +08:00
|
|
|
|
},
|
|
|
|
|
onUnload() {
|
2025-08-16 22:14:42 +08:00
|
|
|
|
this._isActive = false;
|
|
|
|
|
this._clearTimer();
|
2025-07-20 18:22:59 +08:00
|
|
|
|
},
|
2025-08-16 22:14:42 +08:00
|
|
|
|
|
|
|
|
|
onPullDownRefresh() {
|
|
|
|
|
this.fetchOrders(); // stopPullDownRefresh 在 complete 里
|
2025-06-25 15:45:25 +08:00
|
|
|
|
},
|
|
|
|
|
|
2025-08-16 22:14:42 +08:00
|
|
|
|
// ========= 工具函数 =========
|
|
|
|
|
_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)}秒`;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// ========= 拉单 =========
|
2025-07-20 18:22:59 +08:00
|
|
|
|
fetchOrders() {
|
|
|
|
|
wx.request({
|
2025-08-16 22:14:42 +08:00
|
|
|
|
url: baseUrl + '/courseOrder/query/list',
|
2025-07-20 18:22:59 +08:00
|
|
|
|
method: 'POST',
|
2025-08-16 22:14:42 +08:00
|
|
|
|
header: { Authorization: wx.getStorageSync('token') },
|
2025-07-20 18:22:59 +08:00
|
|
|
|
success: res => {
|
|
|
|
|
if (res.data.code === 1) {
|
|
|
|
|
const now = Date.now();
|
2025-08-16 22:14:42 +08:00
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
// 统一按 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 = '';
|
2025-07-20 18:22:59 +08:00
|
|
|
|
}
|
|
|
|
|
} else {
|
2025-08-16 22:14:42 +08:00
|
|
|
|
it.countDown = 0;
|
|
|
|
|
it.countDownStr = '';
|
2025-07-20 18:22:59 +08:00
|
|
|
|
}
|
2025-08-16 22:14:42 +08:00
|
|
|
|
return it;
|
2025-07-20 18:22:59 +08:00
|
|
|
|
});
|
|
|
|
|
|
2025-08-16 22:14:42 +08:00
|
|
|
|
// 渲染 + 启动定时器
|
|
|
|
|
this.setData({ orderList: list }, () => this._startTimer());
|
2025-07-20 18:22:59 +08:00
|
|
|
|
} else {
|
2025-08-16 22:14:42 +08:00
|
|
|
|
notLogin(res.data.message);
|
2025-07-20 18:22:59 +08:00
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
fail: () => {
|
|
|
|
|
wx.showToast({ title: '网络错误', icon: 'none' });
|
2025-08-16 22:14:42 +08:00
|
|
|
|
},
|
|
|
|
|
complete: () => {
|
|
|
|
|
wx.stopPullDownRefresh();
|
2025-06-25 15:45:25 +08:00
|
|
|
|
}
|
2025-07-20 18:22:59 +08:00
|
|
|
|
});
|
2025-06-25 15:45:25 +08:00
|
|
|
|
},
|
|
|
|
|
|
2025-08-16 22:14:42 +08:00
|
|
|
|
// ========= 定时器刷新 =========
|
|
|
|
|
_startTimer() {
|
|
|
|
|
this._clearTimer();
|
|
|
|
|
// 如果已经没有“待支付”的订单,就不启动定时器
|
|
|
|
|
const hasPending = this.data.orderList.some(o => o.orderStatus === '待支付' && o.countDown > 0);
|
|
|
|
|
if (!hasPending) return;
|
|
|
|
|
|
2025-07-20 18:22:59 +08:00
|
|
|
|
this._timer = setInterval(() => {
|
2025-08-16 22:14:42 +08:00
|
|
|
|
// 不在激活页时不做任何 UI 相关动作(同时兜底不弹窗)
|
|
|
|
|
if (!this._isActive) return;
|
|
|
|
|
|
|
|
|
|
let needRerender = false;
|
2025-07-20 18:22:59 +08:00
|
|
|
|
const updated = this.data.orderList.map(item => {
|
|
|
|
|
if (item.orderStatus === '待支付' && item.countDown > 0) {
|
2025-08-16 22:14:42 +08:00
|
|
|
|
const next = item.countDown - 1;
|
|
|
|
|
item.countDown = next;
|
|
|
|
|
item.countDownStr = this._fmtCountDownStr(next);
|
|
|
|
|
needRerender = true;
|
|
|
|
|
|
|
|
|
|
if (next <= 0) {
|
|
|
|
|
item.orderStatus = '交易关闭';
|
2025-07-20 18:22:59 +08:00
|
|
|
|
item.countDownStr = '';
|
2025-08-16 22:14:42 +08:00
|
|
|
|
|
|
|
|
|
// —— 仅在“当前页面可见”时弹一次,并且每单只弹一次;本页全局也只弹一次
|
|
|
|
|
if (this._isActive && !item._expiredNotified && !this.data.hasModalShown) {
|
|
|
|
|
item._expiredNotified = true;
|
2025-07-20 18:22:59 +08:00
|
|
|
|
wx.showModal({
|
|
|
|
|
title: '提示',
|
2025-08-16 22:14:42 +08:00
|
|
|
|
content: '订单超时未支付,已关闭',
|
2025-07-20 18:22:59 +08:00
|
|
|
|
showCancel: false
|
|
|
|
|
});
|
|
|
|
|
this.setData({ hasModalShown: true });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return item;
|
|
|
|
|
});
|
2025-08-16 22:14:42 +08:00
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
}
|
2025-07-20 18:22:59 +08:00
|
|
|
|
}, 1000);
|
2025-06-25 15:45:25 +08:00
|
|
|
|
},
|
|
|
|
|
|
2025-08-16 22:14:42 +08:00
|
|
|
|
// ========= 支付相关 =========
|
2025-08-08 19:21:04 +08:00
|
|
|
|
showIsPayModal(e) {
|
|
|
|
|
const orderId = e.currentTarget.dataset.orderId;
|
|
|
|
|
wx.showModal({
|
2025-08-16 22:14:42 +08:00
|
|
|
|
title: '确认支付',
|
|
|
|
|
content: '是否立即支付该订单?',
|
2025-08-08 19:21:04 +08:00
|
|
|
|
cancelText: '取消',
|
2025-08-16 22:14:42 +08:00
|
|
|
|
confirmText: '去支付',
|
2025-08-08 19:21:04 +08:00
|
|
|
|
success: (res) => {
|
|
|
|
|
if (res.confirm) {
|
|
|
|
|
this.payOrder(orderId);
|
|
|
|
|
} else if (res.cancel) {
|
|
|
|
|
this.setData({ isMaskVisible: false });
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
fail: () => {
|
|
|
|
|
wx.hideLoading();
|
2025-08-16 22:14:42 +08:00
|
|
|
|
wx.showToast({ title: '网络错误', icon: 'none' });
|
2025-08-08 19:21:04 +08:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
},
|
2025-08-16 22:14:42 +08:00
|
|
|
|
|
2025-08-08 19:21:04 +08:00
|
|
|
|
payOrder(orderId) {
|
2025-08-16 22:14:42 +08:00
|
|
|
|
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' });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// ========= 跳转 & 取消 =========
|
2025-07-20 18:22:59 +08:00
|
|
|
|
gotoOrderDetail(e) {
|
|
|
|
|
const orderId = e.currentTarget.dataset.id;
|
|
|
|
|
wx.navigateTo({
|
|
|
|
|
url: `/pages/course/orderDetail/orderDetail?id=${orderId}`,
|
2025-08-16 22:14:42 +08:00
|
|
|
|
});
|
2025-07-20 18:22:59 +08:00
|
|
|
|
},
|
2025-06-25 15:45:25 +08:00
|
|
|
|
|
2025-08-06 13:16:08 +08:00
|
|
|
|
cancelOrder(e) {
|
|
|
|
|
const id = e.currentTarget.dataset.id;
|
2025-07-20 18:22:59 +08:00
|
|
|
|
wx.showModal({
|
|
|
|
|
title: '取消订单',
|
2025-08-16 22:14:42 +08:00
|
|
|
|
content: '是否要取消该订单?',
|
2025-07-20 18:22:59 +08:00
|
|
|
|
success: res => {
|
|
|
|
|
if (res.confirm) {
|
|
|
|
|
wx.request({
|
|
|
|
|
url: baseUrl + "/courseOrder/cancel",
|
|
|
|
|
method: 'POST',
|
|
|
|
|
header: { Authorization: wx.getStorageSync('token') },
|
2025-08-16 22:14:42 +08:00
|
|
|
|
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' })
|
2025-07-20 18:22:59 +08:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
2025-08-08 19:21:04 +08:00
|
|
|
|
}
|
2025-06-25 15:45:25 +08:00
|
|
|
|
});
|