309 lines
9.2 KiB
TypeScript
309 lines
9.2 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 { navto, toast } from "@/utils/helper";
|
||
import { delay } from "@/utils";
|
||
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 { 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();
|
||
|
||
await delay(1000);
|
||
|
||
if (from === "publish") {
|
||
handleShare(true);
|
||
}
|
||
};
|
||
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),
|
||
});
|
||
|
||
// 位置更新后,重新获取详情页数据(因为距离等信息可能发生变化)
|
||
// 注意:这里不调用 fetchDetail,避免与 useDidShow 中的调用重复
|
||
// 如果需要更新距离信息,可以在 fetchDetail 成功后根据当前位置重新计算
|
||
// if (from === "publish") {
|
||
// handleShare(true);
|
||
// }
|
||
} catch (error) {
|
||
console.error("用户位置更新失败", error);
|
||
}
|
||
};
|
||
|
||
const fetchDetail = async () => {
|
||
if (!id) return;
|
||
|
||
const res = await DetailService.getDetail(Number(id)).catch((e) => {
|
||
// 跳转到空状态页面
|
||
(Taro as any).redirectTo({
|
||
url: "/other_pages/emptyState/index",
|
||
});
|
||
return e;
|
||
});
|
||
|
||
if (res.code === 0) {
|
||
setDetail(res.data);
|
||
fetchUserInfoById(res.data.publisher_id);
|
||
}
|
||
|
||
// 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(
|
||
userId === myInfo.id
|
||
? "/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);
|
||
|
||
const [scrollToTarget, setScrollToTarget] = useState("");
|
||
|
||
return (
|
||
<ScrollView
|
||
className={styles["detail-page"]}
|
||
scrollY
|
||
onScroll={onScroll}
|
||
onScrollToUpper={() => {
|
||
setGlass(false);
|
||
}}
|
||
enhanced
|
||
showScrollbar={false}
|
||
scrollIntoView={scrollToTarget}
|
||
// scroll-with-animation
|
||
>
|
||
{/* 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)}
|
||
onScrollTo={setScrollToTarget}
|
||
/>
|
||
{/* 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;
|