修改地点选择
This commit is contained in:
@@ -25,7 +25,7 @@ const CommonPopup: React.FC<CommonPopupProps> = ({
|
|||||||
onClose,
|
onClose,
|
||||||
className,
|
className,
|
||||||
title,
|
title,
|
||||||
showHeader = true,
|
showHeader = false,
|
||||||
hideFooter = false,
|
hideFooter = false,
|
||||||
cancelText = '返回',
|
cancelText = '返回',
|
||||||
confirmText = '完成',
|
confirmText = '完成',
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
.common-popup {
|
.common-popup {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
max-height: 80vh;
|
max-height: calc(100vh - 10px);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background-color: theme.$page-background-color;
|
background-color: theme.$page-background-color;
|
||||||
@@ -20,17 +20,18 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.common-popup__body {
|
.common-popup__body {
|
||||||
padding: 16px;
|
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.common-popup__footer {
|
.common-popup__footer {
|
||||||
padding: 12px 16px;
|
padding: 8px 10px 0 10px;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 12px;
|
gap: 8px;
|
||||||
border-top: 1px solid #f0f1f5;
|
background: #FFF;
|
||||||
|
padding-bottom: env(safe-area-inset-bottom);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.common-popup__btn {
|
.common-popup__btn {
|
||||||
@@ -41,9 +42,21 @@
|
|||||||
background: #f5f6f7;
|
background: #f5f6f7;
|
||||||
color: #1f2329;
|
color: #1f2329;
|
||||||
border: none;
|
border: none;
|
||||||
|
width: 154px;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 0.5px solid rgba(0, 0, 0, 0.06);
|
||||||
|
background: #fff;
|
||||||
|
padding: 4px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.common-popup__btn-confirm {
|
.common-popup__btn-confirm {
|
||||||
/* 使用按钮组件的 primary 样式 */
|
/* 使用按钮组件的 primary 样式 */
|
||||||
|
width: 154px;
|
||||||
|
height: 36px;
|
||||||
|
border: 0.5px solid rgba(0, 0, 0, 0.06);
|
||||||
|
background: #000;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 4px 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -26,6 +26,11 @@ const FormSwitch: React.FC<FormSwitchProps> = ({ value, onChange, subTitle, info
|
|||||||
{showTip && <View className='info-popover-mask' onClick={() => setShowTip(false)} />}
|
{showTip && <View className='info-popover-mask' onClick={() => setShowTip(false)} />}
|
||||||
<View className='auto-degrade-section'>
|
<View className='auto-degrade-section'>
|
||||||
<View className='auto-degrade-item'>
|
<View className='auto-degrade-item'>
|
||||||
|
<Checkbox
|
||||||
|
className='auto-degrade-checkbox nut-checkbox-black'
|
||||||
|
checked={value}
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
<View className='auto-degrade-content'>
|
<View className='auto-degrade-content'>
|
||||||
<Text className='auto-degrade-text'>{subTitle}</Text>
|
<Text className='auto-degrade-text'>{subTitle}</Text>
|
||||||
{
|
{
|
||||||
@@ -43,11 +48,6 @@ const FormSwitch: React.FC<FormSwitchProps> = ({ value, onChange, subTitle, info
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
</View>
|
</View>
|
||||||
<Checkbox
|
|
||||||
className='auto-degrade-checkbox nut-checkbox-black'
|
|
||||||
checked={value}
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
.auto-degrade-item {
|
.auto-degrade-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
gap: 8px;
|
||||||
.auto-degrade-content {
|
.auto-degrade-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -62,10 +62,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.auto-degrade-checkbox {
|
.auto-degrade-checkbox {
|
||||||
.nut-checkbox-icon {
|
width: 18px;
|
||||||
width: 14px;
|
height: 18px;
|
||||||
height: 14px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,8 @@
|
|||||||
.select-stadium-popup {
|
|
||||||
.nut-popup {
|
|
||||||
max-height: 85vh;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.nut-popup__content {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.select-stadium {
|
.select-stadium {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 85vh;
|
height: calc(100vh - 10px);
|
||||||
background: #f5f5f5;
|
background: #f5f5f5;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -21,19 +12,46 @@
|
|||||||
// 搜索区域
|
// 搜索区域
|
||||||
.search-section {
|
.search-section {
|
||||||
background: #f5f5f5;
|
background: #f5f5f5;
|
||||||
padding: 16px;
|
padding: 26px 15px 0px 15px;
|
||||||
|
|
||||||
.search-wrapper {
|
.search-wrapper {
|
||||||
background: white;
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 12px 16px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|
||||||
.search-icon {
|
.search-bar {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: #999;
|
display: flex;
|
||||||
|
height: 44px;
|
||||||
|
padding: 0 12px;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
flex: 1;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 0.5px solid rgba(0, 0, 0, 0.06);
|
||||||
|
background: #FFF;
|
||||||
|
box-shadow: 0 4px 48px 0 rgba(0, 0, 0, 0.08);
|
||||||
|
.search-icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
border-radius: 50%;
|
||||||
|
cursor: pointer;
|
||||||
|
&:active {
|
||||||
|
background: rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
.clear-icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-input {
|
.search-input {
|
||||||
@@ -43,26 +61,32 @@
|
|||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
.search-placeholder{
|
||||||
|
color: rgba(60, 60, 67, 0.60);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.map-btn {
|
.map-btn {
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
border-radius: 16px;
|
|
||||||
background: #f0f8ff;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
height: 44px;
|
||||||
|
padding: 0 12px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
gap: 2px;
|
||||||
cursor: pointer;
|
border-radius: 999px;
|
||||||
transition: background-color 0.2s;
|
border: 0.5px solid rgba(0, 0, 0, 0.06);
|
||||||
|
background: #FFF;
|
||||||
|
box-shadow: 0 4px 48px 0 rgba(0, 0, 0, 0.08);
|
||||||
|
box-sizing: border-box;
|
||||||
&:active {
|
&:active {
|
||||||
background: #e0f0ff;
|
background: #e0f0ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.map-icon {
|
.map-icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
.map-text {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: #007AFF;
|
color: #000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,34 +94,42 @@
|
|||||||
|
|
||||||
// 热门球场区域
|
// 热门球场区域
|
||||||
.hot-section {
|
.hot-section {
|
||||||
background: #f5f5f5;
|
padding: 23px 20px 10px 20px;
|
||||||
padding: 0 16px 16px 16px;
|
|
||||||
|
|
||||||
.hot-header {
|
.hot-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 30px;
|
|
||||||
|
|
||||||
.hot-title {
|
.hot-title {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #333;
|
color: #000;
|
||||||
|
}
|
||||||
|
.hot-stadium-line{
|
||||||
|
height: 6px;
|
||||||
|
width: 1px;
|
||||||
|
background: rgba(22, 24, 35, 0.12);
|
||||||
|
margin: 0 12px;;
|
||||||
}
|
}
|
||||||
|
|
||||||
.booking-section {
|
.booking-section {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
color: rgba(0, 0, 0, 0.50);
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
.booking-title {
|
.booking-title {
|
||||||
font-size: 16px;
|
font-size: 15px;
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.booking-status {
|
.booking-status {
|
||||||
font-size: 12px;
|
display: flex;
|
||||||
color: #999;
|
padding: 2px 5px;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 0.5px solid rgba(0, 0, 0, 0.16);
|
||||||
|
background: #FFF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,44 +139,51 @@
|
|||||||
.stadium-list {
|
.stadium-list {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
width: auto;
|
width: auto;
|
||||||
padding: 0 16px;
|
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
|
|
||||||
.stadium-item {
|
.stadium-item {
|
||||||
border-radius: 20px;
|
padding: 16px 20px;
|
||||||
height: 40px;
|
|
||||||
margin-bottom: 6px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
|
||||||
color: #000;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
border: 1px solid rgba(0, 0, 0, 0.12);
|
gap: 12px;
|
||||||
&:last-child {
|
.stadium-item-left{
|
||||||
margin-bottom: 0;
|
display: flex;
|
||||||
}
|
padding: 14px;
|
||||||
|
justify-content: center;
|
||||||
&.selected {
|
align-items: center;
|
||||||
background: #f0f8ff;
|
border-radius: 12px;
|
||||||
|
border: 0.5px solid rgba(0, 0, 0, 0.08);
|
||||||
&::after {
|
background: rgba(0, 0, 0, 0.06);
|
||||||
content: '✓';
|
.stadium-icon{
|
||||||
position: absolute;
|
width: 20px;
|
||||||
right: 16px;
|
height: 20px;
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
color: #007AFF;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.stadium-item-right{
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
.stadium-name{
|
.stadium-name{
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: #333;
|
color: #000;
|
||||||
line-height: 1.4;
|
font-weight: 600;
|
||||||
padding-right: 30px;
|
line-height: 24px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.stadium-address{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
color: rgba(0, 0, 0, 0.80);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.stadium-map-icon{
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { View, Text, Input, ScrollView } from '@tarojs/components'
|
import { View, Text, Input, ScrollView, Image } from '@tarojs/components'
|
||||||
import { Popup } from '@nutui/nutui-react-taro'
|
import { Popup } from '@nutui/nutui-react-taro'
|
||||||
import Taro from '@tarojs/taro'
|
import Taro from '@tarojs/taro'
|
||||||
import StadiumDetail from './StadiumDetail'
|
import StadiumDetail from './StadiumDetail'
|
||||||
import CommonPopup from '../CommonPopup'
|
import CommonPopup from '../CommonPopup'
|
||||||
import './SelectStadium.scss'
|
import './SelectStadium.scss'
|
||||||
|
import images from '@/config/images'
|
||||||
|
|
||||||
export interface Stadium {
|
export interface Stadium {
|
||||||
id: string
|
id?: string
|
||||||
name: string
|
name: string
|
||||||
address?: string
|
address?: string
|
||||||
|
istance?: string
|
||||||
|
longitude?: number
|
||||||
|
latitude?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SelectStadiumProps {
|
interface SelectStadiumProps {
|
||||||
@@ -19,15 +23,15 @@ interface SelectStadiumProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const stadiumList: Stadium[] = [
|
const stadiumList: Stadium[] = [
|
||||||
{ id: '1', name: '静安网球馆', address: '静安区' },
|
{ id: '1', name: '静安网球馆', address: '浦东新区东园路18号', istance: '100米' , longitude: 121.4367, latitude: 31.2304},
|
||||||
{ id: '2', name: '芦湾体育馆', address: '芦湾区' },
|
{ id: '2', name: '芦湾体育馆', address: '浦东新区东园路18号', istance: '100米' , longitude: 121.4367, latitude: 31.2304 },
|
||||||
{ id: '3', name: '静安网球馆', address: '静安区' },
|
{ id: '3', name: '静安网球馆', address: '浦东新区东园路18号', istance: '100米' , longitude: 121.4367, latitude: 31.2304 },
|
||||||
{ id: '4', name: '徐汇游泳中心', address: '徐汇区' },
|
{ id: '4', name: '徐汇游泳中心', address: '浦东新区东园路18号', istance: '100米' , longitude: 121.4367, latitude: 31.2304 },
|
||||||
{ id: '5', name: '汇龙新城小区', address: '新城区' },
|
{ id: '5', name: '汇龙新城小区', address: '浦东新区东园路18号', istance: '100米' , longitude: 121.4367, latitude: 31.2304 },
|
||||||
{ id: '6', name: '翠湖御苑小区', address: '翠湖区' },
|
{ id: '6', name: '翠湖御苑小区', address: '浦东新区东园路18号', istance: '100米' , longitude: 121.4367, latitude: 31.2304 },
|
||||||
{ id: '7', name: '仁恒河滨花园网球场', address: '浦东新区' },
|
{ id: '7', name: '仁恒河滨花园网球场', address: '浦东新区东园路18号', istance: '100米' , longitude: 121.4367, latitude: 31.2304 },
|
||||||
{ id: '8', name: 'Our Tennis 东江球场', address: '浦东新区' },
|
{ id: '8', name: 'Our Tennis 东江球场', address: '浦东新区东园路18号', istance: '100米' , longitude: 121.4367, latitude: 31.2304 },
|
||||||
{ id: '9', name: '上海琦梦网球俱乐部', address: '浦东新区' }
|
{ id: '9', name: '上海琦梦网球俱乐部', address: '浦东新区东园路18号', istance: '100米' , longitude: 121.4367, latitude: 31.2304 }
|
||||||
]
|
]
|
||||||
|
|
||||||
const SelectStadium: React.FC<SelectStadiumProps> = ({
|
const SelectStadium: React.FC<SelectStadiumProps> = ({
|
||||||
@@ -68,12 +72,13 @@ const SelectStadium: React.FC<SelectStadiumProps> = ({
|
|||||||
Taro.chooseLocation({
|
Taro.chooseLocation({
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
console.log('选择位置成功:', res)
|
console.log('选择位置成功:', res)
|
||||||
// 可以根据位置信息搜索附近的场馆
|
setSelectedStadium({
|
||||||
// 这里可以调用相关API获取附近场馆信息
|
name: res.name,
|
||||||
Taro.showToast({
|
address: res.address,
|
||||||
title: '位置选择成功',
|
longitude: res.longitude,
|
||||||
icon: 'success'
|
latitude: res.latitude
|
||||||
})
|
})
|
||||||
|
setShowDetail(true)
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
console.error('选择位置失败:', err)
|
console.error('选择位置失败:', err)
|
||||||
@@ -112,23 +117,31 @@ const SelectStadium: React.FC<SelectStadiumProps> = ({
|
|||||||
setSearchValue('')
|
setSearchValue('')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const markSearchText = (text: string) => {
|
||||||
|
return text.replace(searchValue, `<span style="color: #007AFF;">${searchValue}</span>`)
|
||||||
|
}
|
||||||
|
|
||||||
// 如果显示详情页面
|
// 如果显示详情页面
|
||||||
if (showDetail && selectedStadium) {
|
if (showDetail && selectedStadium) {
|
||||||
return (
|
return (
|
||||||
<Popup
|
<CommonPopup
|
||||||
visible={visible}
|
visible={visible}
|
||||||
|
onClose={handleCancel}
|
||||||
|
cancelText="返回"
|
||||||
|
confirmText="确认"
|
||||||
|
className="select-stadium-popup"
|
||||||
|
onCancel={handleCancel}
|
||||||
|
onConfirm={handleListConfirm}
|
||||||
position="bottom"
|
position="bottom"
|
||||||
round
|
round
|
||||||
closeable={false}
|
|
||||||
onClose={handleCancel}
|
|
||||||
className="select-stadium-popup"
|
|
||||||
>
|
>
|
||||||
<StadiumDetail
|
<StadiumDetail
|
||||||
stadium={selectedStadium}
|
stadium={selectedStadium}
|
||||||
onBack={handleBackToList}
|
onBack={handleBackToList}
|
||||||
onConfirm={handleConfirm}
|
onConfirm={handleConfirm}
|
||||||
/>
|
/>
|
||||||
</Popup>
|
</CommonPopup>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,6 +149,7 @@ const SelectStadium: React.FC<SelectStadiumProps> = ({
|
|||||||
return (
|
return (
|
||||||
<CommonPopup
|
<CommonPopup
|
||||||
visible={visible}
|
visible={visible}
|
||||||
|
hideFooter
|
||||||
onClose={handleCancel}
|
onClose={handleCancel}
|
||||||
cancelText="返回"
|
cancelText="返回"
|
||||||
confirmText="完成"
|
confirmText="完成"
|
||||||
@@ -150,16 +164,29 @@ const SelectStadium: React.FC<SelectStadiumProps> = ({
|
|||||||
{/* 搜索框 */}
|
{/* 搜索框 */}
|
||||||
<View className='search-section'>
|
<View className='search-section'>
|
||||||
<View className='search-wrapper'>
|
<View className='search-wrapper'>
|
||||||
<View className='search-icon'>🔍</View>
|
<View className='search-bar'>
|
||||||
|
<Image src={images.ICON_SEARCH} className='search-icon' />
|
||||||
<Input
|
<Input
|
||||||
className='search-input'
|
className='search-input'
|
||||||
placeholder='搜索'
|
placeholder='搜索'
|
||||||
|
placeholderClass='search-placeholder'
|
||||||
value={searchValue}
|
value={searchValue}
|
||||||
onInput={handleSearchInput}
|
onInput={handleSearchInput}
|
||||||
/>
|
/>
|
||||||
<View className='map-btn' onClick={handleMapLocation}>
|
{searchValue && (
|
||||||
<Text className='map-icon'>📍</Text>
|
<View className='clear-btn' onClick={() => setSearchValue('')}>
|
||||||
|
<Image src={images.ICON_REMOVE} className='clear-icon' />
|
||||||
</View>
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
{
|
||||||
|
!searchValue && (
|
||||||
|
<View className='map-btn' onClick={handleMapLocation}>
|
||||||
|
<Image src={images.ICON_MAP} className='map-icon' />
|
||||||
|
<Text className='map-text'>地图</Text>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
@@ -167,6 +194,7 @@ const SelectStadium: React.FC<SelectStadiumProps> = ({
|
|||||||
<View className='hot-section'>
|
<View className='hot-section'>
|
||||||
<View className='hot-header'>
|
<View className='hot-header'>
|
||||||
<Text className='hot-title'>热门球场</Text>
|
<Text className='hot-title'>热门球场</Text>
|
||||||
|
<View className='hot-stadium-line'></View>
|
||||||
<View className='booking-section'>
|
<View className='booking-section'>
|
||||||
<Text className='booking-title'>预定球场</Text>
|
<Text className='booking-title'>预定球场</Text>
|
||||||
<Text className='booking-status'>敬请期待</Text>
|
<Text className='booking-status'>敬请期待</Text>
|
||||||
@@ -182,9 +210,37 @@ const SelectStadium: React.FC<SelectStadiumProps> = ({
|
|||||||
className={`stadium-item ${selectedStadium?.id === stadium.id ? 'selected' : ''}`}
|
className={`stadium-item ${selectedStadium?.id === stadium.id ? 'selected' : ''}`}
|
||||||
onClick={() => handleStadiumSelect(stadium)}
|
onClick={() => handleStadiumSelect(stadium)}
|
||||||
>
|
>
|
||||||
<Text className='stadium-name'>{stadium.name}</Text>
|
<View className='stadium-item-left'>
|
||||||
|
<Image src={images.ICON_STADIUM} className='stadium-icon' />
|
||||||
|
</View>
|
||||||
|
<View className='stadium-item-right'>
|
||||||
|
<View className='stadium-name' dangerouslySetInnerHTML={{ __html: markSearchText(stadium.name) }}></View>
|
||||||
|
<View className='stadium-address'>
|
||||||
|
<Text>{stadium.istance} · </Text>
|
||||||
|
<Text>{stadium.address}</Text>
|
||||||
|
<Image src={images.ICON_ARRORW_SMALL} className='stadium-map-icon' />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
|
{
|
||||||
|
searchValue && (<View
|
||||||
|
className={`stadium-item`}
|
||||||
|
onClick={() => handleMapLocation()}
|
||||||
|
>
|
||||||
|
<View className='stadium-item-left'>
|
||||||
|
<Image src={images.ICON_MAP_SEARCH} className='stadium-icon' />
|
||||||
|
</View>
|
||||||
|
<View className='stadium-item-right'>
|
||||||
|
<View className='stadium-name'>没有找到球场?去地图定位</View>
|
||||||
|
<View className='stadium-address'>
|
||||||
|
<Text>腾讯地图</Text>
|
||||||
|
<Image src={images.ICON_ARRORW_SMALL} className='stadium-map-icon' />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>)
|
||||||
|
}
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</View>
|
</View>
|
||||||
</CommonPopup>
|
</CommonPopup>
|
||||||
|
|||||||
@@ -9,40 +9,59 @@
|
|||||||
padding-bottom: env(safe-area-inset-bottom);
|
padding-bottom: env(safe-area-inset-bottom);
|
||||||
|
|
||||||
// 已选球场
|
// 已选球场
|
||||||
.selected-venue-section {
|
// 场馆列表
|
||||||
padding: 20px 16px 16px 16px;
|
.stadium-item {
|
||||||
border-bottom: 1px solid #e5e5e5;
|
padding: 32px 20px 16px 20px;
|
||||||
flex-shrink: 0;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
.section-title {
|
position: relative;
|
||||||
font-size: 16px;
|
gap: 12px;
|
||||||
font-weight: 600;
|
.stadium-item-left{
|
||||||
color: #333;
|
display: flex;
|
||||||
margin-bottom: 12px;
|
padding: 14px;
|
||||||
display: block;
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 0.5px solid rgba(0, 0, 0, 0.08);
|
||||||
|
background: rgba(0, 0, 0, 0.06);
|
||||||
|
.stadium-icon{
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.venue-button {
|
.stadium-item-right{
|
||||||
background: #333;
|
flex: 1;
|
||||||
border-radius: 24px;
|
display: flex;
|
||||||
padding: 12px 24px;
|
flex-direction: column;
|
||||||
display: inline-block;
|
gap: 4px;
|
||||||
|
.stadium-name{
|
||||||
.venue-name {
|
|
||||||
color: white;
|
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 500;
|
color: #000;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 24px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.stadium-address{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
color: rgba(0, 0, 0, 0.80);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.stadium-map-icon{
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 场地类型
|
// 场地类型
|
||||||
.venue-type-section {
|
.venue-type-section {
|
||||||
padding: 16px;
|
|
||||||
border-bottom: 1px solid #e5e5e5;
|
border-bottom: 1px solid #e5e5e5;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
|
||||||
.section-title {
|
.section-title {
|
||||||
|
padding: 18px 20px 10px 20px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #333;
|
color: #333;
|
||||||
@@ -53,7 +72,7 @@
|
|||||||
.option-buttons {
|
.option-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
|
padding: 0 15px;
|
||||||
.option-btn {
|
.option-btn {
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import React, { useState } from 'react'
|
import React, { useState, useCallback } from 'react'
|
||||||
import { View, Text, Input } from '@tarojs/components'
|
import { View, Text, Image } from '@tarojs/components'
|
||||||
|
import images from '@/config/images'
|
||||||
import './StadiumDetail.scss'
|
import './StadiumDetail.scss'
|
||||||
|
import TextareaTag from '@/components/TextareaTag'
|
||||||
|
import CoverImageUpload, { type CoverImage } from '@/components/CoverImageUpload'
|
||||||
|
|
||||||
export interface Stadium {
|
export interface Stadium {
|
||||||
id: string
|
id?: string
|
||||||
name: string
|
name: string
|
||||||
address?: string
|
address?: string
|
||||||
}
|
}
|
||||||
@@ -14,6 +17,29 @@ interface StadiumDetailProps {
|
|||||||
onConfirm: (stadium: Stadium, venueType: string, groundMaterial: string, additionalInfo: string) => void
|
onConfirm: (stadium: Stadium, venueType: string, groundMaterial: string, additionalInfo: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const stadiumInfo = [
|
||||||
|
{
|
||||||
|
label: '场地类型',
|
||||||
|
options: ['室内', '室外', '室外雨棚'],
|
||||||
|
type: 'tags'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '地面材质',
|
||||||
|
options: ['硬地', '红土', '草地'],
|
||||||
|
type: 'tags'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '场地信息补充',
|
||||||
|
options: ['1号场', '2号场', '3号场', '4号场', '有空调', '6号场'],
|
||||||
|
type: 'textareaTag'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '场地预定截图',
|
||||||
|
options: ['有其他场地信息可备注'],
|
||||||
|
type: 'image'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
const StadiumDetail: React.FC<StadiumDetailProps> = ({
|
const StadiumDetail: React.FC<StadiumDetailProps> = ({
|
||||||
stadium,
|
stadium,
|
||||||
onBack,
|
onBack,
|
||||||
@@ -22,79 +48,108 @@ const StadiumDetail: React.FC<StadiumDetailProps> = ({
|
|||||||
const [venueType, setVenueType] = useState('室内')
|
const [venueType, setVenueType] = useState('室内')
|
||||||
const [groundMaterial, setGroundMaterial] = useState('硬地')
|
const [groundMaterial, setGroundMaterial] = useState('硬地')
|
||||||
const [additionalInfo, setAdditionalInfo] = useState('')
|
const [additionalInfo, setAdditionalInfo] = useState('')
|
||||||
|
const [imagesList, setImagesList] = useState<CoverImage[]>([])
|
||||||
const venueTypes = ['室内', '室外', '室外雨棚']
|
|
||||||
const groundMaterials = ['硬地', '红土', '草地']
|
|
||||||
|
|
||||||
const handleConfirm = () => {
|
const handleConfirm = () => {
|
||||||
onConfirm(stadium, venueType, groundMaterial, additionalInfo)
|
onConfirm(stadium, venueType, groundMaterial, additionalInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleMapLocation = () => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSelectedByLabel = useCallback((label: string) => {
|
||||||
|
if (label === '场地类型') return venueType
|
||||||
|
if (label === '地面材质') return groundMaterial
|
||||||
|
return ''
|
||||||
|
}, [venueType, groundMaterial])
|
||||||
|
|
||||||
|
const setSelectedByLabel = useCallback((label: string, value: string) => {
|
||||||
|
if (label === '场地类型') {
|
||||||
|
setVenueType(value)
|
||||||
|
} else if (label === '地面材质') {
|
||||||
|
setGroundMaterial(value)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
console.log(stadium,'stadiumstadium');
|
||||||
return (
|
return (
|
||||||
<View className='stadium-detail'>
|
<View className='stadium-detail'>
|
||||||
{/* 已选球场 */}
|
{/* 已选球场 */}
|
||||||
<View className='selected-venue-section'>
|
<View
|
||||||
<Text className='section-title'>已选球场</Text>
|
className={`stadium-item`}
|
||||||
<View className='venue-button'>
|
onClick={() => handleMapLocation()}
|
||||||
<Text className='venue-name'>{stadium.name}</Text>
|
>
|
||||||
|
<View className='stadium-item-left'>
|
||||||
|
<Image src={images.ICON_STADIUM} className='stadium-icon' />
|
||||||
|
</View>
|
||||||
|
<View className='stadium-item-right'>
|
||||||
|
<View className='stadium-name'>{stadium.name}</View>
|
||||||
|
<View className='stadium-address'>
|
||||||
|
<Text>{stadium.address}</Text>
|
||||||
|
<Image src={images.ICON_ARRORW_SMALL} className='stadium-map-icon' />
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* 场地类型 */}
|
{stadiumInfo.map((item) => {
|
||||||
<View className='venue-type-section'>
|
if (item.type === 'tags') {
|
||||||
<Text className='section-title'>场地类型</Text>
|
const selected = getSelectedByLabel(item.label)
|
||||||
|
return (
|
||||||
|
<View className='venue-type-section' key={item.label}>
|
||||||
|
<Text className='section-title'>{item.label}</Text>
|
||||||
<View className='option-buttons'>
|
<View className='option-buttons'>
|
||||||
{venueTypes.map((type) => (
|
{item.options.map((opt) => (
|
||||||
<View
|
<View
|
||||||
key={type}
|
key={opt}
|
||||||
className={`option-btn ${venueType === type ? 'selected' : ''}`}
|
className={`option-btn ${selected === opt ? 'selected' : ''}`}
|
||||||
onClick={() => setVenueType(type)}
|
onClick={() => setSelectedByLabel(item.label, opt)}
|
||||||
>
|
>
|
||||||
<Text className='option-text'>{type}</Text>
|
<Text className='option-text'>{opt}</Text>
|
||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
)
|
||||||
{/* 地面材质 */}
|
}
|
||||||
<View className='ground-material-section'>
|
|
||||||
<Text className='section-title'>地面材质</Text>
|
if (item.type === 'textareaTag') {
|
||||||
<View className='option-buttons'>
|
return (
|
||||||
{groundMaterials.map((material) => (
|
<View className='venue-type-section' key={item.label}>
|
||||||
<View
|
<Text className='section-title'>{item.label}</Text>
|
||||||
key={material}
|
<View className='option-buttons'>
|
||||||
className={`option-btn ${groundMaterial === material ? 'selected' : ''}`}
|
<TextareaTag
|
||||||
onClick={() => setGroundMaterial(material)}
|
key={item.label}
|
||||||
>
|
value={additionalInfo}
|
||||||
<Text className='option-text'>{material}</Text>
|
onChange={setAdditionalInfo}
|
||||||
</View>
|
placeholder='有其他场地信息可备注'
|
||||||
))}
|
options={(item.options || []).map((o) => ({ label: o, value: o }))}
|
||||||
</View>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
</View>
|
||||||
{/* 场地信息补充 */}
|
|
||||||
<View className='additional-info-section'>
|
)
|
||||||
<Text className='section-title'>场地信息补充</Text>
|
}
|
||||||
<Input
|
|
||||||
className='additional-input'
|
if (item.type === 'image') {
|
||||||
placeholder='有其他场地信息可备注'
|
return (
|
||||||
value={additionalInfo}
|
<View className='venue-type-section' key={item.label}>
|
||||||
onInput={(e) => setAdditionalInfo(e.detail.value)}
|
<Text className='section-title'>{item.label}</Text>
|
||||||
/>
|
<View className='option-buttons'>
|
||||||
</View>
|
<CoverImageUpload
|
||||||
|
key={item.label}
|
||||||
{/* 底部按钮 */}
|
images={imagesList}
|
||||||
<View className='bottom-actions'>
|
onChange={setImagesList}
|
||||||
<View className='action-buttons'>
|
/>
|
||||||
<View className='cancel-btn' onClick={onBack}>
|
</View>
|
||||||
<Text className='cancel-text'>取消</Text>
|
</View>
|
||||||
</View>
|
|
||||||
<View className='confirm-btn' onClick={handleConfirm}>
|
)
|
||||||
<Text className='confirm-text'>完成</Text>
|
}
|
||||||
</View>
|
|
||||||
</View>
|
return null
|
||||||
</View>
|
})}
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,10 @@
|
|||||||
.textarea-tag {
|
.textarea-tag {
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
padding: 10px 16px 5px 10px;
|
padding: 10px 16px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
.input-wrapper {
|
.input-wrapper {
|
||||||
padding-bottom: 10px;
|
margin-top: 8px;
|
||||||
|
|
||||||
.additional-input {
|
.additional-input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 46px;
|
height: 46px;
|
||||||
@@ -37,7 +36,6 @@
|
|||||||
gap: 6px;
|
gap: 6px;
|
||||||
.nut-checkbox{
|
.nut-checkbox{
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
margin-bottom: 5px;
|
|
||||||
.nut-checkbox-button{
|
.nut-checkbox-button{
|
||||||
border: 1px solid theme.$primary-border-color;
|
border: 1px solid theme.$primary-border-color;
|
||||||
color: theme.$primary-color;
|
color: theme.$primary-color;
|
||||||
|
|||||||
@@ -45,19 +45,6 @@ const TextareaTag: React.FC<TextareaTagProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View className='textarea-tag'>
|
<View className='textarea-tag'>
|
||||||
{/* 输入框 */}
|
|
||||||
<View className='input-wrapper'>
|
|
||||||
<Textarea
|
|
||||||
className='additional-input'
|
|
||||||
placeholder={placeholder}
|
|
||||||
value={value}
|
|
||||||
placeholderClass='textarea-placeholder'
|
|
||||||
onInput={handleInputChange}
|
|
||||||
maxlength={maxLength}
|
|
||||||
autoHeight={false}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* 选择选项 */}
|
{/* 选择选项 */}
|
||||||
<View className='options-wrapper'>
|
<View className='options-wrapper'>
|
||||||
<View className='options-list'>
|
<View className='options-list'>
|
||||||
@@ -82,6 +69,20 @@ const TextareaTag: React.FC<TextareaTagProps> = ({
|
|||||||
}
|
}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
{/* 输入框 */}
|
||||||
|
<View className='input-wrapper'>
|
||||||
|
<Textarea
|
||||||
|
className='additional-input'
|
||||||
|
placeholder={placeholder}
|
||||||
|
value={value}
|
||||||
|
placeholderClass='textarea-placeholder'
|
||||||
|
onInput={handleInputChange}
|
||||||
|
maxlength={maxLength}
|
||||||
|
autoHeight={false}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -162,10 +162,9 @@ export const publishBallFormSchema: FormFieldConfig[] = [
|
|||||||
placeholder: '补充性别偏好、特殊要求和注意事项等信息',
|
placeholder: '补充性别偏好、特殊要求和注意事项等信息',
|
||||||
required: true,
|
required: true,
|
||||||
options:[
|
options:[
|
||||||
{ label: '新手', value: 'beginner' },
|
{ label: '仅限男生', value: 'beginner' },
|
||||||
{ label: '进阶', value: 'intermediate' },
|
{ label: '仅限女生', value: 'intermediate' },
|
||||||
{ label: '高手', value: 'advanced' },
|
{ label: '性别不限', value: 'advanced' }
|
||||||
{ label: '不限', value: 'any' }
|
|
||||||
],
|
],
|
||||||
rules: [
|
rules: [
|
||||||
{ max: 100, message: '补充要求不能超过100个字符' }
|
{ max: 100, message: '补充要求不能超过100个字符' }
|
||||||
|
|||||||
@@ -8,4 +8,9 @@ export default {
|
|||||||
ICON_COST: require('@/static/publishBall/icon-cost.svg'),
|
ICON_COST: require('@/static/publishBall/icon-cost.svg'),
|
||||||
ICON_TIPS: require('@/static/publishBall/icon-tips.svg'),
|
ICON_TIPS: require('@/static/publishBall/icon-tips.svg'),
|
||||||
ICON_ARROW_RIGHT: require('@/static/publishBall/icon-arrow-right.svg'),
|
ICON_ARROW_RIGHT: require('@/static/publishBall/icon-arrow-right.svg'),
|
||||||
|
ICON_SEARCH: require('@/static/publishBall/icon-search.svg'),
|
||||||
|
ICON_MAP: require('@/static/publishBall/icon-map.svg'),
|
||||||
|
ICON_STADIUM: require('@/static/publishBall/icon-stadium.svg'),
|
||||||
|
ICON_ARRORW_SMALL: require('@/static/publishBall/icon-arrow-small.svg'),
|
||||||
|
ICON_MAP_SEARCH: require('@/static/publishBall/icon-map-search.svg'),
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 257 B After Width: | Height: | Size: 257 B |
Reference in New Issue
Block a user