Merge branch 'feat/liujie'

This commit is contained in:
2025-10-01 09:38:17 +08:00
15 changed files with 678 additions and 309 deletions

View File

@@ -9,6 +9,7 @@
justify-content: space-between; justify-content: space-between;
// padding: 20px; // padding: 20px;
box-sizing: border-box; box-sizing: border-box;
padding-bottom: 40px;
.entryCard { .entryCard {
width: 100%; width: 100%;
@@ -53,3 +54,50 @@
} }
} }
} }
.picker {
width: 100%;
height: 252px;
}
.actions {
width: 100%;
padding: 10px;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
.buttonWrap {
width: 50%;
height: 50px;
border-radius: 16px;
border: 1px solid rgba(0, 0, 0, 0.06);
box-shadow: 0 8px 64px 0 rgba(0, 0, 0, 0.1);
backdrop-filter: blur(16px);
overflow: hidden;
.button {
width: 100%;
height: 50px;
color: #000;
background: #fff;
font-feature-settings: "liga" off, "clig" off;
font-family: "PingFang SC";
font-size: 16px;
font-style: normal;
font-weight: 600;
line-height: normal;
display: flex;
align-items: center;
justify-content: center;
&.primary {
color: #fff;
background: #000;
}
}
}
}

View File

@@ -7,14 +7,26 @@ import React, {
} from "react"; } from "react";
import { Button, Input, View, Text, Image } from "@tarojs/components"; import { Button, Input, View, Text, Image } from "@tarojs/components";
import Taro from "@tarojs/taro"; import Taro from "@tarojs/taro";
import classnames from "classnames";
import CommonPopup from "../CommonPopup"; import CommonPopup from "../CommonPopup";
import { getCurrentFullPath } from "@/utils"; import { getCurrentFullPath } from "@/utils";
import evaluateService from "@/services/evaluateService"; import evaluateService from "@/services/evaluateService";
import { useUserActions } from "@/store/userStore";
import { EvaluateCallback, EvaluateScene } from "@/store/evaluateStore";
import NTRPTestEntryCard from "../NTRPTestEntryCard"; import NTRPTestEntryCard from "../NTRPTestEntryCard";
import NtrpPopupGuide from "../NTRPPopupGuide"; import NtrpPopupGuide from "../NTRPPopupGuide";
import Picker from "../Picker/Picker";
import CloseIcon from "@/static/ntrp/ntrp_popup_close.svg"; import CloseIcon from "@/static/ntrp/ntrp_popup_close.svg";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
const ntrpLevels = ["1.5", "2.0", "2.5", "3.0", "3.5", "4.0", "4.5"];
const options = [
ntrpLevels.map((item) => ({
text: item,
value: item,
})),
];
export enum EvaluateType { export enum EvaluateType {
EDIT = "edit", EDIT = "edit",
EVALUATE = "evaluate", EVALUATE = "evaluate",
@@ -33,45 +45,57 @@ export enum SceneType {
} }
interface NTRPEvaluatePopupProps { interface NTRPEvaluatePopupProps {
types: EvaluateType[]; // types: EvaluateType[];
displayCondition: DisplayConditionType; // displayCondition: DisplayConditionType;
scene: SceneType; // scene: SceneType;
showGuide: boolean; showGuide: boolean;
children: React.ReactNode; type: EvaluateScene;
// children: React.ReactNode;
} }
function showCondition(scene, ntrp) { // function showCondition(scene, ntrp) {
if (scene === "list") { // if (scene === "list") {
// TODO: 显示频率 // // TODO: 显示频率
return Math.random() < 0.1 && [0, undefined].includes(ntrp); // return Math.random() < 0.1 && [0, undefined].includes(ntrp);
} // }
return ntrp === "0"; // return true;
} // return !ntrpLevels.includes(ntrp);
// }
const NTRPEvaluatePopup = (props: NTRPEvaluatePopupProps, ref) => { const NTRPEvaluatePopup = (props: NTRPEvaluatePopupProps, ref) => {
const { const {
types = ["edit", "evaluate"], // types = ["edit", "evaluate"],
displayCondition = "auto", // displayCondition = "auto",
scene = "list", // scene = "list",
type,
showGuide = false, showGuide = false,
} = props; } = props;
const [visible, setVisible] = useState(true); const [visible, setVisible] = useState(false);
const [ntrp, setNtrp] = useState<undefined | string>(); const [ntrp, setNtrp] = useState<string>("");
const [guideShow, setGuideShow] = useState(() => props.showGuide); const [guideShow, setGuideShow] = useState(() => showGuide);
const { updateUserInfo } = useUserActions();
const [evaCallback, setEvaCallback] = useState<EvaluateCallback>({
type: "",
next: () => {},
onCancel: () => {},
});
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
show: () => setVisible(true), show: (evaluateCallback) => {
setVisible(true);
setEvaCallback(evaluateCallback);
},
})); }));
function handleEvaluate() { // function handleEvaluate() {
setVisible(false); // setVisible(false);
// TODO: 实现NTRP评估逻辑 // // TODO: 实现NTRP评估逻辑
Taro.navigateTo({ // Taro.navigateTo({
url: `/other_pages/ntrp-evaluate/index?redirect=${encodeURIComponent( // url: `/other_pages/ntrp-evaluate/index?redirect=${encodeURIComponent(
getCurrentFullPath() // getCurrentFullPath()
)}`, // )}`,
}); // });
} // }
useEffect(() => { useEffect(() => {
getNtrp(); getNtrp();
@@ -80,20 +104,32 @@ 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) setNtrp(res.data.user_ntrp_level);
setNtrp("0");
} else { } else {
setNtrp("0"); setNtrp("");
} }
} }
const showEntry = // const showEntry =
displayCondition === "auto" // displayCondition === "auto"
? showCondition(scene, ntrp) // ? showCondition(scene, ntrp)
: displayCondition === "always"; // : displayCondition === "always";
function handleClose() { function handleClose() {
console.log("hide ....");
setVisible(false); setVisible(false);
setGuideShow(showGuide);
}
async function handleChangeNtrp() {
Taro.showLoading({ title: "修改中" });
await updateUserInfo({ ntrp_level: ntrp });
Taro.showToast({
title: "NTRP水平修改成功",
icon: "none",
});
evaCallback.next(true);
handleClose();
} }
return ( return (
@@ -108,10 +144,11 @@ const NTRPEvaluatePopup = (props: NTRPEvaluatePopupProps, ref) => {
> >
{guideShow ? ( {guideShow ? (
<NtrpPopupGuide <NtrpPopupGuide
close={handleClose} close={() => handleClose()}
skipGuide={() => { skipGuide={() => {
setGuideShow(false); setGuideShow(false);
}} }}
evaluateCallback={evaCallback}
/> />
) : ( ) : (
<View className={styles.container}> <View className={styles.container}>
@@ -122,12 +159,42 @@ const NTRPEvaluatePopup = (props: NTRPEvaluatePopupProps, ref) => {
</View> </View>
</View> </View>
<View className={styles.entryCard}> <View className={styles.entryCard}>
<NTRPTestEntryCard /> <NTRPTestEntryCard type={type} evaluateCallback={evaCallback} />
</View>
<View className={styles.picker}>
{/* FIXME: 有异常渲染问题 */}
{visible && (
<Picker
visible
options={options}
defaultValue={[ntrp]}
onChange={(val) => {
console.log(val[0]);
if (val[0]?.value) {
setNtrp(val[0]?.value as string);
}
}}
/>
)}
</View>
<View className={styles.actions}>
<View className={styles.buttonWrap}>
<Button className={styles.button} onClick={handleClose}>
</Button>
</View>
<View className={styles.buttonWrap}>
<Button
className={classnames(styles.button, styles.primary)}
onClick={handleChangeNtrp}
>
</Button>
</View>
</View> </View>
</View> </View>
)} )}
</CommonPopup> </CommonPopup>
{showEntry ? props.children : ""}
</> </>
); );
}; };

View File

@@ -3,22 +3,32 @@ import { View, Text, Button, Image } from "@tarojs/components";
import Taro from "@tarojs/taro"; import Taro from "@tarojs/taro";
import classnames from "classnames"; import classnames from "classnames";
import { useUserInfo } from "@/store/userStore"; import { useUserInfo } from "@/store/userStore";
import { useEvaluate, EvaluateCallback } from "@/store/evaluateStore";
import { delay } from "@/utils";
import ArrwoRight from "@/static/ntrp/ntrp_arrow_right.svg"; import ArrwoRight from "@/static/ntrp/ntrp_arrow_right.svg";
import CloseIcon from "@/static/ntrp/ntrp_popup_close.svg"; import CloseIcon from "@/static/ntrp/ntrp_popup_close.svg";
import DocCopy from "@/static/ntrp/ntrp_doc_copy.svg"; import DocCopy from "@/static/ntrp/ntrp_doc_copy.svg";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
function NtrpPopupGuide(props: { close: () => void; skipGuide: () => void }) { function NtrpPopupGuide(props: {
const { close, skipGuide } = props; close: () => void;
skipGuide: () => void;
evaluateCallback: EvaluateCallback;
}) {
const { close, skipGuide, evaluateCallback } = props;
const userInfo = useUserInfo(); const userInfo = useUserInfo();
const { setCallback } = useEvaluate();
function handleTest() { async function handleTest() {
Taro.redirectTo({ setCallback(evaluateCallback);
url: `/other_pages/ntrp-evaluate/index`, Taro.navigateTo({
url: `/other_pages/ntrp-evaluate/index?stage=test`,
}); });
await delay(1000);
close();
} }
return ( return (
<View className={styles.container}> <View className={styles.container} onClick={(e) => e.stopPropagation()}>
<View className={styles.header}> <View className={styles.header}>
<View className={styles.closeBtn} onClick={close}> <View className={styles.closeBtn} onClick={close}>
<Image className={styles.closeIcon} src={CloseIcon} /> <Image className={styles.closeIcon} src={CloseIcon} />

View File

@@ -1,20 +1,72 @@
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { View, Image, Text } from "@tarojs/components"; import { View, Image, Text } from "@tarojs/components";
import Taro from "@tarojs/taro";
import { useUserInfo, useUserActions } from "@/store/userStore"; import { useUserInfo, useUserActions } from "@/store/userStore";
// import { getCurrentFullPath } from "@/utils";
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 {
EvaluateScene,
useEvaluate,
EvaluateCallback,
} from "@/store/evaluateStore";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
function NTRPTestEntryCard(props) { function NTRPTestEntryCard(props: {
type: EvaluateScene;
evaluateCallback?: EvaluateCallback;
}) {
const { type, evaluateCallback } = props;
const userInfo = useUserInfo(); const userInfo = useUserInfo();
const { setCallback } = useEvaluate();
// const { fetchUserInfo } = useUserActions() // const { fetchUserInfo } = useUserActions()
// useEffect(() => { // useEffect(() => {
// fetchUserInfo() // fetchUserInfo()
// }, []) // }, [])
const { type } = props;
return type === "list" ? ( function handleTest() {
<View className={styles.higher}> switch (type) {
case (EvaluateScene.list, EvaluateScene.share):
setCallback({
type,
next: () => {
Taro.redirectTo({ url: "/game_pages/list/index" });
},
onCancel: () => {
Taro.redirectTo({ url: "/game_pages/list/index" });
},
});
case (EvaluateScene.detail, EvaluateScene.publish):
setCallback(evaluateCallback as EvaluateCallback);
case (EvaluateScene.user, EvaluateScene.userEdit):
setCallback({
type,
next: () => {
Taro.redirectTo({ url: "/game_pages/list/index" });
},
onCancel: () => {
Taro.redirectTo({ url: "/user_pages/myself/index" });
},
});
default:
setCallback({
type,
next: () => {
Taro.redirectTo({ url: "/game_pages/list/index" });
},
onCancel: () => {
Taro.redirectTo({ url: "/game_pages/list/index" });
},
});
}
Taro.redirectTo({
url: `/other_pages/ntrp-evaluate/index?stage=test`,
});
}
return type === EvaluateScene.list ? (
<View className={styles.higher} onClick={handleTest}>
<View className={styles.desc}> <View className={styles.desc}>
<View> <View>
<View className={styles.title}> <View className={styles.title}>
@@ -48,7 +100,7 @@ function NTRPTestEntryCard(props) {
</View> </View>
</View> </View>
) : ( ) : (
<View className={styles.lower}> <View className={styles.lower} onClick={handleTest}>
<View className={styles.desc}> <View className={styles.desc}>
<View className={styles.title}> <View className={styles.title}>
<Text></Text> <Text></Text>

View File

@@ -7,6 +7,8 @@ import {
renderYearMonthDay, renderYearMonthDay,
renderHourMinute, renderHourMinute,
} from "./PickerData"; } from "./PickerData";
import NTRPTestEntryCard from "../NTRPTestEntryCard";
import { EvaluateScene } from "@/store/evaluateStore";
import imgs from "@/config/images"; import imgs from "@/config/images";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
interface PickerOption { interface PickerOption {
@@ -106,25 +108,28 @@ const PopupPicker = ({
zIndex={1000} zIndex={1000}
> >
{type === "ntrp" && ( {type === "ntrp" && (
<View className={`${styles["examination-btn"]}}`}> <View className={styles.evaluateCardWrap}>
<View className={`${styles["text-container"]}}`}> <NTRPTestEntryCard type={EvaluateScene.userEdit} />
<View className={`${styles["text-title"]}}`}>
<Text>NTRP</Text>
</View>
<View className={`${styles["text-btn"]}}`}>
<Text></Text>
<Image src={imgs.ICON_ARROW_GREEN} className={`${styles["icon-arrow"]}`}></Image>
</View>
</View>
<View className={`${styles["img-container"]}}`}>
<View className={`${styles["img-box"]}`}>
<Image src={img!}></Image>
</View>
<View className={`${styles["img-box"]}`}>
<Image src={imgs.ICON_EXAMINATION}></Image>
</View>
</View>
</View> </View>
// <View className={`${styles["examination-btn"]}}`}>
// <View className={`${styles["text-container"]}}`}>
// <View className={`${styles["text-title"]}}`}>
// 不知道自己的<Text>NTRP</Text>水平
// </View>
// <View className={`${styles["text-btn"]}}`}>
// <Text>快速测试</Text>
// <Image src={imgs.ICON_ARROW_GREEN} className={`${styles["icon-arrow"]}`}></Image>
// </View>
// </View>
// <View className={`${styles["img-container"]}}`}>
// <View className={`${styles["img-box"]}`}>
// <Image src={img!}></Image>
// </View>
// <View className={`${styles["img-box"]}`}>
// <Image src={imgs.ICON_EXAMINATION}></Image>
// </View>
// </View>
// </View>
)} )}
<Picker <Picker
visible={visible} visible={visible}

View File

@@ -26,90 +26,89 @@
} }
} }
.examination-btn { .evaluateCardWrap {
padding: 8px 16px; padding: 16px;
margin: 16px; }
border-radius: 12px;
border: 1px solid rgba(0, 0, 0, 0.08);
display: flex;
justify-content: space-between;
align-items: center;
box-sizing: border-box;
background: linear-gradient(to bottom,
#CCFFF2,
/* 开始颜色 */
#F7FFFD
/* 结束颜色 */
),
repeating-linear-gradient(90deg,
/* 垂直方向 */
rgba(0, 0, 0, 1),
/* 条纹的开始颜色 */
rgba(0, 0, 0, 0.01) 1px,
/* 条纹的结束颜色及宽度 */
#CCFFF2 8px,
/* 条纹之间的开始颜色 */
#F7FFFD 10px
/* 条纹之间的结束颜色及宽度 */
);
background-blend-mode: luminosity;
/* 将两个渐变层叠在一起 */
.text-container { // .examination-btn {
.text-title { // padding: 8px 16px;
font-family: Noto Sans SC; // margin: 16px;
font-weight: 900; // border-radius: 12px;
color: #2a4d44; // border: 1px solid rgba(0, 0, 0, 0.08);
font-size: 16px; // display: flex;
margin-bottom: 4px; // justify-content: space-between;
// align-items: center;
// box-sizing: border-box;
// background: linear-gradient(
// to bottom,
// #ccfff2,
// /* 开始颜色 */ #f7fffd /* 结束颜色 */
// ),
// repeating-linear-gradient(
// 90deg,
// /* 垂直方向 */ rgba(0, 0, 0, 1),
// /* 条纹的开始颜色 */ rgba(0, 0, 0, 0.01) 1px,
// /* 条纹的结束颜色及宽度 */ #ccfff2 8px,
// /* 条纹之间的开始颜色 */ #f7fffd 10px /* 条纹之间的结束颜色及宽度 */
// );
// background-blend-mode: luminosity;
// /* 将两个渐变层叠在一起 */
Text { // .text-container {
color: #00e5ad; // .text-title {
} // font-family: Noto Sans SC;
} // font-weight: 900;
// color: #2a4d44;
// font-size: 16px;
// margin-bottom: 4px;
.text-btn { // Text {
font-size: 12px; // color: #00e5ad;
color: #5ca693; // }
display: flex; // }
align-items: center;
gap: 6px;
.icon-arrow { // .text-btn {
width: 12px; // font-size: 12px;
height: 12px; // color: #5ca693;
} // display: flex;
} // align-items: center;
} // gap: 6px;
.img-container { // .icon-arrow {
display: flex; // width: 12px;
// height: 12px;
// }
// }
// }
.img-box { // .img-container {
width: 47px; // display: flex;
height: 47px;
border: 3px solid #fff;
border-radius: 50%;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
Image { // .img-box {
width: 100%; // width: 47px;
height: 100%; // height: 47px;
} // border: 3px solid #fff;
// border-radius: 50%;
// overflow: hidden;
// display: flex;
// justify-content: center;
// align-items: center;
&:nth-child(2) { // Image {
border-radius: 8px; // width: 100%;
background-color: #ccfff2; // height: 100%;
transform: scale(0.88) rotate(15deg) translateX(-10px); // }
Image { // &:nth-child(2) {
width: 66%; // border-radius: 8px;
height: 66%; // background-color: #ccfff2;
} // transform: scale(0.88) rotate(15deg) translateX(-10px);
}
} // Image {
} // width: 66%;
} // height: 66%;
// }
// }
// }
// }
// }

View File

@@ -1,9 +1,16 @@
import React, { useState } from "react"; import React, { useState, useRef } from "react";
import { View, Text, Image } from "@tarojs/components"; import { View, Text, Image } from "@tarojs/components";
import Taro from "@tarojs/taro"; import Taro from "@tarojs/taro";
import { useUserInfo } from "@/store/userStore";
import {
useEvaluate,
EvaluateCallback,
EvaluateScene,
} from "@/store/evaluateStore";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
import images from "@/config/images"; import images from "@/config/images";
import AiImportPopup from "@/publish_pages/publishBall/components/AiImportPopup"; import AiImportPopup from "@/publish_pages/publishBall/components/AiImportPopup";
import NTRPEvaluatePopup from "../NTRPEvaluatePopup";
export interface PublishMenuProps { export interface PublishMenuProps {
onPersonalPublish?: () => void; onPersonalPublish?: () => void;
@@ -13,6 +20,10 @@ export interface PublishMenuProps {
const PublishMenu: React.FC<PublishMenuProps> = () => { const PublishMenu: React.FC<PublishMenuProps> = () => {
const [isVisible, setIsVisible] = useState(false); const [isVisible, setIsVisible] = useState(false);
const [aiImportVisible, setAiImportVisible] = useState(false); const [aiImportVisible, setAiImportVisible] = useState(false);
const userInfo = useUserInfo();
const ntrpRef = useRef<{
show: (evaluateCallback: EvaluateCallback) => void;
}>({ show: () => {} });
const handleIconClick = () => { const handleIconClick = () => {
setIsVisible(!isVisible); setIsVisible(!isVisible);
@@ -20,18 +31,42 @@ const PublishMenu: React.FC<PublishMenuProps> = () => {
const handleOverlayClick = () => { const handleOverlayClick = () => {
setIsVisible(false); setIsVisible(false);
}; };
const handleMenuItemClick = (type: "individual" | "group" | "ai") => { const handleMenuClick = (type: "individual" | "group" | "ai") => {
// 跳转到publishBall页面并传递type参数 // 跳转到publishBall页面并传递type参数
console.log(type, "type"); console.log(type, "type");
setIsVisible(false);
if (type === "ai") { if (type === "ai") {
setAiImportVisible(true); setAiImportVisible(true);
setIsVisible(false);
return; return;
} }
Taro.navigateTo({ Taro.navigateTo({
url: `/publish_pages/publishBall/index?type=${type}`, url: `/publish_pages/publishBall/index?type=${type}`,
}); });
setIsVisible(false); };
const handleMenuItemClick = (type: "individual" | "group" | "ai") => {
if (!userInfo.ntrp_level) {
ntrpRef.current.show({
type: EvaluateScene.publish,
next: (flag) => {
if (flag) {
handleMenuClick(type);
} else if (type === "ai") {
Taro.navigateBack();
setAiImportVisible(true);
} else {
Taro.redirectTo({
url: `/publish_pages/publishBall/index?type=${type}`,
});
}
},
onCancel: () => {
Taro.navigateBack();
},
});
setIsVisible(false);
return;
}
handleMenuClick(type);
}; };
const handleAiImportClose = () => { const handleAiImportClose = () => {
@@ -136,6 +171,8 @@ const PublishMenu: React.FC<PublishMenuProps> = () => {
onClose={handleAiImportClose} onClose={handleAiImportClose}
onManualPublish={handleManualPublish} onManualPublish={handleManualPublish}
/> />
<NTRPEvaluatePopup type={EvaluateScene.publish} ref={ntrpRef} showGuide />
</View> </View>
); );
}; };

View File

@@ -24,6 +24,7 @@ import Comments from "./Comments";
import GeneralNavbar from "./GeneralNavbar"; import GeneralNavbar from "./GeneralNavbar";
import RadarChart from './Radar' import RadarChart from './Radar'
import EmptyState from './EmptyState'; import EmptyState from './EmptyState';
import NTRPTestEntryCard from './NTRPTestEntryCard'
export { export {
ActivityTypeSwitch, ActivityTypeSwitch,
@@ -53,4 +54,5 @@ export {
GeneralNavbar, GeneralNavbar,
RadarChart, RadarChart,
EmptyState, EmptyState,
NTRPTestEntryCard,
}; };

View File

@@ -3,6 +3,10 @@ import ListCard from "@/components/ListCard";
import ListLoadError from "@/components/ListLoadError"; import ListLoadError from "@/components/ListLoadError";
import ListCardSkeleton from "@/components/ListCardSkeleton"; import ListCardSkeleton from "@/components/ListCardSkeleton";
import { useReachBottom } from "@tarojs/taro"; import { useReachBottom } from "@tarojs/taro";
import { useUserInfo } from "@/store/userStore";
import { setStorage, getStorage } from "@/store/storage";
import { NTRPTestEntryCard } from "@/components";
import { EvaluateScene } from "@/store/evaluateStore";
import "./index.scss"; import "./index.scss";
import { useRef, useEffect } from "react"; import { useRef, useEffect } from "react";
@@ -17,6 +21,8 @@ const ListContainer = (props) => {
} = props; } = props;
const timerRef = useRef<NodeJS.Timeout | null>(null); const timerRef = useRef<NodeJS.Timeout | null>(null);
const userInfo = useUserInfo();
useReachBottom(() => { useReachBottom(() => {
// 加载更多方法 // 加载更多方法
timerRef.current = setTimeout(() => { timerRef.current = setTimeout(() => {
@@ -46,19 +52,46 @@ const ListContainer = (props) => {
); );
}; };
// 对于没有ntrp等级的用户每个月展示一次, 插在第三个位置
function insertEvaluateCard(list) {
if (!list || list.length === 0) {
return list;
}
if (userInfo.ntrp_level) {
return list;
}
const lastShowTime = getStorage("list_evaluate_card");
if (!lastShowTime) {
setStorage("list_evaluate_card", Date.now());
}
if (Date.now() - Number(lastShowTime) < 30 * 24 * 60 * 60 * 1000) {
return list;
}
if (list.length <= 3) {
return [...list, { type: "evaluateCard" }];
}
const [item1, item2, item3, ...rest] = list;
return [item1, item2, item3, { type: "evaluateCard" }, ...rest];
}
// 渲染列表 // 渲染列表
const renderList = (list) => { const renderList = (list) => {
// 请求数据为空 // 请求数据为空
if (!loading && list?.length === 0) { if (!loading && (!list || list?.length === 0)) {
return <ListLoadError reload={reload} text="暂无数据" />; return <ListLoadError reload={reload} text="暂无数据" />;
} }
// 渲染数据 // 渲染数据
return ( return (
<> <>
{list?.map((match, index) => ( {insertEvaluateCard(list).map((match, index) => {
<ListCard key={match.id || index} {...match} /> if (match.type === "evaluateCard") {
))} return (
<NTRPTestEntryCard key="evaluate" type={EvaluateScene.list} />
);
}
return <ListCard key={match.id || index} {...match} />;
})}
</> </>
); );
}; };
@@ -74,15 +107,15 @@ const ListContainer = (props) => {
onScrollToLower={handleScrollToLower} onScrollToLower={handleScrollToLower}
upperThreshold={60} upperThreshold={60}
> */} > */}
{renderList(data)} {renderList(data)}
{/* 显示骨架屏 */} {/* 显示骨架屏 */}
{loading && renderSkeleton()} {loading && renderSkeleton()}
{/* <View className="recommendTextWrapper"> {/* <View className="recommendTextWrapper">
<Text className="recommendText">搜索结果较少,已为你推荐其他内容</Text> <Text className="recommendText">搜索结果较少,已为你推荐其他内容</Text>
</View> </View>
{renderList(recommendList)} */} {renderList(recommendList)} */}
{/* 到底了 */} {/* 到底了 */}
{data?.length > 0 && <View className="bottomTextWrapper"></View>} {data?.length > 0 && <View className="bottomTextWrapper"></View>}
{/* </ScrollView> */} {/* </ScrollView> */}
</View> </View>
); );

View File

@@ -24,11 +24,11 @@ import {
GameManagePopup, GameManagePopup,
Comments, Comments,
} from "@/components"; } from "@/components";
import { // import {
EvaluateType, // EvaluateType,
SceneType, // SceneType,
DisplayConditionType, // DisplayConditionType,
} from "@/components/NTRPEvaluatePopup"; // } from "@/components/NTRPEvaluatePopup";
import DetailService, { import DetailService, {
MATCH_STATUS, MATCH_STATUS,
IsSubstituteSupported, IsSubstituteSupported,
@@ -36,7 +36,9 @@ import DetailService, {
import * as LoginService from "@/services/loginService"; import * as LoginService from "@/services/loginService";
import OrderService from "@/services/orderService"; import OrderService from "@/services/orderService";
import { getCurrentLocation, calculateDistance } from "@/utils/locationUtils"; import { getCurrentLocation, calculateDistance } from "@/utils/locationUtils";
// import { getCurrentFullPath } from "@/utils";
import { useUserInfo, useUserActions } from "@/store/userStore"; import { useUserInfo, useUserActions } from "@/store/userStore";
import { EvaluateCallback, EvaluateScene } from "@/store/evaluateStore";
import img from "@/config/images"; import img from "@/config/images";
import styles from "./style.module.scss"; import styles from "./style.module.scss";
import "./index.scss"; import "./index.scss";
@@ -280,7 +282,9 @@ function StickyButton(props) {
getCommentCount, getCommentCount,
} = props; } = props;
const [commentCount, setCommentCount] = useState(0); const [commentCount, setCommentCount] = useState(0);
const ntrpRef = useRef(null); const ntrpRef = useRef<{
show: (evaluateCallback: EvaluateCallback) => void;
}>({ show: () => {} });
const { const {
id, id,
price, price,
@@ -294,8 +298,22 @@ function StickyButton(props) {
const gameManageRef = useRef(); const gameManageRef = useRef();
function handleSelfEvaluate() { function handleSelfEvaluate() {
// TODO: 打开自评弹窗 ntrpRef?.current?.show({
ntrpRef?.current?.show(); type: EvaluateScene.detail,
next: (flag) => {
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();
},
});
} }
useEffect(() => { useEffect(() => {
@@ -385,17 +403,7 @@ function StickyButton(props) {
}; };
} else if (can_assess) { } else if (can_assess) {
return { return {
text: () => ( text: () => <Text>¥{displayPrice} </Text>,
<NTRPEvaluatePopup
ref={ntrpRef}
types={[EvaluateType.EDIT, EvaluateType.EVALUATE]}
scene={SceneType.DETAIL}
displayCondition={DisplayConditionType.AUTO}
showGuide={false}
>
<Text>¥{displayPrice} </Text>
</NTRPEvaluatePopup>
),
action: handleSelfEvaluate, action: handleSelfEvaluate,
}; };
} }
@@ -477,6 +485,7 @@ function StickyButton(props) {
</View> </View>
</View> </View>
<GameManagePopup ref={gameManageRef} /> <GameManagePopup ref={gameManageRef} />
<NTRPEvaluatePopup type={EvaluateScene.detail} ref={ntrpRef} showGuide />
</> </>
); );
} }

View File

@@ -2,7 +2,11 @@ import SearchBar from "@/components/SearchBar";
import FilterPopup from "@/components/FilterPopup"; import FilterPopup from "@/components/FilterPopup";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
import { useEffect, useRef, useCallback, useState } from "react"; import { useEffect, useRef, useCallback, useState } from "react";
import Taro, { usePageScroll, useShareAppMessage, useShareTimeline } from "@tarojs/taro"; import Taro, {
usePageScroll,
useShareAppMessage,
useShareTimeline,
} from "@tarojs/taro";
import { useListStore } from "@/store/listStore"; import { useListStore } from "@/store/listStore";
import { useGlobalState } from "@/store/global"; import { useGlobalState } from "@/store/global";
import { View } from "@tarojs/components"; import { View } from "@tarojs/components";
@@ -13,14 +17,17 @@ import DistanceQuickFilter from "@/components/DistanceQuickFilter";
import { withAuth } from "@/components"; import { withAuth } from "@/components";
import { updateUserLocation } from "@/services/userService"; import { updateUserLocation } from "@/services/userService";
// import ShareCardCanvas from "@/components/ShareCardCanvas"; // import ShareCardCanvas from "@/components/ShareCardCanvas";
import { useUserActions } from "@/store/userStore";
import { useDictionaryStore } from "@/store/dictionaryStore"; import { useDictionaryStore } from "@/store/dictionaryStore";
const ListPage = () => { const ListPage = () => {
// 从 store 获取数据和方法 // 从 store 获取数据和方法
const store = useListStore() || {}; const store = useListStore() || {};
const { statusNavbarHeightInfo, getCurrentLocationInfo } = useGlobalState() || {}; const { fetchUserInfo } = useUserActions();
const { statusNavbarHeightInfo, getCurrentLocationInfo } =
useGlobalState() || {};
const { totalHeight } = statusNavbarHeightInfo || {}; const { totalHeight } = statusNavbarHeightInfo || {};
const { const {
@@ -49,48 +56,52 @@ const ListPage = () => {
filterOptions, filterOptions,
distanceQuickFilter, distanceQuickFilter,
isShowInputCustomerNavBar, isShowInputCustomerNavBar,
pageOption pageOption,
} = listPageState || {}; } = listPageState || {};
// 防抖的滚动处理函数 // 防抖的滚动处理函数
const handleScroll = useCallback((res) => { const handleScroll = useCallback(
const currentScrollTop = res?.scrollTop || 0; (res) => {
const currentScrollTop = res?.scrollTop || 0;
// 添加缓冲区,避免临界点频繁切换 // 添加缓冲区,避免临界点频繁切换
const buffer = 10; // 10px 缓冲区 const buffer = 10; // 10px 缓冲区
const shouldShowInputNav = currentScrollTop >= (totalHeight + buffer); const shouldShowInputNav = currentScrollTop >= totalHeight + buffer;
const shouldHideInputNav = currentScrollTop < (totalHeight - buffer); const shouldHideInputNav = currentScrollTop < totalHeight - buffer;
// 清除之前的定时器 // 清除之前的定时器
if (scrollTimeoutRef.current) { if (scrollTimeoutRef.current) {
clearTimeout(scrollTimeoutRef.current); clearTimeout(scrollTimeoutRef.current);
}
// 防抖处理,避免频繁更新状态
scrollTimeoutRef.current = setTimeout(() => {
// 只有在状态真正需要改变时才更新
if (shouldShowInputNav && !isShowInputCustomerNavBar) {
updateListPageState({
isShowInputCustomerNavBar: true
});
} else if (shouldHideInputNav && isShowInputCustomerNavBar) {
updateListPageState({
isShowInputCustomerNavBar: false
});
} }
lastScrollTopRef.current = currentScrollTop; // 防抖处理,避免频繁更新状态
}, 16); // 约60fps的防抖间隔 scrollTimeoutRef.current = setTimeout(() => {
}, [totalHeight, isShowInputCustomerNavBar, updateState]); // 只有在状态真正需要改变时才更新
if (shouldShowInputNav && !isShowInputCustomerNavBar) {
updateListPageState({
isShowInputCustomerNavBar: true,
});
} else if (shouldHideInputNav && isShowInputCustomerNavBar) {
updateListPageState({
isShowInputCustomerNavBar: false,
});
}
lastScrollTopRef.current = currentScrollTop;
}, 16); // 约60fps的防抖间隔
},
[totalHeight, isShowInputCustomerNavBar, updateState]
);
usePageScroll(handleScroll); usePageScroll(handleScroll);
const scrollContextRef = useRef(null) const scrollContextRef = useRef(null);
const scrollTimeoutRef = useRef<NodeJS.Timeout | null>(null) const scrollTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const lastScrollTopRef = useRef(0) const lastScrollTopRef = useRef(0);
useEffect(() => { useEffect(() => {
getLocation() getLocation();
fetchUserInfo();
}, []); }, []);
// 监听数据变化,如果是第一页就滚动到顶部 // 监听数据变化,如果是第一页就滚动到顶部
@@ -98,7 +109,7 @@ const ListPage = () => {
if (pageOption?.page === 1 && matches?.length > 0) { if (pageOption?.page === 1 && matches?.length > 0) {
Taro.pageScrollTo({ Taro.pageScrollTo({
scrollTop: 0, scrollTop: 0,
duration: 300 duration: 300,
}); });
} }
}, [matches, pageOption?.page]); }, [matches, pageOption?.page]);
@@ -112,7 +123,6 @@ const ListPage = () => {
}; };
}, []); }, []);
// 监听距离和排序方式变化,自动调用接口 // 监听距离和排序方式变化,自动调用接口
// useEffect(() => { // useEffect(() => {
// // 只有当 distanceQuickFilter 有值时才调用接口 // // 只有当 distanceQuickFilter 有值时才调用接口
@@ -126,7 +136,7 @@ const ListPage = () => {
// 获取位置信息 // 获取位置信息
const getLocation = async () => { const getLocation = async () => {
const location = await getCurrentLocationInfo() const location = await getCurrentLocationInfo();
// 保存位置到全局状态 // 保存位置到全局状态
updateState({ location }); updateState({ location });
@@ -136,7 +146,7 @@ const ListPage = () => {
try { try {
await updateUserLocation(location.latitude, location.longitude); await updateUserLocation(location.latitude, location.longitude);
} catch (error) { } catch (error) {
console.error('更新用户位置失败:', error); console.error("更新用户位置失败:", error);
} }
} }
fetchGetGamesCount(); fetchGetGamesCount();
@@ -144,7 +154,7 @@ const ListPage = () => {
// 页面加载时获取数据 // 页面加载时获取数据
getMatchesData(); getMatchesData();
return location; return location;
} };
const refreshMatches = () => { const refreshMatches = () => {
initialFilterSearch(true); initialFilterSearch(true);
@@ -188,7 +198,7 @@ const ListPage = () => {
const handleFilterConfirm = () => { const handleFilterConfirm = () => {
toggleShowPopup(); toggleShowPopup();
getMatchesData(); getMatchesData();
} };
/** /**
* @description 综合筛选弹框 * @description 综合筛选弹框
@@ -196,7 +206,7 @@ const ListPage = () => {
*/ */
const toggleShowPopup = () => { const toggleShowPopup = () => {
updateListPageState({ updateListPageState({
isShowFilterPopup: !isShowFilterPopup isShowFilterPopup: !isShowFilterPopup,
}); });
}; };
@@ -208,7 +218,7 @@ const ListPage = () => {
updateFilterOptions(params); updateFilterOptions(params);
}; };
const handleSearchChange = () => { }; const handleSearchChange = () => {};
// 距离筛选 // 距离筛选
const handleDistanceOrQuickChange = (name, value) => { const handleDistanceOrQuickChange = (name, value) => {
@@ -278,10 +288,10 @@ const ListPage = () => {
} catch (error) { } catch (error) {
console.error("初始化字典数据失败:", error); console.error("初始化字典数据失败:", error);
} }
} };
useEffect(() => { useEffect(() => {
initDictionaryData() initDictionaryData();
}, []); }, []);
return ( return (
@@ -311,9 +321,7 @@ const ListPage = () => {
/> />
</View> </View>
)} )}
<View <View className={`${styles.listTopSearchWrapper}`}>
className={`${styles.listTopSearchWrapper}`}
>
<SearchBar <SearchBar
handleFilterIcon={toggleShowPopup} handleFilterIcon={toggleShowPopup}
isSelect={filterCount > 0} isSelect={filterCount > 0}
@@ -322,13 +330,14 @@ const ListPage = () => {
value={searchValue} value={searchValue}
onInputClick={handleSearchClick} onInputClick={handleSearchClick}
/> />
</View> </View>
{/* 筛选 */} {/* 筛选 */}
<View className={styles.listTopFilterWrapper} <View
className={styles.listTopFilterWrapper}
style={{ style={{
top: totalHeight - 1, top: totalHeight - 1,
}}> }}
>
<DistanceQuickFilter <DistanceQuickFilter
cityOptions={distanceData} cityOptions={distanceData}
quickOptions={quickFilterData} quickOptions={quickFilterData}

View File

@@ -1,4 +1,4 @@
@use '~@/scss/images.scss' as img; @use "~@/scss/images.scss" as img;
.message-container { .message-container {
width: 100%; width: 100%;
@@ -6,15 +6,16 @@
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
background: #FFFFFF; background: #ffffff;
// 顶部导航栏 // 顶部导航栏
.navbar { .navbar {
height: 100px; height: 56px;
background: #FFFFFF; background: #ffffff;
position: sticky; position: sticky;
top: 0; top: 0;
z-index: 100; z-index: 100;
box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.06);
.navbar-content { .navbar-content {
height: 56px; height: 56px;
@@ -34,7 +35,7 @@
} }
.navbar-title { .navbar-title {
font-family: 'PingFang SC'; font-family: "PingFang SC";
font-weight: 600; font-weight: 600;
font-size: 20px; font-size: 20px;
line-height: 1.4; line-height: 1.4;
@@ -51,13 +52,27 @@
align-items: stretch; align-items: stretch;
gap: 20px; gap: 20px;
padding: 6px 24px; padding: 6px 24px;
background: #FFFFFF; background: #ffffff;
.tab-item { .tab-item {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
}
}
// 消息列表
.message-list {
flex: 1;
overflow: hidden;
box-sizing: border-box;
// margin-bottom:100px;
background-color: none !important;
.message-list-content {
display: flex;
flex-direction: column;
padding: 12px 12px 112px;
gap: 8px; gap: 8px;
padding: 12px 15px; padding: 12px 15px;
flex: 1; flex: 1;
@@ -72,7 +87,7 @@
} }
.tab-text { .tab-text {
font-family: 'PingFang SC'; font-family: "PingFang SC";
font-weight: 600; font-weight: 600;
font-size: 16px; font-size: 16px;
line-height: 1.5; line-height: 1.5;
@@ -98,7 +113,7 @@
.message-scroll { .message-scroll {
flex: 1; flex: 1;
overflow: hidden; overflow: hidden;
background: #FFFFFF; background: #ffffff;
.message-cards { .message-cards {
padding: 12px; padding: 12px;
@@ -110,7 +125,7 @@
// 系统消息卡片 // 系统消息卡片
.message-card { .message-card {
background: #FFFFFF; background: #ffffff;
border: 0.5px solid rgba(0, 0, 0, 0.08); border: 0.5px solid rgba(0, 0, 0, 0.08);
border-radius: 20px; border-radius: 20px;
box-shadow: 0px 4px 36px 0px rgba(0, 0, 0, 0.06); box-shadow: 0px 4px 36px 0px rgba(0, 0, 0, 0.06);
@@ -126,7 +141,7 @@
padding: 12px 15px 0; padding: 12px 15px 0;
.card-title { .card-title {
font-family: 'PingFang SC'; font-family: "PingFang SC";
font-weight: 600; font-weight: 600;
font-size: 16px; font-size: 16px;
line-height: 1.5; line-height: 1.5;
@@ -141,7 +156,7 @@
gap: 2px; gap: 2px;
.card-time { .card-time {
font-family: 'PingFang SC'; font-family: "PingFang SC";
font-weight: 400; font-weight: 400;
font-size: 12px; font-size: 12px;
line-height: 1.5; line-height: 1.5;
@@ -156,7 +171,7 @@
gap: 2px; gap: 2px;
.card-content { .card-content {
font-family: 'PingFang SC'; font-family: "PingFang SC";
font-weight: 400; font-weight: 400;
font-size: 14px; font-size: 14px;
line-height: 1.43; line-height: 1.43;
@@ -182,7 +197,7 @@
cursor: pointer; cursor: pointer;
.action-text { .action-text {
font-family: 'PingFang SC'; font-family: "PingFang SC";
font-weight: 600; font-weight: 600;
font-size: 14px; font-size: 14px;
line-height: 1.43; line-height: 1.43;
@@ -195,7 +210,7 @@
position: absolute; position: absolute;
left: -16px; left: -16px;
top: -9px; top: -9px;
width: 14px;; width: 14px;
height: 14px; height: 14px;
} }
} }
@@ -215,7 +230,7 @@
padding: 24px 0 12px; padding: 24px 0 12px;
.tip-text { .tip-text {
font-family: 'PingFang SC'; font-family: "PingFang SC";
font-weight: 400; font-weight: 400;
font-size: 14px; font-size: 14px;
line-height: 1.71; line-height: 1.71;
@@ -232,7 +247,12 @@
width: 60px; width: 60px;
height: 60px; height: 60px;
border-radius: 50%; border-radius: 50%;
background: radial-gradient(circle at 27% 8%, rgba(189, 255, 74, 1) 17%, rgba(149, 242, 62, 1) 54%, rgba(50, 216, 56, 1) 100%); background: radial-gradient(
circle at 27% 8%,
rgba(189, 255, 74, 1) 17%,
rgba(149, 242, 62, 1) 54%,
rgba(50, 216, 56, 1) 100%
);
border: 2px solid rgba(0, 0, 0, 0.06); border: 2px solid rgba(0, 0, 0, 0.06);
box-shadow: 0px 4px 48px 0px rgba(0, 0, 0, 0.08); box-shadow: 0px 4px 48px 0px rgba(0, 0, 0, 0.08);
display: flex; display: flex;
@@ -253,31 +273,31 @@
// 加号图标 - 竖线 // 加号图标 - 竖线
&::before { &::before {
content: ''; content: "";
position: absolute; position: absolute;
left: 50%; left: 50%;
top: 50%; top: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
width: 4px; width: 4px;
height: 25px; height: 25px;
background: #FFFFFF; background: #ffffff;
border-radius: 2px; border-radius: 2px;
box-shadow: 0px 4px 12px 0px rgba(0, 0, 0, 0.2); box-shadow: 0px 4px 12px 0px rgba(0, 0, 0, 0.2);
} }
// 加号图标 - 横线 // 加号图标 - 横线
&::after { &::after {
content: ''; content: "";
position: absolute; position: absolute;
left: 50%; left: 50%;
top: 50%; top: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
width: 25px; width: 25px;
height: 4px; height: 4px;
background: #FFFFFF; background: #ffffff;
border-radius: 2px; border-radius: 2px;
box-shadow: 0px 4px 12px 0px rgba(0, 0, 0, 0.2); box-shadow: 0px 4px 12px 0px rgba(0, 0, 0, 0.2);
} }
} }
} }
} }

View File

@@ -10,6 +10,7 @@ import evaluateService, {
TestResultData, TestResultData,
} 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 { delay, getCurrentFullPath } from "@/utils"; import { delay, getCurrentFullPath } from "@/utils";
import CloseIcon from "@/static/ntrp/ntrp_close_icon.svg"; import CloseIcon from "@/static/ntrp/ntrp_close_icon.svg";
import DocCopy from "@/static/ntrp/ntrp_doc_copy.svg"; import DocCopy from "@/static/ntrp/ntrp_doc_copy.svg";
@@ -28,15 +29,10 @@ enum StageType {
RESULT = "result", RESULT = "result",
} }
enum SourceType {
DETAIL = 'detail',
PUBLISH = 'publish',
}
const sourceTypeToTextMap = new Map([ const sourceTypeToTextMap = new Map([
[SourceType.DETAIL, '继续加入球局'], [EvaluateScene.detail, "继续加入球局"],
[SourceType.PUBLISH, '继续发布球局'], [EvaluateScene.publish, "继续发布球局"],
]) ]);
function adjustRadarLabels( function adjustRadarLabels(
source: [string, number][], source: [string, number][],
@@ -80,18 +76,31 @@ function adjustRadarLabels(
return result as [string, number][]; return result as [string, number][];
} }
function isEmptyArrowFunction(fn) {
return (
typeof fn === "function" &&
!fn.hasOwnProperty("prototype") && // 排除普通函数
fn.toString().replace(/\s+/g, "") === "()=>{}"
);
}
function CommonGuideBar(props) { function CommonGuideBar(props) {
const { title, confirm } = props; const { title, confirm } = props;
const { params } = useRouter(); const { onCancel } = useEvaluate();
const { redirect } = params; // const userInfo = useUserInfo()
function handleClose() { function handleClose() {
//TODO: 二次确认 //TODO: 二次确认
if (confirm) { if (confirm) {
} }
Taro.redirectTo({ try {
url: redirect ? redirect : "/game_pages/list/index", if (isEmptyArrowFunction(onCancel)) {
}); Taro.redirectTo({ url: "/game_pages/list/index" });
}
onCancel();
} catch {
Taro.redirectTo({ url: "/game_pages/list/index" });
}
} }
return ( return (
@@ -106,12 +115,12 @@ function CommonGuideBar(props) {
); );
} }
function Intro(props) { function Intro() {
const { redirect } = props;
const [ntrpData, setNtrpData] = useState<LastTimeTestResult>(); const [ntrpData, setNtrpData] = useState<LastTimeTestResult>();
const userInfo = useUserInfo(); const userInfo = useUserInfo();
const { fetchUserInfo } = useUserActions(); const { fetchUserInfo } = useUserActions();
const [ready, setReady] = useState(false); const [ready, setReady] = useState(false);
const { setCallback } = useEvaluate();
const { last_test_result: { ntrp_level, create_time, id } = {} } = const { last_test_result: { ntrp_level, create_time, id } = {} } =
ntrpData || {}; ntrpData || {};
@@ -136,10 +145,24 @@ function Intro(props) {
} }
function handleNext(type) { function handleNext(type) {
setCallback({
type: EvaluateScene.share,
next: () => {
Taro.redirectTo({ url: "/game_pages/list/index" });
},
onCancel: () => {
Taro.redirectTo({ url: "/game_pages/list/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}` : ""
}${redirect ? `&redirect=${redirect}` : ""}`, }`,
}); });
} }
@@ -249,8 +272,7 @@ function Intro(props) {
); );
} }
function Test(props) { function Test() {
const { redirect } = props;
const [disabled, setDisabled] = useState(false); const [disabled, setDisabled] = useState(false);
const [index, setIndex] = useState(0); const [index, setIndex] = useState(0);
const [questions, setQuestions] = useState< const [questions, setQuestions] = useState<
@@ -295,9 +317,7 @@ function Test(props) {
}); });
if (res.code === 0) { if (res.code === 0) {
Taro.redirectTo({ Taro.redirectTo({
url: `/other_pages/ntrp-evaluate/index?stage=${StageType.RESULT}&id=${ url: `/other_pages/ntrp-evaluate/index?stage=${StageType.RESULT}&id=${res.data.record_id}`,
res.data.record_id
}${redirect ? `&redirect=${redirect}` : ""}`,
}); });
} }
} catch (e) { } catch (e) {
@@ -378,11 +398,12 @@ function Test(props) {
); );
} }
function Result(props) { function Result() {
const { params } = useRouter(); const { params } = useRouter();
const { id, type, redirect } = params; const { id } = params;
const userInfo = useUserInfo(); const userInfo = useUserInfo();
const { fetchUserInfo } = useUserActions(); const { fetchUserInfo } = useUserActions();
const { type, next, clear } = useEvaluate();
const radarRef = useRef(); const radarRef = useRef();
const [result, setResult] = useState<TestResultData>(); const [result, setResult] = useState<TestResultData>();
@@ -425,9 +446,7 @@ function Result(props) {
function handleReTest() { function handleReTest() {
Taro.redirectTo({ Taro.redirectTo({
url: `/other_pages/ntrp-evaluate/index?stage=${StageType.TEST}${ url: `/other_pages/ntrp-evaluate/index?stage=${StageType.TEST}`,
redirect ? `&redirect=${redirect}` : ""
}`,
}); });
} }
@@ -437,11 +456,13 @@ function Result(props) {
}); });
} }
function handleGoon () { async function handleGoon() {
if (type) { if (type) {
Taro.redirectTo({ url: redirect }) next();
await delay(1500);
clear();
} else { } else {
handleViewGames() handleViewGames();
} }
} }
@@ -495,7 +516,7 @@ function Result(props) {
async function handleSaveImage() { async function handleSaveImage() {
if (!userInfo.id) { if (!userInfo.id) {
return return;
} }
const url = await genCardImage(); const url = await genCardImage();
Taro.saveImageToPhotosAlbum({ filePath: url }); Taro.saveImageToPhotosAlbum({ filePath: url });
@@ -511,11 +532,11 @@ function Result(props) {
}; };
}); });
function handleAuth () { function handleAuth() {
if (userInfo.id) { if (userInfo.id) {
return true return true;
} }
const currentPage = getCurrentFullPath() const currentPage = getCurrentFullPath();
Taro.redirectTo({ Taro.redirectTo({
url: `/login_pages/index/index${ url: `/login_pages/index/index${
currentPage ? `?redirect=${encodeURIComponent(currentPage)}` : "" currentPage ? `?redirect=${encodeURIComponent(currentPage)}` : ""
@@ -576,11 +597,17 @@ function Result(props) {
)} )}
<View className={styles.actions}> <View className={styles.actions}>
<View className={styles.viewGame} onClick={handleGoon}> <View className={styles.viewGame} onClick={handleGoon}>
<Button className={styles.viewGameBtn}>{sourceTypeToTextMap.get(type) || '去看看球局'}</Button> <Button className={styles.viewGameBtn}>
{sourceTypeToTextMap.get(type) || "去看看球局"}
</Button>
</View> </View>
<View className={styles.otherActions}> <View className={styles.otherActions}>
<View className={styles.share}> <View className={styles.share}>
<Button className={styles.shareBtn} openType={userInfo.id ? 'share' : undefined} onClick={handleAuth}> <Button
className={styles.shareBtn}
openType={userInfo.id ? "share" : undefined}
onClick={handleAuth}
>
<Image className={styles.wechatIcon} src={WechatIcon} /> <Image className={styles.wechatIcon} src={WechatIcon} />
<Text></Text> <Text></Text>
</Button> </Button>
@@ -614,30 +641,30 @@ const ComponentsMap = {
}; };
function NtrpEvaluate() { function NtrpEvaluate() {
const { updateUserInfo } = useUserActions(); // const { updateUserInfo } = useUserActions();
const { params } = useRouter(); const { params } = useRouter();
const { redirect } = params; // const { redirect } = params;
const stage = params.stage as StageType; const stage = params.stage as StageType;
async function handleUpdateNtrp() { // async function handleUpdateNtrp() {
await updateUserInfo({ // await updateUserInfo({
ntrp_level: "4.0", // ntrp_level: "4.0",
}); // });
Taro.showToast({ // Taro.showToast({
title: "更新成功", // title: "更新成功",
icon: "success", // icon: "success",
duration: 2000, // duration: 2000,
}); // });
await delay(2000); // await delay(2000);
if (redirect) { // if (redirect) {
Taro.redirectTo({ url: decodeURIComponent(redirect) }); // Taro.redirectTo({ url: decodeURIComponent(redirect) });
} // }
} // }
const Component = ComponentsMap[stage]; const Component = ComponentsMap[stage];
return <Component redirect={redirect} />; return <Component />;
} }
export default withAuth(NtrpEvaluate); export default withAuth(NtrpEvaluate);

View File

@@ -0,0 +1,43 @@
import { create } from "zustand";
export enum EvaluateScene {
list,
publish,
detail,
user,
userEdit,
share,
}
export interface EvaluateCallback {
type: EvaluateScene | ''
next: (flag?: boolean) => void,
onCancel: () => void,
}
export interface EvaluateCallbackType extends EvaluateCallback {
setCallback: (options: { type: EvaluateScene | '', next: () => void, onCancel: () => void }) => void,
clear: () => void,
}
export const useEvaluateCallback = create<EvaluateCallbackType>()((set) => ({
type: '',
next: () => { },
onCancel: () => { },
setCallback: ({ type, next, onCancel }) => {
set({
type,
next,
onCancel,
})
},
clear: () => { set({ type: '', next: () => { }, onCancel: () => { } }) }
}));
export const useEvaluate = () => useEvaluateCallback(({ type, next, onCancel, setCallback, clear }) => ({
type,
next,
onCancel,
setCallback,
clear,
}))

View File

@@ -7,7 +7,8 @@ import { UserInfoCard, UserInfo } from "@/components/UserInfo/index";
import { UserService } from "@/services/userService"; import { UserService } from "@/services/userService";
import ListContainer from "@/container/listContainer"; import ListContainer from "@/container/listContainer";
import { TennisMatch } from "@/../types/list/types"; import { TennisMatch } from "@/../types/list/types";
import { withAuth } from "@/components"; import { withAuth, NTRPTestEntryCard } from "@/components";
import { EvaluateScene } from "@/store/evaluateStore";
const MyselfPage: React.FC = () => { const MyselfPage: React.FC = () => {
// 获取页面参数 // 获取页面参数
@@ -34,9 +35,9 @@ const MyselfPage: React.FC = () => {
occupation: "加载中...", occupation: "加载中...",
ntrp_level: "NTRP 3.0", ntrp_level: "NTRP 3.0",
personal_profile: "加载中...", personal_profile: "加载中...",
gender: '', gender: "",
country: '', country: "",
province: '', province: "",
}); });
// 球局记录状态 // 球局记录状态
@@ -50,7 +51,7 @@ const MyselfPage: React.FC = () => {
// 当前激活的标签页 // 当前激活的标签页
const [active_tab, setActiveTab] = useState<"hosted" | "participated">( const [active_tab, setActiveTab] = useState<"hosted" | "participated">(
"hosted", "hosted"
); );
// 加载用户数据 // 加载用户数据
@@ -131,7 +132,10 @@ const MyselfPage: React.FC = () => {
games_data = await UserService.get_participated_games(user_info.id); games_data = await UserService.get_participated_games(user_info.id);
} }
const sorted_games = games_data.sort((a, b) => { const sorted_games = games_data.sort((a, b) => {
return new Date(a.original_start_time.replace(/\s/, 'T')).getTime() - new Date(b.original_start_time.replace(/\s/, 'T')).getTime(); return (
new Date(a.original_start_time.replace(/\s/, "T")).getTime() -
new Date(b.original_start_time.replace(/\s/, "T")).getTime()
);
}); });
const { notEndGames, finishedGames } = classifyGameRecords(sorted_games); const { notEndGames, finishedGames } = classifyGameRecords(sorted_games);
set_game_records(notEndGames); set_game_records(notEndGames);
@@ -147,7 +151,7 @@ const MyselfPage: React.FC = () => {
try { try {
const new_following_state = await UserService.toggle_follow( const new_following_state = await UserService.toggle_follow(
user_id, user_id,
is_following, is_following
); );
setIsFollowing(new_following_state); setIsFollowing(new_following_state);
@@ -221,6 +225,8 @@ const MyselfPage: React.FC = () => {
</View> </View>
</View> </View>
<NTRPTestEntryCard type={EvaluateScene.user} />
{/* 球局类型标签页 */} {/* 球局类型标签页 */}
<View className="game_tabs_section"> <View className="game_tabs_section">
<View className="tab_container"> <View className="tab_container">
@@ -231,7 +237,9 @@ const MyselfPage: React.FC = () => {
<Text className="tab_text"></Text> <Text className="tab_text"></Text>
</View> </View>
<View <View
className={`tab_item ${active_tab === "participated" ? "active" : ""}`} className={`tab_item ${
active_tab === "participated" ? "active" : ""
}`}
onClick={() => setActiveTab("participated")} onClick={() => setActiveTab("participated")}
> >
<Text className="tab_text"></Text> <Text className="tab_text"></Text>
@@ -248,7 +256,7 @@ const MyselfPage: React.FC = () => {
loading={loading} loading={loading}
error={null} error={null}
reload={load_game_data} reload={load_game_data}
loadMoreMatches={() => { }} loadMoreMatches={() => {}}
/> />
</ScrollView> </ScrollView>
</View> </View>
@@ -271,7 +279,7 @@ const MyselfPage: React.FC = () => {
loading={loading} loading={loading}
error={null} error={null}
reload={load_game_data} reload={load_game_data}
loadMoreMatches={() => { }} loadMoreMatches={() => {}}
/> />
</ScrollView> </ScrollView>
{/* </View> */} {/* </View> */}