修改发布
This commit is contained in:
@@ -53,6 +53,7 @@
|
||||
"@tarojs/runtime": "4.1.5",
|
||||
"@tarojs/shared": "4.1.5",
|
||||
"@tarojs/taro": "4.1.5",
|
||||
"dayjs": "^1.11.13",
|
||||
"qqmap-wx-jssdk": "^1.0.0",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
export { default } from './FormBasicInfo'
|
||||
export type { FormBasicInfoProps } from './FormBasicInfo'
|
||||
@@ -24,6 +24,7 @@ const TextareaTag: React.FC<TextareaTagProps> = ({
|
||||
}) => {
|
||||
// 处理输入框变化
|
||||
const [tags, setTags] = useState<string[]>([])
|
||||
console.log(value, 'options')
|
||||
const handleInputChange = useCallback((e: any) => {
|
||||
onChange(e.detail.value)
|
||||
}, [onChange])
|
||||
@@ -43,6 +44,7 @@ const TextareaTag: React.FC<TextareaTagProps> = ({
|
||||
onChange(newValue)
|
||||
}, [value, onChange])
|
||||
|
||||
console.log(options, 'options')
|
||||
return (
|
||||
<View className='textarea-tag'>
|
||||
{/* 选择选项 */}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React from 'react'
|
||||
import { View, Text, Picker } from '@tarojs/components'
|
||||
import { getDate, getTime } from '@/utils/timeUtils'
|
||||
import './TimeSelector.scss'
|
||||
|
||||
export interface TimeRange {
|
||||
startDate: string
|
||||
startTime: string
|
||||
endTime: string
|
||||
start_time: string
|
||||
end_time: string
|
||||
}
|
||||
|
||||
interface TimeSelectorProps {
|
||||
@@ -15,40 +15,13 @@ interface TimeSelectorProps {
|
||||
|
||||
const TimeSelector: React.FC<TimeSelectorProps> = ({
|
||||
value = {
|
||||
startDate: '',
|
||||
startTime: '',
|
||||
endTime: ''
|
||||
start_time: '',
|
||||
end_time: ''
|
||||
},
|
||||
onChange
|
||||
}) => {
|
||||
// 格式化日期显示
|
||||
const formatDate = (dateStr: string) => {
|
||||
return dateStr.replace(/-/g, '年').replace(/-/g, '月') + '日'
|
||||
}
|
||||
|
||||
// 处理开始日期变化
|
||||
const handleStartDateChange = (e: any) => {
|
||||
onChange({
|
||||
...value,
|
||||
startDate: e.detail.value
|
||||
})
|
||||
}
|
||||
|
||||
// 处理开始时间变化
|
||||
const handleStartTimeChange = (e: any) => {
|
||||
onChange({
|
||||
...value,
|
||||
startTime: e.detail.value
|
||||
})
|
||||
}
|
||||
|
||||
// 处理结束时间变化
|
||||
const handleEndTimeChange = (e: any) => {
|
||||
onChange({
|
||||
...value,
|
||||
endTime: e.detail.value
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<View className='time-selector'>
|
||||
@@ -61,8 +34,8 @@ const TimeSelector: React.FC<TimeSelectorProps> = ({
|
||||
<View className='time-content'>
|
||||
<Text className='time-label'>开始时间</Text>
|
||||
<view className='time-text-wrapper'>
|
||||
<Text className='time-text'>2025年11月23日</Text>
|
||||
<Text className='time-text time-am'>8:00 AM</Text>
|
||||
<Text className='time-text'>{getDate(value.start_time)}</Text>
|
||||
<Text className='time-text time-am'>{getTime(value.start_time)}</Text>
|
||||
</view>
|
||||
</View>
|
||||
</View>
|
||||
@@ -75,8 +48,7 @@ const TimeSelector: React.FC<TimeSelectorProps> = ({
|
||||
<View className='time-content'>
|
||||
<Text className='time-label'>结束时间</Text>
|
||||
<view className='time-text-wrapper'>
|
||||
<Text className='time-text'>2025年11月23日</Text>
|
||||
<Text className='time-text time-am'>8:00 AM</Text>
|
||||
<Text className='time-text time-am'>{getTime(value.end_time)}</Text>
|
||||
</view>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export { default } from './TitleInput'
|
||||
@@ -3,14 +3,14 @@ import { View } from '@tarojs/components'
|
||||
import { TextArea } from '@nutui/nutui-react-taro'
|
||||
import './index.scss'
|
||||
|
||||
interface TitleInputProps {
|
||||
interface TitleTextareaProps {
|
||||
value: string
|
||||
onChange: (value: string) => void
|
||||
maxLength?: number
|
||||
placeholder?: string
|
||||
}
|
||||
|
||||
const TitleInput: React.FC<TitleInputProps> = ({
|
||||
const TitleTextarea: React.FC<TitleTextareaProps> = ({
|
||||
value,
|
||||
onChange,
|
||||
maxLength = 20,
|
||||
@@ -32,4 +32,4 @@ const TitleInput: React.FC<TitleInputProps> = ({
|
||||
)
|
||||
}
|
||||
|
||||
export default TitleInput
|
||||
export default TitleTextarea
|
||||
1
src/components/TitleTextarea/index.ts
Normal file
1
src/components/TitleTextarea/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './TitleTextarea'
|
||||
@@ -2,12 +2,11 @@ import ActivityTypeSwitch from './ActivityTypeSwitch'
|
||||
import TextareaTag from './TextareaTag'
|
||||
import FormSwitch from './FormSwitch'
|
||||
import ImageUpload from './ImageUpload'
|
||||
import FormBasicInfo from './FormBasicInfo'
|
||||
import Range from './Range'
|
||||
import NumberInterval from './NumberInterval'
|
||||
import { SelectStadium, StadiumDetail } from './SelectStadium'
|
||||
|
||||
import TimeSelector from './TimeSelector'
|
||||
import TitleInput from './TitleInput'
|
||||
import TitleTextarea from './TitleTextarea'
|
||||
import CommonPopup from './CommonPopup'
|
||||
|
||||
export {
|
||||
@@ -15,13 +14,10 @@ export {
|
||||
TextareaTag,
|
||||
FormSwitch,
|
||||
ImageUpload,
|
||||
FormBasicInfo,
|
||||
Range,
|
||||
NumberInterval,
|
||||
SelectStadium,
|
||||
TimeSelector,
|
||||
TitleInput,
|
||||
StadiumDetail,
|
||||
TitleTextarea,
|
||||
CommonPopup
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { type TimeRange } from './TimeSelector'
|
||||
import { type Stadium } from './SelectStadium'
|
||||
import { type ActivityType } from './ActivityTypeSwitch'
|
||||
import { type CoverImage } from './ImageUpload'
|
||||
export type { TimeRange, Stadium, ActivityType, CoverImage }
|
||||
export type { TimeRange, ActivityType, CoverImage }
|
||||
@@ -43,7 +43,7 @@ export interface FormFieldConfig {
|
||||
// 发布球局表单配置
|
||||
export const publishBallFormSchema: FormFieldConfig[] = [
|
||||
{
|
||||
key: 'coverImages',
|
||||
key: 'image_list',
|
||||
label: '活动封页',
|
||||
type: FieldType.UPLOADIMAGE,
|
||||
placeholder: '请选择活动类型',
|
||||
@@ -59,115 +59,101 @@ export const publishBallFormSchema: FormFieldConfig[] = [
|
||||
placeholder: '好的标题更吸引人哦',
|
||||
required: true,
|
||||
props: {
|
||||
maxLength: 80
|
||||
},
|
||||
rules: [
|
||||
{ required: true, message: '请输入活动标题' },
|
||||
{ max: 20, message: '标题不能超过20个字符' }
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'date',
|
||||
label: '',
|
||||
type: FieldType.TIMEINTERVAL,
|
||||
placeholder: '请选择活动日期',
|
||||
required: true,
|
||||
rules: [
|
||||
{ required: true, message: '请选择活动日期' }
|
||||
]
|
||||
maxLength: 20
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'timeRange',
|
||||
label: '活动信息',
|
||||
type: FieldType.ACTIVITYINFO,
|
||||
placeholder: '请选择活动时间',
|
||||
required: true,
|
||||
rules: [
|
||||
{ required: true, message: '请选择活动时间' }
|
||||
],
|
||||
children: [
|
||||
{
|
||||
key: 'fee',
|
||||
label: '费用',
|
||||
iconType: 'ICON_COST',
|
||||
type: FieldType.NUMBER,
|
||||
placeholder: '请输入活动费用(元)',
|
||||
defaultValue: 0,
|
||||
rules: [
|
||||
{ min: 0, message: '费用不能为负数' },
|
||||
{ max: 1000, message: '费用不能超过1000元' }
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'location',
|
||||
label: '地点',
|
||||
iconType: 'ICON_LOCATION',
|
||||
type: FieldType.LOCATION,
|
||||
placeholder: '请选择活动地点',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: 'sport',
|
||||
label: '玩法',
|
||||
iconType: 'ICON_GAMEPLAY',
|
||||
type: FieldType.SELECT,
|
||||
placeholder: '请选择玩法',
|
||||
required: true,
|
||||
options: [
|
||||
{ label: '篮球', value: 'basketball' },
|
||||
{ label: '足球', value: 'football' },
|
||||
{ label: '羽毛球', value: 'badminton' },
|
||||
{ label: '网球', value: 'tennis' },
|
||||
{ label: '乒乓球', value: 'pingpong' },
|
||||
{ label: '排球', value: 'volleyball' }
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'minParticipants',
|
||||
label: '人数要求',
|
||||
type: FieldType.NUMBERINTERVAL,
|
||||
placeholder: '请输入最少参与人数',
|
||||
defaultValue: 1,
|
||||
props: {
|
||||
showSummary: true,
|
||||
summary: '最少1人,最多4人',
|
||||
},
|
||||
rules: [
|
||||
{ min: 1, message: '最少参与人数不能为0' },
|
||||
{ max: 4, message: '最少参与人数不能超过100人' }
|
||||
],
|
||||
label: '',
|
||||
type: FieldType.TIMEINTERVAL,
|
||||
placeholder: '请选择活动日期',
|
||||
required: true
|
||||
},
|
||||
// {
|
||||
// key: 'activityInfo',
|
||||
// label: '活动信息',
|
||||
// type: FieldType.ACTIVITYINFO,
|
||||
// placeholder: '请选择活动时间',
|
||||
// required: true,
|
||||
// rules: [
|
||||
// { required: true, message: '请选择活动时间' }
|
||||
// ],
|
||||
// children: [
|
||||
// {
|
||||
// key: 'price',
|
||||
// label: '费用',
|
||||
// iconType: 'ICON_COST',
|
||||
// type: FieldType.NUMBER,
|
||||
// placeholder: '请输入活动费用(元)',
|
||||
// defaultValue: 0,
|
||||
// rules: [
|
||||
// { min: 0, message: '费用不能为负数' },
|
||||
// { max: 1000, message: '费用不能超过1000元' }
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// key: 'location',
|
||||
// label: '地点',
|
||||
// iconType: 'ICON_LOCATION',
|
||||
// type: FieldType.LOCATION,
|
||||
// placeholder: '请选择活动地点',
|
||||
// required: true,
|
||||
// },
|
||||
// {
|
||||
// key: 'play_type',
|
||||
// label: '玩法',
|
||||
// iconType: 'ICON_GAMEPLAY',
|
||||
// type: FieldType.SELECT,
|
||||
// placeholder: '请选择玩法',
|
||||
// required: true,
|
||||
// options: [
|
||||
// { label: '篮球', value: 'basketball' },
|
||||
// { label: '足球', value: 'football' },
|
||||
// { label: '羽毛球', value: 'badminton' },
|
||||
// { label: '网球', value: 'tennis' },
|
||||
// { label: '乒乓球', value: 'pingpong' },
|
||||
// { label: '排球', value: 'volleyball' }
|
||||
// ],
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// key: 'players',
|
||||
// label: '人数要求',
|
||||
// type: FieldType.NUMBERINTERVAL,
|
||||
// placeholder: '请输入最少参与人数',
|
||||
// defaultValue: 1,
|
||||
// props: {
|
||||
// showSummary: true,
|
||||
// summary: '最少1人,最多4人',
|
||||
// }
|
||||
// },
|
||||
|
||||
// {
|
||||
// key: 'skill_level',
|
||||
// label: 'NTRP 水平要求',
|
||||
// type: FieldType.RANGE,
|
||||
// placeholder: '请选择开始时间',
|
||||
// required: true,
|
||||
// props: {
|
||||
// showTitle: false,
|
||||
// showSummary: true,
|
||||
// className: 'ntrp-range',
|
||||
// step: 0.5,
|
||||
// min: 1.0,
|
||||
// max: 5.0,
|
||||
// }
|
||||
// },
|
||||
{
|
||||
key: 'ntpLevel',
|
||||
label: 'NTRP 水平要求',
|
||||
type: FieldType.RANGE,
|
||||
placeholder: '请选择开始时间',
|
||||
required: true,
|
||||
props: {
|
||||
showTitle: false,
|
||||
showSummary: true,
|
||||
className: 'ntrp-range',
|
||||
step: 0.5,
|
||||
min: 1.0,
|
||||
max: 5.0,
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'additionalRequirements',
|
||||
key: 'descriptionInfo',
|
||||
label: '补充要求(选填)',
|
||||
type: FieldType.TEXTAREATAG,
|
||||
placeholder: '补充性别偏好、特殊要求和注意事项等信息',
|
||||
required: true,
|
||||
options:[
|
||||
{ label: '仅限男生', value: 'beginner' },
|
||||
{ label: '仅限女生', value: 'intermediate' },
|
||||
{ label: '性别不限', value: 'advanced' }
|
||||
],
|
||||
rules: [
|
||||
{ max: 100, message: '补充要求不能超过100个字符' }
|
||||
{ label: '仅限男生', value: '仅限男生' },
|
||||
{ label: '仅限女生', value: '仅限女生' },
|
||||
{ label: '性别不限', value: '性别不限' }
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -180,9 +166,6 @@ export const publishBallFormSchema: FormFieldConfig[] = [
|
||||
subTitle: '开启自动候补逻辑',
|
||||
showToast: true,
|
||||
description: '开启后,当活动人数不足时,系统会自动将活动状态改为“候补”,并通知用户。',
|
||||
},
|
||||
rules: [
|
||||
{ required: true, message: '请选择开启自动候补逻辑' }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -75,6 +75,9 @@
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-align: right;
|
||||
&.selected{
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
.arrow{
|
||||
width: 16px;
|
||||
@@ -1,33 +1,36 @@
|
||||
import React from 'react'
|
||||
import React, { useState } from 'react'
|
||||
import { View, Text, Input, Image, Picker } from '@tarojs/components'
|
||||
import { Stadium } from '../SelectStadium'
|
||||
import PopupGameplay from '../PopupGameplay'
|
||||
import img from '@/config/images';
|
||||
import './FormBasicInfo.scss'
|
||||
import { FormFieldConfig } from '@/config/formSchema/publishBallFormSchema';
|
||||
|
||||
interface FormBasicInfoProps {
|
||||
fee: string
|
||||
location: string
|
||||
gameplay: string
|
||||
selectedStadium: Stadium | null
|
||||
onFeeChange: (value: string) => void
|
||||
onLocationChange: (value: string) => void
|
||||
onGameplayChange: (value: string) => void
|
||||
onStadiumSelect: () => void
|
||||
value: any
|
||||
onChange: (value: any) => void
|
||||
children: FormFieldConfig[]
|
||||
}
|
||||
|
||||
const FormBasicInfo: React.FC<FormBasicInfoProps> = ({
|
||||
fee,
|
||||
location,
|
||||
gameplay,
|
||||
selectedStadium,
|
||||
onFeeChange,
|
||||
onLocationChange,
|
||||
onGameplayChange,
|
||||
onStadiumSelect,
|
||||
value,
|
||||
onChange,
|
||||
children
|
||||
}) => {
|
||||
const [gameplayVisible, setGameplayVisible] = useState(false)
|
||||
|
||||
const handleGameplaySelect = () => {
|
||||
setGameplayVisible(true)
|
||||
}
|
||||
|
||||
const handleGameplayConfirm = (selectedGameplay: string) => {
|
||||
onGameplayChange(selectedGameplay)
|
||||
setGameplayVisible(false)
|
||||
}
|
||||
|
||||
const handleGameplayClose = () => {
|
||||
setGameplayVisible(false)
|
||||
}
|
||||
|
||||
const renderChildren = () => {
|
||||
return children.map((child: any, index: number) => {
|
||||
return <View className='form-item'>
|
||||
@@ -43,8 +46,8 @@ const FormBasicInfo: React.FC<FormBasicInfoProps> = ({
|
||||
placeholder='请输入'
|
||||
placeholderClass='title-placeholder'
|
||||
type='digit'
|
||||
value={fee}
|
||||
onInput={(e) => onFeeChange(e.detail.value)}
|
||||
value={value[child.key]}
|
||||
onInput={(e) => onChange(child.key, e.detail.value)}
|
||||
/>
|
||||
<Text className='unit'>元/每人</Text>
|
||||
</View>
|
||||
@@ -53,9 +56,9 @@ const FormBasicInfo: React.FC<FormBasicInfoProps> = ({
|
||||
{
|
||||
index === 1 && (<View className='form-wrapper'>
|
||||
<Text className='form-item-label'>{child.label}</Text>
|
||||
<View className='form-right-wrapper' onClick={onStadiumSelect}>
|
||||
<Text className={`right-text ${selectedStadium ? 'selected' : ''}`}>
|
||||
{selectedStadium ? selectedStadium.name : '请选择'}
|
||||
<View className='form-right-wrapper' onClick={() => {}}>
|
||||
<Text className={`right-text ${value[child.key] ? 'selected' : ''}`}>
|
||||
{value[child.key] ? value[child.key] : '请选择'}
|
||||
</Text>
|
||||
<Image src={img.ICON_ARROW_RIGHT} className='arrow'/>
|
||||
</View>
|
||||
@@ -64,9 +67,9 @@ const FormBasicInfo: React.FC<FormBasicInfoProps> = ({
|
||||
{
|
||||
index === 2 && ( <View className='form-wrapper'>
|
||||
<Text className='form-item-label'>{child.label}</Text>
|
||||
<View className='form-right-wrapper'>
|
||||
<Text className={`right-text ${gameplay ? 'selected' : ''}`}>
|
||||
{gameplay ? gameplay : '请选择'}
|
||||
<View className='form-right-wrapper' onClick={handleGameplaySelect}>
|
||||
<Text className={`right-text ${value[child.key] ? 'selected' : ''}`}>
|
||||
{value[child.key] ? value[child.key] : '请选择'}
|
||||
</Text>
|
||||
<Image src={img.ICON_ARROW_RIGHT} className='arrow'/>
|
||||
</View>
|
||||
@@ -78,7 +81,15 @@ const FormBasicInfo: React.FC<FormBasicInfoProps> = ({
|
||||
return (
|
||||
<View className='form-basic-info'>
|
||||
{/* 费用 */}
|
||||
{renderChildren()}
|
||||
{/* {renderChildren()} */}
|
||||
|
||||
{/* 玩法选择弹窗 */}
|
||||
{/* <PopupGameplay
|
||||
visible={gameplayVisible}
|
||||
onClose={handleGameplayClose}
|
||||
onConfirm={handleGameplayConfirm}
|
||||
selectedGameplay={value[children[2].key]}
|
||||
/> */}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
1
src/pages/publishBall/components/FormBasicInfo/index.ts
Normal file
1
src/pages/publishBall/components/FormBasicInfo/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './FormBasicInfo'
|
||||
@@ -0,0 +1,34 @@
|
||||
.optionsList {
|
||||
padding: 26px 15px 16px 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.optionItem {
|
||||
display: flex;
|
||||
min-height: 40px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex: 1 0 0;
|
||||
border-radius: 999px;
|
||||
border: 0.5px solid rgba(0, 0, 0, 0.12);
|
||||
background: #FFF;
|
||||
}
|
||||
|
||||
|
||||
.optionItem.selected {
|
||||
border: 0.5px solid rgba(0, 0, 0, 0.06);
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
background: #000;
|
||||
.optionText {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.optionText {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { View, Text } from '@tarojs/components'
|
||||
import { CommonPopup } from '../../../../components'
|
||||
import styles from './PopupGameplay.module.scss'
|
||||
|
||||
interface PopupGameplayProps {
|
||||
visible: boolean
|
||||
onClose: () => void
|
||||
onConfirm: (selectedGameplay: string) => void
|
||||
selectedGameplay?: string
|
||||
}
|
||||
|
||||
export default function PopupGameplay({ visible, onClose, onConfirm, selectedGameplay = '不限' }: PopupGameplayProps) {
|
||||
const [selectedOption, setSelectedOption] = useState(selectedGameplay)
|
||||
|
||||
const options = ['不限', '单打', '双打', '拉球']
|
||||
|
||||
useEffect(() => {
|
||||
if (visible && selectedGameplay) {
|
||||
setSelectedOption(selectedGameplay)
|
||||
}
|
||||
}, [visible, selectedGameplay])
|
||||
|
||||
const handleOptionSelect = (option: string) => {
|
||||
setSelectedOption(option)
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
onClose()
|
||||
}
|
||||
|
||||
const handleConfirm = () => {
|
||||
onConfirm(selectedOption)
|
||||
}
|
||||
|
||||
return (
|
||||
<CommonPopup
|
||||
visible={visible}
|
||||
onClose={handleClose}
|
||||
showHeader={false}
|
||||
onConfirm={handleConfirm}
|
||||
confirmText='确定'
|
||||
cancelText='取消'
|
||||
>
|
||||
<View className={styles.optionsList}>
|
||||
{options.map((option) => (
|
||||
<View
|
||||
key={option}
|
||||
className={`${styles.optionItem} ${selectedOption === option ? styles.selected : ''}`}
|
||||
onClick={() => handleOptionSelect(option)}
|
||||
>
|
||||
<Text className={styles.optionText}>{option}</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</CommonPopup>
|
||||
)
|
||||
}
|
||||
1
src/pages/publishBall/components/PopupGameplay/index.ts
Normal file
1
src/pages/publishBall/components/PopupGameplay/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './PopupGameplay'
|
||||
@@ -1,9 +1,8 @@
|
||||
import React, { useState } from 'react'
|
||||
import { View, Text, Input, ScrollView, Image } from '@tarojs/components'
|
||||
import { Popup } from '@nutui/nutui-react-taro'
|
||||
import Taro from '@tarojs/taro'
|
||||
import StadiumDetail from './StadiumDetail'
|
||||
import CommonPopup from '../CommonPopup'
|
||||
import { CommonPopup } from '../../../../components'
|
||||
import './SelectStadium.scss'
|
||||
import images from '@/config/images'
|
||||
|
||||
@@ -6,28 +6,52 @@ import PublishForm from './publishForm'
|
||||
import { publishBallFormSchema } from '../../config/formSchema/publishBallFormSchema';
|
||||
import { PublishBallFormData } from '../../../types/publishBall';
|
||||
import PublishService from '@/services/publishService';
|
||||
import { getNextHourTime, getEndTime } from '@/utils/timeUtils';
|
||||
import styles from './index.module.scss'
|
||||
import images from '@/config/images'
|
||||
|
||||
const defaultFormData: PublishBallFormData = {
|
||||
title: '',
|
||||
image_list: [],
|
||||
timeRange: {
|
||||
start_time: getNextHourTime(),
|
||||
end_time: getEndTime(getNextHourTime())
|
||||
},
|
||||
activityInfo: {
|
||||
play_type: '不限',
|
||||
price: '',
|
||||
venue_id: null,
|
||||
location_name: '',
|
||||
location: '',
|
||||
latitude: '',
|
||||
longitude: '',
|
||||
court_type: '',
|
||||
court_surface: '',
|
||||
venue_description_tag: [],
|
||||
venue_description: '',
|
||||
venue_image_list: [],
|
||||
},
|
||||
players: {
|
||||
current_players: 0,
|
||||
max_players: 0,
|
||||
},
|
||||
skill_level: {
|
||||
skill_level_max: 5.0,
|
||||
skill_level_min: 1.0,
|
||||
},
|
||||
descriptionInfo: {
|
||||
description: '',
|
||||
description_tag: [],
|
||||
},
|
||||
is_substitute_supported: false,
|
||||
is_wechat_contact: false,
|
||||
wechat_contact: ''
|
||||
}
|
||||
|
||||
const PublishBall: React.FC = () => {
|
||||
const [activityType, setActivityType] = useState<ActivityType>('individual')
|
||||
const [formData, setFormData] = useState<PublishBallFormData[]>([
|
||||
{
|
||||
title: '',
|
||||
timeRange: {
|
||||
startDate: '2025-11-23',
|
||||
startTime: '08:00',
|
||||
endTime: '10:00'
|
||||
},
|
||||
fee: '',
|
||||
location: '',
|
||||
gameplay: '',
|
||||
minParticipants: 1,
|
||||
maxParticipants: 4,
|
||||
ntpLevel: [2.0, 4.0],
|
||||
additionalRequirements: '',
|
||||
autoDegrade: false
|
||||
}
|
||||
defaultFormData
|
||||
])
|
||||
|
||||
// 删除确认弹窗状态
|
||||
@@ -56,21 +80,12 @@ const PublishBall: React.FC = () => {
|
||||
}
|
||||
|
||||
const handleAdd = () => {
|
||||
const newStartTime = getNextHourTime()
|
||||
setFormData(prev => [...prev, {
|
||||
...defaultFormData,
|
||||
title: '',
|
||||
timeRange: {
|
||||
startDate: '2025-11-23',
|
||||
startTime: '08:00',
|
||||
endTime: '10:00'
|
||||
},
|
||||
fee: '',
|
||||
location: '',
|
||||
gameplay: '',
|
||||
minParticipants: 1,
|
||||
maxParticipants: 4,
|
||||
ntpLevel: [2.0, 4.0],
|
||||
additionalRequirements: '',
|
||||
autoDegrade: false
|
||||
start_time: newStartTime,
|
||||
end_time: getEndTime(newStartTime)
|
||||
}])
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,11 @@ import React, { useState } from 'react'
|
||||
import { View, Text } from '@tarojs/components'
|
||||
|
||||
import Taro from '@tarojs/taro'
|
||||
import { ImageUpload, Range, TimeSelector, TextareaTag, SelectStadium, NumberInterval, TitleInput, FormBasicInfo, FormSwitch } from '../../components'
|
||||
import { type Stadium, type CoverImage } from '../../components/index.types'
|
||||
import { ImageUpload, Range, TimeSelector, TextareaTag, NumberInterval, TitleTextarea, FormSwitch } from '../../components'
|
||||
import { SelectStadium } from './components/SelectStadium'
|
||||
import FormBasicInfo from './components/FormBasicInfo'
|
||||
import { type CoverImage } from '../../components/index.types'
|
||||
import { Stadium } from './components/SelectStadium'
|
||||
import { FormFieldConfig, FieldType } from '../../config/formSchema/publishBallFormSchema'
|
||||
import { PublishBallFormData } from '../../../types/publishBall';
|
||||
|
||||
@@ -11,7 +14,7 @@ import styles from './index.module.scss'
|
||||
|
||||
// 组件映射器
|
||||
const componentMap = {
|
||||
[FieldType.TEXT]: TitleInput,
|
||||
[FieldType.TEXT]: TitleTextarea,
|
||||
[FieldType.TIMEINTERVAL]: TimeSelector,
|
||||
[FieldType.RANGE]: Range,
|
||||
[FieldType.TEXTAREATAG]: TextareaTag,
|
||||
@@ -95,7 +98,7 @@ const PublishForm: React.FC<{
|
||||
const Component = componentMap[item.type]
|
||||
const optionProps = {
|
||||
...item.props,
|
||||
...(item.key === 'additionalRequirements' ? { options: item.options } : {}),
|
||||
...(item.type === FieldType.TEXTAREATAG ? { options: item.options } : {}),
|
||||
...(item.props?.className ? { className: styles[item.props.className] } : {})
|
||||
}
|
||||
console.log(item.props?.className)
|
||||
@@ -120,15 +123,10 @@ const PublishForm: React.FC<{
|
||||
{/* 费用地点玩法区域 - 合并白色块 */}
|
||||
<View className={styles['bg-section']}>
|
||||
<FormBasicInfo
|
||||
fee={formData.fee}
|
||||
location={formData.location}
|
||||
gameplay={formData.gameplay}
|
||||
selectedStadium={selectedStadium}
|
||||
children={item.children || []}
|
||||
onFeeChange={(value) => updateFormData('fee', value)}
|
||||
onLocationChange={(value) => updateFormData('location', value)}
|
||||
onGameplayChange={(value) => updateFormData('gameplay', value)}
|
||||
onStadiumSelect={() => setShowStadiumSelector(true)}
|
||||
value={formData[item.key]}
|
||||
onChange={(value) => updateFormData(item.key as keyof PublishBallFormData, value)}
|
||||
{...optionProps}
|
||||
/>
|
||||
</View>
|
||||
</>
|
||||
|
||||
44
src/utils/timeUtils.ts
Normal file
44
src/utils/timeUtils.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
/**
|
||||
* 获取下一个整点时间
|
||||
* @returns 格式为 YYYY-MM-DD HH:mm 的字符串
|
||||
*/
|
||||
export const getNextHourTime = (): string => {
|
||||
const now = dayjs()
|
||||
const nextHour = now.add(1, 'hour').startOf('hour')
|
||||
return nextHour.format('YYYY-MM-DD HH:mm')
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据开始时间计算结束时间(2小时后)
|
||||
* @param startTime 开始时间,格式为 YYYY-MM-DD HH:mm
|
||||
* @returns 格式为 YYYY-MM-DD HH:mm 的字符串
|
||||
*/
|
||||
export const getEndTime = (startTime: string): string => {
|
||||
const startDateTime = dayjs(startTime)
|
||||
const endDateTime = startDateTime.add(2, 'hour')
|
||||
return endDateTime.format('YYYY-MM-DD HH:mm')
|
||||
}
|
||||
|
||||
|
||||
export const getDate = (date: string): string => {
|
||||
return dayjs(date).format('YYYY年MM月DD日')
|
||||
}
|
||||
|
||||
export const getTime = (time: string): string => {
|
||||
const timeObj = dayjs(time)
|
||||
const hour = timeObj.hour()
|
||||
const minute = timeObj.minute()
|
||||
|
||||
// 判断是上午还是下午
|
||||
const period = hour < 12 ? 'AM' : 'PM'
|
||||
|
||||
// 转换为12小时制
|
||||
const hour12 = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour
|
||||
|
||||
// 格式化分钟,保证两位数
|
||||
const minuteStr = minute.toString().padStart(2, '0')
|
||||
|
||||
return `${hour12}:${minuteStr} ${period}`
|
||||
}
|
||||
@@ -1,15 +1,38 @@
|
||||
import { TimeRange } from "@/components/index.types"
|
||||
|
||||
|
||||
export interface PublishBallFormData {
|
||||
title: string
|
||||
timeRange: TimeRange
|
||||
fee: string
|
||||
location: string
|
||||
gameplay: string
|
||||
minParticipants: number
|
||||
maxParticipants: number
|
||||
ntpLevel: number[]
|
||||
additionalRequirements: string
|
||||
autoDegrade: boolean
|
||||
title: string // 球局标题
|
||||
image_list: Array<string>[] // 球局封面
|
||||
timeRange: {
|
||||
start_time: string,
|
||||
end_time: string
|
||||
}
|
||||
activityInfo: {
|
||||
play_type: string // 玩法类型
|
||||
price: number | string // 价格
|
||||
venue_id?: number | null // 场地id
|
||||
location_name?: string // 场地名称
|
||||
location?: string // 场地地址
|
||||
latitude?: string // 纬度
|
||||
longitude?: string // 经度
|
||||
court_type?: string // 场地类型 1: 室内 2: 室外
|
||||
court_surface?: string // 场地表面 1: 硬地 2: 红土 3: 草地
|
||||
venue_description_tag?: Array<string>[] // 场地描述标签
|
||||
venue_description?: string // 场地描述
|
||||
venue_image_list?: Array<string>[] // 场地图片
|
||||
}
|
||||
players:{
|
||||
current_players: number // 当前人数
|
||||
max_players: number // 最大人数
|
||||
}
|
||||
skill_level: {
|
||||
skill_level_max: number // 最高水平要求(NTRP)
|
||||
skill_level_min: number //最低水平要求(NTRP)
|
||||
}
|
||||
descriptionInfo: {
|
||||
description: string // 备注
|
||||
description_tag: Array<string>[] // 备注标签
|
||||
}
|
||||
is_substitute_supported: boolean // 是否支持替补
|
||||
is_wechat_contact: boolean // 是否需要微信联系
|
||||
wechat_contact: string // 微信联系
|
||||
}
|
||||
@@ -4099,6 +4099,11 @@ data-view-byte-offset@^1.0.1:
|
||||
es-errors "^1.3.0"
|
||||
is-data-view "^1.0.1"
|
||||
|
||||
dayjs@^1.11.13:
|
||||
version "1.11.13"
|
||||
resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c"
|
||||
integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==
|
||||
|
||||
debug@2.6.9:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||
|
||||
Reference in New Issue
Block a user