728 lines
25 KiB
TypeScript
728 lines
25 KiB
TypeScript
import React, { useState, useEffect, useRef, useImperativeHandle, forwardRef } from 'react'
|
||
import { View, Text, Image, Map, ScrollView } from '@tarojs/components'
|
||
import { Avatar, Popover, ImagePreview } from '@nutui/nutui-react-taro'
|
||
import Taro, { useRouter, useShareAppMessage, useShareTimeline, useDidShow } from '@tarojs/taro'
|
||
import dayjs from 'dayjs'
|
||
import 'dayjs/locale/zh-cn'
|
||
// 导入API服务
|
||
import { CommonPopup, withAuth } from '@/components'
|
||
import DetailService, { MATCH_STATUS} from '@/services/detailService'
|
||
import { getCurrentLocation, calculateDistance } from '@/utils/locationUtils'
|
||
import {
|
||
useUserInfo,
|
||
useUserActions,
|
||
} from '@/store/userStore'
|
||
import img from '@/config/images'
|
||
// import { getTextColorOnImage } from '../../utils'
|
||
import './index.scss'
|
||
|
||
dayjs.locale('zh-cn')
|
||
|
||
// 将·作为连接符插入到标签文本之间
|
||
function insertDotInTags(tags: string[]) {
|
||
return tags.join('-·-').split('-')
|
||
}
|
||
|
||
function GameTags(props) {
|
||
const { detail } = props
|
||
const tags = [{
|
||
name: '🕙 急招',
|
||
icon: '',
|
||
}, {
|
||
name: '🔥 本周热门',
|
||
icon: '',
|
||
}, {
|
||
name: '🎉 新活动',
|
||
icon: '',
|
||
}, {
|
||
name: '官方组织',
|
||
icon: '',
|
||
}]
|
||
return (
|
||
<View className='detail-page-content-avatar-tags'>
|
||
<View className='detail-page-content-avatar-tags-avatar'>
|
||
{/* network image mock */}
|
||
<Image className='detail-page-content-avatar-tags-avatar-image' src="https://img.yzcdn.cn/vant/cat.jpeg" />
|
||
</View>
|
||
<View className='detail-page-content-avatar-tags-tags'>
|
||
{tags.map((tag, index) => (
|
||
<View key={index} className='detail-page-content-avatar-tags-tags-tag'>
|
||
{tag.icon && <Image src={tag.icon} />}
|
||
<Text>{tag.name}</Text>
|
||
</View>
|
||
))}
|
||
</View>
|
||
</View>
|
||
)
|
||
}
|
||
|
||
type CourselItemType = {
|
||
url: string
|
||
width: number
|
||
height: number
|
||
}
|
||
function Coursel(props) {
|
||
const { detail } = props
|
||
const [list, setList] = useState<CourselItemType[]>([])
|
||
const [listWidth, setListWidth] = useState(0)
|
||
const { image_list } = detail
|
||
|
||
async function getImagesMsg (imageList) {
|
||
const latest_list: CourselItemType[] = []
|
||
const sys_info = await Taro.getSystemInfo()
|
||
console.log(sys_info, 'info')
|
||
const max_width = sys_info.screenWidth - 30
|
||
const max_height = 240
|
||
const current_aspect_ratio = max_width / max_height
|
||
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])
|
||
|
||
return (
|
||
<View className="detail-swiper-container">
|
||
<View className="detail-swiper-scroll-container" style={{ width: listWidth + 'px' }}>
|
||
{
|
||
list.map((item, index) => {
|
||
return (
|
||
<View className='detail-swiper-item' key={index}>
|
||
<Image
|
||
src={item.url}
|
||
mode="aspectFill"
|
||
className='detail-swiper-item-image'
|
||
style={{ width: item.width + 'px', height: item.height + 'px' }}
|
||
/>
|
||
</View>
|
||
)
|
||
})
|
||
}
|
||
</View>
|
||
</View>
|
||
)
|
||
}
|
||
|
||
// 分享弹窗
|
||
const SharePopup = forwardRef(({ id, from }: { id: string, from: string }, ref) => {
|
||
const [visible, setVisible] = useState(false)
|
||
|
||
useImperativeHandle(ref, () => ({
|
||
show: () => {
|
||
setVisible(true)
|
||
}
|
||
}))
|
||
|
||
// function handleShareToWechat() {
|
||
// useShareAppMessage(() => {
|
||
// return {
|
||
// title: '分享',
|
||
// path: `/pages/detail/index?id=${id}&from=share`,
|
||
// }
|
||
// })
|
||
// }
|
||
|
||
// function handleShareToWechatMoments() {
|
||
// useShareTimeline(() => {
|
||
// return {
|
||
// title: '分享',
|
||
// path: `/pages/detail/index?id=${id}&from=share`,
|
||
// }
|
||
// })
|
||
// }
|
||
|
||
// function handleSaveToLocal() {
|
||
// Taro.saveImageToPhotosAlbum({
|
||
// filePath: images[0],
|
||
// success: () => {
|
||
// Taro.showToast({ title: '保存成功', icon: 'success' })
|
||
// },
|
||
// fail: () => {
|
||
// Taro.showToast({ title: '保存失败', icon: 'none' })
|
||
// },
|
||
// })
|
||
// }
|
||
|
||
return (
|
||
<CommonPopup
|
||
title="分享"
|
||
visible={visible}
|
||
onClose={() => { setVisible(false) }}
|
||
hideFooter
|
||
style={{ minHeight: '100px' }}
|
||
>
|
||
<View catchMove className='share-popup-content'>
|
||
分享卡片
|
||
</View>
|
||
</CommonPopup>
|
||
)
|
||
})
|
||
|
||
// 底部操作栏
|
||
function StickyButton(props) {
|
||
const { handleShare, handleJoinGame, detail } = props
|
||
const userInfo = useUserInfo()
|
||
const { id } = userInfo
|
||
const { publisher_id, match_status, price } = detail || {}
|
||
|
||
const role = Number(publisher_id) === id ? 'ownner' : 'visitor'
|
||
return (
|
||
<View className="sticky-bottom-bar">
|
||
<View className="sticky-bottom-bar-share-and-comment">
|
||
<View className='sticky-bottom-bar-share' onClick={handleShare}>
|
||
<Image className='sticky-bottom-bar-share-icon' src={img.ICON_DETAIL_SHARE} />
|
||
<Text className='sticky-bottom-bar-share-text'>分享</Text>
|
||
</View>
|
||
<View className='sticky-bottom-bar-share-and-comment-separator' />
|
||
<View className='sticky-bottom-bar-comment' onClick={() => { Taro.showToast({ title: 'To be continued', icon: 'none' }) }}>
|
||
<Image className='sticky-bottom-bar-comment-icon' src={img.ICON_DETAIL_COMMENT_DARK} />
|
||
<Text className='sticky-bottom-bar-comment-text'>32</Text>
|
||
</View>
|
||
</View>
|
||
<View className="sticky-bottom-bar-join-game" onClick={handleJoinGame}>
|
||
<Text>🎾</Text>
|
||
<Text>立即加入</Text>
|
||
<View className='game-price'>
|
||
<Text>¥ {price}</Text>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
)
|
||
}
|
||
|
||
// 球局信息
|
||
function GameInfo(props) {
|
||
const { detail, currentLocation } = props
|
||
const { latitude, longitude, location, location_name, start_time, end_time } = detail || {}
|
||
|
||
const openMap = () => {
|
||
Taro.openLocation({
|
||
latitude, // 纬度(必填)
|
||
longitude, // 经度(必填)
|
||
name: location_name, // 位置名(可选)
|
||
address: location, // 地址详情(可选)
|
||
scale: 15, // 地图缩放级别(1-28)
|
||
})
|
||
}
|
||
|
||
const [c_latitude, c_longitude] = currentLocation
|
||
const distance = calculateDistance(c_latitude, c_longitude, latitude, longitude) / 1000
|
||
|
||
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='detail-page-content-game-info'>
|
||
{/* Date and Weather */}
|
||
<View className='detail-page-content-game-info-date-weather'>
|
||
{/* Calendar and Date time */}
|
||
<View className='detail-page-content-game-info-date-weather-calendar-date'>
|
||
{/* Calendar */}
|
||
<View className='detail-page-content-game-info-date-weather-calendar-date-calendar'>
|
||
<View className="month">{startMonth}月</View>
|
||
<View className="day">{startDay}</View>
|
||
</View>
|
||
{/* Date time */}
|
||
<View className='detail-page-content-game-info-date-weather-calendar-date-date'>
|
||
<View className="date">{startDate}</View>
|
||
<View className="venue-time">{gameRange} ({game_length}小时)</View>
|
||
</View>
|
||
</View>
|
||
{/* Weather */}
|
||
<View className='detail-page-content-game-info-date-weather-weather'>
|
||
{/* Weather icon */}
|
||
<View className='detail-page-content-game-info-date-weather-weather-icon'>
|
||
<Image className="weather-icon" src={img.ICON_WEATHER_SUN} />
|
||
</View>
|
||
{/* Weather text and temperature */}
|
||
<View className='detail-page-content-game-info-date-weather-weather-text-temperature'>
|
||
<Text>28℃ - 32℃</Text>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
{/* Place */}
|
||
<View className='detail-page-content-game-info-place'>
|
||
{/* venue location message */}
|
||
<View className='location-message'>
|
||
{/* location icon */}
|
||
<View className='location-message-icon'>
|
||
<Image className='location-message-icon-image' src={img.ICON_DETAIL_MAP} />
|
||
</View>
|
||
{/* location message */}
|
||
<View className='location-message-text'>
|
||
{/* venue name and distance */}
|
||
<View className='location-message-text-name-distance' onClick={openMap}>
|
||
<Text>{location_name || '-'}</Text>
|
||
<Text>·</Text>
|
||
<Text>{distance.toFixed(1)}km</Text>
|
||
<Image className='location-message-text-name-distance-arrow' src={img.ICON_DETAIL_ARROW_RIGHT} />
|
||
</View>
|
||
{/* venue address */}
|
||
<View className='location-message-text-address'>
|
||
<Text>{location || '-'}</Text>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
{/* venue map */}
|
||
<View className='location-map'>
|
||
{longitude && latitude && (
|
||
<Map
|
||
className='location-map-map'
|
||
longitude={latitude}
|
||
latitude={longitude}
|
||
markers={[{ id: 1, latitude: longitude, longitude: latitude, iconPath: require('@/static/detail/icon-stark.svg'), width: 16, height: 16 }]}
|
||
includePoints={[{ latitude: longitude, longitude: latitude }, { latitude: currentLocation[0], longitude: currentLocation[1] }]}
|
||
includePadding={{ left: 50, right: 50, top: 50, bottom: 50 }}
|
||
onError={() => {}}
|
||
// hide business msg
|
||
showLocation
|
||
theme='dark'
|
||
/>
|
||
)}
|
||
</View>
|
||
</View>
|
||
</View>
|
||
)
|
||
}
|
||
|
||
// 场馆信息
|
||
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.map(c => c.url),
|
||
})
|
||
}
|
||
return (
|
||
<View className='detail-page-content-venue'>
|
||
{/* venue detail title and venue ordered status */}
|
||
<View className='venue-detail-title'>
|
||
<Text>场馆详情</Text>
|
||
{venue_image_list?.length > 0 ?
|
||
<>
|
||
<Text>·</Text>
|
||
<View className="venue-reserve-status" onClick={showScreenShot}>
|
||
<Text>已订场</Text>
|
||
<Image className="venue-reserve-screenshot" src={img.ICON_DETAIL_ARROW_RIGHT} />
|
||
</View>
|
||
</>
|
||
:
|
||
''
|
||
}
|
||
</View>
|
||
{/* venue detail content */}
|
||
<View className='venue-detail-content'>
|
||
{/* venue detail tags */}
|
||
<View className='venue-detail-content-tags'>
|
||
{insertDotInTags(venue_description_tag).map((tag, index) => (
|
||
<View key={index} className='venue-detail-content-tags-tag'>
|
||
<Text>{tag}</Text>
|
||
</View>
|
||
))}
|
||
</View>
|
||
{/* venue remarks */}
|
||
<View className='venue-detail-content-remarks'>
|
||
<Text>{venue_description}</Text>
|
||
</View>
|
||
</View>
|
||
<CommonPopup
|
||
visible={visible}
|
||
onClose={onClose}
|
||
round
|
||
hideFooter
|
||
position='bottom'
|
||
zIndex={1001}
|
||
>
|
||
<View className="venue-screenshot-title">预定截图</View>
|
||
<ScrollView
|
||
scrollY
|
||
className="venue-screenshot-scroll-view"
|
||
>
|
||
<View className="venue-screenshot-image-list">
|
||
{venue_image_list.map(item => {
|
||
return (
|
||
<View className="venue-screenshot-image-item" onClick={previewImage.bind(null, item.url)}>
|
||
<Image className="venue-screenshot-image-item-image" src={item.url} />
|
||
</View>
|
||
)
|
||
})}
|
||
</View>
|
||
</ScrollView>
|
||
</CommonPopup>
|
||
</View>
|
||
)
|
||
}
|
||
|
||
function genNTRPRequirementText(min, max) {
|
||
if (min && max) {
|
||
return `${min} - ${max} 之间`
|
||
} else if (max) {
|
||
return `${max} 以上`
|
||
}
|
||
return '没有要求'
|
||
}
|
||
// 玩法要求
|
||
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='detail-page-content-gameplay-requirements'>
|
||
{/* title */}
|
||
<View className="gameplay-requirements-title">
|
||
<Text>玩法要求</Text>
|
||
</View>
|
||
{/* requirements */}
|
||
<View className='gameplay-requirements'>
|
||
{requirements.map((item, index) => (
|
||
<View key={index} className='gameplay-requirements-item'>
|
||
<Text className='gameplay-requirements-item-title'>{item.title}</Text>
|
||
<Text className='gameplay-requirements-item-desc'>{item.desc}</Text>
|
||
</View>
|
||
))}
|
||
</View>
|
||
</View>
|
||
)
|
||
}
|
||
|
||
// 参与者
|
||
function Participants(props) {
|
||
const { detail = {} } = props
|
||
const participants = detail.participants || []
|
||
const organizer_id = Number(detail.publisher_id)
|
||
return (
|
||
<View className='detail-page-content-participants'>
|
||
<View className='participants-title'>
|
||
<Text>参与者</Text>
|
||
<Text>·</Text>
|
||
<Text>剩余空位 3</Text>
|
||
</View>
|
||
<View className='participants-list'>
|
||
{/* application */}
|
||
<View className='participants-list-application' onClick={() => { Taro.showToast({ title: 'To be continued', icon: 'none' }) }}>
|
||
<Image className='participants-list-application-icon' src={img.ICON_DETAIL_APPLICATION_ADD} />
|
||
<Text className='participants-list-application-text'>申请加入</Text>
|
||
</View>
|
||
{/* participants list */}
|
||
<ScrollView className='participants-list-scroll' scrollX>
|
||
<View className='participants-list-scroll-content' style={{ width: `${participants.length * 103 + (participants.length - 1) * 8}px` }}>
|
||
{participants.map((participant) => {
|
||
const { user: { avatar_url, nickname, level, id: participant_user_id } } = participant
|
||
const role = participant_user_id === organizer_id ? '组织者' : '参与者'
|
||
return (
|
||
<View key={participant.id} className='participants-list-item'>
|
||
<Avatar className='participants-list-item-avatar' src={avatar_url} />
|
||
<Text className='participants-list-item-name'>{nickname || '未知'}</Text>
|
||
<Text className='participants-list-item-level'>{level || '未知'}</Text>
|
||
<Text className='participants-list-item-role'>{role}</Text>
|
||
</View>
|
||
)
|
||
})}
|
||
</View>
|
||
</ScrollView>
|
||
</View>
|
||
</View>
|
||
)
|
||
}
|
||
|
||
function SupplementalNotes(props) {
|
||
const { detail: { description, description_tag = [] } } = props
|
||
return (
|
||
<View className='detail-page-content-supplemental-notes'>
|
||
<View className='supplemental-notes-title'>
|
||
<Text>补充说明</Text>
|
||
</View>
|
||
<View className='supplemental-notes-content'>
|
||
{/* supplemental notes tags */}
|
||
<View className='supplemental-notes-content-tags'>
|
||
{insertDotInTags(description_tag).map((tag, index) => (
|
||
<View key={index} className='supplemental-notes-content-tags-tag'>
|
||
<Text>{tag}</Text>
|
||
</View>
|
||
))}
|
||
</View>
|
||
{/* supplemental notes content */}
|
||
<View className='supplemental-notes-content-text'>
|
||
<Text>{description}</Text>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
)
|
||
}
|
||
|
||
function OrganizerInfo(props) {
|
||
const recommendGames = [
|
||
{
|
||
title: '黄浦日场对拉',
|
||
time: '2025-08-25 9:00',
|
||
timeLength: '2小时',
|
||
venue: '上海体育场',
|
||
veuneType: '室外',
|
||
distance: '1.2km',
|
||
avatar: 'https://img.yzcdn.cn/vant/cat.jpeg',
|
||
applications: 10,
|
||
checkedApplications: 3,
|
||
levelRequirements: 'NTRP 3.5',
|
||
playType: '双打',
|
||
},
|
||
{
|
||
title: '黄浦夜场对拉',
|
||
time: '2025-08-25 19:00',
|
||
timeLength: '2小时',
|
||
venue: '上海体育场',
|
||
veuneType: '室外',
|
||
distance: '1.2km',
|
||
avatar: 'https://img.yzcdn.cn/vant/cat.jpeg',
|
||
applications: 10,
|
||
checkedApplications: 3,
|
||
levelRequirements: 'NTRP 3.5',
|
||
playType: '双打',
|
||
},
|
||
{
|
||
title: '黄浦全天对拉',
|
||
time: '2025-08-25 9:00',
|
||
timeLength: '12小时',
|
||
venue: '上海体育场',
|
||
veuneType: '室外',
|
||
distance: '1.2km',
|
||
avatar: 'https://img.yzcdn.cn/vant/cat.jpeg',
|
||
applications: 10,
|
||
checkedApplications: 3,
|
||
levelRequirements: 'NTRP 3.5',
|
||
playType: '双打',
|
||
},
|
||
]
|
||
return (
|
||
<View className='detail-page-content-organizer-recommend-games'>
|
||
{/* orgnizer title */}
|
||
<View className='organizer-title'>
|
||
<Text>组织者</Text>
|
||
</View>
|
||
{/* organizer avatar and name */}
|
||
<View className='organizer-avatar-name'>
|
||
<Avatar className='organizer-avatar-name-avatar' src="https://img.yzcdn.cn/vant/cat.jpeg" />
|
||
<View className='organizer-avatar-name-message'>
|
||
<Text className='organizer-avatar-name-message-name'>Light</Text>
|
||
<View className='organizer-avatar-name-message-stats'>
|
||
<Text>已组织 8 次</Text>
|
||
<View className='organizer-avatar-name-message-stats-separator' />
|
||
<Text>NTRP 3.5</Text>
|
||
</View>
|
||
</View>
|
||
<View className="organizer-actions">
|
||
<View className="organizer-actions-follow">
|
||
<Image className='organizer-actions-follow-icon' src={img.ICON_DETAIL_APPLICATION_ADD} />
|
||
<Text className='organizer-actions-follow-text'>关注</Text>
|
||
</View>
|
||
<View className="organizer-actions-comment">
|
||
<Image className='organizer-actions-comment-icon' src={img.ICON_DETAIL_COMMENT} />
|
||
</View>
|
||
</View>
|
||
</View>
|
||
{/* recommend games by organizer */}
|
||
<View className='organizer-recommend-games'>
|
||
<View className='organizer-recommend-games-title'>
|
||
<Text>TA的更多活动</Text>
|
||
<Image className='organizer-recommend-games-title-arrow' src={img.ICON_DETAIL_ARROW_RIGHT} />
|
||
</View>
|
||
<ScrollView className='recommend-games-list' scrollX>
|
||
<View className='recommend-games-list-content'>
|
||
{recommendGames.map((game, index) => (
|
||
<View key={index} className='recommend-games-list-item'>
|
||
{/* game title */}
|
||
<View className='recommend-games-list-item-title'>
|
||
<Text>{game.title}</Text>
|
||
<Image className='recommend-games-list-item-title-arrow' src={img.ICON_DETAIL_ARROW_RIGHT} />
|
||
</View>
|
||
{/* game time and range */}
|
||
<View className='recommend-games-list-item-time-range'>
|
||
<Text>{game.time}</Text>
|
||
<Text>{game.timeLength}</Text>
|
||
</View>
|
||
{/* game location、vunue、distance */}
|
||
<View className='recommend-games-list-item-location-venue-distance'>
|
||
<Text>{game.venue}</Text>
|
||
<Text>·</Text>
|
||
<Text>{game.veuneType}</Text>
|
||
<Text>·</Text>
|
||
<Text>{game.distance}</Text>
|
||
</View>
|
||
{/* organizer avatar、applications、level requirements、play type */}
|
||
<View className='recommend-games-list-item-addon'>
|
||
<Avatar className='recommend-games-list-item-addon-avatar' src={game.avatar} />
|
||
<View className='recommend-games-list-item-addon-message'>
|
||
<View className='recommend-games-list-item-addon-message-applications'>
|
||
<Text>报名人数 {game.checkedApplications}/{game.applications}</Text>
|
||
</View>
|
||
<View className='recommend-games-list-item-addon-message-level-requirements'>
|
||
<Text>{game.levelRequirements}</Text>
|
||
</View>
|
||
<View className='recommend-games-list-item-addon-message-play-type'>
|
||
<Text>{game.playType}</Text>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
))}
|
||
</View>
|
||
</ScrollView>
|
||
</View>
|
||
</View>
|
||
)
|
||
}
|
||
|
||
function Index() {
|
||
const [detail, setDetail] = useState<any>({})
|
||
const { params } = useRouter()
|
||
const [currentLocation, setCurrentLocation] = useState<[number, number]>([0, 0])
|
||
const { id, from } = params
|
||
const { fetchUserInfo, updateUserInfo } = useUserActions()
|
||
|
||
const sharePopupRef = useRef<any>(null)
|
||
|
||
useDidShow(async () => {
|
||
await updateLocation()
|
||
await fetchUserInfo()
|
||
await fetchDetail()
|
||
})
|
||
|
||
const updateLocation = async () => {
|
||
try {
|
||
const location = await getCurrentLocation()
|
||
setCurrentLocation([location.latitude, location.longitude])
|
||
await updateUserInfo({ latitude: location.latitude, longitude: location.longitude })
|
||
} catch (error) {
|
||
console.error('用户位置更新失败', error)
|
||
}
|
||
}
|
||
|
||
const fetchDetail = async () => {
|
||
const res = await DetailService.getDetail(Number(id))
|
||
if (res.code === 0) {
|
||
setDetail(res.data)
|
||
}
|
||
}
|
||
|
||
function handleShare() {
|
||
sharePopupRef.current.show()
|
||
}
|
||
|
||
const handleJoinGame = () => {
|
||
Taro.navigateTo({
|
||
url: `/pages/orderCheck/index?gameId=${id}`,
|
||
})
|
||
}
|
||
|
||
function handleBack() {
|
||
const pages = Taro.getCurrentPages()
|
||
if (pages.length <= 1) {
|
||
Taro.redirectTo({
|
||
url: '/pages/list/index',
|
||
})
|
||
} else {
|
||
Taro.navigateBack()
|
||
}
|
||
}
|
||
|
||
|
||
console.log('detail', detail)
|
||
const backgroundImage = detail?.image_list?.[0] ? { backgroundImage: `url(${detail?.image_list?.[0]})` } : {}
|
||
|
||
return (
|
||
<View className='detail-page'>
|
||
{/* custom navbar */}
|
||
<view className="custom-navbar">
|
||
<View className='detail-navigator'>
|
||
<View className='detail-navigator-back' onClick={handleBack}>
|
||
<Image className='detail-navigator-back-icon' src={img.ICON_ARROW_LEFT} />
|
||
</View>
|
||
<View className='detail-navigator-icon'>
|
||
<Image className='detail-navigator-logo-icon' src={img.ICON_LOGO_GO} />
|
||
</View>
|
||
</View>
|
||
</view>
|
||
<View className='detail-page-bg' style={backgroundImage} />
|
||
{/* swiper */}
|
||
<Coursel detail={detail} />
|
||
{/* content */}
|
||
<View className='detail-page-content'>
|
||
{/* avatar and tags */}
|
||
<GameTags detail={detail} />
|
||
{/* title */}
|
||
<View className='detail-page-content-title'>
|
||
<Text className='detail-page-content-title-text'>{detail.title}</Text>
|
||
</View>
|
||
{/* Date and Place and weather */}
|
||
<GameInfo detail={detail} currentLocation={currentLocation} />
|
||
{/* detail */}
|
||
<VenueInfo detail={detail} />
|
||
{/* gameplay requirements */}
|
||
<GamePlayAndRequirement detail={detail} />
|
||
{/* participants */}
|
||
<Participants detail={detail} />
|
||
{/* supplemental notes */}
|
||
<SupplementalNotes detail={detail} />
|
||
{/* organizer and recommend games by organizer */}
|
||
<OrganizerInfo detail={detail} />
|
||
{/* sticky bottom action bar */}
|
||
<StickyButton handleShare={handleShare} handleJoinGame={handleJoinGame} detail={detail} />
|
||
{/* share popup */}
|
||
<SharePopup ref={sharePopupRef} id={id as string} from={from as string} />
|
||
</View>
|
||
</View>
|
||
)
|
||
}
|
||
|
||
export default withAuth(Index) |