import React, { useState, useEffect } from 'react' import { View, Text, Button, Image } from '@tarojs/components' import { Checkbox } from '@nutui/nutui-react-taro' import Taro from '@tarojs/taro' import { type ActivityType } from '../../components/ActivityTypeSwitch' import CommonDialog from '../../components/CommonDialog' import { withAuth } from '@/components' import PublishForm from './publishForm' import { FormFieldConfig, publishBallFormSchema } from '../../config/formSchema/publishBallFormSchema'; import { PublishBallFormData } from '../../../types/publishBall'; import PublishService from '@/services/publishService'; import { getNextHourTime, getEndTime, delay } from '@/utils'; import { useGlobalState } from "@/store/global" import GeneralNavbar from "@/components/GeneralNavbar" import images from '@/config/images' import { useUserInfo } from '@/store/userStore' import styles from './index.module.scss' import dayjs from 'dayjs' import { usePublishBallData } from '@/store/publishBallStore' 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: { min: 1, max: 1, organizer_joined: true }, skill_level: [1.0, 5.0], descriptionInfo: { description: '', description_tag: [], }, is_substitute_supported: true, wechat: { is_wechat_contact: true, wechat_contact: '', default_wechat_contact: '' } } const PublishBall: React.FC = () => { const [activityType, setActivityType] = useState('individual') const [isSubmitDisabled, setIsSubmitDisabled] = useState(false) const userInfo = useUserInfo(); const publishAiData = usePublishBallData() const { statusNavbarHeightInfo } = useGlobalState(); // 获取页面参数并设置导航标题 const [optionsConfig, setOptionsConfig] = useState(publishBallFormSchema) console.log(userInfo, 'userInfo'); const [formData, setFormData] = useState([defaultFormData]) const [checked, setChecked] = useState(true) const [titleBar, setTitleBar] = useState('发布') // 删除确认弹窗状态 const [deleteConfirm, setDeleteConfirm] = useState<{ visible: boolean; index: number; }>({ visible: false, index: -1 }) // 更新表单数据 const updateFormData = (key: keyof PublishBallFormData, value: any, index: number) => { console.log(key, value, index, 'key, value, index'); setFormData(prev => { const newData = [...prev] newData[index] = { ...newData[index], [key]: value } console.log(newData, 'newData'); return newData }) } // 检查相邻两组数据是否相同 const checkAdjacentDataSame = (formDataArray: PublishBallFormData[]) => { if (formDataArray.length < 2) return false const lastIndex = formDataArray.length - 1 const secondLastIndex = formDataArray.length - 2 const lastData = formDataArray[lastIndex] const secondLastData = formDataArray[secondLastIndex] // 比较关键字段是否相同 return (JSON.stringify(lastData) === JSON.stringify(secondLastData)) } const handleAdd = () => { // 检查最后两组数据是否相同 if (checkAdjacentDataSame(formData)) { Taro.showToast({ title: '信息不可与前序场完全一致', icon: 'none' }) return } const newStartTime = getNextHourTime() setFormData(prev => [...prev, { ...defaultFormData, title: '', timeRange: { start_time: newStartTime, end_time: getEndTime(newStartTime) } }]) } // 复制上一场数据 const handleCopyPrevious = (index: number) => { if (index > 0) { setFormData(prev => { const newData = [...prev] newData[index] = { ...newData[index - 1] } return newData }) Taro.showToast({ title: '复制上一场填入', icon: 'success' }) } } // 删除确认弹窗 const showDeleteConfirm = (index: number) => { setDeleteConfirm({ visible: true, index }) } // 关闭删除确认弹窗 const closeDeleteConfirm = () => { setDeleteConfirm({ visible: false, index: -1 }) } // 确认删除 const confirmDelete = () => { if (deleteConfirm.index >= 0) { setFormData(prev => prev.filter((_, index) => index !== deleteConfirm.index)) closeDeleteConfirm() Taro.showToast({ title: '已删除该场次', icon: 'success' }) } } const validateFormData = (formData: PublishBallFormData, isOnSubmit: boolean = false) => { const { activityInfo, title, timeRange } = formData; const { play_type, price, location_name } = activityInfo; // if (!image_list?.length) { // if (!isOnSubmit) { // Taro.showToast({ // title: `请上传活动封面`, // icon: 'none' // }) // } // return false // } if (!title) { if (!isOnSubmit) { Taro.showToast({ title: `请输入活动标题`, icon: 'none' }) } return false } if (!price || (typeof price === 'number' && price <= 0) || (typeof price === 'string' && !price.trim())) { if (!isOnSubmit) { Taro.showToast({ title: `请输入费用`, icon: 'none' }) } return false } if (!play_type || !play_type.trim()) { if (!isOnSubmit) { Taro.showToast({ title: `请选择玩法类型`, icon: 'none' }) } return false } if (!location_name || !location_name.trim()) { if (!isOnSubmit) { Taro.showToast({ title: `请选择场地`, icon: 'none' }) } return false } // 时间范围校验:结束时间需晚于开始时间,且至少间隔30分钟(支持跨天) if (timeRange?.start_time && timeRange?.end_time) { const start = dayjs(timeRange.start_time) const end = dayjs(timeRange.end_time) if (!end.isAfter(start)) { if (!isOnSubmit) { Taro.showToast({ title: `结束时间需晚于开始时间`, icon: 'none' }) } return false } if (end.isBefore(start.add(30, 'minute'))) { if (!isOnSubmit) { Taro.showToast({ title: `时间间隔至少30分钟`, icon: 'none' }) } return false } } return true } const validateOnSubmit = () => { const isValid = activityType === 'individual' ? validateFormData(formData[0], true) : formData.every(item => validateFormData(item, true)) if (!isValid) { return false } return true } // 提交表单 const handleSubmit = async () => { // 基础验证 console.log(formData, 'formData'); if (activityType === 'individual') { const isValid = validateFormData(formData[0]) if (!isValid) { return } const { activityInfo, descriptionInfo, timeRange, players, skill_level, image_list, wechat, ...rest } = formData[0]; const { min, max, organizer_joined } = players; const options = { ...rest, ...activityInfo, ...descriptionInfo, ...timeRange, max_players: max, min_players: min, organizer_joined, skill_level_min: skill_level[0], skill_level_max: skill_level[1], image_list: image_list.map(item => item.url), is_wechat_contact: wechat.is_wechat_contact, wechat_contact: wechat.wechat_contact || wechat.default_wechat_contact, } const res = await PublishService.createPersonal(options); if (res.code === 0 && res.data) { Taro.showToast({ title: '发布成功', icon: 'success' }) delay(1000) // 如果是个人球局,则跳转到详情页,并自动分享 // 如果是畅打,则跳转第一个球局详情页,并自动分享 @刘杰 Taro.navigateTo({ // @ts-expect-error: id url: `/game_pages/detail/index?id=${(res as any).data?.id || 1}&from=publish&autoShare=1` }) } else { Taro.showToast({ title: res.message, icon: 'none' }) } } if (activityType === 'group') { const isValid = formData.every(item => validateFormData(item)) if (!isValid) { return } if (checkAdjacentDataSame(formData)) { Taro.showToast({ title: '信息不可与前序场完全一致', icon: 'none' }) return } const options = formData.map((item) => { const { activityInfo, descriptionInfo, timeRange, players, skill_level, ...rest } = item; const { min, max, organizer_joined } = players; return { ...rest, ...activityInfo, ...descriptionInfo, ...timeRange, max_players: max, min_players: min, organizer_joined, skill_level_min: skill_level[0], skill_level_max: skill_level[1], image_list: item.image_list.map(img => img.url) } }) const res = await PublishService.create_play_pmoothlys({rows: options}); if (res.code === 0 && res.data) { Taro.showToast({ title: '发布成功', icon: 'success' }) delay(1000) // 如果是个人球局,则跳转到详情页,并自动分享 // 如果是畅打,则跳转第一个球局详情页,并自动分享 @刘杰 Taro.navigateTo({ // @ts-expect-error: id url: `/game_pages/detail/index?id=${(res as any).data?.[0]?.id || 1}&from=publish&autoShare=1` }) } else { Taro.showToast({ title: res.message, icon: 'none' }) } } } const mergeWithDefault = (data: any): PublishBallFormData => { const userPhone = (userInfo as any)?.phone || '' const { start_time, end_time, play_type, price, description, description_tag, max_players, min_players, skill_level_max, skill_level_min, venueDtl } = data; let activityInfo = {}; if (venueDtl) { const { latitude, longitude,venue_type, surface_type, facilities, name, id } = venueDtl; activityInfo = { latitude, longitude, court_type: venue_type, court_surface: surface_type, venue_description: facilities, location_name: name, venue_id: id } } return { ...defaultFormData, ...data, timeRange: { ...defaultFormData.timeRange, start_time, end_time, }, activityInfo: { ...defaultFormData.activityInfo, ...(play_type ? { play_type } : {}), ...((price) ? { price } : {}), ...activityInfo }, descriptionInfo: { ...defaultFormData.descriptionInfo, ...(description ? { description } : {}), ...(description_tag ? { description_tag } : {}), }, ...(skill_level_max && skill_level_min ? { skill_level: [skill_level_min, skill_level_max] } : {}), ...(max_players && min_players ? { players: { min: min_players, max: max_players, organizer_joined: true } } : {}), wechat: { ...defaultFormData.wechat, default_wechat_contact: userPhone } } } const formatConfig = () => { const newFormSchema = publishBallFormSchema.reduce((acc, item) => { if (item.prop === 'wechat') { return acc } if (item.prop === 'image_list') { if (item.props) { item.props.source = ['album', 'history'] } } if (item.prop === 'players') { if (item.props) { item.props.max = 100 } } acc.push(item) return acc }, [] as FormFieldConfig[]) setOptionsConfig(newFormSchema) } const initFormData = () => { const currentInstance = Taro.getCurrentInstance() const params = currentInstance.router?.params const userPhone = (userInfo as any)?.phone || '' if (params?.type) { const type = params.type as ActivityType if (type === 'individual' || type === 'group') { setActivityType(type) if (type === 'group') { formatConfig() setFormData([defaultFormData]) setTitleBar('发布畅打活动') } else { setTitleBar('发布') setFormData([{...defaultFormData, wechat: { ...defaultFormData.wechat, default_wechat_contact: userPhone } }]) } } else if (type === 'ai') { // 从 Store 注入 AI 生成的表单 JSON if (publishAiData && Array.isArray(publishAiData) && publishAiData.length > 0) { Taro.showToast({ title: '智能识别成功,请完善剩余信息', icon: 'none' }) const merged = publishAiData.map(item => mergeWithDefault(item)) setFormData(merged.length ? merged : [defaultFormData]) if (merged.length === 1) { setTitleBar('发布') setActivityType('individual') } else { formatConfig() setTitleBar('发布畅打活动') setActivityType('group') } } else { setFormData([defaultFormData]) setTitleBar('发布') setActivityType('individual') } } } } const onCheckedChange = (checked: boolean) => { setChecked(checked) } useEffect(() => { const isValid = validateOnSubmit() if (!isValid) { setIsSubmitDisabled(true) } else { setIsSubmitDisabled(false) } console.log(formData, 'formData'); }, [formData]) useEffect(() => { initFormData() }, []) return ( {/* 活动类型切换 */} {/* */} { formData.map((item, index) => ( {/* 场次标题行 */} {activityType === 'group' && index > 0 && ( 第{index + 1}场 showDeleteConfirm(index)} > {index > 0 && ( handleCopyPrevious(index)} > 复制上一场 )} )} updateFormData(key, value, index)} optionsConfig={optionsConfig} /> )) } { activityType === 'group' && ( 再添加一场 ) } {/* 删除确认弹窗 */} {/* 完成按钮 */} { activityType === 'individual' && ( 点击确定发布约球,即表示已经同意条款 Taro.navigateTo({url: '/publish_pages/publishBall/footballRules/index'})}>《约球规则》 ) } { activityType === 'group' && ( 已认证 徐汇爱打球官方球场,请严格遵守签约协议 ) } ) } export default withAuth(PublishBall)