发布页开发
This commit is contained in:
5
src/pages/mapDisplay/index.tsx
Normal file
5
src/pages/mapDisplay/index.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
// import MapPlugin from "src/components/MapDisplay/mapPlugin";
|
||||
import MapDisplay from "src/components/MapDisplay";
|
||||
export default function MapDisplayPage() {
|
||||
return <MapDisplay />
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '发布个人约球'
|
||||
navigationBarTitleText: '发布',
|
||||
navigationBarBackgroundColor: '#FAFAFA'
|
||||
})
|
||||
@@ -1,402 +1,202 @@
|
||||
.publish-ball-page {
|
||||
background: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
padding-bottom: 200px;
|
||||
@use '~@/scss/themeColor.scss' as theme;
|
||||
|
||||
.reminder {
|
||||
background: #fff8dc;
|
||||
padding: 16px;
|
||||
margin: 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
line-height: 1.4;
|
||||
.publish-ball {
|
||||
min-height: 100vh;
|
||||
background: theme.$page-background-color;
|
||||
padding: 4px 16px 0 16px;
|
||||
position: relative;
|
||||
&__scroll {
|
||||
height: calc(100vh - 120px);
|
||||
}
|
||||
|
||||
.match-form {
|
||||
background: #fff;
|
||||
margin: 16px;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
position: relative;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||
&__content {
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
|
||||
.delete-btn {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
background: #ff4757;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
|
||||
|
||||
// 标题区域 - 独立白色块
|
||||
.bg-section {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 8px;
|
||||
position: relative;
|
||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||
box-shadow: 0px 4px 36px 0px rgba(0, 0, 0, 0.06);
|
||||
display: flex;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 活动描述文本 - 灰色背景
|
||||
.activity-description {
|
||||
margin-bottom: 20px;
|
||||
padding: 0 8px;
|
||||
|
||||
.description-text {
|
||||
font-size: 12px;
|
||||
color:rgba(60, 60, 67, 0.6) ;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
// 表单分组区域 - 费用地点玩法白色块
|
||||
.form-group-section {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
padding: 20px 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
// 区域标题 - 灰色背景
|
||||
.section-title-wrapper {
|
||||
padding: 0 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 44px;
|
||||
padding-top: 5px;
|
||||
box-sizing: border-box;
|
||||
font-size: 14px;
|
||||
|
||||
.section-title {
|
||||
font-size: 16px;
|
||||
color: theme.$primary-color;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 24px;
|
||||
.section-summary {
|
||||
font-size: 14px;
|
||||
color: theme.$input-placeholder-color;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 12px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
// 封面上传
|
||||
.cover-upload {
|
||||
.upload-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
|
||||
.upload-btn {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border: 2px dashed #ddd;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #fafafa;
|
||||
|
||||
.plus-icon {
|
||||
font-size: 24px;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.image-item {
|
||||
position: relative;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
|
||||
.cover-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.remove-btn {
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
right: -6px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: #ff4757;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 主题输入
|
||||
.theme-input {
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
padding: 0 16px;
|
||||
font-size: 14px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
// 时间显示
|
||||
.time-display {
|
||||
background: #f8f8f8;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
// 自动降分选择 - 白色块
|
||||
.auto-degrade-section {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
padding: 20px 16px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.time-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
.auto-degrade-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.time-label {
|
||||
width: 40px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.time-value {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
margin-left: 16px;
|
||||
|
||||
&:last-child {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 场地输入
|
||||
.venue-input-container {
|
||||
.auto-degrade-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
padding-right: 16px;
|
||||
flex: 1;
|
||||
|
||||
.venue-input {
|
||||
flex: 1;
|
||||
height: 44px;
|
||||
padding: 0 16px;
|
||||
font-size: 14px;
|
||||
border: none;
|
||||
.auto-degrade-text {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
font-size: 18px;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
// 价格输入
|
||||
.price-input {
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
padding: 0 16px;
|
||||
font-size: 14px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
// 玩法选择
|
||||
.play-style-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
|
||||
.play-style-btn {
|
||||
padding: 8px 20px;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 20px;
|
||||
background: #fff;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
|
||||
&.selected {
|
||||
background: #000;
|
||||
color: #fff;
|
||||
border-color: #000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 人数控制
|
||||
.player-count-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.count-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.count-label {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.count-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.count-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #fff;
|
||||
font-size: 18px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.count-number {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
min-width: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.separator {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
// NTRP滑动条
|
||||
.ntrp-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
|
||||
.ntrp-label {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.slider-container {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
|
||||
.slider-track {
|
||||
height: 4px;
|
||||
background: #e0e0e0;
|
||||
border-radius: 2px;
|
||||
position: relative;
|
||||
|
||||
.slider-fill {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
background: #333;
|
||||
border-radius: 2px;
|
||||
left: 20%;
|
||||
right: 20%;
|
||||
}
|
||||
|
||||
.slider-thumb {
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: #333;
|
||||
border-radius: 50%;
|
||||
top: -8px;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 性别选择
|
||||
.requirements-hint {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.gender-buttons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
|
||||
.gender-btn {
|
||||
padding: 8px 16px;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 6px;
|
||||
background: #fff;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
|
||||
&.selected {
|
||||
background: #000;
|
||||
color: #fff;
|
||||
border-color: #000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 自动候补
|
||||
.standby-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.checkbox {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 3px;
|
||||
.info-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 1px solid #999;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #fff;
|
||||
|
||||
&.checked {
|
||||
background: #000;
|
||||
border-color: #000;
|
||||
|
||||
.checkmark {
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.auto-degrade-checkbox {
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 底部操作区
|
||||
.bottom-actions {
|
||||
// 提交区域
|
||||
.submit-section {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #fff;
|
||||
padding: 20px 16px 30px;
|
||||
background: white;
|
||||
padding: 16px;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
|
||||
.add-match-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
height: 44px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
margin-bottom: 12px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
|
||||
.plus-icon {
|
||||
font-size: 20px;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.complete-btn {
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
background: #000;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
background: #333;
|
||||
color: white;
|
||||
border-radius: 24px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 12px;
|
||||
margin-bottom: 8px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.disclaimer {
|
||||
.submit-tip {
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
text-align: center;
|
||||
line-height: 1.4;
|
||||
|
||||
.link {
|
||||
color: #007AFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 加载状态遮罩保持原样
|
||||
&__loading {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 9999;
|
||||
|
||||
.loading-spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 3px solid rgba(255, 255, 255, 0.3);
|
||||
border-top: 3px solid #fff;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 旋转动画
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,62 +1,151 @@
|
||||
import React from 'react'
|
||||
import { View } from '@tarojs/components'
|
||||
import React, { useState } from 'react'
|
||||
import { View, Text, Input, Button, Image, ScrollView, Picker } from '@tarojs/components'
|
||||
import Taro from '@tarojs/taro'
|
||||
import DynamicForm from '../../components/DynamicForm/DynamicForm'
|
||||
import { publishBallFormConfig } from '../../config/formSchema/bulishBallFormSchema'
|
||||
import ActivityTypeSwitch, { type ActivityType } from '../../components/ActivityTypeSwitch'
|
||||
import PublishForm from './publishForm'
|
||||
import { publishBallFormSchema } from '../../config/formSchema/publishBallFormSchema';
|
||||
import './index.scss'
|
||||
|
||||
const PublishBallPage: React.FC = () => {
|
||||
// 提交成功回调
|
||||
const handleSubmitSuccess = (response: any) => {
|
||||
console.log('发布成功:', response)
|
||||
interface FormData {
|
||||
activityType: ActivityType
|
||||
title: string
|
||||
timeRange: TimeRange
|
||||
fee: string
|
||||
location: string
|
||||
gameplay: string
|
||||
minParticipants: number
|
||||
maxParticipants: number
|
||||
ntpLevel: NTRPRange
|
||||
additionalRequirements: string
|
||||
autoDegrade: boolean
|
||||
}
|
||||
|
||||
const PublishBall: React.FC = () => {
|
||||
const [coverImages, setCoverImages] = useState<CoverImage[]>([])
|
||||
const [showStadiumSelector, setShowStadiumSelector] = useState(false)
|
||||
const [selectedStadium, setSelectedStadium] = useState<Stadium | null>(null)
|
||||
const [formData, setFormData] = useState<FormData>({
|
||||
activityType: 'individual', // 默认值
|
||||
title: '',
|
||||
timeRange: {
|
||||
startDate: '2025-11-23',
|
||||
startTime: '08:00',
|
||||
endTime: '10:00'
|
||||
},
|
||||
fee: '',
|
||||
location: '',
|
||||
gameplay: '',
|
||||
minParticipants: 1,
|
||||
maxParticipants: 4,
|
||||
ntpLevel: { min: 2.0, max: 4.0 },
|
||||
additionalRequirements: '',
|
||||
autoDegrade: false
|
||||
})
|
||||
|
||||
// 处理封面图片变化
|
||||
const handleCoverImagesChange = (images: CoverImage[]) => {
|
||||
setCoverImages(images)
|
||||
}
|
||||
|
||||
// 更新表单数据
|
||||
const updateFormData = (key: keyof FormData, value: any) => {
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
[key]: value
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 获取人数要求显示文本
|
||||
const getParticipantsText = () => {
|
||||
return `最少${formData.minParticipants}人,最多${formData.maxParticipants}人`
|
||||
}
|
||||
|
||||
// 处理NTRP范围变化
|
||||
const handleNTRPChange = (range: NTRPRange) => {
|
||||
updateFormData('ntpLevel', range)
|
||||
}
|
||||
|
||||
// 处理时间范围变化
|
||||
const handleTimeRangeChange = (timeRange: TimeRange) => {
|
||||
updateFormData('timeRange', timeRange)
|
||||
}
|
||||
|
||||
// 处理补充要求变化
|
||||
const handleAdditionalRequirementsChange = (value: string) => {
|
||||
updateFormData('additionalRequirements', value)
|
||||
}
|
||||
|
||||
// 处理场馆选择
|
||||
const handleStadiumSelect = (stadium: Stadium | null) => {
|
||||
setSelectedStadium(stadium)
|
||||
if (stadium) {
|
||||
updateFormData('location', stadium.name)
|
||||
}
|
||||
setShowStadiumSelector(false)
|
||||
}
|
||||
|
||||
// 处理活动类型变化
|
||||
const handleActivityTypeChange = (type: ActivityType) => {
|
||||
updateFormData('activityType', type)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// 提交表单
|
||||
const handleSubmit = async () => {
|
||||
// 基础验证
|
||||
if (!formData.title.trim()) {
|
||||
Taro.showToast({
|
||||
title: '请输入活动标题',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (coverImages.length === 0) {
|
||||
Taro.showToast({
|
||||
title: '请至少上传一张活动封面',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: 实现提交逻辑
|
||||
console.log('提交数据:', { coverImages, formData })
|
||||
|
||||
Taro.showModal({
|
||||
Taro.showToast({
|
||||
title: '发布成功',
|
||||
content: response.data.length > 1 ?
|
||||
`成功发布${response.data.length}场约球活动!` :
|
||||
'约球活动发布成功!',
|
||||
showCancel: false,
|
||||
success: () => {
|
||||
// 可以跳转到活动列表页面
|
||||
// Taro.navigateTo({ url: '/pages/matchList/matchList' })
|
||||
}
|
||||
icon: 'success'
|
||||
})
|
||||
}
|
||||
|
||||
// 提交失败回调
|
||||
const handleSubmitError = (error: any) => {
|
||||
console.error('发布失败:', error)
|
||||
|
||||
Taro.showModal({
|
||||
title: '发布失败',
|
||||
content: error.message || '网络错误,请稍后重试',
|
||||
showCancel: false
|
||||
})
|
||||
}
|
||||
|
||||
// 添加表单
|
||||
const handleAddForm = () => {
|
||||
console.log('添加新表单')
|
||||
}
|
||||
|
||||
// 删除表单
|
||||
const handleDeleteForm = (index: number) => {
|
||||
console.log('删除表单:', index)
|
||||
}
|
||||
|
||||
return (
|
||||
<View className='publish-ball-page'>
|
||||
<DynamicForm
|
||||
config={publishBallFormConfig}
|
||||
formType='publishBall'
|
||||
enableApiSubmit={true}
|
||||
onSubmitSuccess={handleSubmitSuccess}
|
||||
onSubmitError={handleSubmitError}
|
||||
onAddForm={handleAddForm}
|
||||
onDeleteForm={handleDeleteForm}
|
||||
<View className='publish-ball'>
|
||||
{/* 活动类型切换 */}
|
||||
<ActivityTypeSwitch
|
||||
value={formData.activityType}
|
||||
onChange={handleActivityTypeChange}
|
||||
/>
|
||||
<ScrollView className='publish-ball__scroll' scrollY>
|
||||
<PublishForm formData={formData} onChange={updateFormData} optionsConfig={publishBallFormSchema} />
|
||||
</ScrollView>
|
||||
|
||||
|
||||
{/* 完成按钮 */}
|
||||
<View className='submit-section'>
|
||||
<Button className='submit-btn' onClick={handleSubmit}>
|
||||
完成
|
||||
</Button>
|
||||
<Text className='submit-tip'>
|
||||
点击确定发布约球,即表示已经同意条款
|
||||
<Text className='link'>《约球规则》</Text>
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export default PublishBallPage
|
||||
export default PublishBall
|
||||
|
||||
198
src/pages/publishBall/publishForm.tsx
Normal file
198
src/pages/publishBall/publishForm.tsx
Normal file
@@ -0,0 +1,198 @@
|
||||
import React, { useState } from 'react'
|
||||
import { View, Text } from '@tarojs/components'
|
||||
|
||||
import Taro from '@tarojs/taro'
|
||||
import { CoverImageUpload, NTRPSlider, TimeSelector, TextareaTag, SelectStadium, ParticipantsControl, TitleInput, FormBasicInfo, AutoDegradeSwitch } from '../../components'
|
||||
import { type NTRPRange, type TimeRange, type Stadium, type ActivityType, type CoverImage } from '../../components/index.types'
|
||||
import { FormFieldConfig, FieldType } from '../../config/formSchema/publishBallFormSchema'
|
||||
import './index.scss'
|
||||
|
||||
interface FormData {
|
||||
activityType: ActivityType
|
||||
title: string
|
||||
timeRange: TimeRange
|
||||
fee: string
|
||||
location: string
|
||||
gameplay: string
|
||||
minParticipants: number
|
||||
maxParticipants: number
|
||||
ntpLevel: NTRPRange
|
||||
TextareaTag: string
|
||||
autoDegrade: boolean
|
||||
}
|
||||
|
||||
// 组件映射器
|
||||
const componentMap = {
|
||||
[FieldType.TEXT]: TitleInput,
|
||||
[FieldType.TIMEINTERVAL]: TimeSelector,
|
||||
[FieldType.RANGE]: NTRPSlider,
|
||||
[FieldType.TEXTAREATAG]: TextareaTag,
|
||||
[FieldType.NUMBERINTERVAL]: ParticipantsControl,
|
||||
[FieldType.UPLOADIMAGE]: CoverImageUpload,
|
||||
[FieldType.ACTIVITYINFO]: FormBasicInfo,
|
||||
[FieldType.CHECKBOX]: AutoDegradeSwitch,
|
||||
}
|
||||
|
||||
const PublishForm: React.FC<{
|
||||
formData: FormData,
|
||||
onChange: (key: keyof FormData, value: any) => void,
|
||||
optionsConfig: FormFieldConfig[] }> = ({ formData, onChange, optionsConfig }) => {
|
||||
const [coverImages, setCoverImages] = useState<CoverImage[]>([])
|
||||
const [showStadiumSelector, setShowStadiumSelector] = useState(false)
|
||||
const [selectedStadium, setSelectedStadium] = useState<Stadium | null>(null)
|
||||
|
||||
// 处理封面图片变化
|
||||
const handleCoverImagesChange = (images: CoverImage[]) => {
|
||||
setCoverImages(images)
|
||||
}
|
||||
|
||||
// 更新表单数据
|
||||
const updateFormData = (key: keyof FormData, value: any) => {
|
||||
onChange(key, value)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 获取人数要求显示文本
|
||||
const getParticipantsText = () => {
|
||||
return `最少${formData.minParticipants}人,最多${formData.maxParticipants}人`
|
||||
}
|
||||
|
||||
// 处理NTRP范围变化
|
||||
const handleNTRPChange = (range: NTRPRange) => {
|
||||
updateFormData('ntpLevel', range)
|
||||
}
|
||||
|
||||
// 处理时间范围变化
|
||||
const handleTimeRangeChange = (timeRange: TimeRange) => {
|
||||
updateFormData('timeRange', timeRange)
|
||||
}
|
||||
|
||||
// 处理补充要求变化
|
||||
const handleAdditionalRequirementsChange = (value: string) => {
|
||||
updateFormData('additionalRequirements', value)
|
||||
}
|
||||
|
||||
// 处理场馆选择
|
||||
const handleStadiumSelect = (stadium: Stadium | null) => {
|
||||
setSelectedStadium(stadium)
|
||||
if (stadium) {
|
||||
updateFormData('location', stadium.name)
|
||||
}
|
||||
setShowStadiumSelector(false)
|
||||
}
|
||||
|
||||
// 处理活动类型变化
|
||||
const handleActivityTypeChange = (type: ActivityType) => {
|
||||
updateFormData('activityType', type)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// 提交表单
|
||||
const handleSubmit = async () => {
|
||||
// 基础验证
|
||||
if (!formData.title.trim()) {
|
||||
Taro.showToast({
|
||||
title: '请输入活动标题',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (coverImages.length === 0) {
|
||||
Taro.showToast({
|
||||
title: '请至少上传一张活动封面',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: 实现提交逻辑
|
||||
console.log('提交数据:', { coverImages, formData })
|
||||
|
||||
Taro.showToast({
|
||||
title: '发布成功',
|
||||
icon: 'success'
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<View className='publish-form'>
|
||||
<View className='publish-ball__content'>
|
||||
{
|
||||
optionsConfig.map((item) => {
|
||||
const Component = componentMap[item.type]
|
||||
const optionProps = {
|
||||
...item.props,
|
||||
...(item.key === 'additionalRequirements' ? { options: item.options } : {})
|
||||
}
|
||||
console.log(optionProps, item.label);
|
||||
if (item.type === FieldType.UPLOADIMAGE) {
|
||||
/* 活动封面 */
|
||||
return <CoverImageUpload
|
||||
images={coverImages}
|
||||
onChange={handleCoverImagesChange}
|
||||
{...item.props}
|
||||
/>
|
||||
}
|
||||
if (item.type === FieldType.ACTIVITYINFO) {
|
||||
return <>
|
||||
<View className='activity-description'>
|
||||
<Text className='description-text'>
|
||||
活动开始前2小时未达到最低人数,活动自动取消;活动
|
||||
结束后,报名者累计到达最低人数时,一旦到达期即有序。
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{/* 费用地点玩法区域 - 合并白色块 */}
|
||||
<View className='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)}
|
||||
/>
|
||||
</View>
|
||||
</>
|
||||
}
|
||||
return (
|
||||
<View className='section-wrapper'>
|
||||
{
|
||||
item.label && <View className='section-title-wrapper' >
|
||||
<Text className='section-title'>{item.label}</Text>
|
||||
<Text className='section-summary'>最少1人,最多4人</Text>
|
||||
</View>
|
||||
}
|
||||
<View className='bg-section'>
|
||||
<Component
|
||||
value={formData[item.key]}
|
||||
onChange={(value) => updateFormData(item.key as keyof FormData, value)}
|
||||
{...optionProps}
|
||||
placeholder={item.placeholder}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
})
|
||||
}
|
||||
</View>
|
||||
|
||||
{/* 场馆选择弹窗 */}
|
||||
<SelectStadium
|
||||
visible={showStadiumSelector}
|
||||
onClose={() => setShowStadiumSelector(false)}
|
||||
onConfirm={handleStadiumSelect}
|
||||
/>
|
||||
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export default PublishForm
|
||||
Reference in New Issue
Block a user