import React, { useRef } from "react"; import Taro from "@tarojs/taro"; import { View, Text, Image, ScrollView } from "@tarojs/components"; import dayjs from "dayjs"; import classnames from "classnames"; import img from "@/config/images"; import { useUserInfo } from "@/store/userStore"; import { formatNtrpDisplay, toast, navto } from "@/utils/helper"; // import RMB_ICON from "@/static/detail/rmb.svg"; import { MATCH_STATUS, IsSubstituteSupported } from "@/services/detailService"; import OrderService from "@/services/orderService"; import styles from "./index.module.scss"; import NTRPEvaluatePopup from "@/components/NTRPEvaluatePopup"; import { EvaluateCallback, EvaluateScene } from "@/store/evaluateStore"; function isFull(counts) { const { max_players, current_players, max_substitute_players, current_substitute_count, is_substitute_supported, } = counts; if ( max_players === current_players && is_substitute_supported === IsSubstituteSupported.NOTSUPPORT ) { return true; } else if ( max_players === current_players && is_substitute_supported === IsSubstituteSupported.SUPPORT ) { return max_substitute_players === current_substitute_count; } return false; } function matchNtrpRequestment( target?: string, min?: string, max?: string ): boolean { // 目标值为空或 undefined if (!target?.trim()) return true; // 提取目标值中的第一个数字 const match = target.match(/-?\d+(\.\d+)?/); if (!match) return true; const value = parseFloat(match[0]); const minNum = min !== undefined ? parseFloat(min) : undefined; const maxNum = max !== undefined ? parseFloat(max) : undefined; // min 和 max 都未定义 → 直接通过 if (minNum === undefined && maxNum === undefined) return true; // min = max 或只有一边 undefined → 参考值判断,包含端点 if (minNum === undefined || maxNum === undefined || minNum === maxNum) { return value >= (minNum ?? maxNum!); } // 正常区间判断,包含端点 return value >= minNum && value <= maxNum; } // 参与者 export default function Participants(props) { const { detail = {}, handleJoinGame, handleViewUserInfo } = props; const ntrpRef = useRef<{ show: (evaluateCallback: EvaluateCallback) => void; }>({ show: () => {} }); const userInfo = useUserInfo(); const participants = detail.participants || []; // const participants = Array(10) // .fill(0) // .map((_, index) => ({ // id: 337 + index, // join_time: "2025-12-06 11:06:24", // status: "joined", // is_organizer: true, // user: { // id: 18, // nickname: "小猫开刀削面店往猫毛里面下面条", // avatar_url: // "https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/d284060f-248b-4d58-a153-4d37c0ca77c8.jpg", // phone: "18513125687", // ntrp_level: "1.5", // }, // })); const { participant_count, max_participants, user_action_status = {}, start_time, price, ntrp_level, skill_level_min, skill_level_max, is_organizer, match_status, end_time, id, } = detail || {}; const { can_join, can_pay, can_substitute, is_substituting, waiting_start } = user_action_status; const showApplicationEntry = [can_pay, can_substitute, is_substituting, waiting_start].every( (item) => !item ) && can_join && dayjs(start_time).isAfter(dayjs()); // 检查手机号绑定的包装函数 const checkPhoneAndExecute = (action: () => void) => { return () => { if (!userInfo?.phone) { Taro.showModal({ title: "提示", content: "该功能需要绑定手机号", confirmText: "去绑定", cancelText: "取消", success: (res) => { if (res.confirm) { const currentPath = Taro.getCurrentInstance().router?.path || ""; const currentParams = Taro.getCurrentInstance().router?.params || {}; const queryString = Object.keys(currentParams) .map((key) => `${key}=${currentParams[key]}`) .join("&"); const fullPath = queryString ? `${currentPath}?${queryString}` : currentPath; Taro.navigateTo({ url: `/login_pages/index/index?redirect=${encodeURIComponent( fullPath )}`, }); } }, }); return; } action(); }; }; const matchNtrpReq = matchNtrpRequestment( userInfo?.ntrp_level, skill_level_min, skill_level_max ); function handleSelfEvaluate() { ntrpRef?.current?.show({ type: EvaluateScene.detail, next: ({ flag, score }) => { if (!matchNtrpRequestment(score, skill_level_min, skill_level_max)) { toast("您当前不符合此球局NTRP水平要求,去看看其他活动吧~"); return; } if (flag) { Taro.navigateTo({ url: `/order_pages/orderDetail/index?gameId=${id}`, }); return; } Taro.redirectTo({ url: `/order_pages/orderDetail/index?gameId=${id}` }); }, onCancel: () => { // Taro.redirectTo({ url: `/game_pages/detail/index?id=${id}` }); Taro.navigateBack(); }, }); } function generateTextAndAction( user_action_status: null | { [key: string]: boolean } ): | undefined | { text: string | React.FC; action?: () => void; available?: boolean } { if (!user_action_status) { return; } // const priceStrArr = price?.toString().split(".") ?? []; // const displayPrice = is_organizer ? ( // <> // 0 // {/* .00 */} // // ) : ( // <> // {priceStrArr[0]} // .{priceStrArr[1]} // // ); // user_action_status.can_assess = true; // user_action_status.can_join = false; // console.log(user_action_status, "user_action"); const { can_assess, can_join, can_substitute, can_pay, is_substituting, waiting_start, } = user_action_status || {}; if (MATCH_STATUS.CANCELED === match_status) { return { text: "活动已取消", available: false, action: () => toast("活动已取消,去看看其他活动吧~"), }; } else if (MATCH_STATUS.FINISHED === match_status) { return { text: "活动已结束", available: false, action: () => toast("活动已结束,去看看其他活动吧~"), }; } else if (dayjs(end_time).isBefore(dayjs())) { return { text: "活动已结束", available: false, action: () => toast("活动已结束,去看看其他活动吧~"), }; } else if (dayjs(start_time).isBefore(dayjs())) { return { text: "活动已开始", available: false, action: () => toast("活动已开始,去看看其他活动吧~"), }; } else if (isFull(detail)) { return { text: "活动已满员", available: false, action: () => toast("活动已满员,去看看其他活动吧~"), }; } if (waiting_start) { return { text: "已加入", action: () => toast("您已参与了本次活动"), }; } else if (is_substituting) { return { text: "已加入候补", action: () => toast("您已加入候补,候补失败会全额退款~"), }; } else if (can_pay) { return { text: "继续支付", action: checkPhoneAndExecute(async () => { const res = await OrderService.getUnpaidOrder(id); if (res.code === 0) { navto( `/order_pages/orderDetail/index?id=${res.data.order_info.order_id}` ); } }), }; } else if (!matchNtrpReq) { return { text: "立即加入1", available: false, action: () => toast("您当前不符合此球局NTRP水平要求,去看看其他活动吧~"), }; } else if (can_substitute) { return { text: "我要候补", action: checkPhoneAndExecute(handleJoinGame), }; } else if (can_join) { return { text: "立即加入2", action: checkPhoneAndExecute(handleJoinGame), }; } else if (can_assess) { return { text: "立即加入3", action: checkPhoneAndExecute(handleSelfEvaluate), }; } return { text: "球局无法加入", available: false, }; } const { action = () => {} } = generateTextAndAction(user_action_status)!; const leftCount = max_participants - participant_count; return ( <> 参与者 · {leftCount > 0 ? `剩余空位 ${leftCount}` : "已满员"} {participant_count > 0 || showApplicationEntry ? ( {/* application */} {showApplicationEntry && ( { action?.(); }} > 申请加入 )} {/* participants list */} {participants.map((participant) => { const { is_organizer, user: { avatar_url, nickname, level, ntrp_level, id: participant_user_id, }, } = participant; const role = is_organizer ? "组织者" : "参与者"; // 优先使用 ntrp_level,如果没有则使用 level const ntrpValue = ntrp_level || level; // 格式化显示 NTRP,如果没有值则显示"初学者" const displayNtrp = ntrpValue ? formatNtrpDisplay(ntrpValue) : "初学者"; return ( {nickname || "未知"} {displayNtrp} {role} ); })} ) : ( "" )} ); }