333 lines
9.9 KiB
TypeScript
333 lines
9.9 KiB
TypeScript
import React, { useState, useRef, useEffect } from 'react'
|
||
import { View, Text, Input, ScrollView, Image } from '@tarojs/components'
|
||
import Taro from '@tarojs/taro'
|
||
import { Loading } from '@nutui/nutui-react-taro'
|
||
import StadiumDetail, { StadiumDetailRef } from './StadiumDetail'
|
||
import { CommonPopup, CustomPopup } from '../../../../components'
|
||
import { getLocation } from '@/utils/locationUtils'
|
||
import PublishService from '@/services/publishService'
|
||
import images from '@/config/images'
|
||
import './SelectStadium.scss'
|
||
|
||
export interface Stadium {
|
||
id?: string
|
||
name: string
|
||
address?: string
|
||
distance_km?: number | null | undefined
|
||
longitude?: number
|
||
latitude?: number
|
||
}
|
||
|
||
interface SelectStadiumProps {
|
||
visible: boolean
|
||
onClose: () => void
|
||
onConfirm: (stadium: Stadium | null) => void
|
||
}
|
||
|
||
|
||
const SelectStadium: React.FC<SelectStadiumProps> = ({
|
||
visible,
|
||
onClose,
|
||
onConfirm
|
||
}) => {
|
||
const [searchValue, setSearchValue] = useState('')
|
||
const [selectedStadium, setSelectedStadium] = useState<Stadium | null>(null)
|
||
const [showDetail, setShowDetail] = useState(false)
|
||
const stadiumDetailRef = useRef<StadiumDetailRef>(null)
|
||
const [stadiumList, setStadiumList] = useState<Stadium[]>([])
|
||
const [loading, setLoading] = useState(false)
|
||
// const [keyboardVisible, setKeyboardVisible] = useState(false);
|
||
const initData = async () => {
|
||
setLoading(true)
|
||
try {
|
||
const location = await getLocation()
|
||
if (location.latitude && location.longitude) {
|
||
const res = await PublishService.getStadiumList({
|
||
seachOption: {
|
||
latitude: location.latitude,
|
||
longitude: location.longitude
|
||
}
|
||
})
|
||
if (res.code === 0 && res.data) {
|
||
setStadiumList(res.data.rows || [])
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('获取场馆列表失败:', error)
|
||
} finally {
|
||
setLoading(false)
|
||
}
|
||
}
|
||
|
||
|
||
useEffect(() => {
|
||
if (visible) {
|
||
initData()
|
||
}
|
||
}, [visible])
|
||
|
||
if (!visible) return null
|
||
|
||
// 过滤场馆列表
|
||
const filteredStadiums = stadiumList.filter(stadium =>
|
||
stadium.name.toLowerCase().includes(searchValue.toLowerCase())
|
||
)
|
||
|
||
// 处理场馆选择
|
||
const handleStadiumSelect = (stadium: Stadium) => {
|
||
setSelectedStadium(stadium)
|
||
setShowDetail(true)
|
||
}
|
||
|
||
const calculateDistance = (stadium: Stadium) => {
|
||
const distance_km = stadium.distance_km
|
||
if (!distance_km) return ''
|
||
if (distance_km && distance_km > 1) {
|
||
return distance_km.toFixed(1) + 'km'
|
||
}
|
||
return (distance_km * 1000).toFixed(0) + 'm'
|
||
}
|
||
|
||
|
||
// 处理搜索框输入
|
||
const handleSearchInput = (e: any) => {
|
||
setSearchValue(e.detail.value)
|
||
}
|
||
|
||
// 处理地图选择位置
|
||
const handleMapLocation = () => {
|
||
Taro.chooseLocation({
|
||
success: (res) => {
|
||
setSelectedStadium({
|
||
name: res.name,
|
||
address: res.address,
|
||
longitude: res.longitude,
|
||
latitude: res.latitude
|
||
})
|
||
setShowDetail(true)
|
||
},
|
||
fail: (err: { errMsg: string }) => {
|
||
console.error('选择位置失败:', err)
|
||
const { errMsg } = err || {};
|
||
if (!errMsg.includes('fail cancel')) {
|
||
Taro.showToast({
|
||
title: errMsg,
|
||
icon: "none",
|
||
});
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// 处理确认
|
||
const handleConfirm = () => {
|
||
if (stadiumDetailRef.current) {
|
||
const formData = stadiumDetailRef.current.getFormData()
|
||
console.log('获取球馆表单数据:', formData)
|
||
const { description, ...rest } = formData
|
||
const { description: descriptionInfo} = description;
|
||
if (descriptionInfo.length > 200 ) {
|
||
Taro.showToast({
|
||
title: `场地信息补充最多可输入200个字`,
|
||
icon: "none",
|
||
});
|
||
return;
|
||
};
|
||
onConfirm({ ...rest, ...description })
|
||
setSelectedStadium(null)
|
||
setSearchValue('')
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
// 处理取消
|
||
const handleCancel = () => {
|
||
onClose()
|
||
setShowDetail(false)
|
||
setSelectedStadium(null)
|
||
setSearchValue('')
|
||
}
|
||
|
||
const handleDetailCancel = () => {
|
||
setShowDetail(false)
|
||
setSelectedStadium(null)
|
||
setSearchValue('')
|
||
}
|
||
|
||
const handleItemLocation = (stadium: Stadium) => {
|
||
if (stadium.latitude && stadium.longitude) {
|
||
Taro.openLocation({
|
||
latitude: stadium.latitude * 1,
|
||
longitude: stadium.longitude * 1,
|
||
name: stadium.name,
|
||
address: stadium.address,
|
||
success: (res) => {
|
||
console.log(res, 'resres')
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
const markSearchText = (text: string) => {
|
||
if (!searchValue) return text
|
||
return text.replace(
|
||
new RegExp(searchValue, 'gi'),
|
||
`<span class="highlight-text">${searchValue}</span>`
|
||
)
|
||
}
|
||
// const handleAnyInput = (value) => {
|
||
// setKeyboardVisible(value)
|
||
// }
|
||
|
||
|
||
|
||
|
||
// 如果显示详情页面
|
||
if (showDetail && selectedStadium) {
|
||
return (
|
||
<CustomPopup
|
||
visible={visible}
|
||
onClose={handleCancel}
|
||
cancelText="返回"
|
||
confirmText="确认"
|
||
onCancel={handleDetailCancel}
|
||
onConfirm={handleConfirm}
|
||
>
|
||
{/* 内容区域 */}
|
||
<StadiumDetail
|
||
ref={stadiumDetailRef}
|
||
stadium={selectedStadium}
|
||
/>
|
||
</CustomPopup>
|
||
)
|
||
}
|
||
|
||
// 显示球馆列表
|
||
return (
|
||
<CommonPopup
|
||
visible={visible}
|
||
hideFooter
|
||
onClose={handleCancel}
|
||
cancelText="返回"
|
||
confirmText="完成"
|
||
className="select-stadium-popup"
|
||
position="bottom"
|
||
round
|
||
>
|
||
|
||
<View className='select-stadium'>
|
||
{/* 搜索框 */}
|
||
<View className='search-section'>
|
||
<View className='search-wrapper'>
|
||
<View className='search-bar'>
|
||
<Image src={images.ICON_SEARCH} className='search-icon' />
|
||
<Input
|
||
className='search-input'
|
||
placeholder='搜索'
|
||
placeholderClass='search-placeholder'
|
||
value={searchValue}
|
||
onInput={handleSearchInput}
|
||
/>
|
||
{searchValue && (
|
||
<View className='clear-btn' onClick={() => setSearchValue('')}>
|
||
<Image src={images.ICON_REMOVE} className='clear-icon' />
|
||
</View>
|
||
)}
|
||
</View>
|
||
{
|
||
!searchValue && (
|
||
<View className='map-btn' onClick={handleMapLocation}>
|
||
<Image src={images.ICON_MAP} className='map-icon' />
|
||
<Text className='map-text'>地图</Text>
|
||
</View>
|
||
)
|
||
}
|
||
</View>
|
||
</View>
|
||
|
||
{/* 热门球场标题 */}
|
||
<View className='hot-section'>
|
||
<View className='hot-header'>
|
||
<Text className='hot-title'>热门球场</Text>
|
||
<View className='hot-stadium-line'></View>
|
||
<View className='booking-section'>
|
||
<Text className='booking-title'>预定球场</Text>
|
||
<Text className='booking-status'>敬请期待</Text>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
{
|
||
loading ? (
|
||
<View className='stadium-item-loading'>
|
||
<Loading type="circular" className='loading-icon'>加载中</Loading>
|
||
</View>
|
||
) : (
|
||
<ScrollView className='stadium-list' refresherBackground="#FAFAFA"
|
||
scrollWithAnimation
|
||
enhanced
|
||
showScrollbar={false}
|
||
scrollY>
|
||
{filteredStadiums.map((stadium) => (
|
||
<View
|
||
key={stadium.id}
|
||
className={`stadium-item ${selectedStadium?.id === stadium.id ? 'selected' : ''}`}
|
||
onClick={() => handleStadiumSelect(stadium)}
|
||
>
|
||
<View className='stadium-item-left'>
|
||
<Image src={images.ICON_STADIUM} className='stadium-icon' />
|
||
</View>
|
||
<View className='stadium-item-right'>
|
||
<View className='stadium-name' dangerouslySetInnerHTML={{ __html: markSearchText(stadium.name) }}></View>
|
||
<View className='stadium-address'>
|
||
<Text
|
||
className='stadium-distance'
|
||
onClick={(e) => {
|
||
e.stopPropagation()
|
||
handleItemLocation(stadium)
|
||
}}
|
||
>
|
||
{calculateDistance(stadium)} ·
|
||
</Text>
|
||
<Text
|
||
className='stadium-address-text'
|
||
onClick={(e) => {
|
||
e.stopPropagation()
|
||
handleItemLocation(stadium)
|
||
}}
|
||
>
|
||
{stadium.address}
|
||
</Text>
|
||
<Image src={images.ICON_ARRORW_SMALL} className='stadium-map-icon' />
|
||
</View>
|
||
</View>
|
||
|
||
</View>
|
||
))}
|
||
{searchValue && (
|
||
<View className='stadium-item map-search-item' onClick={handleMapLocation}>
|
||
<View className='stadium-item-left'>
|
||
<Image src={images.ICON_MAP_SEARCH} className='stadium-icon' />
|
||
</View>
|
||
<View className='stadium-item-right'>
|
||
<View className='stadium-name'>没有找到球场?去地图定位</View>
|
||
<View className='stadium-address'>
|
||
<Text className='map-search-text'>腾讯地图</Text>
|
||
<Image src={images.ICON_ARRORW_SMALL} className='stadium-map-icon' />
|
||
</View>
|
||
</View>
|
||
</View>
|
||
)}
|
||
</ScrollView>
|
||
)
|
||
}
|
||
{/* 场馆列表 */}
|
||
|
||
</View>
|
||
</CommonPopup>
|
||
)
|
||
}
|
||
|
||
export default SelectStadium
|