feat: 分包

This commit is contained in:
2025-09-12 16:28:46 +08:00
parent 19701bd246
commit ee0b5763de
39 changed files with 75 additions and 64 deletions

View File

@@ -0,0 +1,306 @@
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 } 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 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) => {
console.error('选择位置失败:', err)
}
})
}
// 处理确认
const handleConfirm = () => {
if (stadiumDetailRef.current) {
const formData = stadiumDetailRef.current.getFormData()
console.log('获取球馆表单数据:', formData)
const { description, ...rest } = formData
onConfirm({ ...rest, ...description })
setSelectedStadium(null)
setSearchValue('')
}
}
// 处理取消
const handleCancel = () => {
onClose()
setShowDetail(false)
setSelectedStadium(null)
setSearchValue('')
}
const handleItemLocation = (stadium: Stadium) => {
if (stadium.latitude && stadium.longitude) {
Taro.openLocation({
latitude: stadium.latitude,
longitude: stadium.longitude,
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>`
)
}
// 如果显示详情页面
if (showDetail && selectedStadium) {
return (
<CommonPopup
visible={visible}
onClose={handleCancel}
cancelText="返回"
confirmText="确认"
className="select-stadium-popup"
onCancel={handleCancel}
onConfirm={handleConfirm}
position="bottom"
round
>
<StadiumDetail
ref={stadiumDetailRef}
stadium={selectedStadium}
/>
</CommonPopup>
)
}
// 显示球馆列表
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' 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