修改发布增加拖动
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
export default defineAppConfig({
|
||||
pages: [
|
||||
'pages/list/index',
|
||||
'pages/publishBall/index',
|
||||
|
||||
// 'pages/userInfo/myself/index',
|
||||
'pages/login/index/index',
|
||||
'pages/login/verification/index',
|
||||
'pages/login/terms/index',
|
||||
'pages/publishBall/index',
|
||||
|
||||
// 'pages/mapDisplay/index',
|
||||
'pages/detail/index',
|
||||
'pages/message/index',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import React, { useRef, useState } from 'react'
|
||||
import { View, Text } from '@tarojs/components'
|
||||
import { Popup, Button } from '@nutui/nutui-react-taro'
|
||||
import styles from './index.module.scss'
|
||||
@@ -19,6 +19,7 @@ export interface CommonPopupProps {
|
||||
children?: React.ReactNode
|
||||
className?: string
|
||||
style?: React.CSSProperties
|
||||
enableDragToClose?: boolean
|
||||
}
|
||||
|
||||
const CommonPopup: React.FC<CommonPopupProps> = ({
|
||||
@@ -36,8 +37,14 @@ const CommonPopup: React.FC<CommonPopupProps> = ({
|
||||
round = true,
|
||||
zIndex,
|
||||
style,
|
||||
children
|
||||
children,
|
||||
enableDragToClose = true
|
||||
}) => {
|
||||
const [dragOffset, setDragOffset] = useState(0)
|
||||
const [isDragging, setIsDragging] = useState(false)
|
||||
const touchStartY = useRef(0)
|
||||
const popupRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const handleCancel = () => {
|
||||
if (onCancel) {
|
||||
onCancel()
|
||||
@@ -46,6 +53,39 @@ const CommonPopup: React.FC<CommonPopupProps> = ({
|
||||
}
|
||||
}
|
||||
|
||||
const handleTouchStart = (e: any) => {
|
||||
if (!enableDragToClose) return
|
||||
|
||||
touchStartY.current = e.touches[0].clientY
|
||||
setIsDragging(true)
|
||||
}
|
||||
|
||||
const handleTouchMove = (e: any) => {
|
||||
if (!enableDragToClose || !isDragging) return
|
||||
|
||||
const currentY = e.touches[0].clientY
|
||||
const deltaY = currentY - touchStartY.current
|
||||
|
||||
// 只允许向下拖动,限制最大拖动距离
|
||||
if (deltaY > 0) {
|
||||
setDragOffset(Math.min(deltaY, 200))
|
||||
}
|
||||
}
|
||||
|
||||
const handleTouchEnd = () => {
|
||||
if (!enableDragToClose || !isDragging) return
|
||||
|
||||
setIsDragging(false)
|
||||
|
||||
// 如果拖动距离超过阈值,关闭弹窗
|
||||
if (dragOffset > 100) {
|
||||
onClose()
|
||||
}
|
||||
|
||||
// 重置拖动偏移
|
||||
setDragOffset(0)
|
||||
}
|
||||
|
||||
return (
|
||||
<Popup
|
||||
visible={visible}
|
||||
@@ -54,8 +94,27 @@ const CommonPopup: React.FC<CommonPopupProps> = ({
|
||||
closeable={false}
|
||||
onClose={onClose}
|
||||
className={`${styles['common-popup']} ${className ? className : ''}`}
|
||||
style={{ zIndex: zIndex ? zIndex : undefined, ...style }}
|
||||
style={{
|
||||
zIndex: zIndex ? zIndex : undefined,
|
||||
...style
|
||||
}}
|
||||
>
|
||||
{enableDragToClose && (
|
||||
<View className={styles['common-popup__drag-handle-container']}>
|
||||
<View
|
||||
className={styles['common-popup__drag-handle']}
|
||||
style={{
|
||||
transform: `translateX(-50%) translateY(${dragOffset * 0.3}px)`,
|
||||
opacity: isDragging ? 0.8 : 1,
|
||||
transition: isDragging ? 'none' : 'all 0.3s ease-out'
|
||||
}}
|
||||
onTouchStart={handleTouchStart}
|
||||
onTouchMove={handleTouchMove}
|
||||
onTouchEnd={handleTouchEnd}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{showHeader && (
|
||||
<View className={styles['common-popup__header']}>
|
||||
{typeof title === 'string' ? <Text className={styles['common-popup__title']}>{title}</Text> : title}
|
||||
|
||||
@@ -1,6 +1,25 @@
|
||||
@use '~@/scss/themeColor.scss' as theme;
|
||||
|
||||
.common-popup {
|
||||
.common-popup__drag-handle-container {
|
||||
position: position;
|
||||
}
|
||||
.common-popup__drag-handle {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
left: 50%;
|
||||
width: 32px;
|
||||
height: 4px;
|
||||
background-color: rgba(22, 24, 35, 0.20);
|
||||
border-radius: 2px;
|
||||
z-index: 10;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
|
||||
&:active {
|
||||
background-color: #9ca3af;
|
||||
}
|
||||
}
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
max-height: calc(100vh - 10px);
|
||||
|
||||
@@ -33,8 +33,8 @@
|
||||
.info-popover {
|
||||
position: absolute;
|
||||
bottom: 22px;
|
||||
left: -65px;
|
||||
width: 130px;
|
||||
left: -92px;
|
||||
width: 184px;
|
||||
padding:12px;
|
||||
background: rgba(57, 59, 68, 0.90);
|
||||
color: #fff;
|
||||
@@ -51,7 +51,7 @@
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -6px;
|
||||
left: 68px; /* 对齐图标(宽12px),可按需微调 */
|
||||
left: 94px; /* 对齐图标(宽12px),可按需微调 */
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 6px solid transparent;
|
||||
|
||||
@@ -61,9 +61,7 @@ const HourMinutePicker: React.FC<HourMinutePickerProps> = ({
|
||||
|
||||
return (
|
||||
<View className={styles['hour-minute-picker-popup']}>
|
||||
{/* 拖拽手柄 */}
|
||||
<View className={styles['drag-handle']} />
|
||||
|
||||
{/* 拖拽手柄 */}
|
||||
{/* 时间选择器 */}
|
||||
<View className={styles['picker-container']}>
|
||||
{/* 多列选择器 */}
|
||||
|
||||
@@ -8,14 +8,6 @@
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.drag-handle {
|
||||
width: 40px;
|
||||
height: 4px;
|
||||
background-color: #e0e0e0;
|
||||
border-radius: 2px;
|
||||
margin: 0 auto 20px;
|
||||
}
|
||||
|
||||
.picker-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -6,16 +6,30 @@ import { InputNumber } from '@nutui/nutui-react-taro'
|
||||
interface NumberIntervalProps {
|
||||
value: [number, number]
|
||||
onChange: (value: [number, number]) => void
|
||||
min: number
|
||||
max: number
|
||||
}
|
||||
|
||||
const NumberInterval: React.FC<NumberIntervalProps> = ({
|
||||
value,
|
||||
onChange
|
||||
onChange,
|
||||
min,
|
||||
max
|
||||
}) => {
|
||||
const [minParticipants, maxParticipants] = value || [1, 4]
|
||||
const [minParticipants, maxParticipants] = value || [1, 1]
|
||||
|
||||
const handleChange = (value: [number | string, number | string]) => {
|
||||
onChange([Number(value[0]), Number(value[1])])
|
||||
const newMin = Number(value[0])
|
||||
const newMax = Number(value[1])
|
||||
|
||||
// 确保最少人数不能大于最多人数
|
||||
if (newMin > newMax) {
|
||||
return
|
||||
}
|
||||
|
||||
onChange([newMin, newMax])
|
||||
}
|
||||
|
||||
return (
|
||||
<View className='participants-control-section'>
|
||||
<View className='participant-control'>
|
||||
@@ -24,7 +38,7 @@ const NumberInterval: React.FC<NumberIntervalProps> = ({
|
||||
<InputNumber
|
||||
className="format-width"
|
||||
defaultValue={minParticipants}
|
||||
min={minParticipants}
|
||||
min={min}
|
||||
max={maxParticipants}
|
||||
onChange={(value) => handleChange([value, maxParticipants])}
|
||||
formatter={(value) => `${value}人`}
|
||||
@@ -37,9 +51,9 @@ const NumberInterval: React.FC<NumberIntervalProps> = ({
|
||||
<InputNumber
|
||||
className="format-width"
|
||||
defaultValue={maxParticipants}
|
||||
onChange={(value) => handleChange([value, maxParticipants])}
|
||||
onChange={(value) => handleChange([minParticipants, value])}
|
||||
min={minParticipants}
|
||||
max={maxParticipants}
|
||||
max={max}
|
||||
formatter={(value) => `${value}人`}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -181,7 +181,7 @@
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
&.rotated {
|
||||
transform: rotate(45deg);
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ const TextareaTag: React.FC<TextareaTagProps> = ({
|
||||
value,
|
||||
onChange,
|
||||
placeholder = '请输入',
|
||||
maxLength = 500,
|
||||
maxLength = 1000,
|
||||
options = []
|
||||
}) => {
|
||||
// 处理文本输入变化
|
||||
|
||||
@@ -50,7 +50,8 @@ export const publishBallFormSchema: FormFieldConfig[] = [
|
||||
placeholder: '请选择活动类型',
|
||||
required: true,
|
||||
props: {
|
||||
maxCount: 9
|
||||
maxCount: 9,
|
||||
source: ['album', 'history', 'preset']
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -121,7 +122,8 @@ export const publishBallFormSchema: FormFieldConfig[] = [
|
||||
defaultValue: 1,
|
||||
props: {
|
||||
showSummary: true,
|
||||
summary: '最少1人,最多4人',
|
||||
min: 1,
|
||||
max: 20,
|
||||
}
|
||||
},
|
||||
|
||||
@@ -146,6 +148,9 @@ export const publishBallFormSchema: FormFieldConfig[] = [
|
||||
type: FieldType.TEXTAREATAG,
|
||||
placeholder: '补充性别偏好、特殊要求和注意事项等信息',
|
||||
required: true,
|
||||
props: {
|
||||
maxLength: 1000,
|
||||
},
|
||||
options:[
|
||||
{ label: '仅限男生', value: '仅限男生' },
|
||||
{ label: '仅限女生', value: '仅限女生' },
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useCallback, useEffect } from 'react'
|
||||
import { View, Text, Input, Image, Picker } from '@tarojs/components'
|
||||
import { View, Text, Input, Image } from '@tarojs/components'
|
||||
import PopupGameplay from '../PopupGameplay'
|
||||
import img from '@/config/images';
|
||||
import { FormFieldConfig } from '@/config/formSchema/publishBallFormSchema';
|
||||
@@ -65,10 +65,52 @@ const FormBasicInfo: React.FC<FormBasicInfoProps> = ({
|
||||
})
|
||||
setShowStadiumSelector(false)
|
||||
}
|
||||
|
||||
const handleChange = useCallback((key: string, costValue: any) => {
|
||||
// 价格输入限制:¥0.00–9999.99
|
||||
console.log(costValue, 'valuevalue');
|
||||
|
||||
const handleChange = useCallback((key: string, value: any) => {
|
||||
onChange({...value, [key]: value})
|
||||
}, [onChange])
|
||||
if (key === children[0]?.prop) {
|
||||
// 允许清空
|
||||
if (costValue === '') {
|
||||
onChange({...value, [key]: ''});
|
||||
return;
|
||||
}
|
||||
|
||||
// 只允许数字和一个小数点
|
||||
const filteredValue = costValue.replace(/[^\d.]/g, '');
|
||||
|
||||
// 确保只有一个小数点
|
||||
const parts = filteredValue.split('.');
|
||||
if (parts.length > 2) {
|
||||
return; // 不更新,保持原值
|
||||
}
|
||||
|
||||
// 限制小数点后最多2位
|
||||
if (parts.length === 2 && parts[1].length > 2) {
|
||||
return; // 不更新,保持原值
|
||||
}
|
||||
|
||||
const numValue = parseFloat(filteredValue);
|
||||
if (isNaN(numValue)) {
|
||||
onChange({...value, [key]: ''});
|
||||
return;
|
||||
}
|
||||
if (numValue < 0) {
|
||||
onChange({...value, [key]: '0'});
|
||||
return;
|
||||
}
|
||||
if (numValue > 9999.99) {
|
||||
onChange({...value, [key]: '9999.99'});
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用过滤后的值
|
||||
onChange({...value, [key]: filteredValue});
|
||||
return;
|
||||
}
|
||||
onChange({...value, [key]: costValue})
|
||||
}, [onChange, children])
|
||||
|
||||
useEffect(() => {
|
||||
if (children.length > 2) {
|
||||
@@ -76,6 +118,10 @@ const FormBasicInfo: React.FC<FormBasicInfoProps> = ({
|
||||
setPlayGame(options)
|
||||
}
|
||||
}, [children])
|
||||
|
||||
useEffect(() => {
|
||||
console.log(value, 'valuevalue');
|
||||
}, [value])
|
||||
const renderChildren = () => {
|
||||
return children.map((child: any, index: number) => {
|
||||
return <View className='form-item'>
|
||||
@@ -91,6 +137,7 @@ const FormBasicInfo: React.FC<FormBasicInfoProps> = ({
|
||||
placeholder='请输入'
|
||||
placeholderClass='title-placeholder'
|
||||
type='digit'
|
||||
maxlength={7}
|
||||
value={value[child.prop]}
|
||||
onInput={(e) => handleChange(child.prop, e.detail.value)}
|
||||
/>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react'
|
||||
import { View, Text } from '@tarojs/components'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import { View, Text, Input } from '@tarojs/components'
|
||||
import { Checkbox } from '@nutui/nutui-react-taro'
|
||||
import styles from './index.module.scss'
|
||||
interface FormSwitchProps {
|
||||
@@ -10,7 +10,14 @@ interface FormSwitchProps {
|
||||
}
|
||||
|
||||
const FormSwitch: React.FC<FormSwitchProps> = ({ value, onChange, subTitle, wechatId }) => {
|
||||
|
||||
const [editWechat, setEditWechat] = useState(false)
|
||||
const editWechatId = () => {
|
||||
|
||||
}
|
||||
const setWechatId = useCallback((e: any) => {
|
||||
const value = e.target.value
|
||||
onChange(value)
|
||||
}, [])
|
||||
return (
|
||||
<>
|
||||
<View className={styles['wechat-contact-section']}>
|
||||
@@ -28,7 +35,14 @@ const FormSwitch: React.FC<FormSwitchProps> = ({ value, onChange, subTitle, wech
|
||||
wechatId && (
|
||||
<View className={styles['wechat-contact-id']}>
|
||||
<Text className={styles['wechat-contact-text']}>微信号: {wechatId.replace(/(\d{3})(\d{4})(\d{4})/, '$1 $2 $3')}</Text>
|
||||
<View className={styles['wechat-contact-edit']}>修改</View>
|
||||
<View className={styles['wechat-contact-edit']} onClick={editWechatId}>修改</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
{
|
||||
editWechat && (
|
||||
<View className={styles['wechat-contact-edit']}>
|
||||
<Input value={wechatId} onInput={setWechatId} />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -184,6 +184,9 @@
|
||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||
background: #000;
|
||||
box-shadow: 0 8px 64px 0 rgba(0, 0, 0, 0.10);
|
||||
&.submit-btn-disabled {
|
||||
color: rgba(255, 255, 255, 0.30);
|
||||
}
|
||||
}
|
||||
|
||||
.submit-tip {
|
||||
@@ -192,12 +195,21 @@
|
||||
color: #999;
|
||||
line-height: 1.4;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 12px 0;
|
||||
justify-content: center;
|
||||
padding: 12px 0;
|
||||
align-items: center;
|
||||
.link {
|
||||
color: #007AFF;
|
||||
}
|
||||
}
|
||||
|
||||
.submit-checkbox {
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
:global(.nut-icon-Checked){
|
||||
background: rgba(22, 24, 35, 0.75)!important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 加载状态遮罩保持原样
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
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 ActivityTypeSwitch, { type ActivityType } from '../../components/ActivityTypeSwitch'
|
||||
import CommonDialog from '../../components/CommonDialog'
|
||||
import PublishForm from './publishForm'
|
||||
import { publishBallFormSchema } from '../../config/formSchema/publishBallFormSchema';
|
||||
import { FormFieldConfig, publishBallFormSchema } from '../../config/formSchema/publishBallFormSchema';
|
||||
import { PublishBallFormData } from '../../../types/publishBall';
|
||||
import PublishService from '@/services/publishService';
|
||||
import { getNextHourTime, getEndTime, delay } from '@/utils';
|
||||
@@ -14,7 +14,7 @@ import styles from './index.module.scss'
|
||||
|
||||
const defaultFormData: PublishBallFormData = {
|
||||
title: '',
|
||||
image_list: ['https://static-o.oss-cn-shenzhen.aliyuncs.com/images/tpbj/tpss10.jpg'],
|
||||
image_list: [],
|
||||
timeRange: {
|
||||
start_time: getNextHourTime(),
|
||||
end_time: getEndTime(getNextHourTime())
|
||||
@@ -33,7 +33,7 @@ const defaultFormData: PublishBallFormData = {
|
||||
venue_description: '',
|
||||
venue_image_list: [],
|
||||
},
|
||||
players: [1, 4],
|
||||
players: [1, 1],
|
||||
skill_level: [1.0, 5.0],
|
||||
descriptionInfo: {
|
||||
description: '',
|
||||
@@ -46,32 +46,13 @@ const defaultFormData: PublishBallFormData = {
|
||||
|
||||
const PublishBall: React.FC = () => {
|
||||
const [activityType, setActivityType] = useState<ActivityType>('individual')
|
||||
|
||||
const [isSubmitDisabled, setIsSubmitDisabled] = useState(false)
|
||||
// 获取页面参数并设置导航标题
|
||||
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 [optionsConfig, setOptionsConfig] = useState<FormFieldConfig[]>(publishBallFormSchema)
|
||||
const [formData, setFormData] = useState<PublishBallFormData[]>([
|
||||
defaultFormData
|
||||
])
|
||||
const [checked, setChecked] = useState(true)
|
||||
|
||||
// 删除确认弹窗状态
|
||||
const [deleteConfirm, setDeleteConfirm] = useState<{
|
||||
@@ -103,7 +84,29 @@ const PublishBall: React.FC = () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 检查相邻两组数据是否相同
|
||||
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,
|
||||
@@ -114,6 +117,7 @@ const PublishBall: React.FC = () => {
|
||||
}
|
||||
}])
|
||||
}
|
||||
|
||||
|
||||
// 复制上一场数据
|
||||
const handleCopyPrevious = (index: number) => {
|
||||
@@ -124,7 +128,7 @@ const PublishBall: React.FC = () => {
|
||||
return newData
|
||||
})
|
||||
Taro.showToast({
|
||||
title: '已复制上一场数据',
|
||||
title: '复制上一场填入',
|
||||
icon: 'success'
|
||||
})
|
||||
}
|
||||
@@ -158,42 +162,59 @@ const PublishBall: React.FC = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const validateFormData = (formData: PublishBallFormData) => {
|
||||
const validateFormData = (formData: PublishBallFormData, isOnSubmit: boolean = false) => {
|
||||
const { activityInfo, image_list, title } = formData;
|
||||
const { play_type, price, location_name } = activityInfo;
|
||||
if (!image_list?.length) {
|
||||
Taro.showToast({
|
||||
title: `请上传活动封面`,
|
||||
icon: 'none'
|
||||
})
|
||||
if (!isOnSubmit) {
|
||||
Taro.showToast({
|
||||
title: `请上传活动封面`,
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
return false
|
||||
}
|
||||
if (!title) {
|
||||
Taro.showToast({
|
||||
title: `请输入活动标题`,
|
||||
icon: 'none'
|
||||
})
|
||||
if (!isOnSubmit) {
|
||||
Taro.showToast({
|
||||
title: `请输入活动标题`,
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
return false
|
||||
}
|
||||
if (!price || (typeof price === 'number' && price <= 0) || (typeof price === 'string' && !price.trim())) {
|
||||
Taro.showToast({
|
||||
title: `请输入费用`,
|
||||
icon: 'none'
|
||||
})
|
||||
if (!isOnSubmit) {
|
||||
Taro.showToast({
|
||||
title: `请输入费用`,
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
return false
|
||||
}
|
||||
if (!play_type || !play_type.trim()) {
|
||||
Taro.showToast({
|
||||
title: `请选择玩法类型`,
|
||||
icon: 'none'
|
||||
})
|
||||
if (!isOnSubmit) {
|
||||
Taro.showToast({
|
||||
title: `请选择玩法类型`,
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
return false
|
||||
}
|
||||
if (!location_name || !location_name.trim()) {
|
||||
Taro.showToast({
|
||||
title: `请选择场地`,
|
||||
icon: 'none'
|
||||
})
|
||||
if (!isOnSubmit) {
|
||||
Taro.showToast({
|
||||
title: `请选择场地`,
|
||||
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
|
||||
@@ -208,7 +229,7 @@ const PublishBall: React.FC = () => {
|
||||
if (!isValid) {
|
||||
return
|
||||
}
|
||||
const { activityInfo, descriptionInfo, timeRange, players, skill_level, ...rest } = formData[0];
|
||||
const { activityInfo, descriptionInfo, timeRange, players, skill_level,image_list, ...rest } = formData[0];
|
||||
const options = {
|
||||
...rest,
|
||||
...activityInfo,
|
||||
@@ -217,7 +238,8 @@ const PublishBall: React.FC = () => {
|
||||
max_players: players[1],
|
||||
current_players: players[0],
|
||||
skill_level_min: skill_level[0],
|
||||
skill_level_max: skill_level[1]
|
||||
skill_level_max: skill_level[1],
|
||||
image_list: image_list.map(item => item.url)
|
||||
}
|
||||
const res = await PublishService.createPersonal(options);
|
||||
if (res.code === 0 && res.data) {
|
||||
@@ -244,9 +266,16 @@ const PublishBall: React.FC = () => {
|
||||
if (!isValid) {
|
||||
return
|
||||
}
|
||||
formData.forEach(async (item) => {
|
||||
if (checkAdjacentDataSame(formData)) {
|
||||
Taro.showToast({
|
||||
title: '信息不可与前序场完全一致',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
const options = formData.map((item) => {
|
||||
const { activityInfo, descriptionInfo, timeRange, players, skill_level, ...rest } = item;
|
||||
const options = {
|
||||
return {
|
||||
...rest,
|
||||
...activityInfo,
|
||||
...descriptionInfo,
|
||||
@@ -254,21 +283,89 @@ const PublishBall: React.FC = () => {
|
||||
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'
|
||||
})
|
||||
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: `/pages/detail/index?id=${res.data?.[0].id || 1}&from=publish&autoShare=1`
|
||||
})
|
||||
} else {
|
||||
Taro.showToast({
|
||||
title: res.message,
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const initFormData = () => {
|
||||
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 newFormSchema = publishBallFormSchema.reduce((acc, item) => {
|
||||
if (item.prop === 'is_wechat_contact') {
|
||||
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)
|
||||
setFormData([defaultFormData])
|
||||
}
|
||||
// 根据type设置导航标题
|
||||
if (type === 'group') {
|
||||
Taro.setNavigationBarTitle({
|
||||
title: '发布畅打活动'
|
||||
})
|
||||
} else {
|
||||
Taro.setNavigationBarTitle({
|
||||
title: '发布'
|
||||
})
|
||||
}
|
||||
}
|
||||
handleActivityTypeChange(type)
|
||||
}
|
||||
}
|
||||
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 (
|
||||
@@ -314,7 +411,7 @@ const PublishBall: React.FC = () => {
|
||||
<PublishForm
|
||||
formData={item}
|
||||
onChange={(key, value) => updateFormData(key, value, index)}
|
||||
optionsConfig={publishBallFormSchema}
|
||||
optionsConfig={optionsConfig}
|
||||
/>
|
||||
</View>
|
||||
))
|
||||
@@ -339,16 +436,31 @@ const PublishBall: React.FC = () => {
|
||||
contentTitle="确认移除该场次?"
|
||||
contentDesc="该操作不可恢复"
|
||||
/>
|
||||
|
||||
{/* 完成按钮 */}
|
||||
<View className={styles['submit-section']}>
|
||||
<Button className={styles['submit-btn']} onClick={handleSubmit}>
|
||||
<Button className={`${styles['submit-btn']} ${isSubmitDisabled ? styles['submit-btn-disabled'] : ''}`} onClick={handleSubmit}>
|
||||
发布
|
||||
</Button>
|
||||
<Text className={styles['submit-tip']}>
|
||||
点击确定发布约球,即表示已经同意条款
|
||||
<Text className={styles['link']}>《约球规则》</Text>
|
||||
</Text>
|
||||
{
|
||||
activityType === 'individual' && (
|
||||
<Text className={styles['submit-tip']}>
|
||||
点击确定发布约球,即表示已经同意条款
|
||||
<Text className={styles['link']}>《约球规则》</Text>
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
{
|
||||
activityType === 'group' && (
|
||||
<View className={styles['submit-tip']}>
|
||||
<Checkbox
|
||||
className={styles['submit-checkbox']}
|
||||
checked={checked}
|
||||
onChange={onCheckedChange}
|
||||
/>
|
||||
已认证 徐汇爱打球官方球场,请严格遵守签约协议
|
||||
</View>
|
||||
)
|
||||
}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
|
||||
@@ -31,13 +31,16 @@ const PublishForm: React.FC<{
|
||||
// 字典数据相关
|
||||
const { getDictionaryValue } = useDictionaryActions()
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
setCoverImages(formData.image_list)
|
||||
}, [formData.image_list])
|
||||
|
||||
// 处理封面图片变化
|
||||
const handleCoverImagesChange = (fn: (images: CoverImage[]) => CoverImage[]) => {
|
||||
if (fn instanceof Function) {
|
||||
setCoverImages(fn(coverImages))
|
||||
} else {
|
||||
setCoverImages(fn)
|
||||
}
|
||||
const newImages = fn instanceof Function ? fn(coverImages) : fn
|
||||
setCoverImages(newImages)
|
||||
onChange('image_list', newImages)
|
||||
}
|
||||
|
||||
// 更新表单数据
|
||||
@@ -113,12 +116,19 @@ const PublishForm: React.FC<{
|
||||
return '';
|
||||
}
|
||||
|
||||
const getPlayersText = (players: [number, number]) => {
|
||||
const [min, max] = players
|
||||
return `最少${min}人,最多${max}人`
|
||||
}
|
||||
|
||||
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>
|
||||
if (item.prop === 'players') {
|
||||
return <Text className={styles['section-summary']}>{getPlayersText(formData.players)}</Text>
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ export interface PublishBallData {
|
||||
skill_level_max: number // 水平要求(NTRP)
|
||||
description: string // 备注
|
||||
description_tag: string[] // 备注标签
|
||||
is_substitute_supported: boolean // 是否支持替补
|
||||
is_wechat_contact: boolean // 是否需要微信联系
|
||||
is_substitute_supported?: boolean // 是否支持替补
|
||||
is_wechat_contact?: boolean // 是否需要微信联系
|
||||
wechat_contact?: string // 微信联系
|
||||
}
|
||||
|
||||
@@ -123,8 +123,8 @@ class PublishService {
|
||||
showLoading: false })
|
||||
}
|
||||
// 畅打发布
|
||||
async create_play_pmoothly(data: PublishBallData): Promise<ApiResponse<Response>> {
|
||||
return httpService.post('/games/create_play_pmoothly', data, {
|
||||
async create_play_pmoothlys(data: {rows: PublishBallData[]}): Promise<ApiResponse<Response>> {
|
||||
return httpService.post('/games/create_play_pmoothlys', data, {
|
||||
showLoading: true,
|
||||
loadingText: '发布中...'
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user