Merge branch master into feature/juguohong/20250816
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '球局详情',
|
||||
navigationStyle: 'custom',
|
||||
enableShareAppMessage: true,
|
||||
})
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
24
src/game_pages/detail/style.module.scss
Normal file
24
src/game_pages/detail/style.module.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 (
|
||||
<>
|
||||
|
||||
Reference in New Issue
Block a user