227 lines
7.4 KiB
TypeScript
227 lines
7.4 KiB
TypeScript
import { request } from "../../utils/request";
|
|
import { receiveRedPacket } from "../../utils/util";
|
|
|
|
const app = getApp<IAppOption>();
|
|
|
|
const formatDate = (isoString: string): string => {
|
|
const date = new Date(isoString);
|
|
const year = date.getFullYear();
|
|
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
const day = String(date.getDate()).padStart(2, "0");
|
|
const hours = String(date.getHours()).padStart(2, "0");
|
|
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
|
};
|
|
|
|
const RED_STATE_MAP: Record<number, string> = {
|
|
10: "未发送",
|
|
11: "等待领取",
|
|
20: "正在发送",
|
|
21: "等待确认",
|
|
30: "发送失败",
|
|
31: "个数不够",
|
|
32: "账户余额不足",
|
|
33: "客户未实名",
|
|
34: "其它原因",
|
|
40: "已领取",
|
|
50: "已过期",
|
|
};
|
|
|
|
const getStateText = (state: number) => {
|
|
return RED_STATE_MAP[state] || "发放中";
|
|
};
|
|
|
|
const FAILED_STATES = [30, 31, 32, 33, 34];
|
|
|
|
const canReceive = (state: number) => {
|
|
return state === 11 || state === 21 || FAILED_STATES.includes(state);
|
|
};
|
|
|
|
const isRetry = (state: number) => {
|
|
return FAILED_STATES.includes(state);
|
|
};
|
|
|
|
// 领取后延迟刷新的定时器 ID,用于在切 tab 或页面卸载时取消
|
|
let refreshTimer: ReturnType<typeof setTimeout> | null = null;
|
|
|
|
// 缓存登录信息,避免每次请求重复读取 storage
|
|
let cachedOpenId = wx.getStorageSync("openId");
|
|
let cachedAppId = wx.getStorageSync("appId");
|
|
|
|
Page({
|
|
data: {
|
|
list: [] as any[],
|
|
loading: false,
|
|
hasMore: true,
|
|
current: 1,
|
|
pageSize: 15,
|
|
activeTab: 0,
|
|
tabs: [
|
|
{ label: '待领取', value: 0, type: 1 },
|
|
{ label: '已领取', value: 1, type: 2 },
|
|
{ label: '已过期', value: 2, type: 3 },
|
|
],
|
|
summary: {
|
|
totalAmount: '0.00',
|
|
receivedAmount: '0.00',
|
|
pendingAmount: '0.00',
|
|
expiredAmount: '0.00',
|
|
},
|
|
safeBottom: app.globalData.safeBottom,
|
|
safeTop: app.globalData.safeTop,
|
|
},
|
|
|
|
async onLoad() {
|
|
await this.fetchRecords(1, false, this.data.tabs[0].type);
|
|
},
|
|
|
|
onReachBottom() {
|
|
if (this.data.loading || !this.data.hasMore) return;
|
|
// 有延迟刷新待执行时,不触发滚动加载,避免重复请求
|
|
if (refreshTimer) return;
|
|
const nextPage = this.data.current + 1;
|
|
this.fetchRecords(nextPage, true, this.data.tabs[this.data.activeTab].type);
|
|
},
|
|
|
|
onTabChange(e: any) {
|
|
const index = e.detail.index;
|
|
const tab = this.data.tabs[index];
|
|
this.setData({ activeTab: index });
|
|
// 切换 tab 时取消待执行的延迟刷新,避免用旧 tab 发无效请求
|
|
if (refreshTimer) {
|
|
clearTimeout(refreshTimer);
|
|
refreshTimer = null;
|
|
}
|
|
this.fetchRecords(1, false, tab.type);
|
|
},
|
|
|
|
onUnload() {
|
|
// 页面卸载时取消待执行的延迟刷新
|
|
if (refreshTimer) {
|
|
clearTimeout(refreshTimer);
|
|
refreshTimer = null;
|
|
}
|
|
},
|
|
|
|
async handleReceive(e: any) {
|
|
const { id, outrewardsid } = e.currentTarget.dataset;
|
|
|
|
// 防止重复点击
|
|
if (this.data.loading) return;
|
|
|
|
const params: Record<string, any> = {
|
|
appId: cachedAppId,
|
|
openId: cachedOpenId,
|
|
};
|
|
if (outrewardsid) params.outRewardsId = outrewardsid;
|
|
if (id) params.redId = id;
|
|
|
|
try {
|
|
await receiveRedPacket(
|
|
params,
|
|
'/app/general/receive-red',
|
|
cachedAppId
|
|
);
|
|
|
|
// 乐观更新:先立即更新本地数据,避免后端状态同步延迟导致展示不友好
|
|
const { list, summary } = this.data;
|
|
const targetItem = list.find((item: any) => item.id === id);
|
|
|
|
if (targetItem) {
|
|
const amount = targetItem.amount || 0;
|
|
const amountYuan = amount / 100;
|
|
|
|
const updatedList = list.map((item: any) =>
|
|
item.id === id
|
|
? { ...item, state: 40, stateText: getStateText(40), canReceive: false, isRetry: false }
|
|
: item
|
|
);
|
|
|
|
const updatedSummary = {
|
|
...summary,
|
|
pendingAmount: Math.max(0, parseFloat(summary.pendingAmount) - amountYuan).toFixed(2),
|
|
receivedAmount: (parseFloat(summary.receivedAmount) + amountYuan).toFixed(2),
|
|
};
|
|
|
|
this.setData({ list: updatedList, summary: updatedSummary });
|
|
}
|
|
|
|
// 确保后端有足够时间同步后刷新,使用实时 activeTab 避免闭包过期
|
|
this.setData({ loading: true });
|
|
refreshTimer = setTimeout(() => {
|
|
refreshTimer = null;
|
|
this.setData({ loading: false });
|
|
this.fetchRecords(1, false, this.data.tabs[this.data.activeTab].type);
|
|
}, 3000);
|
|
} catch (err) {
|
|
this.setData({ loading: false });
|
|
console.error('领取红包失败:', err);
|
|
}
|
|
},
|
|
|
|
async fetchRecords(page: number, append = false, type: number) {
|
|
if (!cachedOpenId) {
|
|
wx.showToast({ title: "请先登录", icon: "none" });
|
|
return;
|
|
}
|
|
|
|
if (append) {
|
|
this.setData({ loading: true });
|
|
} else {
|
|
wx.showLoading({ title: "加载中" });
|
|
}
|
|
|
|
try {
|
|
const res = await request({
|
|
options: {
|
|
url: "/app/saas/query-red",
|
|
data: {
|
|
openId: cachedOpenId,
|
|
appId: cachedAppId,
|
|
current: page,
|
|
pageSize: this.data.pageSize,
|
|
type,
|
|
},
|
|
},
|
|
isLoading: !append,
|
|
});
|
|
|
|
const records = res.data?.records || res.records || [];
|
|
const items = records.map((item: any) => ({
|
|
...item,
|
|
date: formatDate(item.createdTime),
|
|
stateText: getStateText(item.state),
|
|
amountText: `${(item.amount / 100).toFixed(2)}`,
|
|
canReceive: canReceive(item.state),
|
|
isRetry: isRetry(item.state),
|
|
}));
|
|
|
|
this.setData({
|
|
list: append ? [...this.data.list, ...items] : items,
|
|
current: page,
|
|
hasMore: records.length >= this.data.pageSize,
|
|
});
|
|
|
|
// 汇总数据在 res.data 根层级,单位是分,转为元
|
|
const d = res.data;
|
|
if (d && d.totalAmount !== undefined) {
|
|
this.setData({
|
|
summary: {
|
|
totalAmount: (d.totalAmount / 100).toFixed(2),
|
|
receivedAmount: (d.receivedAmount / 100).toFixed(2),
|
|
pendingAmount: (d.pendingAmount / 100).toFixed(2),
|
|
expiredAmount: (d.expiredAmount / 100).toFixed(2),
|
|
},
|
|
});
|
|
}
|
|
} catch (err) {
|
|
console.error("加载红包记录失败:", err);
|
|
} finally {
|
|
if (append) {
|
|
this.setData({ loading: false });
|
|
} else {
|
|
wx.hideLoading();
|
|
}
|
|
}
|
|
},
|
|
}); |