Files
mini-programs/src/game_pages/detail/index.tsx
张成 a67383d12c 1
2025-12-04 10:40:58 +08:00

310 lines
9.1 KiB
TypeScript

import { useState, useEffect, useRef } from "react";
import { View, Text, Image, ScrollView } from "@tarojs/components";
import Taro, { useRouter, useDidShow } from "@tarojs/taro";
import classnames from "classnames";
import { throttle } from "@tarojs/runtime";
// 导入API服务
import { withAuth, Comments } from "@/components";
import DetailService from "@/services/detailService";
import * as LoginService from "@/services/loginService";
import { getCurrentLocation } from "@/utils/locationUtils";
import { useUserInfo, useUserActions } from "@/store/userStore";
import { useGlobalState } from "@/store/global";
import { waitForAuthInit } from "@/utils/authInit";
import { requireLoginWithPhone } from "@/utils/helper";
import GameTags from "./components/GameTags";
import Carousel from "./components/Carousel";
import StickyBottom from "./components/StickyBottom";
import GameInfo from "./components/GameInfo";
import VenueInfo from "./components/VenueInfo";
import GamePlayAndRequirement from "./components/GamePlayAndReq";
import Participants from "./components/Participants";
import SupplementalNotes from "./components/SupplementalNotes";
import OrganizerInfo from "./components/OrganizerInfo";
import SharePopup from "./components/SharePopup";
import EmptyState from "./components/EmptyState";
import { navto, toast } from "@/utils/helper";
import ArrowLeft from "@/static/detail/icon-arrow-left.svg";
// import Logo from "@/static/detail/icon-logo-go.svg";
import styles from "./index.module.scss";
function Index() {
const [detail, setDetail] = useState<any>({});
const [showEmptyState, setShowEmptyState] = useState(false);
const { params } = useRouter();
const [currentLocation, setCurrentLocation] = useState<[number, number]>([
0, 0,
]);
const { id, from, message_id } = params;
const [userInfo, setUserInfo] = useState({}); // 组织者的userInfo
const { fetchUserInfo } = useUserActions(); // 获取登录用户的userInfo
const myInfo = useUserInfo();
const { statusNavbarHeightInfo } = useGlobalState();
const { statusBarHeight, navBarHeight, totalHeight } = statusNavbarHeightInfo;
const isMyOwn = userInfo.id === myInfo.id;
const sharePopupRef = useRef<any>(null);
const commentRef = useRef();
useEffect(() => {
const init = async () => {
updateLocation();
// 先等待静默登录完成
await waitForAuthInit();
// 然后再获取用户信息
await fetchUserInfo();
};
init();
}, []);
useDidShow(() => {
// await updateLocation();
// await fetchUserInfo();
if (id) {
Taro.showLoading();
fetchDetail();
}
});
const updateLocation = async () => {
try {
const { address, ...location } = await getCurrentLocation();
setCurrentLocation([location.latitude, location.longitude]);
// 使用 userStore 中的统一位置更新方法
// await updateUserInfo({ latitude: location.latitude, longitude: location.longitude })
await DetailService.updateLocation({
latitude: Number(location.latitude),
longitude: Number(location.longitude),
});
// 位置更新后,重新获取详情页数据(因为距离等信息可能发生变化)
await fetchDetail();
if (from === "publish") {
handleShare(true);
}
} catch (error) {
console.error("用户位置更新失败", error);
}
};
const fetchDetail = async () => {
if (!id) return;
try {
const res = await DetailService.getDetail(Number(id));
if (res.code === 0) {
setDetail(res.data);
fetchUserInfoById(res.data.publisher_id);
setShowEmptyState(false);
}
} catch (e) {
if (e.message === "球局不存在") {
setShowEmptyState(true);
}
}
Taro.hideLoading();
};
const onUpdateUserInfo = () => {
fetchUserInfoById(detail.publisher_id);
};
async function fetchUserInfoById(user_id) {
const userDetailInfo = await LoginService.getUserInfoById(user_id);
if (userDetailInfo.code === 0) {
setUserInfo(userDetailInfo.data);
}
}
function handleShare(flag = false) {
sharePopupRef.current.show(flag);
}
const handleJoinGame = async () => {
// 检查登录状态和手机号
if (!requireLoginWithPhone()) {
return; // 未登录或未绑定手机号,已跳转到登录页
}
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) {
Taro.redirectTo({
url: "/main_pages/index",
});
} else {
Taro.navigateBack();
}
}
function handleViewUserInfo(userId) {
navto(
isMyOwn
? "/user_pages/myself/index"
: `/user_pages/other/index?userid=${userId}`
);
}
const backgroundImage = detail?.image_list?.[0]
? { opacity: 1, backgroundImage: `url(${detail?.image_list?.[0]})` }
: { opacity: 0 };
const [glass, setGlass] = useState(false);
const onScroll = throttle((e) => {
const top = e.detail.scrollTop;
setGlass(top > 20);
}, 16);
// 如果显示空状态,渲染空状态页面
if (showEmptyState) {
return (
<EmptyState
onGoToOtherGames={() => {
Taro.switchTab({
url: '/main_pages/index',
});
}}
onGoToHome={handleBack}
/>
);
}
return (
<ScrollView
className={styles["detail-page"]}
scrollY
onScroll={onScroll}
onScrollToUpper={() => {
setGlass(false);
}}
enhanced
showScrollbar={false}
>
{/* custom navbar */}
<View
className={classnames(
styles["custom-navbar"],
glass ? styles.glass : ""
)}
style={{
height: `${totalHeight}px`,
paddingTop: `${statusBarHeight}px`,
}}
>
<View className={styles["detail-navigator"]}>
<View
className={styles["detail-navigator-back"]}
onClick={handleBack}
>
<Image
className={styles["detail-navigator-back-icon"]}
src={ArrowLeft}
/>
</View>
{/* <View className={styles["detail-navigator-icon"]}>
<Image
className={styles["detail-navigator-logo-icon"]}
src={Logo}
/>
</View> */}
</View>
</View>
<View className={styles["detail-page-bg"]} style={backgroundImage} />
<View style={{ paddingTop: `${totalHeight}px` }}> </View>
{/* swiper */}
<Carousel detail={detail} />
{/* content */}
<View className={styles["detail-page-content"]}>
{/* avatar and tags */}
<GameTags
detail={detail}
userInfo={userInfo}
handleViewUserInfo={handleViewUserInfo}
/>
{/* title */}
<View className={styles["detail-page-content-title"]}>
<Text className={styles["detail-page-content-title-text"]}>
{detail.title}
</Text>
</View>
{/* Date and Place and weather */}
<GameInfo detail={detail} currentLocation={currentLocation} />
{/* detail */}
<VenueInfo detail={detail} />
{/* gameplay requirements */}
<GamePlayAndRequirement detail={detail} />
{/* participants */}
<Participants
detail={detail}
handleJoinGame={handleJoinGame}
handleViewUserInfo={handleViewUserInfo}
/>
{/* supplemental notes */}
<SupplementalNotes detail={detail} />
{/* organizer and recommend games by organizer */}
<OrganizerInfo
detail={detail}
userInfo={userInfo}
currentLocation={currentLocation}
onUpdateUserInfo={onUpdateUserInfo}
handleViewUserInfo={handleViewUserInfo}
handleAddComment={() => {
commentRef.current && commentRef.current.addComment();
}}
/>
<Comments
ref={commentRef}
game_id={Number(detail.id)}
message_id={message_id ? Number(message_id) : undefined}
publisher_id={Number(detail.publisher_id)}
/>
{/* sticky bottom action bar */}
<StickyBottom
handleShare={handleShare}
handleJoinGame={handleJoinGame}
detail={detail}
onStatusChange={onStatusChange}
handleAddComment={() => {
commentRef.current && commentRef.current.addComment();
}}
getCommentCount={
commentRef.current && commentRef.current.getCommentCount
}
currentUserInfo={myInfo}
/>
{/* share popup */}
<SharePopup
ref={sharePopupRef}
id={id as string}
from={from as string}
detail={detail}
userInfo={userInfo}
/>
</View>
</ScrollView>
);
}
export default Index;