This commit is contained in:
张成
2025-11-15 22:05:33 +08:00
28 changed files with 1080 additions and 646 deletions

View File

@@ -104,7 +104,12 @@ const NTRPEvaluatePopup = (props: NTRPEvaluatePopupProps, ref) => {
async function getNtrp() { async function getNtrp() {
const res = await evaluateService.getLastResult(); const res = await evaluateService.getLastResult();
if (res.code === 0 && res.data.has_ntrp_level) { if (res.code === 0 && res.data.has_ntrp_level) {
setNtrp(res.data.user_ntrp_level); const match = res.data.user_ntrp_level.match(/-?\d+(\.\d+)?/);
if (!match) {
setNtrp("");
return;
}
setNtrp(match[0] as string);
} else { } else {
setNtrp(""); setNtrp("");
} }
@@ -128,7 +133,7 @@ const NTRPEvaluatePopup = (props: NTRPEvaluatePopupProps, ref) => {
title: "NTRP水平修改成功", title: "NTRP水平修改成功",
icon: "none", icon: "none",
}); });
evaCallback.next(true); evaCallback.next({ flag: true, score: String(ntrp) });
handleClose(); handleClose();
} }

View File

@@ -1,8 +1,9 @@
import React, { useEffect } from "react"; import React, { useState, useEffect, useCallback, memo } from "react";
import { View, Image, Text } from "@tarojs/components"; import { View, Image, Text } from "@tarojs/components";
import Taro from "@tarojs/taro"; import Taro from "@tarojs/taro";
import { useUserInfo, useUserActions } from "@/store/userStore"; import { useUserInfo, useUserActions } from "@/store/userStore";
// import { getCurrentFullPath } from "@/utils"; // import { getCurrentFullPath } from "@/utils";
import evaluateService, { StageType } from "@/services/evaluateService";
import DocCopy from "@/static/ntrp/ntrp_doc_copy.svg"; import DocCopy from "@/static/ntrp/ntrp_doc_copy.svg";
import ArrowRight from "@/static/ntrp/ntrp_arrow_right_color.svg"; import ArrowRight from "@/static/ntrp/ntrp_arrow_right_color.svg";
import { import {
@@ -16,18 +17,39 @@ function NTRPTestEntryCard(props: {
type: EvaluateScene; type: EvaluateScene;
evaluateCallback?: EvaluateCallback; evaluateCallback?: EvaluateCallback;
}) { }) {
const [testFlag, setTestFlag] = useState(false);
const { type, evaluateCallback } = props; const { type, evaluateCallback } = props;
const userInfo = useUserInfo(); const userInfo = useUserInfo();
const { setCallback } = useEvaluate(); const { setCallback } = useEvaluate();
// const { fetchUserInfo } = useUserActions() const { fetchUserInfo } = useUserActions();
// useEffect(() => { console.log(userInfo);
// fetchUserInfo()
// }, [])
function handleTest() { useEffect(() => {
if (!userInfo.id) {
fetchUserInfo();
}
evaluateService.getLastResult().then((res) => {
setTestFlag(res.code === 0 && res.data.has_ntrp_level);
});
}, [userInfo.id]);
const handleTest = useCallback(
function () {
switch (type) { switch (type) {
case (EvaluateScene.list, EvaluateScene.share): case EvaluateScene.list:
setCallback({
type,
next: () => {
Taro.redirectTo({ url: "/game_pages/list/index" });
},
onCancel: () => {
// Taro.redirectTo({ url: "/game_pages/list/index" });
Taro.navigateBack();
},
});
break;
case EvaluateScene.share:
setCallback({ setCallback({
type, type,
next: () => { next: () => {
@@ -37,18 +59,35 @@ function NTRPTestEntryCard(props: {
Taro.redirectTo({ url: "/main_pages/index" }); Taro.redirectTo({ url: "/main_pages/index" });
}, },
}); });
case (EvaluateScene.detail, EvaluateScene.publish): break;
case EvaluateScene.detail:
case EvaluateScene.publish:
setCallback(evaluateCallback as EvaluateCallback); setCallback(evaluateCallback as EvaluateCallback);
case (EvaluateScene.user, EvaluateScene.userEdit): break;
case EvaluateScene.user:
setCallback({ setCallback({
type, type,
next: () => { next: () => {
Taro.redirectTo({ url: "/main_pages/index" }); Taro.redirectTo({ url: "/main_pages/index" });
}, },
onCancel: () => { onCancel: () => {
Taro.redirectTo({ url: "/user_pages/myself/index" }); // Taro.redirectTo({ url: "/user_pages/myself/index" });
Taro.navigateBack();
}, },
}); });
break;
case EvaluateScene.userEdit:
setCallback({
type,
next: () => {
Taro.redirectTo({ url: "/game_pages/list/index" });
},
onCancel: () => {
// Taro.redirectTo({ url: "/user_pages/edit/index" });
Taro.navigateBack();
},
});
break;
default: default:
setCallback({ setCallback({
type, type,
@@ -60,10 +99,14 @@ function NTRPTestEntryCard(props: {
}, },
}); });
} }
Taro.redirectTo({ Taro.navigateTo({
url: `/other_pages/ntrp-evaluate/index?stage=test`, url: `/other_pages/ntrp-evaluate/index?stage=${
testFlag ? StageType.INTRO : StageType.TEST
}`,
}); });
} },
[setCallback]
);
return type === EvaluateScene.list ? ( return type === EvaluateScene.list ? (
<View className={styles.higher} onClick={handleTest}> <View className={styles.higher} onClick={handleTest}>
@@ -131,4 +174,5 @@ function NTRPTestEntryCard(props: {
); );
} }
export default NTRPTestEntryCard; export default memo(NTRPTestEntryCard);
// export default NTRPTestEntryCard;

View File

@@ -55,7 +55,7 @@ const PublishMenu: React.FC<PublishMenuProps> = (props) => {
if (!userInfo.ntrp_level) { if (!userInfo.ntrp_level) {
ntrpRef.current.show({ ntrpRef.current.show({
type: EvaluateScene.publish, type: EvaluateScene.publish,
next: (flag) => { next: ({ flag }) => {
if (flag) { if (flag) {
handleMenuClick(type); handleMenuClick(type);
} else if (type === "ai") { } else if (type === "ai") {

View File

@@ -8,7 +8,7 @@ import { setStorage, getStorage } from "@/store/storage";
import { NTRPTestEntryCard } from "@/components"; import { NTRPTestEntryCard } from "@/components";
import { EvaluateScene } from "@/store/evaluateStore"; import { EvaluateScene } from "@/store/evaluateStore";
import "./index.scss"; import "./index.scss";
import { useRef, useEffect, useState } from "react"; import { useRef, useEffect, useState, useMemo } from "react";
const ListContainer = (props) => { const ListContainer = (props) => {
const { const {
@@ -26,6 +26,7 @@ const ListContainer = (props) => {
style, style,
collapse = false, collapse = false,
defaultShowNum, defaultShowNum,
evaluateFlag,
} = props; } = props;
const timerRef = useRef<NodeJS.Timeout | null>(null); const timerRef = useRef<NodeJS.Timeout | null>(null);
const loadingStartTimeRef = useRef<number | null>(null); const loadingStartTimeRef = useRef<number | null>(null);
@@ -47,12 +48,10 @@ const ListContainer = (props) => {
}); });
useEffect(() => { useEffect(() => {
setShowNumber( setShowNumber(() => {
() => { return defaultShowNum === undefined ? data?.length : defaultShowNum;
return defaultShowNum === undefined ? data?.length : defaultShowNum });
}, [data]);
})
}, [data])
// 控制骨架屏显示逻辑 // 控制骨架屏显示逻辑
useEffect(() => { useEffect(() => {
@@ -102,6 +101,7 @@ const ListContainer = (props) => {
// 对于没有ntrp等级的用户每个月展示一次, 插在第三个位置 // 对于没有ntrp等级的用户每个月展示一次, 插在第三个位置
function insertEvaluateCard(list) { function insertEvaluateCard(list) {
if (!evaluateFlag) return list;
if (!list || list.length === 0) { if (!list || list.length === 0) {
return list; return list;
} }
@@ -122,6 +122,11 @@ const ListContainer = (props) => {
return [item1, item2, item3, { type: "evaluateCard" }, ...rest]; return [item1, item2, item3, { type: "evaluateCard" }, ...rest];
} }
const memoizedList = useMemo(
() => insertEvaluateCard(data),
[evaluateFlag, data, userInfo.ntrp_level]
);
// 渲染列表 // 渲染列表
const renderList = (list) => { const renderList = (list) => {
// 请求数据为空 // 请求数据为空
@@ -137,12 +142,12 @@ const ListContainer = (props) => {
); );
} }
showNumber !== undefined && (list = list.slice(0, showNumber)) showNumber !== undefined && (list = list.slice(0, showNumber));
// 渲染数据 // 渲染数据
return ( return (
<> <>
{insertEvaluateCard(list).map((match, index) => { {memoizedList.map((match, index) => {
if (match.type === "evaluateCard") { if (match.type === "evaluateCard") {
return ( return (
<NTRPTestEntryCard key="evaluate" type={EvaluateScene.list} /> <NTRPTestEntryCard key="evaluate" type={EvaluateScene.list} />
@@ -164,20 +169,33 @@ const ListContainer = (props) => {
</View> </View>
{renderList(recommendList)} */} {renderList(recommendList)} */}
{/* 到底了 */} {/* 到底了 */}
{collapse ? {collapse ? (
data?.length > defaultShowNum ? data?.length > defaultShowNum ? (
data?.length > showNumber ? data?.length > showNumber ? (
<View className="collapse-btn fold" onClick={() => { setShowNumber(data?.length) }}> <View
className="collapse-btn fold"
onClick={() => {
setShowNumber(data?.length);
}}
>
<Text></Text> <Text></Text>
<Image src={require("@/static/userInfo/fold.svg")}></Image> <Image src={require("@/static/userInfo/fold.svg")}></Image>
</View> : </View>
<View className="collapse-btn" onClick={() => { setShowNumber(defaultShowNum) }}> ) : (
<View
className="collapse-btn"
onClick={() => {
setShowNumber(defaultShowNum);
}}
>
<Text></Text> <Text></Text>
<Image src={require("@/static/userInfo/fold.svg")}></Image> <Image src={require("@/static/userInfo/fold.svg")}></Image>
</View> </View>
: )
null ) : null
: data?.length > 0 && <View className="bottomTextWrapper"></View>} ) : (
data?.length > 0 && <View className="bottomTextWrapper"></View>
)}
</View> </View>
); );
}; };

View File

@@ -204,6 +204,9 @@ export default function GameInfo(props) {
// hide business msg // hide business msg
showLocation showLocation
theme="dark" theme="dark"
enableScroll={false}
enableZoom={false}
onTap={openMap}
/> />
)} )}
</View> </View>

View File

@@ -1,5 +1,5 @@
import { View, Text } from "@tarojs/components"; import { View, Text } from "@tarojs/components";
import { genNTRPRequirementText } from "../../utils/helper"; import { genNTRPRequirementText } from "@/utils/helper";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
// 玩法要求 // 玩法要求

View File

@@ -6,7 +6,7 @@ import { calculateDistance } from "@/utils";
import { useUserInfo } from "@/store/userStore"; import { useUserInfo } from "@/store/userStore";
import * as LoginService from "@/services/loginService"; import * as LoginService from "@/services/loginService";
import img from "@/config/images"; import img from "@/config/images";
import { navto } from "../../utils/helper"; import { navto } from "@/utils/helper";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
function genRecommendGames(games, location, avatar) { function genRecommendGames(games, location, avatar) {

View File

@@ -12,7 +12,7 @@ import WechatLogo from "@/static/detail/wechat_icon.svg";
// import WechatTimeline from "@/static/detail/wechat_timeline.svg"; // import WechatTimeline from "@/static/detail/wechat_timeline.svg";
import LinkIcon from "@/static/detail/link.svg"; import LinkIcon from "@/static/detail/link.svg";
import CrossIcon from "@/static/detail/cross.svg"; import CrossIcon from "@/static/detail/cross.svg";
import { genNTRPRequirementText, navto } from "../../utils/helper"; import { genNTRPRequirementText, navto } from "@/utils/helper";
import { DayOfWeekMap } from "../../config"; import { DayOfWeekMap } from "../../config";
import styles from "./index.module.scss"; import styles from "./index.module.scss";

View File

@@ -88,7 +88,7 @@
&.disabled > .sticky-bottom-bar-join-game { &.disabled > .sticky-bottom-bar-join-game {
background: #b4b4b4; background: #b4b4b4;
color: rgba(60, 60, 67, 0.6); color: rgba(60, 60, 67, 0.6);
pointer-events: none; // pointer-events: none;
} }
.sticky-bottom-bar-join-game { .sticky-bottom-bar-join-game {
@@ -147,6 +147,7 @@
font-feature-settings: "liga" off, "clig" off; font-feature-settings: "liga" off, "clig" off;
font-family: "DingTalk JinBuTi"; font-family: "DingTalk JinBuTi";
// font-style: italic; // font-style: italic;
font-style: normal;
font-size: 18px; font-size: 18px;
font-weight: 400; font-weight: 400;
line-height: 20px; line-height: 20px;

View File

@@ -9,7 +9,7 @@ import { MATCH_STATUS, IsSubstituteSupported } from "@/services/detailService";
import { GameManagePopup, NTRPEvaluatePopup } from "@/components"; import { GameManagePopup, NTRPEvaluatePopup } from "@/components";
import img from "@/config/images"; import img from "@/config/images";
import RMB_ICON from "@/static/detail/rmb.svg"; import RMB_ICON from "@/static/detail/rmb.svg";
import { toast, navto } from "../../utils/helper"; import { toast, navto } from "@/utils/helper";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
function isFull(counts) { function isFull(counts) {
@@ -36,6 +36,34 @@ function isFull(counts) {
return false; 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 StickyButton(props) { export default function StickyButton(props) {
const { const {
@@ -45,6 +73,7 @@ export default function StickyButton(props) {
onStatusChange, onStatusChange,
handleAddComment, handleAddComment,
getCommentCount, getCommentCount,
currentUserInfo,
} = props; } = props;
const [commentCount, setCommentCount] = useState(0); const [commentCount, setCommentCount] = useState(0);
const ntrpRef = useRef<{ const ntrpRef = useRef<{
@@ -58,14 +87,28 @@ export default function StickyButton(props) {
start_time, start_time,
end_time, end_time,
is_organizer, is_organizer,
skill_level_max,
skill_level_min,
} = detail || {}; } = detail || {};
const { ntrp_level } = currentUserInfo || {};
const matchNtrpReq = matchNtrpRequestment(
ntrp_level,
skill_level_min,
skill_level_max
);
const gameManageRef = useRef(); const gameManageRef = useRef();
function handleSelfEvaluate() { function handleSelfEvaluate() {
ntrpRef?.current?.show({ ntrpRef?.current?.show({
type: EvaluateScene.detail, type: EvaluateScene.detail,
next: (flag) => { next: ({ flag, score }) => {
if (!matchNtrpRequestment(score, skill_level_min, skill_level_max)) {
toast("您当前不符合此球局NTRP水平要求去看看其他活动吧");
return;
}
if (flag) { if (flag) {
Taro.navigateTo({ Taro.navigateTo({
url: `/order_pages/orderDetail/index?gameId=${id}`, url: `/order_pages/orderDetail/index?gameId=${id}`,
@@ -123,31 +166,31 @@ export default function StickyButton(props) {
return { return {
text: "活动已取消", text: "活动已取消",
available: false, available: false,
// action: () => toast("活动已取消"), action: () => toast("活动已取消,去看看其他活动吧~"),
}; };
} else if (MATCH_STATUS.FINISHED === match_status) { } else if (MATCH_STATUS.FINISHED === match_status) {
return { return {
text: "活动已结束", text: "活动已结束",
available: false, available: false,
// action: () => toast("活动已取消"), action: () => toast("活动已结束,去看看其他活动吧~"),
}; };
} else if (dayjs(end_time).isBefore(dayjs())) { } else if (dayjs(end_time).isBefore(dayjs())) {
return { return {
text: "活动已结束", text: "活动已结束",
available: false, available: false,
// action: () => toast("活动已结束"), action: () => toast("活动已结束,去看看其他活动吧~"),
}; };
} else if (dayjs(start_time).isBefore(dayjs())) { } else if (dayjs(start_time).isBefore(dayjs())) {
return { return {
text: "活动已开始", text: "活动已开始",
available: false, available: false,
// action: () => toast("活动已开始"), action: () => toast("活动已开始,去看看其他活动吧~"),
}; };
} else if (isFull(detail)) { } else if (isFull(detail)) {
return { return {
text: "活动已满员", text: "活动已满员",
available: false, available: false,
// action: () => toast("活动已满员"), action: () => toast("活动已满员,去看看其他活动吧~"),
}; };
} }
if (waiting_start) { if (waiting_start) {
@@ -159,7 +202,7 @@ export default function StickyButton(props) {
<Text className={styles.btnText}></Text> <Text className={styles.btnText}></Text>
</> </>
), ),
action: () => toast("已加入"), action: () => toast("您已参与了本次活动"),
}; };
} else if (is_substituting) { } else if (is_substituting) {
return { return {
@@ -170,7 +213,7 @@ export default function StickyButton(props) {
<Text className={styles.btnText}></Text> <Text className={styles.btnText}></Text>
</> </>
), ),
action: () => toast("已加入候补"), action: () => toast("已加入候补,候补失败会全额退款~"),
}; };
} else if (can_pay) { } else if (can_pay) {
return { return {
@@ -190,6 +233,19 @@ export default function StickyButton(props) {
} }
}, },
}; };
} else if (!matchNtrpReq) {
return {
text: () => (
<>
<Image className={styles.crrrencySymbol} src={RMB_ICON} />
{displayPrice}
<Text className={styles.btnText}></Text>
</>
),
available: false,
action: () =>
toast("您当前不符合此球局NTRP水平要求去看看其他活动吧"),
};
} else if (can_substitute) { } else if (can_substitute) {
return { return {
text: () => ( text: () => (

View File

@@ -1,6 +1,6 @@
import { Text, View } from "@tarojs/components"; import { Text, View } from "@tarojs/components";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
import { insertDotInTags } from "../../utils/helper"; import { insertDotInTags } from "@/utils/helper";
export default function SupplementalNotes(props) { export default function SupplementalNotes(props) {
const { const {

View File

@@ -4,7 +4,7 @@ import Taro from "@tarojs/taro";
import { CommonPopup } from "@/components"; import { CommonPopup } from "@/components";
import img from "@/config/images"; import img from "@/config/images";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
import { insertDotInTags } from "../../utils/helper"; import { insertDotInTags } from "@/utils/helper";
// 场馆信息 // 场馆信息
export default function VenueInfo(props) { export default function VenueInfo(props) {

View File

@@ -1,8 +1,7 @@
.detail-page { .detail-page {
width: 100%; width: 100%;
height: 100%; height: 100%;
// background-color: #FAFAFA; position: relative;
// padding-bottom: env(safe-area-inset-bottom);
.custom-navbar { .custom-navbar {
height: 56px; /* 通常与原生导航栏高度一致 */ height: 56px; /* 通常与原生导航栏高度一致 */
@@ -11,43 +10,55 @@
justify-content: center; justify-content: center;
// background-color: #fff; // background-color: #fff;
color: #000; color: #000;
padding-top: 44px; /* 适配状态栏 */ // padding-top: 44px; /* 适配状态栏 */
position: sticky; position: fixed;
width: 100%;
top: 0; top: 0;
box-sizing: border-box;
z-index: 100; z-index: 100;
overflow: hidden; overflow: hidden;
background-color: rgba(0, 0, 0, 0.2); border-bottom: 1px solid transparent;
transition: background 0.25s ease, backdrop-filter 0.25s ease;
&.glass {
background: rgba(0, 0, 0, 0.2);
box-shadow: 0 0 4px 0 rgba(255, 255, 255, 0.25) inset;
backdrop-filter: blur(6px);
border-color: rgba(0, 0, 0, 0.1);
}
} }
.detail-navigator { .detail-navigator {
height: 30px; height: 40px;
width: 80px; width: 80px;
border-radius: 15px; border-radius: 15px;
position: absolute; position: absolute;
left: 12px; left: 12px;
border: 1px solid #888; // border: 1px solid #888;
box-sizing: border-box; box-sizing: border-box;
color: #fff; color: #fff;
display: flex; display: flex;
align-items: center; align-items: center;
background: rgba(0, 0, 0, 0.1); // background: rgba(0, 0, 0, 0.1);
.detail-navigator-back { // .detail-navigator-back {
border-right: 1px solid #444; // border-right: 1px solid #444;
} // }
.detail-navigator-back, .detail-navigator-back,
.detail-navigator-icon { .detail-navigator-icon {
height: 20px; height: 100%;
width: 50%; width: 100%;
display: flex; display: flex;
justify-content: center; justify-content: flex-start;
align-items: center;
& > .detail-navigator-back-icon { & > .detail-navigator-back-icon {
width: 20px; width: 16px;
height: 20px; height: 16px;
color: #fff; color: #fff;
margin-left: 8px;
} }
& > .detail-navigator-logo-icon { & > .detail-navigator-logo-icon {

View File

@@ -1,6 +1,8 @@
import { useState, useEffect, useRef } from "react"; import { useState, useEffect, useRef } from "react";
import { View, Text, Image } from "@tarojs/components"; import { View, Text, Image, ScrollView } from "@tarojs/components";
import Taro, { useRouter, useDidShow } from "@tarojs/taro"; import Taro, { useRouter, useDidShow } from "@tarojs/taro";
import classnames from "classnames";
import { throttle } from "@tarojs/runtime";
// 导入API服务 // 导入API服务
import { withAuth, Comments } from "@/components"; import { withAuth, Comments } from "@/components";
import DetailService from "@/services/detailService"; import DetailService from "@/services/detailService";
@@ -18,9 +20,9 @@ import Participants from "./components/Participants";
import SupplementalNotes from "./components/SupplementalNotes"; import SupplementalNotes from "./components/SupplementalNotes";
import OrganizerInfo from "./components/OrganizerInfo"; import OrganizerInfo from "./components/OrganizerInfo";
import SharePopup from "./components/SharePopup"; import SharePopup from "./components/SharePopup";
import { navto, toast } from "./utils/helper"; import { navto, toast } from "@/utils/helper";
import ArrowLeft from "@/static/detail/icon-arrow-left.svg"; import ArrowLeft from "@/static/detail/icon-arrow-left.svg";
import Logo from "@/static/detail/icon-logo-go.svg"; // import Logo from "@/static/detail/icon-logo-go.svg";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
function Index() { function Index() {
@@ -35,7 +37,7 @@ function Index() {
const myInfo = useUserInfo(); const myInfo = useUserInfo();
const { statusNavbarHeightInfo } = useGlobalState(); const { statusNavbarHeightInfo } = useGlobalState();
const { statusBarHeight, navBarHeight } = statusNavbarHeightInfo; const { statusBarHeight, navBarHeight, totalHeight } = statusNavbarHeightInfo;
const isMyOwn = userInfo.id === myInfo.id; const isMyOwn = userInfo.id === myInfo.id;
@@ -150,14 +152,33 @@ function Index() {
? { backgroundImage: `url(${detail?.image_list?.[0]})` } ? { backgroundImage: `url(${detail?.image_list?.[0]})` }
: {}; : {};
const [glass, setGlass] = useState(false);
const onScroll = throttle((e) => {
const top = e.detail.scrollTop;
setGlass(top > 20);
}, 16);
return ( return (
<View className={styles["detail-page"]}> <ScrollView
className={styles["detail-page"]}
scrollY
onScroll={onScroll}
onScrollToUpper={() => {
setGlass(false);
}}
enhanced
showScrollbar={false}
>
{/* custom navbar */} {/* custom navbar */}
<view <View
className={styles["custom-navbar"]} className={classnames(
styles["custom-navbar"],
glass ? styles.glass : ""
)}
style={{ style={{
height: `${statusBarHeight}px`, height: `${totalHeight}px`,
paddingTop: `${navBarHeight}px`, paddingTop: `${statusBarHeight}px`,
}} }}
> >
<View className={styles["detail-navigator"]}> <View className={styles["detail-navigator"]}>
@@ -170,14 +191,14 @@ function Index() {
src={ArrowLeft} src={ArrowLeft}
/> />
</View> </View>
<View className={styles["detail-navigator-icon"]}> {/* <View className={styles["detail-navigator-icon"]}>
<Image <Image
className={styles["detail-navigator-logo-icon"]} className={styles["detail-navigator-logo-icon"]}
src={Logo} src={Logo}
/> />
</View> */}
</View> </View>
</View> </View>
</view>
<View className={styles["detail-page-bg"]} style={backgroundImage} /> <View className={styles["detail-page-bg"]} style={backgroundImage} />
{/* swiper */} {/* swiper */}
<Carousel detail={detail} /> <Carousel detail={detail} />
@@ -238,6 +259,7 @@ function Index() {
getCommentCount={ getCommentCount={
commentRef.current && commentRef.current.getCommentCount commentRef.current && commentRef.current.getCommentCount
} }
currentUserInfo={myInfo}
/> />
{/* share popup */} {/* share popup */}
<SharePopup <SharePopup
@@ -248,7 +270,7 @@ function Index() {
userInfo={userInfo} userInfo={userInfo}
/> />
</View> </View>
</View> </ScrollView>
); );
} }

View File

@@ -1,28 +0,0 @@
import Taro from "@tarojs/taro";
export function navto(url) {
Taro.navigateTo({
url: url,
});
}
export function toast(message) {
Taro.showToast({ title: message, icon: "none" });
}
// 将·作为连接符插入到标签文本之间
export function insertDotInTags(tags: string[]) {
if (!tags) return [];
return tags.join("-·-").split("-");
}
export function genNTRPRequirementText(min, max) {
if (min && max && min !== max) {
return `${min} - ${max} 之间`;
} else if (max === "1") {
return "无要求";
} else if (max) {
return `${max} 以上`;
}
return "-";
}

View File

@@ -66,7 +66,7 @@ const ListPage = () => {
const scrollViewRef = useRef(null); // ScrollView 的 ref const scrollViewRef = useRef(null); // ScrollView 的 ref
const scrollTimeoutRef = useRef<NodeJS.Timeout | null>(null); const scrollTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const lastScrollTopRef = useRef(0); const lastScrollTopRef = useRef(0);
const scrollDirectionRef = useRef<'up' | 'down' | null>(null); const scrollDirectionRef = useRef<"up" | "down" | null>(null);
const lastScrollTimeRef = useRef(Date.now()); const lastScrollTimeRef = useRef(Date.now());
const loadingMoreRef = useRef(false); // 防止重复加载更多 const loadingMoreRef = useRef(false); // 防止重复加载更多
const scrollStartPositionRef = useRef(0); // 记录开始滚动的位置 const scrollStartPositionRef = useRef(0); // 记录开始滚动的位置
@@ -74,7 +74,7 @@ const ListPage = () => {
const [scrollTop, setScrollTop] = useState(0); // 控制 ScrollView 滚动位置 const [scrollTop, setScrollTop] = useState(0); // 控制 ScrollView 滚动位置
// 动态控制 GuideBar 的 z-index // 动态控制 GuideBar 的 z-index
const [guideBarZIndex, setGuideBarZIndex] = useState<'low' | 'high'>('high'); const [guideBarZIndex, setGuideBarZIndex] = useState<"low" | "high">("high");
const [isPublishMenuVisible, setIsPublishMenuVisible] = useState(false); const [isPublishMenuVisible, setIsPublishMenuVisible] = useState(false);
const [isDistanceFilterVisible, setIsDistanceFilterVisible] = useState(false); const [isDistanceFilterVisible, setIsDistanceFilterVisible] = useState(false);
const [isCityPickerVisible, setIsCityPickerVisible] = useState(false); const [isCityPickerVisible, setIsCityPickerVisible] = useState(false);
@@ -97,22 +97,31 @@ const ListPage = () => {
// 滚动到顶部的方法 // 滚动到顶部的方法
const scrollToTop = useCallback(() => { const scrollToTop = useCallback(() => {
// 使用一个唯一值触发 scrollTop 更新,确保每次都能滚动到顶部 // 使用一个唯一值触发 scrollTop 更新,确保每次都能滚动到顶部
setScrollTop(prev => prev === 0 ? 0.1 : 0); setScrollTop((prev) => (prev === 0 ? 0.1 : 0));
}, []); }, []);
// 监听所有弹窗和菜单的状态,动态调整 GuideBar 的 z-index // 监听所有弹窗和菜单的状态,动态调整 GuideBar 的 z-index
useEffect(() => { useEffect(() => {
if (isPublishMenuVisible) { if (isPublishMenuVisible) {
// PublishMenu 展开时GuideBar 保持高层级 // PublishMenu 展开时GuideBar 保持高层级
setGuideBarZIndex('high'); setGuideBarZIndex("high");
} else if (isShowFilterPopup || isDistanceFilterVisible || isCityPickerVisible) { } else if (
isShowFilterPopup ||
isDistanceFilterVisible ||
isCityPickerVisible
) {
// 任何筛选组件或选择器展开时GuideBar 降低层级 // 任何筛选组件或选择器展开时GuideBar 降低层级
setGuideBarZIndex('low'); setGuideBarZIndex("low");
} else { } else {
// 都关闭时GuideBar 保持高层级 // 都关闭时GuideBar 保持高层级
setGuideBarZIndex('high'); setGuideBarZIndex("high");
} }
}, [isShowFilterPopup, isPublishMenuVisible, isDistanceFilterVisible, isCityPickerVisible]); }, [
isShowFilterPopup,
isPublishMenuVisible,
isDistanceFilterVisible,
isCityPickerVisible,
]);
// ScrollView 滚动处理函数 // ScrollView 滚动处理函数
const handleScrollViewScroll = useCallback( const handleScrollViewScroll = useCallback(
@@ -133,28 +142,34 @@ const ListPage = () => {
if (Math.abs(scrollDiff) > 15) { if (Math.abs(scrollDiff) > 15) {
if (scrollDiff > 0) { if (scrollDiff > 0) {
// 方向改变时,记录新的起始位置 // 方向改变时,记录新的起始位置
if (newDirection !== 'up') { if (newDirection !== "up") {
scrollStartPositionRef.current = lastScrollTop; scrollStartPositionRef.current = lastScrollTop;
} }
newDirection = 'up'; newDirection = "up";
} else { } else {
// 方向改变时,记录新的起始位置 // 方向改变时,记录新的起始位置
if (newDirection !== 'down') { if (newDirection !== "down") {
scrollStartPositionRef.current = lastScrollTop; scrollStartPositionRef.current = lastScrollTop;
} }
newDirection = 'down'; newDirection = "down";
} }
scrollDirectionRef.current = newDirection; scrollDirectionRef.current = newDirection;
} }
// 计算从开始滚动到现在的累计距离 // 计算从开始滚动到现在的累计距离
const totalScrollDistance = Math.abs(currentScrollTop - scrollStartPositionRef.current); const totalScrollDistance = Math.abs(
currentScrollTop - scrollStartPositionRef.current
);
// 滚动阈值 // 滚动阈值
const positionThreshold = 120; // 需要滚动到距离顶部120px const positionThreshold = 120; // 需要滚动到距离顶部120px
const distanceThreshold = 80; // 需要连续滚动80px才触发 const distanceThreshold = 80; // 需要连续滚动80px才触发
if (newDirection === 'up' && currentScrollTop > positionThreshold && totalScrollDistance > distanceThreshold) { if (
newDirection === "up" &&
currentScrollTop > positionThreshold &&
totalScrollDistance > distanceThreshold
) {
// 上滑超过阈值,且连续滚动距离足够,隐藏搜索框 // 上滑超过阈值,且连续滚动距离足够,隐藏搜索框
if (showSearchBar || !isShowInputCustomerNavBar) { if (showSearchBar || !isShowInputCustomerNavBar) {
setShowSearchBar(false); setShowSearchBar(false);
@@ -164,7 +179,10 @@ const ListPage = () => {
// 重置起始位置 // 重置起始位置
scrollStartPositionRef.current = currentScrollTop; scrollStartPositionRef.current = currentScrollTop;
} }
} else if ((newDirection === 'down' && totalScrollDistance > distanceThreshold) || currentScrollTop <= positionThreshold) { } else if (
(newDirection === "down" && totalScrollDistance > distanceThreshold) ||
currentScrollTop <= positionThreshold
) {
// 下滑且连续滚动距离足够,或者回到顶部附近,显示搜索框 // 下滑且连续滚动距离足够,或者回到顶部附近,显示搜索框
if (!showSearchBar || isShowInputCustomerNavBar) { if (!showSearchBar || isShowInputCustomerNavBar) {
setShowSearchBar(true); setShowSearchBar(true);
@@ -433,7 +451,6 @@ const ListPage = () => {
return ( return (
<> <>
{/* 自定义导航 */} {/* 自定义导航 */}
<HomeNavbar <HomeNavbar
config={{ config={{
@@ -467,7 +484,11 @@ const ListPage = () => {
{/* 固定在顶部的搜索框和筛选 */} {/* 固定在顶部的搜索框和筛选 */}
<View className={styles.fixedHeader}> <View className={styles.fixedHeader}>
{/* 搜索框 - 可隐藏 */} {/* 搜索框 - 可隐藏 */}
<View className={`${styles.listTopSearchWrapper} ${showSearchBar ? styles.show : styles.hide}`}> <View
className={`${styles.listTopSearchWrapper} ${
showSearchBar ? styles.show : styles.hide
}`}
>
<SearchBar <SearchBar
handleFilterIcon={toggleShowPopup} handleFilterIcon={toggleShowPopup}
isSelect={filterCount > 0} isSelect={filterCount > 0}
@@ -507,7 +528,11 @@ const ListPage = () => {
lowerThreshold={100} lowerThreshold={100}
onScrollToLower={async () => { onScrollToLower={async () => {
// 防止重复调用,检查 loading 状态和是否正在加载更多 // 防止重复调用,检查 loading 状态和是否正在加载更多
if (!loading && !loadingMoreRef.current && listPageState?.isHasMoreData) { if (
!loading &&
!loadingMoreRef.current &&
listPageState?.isHasMoreData
) {
loadingMoreRef.current = true; loadingMoreRef.current = true;
try { try {
await loadMoreMatches(); await loadMoreMatches();
@@ -529,6 +554,7 @@ const ListPage = () => {
error={error} error={error}
reload={refreshMatches} reload={refreshMatches}
loadMoreMatches={loadMoreMatches} loadMoreMatches={loadMoreMatches}
evaluateFlag
/> />
</ScrollView> </ScrollView>
</View> </View>
@@ -536,7 +562,11 @@ const ListPage = () => {
)} )}
<GuideBar <GuideBar
currentPage="list" currentPage="list"
guideBarClassName={`${styles.guideBarList} ${guideBarZIndex === 'low' ? styles.guideBarLowZIndex : styles.guideBarHighZIndex}`} guideBarClassName={`${styles.guideBarList} ${
guideBarZIndex === "low"
? styles.guideBarLowZIndex
: styles.guideBarHighZIndex
}`}
onPublishMenuVisibleChange={handlePublishMenuVisibleChange} onPublishMenuVisibleChange={handlePublishMenuVisibleChange}
/> />
</> </>

View File

@@ -13,7 +13,7 @@ import WechatLogo from "@/static/detail/wechat_icon.svg";
import WechatTimeline from "@/static/detail/wechat_timeline.svg"; import WechatTimeline from "@/static/detail/wechat_timeline.svg";
import { useUserActions } from "@/store/userStore"; import { useUserActions } from "@/store/userStore";
import { DayOfWeekMap } from "../detail/config"; import { DayOfWeekMap } from "../detail/config";
import { genNTRPRequirementText } from "@/game_pages/detail/utils/helper"; import { genNTRPRequirementText } from "@/utils/helper";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
function SharePoster(props) { function SharePoster(props) {

View File

@@ -36,7 +36,8 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
}) => { }) => {
const store = useListStore() || {}; const store = useListStore() || {};
const { fetchUserInfo } = useUserActions(); const { fetchUserInfo } = useUserActions();
const { statusNavbarHeightInfo, getCurrentLocationInfo } = useGlobalState() || {}; const { statusNavbarHeightInfo, getCurrentLocationInfo } =
useGlobalState() || {};
const { totalHeight = 98 } = statusNavbarHeightInfo || {}; const { totalHeight = 98 } = statusNavbarHeightInfo || {};
const { const {
@@ -77,7 +78,7 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
const scrollViewRef = useRef(null); const scrollViewRef = useRef(null);
const scrollTimeoutRef = useRef<NodeJS.Timeout | null>(null); const scrollTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const lastScrollTopRef = useRef(0); const lastScrollTopRef = useRef(0);
const scrollDirectionRef = useRef<'up' | 'down' | null>(null); const scrollDirectionRef = useRef<"up" | "down" | null>(null);
const lastScrollTimeRef = useRef(Date.now()); const lastScrollTimeRef = useRef(Date.now());
const loadingMoreRef = useRef(false); const loadingMoreRef = useRef(false);
const scrollStartPositionRef = useRef(0); const scrollStartPositionRef = useRef(0);
@@ -86,17 +87,20 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
const [refreshing, setRefreshing] = useState(false); const [refreshing, setRefreshing] = useState(false);
// 处理距离筛选显示/隐藏 // 处理距离筛选显示/隐藏
const handleDistanceFilterVisibleChange = useCallback((visible: boolean) => { const handleDistanceFilterVisibleChange = useCallback(
(visible: boolean) => {
onDistanceFilterVisibleChange?.(visible); onDistanceFilterVisibleChange?.(visible);
onNavStateChange?.({ isDistanceFilterVisible: visible }); onNavStateChange?.({ isDistanceFilterVisible: visible });
}, [onDistanceFilterVisibleChange, onNavStateChange]); },
[onDistanceFilterVisibleChange, onNavStateChange]
);
// 处理城市选择器显示/隐藏(由主容器统一管理,通过 onNavStateChange 通知) // 处理城市选择器显示/隐藏(由主容器统一管理,通过 onNavStateChange 通知)
// 注意CustomerNavBar 的 onCityPickerVisibleChange 由主容器直接处理 // 注意CustomerNavBar 的 onCityPickerVisibleChange 由主容器直接处理
// 滚动到顶部(用于 ScrollView 内部滚动) // 滚动到顶部(用于 ScrollView 内部滚动)
const scrollToTopInternal = useCallback(() => { const scrollToTopInternal = useCallback(() => {
setScrollTop(prev => prev === 0 ? 0.1 : 0); setScrollTop((prev) => (prev === 0 ? 0.1 : 0));
}, []); }, []);
// 监听外部滚动触发 // 监听外部滚动触发
@@ -120,24 +124,30 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
let newDirection = scrollDirectionRef.current; let newDirection = scrollDirectionRef.current;
if (Math.abs(scrollDiff) > 15) { if (Math.abs(scrollDiff) > 15) {
if (scrollDiff > 0) { if (scrollDiff > 0) {
if (newDirection !== 'up') { if (newDirection !== "up") {
scrollStartPositionRef.current = lastScrollTop; scrollStartPositionRef.current = lastScrollTop;
} }
newDirection = 'up'; newDirection = "up";
} else { } else {
if (newDirection !== 'down') { if (newDirection !== "down") {
scrollStartPositionRef.current = lastScrollTop; scrollStartPositionRef.current = lastScrollTop;
} }
newDirection = 'down'; newDirection = "down";
} }
scrollDirectionRef.current = newDirection; scrollDirectionRef.current = newDirection;
} }
const totalScrollDistance = Math.abs(currentScrollTop - scrollStartPositionRef.current); const totalScrollDistance = Math.abs(
currentScrollTop - scrollStartPositionRef.current
);
const positionThreshold = 120; const positionThreshold = 120;
const distanceThreshold = 80; const distanceThreshold = 80;
if (newDirection === 'up' && currentScrollTop > positionThreshold && totalScrollDistance > distanceThreshold) { if (
newDirection === "up" &&
currentScrollTop > positionThreshold &&
totalScrollDistance > distanceThreshold
) {
if (showSearchBar || !isShowInputCustomerNavBar) { if (showSearchBar || !isShowInputCustomerNavBar) {
setShowSearchBar(false); setShowSearchBar(false);
updateListPageState({ updateListPageState({
@@ -146,7 +156,10 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
onNavStateChange?.({ isShowInputCustomerNavBar: true }); onNavStateChange?.({ isShowInputCustomerNavBar: true });
scrollStartPositionRef.current = currentScrollTop; scrollStartPositionRef.current = currentScrollTop;
} }
} else if ((newDirection === 'down' && totalScrollDistance > distanceThreshold) || currentScrollTop <= positionThreshold) { } else if (
(newDirection === "down" && totalScrollDistance > distanceThreshold) ||
currentScrollTop <= positionThreshold
) {
if (!showSearchBar || isShowInputCustomerNavBar) { if (!showSearchBar || isShowInputCustomerNavBar) {
setShowSearchBar(true); setShowSearchBar(true);
updateListPageState({ updateListPageState({
@@ -160,7 +173,12 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
lastScrollTopRef.current = currentScrollTop; lastScrollTopRef.current = currentScrollTop;
lastScrollTimeRef.current = currentTime; lastScrollTimeRef.current = currentTime;
}, },
[showSearchBar, isShowInputCustomerNavBar, updateListPageState, onNavStateChange] [
showSearchBar,
isShowInputCustomerNavBar,
updateListPageState,
onNavStateChange,
]
); );
useEffect(() => { useEffect(() => {
@@ -341,7 +359,11 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
</View> </View>
)} )}
<View className={styles.fixedHeader}> <View className={styles.fixedHeader}>
<View className={`${styles.listTopSearchWrapper} ${showSearchBar ? styles.show : styles.hide}`}> <View
className={`${styles.listTopSearchWrapper} ${
showSearchBar ? styles.show : styles.hide
}`}
>
<SearchBar <SearchBar
handleFilterIcon={toggleShowPopup} handleFilterIcon={toggleShowPopup}
isSelect={filterCount > 0} isSelect={filterCount > 0}
@@ -378,7 +400,11 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
onRefresherRefresh={handleRefresh} onRefresherRefresh={handleRefresh}
lowerThreshold={100} lowerThreshold={100}
onScrollToLower={async () => { onScrollToLower={async () => {
if (!loading && !loadingMoreRef.current && listPageState?.isHasMoreData) { if (
!loading &&
!loadingMoreRef.current &&
listPageState?.isHasMoreData
) {
loadingMoreRef.current = true; loadingMoreRef.current = true;
try { try {
await loadMoreMatches(); await loadMoreMatches();
@@ -399,6 +425,7 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
error={error} error={error}
reload={refreshMatches} reload={refreshMatches}
loadMoreMatches={loadMoreMatches} loadMoreMatches={loadMoreMatches}
evaluateFlag
/> />
</ScrollView> </ScrollView>
</View> </View>
@@ -409,4 +436,3 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
}; };
export default ListPageContent; export default ListPageContent;

View File

@@ -26,7 +26,9 @@ const MyselfPageContent: React.FC = () => {
const [ended_game_records, setEndedGameRecords] = useState<TennisMatch[]>([]); const [ended_game_records, setEndedGameRecords] = useState<TennisMatch[]>([]);
const [loading] = useState(false); const [loading] = useState(false);
const [is_following, setIsFollowing] = useState(false); const [is_following, setIsFollowing] = useState(false);
const [active_tab, setActiveTab] = useState<"hosted" | "participated">("hosted"); const [active_tab, setActiveTab] = useState<"hosted" | "participated">(
"hosted"
);
useEffect(() => { useEffect(() => {
pickerOption.getCities(); pickerOption.getCities();
@@ -66,7 +68,7 @@ const MyselfPageContent: React.FC = () => {
const load_game_data = async () => { const load_game_data = async () => {
try { try {
if (!user_info || !('id' in user_info)) { if (!user_info || !("id" in user_info)) {
return; return;
} }
let games_data; let games_data;
@@ -136,7 +138,10 @@ const MyselfPageContent: React.FC = () => {
return ( return (
<View className="myself_page"> <View className="myself_page">
<View className="myself_page_content_main" style={{ paddingTop: `${totalHeight}px` }}> <View
className="myself_page_content_main"
style={{ paddingTop: `${totalHeight}px` }}
>
<View className="user_info_section"> <View className="user_info_section">
<UserInfoCard <UserInfoCard
editable={is_current_user} editable={is_current_user}
@@ -234,4 +239,3 @@ const MyselfPageContent: React.FC = () => {
}; };
export default MyselfPageContent; export default MyselfPageContent;

View File

@@ -400,15 +400,49 @@ function OrderMsg(props) {
}, },
{ {
title: "报名人电话", title: "报名人电话",
content: registrant_phone, // content: registrant_phone,
content: registrant_phone ? (
<Text
selectable={true} // 支持长按复制
style={{
color: "#007AFF",
// textDecoration: "underline",
cursor: "pointer",
}}
onClick={() => {
Taro.makePhoneCall({ phoneNumber: registrant_phone });
}}
>
{registrant_phone}
</Text>
) : (
"-"
),
}, },
{ {
title: "组织人微信号", title: "组织人微信号",
content: wechat_contact, content: wechat_contact || "-",
}, },
{ {
title: "组织人电话", title: "组织人电话",
content: wechat_contact, // content: wechat_contact,
content: wechat_contact ? (
<Text
selectable={true} // 支持长按复制
style={{
color: "#007AFF",
// textDecoration: "underline",
cursor: "pointer",
}}
onClick={() => {
Taro.makePhoneCall({ phoneNumber: wechat_contact });
}}
>
{wechat_contact}
</Text>
) : (
"-"
),
}, },
{ {
title: "费用", title: "费用",

View File

@@ -10,7 +10,7 @@
} }
.container { .container {
padding: 100px 12px 40px; padding: 100px 0 40px;
background-color: #fafafa; background-color: #fafafa;
height: 100vh; height: 100vh;
width: 100%; width: 100%;
@@ -24,7 +24,7 @@
height: 100%; height: 100%;
width: 100%; width: 100%;
position: relative; position: relative;
background-color: #fff; background-color: #fafafa;
// .bg { // .bg {
// position: absolute; // position: absolute;
@@ -48,12 +48,13 @@
font-style: normal; font-style: normal;
font-weight: 500; font-weight: 500;
line-height: 18px; line-height: 18px;
background-color: #f9f9f9; // background-color: #f9f9f9;
} }
} }
.orderItem { .orderItem {
width: 100%; // width: calc(100% - 3px);
margin: 0 12px;
// height: 222px; // height: 222px;
background-color: #fff; background-color: #fff;
border-radius: 12px; border-radius: 12px;

View File

@@ -17,7 +17,7 @@ import { withAuth, RefundPopup, GeneralNavbar } from "@/components";
import { payOrder, generateOrderActions } from "@/utils"; import { payOrder, generateOrderActions } from "@/utils";
import emptyContent from "@/static/emptyStatus/publish-empty.png"; import emptyContent from "@/static/emptyStatus/publish-empty.png";
import CustomerIcon from "@/static/order/customer.svg"; import CustomerIcon from "@/static/order/customer.svg";
import { insertDotInTags } from "@/game_pages/detail/utils/helper"; import { insertDotInTags, genNTRPRequirementText } from "@/utils/helper";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
dayjs.locale("zh-cn"); dayjs.locale("zh-cn");
@@ -363,11 +363,7 @@ const OrderList = () => {
<Text>{max_players}</Text> <Text>{max_players}</Text>
</View> </View>
<View className={styles.levelReq}> <View className={styles.levelReq}>
{skill_level_max !== skill_level_min {genNTRPRequirementText(skill_level_min, skill_level_max)}
? `${skill_level_min || "-"}${skill_level_max || "-"}`
: skill_level_min === 1
? "无要求"
: `${skill_level_min} 以上`}
</View> </View>
<View className={styles.playType}>{play_type}</View> <View className={styles.playType}>{play_type}</View>
</View> </View>

View File

@@ -8,6 +8,7 @@ import evaluateService, {
LastTimeTestResult, LastTimeTestResult,
Question, Question,
TestResultData, TestResultData,
StageType,
} from "@/services/evaluateService"; } from "@/services/evaluateService";
import { useUserInfo, useUserActions } from "@/store/userStore"; import { useUserInfo, useUserActions } from "@/store/userStore";
import { useEvaluate, EvaluateScene } from "@/store/evaluateStore"; import { useEvaluate, EvaluateScene } from "@/store/evaluateStore";
@@ -24,12 +25,6 @@ import DownloadIcon from "@/static/ntrp/ntrp_download.svg";
import ReTestIcon from "@/static/ntrp/ntrp_re-action.svg"; import ReTestIcon from "@/static/ntrp/ntrp_re-action.svg";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
enum StageType {
INTRO = "intro",
TEST = "test",
RESULT = "result",
}
const sourceTypeToTextMap = new Map([ const sourceTypeToTextMap = new Map([
[EvaluateScene.detail, "继续加入球局"], [EvaluateScene.detail, "继续加入球局"],
[EvaluateScene.publish, "继续发布球局"], [EvaluateScene.publish, "继续发布球局"],
@@ -174,6 +169,7 @@ function Intro() {
} }
function handleNext(type) { function handleNext(type) {
if (!id) {
setCallback({ setCallback({
type: EvaluateScene.share, type: EvaluateScene.share,
next: () => { next: () => {
@@ -181,13 +177,9 @@ function Intro() {
}, },
onCancel: () => { onCancel: () => {
Taro.redirectTo({ url: "/main_pages/index" }); Taro.redirectTo({ url: "/main_pages/index" });
// if (userInfo.id) {
// Taro.redirectTo({ url: "/game_pages/list/index" });
// } else {
// Taro.exitMiniProgram();
// }
}, },
}); });
}
Taro.redirectTo({ Taro.redirectTo({
url: `/other_pages/ntrp-evaluate/index?stage=${type}${ url: `/other_pages/ntrp-evaluate/index?stage=${type}${
type === StageType.RESULT ? `&id=${id}` : "" type === StageType.RESULT ? `&id=${id}` : ""
@@ -497,7 +489,7 @@ function Result() {
async function handleGoon() { async function handleGoon() {
if (type) { if (type) {
next(); next({ flag: false, score: result?.ntrp_level as string });
await delay(1500); await delay(1500);
clear(); clear();
} else { } else {

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,12 @@
import httpService from "./httpService"; import httpService from "./httpService";
import type { ApiResponse } from "./httpService"; import type { ApiResponse } from "./httpService";
export enum StageType {
INTRO = "intro",
TEST = "test",
RESULT = "result",
}
// 单个选项类型 // 单个选项类型
interface Option { interface Option {
text: string; text: string;

View File

@@ -10,18 +10,25 @@ export enum EvaluateScene {
} }
export interface EvaluateCallback { export interface EvaluateCallback {
type: EvaluateScene | '' type: EvaluateScene | "";
next: (flag?: boolean) => void, // flag是用来区分跳转ntrp测试后的操作和直接修改ntrp水平成功后的操作
onCancel: () => void, // score是用在加入球局前判断是否满足球局要求的返回值限定为必传
// next有两个地方调用ntrp结果页handleGoon、ntrp弹窗NTRPEvaluatePopup直接修改点击保存按钮时
next: ({ flag, score }: { flag?: boolean; score: string }) => void;
onCancel: () => void;
} }
export interface EvaluateCallbackType extends EvaluateCallback { export interface EvaluateCallbackType extends EvaluateCallback {
setCallback: (options: { type: EvaluateScene | '', next: () => void, onCancel: () => void }) => void, setCallback: (options: {
clear: () => void, type: EvaluateScene | "";
next: ({ flag, score }: { flag?: boolean; score: string }) => void;
onCancel: () => void;
}) => void;
clear: () => void;
} }
export const useEvaluateCallback = create<EvaluateCallbackType>()((set) => ({ export const useEvaluateCallback = create<EvaluateCallbackType>()((set) => ({
type: '', type: "",
next: () => { }, next: () => { },
onCancel: () => { }, onCancel: () => { },
setCallback: ({ type, next, onCancel }) => { setCallback: ({ type, next, onCancel }) => {
@@ -29,15 +36,18 @@ export const useEvaluateCallback = create<EvaluateCallbackType>()((set) => ({
type, type,
next, next,
onCancel, onCancel,
}) });
},
clear: () => {
set({ type: "", next: () => { }, onCancel: () => { } });
}, },
clear: () => { set({ type: '', next: () => { }, onCancel: () => { } }) }
})); }));
export const useEvaluate = () => useEvaluateCallback(({ type, next, onCancel, setCallback, clear }) => ({ export const useEvaluate = () =>
useEvaluateCallback(({ type, next, onCancel, setCallback, clear }) => ({
type, type,
next, next,
onCancel, onCancel,
setCallback, setCallback,
clear, clear,
})) }));

View File

@@ -24,3 +24,34 @@ export const sceneRedirectLogic = (options, defaultPage: string) => {
console.error(e); console.error(e);
} }
}; };
export function navto(url) {
Taro.navigateTo({
url: url,
});
}
export function toast(message) {
Taro.showToast({ title: message, icon: "none" });
}
// 将·作为连接符插入到标签文本之间
export function insertDotInTags(tags: string[]) {
if (!tags) return [];
return tags.join("-·-").split("-");
}
function formatNtrpDisplay(val) {
return Number(val).toFixed(1)
}
export function genNTRPRequirementText(min, max) {
if (min && max && min !== max) {
return `${formatNtrpDisplay(min)} - ${formatNtrpDisplay(max)} 之间`;
} else if (max === "1") {
return "无要求";
} else if (max) {
return `${formatNtrpDisplay(max)} 以上`;
}
return "-";
}

5
types/ntrp-evaluate.ts Normal file
View File

@@ -0,0 +1,5 @@
export enum StageType {
INTRO = "intro",
TEST = "test",
RESULT = "result",
}