diff --git a/src/app.config.ts b/src/app.config.ts index def94a1..0ded27a 100644 --- a/src/app.config.ts +++ b/src/app.config.ts @@ -10,11 +10,6 @@ export default defineAppConfig({ "pages/search/index", // 搜索页 "pages/searchResult/index", // 搜索结果页面 "pages/publishBall/index", - "pages/detail/index", - // 'pages/message/index', - // 'pages/orderCheck/index', - - // 'pages/mapDisplay/index', ], subPackages: [ { @@ -22,6 +17,7 @@ export default defineAppConfig({ pages: [ "pages/myself/index", // 个人中心 "pages/edit/index", // 个人中心 + "detail/index", // 球局详情页 "message/index", // 消息页 "orderList/index", // 订单列表页 "orderDetail/index", // 订单详情页 diff --git a/src/components/ListCard/index.tsx b/src/components/ListCard/index.tsx index e68ea26..b9600a5 100644 --- a/src/components/ListCard/index.tsx +++ b/src/components/ListCard/index.tsx @@ -17,19 +17,24 @@ const ListCard: React.FC = ({ play_type, image_list = [], court_type, - key + key, }) => { const renderItemImage = (src: string) => { - return ; + return ( + + ); }; const handleViewDetail = () => { - console.log('id', id) + console.log("id", id); Taro.navigateTo({ - url: `/pages/detail/index?id=${id || 1}&from=list&autoShare=0`, + url: `/mod_user/detail/index?id=${id || 1}&from=list`, }); }; @@ -40,7 +45,9 @@ const ListCard: React.FC = ({ if (image_list?.length === 1) { return ( - {renderItemImage(image_list?.[0])} + + {renderItemImage(image_list?.[0])} + ); } @@ -48,8 +55,12 @@ const ListCard: React.FC = ({ if (image_list?.length === 2) { return ( - {renderItemImage(image_list?.[0])} - {renderItemImage(image_list?.[1])} + + {renderItemImage(image_list?.[0])} + + + {renderItemImage(image_list?.[1])} + ); } @@ -57,9 +68,15 @@ const ListCard: React.FC = ({ // 3张或更多图片 return ( - {renderItemImage(image_list?.[0])} - {renderItemImage(image_list?.[1])} - {renderItemImage(image_list?.[2])} + + {renderItemImage(image_list?.[0])} + + + {renderItemImage(image_list?.[1])} + + + {renderItemImage(image_list?.[2])} + ); }; @@ -69,14 +86,16 @@ const ListCard: React.FC = ({ {/* 左侧内容区域 */} {/* 标题 */} - {title && - {title} - - } + {title && ( + + {title} + + + )} {/* 时间信息 */} @@ -86,8 +105,11 @@ const ListCard: React.FC = ({ {/* 地点,室内外,距离 */} - {location && - {location}} + {location && ( + + {location} + + )} {court_type && `・${court_type}`} {distance_km && `・${distance_km}km`} @@ -98,17 +120,15 @@ const ListCard: React.FC = ({ - {Array.from({ length: 3 }).map( - (_, index) => ( - - - - ) - )} + {Array.from({ length: 3 }).map((_, index) => ( + + + + ))} @@ -120,11 +140,15 @@ const ListCard: React.FC = ({ - {skill_level_min} 至 {skill_level_max} + + {skill_level_min} 至 {skill_level_max} + - {play_type && - {play_type} - } + {play_type && ( + + {play_type} + + )} @@ -135,13 +159,19 @@ const ListCard: React.FC = ({ {/* 畅打球局 */} - + 畅打球局 场馆方: - + 仁恒河滨花园网球场 diff --git a/src/pages/detail/index.config.ts b/src/mod_user/detail/index.config.ts similarity index 100% rename from src/pages/detail/index.config.ts rename to src/mod_user/detail/index.config.ts diff --git a/src/pages/detail/index.scss b/src/mod_user/detail/index.scss similarity index 100% rename from src/pages/detail/index.scss rename to src/mod_user/detail/index.scss diff --git a/src/pages/detail/index.tsx b/src/mod_user/detail/index.tsx similarity index 99% rename from src/pages/detail/index.tsx rename to src/mod_user/detail/index.tsx index 789085e..29ee08e 100644 --- a/src/pages/detail/index.tsx +++ b/src/mod_user/detail/index.tsx @@ -167,7 +167,7 @@ const SharePopup = forwardRef( // useShareAppMessage(() => { // return { // title: '分享', - // path: `/pages/detail/index?id=${id}&from=share`, + // path: `/mod_user/detail/index?id=${id}&from=share`, // } // }) // } @@ -176,7 +176,7 @@ const SharePopup = forwardRef( // useShareTimeline(() => { // return { // title: '分享', - // path: `/pages/detail/index?id=${id}&from=share`, + // path: `/mod_user/detail/index?id=${id}&from=share`, // } // }) // } diff --git a/src/mod_user/pages/other/index.tsx b/src/mod_user/pages/other/index.tsx index dd4b774..9f4b97a 100644 --- a/src/mod_user/pages/other/index.tsx +++ b/src/mod_user/pages/other/index.tsx @@ -105,7 +105,7 @@ const OtherUserPage: React.FC = () => { // 处理球局详情 const handle_game_detail = (game_id: string) => { Taro.navigateTo({ - url: `/pages/detail/index?id=${game_id}`, + url: `/mod_user/detail/index?id=${game_id}&from=personal`, }); }; diff --git a/src/pages/publishBall/index.tsx b/src/pages/publishBall/index.tsx index de070ed..32bc6aa 100644 --- a/src/pages/publishBall/index.tsx +++ b/src/pages/publishBall/index.tsx @@ -1,60 +1,65 @@ -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 images from '@/config/images' -import styles from './index.module.scss' -import dayjs from 'dayjs' +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 images from "@/config/images"; +import styles from "./index.module.scss"; +import dayjs from "dayjs"; const defaultFormData: PublishBallFormData = { - title: '', + title: "", image_list: [], timeRange: { start_time: getNextHourTime(), - end_time: getEndTime(getNextHourTime()) + end_time: getEndTime(getNextHourTime()), }, activityInfo: { - play_type: '不限', - price: '', + play_type: "不限", + price: "", venue_id: null, - location_name: '', - location: '', - latitude: '', - longitude: '', - court_type: '', - court_surface: '', + location_name: "", + location: "", + latitude: "", + longitude: "", + court_type: "", + court_surface: "", venue_description_tag: [], - venue_description: '', + venue_description: "", venue_image_list: [], }, players: [1, 1], skill_level: [1.0, 5.0], descriptionInfo: { - description: '', + description: "", description_tag: [], }, is_substitute_supported: true, is_wechat_contact: true, - wechat_contact: '14223332214' -} + wechat_contact: "14223332214", +}; const PublishBall: React.FC = () => { - const [activityType, setActivityType] = useState('individual') - const [isSubmitDisabled, setIsSubmitDisabled] = useState(false) + const [activityType, setActivityType] = useState("individual"); + const [isSubmitDisabled, setIsSubmitDisabled] = useState(false); // 获取页面参数并设置导航标题 - const [optionsConfig, setOptionsConfig] = useState(publishBallFormSchema) + const [optionsConfig, setOptionsConfig] = useState( + publishBallFormSchema, + ); const [formData, setFormData] = useState([ - defaultFormData - ]) - const [checked, setChecked] = useState(true) + defaultFormData, + ]); + const [checked, setChecked] = useState(true); // 删除确认弹窗状态 const [deleteConfirm, setDeleteConfirm] = useState<{ @@ -62,200 +67,225 @@ const PublishBall: React.FC = () => { index: number; }>({ visible: false, - index: -1 - }) + 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 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 handleActivityTypeChange = (type: ActivityType) => { - if (type === 'group') { - setFormData([defaultFormData]) + if (type === "group") { + setFormData([defaultFormData]); } else { - setFormData([defaultFormData]) + setFormData([defaultFormData]); } - } + }; // 检查相邻两组数据是否相同 const checkAdjacentDataSame = (formDataArray: PublishBallFormData[]) => { - if (formDataArray.length < 2) return false + if (formDataArray.length < 2) return false; - const lastIndex = formDataArray.length - 1 - const secondLastIndex = formDataArray.length - 2 + const lastIndex = formDataArray.length - 1; + const secondLastIndex = formDataArray.length - 2; - const lastData = formDataArray[lastIndex] - const secondLastData = formDataArray[secondLastIndex] + const lastData = formDataArray[lastIndex]; + const secondLastData = formDataArray[secondLastIndex]; // 比较关键字段是否相同 - return (JSON.stringify(lastData) === JSON.stringify(secondLastData)) - } + return JSON.stringify(lastData) === JSON.stringify(secondLastData); + }; const handleAdd = () => { // 检查最后两组数据是否相同 if (checkAdjacentDataSame(formData)) { Taro.showToast({ - title: '信息不可与前序场完全一致', - icon: 'none' - }) - return + title: "信息不可与前序场完全一致", + icon: "none", + }); + return; } - const newStartTime = getNextHourTime() - setFormData(prev => [...prev, { - ...defaultFormData, - title: '', - timeRange: { - start_time: newStartTime, - end_time: getEndTime(newStartTime) - } - }]) - } - + 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 - }) + setFormData((prev) => { + const newData = [...prev]; + newData[index] = { ...newData[index - 1] }; + return newData; + }); Taro.showToast({ - title: '复制上一场填入', - icon: 'success' - }) + title: "复制上一场填入", + icon: "success", + }); } - } + }; // 删除确认弹窗 const showDeleteConfirm = (index: number) => { setDeleteConfirm({ visible: true, - index - }) - } + index, + }); + }; // 关闭删除确认弹窗 const closeDeleteConfirm = () => { setDeleteConfirm({ visible: false, - index: -1 - }) - } + index: -1, + }); + }; // 确认删除 const confirmDelete = () => { if (deleteConfirm.index >= 0) { - setFormData(prev => prev.filter((_, index) => index !== deleteConfirm.index)) - closeDeleteConfirm() + setFormData((prev) => + prev.filter((_, index) => index !== deleteConfirm.index), + ); + closeDeleteConfirm(); Taro.showToast({ - title: '已删除该场次', - icon: 'success' - }) + title: "已删除该场次", + icon: "success", + }); } - } + }; - const validateFormData = (formData: PublishBallFormData, isOnSubmit: boolean = false) => { + const validateFormData = ( + formData: PublishBallFormData, + isOnSubmit: boolean = false, + ) => { const { activityInfo, image_list, title, timeRange } = formData; - const { play_type, price, location_name } = activityInfo; + const { play_type, price, location_name } = activityInfo; if (!image_list?.length) { if (!isOnSubmit) { Taro.showToast({ title: `请上传活动封面`, - icon: 'none' - }) + icon: "none", + }); } - return false + return false; } if (!title) { if (!isOnSubmit) { Taro.showToast({ title: `请输入活动标题`, - icon: 'none' - }) + icon: "none", + }); } - return false + return false; } - if (!price || (typeof price === 'number' && price <= 0) || (typeof price === 'string' && !price.trim())) { + if ( + !price || + (typeof price === "number" && price <= 0) || + (typeof price === "string" && !price.trim()) + ) { if (!isOnSubmit) { Taro.showToast({ title: `请输入费用`, - icon: 'none' - }) + icon: "none", + }); } - return false + return false; } if (!play_type || !play_type.trim()) { if (!isOnSubmit) { Taro.showToast({ title: `请选择玩法类型`, - icon: 'none' - }) + icon: "none", + }); } - return false + return false; } if (!location_name || !location_name.trim()) { if (!isOnSubmit) { Taro.showToast({ title: `请选择场地`, - icon: 'none' - }) + icon: "none", + }); } - return false + return false; } // 时间范围校验:结束时间需晚于开始时间,且至少间隔30分钟(支持跨天) if (timeRange?.start_time && timeRange?.end_time) { - const start = dayjs(timeRange.start_time) - const end = dayjs(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' - }) + icon: "none", + }); } - return false + return false; } - if (end.isBefore(start.add(30, 'minute'))) { + if (end.isBefore(start.add(30, "minute"))) { if (!isOnSubmit) { Taro.showToast({ title: `时间间隔至少30分钟`, - icon: 'none' - }) + icon: "none", + }); } - return false + return false; } } - return true - } + return true; + }; const validateOnSubmit = () => { - const isValid = activityType === 'individual' ? validateFormData(formData[0], true) : formData.every(item => validateFormData(item, true)) + const isValid = + activityType === "individual" + ? validateFormData(formData[0], true) + : formData.every((item) => validateFormData(item, true)); if (!isValid) { - return false + return false; } - return true - } + return true; + }; // 提交表单 const handleSubmit = async () => { // 基础验证 - console.log(formData, 'formData'); - if (activityType === 'individual') { - const isValid = validateFormData(formData[0]) + console.log(formData, "formData"); + if (activityType === "individual") { + const isValid = validateFormData(formData[0]); if (!isValid) { - return + return; } - const { activityInfo, descriptionInfo, timeRange, players, skill_level,image_list, ...rest } = formData[0]; + const { + activityInfo, + descriptionInfo, + timeRange, + players, + skill_level, + image_list, + ...rest + } = formData[0]; const options = { ...rest, ...activityInfo, @@ -265,43 +295,50 @@ const PublishBall: React.FC = () => { current_players: players[0], skill_level_min: skill_level[0], skill_level_max: skill_level[1], - image_list: image_list.map(item => item.url) - } + image_list: image_list.map((item) => item.url), + }; const res = await PublishService.createPersonal(options); if (res.code === 0 && res.data) { Taro.showToast({ - title: '发布成功', - icon: 'success' - }) - delay(1000) + title: "发布成功", + icon: "success", + }); + delay(1000); // 如果是个人球局,则跳转到详情页,并自动分享 // 如果是畅打,则跳转第一个球局详情页,并自动分享 @刘杰 Taro.navigateTo({ // @ts-expect-error: id - url: `/pages/detail/index?id=${res.data.id || 1}&from=publish&autoShare=1` - }) + url: `/mod_user/detail/index?id=${res.data.id || 1}&from=publish`, + }); } else { Taro.showToast({ title: res.message, - icon: 'none' - }) + icon: "none", + }); } } - if (activityType === 'group') { - const isValid = formData.every(item => validateFormData(item)) + if (activityType === "group") { + const isValid = formData.every((item) => validateFormData(item)); if (!isValid) { - return + return; } if (checkAdjacentDataSame(formData)) { Taro.showToast({ - title: '信息不可与前序场完全一致', - icon: 'none' - }) - return + title: "信息不可与前序场完全一致", + icon: "none", + }); + return; } const options = formData.map((item) => { - const { activityInfo, descriptionInfo, timeRange, players, skill_level, ...rest } = item; - return { + const { + activityInfo, + descriptionInfo, + timeRange, + players, + skill_level, + ...rest + } = item; + return { ...rest, ...activityInfo, ...descriptionInfo, @@ -310,146 +347,146 @@ const PublishBall: React.FC = () => { current_players: players[0], 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}); + 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) + title: "发布成功", + icon: "success", + }); + delay(1000); // 如果是个人球局,则跳转到详情页,并自动分享 // 如果是畅打,则跳转第一个球局详情页,并自动分享 @刘杰 Taro.navigateTo({ // @ts-expect-error: id - url: `/pages/detail/index?id=${res.data?.[0].id || 1}&from=publish&autoShare=1` - }) + url: `/mod_user/detail/index?id=${res.data?.[0].id || 1}&from=publish`, + }); } else { Taro.showToast({ title: res.message, - icon: 'none' - }) + icon: "none", + }); } } - } + }; const initFormData = () => { - const currentInstance = Taro.getCurrentInstance() - const params = currentInstance.router?.params + 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) - if (type === 'group') { + const type = params.type as ActivityType; + if (type === "individual" || type === "group") { + setActivityType(type); + if (type === "group") { const newFormSchema = publishBallFormSchema.reduce((acc, item) => { - if (item.prop === 'is_wechat_contact') { - return acc + if (item.prop === "is_wechat_contact") { + return acc; } - if (item.prop === 'image_list') { + if (item.prop === "image_list") { if (item.props) { - item.props.source = ['album', 'history'] + item.props.source = ["album", "history"]; } } - if (item.prop === 'players') { + if (item.prop === "players") { if (item.props) { - item.props.max = 100 + item.props.max = 100; } } - acc.push(item) - return acc - }, [] as FormFieldConfig[]) - setOptionsConfig(newFormSchema) - setFormData([defaultFormData]) + acc.push(item); + return acc; + }, [] as FormFieldConfig[]); + setOptionsConfig(newFormSchema); + setFormData([defaultFormData]); } // 根据type设置导航标题 - if (type === 'group') { + if (type === "group") { Taro.setNavigationBarTitle({ - title: '发布畅打活动' - }) + title: "发布畅打活动", + }); } else { Taro.setNavigationBarTitle({ - title: '发布' - }) + title: "发布", + }); } } - handleActivityTypeChange(type) + handleActivityTypeChange(type); } - } + }; const onCheckedChange = (checked: boolean) => { - setChecked(checked) - } + setChecked(checked); + }; useEffect(() => { - const isValid = validateOnSubmit() + const isValid = validateOnSubmit(); if (!isValid) { - setIsSubmitDisabled(true) + setIsSubmitDisabled(true); } else { - setIsSubmitDisabled(false) + setIsSubmitDisabled(false); } - console.log(formData, 'formData'); - }, [formData]) + console.log(formData, "formData"); + }, [formData]); useEffect(() => { - initFormData() - }, []) + initFormData(); + }, []); return ( - + {/* 活动类型切换 */} - + {/* */} - - { - formData.map((item, index) => ( - - {/* 场次标题行 */} - {activityType === 'group' && index > 0 && ( - - + + {formData.map((item, index) => ( + + {/* 场次标题行 */} + {activityType === "group" && index > 0 && ( + + 第{index + 1}场 showDeleteConfirm(index)} - > - - - - - - - {index > 0 && ( - handleCopyPrevious(index)} - > - 复制上一场 - - )} + className={styles["session-delete"]} + onClick={() => showDeleteConfirm(index)} + > + - )} - updateFormData(key, value, index)} - optionsConfig={optionsConfig} - /> - - )) - } - { - activityType === 'group' && ( - - - 再添加一场 - - ) - } + + {index > 0 && ( + handleCopyPrevious(index)} + > + 复制上一场 + + )} + + + )} + updateFormData(key, value, index)} + optionsConfig={optionsConfig} + /> + + ))} + {activityType === "group" && ( + + + 再添加一场 + + )} {/* 删除确认弹窗 */} @@ -463,33 +500,32 @@ const PublishBall: React.FC = () => { contentDesc="该操作不可恢复" /> {/* 完成按钮 */} - - - { - activityType === 'individual' && ( - - 点击确定发布约球,即表示已经同意条款 - 《约球规则》 - - ) - } - { - activityType === 'group' && ( - - - 已认证 徐汇爱打球官方球场,请严格遵守签约协议 - - ) - } + {activityType === "individual" && ( + + 点击确定发布约球,即表示已经同意条款 + 《约球规则》 + + )} + {activityType === "group" && ( + + + 已认证 徐汇爱打球官方球场,请严格遵守签约协议 + + )} - ) -} + ); +}; -export default withAuth(PublishBall) +export default withAuth(PublishBall);