Merge branch 'feat/liujie'
This commit is contained in:
@@ -9,6 +9,7 @@ export default defineAppConfig({
|
|||||||
"game_pages/search/index", // 搜索页
|
"game_pages/search/index", // 搜索页
|
||||||
"game_pages/searchResult/index", // 搜索结果页面
|
"game_pages/searchResult/index", // 搜索结果页面
|
||||||
"game_pages/detail/index", // 球局详情页
|
"game_pages/detail/index", // 球局详情页
|
||||||
|
"game_pages/sharePoster/index",
|
||||||
],
|
],
|
||||||
subPackages: [
|
subPackages: [
|
||||||
|
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ function CommentItem(props: {
|
|||||||
blink_id,
|
blink_id,
|
||||||
} = props;
|
} = props;
|
||||||
const currentUserInfo = useUserInfo();
|
const currentUserInfo = useUserInfo();
|
||||||
const isGamePublisher = publisher_id === comment.user.id;
|
const isGamePublisher = publisher_id === currentUserInfo.id;
|
||||||
const isCommentPublisher = currentUserInfo.id === comment.user.id;
|
const isCommentPublisher = currentUserInfo.id === comment.user.id;
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
@@ -229,7 +229,7 @@ function CommentItem(props: {
|
|||||||
<Text>{getRelativeDay(comment.create_time)}</Text>
|
<Text>{getRelativeDay(comment.create_time)}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className={styles.location}>
|
<View className={styles.location}>
|
||||||
<Text>上海</Text>
|
<Text>{comment.user.province}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View
|
<View
|
||||||
className={styles.reply}
|
className={styles.reply}
|
||||||
@@ -243,7 +243,7 @@ function CommentItem(props: {
|
|||||||
>
|
>
|
||||||
<Text>回复</Text>
|
<Text>回复</Text>
|
||||||
</View>
|
</View>
|
||||||
{isGamePublisher || isCommentPublisher}
|
{(isGamePublisher || isCommentPublisher) && (
|
||||||
<View
|
<View
|
||||||
className={styles.delete}
|
className={styles.delete}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
@@ -255,6 +255,7 @@ function CommentItem(props: {
|
|||||||
>
|
>
|
||||||
<Text>删除</Text>
|
<Text>删除</Text>
|
||||||
</View>
|
</View>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
{!isReplyComment(comment) &&
|
{!isReplyComment(comment) &&
|
||||||
|
|||||||
34
src/game_pages/detail/components/Carousel/index.module.scss
Normal file
34
src/game_pages/detail/components/Carousel/index.module.scss
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
.detail-swiper-container {
|
||||||
|
height: 270px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 15px 15px 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow-x: scroll;
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
color: transparent; /* 透明色 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-swiper-scroll-container {
|
||||||
|
display: flex;
|
||||||
|
height: 250px;
|
||||||
|
width: auto;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
.detail-swiper-item {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
height: 250px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
&-image {
|
||||||
|
border-radius: 12px;
|
||||||
|
transition: transform 0.5s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
84
src/game_pages/detail/components/Carousel/index.tsx
Normal file
84
src/game_pages/detail/components/Carousel/index.tsx
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import Taro from "@tarojs/taro";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { View, Image } from "@tarojs/components";
|
||||||
|
import styles from "./index.module.scss";
|
||||||
|
|
||||||
|
type CarouselItemType = {
|
||||||
|
url: string;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
};
|
||||||
|
export default function Carousel(props) {
|
||||||
|
const { detail } = props;
|
||||||
|
const [list, setList] = useState<CarouselItemType[]>([]);
|
||||||
|
const [listWidth, setListWidth] = useState(0);
|
||||||
|
const { image_list } = detail;
|
||||||
|
|
||||||
|
async function getImagesMsg(imageList) {
|
||||||
|
const latest_list: CarouselItemType[] = [];
|
||||||
|
const sys_info = await Taro.getSystemInfo();
|
||||||
|
const max_width = sys_info.screenWidth - 30;
|
||||||
|
const max_height = 240;
|
||||||
|
const current_aspect_ratio = max_width / max_height;
|
||||||
|
let container_width = 0;
|
||||||
|
for (const imageUrl of imageList) {
|
||||||
|
const { width, height } = await Taro.getImageInfo({ src: imageUrl });
|
||||||
|
if (width && height) {
|
||||||
|
const aspect_ratio = width / height;
|
||||||
|
const latest_w_h = { width, height };
|
||||||
|
if (aspect_ratio < current_aspect_ratio) {
|
||||||
|
latest_w_h.width = max_height * aspect_ratio;
|
||||||
|
latest_w_h.height = max_height;
|
||||||
|
} else {
|
||||||
|
latest_w_h.width = max_width;
|
||||||
|
latest_w_h.height = max_width / aspect_ratio;
|
||||||
|
}
|
||||||
|
container_width += latest_w_h.width + 12;
|
||||||
|
latest_list.push({
|
||||||
|
url: imageUrl,
|
||||||
|
width: latest_w_h.width,
|
||||||
|
height: latest_w_h.height,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setList(latest_list);
|
||||||
|
setListWidth(container_width);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getImagesMsg(image_list || []);
|
||||||
|
}, [image_list]);
|
||||||
|
|
||||||
|
function previewImage(current_url) {
|
||||||
|
Taro.previewImage({
|
||||||
|
current: current_url,
|
||||||
|
urls: list?.length > 0 ? list.map((c) => c.url) : [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className={styles["detail-swiper-container"]}>
|
||||||
|
<View
|
||||||
|
className={styles["detail-swiper-scroll-container"]}
|
||||||
|
style={{ width: listWidth + "px" }}
|
||||||
|
>
|
||||||
|
{list.map((item, index) => {
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
className={styles["detail-swiper-item"]}
|
||||||
|
key={index}
|
||||||
|
onClick={() => previewImage(item.url)}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={item.url}
|
||||||
|
mode="aspectFill"
|
||||||
|
className={styles["detail-swiper-item-image"]}
|
||||||
|
style={{ width: item.width + "px", height: item.height + "px" }}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
201
src/game_pages/detail/components/GameInfo/index.module.scss
Normal file
201
src/game_pages/detail/components/GameInfo/index.module.scss
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
.detail-page-content-game-info {
|
||||||
|
&-date-weather {
|
||||||
|
padding: 20px 20px 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
// gap: 12px;
|
||||||
|
|
||||||
|
&-calendar-date {
|
||||||
|
width: 60%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
&-calendar {
|
||||||
|
display: flex;
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
border-radius: 12px;
|
||||||
|
// border: 0.5px solid rgba(255, 255, 255, 0.08);
|
||||||
|
background: rgba(255, 255, 255, 0.25);
|
||||||
|
overflow: hidden;
|
||||||
|
color: #fff;
|
||||||
|
background: #536272;
|
||||||
|
|
||||||
|
.month {
|
||||||
|
width: 100%;
|
||||||
|
height: 18px;
|
||||||
|
font-size: 10px;
|
||||||
|
display: flex;
|
||||||
|
padding: 1px auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
// border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
|
background: #7b828b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day {
|
||||||
|
display: flex;
|
||||||
|
width: 48px;
|
||||||
|
height: 30px;
|
||||||
|
// padding-bottom: 6px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
// border: 0.5px solid rgba(255, 255, 255, 0.08);
|
||||||
|
// background: rgba(255, 255, 255, 0.25);
|
||||||
|
// background-color: #536272;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-date {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
gap: 4px;
|
||||||
|
align-self: stretch;
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
.date {
|
||||||
|
color: #fff;
|
||||||
|
font-feature-settings: "liga" off, "clig" off;
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 24px; /* 150% */
|
||||||
|
}
|
||||||
|
|
||||||
|
.venue-time {
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
font-feature-settings: "liga" off, "clig" off;
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 20px; /* 166.667% */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-weather {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
}
|
||||||
|
&-text-temperature {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
font-feature-settings: "liga" off, "clig" off;
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 20px; /* 166.667% */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-place {
|
||||||
|
.location-message {
|
||||||
|
padding: 20px 20px 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 14px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: #4d5865;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
// border: 0.5px solid rgba(255, 255, 255, 0.08);
|
||||||
|
// background: rgba(255, 255, 255, 0.25);
|
||||||
|
|
||||||
|
&-image {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-text {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
gap: 4px;
|
||||||
|
align-self: stretch;
|
||||||
|
|
||||||
|
&-name-distance {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
font-feature-settings: "liga" off, "clig" off;
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 24px; /* 150% */
|
||||||
|
|
||||||
|
&-arrow {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-address {
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
text-align: center;
|
||||||
|
font-feature-settings: "liga" off, "clig" off;
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 20px; /* 166.667% */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.location-map {
|
||||||
|
width: 100%;
|
||||||
|
padding: 20px 20px 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 12px;
|
||||||
|
|
||||||
|
&-map {
|
||||||
|
width: 100%;
|
||||||
|
height: 95px;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
188
src/game_pages/detail/components/GameInfo/index.tsx
Normal file
188
src/game_pages/detail/components/GameInfo/index.tsx
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
import Taro from "@tarojs/taro";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import "dayjs/locale/zh-cn";
|
||||||
|
import { calculateDistance } from "@/utils";
|
||||||
|
import { View, Image, Text, Map } from "@tarojs/components";
|
||||||
|
import img from "@/config/images";
|
||||||
|
import styles from "./index.module.scss";
|
||||||
|
|
||||||
|
dayjs.locale("zh-cn");
|
||||||
|
|
||||||
|
// 球局信息
|
||||||
|
export default function GameInfo(props) {
|
||||||
|
const { detail, currentLocation } = props;
|
||||||
|
const {
|
||||||
|
latitude,
|
||||||
|
longitude,
|
||||||
|
location,
|
||||||
|
location_name,
|
||||||
|
start_time,
|
||||||
|
end_time,
|
||||||
|
weather,
|
||||||
|
} = detail || {};
|
||||||
|
|
||||||
|
const [{ iconDay, tempMax, tempMin }] = weather || [{}];
|
||||||
|
|
||||||
|
const openMap = () => {
|
||||||
|
Taro.openLocation({
|
||||||
|
latitude, // 纬度(必填)
|
||||||
|
longitude, // 经度(必填)
|
||||||
|
name: location_name, // 位置名(可选)
|
||||||
|
address: location, // 地址详情(可选)
|
||||||
|
scale: 15, // 地图缩放级别(1-28)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const [c_latitude, c_longitude] = currentLocation;
|
||||||
|
const distance =
|
||||||
|
latitude && longitude
|
||||||
|
? calculateDistance(c_latitude, c_longitude, latitude, longitude) / 1000
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
const startTime = dayjs(start_time);
|
||||||
|
const endTime = dayjs(end_time);
|
||||||
|
const game_length = endTime.diff(startTime, "minutes") / 60;
|
||||||
|
|
||||||
|
const startMonth = startTime.format("M");
|
||||||
|
const startDay = startTime.format("D");
|
||||||
|
const theDayOfWeek = startTime.format("dddd");
|
||||||
|
const startDate = `${startMonth}月${startDay}日 ${theDayOfWeek}`;
|
||||||
|
const gameRange = `${startTime.format("HH:mm")} - ${endTime.format("HH:mm")}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className={styles["detail-page-content-game-info"]}>
|
||||||
|
{/* Date and Weather */}
|
||||||
|
<View className={styles["detail-page-content-game-info-date-weather"]}>
|
||||||
|
{/* Calendar and Date time */}
|
||||||
|
<View
|
||||||
|
className={
|
||||||
|
styles["detail-page-content-game-info-date-weather-calendar-date"]
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{/* Calendar */}
|
||||||
|
<View
|
||||||
|
className={
|
||||||
|
styles[
|
||||||
|
"detail-page-content-game-info-date-weather-calendar-date-calendar"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<View className={styles.month}>{startMonth}月</View>
|
||||||
|
<View className={styles.day}>{startDay}</View>
|
||||||
|
</View>
|
||||||
|
{/* Date time */}
|
||||||
|
<View
|
||||||
|
className={
|
||||||
|
styles[
|
||||||
|
"detail-page-content-game-info-date-weather-calendar-date-date"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<View className={styles.date}>{startDate}</View>
|
||||||
|
<View className={styles["venue-time"]}>
|
||||||
|
{gameRange} ({game_length}小时)
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
{/* Weather */}
|
||||||
|
<View
|
||||||
|
className={
|
||||||
|
styles["detail-page-content-game-info-date-weather-weather"]
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{/* Weather icon */}
|
||||||
|
<View
|
||||||
|
className={
|
||||||
|
styles["detail-page-content-game-info-date-weather-weather-icon"]
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{/*<Image className="weather-icon" src={img.ICON_WEATHER_SUN} />*/}
|
||||||
|
<i className={`qi-${iconDay}`}></i>
|
||||||
|
</View>
|
||||||
|
{/* Weather text and temperature */}
|
||||||
|
<View
|
||||||
|
className={
|
||||||
|
styles[
|
||||||
|
"detail-page-content-game-info-date-weather-weather-text-temperature"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{tempMin && tempMax && (
|
||||||
|
<Text>
|
||||||
|
{tempMin}℃ - {tempMax}℃
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
{/* Place */}
|
||||||
|
<View className={styles["detail-page-content-game-info-place"]}>
|
||||||
|
{/* venue location message */}
|
||||||
|
<View className={styles["location-message"]}>
|
||||||
|
{/* location icon */}
|
||||||
|
<View className={styles["location-message-icon"]}>
|
||||||
|
<Image
|
||||||
|
className={styles["location-message-icon-image"]}
|
||||||
|
src={img.ICON_DETAIL_MAP}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
{/* location message */}
|
||||||
|
<View className={styles["location-message-text"]}>
|
||||||
|
{/* venue name and distance */}
|
||||||
|
{distance ? (
|
||||||
|
<View
|
||||||
|
className={styles["location-message-text-name-distance"]}
|
||||||
|
onClick={openMap}
|
||||||
|
>
|
||||||
|
<Text>{location_name || "-"}</Text>
|
||||||
|
<Text>·</Text>
|
||||||
|
<Text>{distance.toFixed(1)}km</Text>
|
||||||
|
<Image
|
||||||
|
className={
|
||||||
|
styles["location-message-text-name-distance-arrow"]
|
||||||
|
}
|
||||||
|
src={img.ICON_DETAIL_ARROW_RIGHT}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
|
{/* venue address */}
|
||||||
|
<View className={styles["location-message-text-address"]}>
|
||||||
|
<Text>{location || "-"}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
{/* venue map */}
|
||||||
|
<View className={styles["location-map"]}>
|
||||||
|
{longitude && latitude && (
|
||||||
|
<Map
|
||||||
|
className={styles["location-map-map"]}
|
||||||
|
longitude={c_longitude}
|
||||||
|
latitude={c_latitude}
|
||||||
|
markers={[
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
latitude,
|
||||||
|
longitude,
|
||||||
|
iconPath: require("@/static/detail/icon-stark.svg"),
|
||||||
|
width: 16,
|
||||||
|
height: 16,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
includePoints={[
|
||||||
|
{ latitude, longitude },
|
||||||
|
{ latitude: c_latitude, longitude: c_longitude },
|
||||||
|
]}
|
||||||
|
includePadding={{ left: 50, right: 50, top: 50, bottom: 50 }}
|
||||||
|
onError={() => {}}
|
||||||
|
// hide business msg
|
||||||
|
showLocation
|
||||||
|
theme="dark"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
.detail-page-content-gameplay-requirements {
|
||||||
|
padding: 24px 15px 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.gameplay-requirements-title {
|
||||||
|
overflow: hidden;
|
||||||
|
color: #fff;
|
||||||
|
height: 24px;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
font-feature-settings: "liga" off, "clig" off;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 24px; /* 150% */
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gameplay-requirements {
|
||||||
|
padding: 12px 0 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
align-self: stretch;
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
font-feature-settings: "liga" off, "clig" off;
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 24px; /* 171.429% */
|
||||||
|
}
|
||||||
|
|
||||||
|
&-desc {
|
||||||
|
color: #fff;
|
||||||
|
font-feature-settings: "liga" off, "clig" off;
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
font-size: 15px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 24px; /* 160% */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
46
src/game_pages/detail/components/GamePlayAndReq/index.tsx
Normal file
46
src/game_pages/detail/components/GamePlayAndReq/index.tsx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { View, Text } from "@tarojs/components";
|
||||||
|
import { genNTRPRequirementText } from "../../utils/helper";
|
||||||
|
import styles from "./index.module.scss";
|
||||||
|
|
||||||
|
// 玩法要求
|
||||||
|
export default function GamePlayAndRequirement(props) {
|
||||||
|
const {
|
||||||
|
detail: { skill_level_min, skill_level_max, play_type, game_type },
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const requirements = [
|
||||||
|
{
|
||||||
|
title: "NTRP水平要求",
|
||||||
|
desc: genNTRPRequirementText(skill_level_min, skill_level_max),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "活动玩法",
|
||||||
|
desc: play_type || "-",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "人员构成",
|
||||||
|
desc: game_type || "-",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return (
|
||||||
|
<View className={styles["detail-page-content-gameplay-requirements"]}>
|
||||||
|
{/* title */}
|
||||||
|
<View className={styles["gameplay-requirements-title"]}>
|
||||||
|
<Text>玩法要求</Text>
|
||||||
|
</View>
|
||||||
|
{/* requirements */}
|
||||||
|
<View className={styles["gameplay-requirements"]}>
|
||||||
|
{requirements.map((item, index) => (
|
||||||
|
<View key={index} className={styles["gameplay-requirements-item"]}>
|
||||||
|
<Text className={styles["gameplay-requirements-item-title"]}>
|
||||||
|
{item.title}
|
||||||
|
</Text>
|
||||||
|
<Text className={styles["gameplay-requirements-item-desc"]}>
|
||||||
|
{item.desc}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
44
src/game_pages/detail/components/GameTags/index.module.scss
Normal file
44
src/game_pages/detail/components/GameTags/index.module.scss
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
.detail-page-content-avatar-tags {
|
||||||
|
padding: 20px 20px 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
&-avatar {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
border-radius: 50%;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&-image {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-tags {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
&-tag {
|
||||||
|
display: flex;
|
||||||
|
height: 28px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 0.5px solid rgba(255, 255, 255, 0.08);
|
||||||
|
background: rgba(255, 255, 255, 0.25);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 20px;
|
||||||
|
letter-spacing: -0.23px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
49
src/game_pages/detail/components/GameTags/index.tsx
Normal file
49
src/game_pages/detail/components/GameTags/index.tsx
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import { View, Text, Image } from "@tarojs/components";
|
||||||
|
import styles from "./index.module.scss";
|
||||||
|
|
||||||
|
export default function GameTags(props) {
|
||||||
|
const { userInfo, handleViewUserInfo } = props;
|
||||||
|
const { avatar_url, id } = userInfo;
|
||||||
|
const tags = [
|
||||||
|
// {
|
||||||
|
// name: "🕙 急招",
|
||||||
|
// icon: "",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: "🔥 本周热门",
|
||||||
|
// icon: "",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: "🎉 新活动",
|
||||||
|
// icon: "",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: "官方组织",
|
||||||
|
// icon: "",
|
||||||
|
// },
|
||||||
|
];
|
||||||
|
return (
|
||||||
|
<View className={styles["detail-page-content-avatar-tags"]}>
|
||||||
|
<View className={styles["detail-page-content-avatar-tags-avatar"]}>
|
||||||
|
{/* network image mock */}
|
||||||
|
<Image
|
||||||
|
className={styles["detail-page-content-avatar-tags-avatar-image"]}
|
||||||
|
mode="aspectFill"
|
||||||
|
src={avatar_url}
|
||||||
|
onClick={handleViewUserInfo.bind(null, id)}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View className={styles["detail-page-content-avatar-tags-tags"]}>
|
||||||
|
{tags.map((tag, index) => (
|
||||||
|
<View
|
||||||
|
key={index}
|
||||||
|
className={styles["detail-page-content-avatar-tags-tags-tag"]}
|
||||||
|
>
|
||||||
|
{tag.icon && <Image src={tag.icon} />}
|
||||||
|
<Text>{tag.name}</Text>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
239
src/game_pages/detail/components/OrganizerInfo/index.module.scss
Normal file
239
src/game_pages/detail/components/OrganizerInfo/index.module.scss
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
.detail-page-content-organizer-recommend-games {
|
||||||
|
padding: 24px 15px 10px;
|
||||||
|
|
||||||
|
.organizer-title {
|
||||||
|
overflow: hidden;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
height: 24px;
|
||||||
|
color: #fff;
|
||||||
|
font-feature-settings: "liga" off, "clig" off;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 24px; /* 150% */
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.organizer-avatar-name {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px 0 0;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
&-avatar {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 50%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-message {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
&-name {
|
||||||
|
color: rgba(255, 255, 255, 0.85);
|
||||||
|
font-size: 13px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 24px; /* 184.615% */
|
||||||
|
}
|
||||||
|
|
||||||
|
&-stats {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 16px; /* 133.333% */
|
||||||
|
letter-spacing: 0.06px;
|
||||||
|
|
||||||
|
&-separator {
|
||||||
|
width: 1px;
|
||||||
|
height: 10px;
|
||||||
|
color: rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.organizer-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-left: auto;
|
||||||
|
|
||||||
|
.organizer-actions-follow,
|
||||||
|
.organizer-actions-comment {
|
||||||
|
display: flex;
|
||||||
|
height: 32px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
border-radius: 999px;
|
||||||
|
// border: 0.5px solid rgba(255, 255, 255, 0.10);
|
||||||
|
background: rgba(255, 255, 255, 0.16);
|
||||||
|
box-shadow: 0 4px 48px 0 rgba(0, 0, 0, 0.08);
|
||||||
|
|
||||||
|
& > image {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.organizer-actions-follow {
|
||||||
|
padding: 8px 12px 8px;
|
||||||
|
&-text {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 13px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 20px; /* 153.846% */
|
||||||
|
letter-spacing: -0.23px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.organizer-actions-comment {
|
||||||
|
padding: 8px 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.organizer-recommend-games {
|
||||||
|
padding-top: 20px;
|
||||||
|
|
||||||
|
.organizer-recommend-games-title {
|
||||||
|
overflow: hidden;
|
||||||
|
color: rgba(255, 255, 255, 0.65);
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
font-size: 15px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 24px; /* 160% */
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
align-self: stretch;
|
||||||
|
|
||||||
|
&-arrow {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.recommend-games-list {
|
||||||
|
padding: 10px 0;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
|
||||||
|
&-content {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
|
||||||
|
.recommend-games-list-item {
|
||||||
|
width: 246px;
|
||||||
|
height: 122px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
border-radius: 20px;
|
||||||
|
border: 1px solid rgba(33, 178, 0, 0.2);
|
||||||
|
background: rgba(255, 255, 255, 0.16);
|
||||||
|
padding: 12px 0 12px 15px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 24px;
|
||||||
|
gap: 2px;
|
||||||
|
overflow: hidden;
|
||||||
|
color: rgba(255, 255, 255, 0.85);
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 24px; /* 150% */
|
||||||
|
|
||||||
|
&-arrow {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-time-range {
|
||||||
|
overflow: hidden;
|
||||||
|
color: rgba(255, 255, 255, 0.45);
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 18px; /* 150% */
|
||||||
|
}
|
||||||
|
|
||||||
|
&-location-venue-distance {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
overflow: hidden;
|
||||||
|
color: rgba(255, 255, 255, 0.45);
|
||||||
|
font-feature-settings: "liga" off, "clig" off;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 18px; /* 150% */
|
||||||
|
}
|
||||||
|
|
||||||
|
&-addon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
&-avatar {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border-radius: 50%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-message {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
&-applications,
|
||||||
|
&-level-requirements,
|
||||||
|
&-play-type {
|
||||||
|
color: rgba(255, 255, 255, 0.85);
|
||||||
|
font-size: 11px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 20px; /* 181.818% */
|
||||||
|
letter-spacing: -0.23px;
|
||||||
|
display: flex;
|
||||||
|
height: 20px;
|
||||||
|
padding: 6px 8px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
border-radius: 999px;
|
||||||
|
// border: 0.5px solid rgba(0, 0, 0, 0.16);
|
||||||
|
background: rgba(255, 255, 255, 0.12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
276
src/game_pages/detail/components/OrganizerInfo/index.tsx
Normal file
276
src/game_pages/detail/components/OrganizerInfo/index.tsx
Normal file
@@ -0,0 +1,276 @@
|
|||||||
|
import Taro from "@tarojs/taro";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import { View, Text, Image, ScrollView } from "@tarojs/components";
|
||||||
|
import { calculateDistance } from "@/utils";
|
||||||
|
import { useUserInfo } from "@/store/userStore";
|
||||||
|
import * as LoginService from "@/services/loginService";
|
||||||
|
import img from "@/config/images";
|
||||||
|
import { navto } from "../../utils/helper";
|
||||||
|
import styles from "./index.module.scss";
|
||||||
|
|
||||||
|
function genRecommendGames(games, location, avatar) {
|
||||||
|
return games.map((item) => {
|
||||||
|
const {
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
start_time,
|
||||||
|
end_time,
|
||||||
|
court_type,
|
||||||
|
location_name,
|
||||||
|
current_players,
|
||||||
|
max_players,
|
||||||
|
latitude,
|
||||||
|
longitude,
|
||||||
|
skill_level_max,
|
||||||
|
skill_level_min,
|
||||||
|
play_type,
|
||||||
|
} = item;
|
||||||
|
const [c_latitude, c_longitude] = location;
|
||||||
|
const distance =
|
||||||
|
calculateDistance(c_latitude, c_longitude, latitude, longitude) / 1000;
|
||||||
|
const startTime = dayjs(start_time);
|
||||||
|
const endTime = dayjs(end_time);
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
time: startTime.format("YYYY-MM-DD HH:MM"),
|
||||||
|
timeLength: endTime.diff(startTime, "hour"),
|
||||||
|
venue: location_name,
|
||||||
|
venueType: court_type,
|
||||||
|
distance: `${distance.toFixed(2)}km`,
|
||||||
|
avatar,
|
||||||
|
applications: max_players,
|
||||||
|
checkedApplications: current_players,
|
||||||
|
levelRequirements:
|
||||||
|
skill_level_max !== skill_level_min
|
||||||
|
? `${skill_level_min || "-"}至${skill_level_max || "-"}`
|
||||||
|
: skill_level_min === "1"
|
||||||
|
? "无要求"
|
||||||
|
: `${skill_level_min}以上`,
|
||||||
|
playType: play_type,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function OrganizerInfo(props) {
|
||||||
|
const {
|
||||||
|
userInfo,
|
||||||
|
currentLocation: location,
|
||||||
|
onUpdateUserInfo = () => {},
|
||||||
|
handleViewUserInfo,
|
||||||
|
handleAddComment,
|
||||||
|
} = props;
|
||||||
|
const {
|
||||||
|
id,
|
||||||
|
nickname,
|
||||||
|
avatar_url,
|
||||||
|
is_following,
|
||||||
|
ntrp_level,
|
||||||
|
stats: { hosted_games_count } = {},
|
||||||
|
ongoing_games = [],
|
||||||
|
} = userInfo;
|
||||||
|
|
||||||
|
const myInfo = useUserInfo();
|
||||||
|
const { id: my_id } = myInfo as LoginService.UserInfoType;
|
||||||
|
|
||||||
|
const recommendGames = genRecommendGames(ongoing_games, location, avatar_url);
|
||||||
|
|
||||||
|
const toggleFollow = async (follow) => {
|
||||||
|
try {
|
||||||
|
if (follow) {
|
||||||
|
await LoginService.unFollowUser(id);
|
||||||
|
} else {
|
||||||
|
await LoginService.followUser(id);
|
||||||
|
}
|
||||||
|
onUpdateUserInfo();
|
||||||
|
Taro.showToast({
|
||||||
|
title: `${nickname} ${follow ? "已取消关注" : "已关注"}`,
|
||||||
|
icon: "success",
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: `${nickname} ${follow ? "取消关注失败" : "关注失败"}`,
|
||||||
|
icon: "error",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function handleViewGame(gameId) {
|
||||||
|
navto(`/game_pages/detail/index?id=${gameId}&from=current`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className={styles["detail-page-content-organizer-recommend-games"]}>
|
||||||
|
{/* orgnizer title */}
|
||||||
|
<View className={styles["organizer-title"]}>
|
||||||
|
<Text>组织者</Text>
|
||||||
|
</View>
|
||||||
|
{/* organizer avatar and name */}
|
||||||
|
<View className={styles["organizer-avatar-name"]}>
|
||||||
|
<Image
|
||||||
|
className={styles["organizer-avatar-name-avatar"]}
|
||||||
|
src={avatar_url}
|
||||||
|
mode="aspectFill"
|
||||||
|
onClick={handleViewUserInfo.bind(null, id)}
|
||||||
|
/>
|
||||||
|
<View className={styles["organizer-avatar-name-message"]}>
|
||||||
|
<Text className={styles["organizer-avatar-name-message-name"]}>
|
||||||
|
{nickname}
|
||||||
|
</Text>
|
||||||
|
<View className={styles["organizer-avatar-name-message-stats"]}>
|
||||||
|
<Text>已组织 {hosted_games_count} 次</Text>
|
||||||
|
<View
|
||||||
|
className={
|
||||||
|
styles["organizer-avatar-name-message-stats-separator"]
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Text>NTRP {ntrp_level || "初学者"}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View className={styles["organizer-actions"]}>
|
||||||
|
{my_id === id ? (
|
||||||
|
""
|
||||||
|
) : (
|
||||||
|
<View
|
||||||
|
className={styles["organizer-actions-follow"]}
|
||||||
|
onClick={toggleFollow.bind(null, is_following)}
|
||||||
|
>
|
||||||
|
{is_following ? (
|
||||||
|
<Text className={styles["organizer-actions-follow-text"]}>
|
||||||
|
取消关注
|
||||||
|
</Text>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Image
|
||||||
|
className={styles["organizer-actions-follow-icon"]}
|
||||||
|
src={img.ICON_DETAIL_APPLICATION_ADD}
|
||||||
|
/>
|
||||||
|
<Text className={styles["organizer-actions-follow-text"]}>
|
||||||
|
关注
|
||||||
|
</Text>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
<View
|
||||||
|
className={styles["organizer-actions-comment"]}
|
||||||
|
onClick={() => handleAddComment()}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
className={styles["organizer-actions-comment-icon"]}
|
||||||
|
src={img.ICON_DETAIL_COMMENT}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
{/* recommend games by organizer */}
|
||||||
|
{recommendGames.length > 0 && (
|
||||||
|
<View className={styles["organizer-recommend-games"]}>
|
||||||
|
<View
|
||||||
|
className={styles["organizer-recommend-games-title"]}
|
||||||
|
onClick={handleViewUserInfo.bind(null, id)}
|
||||||
|
>
|
||||||
|
<Text>TA的更多活动</Text>
|
||||||
|
<Image
|
||||||
|
className={styles["organizer-recommend-games-title-arrow"]}
|
||||||
|
src={img.ICON_DETAIL_ARROW_RIGHT}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<ScrollView className={styles["recommend-games-list"]} scrollX>
|
||||||
|
<View className={styles["recommend-games-list-content"]}>
|
||||||
|
{recommendGames.map((game, index) => (
|
||||||
|
<View
|
||||||
|
key={index}
|
||||||
|
className={styles["recommend-games-list-item"]}
|
||||||
|
onClick={handleViewGame.bind(null, game.id)}
|
||||||
|
>
|
||||||
|
{/* game title */}
|
||||||
|
<View className={styles["recommend-games-list-item-title"]}>
|
||||||
|
<Text>{game.title}</Text>
|
||||||
|
<Image
|
||||||
|
className={
|
||||||
|
styles["recommend-games-list-item-title-arrow"]
|
||||||
|
}
|
||||||
|
src={img.ICON_DETAIL_ARROW_RIGHT}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
{/* game time and range */}
|
||||||
|
<View
|
||||||
|
className={styles["recommend-games-list-item-time-range"]}
|
||||||
|
>
|
||||||
|
<Text>{game.time}</Text>
|
||||||
|
<Text>{game.timeLength}</Text>
|
||||||
|
</View>
|
||||||
|
{/* game location、vunue、distance */}
|
||||||
|
<View
|
||||||
|
className={
|
||||||
|
styles[
|
||||||
|
"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={styles["recommend-games-list-item-addon"]}>
|
||||||
|
<Image
|
||||||
|
className={
|
||||||
|
styles["recommend-games-list-item-addon-avatar"]
|
||||||
|
}
|
||||||
|
mode="aspectFill"
|
||||||
|
src={game.avatar}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleViewUserInfo(id);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
className={
|
||||||
|
styles["recommend-games-list-item-addon-message"]
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
className={
|
||||||
|
styles[
|
||||||
|
"recommend-games-list-item-addon-message-applications"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Text>
|
||||||
|
报名人数 {game.checkedApplications}/
|
||||||
|
{game.applications}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
className={
|
||||||
|
styles[
|
||||||
|
"recommend-games-list-item-addon-message-level-requirements"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Text>{game.levelRequirements}</Text>
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
className={
|
||||||
|
styles[
|
||||||
|
"recommend-games-list-item-addon-message-play-type"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Text>{game.playType}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
132
src/game_pages/detail/components/Participants/index.module.scss
Normal file
132
src/game_pages/detail/components/Participants/index.module.scss
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
.detail-page-content-participants {
|
||||||
|
padding: 24px 15px 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.participants-title {
|
||||||
|
display: flex;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
color: #fff;
|
||||||
|
font-feature-settings: "liga" off, "clig" off;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 24px; /* 150% */
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.participants-list {
|
||||||
|
padding: 10px 0 0;
|
||||||
|
height: 162px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
&-application {
|
||||||
|
display: flex;
|
||||||
|
width: 108px;
|
||||||
|
height: 162px;
|
||||||
|
padding: 16px 12px 10px 12px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
align-self: stretch;
|
||||||
|
border-radius: 20px;
|
||||||
|
border: 1px dashed rgba(255, 255, 255, 0.2);
|
||||||
|
background: rgba(255, 255, 255, 0.16);
|
||||||
|
flex: 0 0 auto;
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-text {
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
font-feature-settings: "liga" off, "clig" off;
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 20px; /* 166.667% */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-scroll {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
width: calc(100% - 116px);
|
||||||
|
|
||||||
|
&-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 8px;
|
||||||
|
height: 162px;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
|
||||||
|
.participants-list-item {
|
||||||
|
display: flex;
|
||||||
|
width: 108px;
|
||||||
|
padding: 16px 4px 10px 4px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
border-radius: 20px;
|
||||||
|
border: 0.5px solid rgba(255, 255, 255, 0.2);
|
||||||
|
background: rgba(255, 255, 255, 0.16);
|
||||||
|
flex: 0 0 auto;
|
||||||
|
|
||||||
|
.participants-list-item-avatar {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 50%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-name {
|
||||||
|
width: 100%;
|
||||||
|
color: rgba(255, 255, 255, 0.85);
|
||||||
|
text-align: center;
|
||||||
|
font-feature-settings: "liga" off, "clig" off;
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
font-size: 13px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 24px; /* 184.615% */
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-level {
|
||||||
|
color: rgba(255, 255, 255, 0.45);
|
||||||
|
text-align: center;
|
||||||
|
font-feature-settings: "liga" off, "clig" off;
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 20px; /* 166.667% */
|
||||||
|
}
|
||||||
|
|
||||||
|
&-role {
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
font-feature-settings: "liga" off, "clig" off;
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 20px; /* 166.667% */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
106
src/game_pages/detail/components/Participants/index.tsx
Normal file
106
src/game_pages/detail/components/Participants/index.tsx
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
import { View, Text, Image, ScrollView } from "@tarojs/components";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import img from "@/config/images";
|
||||||
|
import styles from "./index.module.scss";
|
||||||
|
|
||||||
|
// 参与者
|
||||||
|
export default function Participants(props) {
|
||||||
|
const { detail = {}, handleJoinGame, handleViewUserInfo } = props;
|
||||||
|
const participants = detail.participants || [];
|
||||||
|
const {
|
||||||
|
participant_count,
|
||||||
|
max_participants,
|
||||||
|
user_action_status = {},
|
||||||
|
start_time,
|
||||||
|
} = detail;
|
||||||
|
const { can_join, can_pay, can_substitute, is_substituting, waiting_start } =
|
||||||
|
user_action_status;
|
||||||
|
const showApplicationEntry =
|
||||||
|
[can_pay, can_substitute, is_substituting, waiting_start].every(
|
||||||
|
(item) => !item
|
||||||
|
) &&
|
||||||
|
can_join &&
|
||||||
|
dayjs(start_time).isAfter(dayjs());
|
||||||
|
const leftCount = max_participants - participant_count;
|
||||||
|
return (
|
||||||
|
<View className={styles["detail-page-content-participants"]}>
|
||||||
|
<View className={styles["participants-title"]}>
|
||||||
|
<Text>参与者</Text>
|
||||||
|
<Text>·</Text>
|
||||||
|
<Text>{leftCount > 0 ? `剩余空位 ${leftCount}` : "已满员"}</Text>
|
||||||
|
</View>
|
||||||
|
{participant_count > 0 || showApplicationEntry ? (
|
||||||
|
<View className={styles["participants-list"]}>
|
||||||
|
{/* application */}
|
||||||
|
{showApplicationEntry && (
|
||||||
|
<View
|
||||||
|
className={styles["participants-list-application"]}
|
||||||
|
onClick={() => {
|
||||||
|
handleJoinGame();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
className={styles["participants-list-application-icon"]}
|
||||||
|
src={img.ICON_DETAIL_APPLICATION_ADD}
|
||||||
|
/>
|
||||||
|
<Text className={styles["participants-list-application-text"]}>
|
||||||
|
申请加入
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
{/* participants list */}
|
||||||
|
<ScrollView className={styles["participants-list-scroll"]} scrollX>
|
||||||
|
<View
|
||||||
|
className={styles["participants-list-scroll-content"]}
|
||||||
|
style={{
|
||||||
|
width: `${
|
||||||
|
participants.length * 103 + (participants.length - 1) * 8
|
||||||
|
}px`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{participants.map((participant) => {
|
||||||
|
const {
|
||||||
|
is_organizer,
|
||||||
|
user: {
|
||||||
|
avatar_url,
|
||||||
|
nickname,
|
||||||
|
level,
|
||||||
|
id: participant_user_id,
|
||||||
|
},
|
||||||
|
} = participant;
|
||||||
|
const role = is_organizer ? "组织者" : "参与者";
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
key={participant.id}
|
||||||
|
className={styles["participants-list-item"]}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
className={styles["participants-list-item-avatar"]}
|
||||||
|
mode="aspectFill"
|
||||||
|
src={avatar_url}
|
||||||
|
onClick={handleViewUserInfo.bind(
|
||||||
|
null,
|
||||||
|
participant_user_id
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Text className={styles["participants-list-item-name"]}>
|
||||||
|
{nickname || "未知"}
|
||||||
|
</Text>
|
||||||
|
<Text className={styles["participants-list-item-level"]}>
|
||||||
|
{level || "未知"}
|
||||||
|
</Text>
|
||||||
|
<Text className={styles["participants-list-item-role"]}>
|
||||||
|
{role}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -119,80 +119,3 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.posterContainer {
|
|
||||||
background: linear-gradient(180deg, #fff 0%, #fafafa 100%), #fff;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.posterWrap {
|
|
||||||
border-radius: 19.067px;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
|
||||||
background: linear-gradient(180deg, #bfffef 0%, #f2fffc 100%), #fff;
|
|
||||||
box-shadow: 0 6.933px 55.467px 0 rgba(0, 0, 0, 0.1);
|
|
||||||
overflow: hidden;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sharePoster {
|
|
||||||
margin-top: 40px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-around;
|
|
||||||
|
|
||||||
.shareItem {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 12px;
|
|
||||||
color: rgba(0, 0, 0, 0.85);
|
|
||||||
font-feature-settings: "liga" off, "clig" off;
|
|
||||||
font-family: "PingFang SC";
|
|
||||||
font-size: 14px;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 600;
|
|
||||||
line-height: normal;
|
|
||||||
|
|
||||||
background-color: #fff;
|
|
||||||
border: none;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
line-height: normal;
|
|
||||||
font-size: inherit;
|
|
||||||
color: inherit;
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
border: none;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
width: 64px;
|
|
||||||
height: 64px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: #fff;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
|
||||||
box-shadow: 0 8px 64px 0 rgba(0, 0, 0, 0.1);
|
|
||||||
|
|
||||||
&.wechatIcon {
|
|
||||||
background-color: #07c160;
|
|
||||||
}
|
|
||||||
.download {
|
|
||||||
width: 28px;
|
|
||||||
height: 28px;
|
|
||||||
}
|
|
||||||
.wechat {
|
|
||||||
width: 36px;
|
|
||||||
height: 30px;
|
|
||||||
}
|
|
||||||
.timeline {
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
165
src/game_pages/detail/components/SharePopup/index.tsx
Normal file
165
src/game_pages/detail/components/SharePopup/index.tsx
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
import { forwardRef, useState, useEffect, useImperativeHandle } from "react";
|
||||||
|
import { View, Button, Image, Text } from "@tarojs/components";
|
||||||
|
import Taro, { useShareAppMessage } from "@tarojs/taro";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import "dayjs/locale/zh-cn";
|
||||||
|
import classnames from "classnames";
|
||||||
|
import { generateShareImage } from "@/utils";
|
||||||
|
import DetailService from "@/services/detailService";
|
||||||
|
import { CommonPopup } from "@/components";
|
||||||
|
import DownloadIcon from "@/static/detail/download_icon.svg";
|
||||||
|
import WechatLogo from "@/static/detail/wechat_icon.svg";
|
||||||
|
// import WechatTimeline from "@/static/detail/wechat_timeline.svg";
|
||||||
|
import LinkIcon from "@/static/detail/link.svg";
|
||||||
|
import CrossIcon from "@/static/detail/cross.svg";
|
||||||
|
import { genNTRPRequirementText, navto } from "../../utils/helper";
|
||||||
|
import { DayOfWeekMap } from "../../config";
|
||||||
|
import styles from "./index.module.scss";
|
||||||
|
|
||||||
|
dayjs.locale("zh-cn");
|
||||||
|
|
||||||
|
// 分享弹窗
|
||||||
|
export default forwardRef(({ id, from, detail, userInfo }, ref) => {
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [publishFlag, setPublishFlag] = useState(false);
|
||||||
|
// const posterRef = useRef();
|
||||||
|
const { max_participants, participant_count } = detail || {};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (id) {
|
||||||
|
changeMessageType();
|
||||||
|
}
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
async function changeMessageType() {
|
||||||
|
try {
|
||||||
|
const res = await DetailService.getActivityId({
|
||||||
|
business_id: id,
|
||||||
|
business_type: "game",
|
||||||
|
is_private: false,
|
||||||
|
});
|
||||||
|
if (res.code === 0) {
|
||||||
|
Taro.updateShareMenu({
|
||||||
|
withShareTicket: false, // 是否需要返回 shareTicket
|
||||||
|
isUpdatableMessage: true, // 是否是动态消息(需要服务端配置过模版)
|
||||||
|
activityId: res.data.activity_id, // 动态消息的活动 id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
Taro.showToast({ title: e.message, icon: "none" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
show: (publish_flag = false) => {
|
||||||
|
setPublishFlag(publish_flag);
|
||||||
|
setVisible(true);
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
useShareAppMessage(async (res) => {
|
||||||
|
const {
|
||||||
|
play_type,
|
||||||
|
skill_level_max,
|
||||||
|
skill_level_min,
|
||||||
|
start_time,
|
||||||
|
end_time,
|
||||||
|
location_name,
|
||||||
|
venue_image_list,
|
||||||
|
} = detail || {};
|
||||||
|
const startTime = dayjs(start_time);
|
||||||
|
const endTime = dayjs(end_time);
|
||||||
|
const dayofWeek = DayOfWeekMap.get(startTime.day());
|
||||||
|
const gameLength = `${endTime.diff(startTime, "hour")}小时`;
|
||||||
|
const url = await generateShareImage({
|
||||||
|
userAvatar: userInfo.avatar_url,
|
||||||
|
userNickname: userInfo.nickname,
|
||||||
|
gameType: play_type,
|
||||||
|
skillLevel: `NTRP ${genNTRPRequirementText(
|
||||||
|
skill_level_min,
|
||||||
|
skill_level_max
|
||||||
|
)}`,
|
||||||
|
gameDate: `${startTime.format("M月D日")} (${dayofWeek})`,
|
||||||
|
gameTime: `${startTime.format("ah")}点 ${gameLength}`,
|
||||||
|
venueName: location_name,
|
||||||
|
venueImages: venue_image_list ? venue_image_list.map((c) => c.url) : [],
|
||||||
|
});
|
||||||
|
// console.log(res, "res");
|
||||||
|
return {
|
||||||
|
title: detail.title,
|
||||||
|
imageUrl: url || "https://img.yzcdn.cn/vant/cat.jpeg",
|
||||||
|
path: `/game_pages/detail/index?id=${id}&from=share`,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
async function handlePost() {
|
||||||
|
navto(`/game_pages/sharePoster/index?id=${detail.id}`);
|
||||||
|
setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onClose() {
|
||||||
|
setVisible(false);
|
||||||
|
setPublishFlag(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<CommonPopup
|
||||||
|
title="分享至"
|
||||||
|
visible={visible}
|
||||||
|
onClose={onClose}
|
||||||
|
showHeader={false}
|
||||||
|
hideFooter
|
||||||
|
enableDragToClose={false}
|
||||||
|
style={{ minHeight: "100px" }}
|
||||||
|
zIndex={1000}
|
||||||
|
>
|
||||||
|
<View className={styles.shareContainer}>
|
||||||
|
<View catchMove className={styles.title}>
|
||||||
|
{publishFlag ? (
|
||||||
|
<Text className={styles.publishText}>球局发布成功 🎉</Text>
|
||||||
|
) : (
|
||||||
|
<Text>分享至</Text>
|
||||||
|
)}
|
||||||
|
<View className={styles.closeIconWrap} onClick={onClose}>
|
||||||
|
<Image className={styles.closeIcon} src={CrossIcon} />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
{publishFlag && (
|
||||||
|
<View className={styles.shareTip}>
|
||||||
|
<Text>
|
||||||
|
还剩
|
||||||
|
<Text className={styles.specialCount}>
|
||||||
|
{" "}
|
||||||
|
{max_participants - participant_count}{" "}
|
||||||
|
</Text>
|
||||||
|
人加入完成组局, 去邀请好友加入吧~
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
<View className={styles.shareItems}>
|
||||||
|
<Button className={styles.button} openType="share">
|
||||||
|
<View className={classnames(styles.icon, styles.wechatIcon)}>
|
||||||
|
<Image className={styles.wechat} src={WechatLogo} />
|
||||||
|
</View>
|
||||||
|
<Text>微信好友</Text>
|
||||||
|
</Button>
|
||||||
|
<Button className={styles.button} onClick={handlePost}>
|
||||||
|
<View className={styles.icon}>
|
||||||
|
<Image className={styles.download} src={DownloadIcon} />
|
||||||
|
</View>
|
||||||
|
<Text>生成分享图</Text>
|
||||||
|
</Button>
|
||||||
|
<Button className={styles.button}>
|
||||||
|
<View className={styles.icon}>
|
||||||
|
<Image className={styles.linkIcon} src={LinkIcon} />
|
||||||
|
</View>
|
||||||
|
<Text>复制链接</Text>
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</CommonPopup>
|
||||||
|
{/* <PosterPopup ref={posterRef} id={detail.id} /> */}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
124
src/game_pages/detail/components/StickyBottom/index.module.scss
Normal file
124
src/game_pages/detail/components/StickyBottom/index.module.scss
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
.sticky-bottom-bar {
|
||||||
|
position: sticky;
|
||||||
|
bottom: 0;
|
||||||
|
padding: 10px 10px 32px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
height: 92px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
&-share-and-comment {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 52px;
|
||||||
|
width: 120px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 2px 20px;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 16px;
|
||||||
|
border-radius: 16px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
.sticky-bottom-bar-share {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-text {
|
||||||
|
color: rgba(0, 0, 0, 0.85);
|
||||||
|
font-size: 10px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 16px; /* 160% */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-separator {
|
||||||
|
width: 1px;
|
||||||
|
height: 24px;
|
||||||
|
background: rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sticky-bottom-bar-comment {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-text {
|
||||||
|
color: rgba(0, 0, 0, 0.85);
|
||||||
|
font-size: 10px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 16px; /* 160% */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-main-action {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 52px;
|
||||||
|
width: auto;
|
||||||
|
// padding: 2px 6px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
justify-content: center;
|
||||||
|
// gap: 12px;
|
||||||
|
flex: 1 0 0;
|
||||||
|
border-radius: 16px;
|
||||||
|
// border: 1px solid rgba(0, 0, 0, 0.06);
|
||||||
|
background: #fff;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
background-color: #b4b4b4;
|
||||||
|
color: rgba(60, 60, 67, 0.6);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
253
src/game_pages/detail/components/StickyBottom/index.tsx
Normal file
253
src/game_pages/detail/components/StickyBottom/index.tsx
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import Taro from "@tarojs/taro";
|
||||||
|
import classnames from "classnames";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import { Text, View, Image } from "@tarojs/components";
|
||||||
|
import OrderService from "@/services/orderService";
|
||||||
|
import { EvaluateCallback, EvaluateScene } from "@/store/evaluateStore";
|
||||||
|
import { MATCH_STATUS, IsSubstituteSupported } from "@/services/detailService";
|
||||||
|
import { GameManagePopup, NTRPEvaluatePopup } from "@/components";
|
||||||
|
import img from "@/config/images";
|
||||||
|
import { toast, navto } from "../../utils/helper";
|
||||||
|
import styles from "./index.module.scss";
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 底部操作栏
|
||||||
|
export default function StickyButton(props) {
|
||||||
|
const {
|
||||||
|
handleShare,
|
||||||
|
handleJoinGame,
|
||||||
|
detail,
|
||||||
|
onStatusChange,
|
||||||
|
handleAddComment,
|
||||||
|
getCommentCount,
|
||||||
|
} = props;
|
||||||
|
const [commentCount, setCommentCount] = useState(0);
|
||||||
|
const ntrpRef = useRef<{
|
||||||
|
show: (evaluateCallback: EvaluateCallback) => void;
|
||||||
|
}>({ show: () => {} });
|
||||||
|
const {
|
||||||
|
id,
|
||||||
|
price,
|
||||||
|
user_action_status,
|
||||||
|
match_status,
|
||||||
|
start_time,
|
||||||
|
end_time,
|
||||||
|
is_organizer,
|
||||||
|
} = detail || {};
|
||||||
|
|
||||||
|
const gameManageRef = useRef();
|
||||||
|
|
||||||
|
function handleSelfEvaluate() {
|
||||||
|
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(() => {
|
||||||
|
getCommentCount?.((count) => {
|
||||||
|
setCommentCount(count);
|
||||||
|
});
|
||||||
|
}, [getCommentCount]);
|
||||||
|
|
||||||
|
function generateTextAndAction(
|
||||||
|
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 = false;
|
||||||
|
// console.log(user_action_status, "user_action");
|
||||||
|
const {
|
||||||
|
can_assess,
|
||||||
|
can_join,
|
||||||
|
can_substitute,
|
||||||
|
can_pay,
|
||||||
|
is_substituting,
|
||||||
|
waiting_start,
|
||||||
|
} = user_action_status || {};
|
||||||
|
if (MATCH_STATUS.CANCELED === match_status) {
|
||||||
|
return {
|
||||||
|
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: () => <Text>¥{displayPrice} 已加入</Text>,
|
||||||
|
action: () => toast("已加入"),
|
||||||
|
};
|
||||||
|
} else if (is_substituting) {
|
||||||
|
return {
|
||||||
|
text: () => <Text>¥{displayPrice} 已加入候补</Text>,
|
||||||
|
action: () => toast("已加入候补"),
|
||||||
|
};
|
||||||
|
} else if (can_pay) {
|
||||||
|
return {
|
||||||
|
text: () => <Text>¥{price} 继续支付</Text>,
|
||||||
|
action: async () => {
|
||||||
|
const res = await OrderService.getUnpaidOrder(id);
|
||||||
|
if (res.code === 0) {
|
||||||
|
navto(
|
||||||
|
`/order_pages/orderDetail/index?id=${res.data.order_info.order_id}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else if (can_substitute) {
|
||||||
|
return {
|
||||||
|
text: () => <Text>¥{displayPrice} 我要候补</Text>,
|
||||||
|
action: handleJoinGame,
|
||||||
|
};
|
||||||
|
} else if (can_join) {
|
||||||
|
return {
|
||||||
|
text: () => {
|
||||||
|
return <Text>¥{displayPrice} 立即加入</Text>;
|
||||||
|
},
|
||||||
|
action: handleJoinGame,
|
||||||
|
};
|
||||||
|
} else if (can_assess) {
|
||||||
|
return {
|
||||||
|
text: () => <Text>¥{displayPrice} 立即加入</Text>,
|
||||||
|
action: handleSelfEvaluate,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
text: "球局无法加入",
|
||||||
|
available: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!user_action_status) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
text,
|
||||||
|
available = true,
|
||||||
|
action = () => {},
|
||||||
|
} = generateTextAndAction(user_action_status)!;
|
||||||
|
|
||||||
|
let ActionText: React.FC | string = text;
|
||||||
|
|
||||||
|
if (typeof ActionText === "string") {
|
||||||
|
ActionText = () => {
|
||||||
|
return <Text>{text as string}</Text>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<View className={styles["sticky-bottom-bar"]}>
|
||||||
|
<View className={styles["sticky-bottom-bar-share-and-comment"]}>
|
||||||
|
<View
|
||||||
|
className={styles["sticky-bottom-bar-share"]}
|
||||||
|
onClick={() => handleShare()}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
className={styles["sticky-bottom-bar-share-icon"]}
|
||||||
|
src={img.ICON_DETAIL_SHARE}
|
||||||
|
/>
|
||||||
|
<Text className={styles["sticky-bottom-bar-share-text"]}>分享</Text>
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
className={styles["sticky-bottom-bar-share-and-comment-separator"]}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
className={styles["sticky-bottom-bar-comment"]}
|
||||||
|
onClick={() => {
|
||||||
|
// Taro.showToast({ title: "To be continued", icon: "none" });
|
||||||
|
handleAddComment();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
className={styles["sticky-bottom-bar-comment-icon"]}
|
||||||
|
src={img.ICON_DETAIL_COMMENT_DARK}
|
||||||
|
/>
|
||||||
|
<Text className={styles["sticky-bottom-bar-comment-text"]}>
|
||||||
|
{commentCount > 0 ? commentCount : "评论"}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
className={classnames(
|
||||||
|
styles["detail-main-action"],
|
||||||
|
available ? "" : styles.disabled
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={is_organizer ? {} : { margin: "auto" }}
|
||||||
|
className={styles["sticky-bottom-bar-join-game"]}
|
||||||
|
onClick={action}
|
||||||
|
>
|
||||||
|
<ActionText />
|
||||||
|
</View>
|
||||||
|
{is_organizer && (
|
||||||
|
<View
|
||||||
|
className={styles.game_manage}
|
||||||
|
onClick={() => {
|
||||||
|
gameManageRef.current.show(detail, onStatusChange);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
管理
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<GameManagePopup ref={gameManageRef} />
|
||||||
|
<NTRPEvaluatePopup type={EvaluateScene.detail} ref={ntrpRef} showGuide />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
.detail-page-content-supplemental-notes {
|
||||||
|
padding: 24px 15px 0;
|
||||||
|
|
||||||
|
.supplemental-notes-title {
|
||||||
|
overflow: hidden;
|
||||||
|
padding-bottom: 7px;
|
||||||
|
height: 24px;
|
||||||
|
color: #fff;
|
||||||
|
font-feature-settings: "liga" off, "clig" off;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 24px; /* 150% */
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.supplemental-notes-content {
|
||||||
|
padding: 12px 0 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
&-tags {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
&-tag {
|
||||||
|
overflow: hidden;
|
||||||
|
color: #fff;
|
||||||
|
font-feature-settings: "liga" off, "clig" off;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
font-size: 15px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 24px; /* 160% */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-text {
|
||||||
|
overflow: hidden;
|
||||||
|
color: rgba(255, 255, 255, 0.65);
|
||||||
|
font-feature-settings: "liga" off, "clig" off;
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
font-size: 15px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 24px; /* 160% */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/game_pages/detail/components/SupplementalNotes/index.tsx
Normal file
33
src/game_pages/detail/components/SupplementalNotes/index.tsx
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { Text, View } from "@tarojs/components";
|
||||||
|
import styles from "./index.module.scss";
|
||||||
|
import { insertDotInTags } from "../../utils/helper";
|
||||||
|
|
||||||
|
export default function SupplementalNotes(props) {
|
||||||
|
const {
|
||||||
|
detail: { description, description_tag },
|
||||||
|
} = props;
|
||||||
|
return (
|
||||||
|
<View className={styles["detail-page-content-supplemental-notes"]}>
|
||||||
|
<View className={styles["supplemental-notes-title"]}>
|
||||||
|
<Text>补充说明</Text>
|
||||||
|
</View>
|
||||||
|
<View className={styles["supplemental-notes-content"]}>
|
||||||
|
{/* supplemental notes tags */}
|
||||||
|
<View className={styles["supplemental-notes-content-tags"]}>
|
||||||
|
{insertDotInTags(description_tag || []).map((tag, index) => (
|
||||||
|
<View
|
||||||
|
key={index}
|
||||||
|
className={styles["supplemental-notes-content-tags-tag"]}
|
||||||
|
>
|
||||||
|
<Text>{tag}</Text>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
{/* supplemental notes content */}
|
||||||
|
<View className={styles["supplemental-notes-content-text"]}>
|
||||||
|
<Text>{description}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
115
src/game_pages/detail/components/VenueInfo/index.module.scss
Normal file
115
src/game_pages/detail/components/VenueInfo/index.module.scss
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
.detail-page-content-venue {
|
||||||
|
padding: 24px 15px 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.venue-detail-title {
|
||||||
|
display: flex;
|
||||||
|
height: 31px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
color: #fff;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 24px;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
|
||||||
|
.venue-reserve-status {
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
.venue-reserve-screenshot {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.venue-detail-content {
|
||||||
|
padding: 16px 0 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
&-tags {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
&-tag {
|
||||||
|
overflow: hidden;
|
||||||
|
color: #fff;
|
||||||
|
font-feature-settings: "liga" off, "clig" off;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
font-size: 15px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 24px; /* 160% */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-remarks {
|
||||||
|
overflow: hidden;
|
||||||
|
color: rgba(255, 255, 255, 0.65);
|
||||||
|
// font-feature-settings: 'liga' off, 'clig' off;
|
||||||
|
// text-overflow: ellipsis;
|
||||||
|
// white-space: nowrap;
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
font-size: 15px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 24px; /* 160% */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.venue-screenshot-title {
|
||||||
|
display: flex;
|
||||||
|
padding: 18px 20px 10px 20px;
|
||||||
|
align-items: center;
|
||||||
|
align-self: stretch;
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 24px; /* 150% */
|
||||||
|
letter-spacing: -0.23px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.venue-screenshot-scroll-view {
|
||||||
|
max-height: calc(100vh - 260px);
|
||||||
|
overflow-y: auto;
|
||||||
|
padding-bottom: 40px;
|
||||||
|
|
||||||
|
.venue-screenshot-image-list {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 10px 10px;
|
||||||
|
|
||||||
|
.venue-screenshot-image-item {
|
||||||
|
aspect-ratio: 1/1;
|
||||||
|
border-radius: 9px;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.12);
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: rgba(0, 0, 0, 0.06);
|
||||||
|
margin: 0;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.venue-screenshot-image-item-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 9px;
|
||||||
|
margin: 0;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
105
src/game_pages/detail/components/VenueInfo/index.tsx
Normal file
105
src/game_pages/detail/components/VenueInfo/index.tsx
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { View, Text, Image, ScrollView } from "@tarojs/components";
|
||||||
|
import Taro from "@tarojs/taro";
|
||||||
|
import { CommonPopup } from "@/components";
|
||||||
|
import img from "@/config/images";
|
||||||
|
import styles from "./index.module.scss";
|
||||||
|
import { insertDotInTags } from "../../utils/helper";
|
||||||
|
|
||||||
|
// 场馆信息
|
||||||
|
export default function VenueInfo(props) {
|
||||||
|
const { detail } = props;
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const {
|
||||||
|
venue_description,
|
||||||
|
venue_description_tag = [],
|
||||||
|
venue_image_list = [],
|
||||||
|
} = detail;
|
||||||
|
|
||||||
|
function showScreenShot() {
|
||||||
|
setVisible(true);
|
||||||
|
}
|
||||||
|
function onClose() {
|
||||||
|
setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function previewImage(current_url) {
|
||||||
|
Taro.previewImage({
|
||||||
|
current: current_url,
|
||||||
|
urls:
|
||||||
|
venue_image_list?.length > 0 ? venue_image_list.map((c) => c.url) : [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<View className={styles["detail-page-content-venue"]}>
|
||||||
|
{/* venue detail title and venue ordered status */}
|
||||||
|
<View className={styles["venue-detail-title"]}>
|
||||||
|
<Text>场馆详情</Text>
|
||||||
|
{venue_image_list?.length > 0 ? (
|
||||||
|
<>
|
||||||
|
<Text>·</Text>
|
||||||
|
<View
|
||||||
|
className={styles["venue-reserve-status"]}
|
||||||
|
onClick={showScreenShot}
|
||||||
|
>
|
||||||
|
<Text>已订场</Text>
|
||||||
|
<Image
|
||||||
|
className={styles["venue-reserve-screenshot"]}
|
||||||
|
src={img.ICON_DETAIL_ARROW_RIGHT}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
{/* venue detail content */}
|
||||||
|
<View className={styles["venue-detail-content"]}>
|
||||||
|
{/* venue detail tags */}
|
||||||
|
<View className={styles["venue-detail-content-tags"]}>
|
||||||
|
{insertDotInTags(venue_description_tag).map((tag, index) => (
|
||||||
|
<View
|
||||||
|
key={index}
|
||||||
|
className={styles["venue-detail-content-tags-tag"]}
|
||||||
|
>
|
||||||
|
<Text>{tag}</Text>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
{/* venue remarks */}
|
||||||
|
<View className={styles["venue-detail-content-remarks"]}>
|
||||||
|
<Text>{venue_description}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<CommonPopup
|
||||||
|
visible={visible}
|
||||||
|
onClose={onClose}
|
||||||
|
round
|
||||||
|
hideFooter
|
||||||
|
position="bottom"
|
||||||
|
zIndex={1001}
|
||||||
|
>
|
||||||
|
<View className={styles["venue-screenshot-title"]}>预定截图</View>
|
||||||
|
<ScrollView scrollY className={styles["venue-screenshot-scroll-view"]}>
|
||||||
|
<View className={styles["venue-screenshot-image-list"]}>
|
||||||
|
{venue_image_list?.length > 0 &&
|
||||||
|
venue_image_list.map((item) => {
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
className={styles["venue-screenshot-image-item"]}
|
||||||
|
onClick={previewImage.bind(null, item.url)}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
className={styles["venue-screenshot-image-item-image"]}
|
||||||
|
mode="aspectFill"
|
||||||
|
src={item.url}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
|
</CommonPopup>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
111
src/game_pages/detail/index.module.scss
Normal file
111
src/game_pages/detail/index.module.scss
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
.detail-page {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
// padding-bottom: env(safe-area-inset-bottom);
|
||||||
|
|
||||||
|
.custom-navbar {
|
||||||
|
height: 56px; /* 通常与原生导航栏高度一致 */
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
// background-color: #fff;
|
||||||
|
color: #000;
|
||||||
|
padding-top: 44px; /* 适配状态栏 */
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 100;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-navigator {
|
||||||
|
height: 30px;
|
||||||
|
width: 80px;
|
||||||
|
border-radius: 15px;
|
||||||
|
position: absolute;
|
||||||
|
left: 12px;
|
||||||
|
border: 1px solid #888;
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: #fff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
.detail-navigator-back {
|
||||||
|
border-right: 1px solid #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-navigator-back,
|
||||||
|
.detail-navigator-icon {
|
||||||
|
height: 20px;
|
||||||
|
width: 50%;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
& > .detail-navigator-back-icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .detail-navigator-logo-icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-page-bg {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
background-size: cover;
|
||||||
|
// filter: blur(40px);
|
||||||
|
// transform: scale(1.5);
|
||||||
|
z-index: -2;
|
||||||
|
// width: calc(100% + 20px);
|
||||||
|
// height: calc(100% + 20px);
|
||||||
|
// margin: -10px;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: -1;
|
||||||
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(0, 0, 0, 0.8) 0%,
|
||||||
|
rgba(0, 0, 0, 0.4) 100%
|
||||||
|
);
|
||||||
|
backdrop-filter: blur(100px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-page-content {
|
||||||
|
&-title {
|
||||||
|
padding: 20px 20px 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
&-text {
|
||||||
|
overflow: hidden;
|
||||||
|
color: #fff;
|
||||||
|
font-feature-settings: "liga" off, "clig" off;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
font-size: 22px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 32px; /* 145.455% */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
56
src/game_pages/detail/utils/helper.ts
Normal file
56
src/game_pages/detail/utils/helper.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import Taro, {
|
||||||
|
useLoad,
|
||||||
|
} from "@tarojs/taro";
|
||||||
|
|
||||||
|
export function navto(url) {
|
||||||
|
Taro.navigateTo({
|
||||||
|
url: url,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toast(message) {
|
||||||
|
Taro.showToast({ title: message, icon: "none" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将·作为连接符插入到标签文本之间
|
||||||
|
export function insertDotInTags(tags: string[]) {
|
||||||
|
if (!tags) return [];
|
||||||
|
return tags.join("-·-").split("-");
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useSceneRedirect = (defaultPage: string) => {
|
||||||
|
useLoad((options) => {
|
||||||
|
if (options.scene) {
|
||||||
|
try {
|
||||||
|
const decoded = decodeURIComponent(options.scene || "");
|
||||||
|
const params: Record<string, string> = {};
|
||||||
|
decoded.split("&").forEach((pair) => {
|
||||||
|
const [key, value] = pair.split("=");
|
||||||
|
if (key) params[key] = value ? decodeURIComponent(value) : "";
|
||||||
|
});
|
||||||
|
|
||||||
|
// 拼接成 URL query
|
||||||
|
const query = Object.entries(params)
|
||||||
|
.map(([k, v]) => `${k}=${encodeURIComponent(v)}`)
|
||||||
|
.join("&");
|
||||||
|
|
||||||
|
Taro.redirectTo({
|
||||||
|
url: query ? `/${defaultPage}?${query}` : `/${defaultPage}`,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error("scene 解析失败:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export function genNTRPRequirementText(min, max) {
|
||||||
|
if (min && max && min !== max) {
|
||||||
|
return `${min} - ${max} 之间`;
|
||||||
|
} else if (max === "1") {
|
||||||
|
return "无要求";
|
||||||
|
} else if (max) {
|
||||||
|
return `${max} 以上`;
|
||||||
|
}
|
||||||
|
return "-";
|
||||||
|
}
|
||||||
6
src/game_pages/sharePoster/index.config.ts
Normal file
6
src/game_pages/sharePoster/index.config.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '生成分享图',
|
||||||
|
navigationStyle: 'custom',
|
||||||
|
enableShareAppMessage: true,
|
||||||
|
enableShareTimeline: true,
|
||||||
|
})
|
||||||
101
src/game_pages/sharePoster/index.module.scss
Normal file
101
src/game_pages/sharePoster/index.module.scss
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
.navbar {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.posterContainer {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background: linear-gradient(180deg, #fff 0%, #fafafa 100%), #fff;
|
||||||
|
padding: 100px 20px 40px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
// gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.posterWrap {
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 19.067px;
|
||||||
|
// border: 1px solid rgba(0, 0, 0, 0.06);
|
||||||
|
// background: linear-gradient(180deg, #bfffef 0%, #f2fffc 100%), #fff;
|
||||||
|
// box-shadow: 0 6.933px 55.467px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
// overflow: hidden;
|
||||||
|
box-sizing: border-box;
|
||||||
|
.imageContainer {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.poster {
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 6.933px 55.467px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sharePoster {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 40px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-around;
|
||||||
|
|
||||||
|
.shareItem {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
color: rgba(0, 0, 0, 0.85);
|
||||||
|
font-feature-settings: "liga" off, "clig" off;
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: normal;
|
||||||
|
|
||||||
|
background-color: #fff;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
line-height: normal;
|
||||||
|
font-size: inherit;
|
||||||
|
color: inherit;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #fff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||||
|
box-shadow: 0 8px 64px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
&.wechatIcon {
|
||||||
|
background-color: #07c160;
|
||||||
|
}
|
||||||
|
.download {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
}
|
||||||
|
.wechat {
|
||||||
|
width: 36px;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
.timeline {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
134
src/game_pages/sharePoster/index.tsx
Normal file
134
src/game_pages/sharePoster/index.tsx
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
// import React from "react";
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { View, Image, Text, Button } from "@tarojs/components";
|
||||||
|
import Taro, { useRouter } from "@tarojs/taro";
|
||||||
|
import classnames from "classnames";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import { generatePosterImage, base64ToTempFilePath, delay } from "@/utils";
|
||||||
|
import { withAuth } from "@/components";
|
||||||
|
import GeneralNavbar from "@/components/GeneralNavbar";
|
||||||
|
import DetailService from "@/services/detailService";
|
||||||
|
import DownloadIcon from "@/static/detail/download_icon.svg";
|
||||||
|
import WechatLogo from "@/static/detail/wechat_icon.svg";
|
||||||
|
import WechatTimeline from "@/static/detail/wechat_timeline.svg";
|
||||||
|
import { useUserActions } from "@/store/userStore";
|
||||||
|
import { DayOfWeekMap } from "../detail/config";
|
||||||
|
import { genNTRPRequirementText } from "@/game_pages/detail/utils/helper";
|
||||||
|
import styles from "./index.module.scss";
|
||||||
|
|
||||||
|
function SharePoster(props) {
|
||||||
|
const [url, setUrl] = useState("");
|
||||||
|
const { fetchUserInfo } = useUserActions();
|
||||||
|
const { id } = useRouter().params;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchDetail();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
async function fetchDetail() {
|
||||||
|
const res = await DetailService.getDetail(Number(id));
|
||||||
|
handleGenPoster(res.data);
|
||||||
|
}
|
||||||
|
async function handleGenPoster(detail) {
|
||||||
|
const {
|
||||||
|
id,
|
||||||
|
play_type,
|
||||||
|
skill_level_max,
|
||||||
|
skill_level_min,
|
||||||
|
start_time,
|
||||||
|
end_time,
|
||||||
|
location_name,
|
||||||
|
image_list,
|
||||||
|
title,
|
||||||
|
} = detail || {};
|
||||||
|
const userInfo = await fetchUserInfo();
|
||||||
|
const { avatar_url, nickname } = userInfo;
|
||||||
|
const startTime = dayjs(start_time);
|
||||||
|
const endTime = dayjs(end_time);
|
||||||
|
const dayofWeek = DayOfWeekMap.get(startTime.day());
|
||||||
|
const gameLength = `${endTime.diff(startTime, "hour")}小时`;
|
||||||
|
Taro.showLoading({ title: "生成中..." });
|
||||||
|
const qrCodeUrlRes = await DetailService.getQrCodeUrl({
|
||||||
|
page: "game_pages/detail/index",
|
||||||
|
scene: `id=${id}`,
|
||||||
|
});
|
||||||
|
const qrCodeUrl = await base64ToTempFilePath(
|
||||||
|
qrCodeUrlRes.data.qr_code_base64
|
||||||
|
);
|
||||||
|
await delay(100);
|
||||||
|
const url = await generatePosterImage({
|
||||||
|
playType: play_type,
|
||||||
|
ntrp: `NTRP ${genNTRPRequirementText(skill_level_min, skill_level_max)}`,
|
||||||
|
mainCoursal:
|
||||||
|
image_list[0] ||
|
||||||
|
"https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/0621b8cf-f7d6-43ad-b852-7dc39f29a782.png",
|
||||||
|
nickname,
|
||||||
|
avatarUrl: avatar_url,
|
||||||
|
title,
|
||||||
|
locationName: location_name,
|
||||||
|
date: `${startTime.format("M月D日")} (${dayofWeek})`,
|
||||||
|
time: `${startTime.format("ah")}点 ${gameLength}`,
|
||||||
|
qrCodeUrl,
|
||||||
|
});
|
||||||
|
Taro.hideLoading();
|
||||||
|
setUrl(url);
|
||||||
|
}
|
||||||
|
function handleShare() {
|
||||||
|
Taro.showShareImageMenu({
|
||||||
|
path: url,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<GeneralNavbar
|
||||||
|
title="生成分享图"
|
||||||
|
backgroundColor="transparent"
|
||||||
|
className={styles.navbar}
|
||||||
|
/>
|
||||||
|
{url && (
|
||||||
|
<View className={styles.posterContainer}>
|
||||||
|
<View className={styles.posterWrap}>
|
||||||
|
<View className={styles.imageContainer}>
|
||||||
|
<Image className={styles.poster} src={url} mode="widthFix" />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View className={styles.sharePoster}>
|
||||||
|
<Button
|
||||||
|
className={styles.shareItem}
|
||||||
|
plain={true}
|
||||||
|
onClick={handleShare}
|
||||||
|
>
|
||||||
|
<View className={styles.icon}>
|
||||||
|
<Image className={styles.download} src={DownloadIcon} />
|
||||||
|
</View>
|
||||||
|
<Text>保存至手机</Text>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className={styles.shareItem}
|
||||||
|
plain={true}
|
||||||
|
onClick={handleShare}
|
||||||
|
>
|
||||||
|
<View className={classnames(styles.icon, styles.wechatIcon)}>
|
||||||
|
<Image className={styles.wechat} src={WechatLogo} />
|
||||||
|
</View>
|
||||||
|
<Text>微信好友</Text>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className={styles.shareItem}
|
||||||
|
plain={true}
|
||||||
|
onClick={handleShare}
|
||||||
|
>
|
||||||
|
<View className={styles.icon}>
|
||||||
|
<Image className={styles.timeline} src={WechatTimeline} />
|
||||||
|
</View>
|
||||||
|
<Text>朋友圈</Text>
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withAuth(SharePoster);
|
||||||
@@ -16,17 +16,13 @@ import { withAuth, RefundPopup } from "@/components";
|
|||||||
import { payOrder, generateOrderActions } from "@/utils";
|
import { payOrder, generateOrderActions } from "@/utils";
|
||||||
import emptyContent from "@/static/emptyStatus/publish-empty.png";
|
import emptyContent from "@/static/emptyStatus/publish-empty.png";
|
||||||
import CustomerIcon from "@/static/order/customer.svg";
|
import CustomerIcon from "@/static/order/customer.svg";
|
||||||
|
import { insertDotInTags } from "@/game_pages/detail/utils/helper";
|
||||||
import styles from "./index.module.scss";
|
import styles from "./index.module.scss";
|
||||||
|
|
||||||
dayjs.locale("zh-cn");
|
dayjs.locale("zh-cn");
|
||||||
|
|
||||||
const PAGESIZE = 100;
|
const PAGESIZE = 100;
|
||||||
|
|
||||||
// 将·作为连接符插入到标签文本之间
|
|
||||||
function insertDotInTags(tags: string[]) {
|
|
||||||
return tags.join("-·-").split("-");
|
|
||||||
}
|
|
||||||
|
|
||||||
const diffDayMap = new Map([
|
const diffDayMap = new Map([
|
||||||
[0, "今天"],
|
[0, "今天"],
|
||||||
[1, "明天"],
|
[1, "明天"],
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export interface UserInfo {
|
|||||||
id: number
|
id: number
|
||||||
nickname: string
|
nickname: string
|
||||||
avatar_url: string
|
avatar_url: string
|
||||||
|
province: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BaseComment<T = {}> = {
|
export type BaseComment<T = {}> = {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
|
|
||||||
export interface UserState {
|
export interface UserState {
|
||||||
user: UserInfoType | {};
|
user: UserInfoType | {};
|
||||||
fetchUserInfo: () => Promise<void>;
|
fetchUserInfo: () => Promise<UserInfoType | undefined>;
|
||||||
updateUserInfo: (userInfo: Partial<UserInfoType>) => void;
|
updateUserInfo: (userInfo: Partial<UserInfoType>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -16,8 +16,8 @@ export const useUser = create<UserState>()((set) => ({
|
|||||||
fetchUserInfo: async () => {
|
fetchUserInfo: async () => {
|
||||||
try {
|
try {
|
||||||
const res = await fetchUserProfile();
|
const res = await fetchUserProfile();
|
||||||
console.log(res);
|
|
||||||
set({ user: res.data });
|
set({ user: res.data });
|
||||||
|
return res.data
|
||||||
} catch { }
|
} catch { }
|
||||||
},
|
},
|
||||||
updateUserInfo: async (userInfo: Partial<UserInfoType>) => {
|
updateUserInfo: async (userInfo: Partial<UserInfoType>) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user