312 lines
9.3 KiB
TypeScript
312 lines
9.3 KiB
TypeScript
import React, { useState, useCallback, forwardRef, useImperativeHandle, useEffect } from 'react'
|
||
import Taro from '@tarojs/taro'
|
||
import { View, Text, Image, ScrollView } from '@tarojs/components'
|
||
import images from '@/config/images'
|
||
import TextareaTag from '@/components/TextareaTag'
|
||
// import CoverImageUpload, { type CoverImage } from '@/components/ImageUpload'
|
||
import UploadCover, { type CoverImageValue } from '@/components/UploadCover'
|
||
import { useKeyboardHeight } from '@/store/keyboardStore'
|
||
import { useDictionaryActions } from '@/store/dictionaryStore'
|
||
import { normalize_address } from '@/utils/locationUtils'
|
||
|
||
import './StadiumDetail.scss'
|
||
|
||
export interface Stadium {
|
||
id?: string
|
||
venue_id?: string
|
||
name: string
|
||
address?: string
|
||
longitude?: number
|
||
latitude?: number
|
||
distance_km?: number | null
|
||
court_type?: string
|
||
court_surface?: string
|
||
description?: string
|
||
description_tag?: string[]
|
||
venue_image_list?: CoverImageValue[]
|
||
}
|
||
|
||
interface StadiumDetailProps {
|
||
stadium: Stadium
|
||
onAnyInput?: (value: boolean) => void
|
||
}
|
||
|
||
// 定义暴露给父组件的方法接口
|
||
export interface StadiumDetailRef {
|
||
getFormData: () => any
|
||
setFormData: (data: any) => void
|
||
}
|
||
|
||
|
||
// 公共的标题组件
|
||
const SectionTitle: React.FC<{ title: string,prop: string }> = ({ title, prop }) => {
|
||
if (prop === 'venue_image_list') {
|
||
return (
|
||
<View className='section-title'>
|
||
<Text>{title}</Text>
|
||
<View className='heart-wrapper'>
|
||
<Image src={images.ICON_HEART_CIRCLE} className='heart-icon' />
|
||
<View className='icon-bg'></View>
|
||
<Text className='heart-text'>添加截图,平台会优先推荐</Text>
|
||
</View>
|
||
</View>
|
||
)
|
||
}
|
||
return (
|
||
<Text className='section-title'>{title}</Text>
|
||
)
|
||
}
|
||
|
||
// 公共的容器组件
|
||
const SectionContainer: React.FC<{ title: string; children: React.ReactNode, prop: string, type?: string }> = ({ title, children, prop, type }) => (
|
||
<View className={`venue-type-section ${type === 'image' ? 'image-wrap' : ''}`}>
|
||
<SectionTitle title={title} prop={prop}/>
|
||
<View className='option-buttons'>
|
||
{children}
|
||
</View>
|
||
</View>
|
||
)
|
||
|
||
const StadiumDetail = forwardRef<StadiumDetailRef, StadiumDetailProps>(({
|
||
stadium,
|
||
onAnyInput
|
||
}, ref) => {
|
||
const [openPicker, setOpenPicker] = useState(false); //为了解决上传图片时按钮样式问题
|
||
const [scrollTop, setScrollTop] = useState(0);
|
||
const { getDictionaryValue } = useDictionaryActions()
|
||
const court_type = getDictionaryValue('court_type') || []
|
||
const court_surface = getDictionaryValue('court_surface') || []
|
||
const supplementary_information = getDictionaryValue('supplementary_information') || []
|
||
|
||
// 使用全局键盘状态
|
||
const { keyboardHeight, isKeyboardVisible, addListener, initializeKeyboardListener } = useKeyboardHeight()
|
||
|
||
const stadiumInfo = [
|
||
{
|
||
label: '场地类型',
|
||
options: court_type,
|
||
prop: 'court_type',
|
||
type: 'tags'
|
||
},
|
||
{
|
||
label: '地面材质',
|
||
options: court_surface,
|
||
prop: 'court_surface',
|
||
type: 'tags'
|
||
},
|
||
{
|
||
label: '场地信息补充',
|
||
options: supplementary_information,
|
||
prop: 'description',
|
||
type: 'textareaTag'
|
||
},
|
||
{
|
||
label: '场地预定截图',
|
||
options: ['有其他场地信息可备注'],
|
||
prop: 'venue_image_list',
|
||
type: 'image'
|
||
}
|
||
]
|
||
const [formData, setFormData] = useState({
|
||
name: stadium.name,
|
||
address: stadium.address,
|
||
latitude: stadium.latitude,
|
||
longitude: stadium.longitude,
|
||
istance: stadium.distance_km,
|
||
court_type: court_type[0] || '',
|
||
court_surface: court_surface[0] || '',
|
||
additionalInfo: '',
|
||
venue_image_list: [] as CoverImageValue[],
|
||
description:{
|
||
description: '',
|
||
description_tag: []
|
||
},
|
||
venue_id: stadium.id
|
||
})
|
||
|
||
// 暴露方法给父组件
|
||
useImperativeHandle(ref, () => ({
|
||
getFormData: () => formData,
|
||
setFormData: (data: any) => setFormData(data)
|
||
}), [formData, stadium])
|
||
|
||
const calculateDistance = (distance_km: number | null) => {
|
||
if (!distance_km) return ''
|
||
if (distance_km && distance_km > 1) {
|
||
return distance_km.toFixed(1) + 'km'
|
||
}
|
||
return (distance_km * 1000).toFixed(0) + 'm'
|
||
}
|
||
|
||
|
||
const handleMapLocation = () => {
|
||
Taro.chooseLocation({
|
||
success: (res) => {
|
||
console.log(res,'resres');
|
||
setFormData({
|
||
...formData,
|
||
name: res.name,
|
||
address: normalize_address(res.address || ''),
|
||
latitude: res.latitude,
|
||
longitude: res.longitude,
|
||
istance: null
|
||
})
|
||
},
|
||
fail: (err: { errMsg: string }) => {
|
||
console.warn('选择位置失败:', err)
|
||
const { errMsg } = err || {};
|
||
if (!errMsg.includes('fail cancel')) {
|
||
Taro.showToast({
|
||
title: errMsg,
|
||
icon: "none",
|
||
});
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
const updateFormData = useCallback((prop: string, value: any) => {
|
||
setFormData(prev => ({ ...prev, [prop]: value }))
|
||
}, [])
|
||
|
||
const getSelectedByLabel = useCallback((label: string) => {
|
||
if (label === '场地类型') return formData.court_type
|
||
if (label === '地面材质') return formData.court_surface
|
||
return ''
|
||
}, [formData.court_type, formData.court_surface])
|
||
|
||
|
||
|
||
// 使用全局键盘状态监听
|
||
useEffect(() => {
|
||
// 初始化全局键盘监听器
|
||
initializeKeyboardListener()
|
||
|
||
// 添加本地监听器
|
||
const removeListener = addListener((height, visible) => {
|
||
console.log('AiImportPopup 收到键盘变化:', height, visible)
|
||
})
|
||
|
||
return () => {
|
||
removeListener()
|
||
}
|
||
}, [initializeKeyboardListener, addListener])
|
||
|
||
const changeTextarea = (value: boolean) => {
|
||
if (value) {
|
||
// 先滚动到底部
|
||
setScrollTop(140);
|
||
// 使用 setTimeout 确保滚动后再更新 openPicker
|
||
}
|
||
}
|
||
|
||
const changePicker = (value:boolean) => {
|
||
setOpenPicker(value);
|
||
}
|
||
|
||
console.log(stadium,'stadiumstadium');
|
||
|
||
// 计算滚动区域的最大高度
|
||
const scrollMaxHeight = isKeyboardVisible
|
||
? `calc(100vh - ${keyboardHeight+40}px)`
|
||
: '60vh'
|
||
|
||
return (
|
||
<View className='stadium-detail'>
|
||
<ScrollView
|
||
className='stadium-detail-scroll'
|
||
refresherBackground="#FAFAFA"
|
||
scrollY={!openPicker}
|
||
scrollTop={scrollTop}
|
||
style={{ maxHeight: scrollMaxHeight }}
|
||
>
|
||
{/* 已选球场 */}
|
||
<View
|
||
className={`stadium-item`}
|
||
onClick={() => handleMapLocation()}
|
||
>
|
||
<View className='stadium-item-left'>
|
||
<Image src={images.ICON_STADIUM} className='stadium-icon' />
|
||
</View>
|
||
<View className='stadium-item-right'>
|
||
<View className='stadium-name'>{formData.name}</View>
|
||
<View className='stadium-address'>
|
||
<Text>{calculateDistance(formData.istance || null) + ' · '}</Text>
|
||
<Text>{formData.address}</Text>
|
||
<Image src={images.ICON_ARRORW_SMALL} className='stadium-map-icon' />
|
||
</View>
|
||
</View>
|
||
</View>
|
||
|
||
{stadiumInfo.map((item) => {
|
||
if (item.type === 'tags') {
|
||
const selected = getSelectedByLabel(item.label)
|
||
return (
|
||
<SectionContainer key={item.label} title={item.label} prop={item.prop}>
|
||
{item.options.map((opt) => (
|
||
<View
|
||
key={opt}
|
||
className={`option-btn ${selected === opt ? 'selected' : ''}`}
|
||
onClick={() => updateFormData(item.prop, opt)}
|
||
>
|
||
<Text className='option-text'>{opt}</Text>
|
||
</View>
|
||
))}
|
||
</SectionContainer>
|
||
)
|
||
}
|
||
|
||
if (item.type === 'textareaTag') {
|
||
return (
|
||
<SectionContainer key={item.label} title={item.label} prop={item.prop}>
|
||
<View className='textarea-tag-container'>
|
||
<TextareaTag
|
||
value={formData[item.prop]}
|
||
onChange={(value) => {
|
||
updateFormData(item.prop, value)
|
||
}}
|
||
// onBlur={() => {
|
||
// }}
|
||
onFocus={() => {
|
||
changeTextarea(true)
|
||
}}
|
||
placeholder='有其他场地信息可备注'
|
||
options={(item.options || []).map((o) => ({ label: o, value: o }))}
|
||
/>
|
||
</View>
|
||
</SectionContainer>
|
||
)
|
||
}
|
||
|
||
if (item.type === 'image') {
|
||
return (
|
||
<SectionContainer key={item.label} title={item.label} prop={item.prop} type={item.type}>
|
||
<UploadCover
|
||
value={formData[item.prop]}
|
||
changePicker={changePicker}
|
||
uploadText="添加截图"
|
||
onChange={(value) => {
|
||
if (value instanceof Function) {
|
||
const newValue = value(formData[item.prop])
|
||
updateFormData(item.prop, newValue)
|
||
} else {
|
||
updateFormData(item.prop, value)
|
||
}
|
||
}}
|
||
maxCount={9}
|
||
source={['album', 'history']}
|
||
align='left'
|
||
tag="screenshot"
|
||
/>
|
||
</SectionContainer>
|
||
)
|
||
}
|
||
|
||
return null
|
||
})}
|
||
</ScrollView>
|
||
</View>
|
||
)
|
||
})
|
||
|
||
export default StadiumDetail |