From 0774cf5ae63b080335c4b9af437ff4752c0898a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=88=90?= Date: Tue, 9 Dec 2025 23:39:34 +0800 Subject: [PATCH 1/4] 1 --- .../detail/components/Participants/index.tsx | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/src/game_pages/detail/components/Participants/index.tsx b/src/game_pages/detail/components/Participants/index.tsx index 64761bb..bc2ec8e 100644 --- a/src/game_pages/detail/components/Participants/index.tsx +++ b/src/game_pages/detail/components/Participants/index.tsx @@ -73,6 +73,7 @@ export default function Participants(props) { }>({ show: () => {} }); const userInfo = useUserInfo(); const participants = detail.participants || []; + const substitute_members = detail.substitute_members || []; // const participants = Array(10) // .fill(0) // .map((_, index) => ({ @@ -92,6 +93,8 @@ export default function Participants(props) { const { participant_count, max_participants, + substitute_count, + max_substitute_players, user_action_status = {}, start_time, price, @@ -293,6 +296,13 @@ export default function Participants(props) { const { action = () => {} } = generateTextAndAction(user_action_status)!; const leftCount = max_participants - participant_count; + const leftSubstituteCount = (max_substitute_players || 0) - (substitute_count || 0); + const showSubstituteApplicationEntry = + [can_pay, can_join, is_substituting, waiting_start].every( + (item) => !item + ) && + can_substitute && + dayjs(start_time).isAfter(dayjs()); return ( <> @@ -389,6 +399,98 @@ export default function Participants(props) { "" )} + {/* 候补区域 */} + {max_substitute_players > 0 && (substitute_count > 0 || showSubstituteApplicationEntry) && ( + + + 候补 + · + {leftSubstituteCount > 0 ? `剩余空位 ${leftSubstituteCount}` : "已满员"} + + + {/* 候补申请入口 */} + {showSubstituteApplicationEntry && ( + { + action?.(); + }} + > + + + 申请候补 + + + )} + {/* 候补成员列表 */} + + + {substitute_members.map((substitute) => { + const { + is_organizer, + user: { + avatar_url, + nickname, + level, + ntrp_level, + id: substitute_user_id, + }, + } = substitute; + const role = is_organizer ? "组织者" : "参与者"; + // 优先使用 ntrp_level,如果没有则使用 level + const ntrpValue = ntrp_level || level; + // 格式化显示 NTRP,如果没有值则显示"初学者" + const displayNtrp = ntrpValue + ? formatNtrpDisplay(ntrpValue) + : "初学者"; + return ( + + + + {nickname || "未知"} + + + {displayNtrp} + + + {role} + + + ); + })} + + + + + )} ); From e2a8ed4e3218384e7cae0af5a2515330056af3ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=88=90?= Date: Wed, 10 Dec 2025 00:14:09 +0800 Subject: [PATCH 2/4] 1 --- src/order_pages/orderDetail/index.tsx | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/order_pages/orderDetail/index.tsx b/src/order_pages/orderDetail/index.tsx index d177b23..9948e56 100644 --- a/src/order_pages/orderDetail/index.tsx +++ b/src/order_pages/orderDetail/index.tsx @@ -626,10 +626,6 @@ const OrderCheck = () => { return; // 未登录或未绑定手机号,已跳转到登录页 } setPaying(true); - Taro.showLoading({ - title: "支付中...", - mask: true, - }); let payment_params = {}; try { @@ -641,7 +637,6 @@ const OrderCheck = () => { }); } await payOrder(payment_params); - Taro.hideLoading(); Taro.showToast({ title: "支付成功", icon: "success", @@ -655,7 +650,6 @@ const OrderCheck = () => { // delta: 1, // }); } catch (error) { - Taro.hideLoading(); Taro.showToast({ title: error.message, icon: "none", @@ -717,15 +711,16 @@ const OrderCheck = () => { {(!id || (order_status === OrderStatus.PENDING && - cancel_type === CancelType.NONE)) && - !paying && ( + cancel_type === CancelType.NONE)) && ( )} From 7b620210a24c38dc11ed3f85149f573132849cc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=88=90?= Date: Wed, 10 Dec 2025 00:35:15 +0800 Subject: [PATCH 3/4] 1 --- src/game_pages/detail/index.tsx | 3 ++- src/store/userStore.ts | 47 ++++++++++++++++++++++++++++++--- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/game_pages/detail/index.tsx b/src/game_pages/detail/index.tsx index c93baec..a65a3e8 100644 --- a/src/game_pages/detail/index.tsx +++ b/src/game_pages/detail/index.tsx @@ -79,7 +79,8 @@ function Index() { }); // 位置更新后,重新获取详情页数据(因为距离等信息可能发生变化) - await fetchDetail(); + // 注意:这里不调用 fetchDetail,避免与 useDidShow 中的调用重复 + // 如果需要更新距离信息,可以在 fetchDetail 成功后根据当前位置重新计算 if (from === "publish") { handleShare(true); } diff --git a/src/store/userStore.ts b/src/store/userStore.ts index 1f5d3a9..5c22707 100644 --- a/src/store/userStore.ts +++ b/src/store/userStore.ts @@ -15,7 +15,7 @@ export interface UserState { fetchUserInfo: () => Promise; updateUserInfo: (userInfo: Partial) => void; nicknameChangeStatus: Partial; - checkNicknameChangeStatus: () => void; + checkNicknameChangeStatus: (force?: boolean) => void; updateNickname: (nickname: string) => void; // NTRP 测试结果缓存 lastTestResult: LastTimeTestResult | null; @@ -32,6 +32,10 @@ const getTimeNextDate = (time: string) => { return `${year}-${month}-${day}`; }; +// 请求锁,避免重复调用 +let isFetchingLastTestResult = false; +let isCheckingNicknameStatus = false; + export const useUser = create()((set) => ({ user: {}, fetchUserInfo: async () => { @@ -87,8 +91,20 @@ export const useUser = create()((set) => ({ } }, nicknameChangeStatus: {}, - checkNicknameChangeStatus: async () => { + checkNicknameChangeStatus: async (force = false) => { + // 如果正在请求中,直接返回,避免重复调用 + if (isCheckingNicknameStatus) { + return; + } + // 如果已经有状态数据且不是强制更新,跳过,避免重复调用 + if (!force) { + const currentState = useUser.getState(); + if (currentState.nicknameChangeStatus && Object.keys(currentState.nicknameChangeStatus).length > 0) { + return; + } + } try { + isCheckingNicknameStatus = true; const res = await checkNicknameChangeStatusApi(); const { next_period_start_time } = res.data; set({ @@ -99,12 +115,15 @@ export const useUser = create()((set) => ({ }); } catch (error) { console.error("检查昵称变更状态失败:", error); + } finally { + isCheckingNicknameStatus = false; } }, updateNickname: async (nickname) => { try { await updateNicknameApi(nickname); - await useUser.getState().checkNicknameChangeStatus(); + // 更新昵称后强制更新状态 + await useUser.getState().checkNicknameChangeStatus(true); set((state) => ({ user: { ...state.user, nickname }, })); @@ -115,7 +134,27 @@ export const useUser = create()((set) => ({ // NTRP 测试结果缓存 lastTestResult: null, fetchLastTestResult: async () => { + // 如果已经有缓存数据,直接返回,避免重复调用 + const currentState = useUser.getState(); + if (currentState.lastTestResult) { + return currentState.lastTestResult; + } + // 如果正在请求中,等待请求完成,避免重复调用 + if (isFetchingLastTestResult) { + // 等待请求完成,最多等待 3 秒 + let waitCount = 0; + while (isFetchingLastTestResult && waitCount < 30) { + await new Promise((resolve) => setTimeout(resolve, 100)); + waitCount++; + const state = useUser.getState(); + if (state.lastTestResult) { + return state.lastTestResult; + } + } + return null; + } try { + isFetchingLastTestResult = true; const res = await evaluateService.getLastResult(); if (res.code === 0) { set({ lastTestResult: res.data }); @@ -125,6 +164,8 @@ export const useUser = create()((set) => ({ } catch (error) { console.error("获取NTRP测试结果失败:", error); return null; + } finally { + isFetchingLastTestResult = false; } }, })); From 46a59ba28226494544d2a2588d80ae6f4bd132f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=9D=B0?= Date: Wed, 10 Dec 2025 20:08:13 +0800 Subject: [PATCH 4/4] =?UTF-8?q?feat:=20refund=20policy=20=E4=BB=8E?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E6=8E=A5=E5=8F=A3=E8=8E=B7=E5=8F=96=E3=80=81?= =?UTF-8?q?=E6=A2=B3=E7=90=86=E8=AE=A2=E5=8D=95=E6=93=8D=E4=BD=9C=E6=8C=89?= =?UTF-8?q?=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/refundPopup/index.tsx | 13 +++--- src/order_pages/orderDetail/index.tsx | 59 +++++++++++++-------------- src/utils/orderActions.ts | 16 ++++++-- 3 files changed, 47 insertions(+), 41 deletions(-) diff --git a/src/components/refundPopup/index.tsx b/src/components/refundPopup/index.tsx index 616a376..cc83046 100644 --- a/src/components/refundPopup/index.tsx +++ b/src/components/refundPopup/index.tsx @@ -46,8 +46,7 @@ function genRefundNotice(refund_policy) { }; } -function renderCancelContent(checkOrderInfo) { - const { refund_policy = [] } = checkOrderInfo; +function renderCancelContent(refund_policy = []) { const current = dayjs(); const policyList = [ { @@ -65,7 +64,6 @@ function renderCancelContent(checkOrderInfo) { }; }), ]; - console.log("policyList", policyList); const targetIndex = policyList.findIndex((item) => item.beforeCurrent); const { notice } = genRefundNotice(refund_policy); return ( @@ -107,7 +105,7 @@ export type RefundRef = { export default forwardRef(function RefundPopup(_props, ref) { const [visible, setVisible] = useState(false); - const [checkOrderInfo, setCheckOrderInfo] = useState({}); + const [refundPolicy, setRefundPolicy] = useState([]); const [orderData, setOrderData] = useState({}); const onDown = useRef<((result: boolean) => void) | null>(null); @@ -116,11 +114,10 @@ export default forwardRef(function RefundPopup(_props, ref) { })); async function onShow(orderItem, onFinish: (result: boolean) => void) { - const { game_info } = orderItem; + const { refund_policy } = orderItem; onDown.current = onFinish; setOrderData(orderItem); - const res = await orderService.getCheckOrderInfo(game_info.id); - setCheckOrderInfo(res.data); + setRefundPolicy(refund_policy); setVisible(true); } @@ -172,7 +169,7 @@ export default forwardRef(function RefundPopup(_props, ref) { onClick={onClose} /> - {renderCancelContent(checkOrderInfo)} + {renderCancelContent(refundPolicy)} diff --git a/src/order_pages/orderDetail/index.tsx b/src/order_pages/orderDetail/index.tsx index 9948e56..5b89268 100644 --- a/src/order_pages/orderDetail/index.tsx +++ b/src/order_pages/orderDetail/index.tsx @@ -380,8 +380,13 @@ function OrderMsg(props) { wechat_contact, price, } = detail; - const { order_no } = orderDetail; - const { order_info: { registrant_phone } = {} } = checkOrderInfo; + const { order_no, registrant_phone: registrant_phone_from_order } = + orderDetail; + const { + order_info: { registrant_phone: registrant_phone_from_check_order } = {}, + } = checkOrderInfo || {}; + const registrant_phone = + registrant_phone_from_order || registrant_phone_from_check_order; const startTime = dayjs(start_time); const endTime = dayjs(end_time); const startDate = startTime.format("YYYY年M月D日"); @@ -402,13 +407,11 @@ function OrderMsg(props) { }, { title: "报名人电话", - // content: registrant_phone, content: registrant_phone ? ( { @@ -427,7 +430,6 @@ function OrderMsg(props) { }, { title: "组织人电话", - // content: wechat_contact, content: wechat_contact && isPhoneNumber(wechat_contact) ? ( { const [id, gameId] = [Number(stringId), Number(stringGameId)]; const [detail, setDetail] = useState({}); const [location, setLocation] = useState([0, 0]); - const [checkOrderInfo, setCheckOrderInfo] = useState({}); + const [checkOrderInfo, setCheckOrderInfo] = useState(); const [orderDetail, setOrderDetail] = useState({}); const { paying, setPaying } = useOrder(); @@ -584,11 +585,11 @@ const OrderCheck = () => { if (res.code === 0) { gameDetail = res.data; } + checkOrder(gameId); } - if (gameDetail.id) { - setDetail(gameDetail); - onInit(gameDetail.id); - } + setDetail(gameDetail); + const location = await getCurrentLocation(); + setLocation([location.latitude, location.longitude]); } async function checkOrder(gid) { @@ -596,12 +597,6 @@ const OrderCheck = () => { setCheckOrderInfo(orderRes.data); } - async function onInit(gid) { - checkOrder(gid); - const location = await getCurrentLocation(); - setLocation([location.latitude, location.longitude]); - } - async function getPaymentParams() { // 检查登录状态和手机号(创建订单前检查) if (!requireLoginWithPhone()) { @@ -706,23 +701,27 @@ const OrderCheck = () => { checkOrderInfo={checkOrderInfo} /> {/* Refund policy */} - + {/* Disclaimer */} {(!id || (order_status === OrderStatus.PENDING && cancel_type === CancelType.NONE)) && ( - - )} + + )} ); }; diff --git a/src/utils/orderActions.ts b/src/utils/orderActions.ts index eb6b3b6..045a8fc 100644 --- a/src/utils/orderActions.ts +++ b/src/utils/orderActions.ts @@ -6,18 +6,28 @@ export function getOrderStatus(orderData) { if (!order_no) { return 'none' } - const { start_time } = game_info + const { start_time } = game_info || {} + if (!start_time) { console.log('活动开始时间未找到, start_time: ', start_time); } const unPay = order_status === OrderStatus.PENDING && ([CancelType.NONE].includes(cancel_type)); const refund = [RefundStatus.SUCCESS].includes(refund_status); const refunding = [RefundStatus.PENDING].includes(refund_status); const expired = order_status === OrderStatus.FINISHED; - const frozen = dayjs().add(2, 'h').isAfter(dayjs(start_time)) + const frozen = dayjs().isAfter(dayjs(start_time)) const canceled = [CancelType.TIMEOUT, CancelType.USER].includes(cancel_type); - return unPay ? 'unpay' : refund ? 'refund' : canceled ? 'canceled' : expired ? 'expired' : refunding ? 'refunding' : frozen ? 'start' : 'progress' + // return unPay ? 'unpay' : refund ? 'refund' : canceled ? 'canceled' : expired ? 'expired' : refunding ? 'refunding' : is_substitute_order ? 'progress' : frozen ? 'start' : 'progress' + if (unPay) return 'unpay'; + if (refund) return 'refund'; + if (canceled) return 'canceled'; + if (expired) return 'expired'; + if (refunding) return 'refunding'; + if (frozen) return 'start'; + // if (is_substitute_order) return 'progress'; + return 'progress'; + } // scene: list、detail