Merge branch master into feature/juguohong/20250816

This commit is contained in:
李瑞
2025-09-30 15:58:59 +08:00
190 changed files with 14267 additions and 2085 deletions

View File

@@ -1,4 +1,5 @@
export default definePageConfig({
navigationBarTitleText: '球局详情',
navigationStyle: 'custom',
enableShareAppMessage: true,
})

View File

@@ -162,7 +162,6 @@
&-image {
width: 28px;
height: 28px;
border-radius: 50%;
}
}
@@ -690,9 +689,11 @@
background: rgba(255, 255, 255, 0.16);
flex: 0 0 auto;
&-avatar {
.participants-list-item-avatar {
width: 60px;
height: 60px;
border-radius: 50%;
overflow: hidden;
}
&-name {
@@ -806,7 +807,7 @@
}
&-organizer-recommend-games {
padding: 24px 15px 0;
padding: 24px 15px 10px;
.organizer-title {
overflow: hidden;
@@ -836,6 +837,8 @@
&-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
object-fit: cover;
}
&-message {
@@ -1012,6 +1015,8 @@
&-avatar {
width: 20px;
height: 20px;
border-radius: 50%;
object-fit: cover;
}
&-message {
@@ -1061,7 +1066,7 @@
display: flex;
align-items: center;
height: 52px;
width: 113px;
width: 120px;
box-sizing: border-box;
padding: 2px 20px;
justify-content: center;
@@ -1117,60 +1122,58 @@
}
}
&-join-game {
.detail-main-action {
display: flex;
align-items: center;
height: 52px;
width: auto;
padding: 2px 6px;
// padding: 2px 6px;
box-sizing: border-box;
justify-content: center;
gap: 12px;
// gap: 12px;
flex: 1 0 0;
border-radius: 16px;
border: 1px solid rgba(0, 0, 0, 0.06);
// border: 1px solid rgba(0, 0, 0, 0.06);
background: #fff;
overflow: hidden;
&-price {
font-family: "PoetsenOne";
font-size: 28px;
font-weight: 400;
line-height: 24px; /* 114.286% */
letter-spacing: -0.56px;
color: #000;
&.disabled {
background-color: #B4B4B4;
color: rgba(60, 60, 67, 0.60);
pointer-events: none;
}
.sticky-bottom-bar-join-game {
margin-left: auto;
// width: 151px;
display: flex;
align-items: center;
justify-content: center;
flex: 1;
&-price {
font-family: "PoetsenOne";
font-size: 28px;
font-weight: 400;
line-height: 24px; /* 114.286% */
letter-spacing: -0.56px;
color: #000;
}
}
.game_manage {
width: 100px;
margin-left: auto;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: #000;
color: #fff;
pointer-events: all;
}
}
}
}
}
.share-popup-content {
width: 100%;
height: 100%;
padding: 20px 16px env(safe-area-inset-bottom);
box-sizing: border-box;
// padding-bottom: env(safe-area-inset-bottom);
box-sizing: border-box;
display: flex;
justify-content: space-around;
align-items: center;
& > view {
width: 100px;
height: 64px;
border-radius: 12px;
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
& > image {
width: 24px;
height: 24px;
}
& > text {
color: rgba(0, 0, 0, 0.85);
}
}
}

View File

@@ -5,41 +5,53 @@ import React, {
useImperativeHandle,
forwardRef,
} from "react";
import { View, Text, Image, Map, ScrollView } from "@tarojs/components";
import { Avatar } from "@nutui/nutui-react-taro";
import { View, Text, Image, Map, ScrollView, Button } from "@tarojs/components";
// import { Avatar } from "@nutui/nutui-react-taro";
import Taro, {
useRouter,
useShareAppMessage,
useShareTimeline,
useDidShow,
} from "@tarojs/taro";
import classnames from "classnames";
import dayjs from "dayjs";
import "dayjs/locale/zh-cn";
// 导入API服务
import { CommonPopup, withAuth, NTRPEvaluatePopup } from "@/components";
import {
CommonPopup,
withAuth,
NTRPEvaluatePopup,
GameManagePopup,
Comments,
} from "@/components";
import {
EvaluateType,
SceneType,
DisplayConditionType,
} from "@/components/NTRPEvaluatePopup";
import DetailService, { MATCH_STATUS } from "@/services/detailService";
import DetailService, {
MATCH_STATUS,
IsSubstituteSupported,
} from "@/services/detailService";
import * as LoginService from "@/services/loginService";
import OrderService from "@/services/orderService";
import { getCurrentLocation, calculateDistance } from "@/utils/locationUtils";
import { useUserInfo, useUserActions } from "@/store/userStore";
import img from "@/config/images";
import styles from "./style.module.scss";
import "./index.scss";
dayjs.locale("zh-cn");
// 将·作为连接符插入到标签文本之间
function insertDotInTags(tags: string[]) {
if (!tags) return [];
return tags.join("-·-").split("-");
}
function GameTags(props) {
const { userInfo } = props;
const { avatar_url } = userInfo;
const { userInfo, handleViewUserInfo } = props;
const { avatar_url, id } = userInfo;
const tags = [
{
name: "🕙 急招",
@@ -64,7 +76,9 @@ function GameTags(props) {
{/* network image mock */}
<Image
className="detail-page-content-avatar-tags-avatar-image"
mode="aspectFill"
src={avatar_url}
onClick={handleViewUserInfo.bind(null, id)}
/>
</View>
<View className="detail-page-content-avatar-tags-tags">
@@ -96,7 +110,6 @@ function Coursel(props) {
async function getImagesMsg(imageList) {
const latest_list: CourselItemType[] = [];
const sys_info = await Taro.getSystemInfo();
console.log(sys_info, "info");
const max_width = sys_info.screenWidth - 30;
const max_height = 240;
const current_aspect_ratio = max_width / max_height;
@@ -163,14 +176,14 @@ const SharePopup = forwardRef(
},
}));
// function handleShareToWechat() {
// useShareAppMessage(() => {
// return {
// title: '分享',
// path: `/game_pages/detail/index?id=${id}&from=share`,
// }
// })
// }
useShareAppMessage((res) => {
console.log(res, "res");
return {
title: "分享",
imageUrl: "https://img.yzcdn.cn/vant/cat.jpeg",
path: `/game_pages/detail/index?id=${id}&from=share`,
};
});
// function handleShareToWechatMoments() {
// useShareTimeline(() => {
@@ -181,17 +194,19 @@ const SharePopup = forwardRef(
// })
// }
// function handleSaveToLocal() {
// Taro.saveImageToPhotosAlbum({
// filePath: images[0],
// success: () => {
// Taro.showToast({ title: '保存成功', icon: 'success' })
// },
// fail: () => {
// Taro.showToast({ title: '保存失败', icon: 'none' })
// },
// })
// }
function handleSaveToLocal() {
Taro.showToast({ title: "not yet", icon: "error" });
return;
Taro.saveImageToPhotosAlbum({
filePath: "",
success: () => {
Taro.showToast({ title: "保存成功", icon: "success" });
},
fail: () => {
Taro.showToast({ title: "保存失败", icon: "none" });
},
});
}
return (
<CommonPopup
@@ -203,12 +218,28 @@ const SharePopup = forwardRef(
hideFooter
style={{ minHeight: "100px" }}
>
<View catchMove className="share-popup-content">
<View className={styles.shareContainer}>
<View catchMove className={styles.title}>
</View>
<View className={styles.shareItems}>
<Button
className={classnames(styles.button, styles.share)}
openType="share"
>
</Button>
<Button
className={classnames(styles.button, styles.save)}
onClick={handleSaveToLocal}
>
</Button>
</View>
</View>
</CommonPopup>
);
},
}
);
function navto(url) {
@@ -217,34 +248,74 @@ function navto(url) {
});
}
function toast(message) {
Taro.showToast({ title: message, icon: "none" });
}
function isFull(counts) {
const {
max_players,
current_players,
max_substitute_players,
current_substitute_count,
is_substitute_supported,
} = counts;
if (max_players === current_players) {
return true;
} else if (is_substitute_supported === IsSubstituteSupported.SUPPORT) {
return max_substitute_players === current_substitute_count;
}
return false;
}
// 底部操作栏
function StickyButton(props) {
const { handleShare, handleJoinGame, detail } = props;
const {
handleShare,
handleJoinGame,
detail,
onStatusChange,
handleAddComment,
getCommentCount,
} = props;
const [commentCount, setCommentCount] = useState(0);
const ntrpRef = useRef(null);
// const userInfo = useUserInfo();
// const { id } = userInfo;
const {
id,
publisher_id,
match_status,
price,
user_action_status,
match_status,
start_time,
end_time,
is_organizer,
} = detail || {};
const gameManageRef = useRef();
function handleSelfEvaluate() {
// TODO: 打开自评弹窗
ntrpRef?.current?.show();
}
useEffect(() => {
getCommentCount?.((count) => {
setCommentCount(count);
});
}, [getCommentCount]);
function generateTextAndAction(
user_action_status: null | { [key: string]: boolean },
): undefined | { text: string | React.FC; action: () => void } {
user_action_status: null | { [key: string]: boolean }
):
| undefined
| { text: string | React.FC; action?: () => void; available?: boolean } {
if (!user_action_status) {
return;
}
const displayPrice = is_organizer ? 0 : price;
// user_action_status.can_assess = true;
user_action_status.can_join = true;
// user_action_status.can_join = false;
// console.log(user_action_status, "user_action");
const {
can_assess,
can_join,
@@ -253,54 +324,62 @@ function StickyButton(props) {
is_substituting,
waiting_start,
} = user_action_status || {};
if (
Object.values(user_action_status).every((value) => !value) &&
dayjs(end_time).isBefore(dayjs())
) {
if (MATCH_STATUS.CANCELED === match_status) {
return {
text: "球局已结束,查看其他球局",
action: navto.bind(null, "/game_pages/list/index"),
text: "活动已取消",
available: false,
// action: () => toast("活动已取消"),
};
} else if (dayjs(end_time).isBefore(dayjs())) {
return {
text: "活动已结束",
available: false,
// action: () => toast("活动已结束"),
};
} else if (dayjs(start_time).isBefore(dayjs())) {
return {
text: "活动已开始",
available: false,
// action: () => toast("活动已开始"),
};
} else if (isFull(detail)) {
return {
text: "活动已满员",
available: false,
// action: () => toast("活动已满员"),
};
}
if (waiting_start) {
return {
text: "等待开始, 查看更多球局",
action: navto.bind(null, "/game_pages/list/index"),
text: () => <Text>¥{displayPrice} </Text>,
action: () => toast("已加入"),
};
} else if (is_substituting) {
return {
text: "候补中,查看其他球局",
action: navto.bind(null, "/game_pages/list/index"),
text: () => <Text>¥{displayPrice} </Text>,
action: () => toast("已加入候补"),
};
} else if (can_pay) {
return {
text: "继续支付",
text: () => <Text>¥{price} </Text>,
action: async () => {
const res = await OrderService.getUnpaidOrder(id);
if (res.code === 0) {
Taro.navigateTo({
url: `/order_pages/orderDetail/index?id=${res.data.order_info.order_id}`,
});
navto(
`/order_pages/orderDetail/index?id=${res.data.order_info.order_id}`
);
}
},
};
} else if (can_substitute) {
return {
text: "立即候补",
text: () => <Text>¥{displayPrice} </Text>,
action: handleJoinGame,
};
} else if (can_join) {
return {
text: () => {
return (
<>
<Text>🎾</Text>
<Text></Text>
<View className="game-price">
<Text>¥ {price}</Text>
</View>
</>
);
return <Text>¥{displayPrice} </Text>;
},
action: handleJoinGame,
};
@@ -312,8 +391,9 @@ function StickyButton(props) {
types={[EvaluateType.EDIT, EvaluateType.EVALUATE]}
scene={SceneType.DETAIL}
displayCondition={DisplayConditionType.AUTO}
showGuide={false}
>
<Text>NTRP自评</Text>
<Text>¥{displayPrice} </Text>
</NTRPEvaluatePopup>
),
action: handleSelfEvaluate,
@@ -321,7 +401,7 @@ function StickyButton(props) {
}
return {
text: "球局无法加入",
action: () => {},
available: false,
};
}
@@ -329,7 +409,11 @@ function StickyButton(props) {
return "";
}
const { text, action } = generateTextAndAction(user_action_status)!;
const {
text,
available = true,
action = () => {},
} = generateTextAndAction(user_action_status)!;
let ActionText: React.FC | string = text;
@@ -339,36 +423,61 @@ function StickyButton(props) {
};
}
// const role = Number(publisher_id) === id ? "ownner" : "visitor";
return (
<View className="sticky-bottom-bar">
<View className="sticky-bottom-bar-share-and-comment">
<View className="sticky-bottom-bar-share" onClick={handleShare}>
<Image
className="sticky-bottom-bar-share-icon"
src={img.ICON_DETAIL_SHARE}
/>
<Text className="sticky-bottom-bar-share-text"></Text>
<>
<View className="sticky-bottom-bar">
<View className="sticky-bottom-bar-share-and-comment">
<View className="sticky-bottom-bar-share" onClick={handleShare}>
<Image
className="sticky-bottom-bar-share-icon"
src={img.ICON_DETAIL_SHARE}
/>
<Text className="sticky-bottom-bar-share-text"></Text>
</View>
<View className="sticky-bottom-bar-share-and-comment-separator" />
<View
className="sticky-bottom-bar-comment"
onClick={() => {
// Taro.showToast({ title: "To be continued", icon: "none" });
handleAddComment();
}}
>
<Image
className="sticky-bottom-bar-comment-icon"
src={img.ICON_DETAIL_COMMENT_DARK}
/>
<Text className="sticky-bottom-bar-comment-text">
{commentCount > 0 ? commentCount : "评论"}
</Text>
</View>
</View>
<View className="sticky-bottom-bar-share-and-comment-separator" />
<View
className="sticky-bottom-bar-comment"
onClick={() => {
Taro.showToast({ title: "To be continued", icon: "none" });
}}
className={classnames(
"detail-main-action",
available ? "" : "disabled"
)}
>
<Image
className="sticky-bottom-bar-comment-icon"
src={img.ICON_DETAIL_COMMENT_DARK}
/>
<Text className="sticky-bottom-bar-comment-text">32</Text>
<View
style={is_organizer ? {} : { margin: "auto" }}
className="sticky-bottom-bar-join-game"
onClick={action}
>
<ActionText />
</View>
{is_organizer && (
<View
className="game_manage"
onClick={() => {
gameManageRef.current.show(detail, onStatusChange);
}}
>
</View>
)}
</View>
</View>
<View className="sticky-bottom-bar-join-game" onClick={action}>
<ActionText />
</View>
</View>
<GameManagePopup ref={gameManageRef} />
</>
);
}
@@ -382,10 +491,10 @@ function GameInfo(props) {
location_name,
start_time,
end_time,
weather = [{}],
weather,
} = detail || {};
const [{ iconDay, tempMax, tempMin }] = weather;
const [{ iconDay, tempMax, tempMin }] = weather || [{}];
const openMap = () => {
Taro.openLocation({
@@ -539,7 +648,8 @@ function VenueInfo(props) {
function previewImage(current_url) {
Taro.previewImage({
current: current_url,
urls: venue_image_list.map((c) => c.url),
urls:
venue_image_list?.length > 0 ? venue_image_list.map((c) => c.url) : [],
});
}
return (
@@ -588,19 +698,21 @@ function VenueInfo(props) {
<View className="venue-screenshot-title"></View>
<ScrollView scrollY className="venue-screenshot-scroll-view">
<View className="venue-screenshot-image-list">
{venue_image_list.map((item) => {
return (
<View
className="venue-screenshot-image-item"
onClick={previewImage.bind(null, item.url)}
>
<Image
className="venue-screenshot-image-item-image"
src={item.url}
/>
</View>
);
})}
{venue_image_list?.length > 0 &&
venue_image_list.map((item) => {
return (
<View
className="venue-screenshot-image-item"
onClick={previewImage.bind(null, item.url)}
>
<Image
className="venue-screenshot-image-item-image"
mode="aspectFill"
src={item.url}
/>
</View>
);
})}
</View>
</ScrollView>
</CommonPopup>
@@ -611,8 +723,8 @@ function VenueInfo(props) {
function genNTRPRequirementText(min, max) {
if (min && max && min !== max) {
return `${min} - ${max} 之间`;
} else if (max === 1) {
return "没有要求";
} else if (max === "1") {
return "要求";
} else if (max) {
return `${max} 以上`;
}
@@ -661,7 +773,7 @@ function GamePlayAndRequirement(props) {
// 参与者
function Participants(props) {
const { detail = {}, handleJoinGame } = props;
const { detail = {}, handleJoinGame, handleViewUserInfo } = props;
const participants = detail.participants || [];
const {
participant_count,
@@ -672,10 +784,9 @@ function Participants(props) {
user_action_status;
const showApplicationEntry =
[can_pay, can_substitute, is_substituting, waiting_start].every(
(item) => !item,
(item) => !item
) && can_join;
const leftCount = max_participants - participant_count;
const organizer_id = Number(detail.publisher_id);
return (
<View className="detail-page-content-participants">
<View className="participants-title">
@@ -691,7 +802,6 @@ function Participants(props) {
className="participants-list-application"
onClick={() => {
handleJoinGame();
// Taro.showToast({ title: "To be continued", icon: "none" });
}}
>
<Image
@@ -708,11 +818,14 @@ function Participants(props) {
<View
className="participants-list-scroll-content"
style={{
width: `${participants.length * 103 + (participants.length - 1) * 8}px`,
width: `${
participants.length * 103 + (participants.length - 1) * 8
}px`,
}}
>
{participants.map((participant) => {
const {
is_organizer,
user: {
avatar_url,
nickname,
@@ -720,13 +833,17 @@ function Participants(props) {
id: participant_user_id,
},
} = participant;
const role =
participant_user_id === organizer_id ? "组织者" : "参与者";
const role = is_organizer ? "组织者" : "参与者";
return (
<View key={participant.id} className="participants-list-item">
<Avatar
<Image
className="participants-list-item-avatar"
mode="aspectFill"
src={avatar_url}
onClick={handleViewUserInfo.bind(
null,
participant_user_id
)}
/>
<Text className="participants-list-item-name">
{nickname || "未知"}
@@ -750,7 +867,7 @@ function Participants(props) {
function SupplementalNotes(props) {
const {
detail: { description, description_tag = [] },
detail: { description, description_tag },
} = props;
return (
<View className="detail-page-content-supplemental-notes">
@@ -760,7 +877,7 @@ function SupplementalNotes(props) {
<View className="supplemental-notes-content">
{/* supplemental notes tags */}
<View className="supplemental-notes-content-tags">
{insertDotInTags(description_tag).map((tag, index) => (
{insertDotInTags(description_tag || []).map((tag, index) => (
<View key={index} className="supplemental-notes-content-tags-tag">
<Text>{tag}</Text>
</View>
@@ -808,7 +925,12 @@ function genRecommendGames(games, location, avatar) {
avatar,
applications: max_players,
checkedApplications: current_players,
levelRequirements: `NTRP ${genNTRPRequirementText(skill_level_min, skill_level_max)}`,
levelRequirements:
skill_level_max !== skill_level_min
? `${skill_level_min || "-"}${skill_level_max || "-"}`
: skill_level_min === "1"
? "无要求"
: `${skill_level_min}以上`,
playType: play_type,
};
});
@@ -819,6 +941,8 @@ function OrganizerInfo(props) {
userInfo,
currentLocation: location,
onUpdateUserInfo = () => {},
handleViewUserInfo,
handleAddComment,
} = props;
const {
id,
@@ -855,6 +979,10 @@ function OrganizerInfo(props) {
}
};
function handleViewGame(gameId) {
navto(`/game_pages/detail/index?id=${gameId}&from=current`);
}
return (
<View className="detail-page-content-organizer-recommend-games">
{/* orgnizer title */}
@@ -863,7 +991,12 @@ function OrganizerInfo(props) {
</View>
{/* organizer avatar and name */}
<View className="organizer-avatar-name">
<Avatar className="organizer-avatar-name-avatar" src={avatar_url} />
<Image
className="organizer-avatar-name-avatar"
src={avatar_url}
mode="aspectFill"
onClick={handleViewUserInfo.bind(null, id)}
/>
<View className="organizer-avatar-name-message">
<Text className="organizer-avatar-name-message-name">{nickname}</Text>
<View className="organizer-avatar-name-message-stats">
@@ -893,7 +1026,10 @@ function OrganizerInfo(props) {
)}
</View>
)}
<View className="organizer-actions-comment">
<View
className="organizer-actions-comment"
onClick={() => handleAddComment()}
>
<Image
className="organizer-actions-comment-icon"
src={img.ICON_DETAIL_COMMENT}
@@ -902,64 +1038,79 @@ function OrganizerInfo(props) {
</View>
</View>
{/* recommend games by organizer */}
<View className="organizer-recommend-games">
<View className="organizer-recommend-games-title" onClick={() => {}}>
<Text>TA的更多活动</Text>
<Image
className="organizer-recommend-games-title-arrow"
src={img.ICON_DETAIL_ARROW_RIGHT}
/>
</View>
<ScrollView className="recommend-games-list" scrollX>
<View className="recommend-games-list-content">
{recommendGames.map((game, index) => (
<View key={index} className="recommend-games-list-item">
{/* game title */}
<View className="recommend-games-list-item-title">
<Text>{game.title}</Text>
<Image
className="recommend-games-list-item-title-arrow"
src={img.ICON_DETAIL_ARROW_RIGHT}
/>
</View>
{/* game time and range */}
<View className="recommend-games-list-item-time-range">
<Text>{game.time}</Text>
<Text>{game.timeLength}</Text>
</View>
{/* game location、vunue、distance */}
<View className="recommend-games-list-item-location-venue-distance">
<Text>{game.venue}</Text>
<Text>·</Text>
<Text>{game.venueType}</Text>
<Text>·</Text>
<Text>{game.distance}</Text>
</View>
{/* organizer avatar、applications、level requirements、play type */}
<View className="recommend-games-list-item-addon">
<Avatar
className="recommend-games-list-item-addon-avatar"
src={game.avatar}
/>
<View className="recommend-games-list-item-addon-message">
<View className="recommend-games-list-item-addon-message-applications">
<Text>
{game.checkedApplications}/{game.applications}
</Text>
</View>
<View className="recommend-games-list-item-addon-message-level-requirements">
<Text>{game.levelRequirements}</Text>
</View>
<View className="recommend-games-list-item-addon-message-play-type">
<Text>{game.playType}</Text>
{recommendGames.length > 0 && (
<View className="organizer-recommend-games">
<View
className="organizer-recommend-games-title"
onClick={handleViewUserInfo.bind(null, id)}
>
<Text>TA的更多活动</Text>
<Image
className="organizer-recommend-games-title-arrow"
src={img.ICON_DETAIL_ARROW_RIGHT}
/>
</View>
<ScrollView className="recommend-games-list" scrollX>
<View className="recommend-games-list-content">
{recommendGames.map((game, index) => (
<View
key={index}
className="recommend-games-list-item"
onClick={handleViewGame.bind(null, game.id)}
>
{/* game title */}
<View className="recommend-games-list-item-title">
<Text>{game.title}</Text>
<Image
className="recommend-games-list-item-title-arrow"
src={img.ICON_DETAIL_ARROW_RIGHT}
/>
</View>
{/* game time and range */}
<View className="recommend-games-list-item-time-range">
<Text>{game.time}</Text>
<Text>{game.timeLength}</Text>
</View>
{/* game location、vunue、distance */}
<View className="recommend-games-list-item-location-venue-distance">
<Text>{game.venue}</Text>
<Text>·</Text>
<Text>{game.venueType}</Text>
<Text>·</Text>
<Text>{game.distance}</Text>
</View>
{/* organizer avatar、applications、level requirements、play type */}
<View className="recommend-games-list-item-addon">
<Image
className="recommend-games-list-item-addon-avatar"
mode="aspectFill"
src={game.avatar}
onClick={(e) => {
e.stopPropagation();
handleViewUserInfo(id);
}}
/>
<View className="recommend-games-list-item-addon-message">
<View className="recommend-games-list-item-addon-message-applications">
<Text>
{game.checkedApplications}/
{game.applications}
</Text>
</View>
<View className="recommend-games-list-item-addon-message-level-requirements">
<Text>{game.levelRequirements}</Text>
</View>
<View className="recommend-games-list-item-addon-message-play-type">
<Text>{game.playType}</Text>
</View>
</View>
</View>
</View>
</View>
))}
</View>
</ScrollView>
</View>
))}
</View>
</ScrollView>
</View>
)}
</View>
);
}
@@ -973,8 +1124,12 @@ function Index() {
const { id, from } = params;
const [userInfo, setUserInfo] = useState({}); // 组织者的userInfo
const { fetchUserInfo } = useUserActions(); // 获取登录用户的userInfo
const myInfo = useUserInfo();
const isMyOwn = userInfo.id === myInfo.id;
const sharePopupRef = useRef<any>(null);
const commentRef = useRef();
useDidShow(async () => {
await updateLocation();
@@ -1003,10 +1158,16 @@ function Index() {
const fetchDetail = async () => {
if (!id) return;
const res = await DetailService.getDetail(Number(id));
if (res.code === 0) {
setDetail(res.data);
fetchUserInfoById(res.data.publisher_id);
try {
const res = await DetailService.getDetail(Number(id));
if (res.code === 0) {
setDetail(res.data);
fetchUserInfoById(res.data.publisher_id);
}
} catch (e) {
if (e.message === "球局不存在") {
handleBack();
}
}
};
@@ -1017,7 +1178,6 @@ function Index() {
async function fetchUserInfoById(user_id) {
const userDetailInfo = await LoginService.getUserInfoById(user_id);
if (userDetailInfo.code === 0) {
// console.log(userDetailInfo.data);
setUserInfo(userDetailInfo.data);
}
}
@@ -1026,12 +1186,24 @@ function Index() {
sharePopupRef.current.show();
}
const handleJoinGame = () => {
Taro.navigateTo({
url: `/order_pages/orderDetail/index?gameId=${id}`,
});
const handleJoinGame = async () => {
if (isMyOwn) {
const res = await DetailService.organizerJoin(Number(id));
if (res.code === 0) {
toast("加入成功");
fetchDetail();
}
return;
}
navto(`/order_pages/orderDetail/index?gameId=${id}`);
};
function onStatusChange(result) {
if (result) {
fetchDetail();
}
}
function handleBack() {
const pages = Taro.getCurrentPages();
if (pages.length <= 1) {
@@ -1043,7 +1215,10 @@ function Index() {
}
}
console.log("detail", detail);
function handleViewUserInfo(userId) {
navto(`/user_pages/other/index?userid=${userId}`);
}
const backgroundImage = detail?.image_list?.[0]
? { backgroundImage: `url(${detail?.image_list?.[0]})` }
: {};
@@ -1073,7 +1248,11 @@ function Index() {
{/* content */}
<View className="detail-page-content">
{/* avatar and tags */}
<GameTags detail={detail} userInfo={userInfo} />
<GameTags
detail={detail}
userInfo={userInfo}
handleViewUserInfo={handleViewUserInfo}
/>
{/* title */}
<View className="detail-page-content-title">
<Text className="detail-page-content-title-text">{detail.title}</Text>
@@ -1085,7 +1264,11 @@ function Index() {
{/* gameplay requirements */}
<GamePlayAndRequirement detail={detail} />
{/* participants */}
<Participants detail={detail} handleJoinGame={handleJoinGame} />
<Participants
detail={detail}
handleJoinGame={handleJoinGame}
handleViewUserInfo={handleViewUserInfo}
/>
{/* supplemental notes */}
<SupplementalNotes detail={detail} />
{/* organizer and recommend games by organizer */}
@@ -1094,12 +1277,28 @@ function Index() {
userInfo={userInfo}
currentLocation={currentLocation}
onUpdateUserInfo={onUpdateUserInfo}
handleViewUserInfo={handleViewUserInfo}
handleAddComment={() => {
commentRef.current && commentRef.current.addComment();
}}
/>
<Comments
ref={commentRef}
game_id={Number(id)}
publisher_id={Number(detail.publisher_id)}
/>
{/* sticky bottom action bar */}
<StickyButton
handleShare={handleShare}
handleJoinGame={handleJoinGame}
detail={detail}
onStatusChange={onStatusChange}
handleAddComment={() => {
commentRef.current && commentRef.current.addComment();
}}
getCommentCount={
commentRef.current && commentRef.current.getCommentCount
}
/>
{/* share popup */}
<SharePopup

View File

@@ -0,0 +1,24 @@
.shareContainer {
.title {
padding: 20px;
color: #000;
text-align: center;
font-family: "PingFang SC";
font-size: 18px;
font-style: normal;
font-weight: 600;
line-height: 28px;
}
.shareItems {
display: flex;
align-items: center;
justify-content: space-between;
padding-bottom: 60px;
.button {
width: 140px;
height: 40px;
}
}
}

View File

@@ -13,6 +13,7 @@ import DistanceQuickFilter from "@/components/DistanceQuickFilter";
import { withAuth } from "@/components";
import { updateUserLocation } from "@/services/userService";
// import ShareCardCanvas from "@/components/ShareCardCanvas";
import { useDictionaryStore } from "@/store/dictionaryStore";
const ListPage = () => {
@@ -163,11 +164,11 @@ const ListPage = () => {
Taro.stopPullDownRefresh();
// 显示刷新成功提示
Taro.showToast({
title: "刷新成功",
icon: "success",
duration: 1000,
});
// Taro.showToast({
// title: "刷新成功",
// icon: "success",
// duration: 1000,
// });
} catch (error) {
// 刷新失败时也停止动画
Taro.stopPullDownRefresh();
@@ -269,6 +270,19 @@ const ListPage = () => {
// imageUrl: shareImagePath || ''
// }
// })
// 初始化字典数据
const initDictionaryData = async () => {
try {
const { fetchDictionary } = useDictionaryStore.getState();
await fetchDictionary();
} catch (error) {
console.error("初始化字典数据失败:", error);
}
}
useEffect(() => {
initDictionaryData()
}, []);
return (
<>