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}
);
})}
) : (
""
)}
>
);
}