修改发布日历

This commit is contained in:
筱野
2025-08-30 22:25:39 +08:00
parent bb6ec8c183
commit fe14e01267
59 changed files with 2151 additions and 2921 deletions

View File

@@ -5,6 +5,7 @@ import Taro from '@tarojs/taro'
// 导入API服务
import demoApi from '../../services/demoApi'
import commonApi from '../../services/commonApi'
import PublishMenu from '../../components/PublishMenu'
import {
useUserStats,
useUserActions
@@ -286,6 +287,13 @@ function Index() {
<Text className='tip-item'> 使</Text>
</View>
</View>
<PublishMenu
onPersonalPublish={() => {
Taro.navigateTo({
url: '/pages/publishBall/index'
})
}}
/>
</View>
)
}

View File

@@ -1,5 +0,0 @@
// import MapPlugin from "src/components/MapDisplay/mapPlugin";
import MapDisplay from "src/components/MapDisplay";
export default function MapDisplayPage() {
return <MapDisplay />
}

View File

@@ -13,7 +13,7 @@ export interface Stadium {
id?: string
name: string
address?: string
istance?: string
distance_km?: number | null | undefined
longitude?: number
latitude?: number
}
@@ -78,6 +78,15 @@ const SelectStadium: React.FC<SelectStadiumProps> = ({
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) => {
@@ -253,7 +262,7 @@ const SelectStadium: React.FC<SelectStadiumProps> = ({
handleItemLocation(stadium)
}}
>
{stadium.istance} ·
{calculateDistance(stadium)} ·
</Text>
<Text
className='stadium-address-text'

View File

@@ -13,7 +13,7 @@ export interface Stadium {
address?: string
longitude?: number
latitude?: number
istance?: string
distance_km?: number | null
court_type?: string
court_surface?: string
description?: string
@@ -99,7 +99,7 @@ const StadiumDetail = forwardRef<StadiumDetailRef, StadiumDetailProps>(({
address: stadium.address,
latitude: stadium.longitude,
longitude: stadium.latitude,
istance: stadium.istance,
istance: stadium.distance_km,
court_type: court_type[0] || '',
court_surface: court_surface[0] || '',
additionalInfo: '',
@@ -116,6 +116,13 @@ const StadiumDetail = forwardRef<StadiumDetailRef, StadiumDetailProps>(({
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 = () => {
@@ -127,7 +134,8 @@ const StadiumDetail = forwardRef<StadiumDetailRef, StadiumDetailProps>(({
name: res.name,
address: res.address,
latitude: res.longitude,
longitude: res.latitude
longitude: res.latitude,
istance: null
})
},
fail: (err) => {
@@ -165,7 +173,7 @@ const StadiumDetail = forwardRef<StadiumDetailRef, StadiumDetailProps>(({
<View className='stadium-item-right'>
<View className='stadium-name'>{formData.name}</View>
<View className='stadium-address'>
<Text>{formData.istance} · </Text>
<Text>{calculateDistance(formData.istance || null)} · </Text>
<Text>{formData.address}</Text>
<Image src={images.ICON_ARRORW_SMALL} className='stadium-map-icon' />
</View>

View File

@@ -25,7 +25,8 @@
align-items: center;
gap: 4px;
color: rgba(60, 60, 67, 0.50);
font-size: 14px;
&-icon{
width: 16px;
height: 16px;
@@ -230,74 +231,7 @@
}
}
// 删除确认弹窗
.delete-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 10000;
&__content {
background: white;
border-radius: 16px;
padding: 24px;
margin: 0 32px;
max-width: 320px;
width: 100%;
text-align: center;
}
&__title {
display: block;
font-size: 18px;
font-weight: 600;
color: theme.$primary-color;
margin-bottom: 8px;
}
&__desc {
display: block;
font-size: 14px;
color: rgba(60, 60, 67, 0.6);
margin-bottom: 24px;
}
&__actions {
display: flex;
gap: 12px;
.delete-modal__btn {
flex: 1;
height: 44px;
border-radius: 12px;
font-size: 16px;
font-weight: 500;
border: none;
cursor: pointer;
transition: all 0.2s ease;
&:first-child {
background: rgba(0, 0, 0, 0.04);
color: rgba(60, 60, 67, 0.8);
}
&:last-child {
background: #FF3B30;
color: white;
}
&:hover {
opacity: 0.8;
}
}
}
}
}
// 旋转动画

View File

@@ -1,7 +1,9 @@
import React, { useState } from 'react'
import React, { useState, useEffect } from 'react'
import { View, Text, Button, Image } from '@tarojs/components'
import Taro from '@tarojs/taro'
import ActivityTypeSwitch, { type ActivityType } from '../../components/ActivityTypeSwitch'
import { type ActivityType } from '../../components/ActivityTypeSwitch'
// import ActivityTypeSwitch, { type ActivityType } from '../../components/ActivityTypeSwitch'
import CommonDialog from '../../components/CommonDialog'
import PublishForm from './publishForm'
import { publishBallFormSchema } from '../../config/formSchema/publishBallFormSchema';
import { PublishBallFormData } from '../../../types/publishBall';
@@ -44,6 +46,29 @@ const defaultFormData: PublishBallFormData = {
const PublishBall: React.FC = () => {
const [activityType, setActivityType] = useState<ActivityType>('individual')
// 获取页面参数并设置导航标题
useEffect(() => {
const currentInstance = Taro.getCurrentInstance()
const params = currentInstance.router?.params
if (params?.type) {
const type = params.type as ActivityType
if (type === 'individual' || type === 'group') {
setActivityType(type)
// 根据type设置导航标题
if (type === 'group') {
Taro.setNavigationBarTitle({
title: '发布畅打活动'
})
} else {
Taro.setNavigationBarTitle({
title: '发布'
})
}
}
handleActivityTypeChange(type)
}
}, [])
const [formData, setFormData] = useState<PublishBallFormData[]>([
defaultFormData
])
@@ -69,10 +94,13 @@ const PublishBall: React.FC = () => {
}
// 处理活动类型变化
const handleActivityTypeChange = (type: ActivityType) => {
setActivityType(type)
if (type === 'group') {
setFormData([defaultFormData])
} else {
setFormData([defaultFormData])
}
}
const handleAdd = () => {
@@ -80,8 +108,10 @@ const PublishBall: React.FC = () => {
setFormData(prev => [...prev, {
...defaultFormData,
title: '',
start_time: newStartTime,
end_time: getEndTime(newStartTime)
timeRange: {
start_time: newStartTime,
end_time: getEndTime(newStartTime)
}
}])
}
@@ -131,7 +161,7 @@ const PublishBall: React.FC = () => {
const validateFormData = (formData: PublishBallFormData) => {
const { activityInfo, image_list, title } = formData;
const { play_type, price, location_name } = activityInfo;
if (!image_list.length) {
if (!image_list?.length) {
Taro.showToast({
title: `请上传活动封面`,
icon: 'none'
@@ -145,21 +175,21 @@ const PublishBall: React.FC = () => {
})
return false
}
if (!price) {
if (!price || (typeof price === 'number' && price <= 0) || (typeof price === 'string' && !price.trim())) {
Taro.showToast({
title: `请输入费用`,
icon: 'none'
})
return false
}
if (!play_type) {
if (!play_type || !play_type.trim()) {
Taro.showToast({
title: `请选择玩法类型`,
icon: 'none'
})
return false
}
if (!location_name) {
if (!location_name || !location_name.trim()) {
Taro.showToast({
title: `请选择场地`,
icon: 'none'
@@ -202,16 +232,46 @@ const PublishBall: React.FC = () => {
})
}
}
if (activityType === 'group') {
const isValid = formData.every(item => validateFormData(item))
if (!isValid) {
return
}
formData.forEach(async (item) => {
const { activityInfo, descriptionInfo, timeRange, players, skill_level, ...rest } = item;
const options = {
...rest,
...activityInfo,
...descriptionInfo,
...timeRange,
max_players: players[1],
current_players: players[0],
skill_level_min: skill_level[0],
skill_level_max: skill_level[1]
}
const res = await PublishService.create_play_pmoothly(options);
if (res.code === 0 && res.data) {
Taro.showToast({
title: '发布成功',
icon: 'success'
})
}
})
}
}
useEffect(() => {
console.log(formData, 'formData');
}, [])
return (
<View className={styles['publish-ball']}>
{/* 活动类型切换 */}
<View className={styles['activity-type-switch']}>
<ActivityTypeSwitch
{/* <ActivityTypeSwitch
value={activityType}
onChange={handleActivityTypeChange}
/>
/> */}
</View>
<View className={styles['publish-ball__scroll']}>
@@ -263,28 +323,15 @@ const PublishBall: React.FC = () => {
</View>
{/* 删除确认弹窗 */}
{deleteConfirm.visible && (
<View className={styles['delete-modal']}>
<View className={styles['delete-modal__content']}>
<Text className={styles['delete-modal__title']}></Text>
<Text className={styles['delete-modal__desc']}></Text>
<View className={styles['delete-modal__actions']}>
<Button
className={styles['delete-modal__btn']}
onClick={closeDeleteConfirm}
>
</Button>
<Button
className={styles['delete-modal__btn']}
onClick={confirmDelete}
>
</Button>
</View>
</View>
</View>
)}
<CommonDialog
visible={deleteConfirm.visible}
cancelText="再想想"
confirmText="确认移除"
onCancel={closeDeleteConfirm}
onConfirm={confirmDelete}
contentTitle="确认移除该场次?"
contentDesc="该操作不可恢复"
/>
{/* 完成按钮 */}
<View className={styles['submit-section']}>

View File

@@ -84,8 +84,36 @@ const PublishForm: React.FC<{
})
}
const getNTRPText = (ntrp: [number, number]) => {
const [min, max] = ntrp
if (min === 1.0 && max === 5.0) {
return '不限'
}
if (min === 5.0 && max === 5.0) {
return '5.0 及以上'
}
if (min === 1.0 && max === 1.0) {
return `${min.toFixed(1)}`
}
if (min > 1.0 && max === 5.0) {
return `${min.toFixed(1)} 以上`
}
if (min === 1.0 && max < 5.0) {
return `${max.toFixed(1)} 以下`
}
if (min > 1.0 && max < 5.0) {
return `${min.toFixed(1)} - ${max.toFixed(1)}之间`
}
return '';
}
const renderSummary = (item: FormFieldConfig) => {
if (item.props?.showSummary) {
if (item.prop === 'skill_level') {
return <Text className={styles['section-summary']}>{getNTRPText(formData.skill_level)}</Text>
}
return <Text className={styles['section-summary']}>{item.props?.summary}</Text>
}
return null