增加开启自动候补
This commit is contained in:
@@ -58,7 +58,7 @@ export default defineConfig<'webpack5'>(async (merge, { command, mode }) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
cssModules: {
|
cssModules: {
|
||||||
enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
|
enable: true, // 默认为 false,如需使用 css modules 功能,则设为 true
|
||||||
config: {
|
config: {
|
||||||
namingPattern: 'module', // 转换模式,取值为 global/module
|
namingPattern: 'module', // 转换模式,取值为 global/module
|
||||||
generateScopedName: '[name]__[local]___[hash:base64:5]'
|
generateScopedName: '[name]__[local]___[hash:base64:5]'
|
||||||
|
|||||||
@@ -2,24 +2,45 @@
|
|||||||
"miniprogramRoot": "dist/",
|
"miniprogramRoot": "dist/",
|
||||||
"projectname": "playBallTogether",
|
"projectname": "playBallTogether",
|
||||||
"description": "playBallTogether",
|
"description": "playBallTogether",
|
||||||
"appid": "touristappid",
|
"appid": "wx815b533167eb7b53",
|
||||||
"setting": {
|
"setting": {
|
||||||
"urlCheck": true,
|
"urlCheck": true,
|
||||||
"es6": false,
|
"es6": true,
|
||||||
"enhance": false,
|
"enhance": true,
|
||||||
"compileHotReLoad": false,
|
"postcss": false,
|
||||||
"postcss": false,
|
"preloadBackgroundData": false,
|
||||||
"preloadBackgroundData": false,
|
"minified": false,
|
||||||
"minified": false,
|
"newFeature": true,
|
||||||
"newFeature": true,
|
"coverView": true,
|
||||||
"autoAudits": false,
|
"nodeModules": false,
|
||||||
"coverView": true,
|
"autoAudits": false,
|
||||||
"showShadowRootInWxmlPanel": false,
|
"showShadowRootInWxmlPanel": false,
|
||||||
"scopeDataCheck": false,
|
"scopeDataCheck": false,
|
||||||
"useCompilerModule": false
|
"uglifyFileName": false,
|
||||||
|
"checkInvalidKey": true,
|
||||||
|
"checkSiteMap": true,
|
||||||
|
"uploadWithSourceMap": true,
|
||||||
|
"compileHotReLoad": false,
|
||||||
|
"useMultiFrameRuntime": true,
|
||||||
|
"useApiHook": true,
|
||||||
|
"useApiHostProcess": false,
|
||||||
|
"babelSetting": {
|
||||||
|
"ignore": [],
|
||||||
|
"disablePlugins": [],
|
||||||
|
"outputPath": ""
|
||||||
|
},
|
||||||
|
"enableEngineNative": false,
|
||||||
|
"useIsolateContext": true,
|
||||||
|
"useCompilerModule": false,
|
||||||
|
"userConfirmedUseCompilerModuleSwitch": false,
|
||||||
|
"userConfirmedBundleSwitch": false,
|
||||||
|
"packNpmManually": false,
|
||||||
|
"packNpmRelationList": [],
|
||||||
|
"minifyWXSS": true,
|
||||||
|
"minifyWXML": true
|
||||||
},
|
},
|
||||||
"compileType": "miniprogram",
|
"compileType": "miniprogram",
|
||||||
"simulatorType": "wechat",
|
"simulatorType": "wechat",
|
||||||
"simulatorPluginLibVersion": {},
|
"simulatorPluginLibVersion": {},
|
||||||
"condition": {}
|
"condition": {}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react'
|
import React, { useState } from 'react'
|
||||||
import { View, Text } from '@tarojs/components'
|
import { View, Text } from '@tarojs/components'
|
||||||
import { Checkbox } from '@nutui/nutui-react-taro'
|
import { Checkbox, Popover } from '@nutui/nutui-react-taro'
|
||||||
import { Image } from '@tarojs/components'
|
import { Image } from '@tarojs/components'
|
||||||
import images from '@/config/images'
|
import images from '@/config/images'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
@@ -8,19 +8,35 @@ import './index.scss'
|
|||||||
interface FormSwitchProps {
|
interface FormSwitchProps {
|
||||||
value: boolean
|
value: boolean
|
||||||
onChange: (checked: boolean) => void
|
onChange: (checked: boolean) => void
|
||||||
title: string
|
subTitle: string
|
||||||
infoIcon?: string
|
infoIcon?: string
|
||||||
showToast?: boolean
|
showToast?: boolean
|
||||||
|
description?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const FormSwitch: React.FC<FormSwitchProps> = ({ value, onChange, title, infoIcon, showToast = false}) => {
|
const FormSwitch: React.FC<FormSwitchProps> = ({ value, onChange, subTitle, infoIcon, showToast = false, description}) => {
|
||||||
|
const [dark, setDark] = useState(false)
|
||||||
return (
|
return (
|
||||||
<View className='auto-degrade-section'>
|
<View className='auto-degrade-section'>
|
||||||
<View className='auto-degrade-item'>
|
<View className='auto-degrade-item'>
|
||||||
<View className='auto-degrade-content'>
|
<View className='auto-degrade-content'>
|
||||||
<Text className='auto-degrade-text'>{title}</Text>
|
<Text className='auto-degrade-text'>{subTitle}</Text>
|
||||||
{
|
{
|
||||||
showToast && <View className='info-icon'><Image src={images.ICON_TIPS || infoIcon} /></View>
|
showToast && <Popover
|
||||||
|
visible={dark}
|
||||||
|
list={[{
|
||||||
|
key: 'key1',
|
||||||
|
name: description || '',
|
||||||
|
}]}
|
||||||
|
theme="dark"
|
||||||
|
location="bottom-start"
|
||||||
|
style={{ marginInlineEnd: '30px' }}
|
||||||
|
onClick={() => {
|
||||||
|
dark ? setDark(false) : setDark(true)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View className='info-icon'><Image src={images.ICON_TIPS || infoIcon} className='info-img' /></View>
|
||||||
|
</Popover>
|
||||||
}
|
}
|
||||||
</View>
|
</View>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|||||||
@@ -3,11 +3,13 @@
|
|||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
height: 44px;
|
height: 44px;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
.auto-degrade-item {
|
.auto-degrade-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
.auto-degrade-content {
|
.auto-degrade-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -20,23 +22,21 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.info-icon {
|
.info-icon {
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
background: #999;
|
|
||||||
color: #fff;
|
|
||||||
border-radius: 50%;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: 12px;
|
padding-left: 4px;
|
||||||
font-weight: bold;
|
.info-img{
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.auto-degrade-checkbox {
|
.auto-degrade-checkbox {
|
||||||
:global(.nut-checkbox__icon) {
|
.nut-checkbox-icon {
|
||||||
width: 20px;
|
width: 14px;
|
||||||
height: 20px;
|
height: 14px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
93
src/components/Range/README.md
Normal file
93
src/components/Range/README.md
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
# NtrpRange 范围选择器组件
|
||||||
|
|
||||||
|
基于NutUI Range组件的双滑块范围选择器,通过CSS样式覆盖完全匹配设计稿,支持自定义范围、步长和回调函数。
|
||||||
|
|
||||||
|
## 功能特性
|
||||||
|
|
||||||
|
- 🎯 双滑块设计,支持选择范围区间
|
||||||
|
- 🎨 精准还原设计稿的视觉效果
|
||||||
|
- 📱 响应式设计,支持移动端
|
||||||
|
- 🎮 流畅的拖拽交互体验
|
||||||
|
- ⚙️ 可配置的最小值、最大值和步长
|
||||||
|
- 🔒 支持禁用状态
|
||||||
|
- 📊 实时值变化回调
|
||||||
|
|
||||||
|
## 基本用法
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import NtrpRange from '@/components/Range';
|
||||||
|
|
||||||
|
const MyComponent = () => {
|
||||||
|
const [value, setValue] = useState<[number, number]>([2.0, 4.0]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NtrpRange
|
||||||
|
min={2.0}
|
||||||
|
max={4.0}
|
||||||
|
step={0.5}
|
||||||
|
value={value}
|
||||||
|
onChange={setValue}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## 属性说明
|
||||||
|
|
||||||
|
| 属性 | 类型 | 默认值 | 说明 |
|
||||||
|
|------|------|--------|------|
|
||||||
|
| `min` | `number` | `2.0` | 最小值 |
|
||||||
|
| `max` | `number` | `4.0` | 最大值 |
|
||||||
|
| `step` | `number` | `0.5` | 步长 |
|
||||||
|
| `value` | `[number, number]` | `[min, max]` | 当前选择的范围值 |
|
||||||
|
| `onChange` | `(value: [number, number]) => void` | - | 值变化时的回调函数 |
|
||||||
|
| `disabled` | `boolean` | `false` | 是否禁用 |
|
||||||
|
|
||||||
|
## 技术实现
|
||||||
|
|
||||||
|
- 基于NutUI Range组件,确保拖拽功能的可靠性
|
||||||
|
- 通过CSS样式覆盖,完全匹配设计稿视觉效果
|
||||||
|
- TypeScript + React Hooks
|
||||||
|
- 响应式设计,支持移动端
|
||||||
|
|
||||||
|
## 设计规范
|
||||||
|
|
||||||
|
组件严格按照设计稿实现,包含以下视觉元素:
|
||||||
|
|
||||||
|
- **网球图标**: 黑色轮廓的网球图标
|
||||||
|
- **标题**: "NTRP水平区间" 文字
|
||||||
|
- **标签**: 左右两端的范围标签(如"2.0及以下"、"4.0及以上")
|
||||||
|
- **滑块轨道**: 圆角矩形容器,带有浅灰色边框和阴影
|
||||||
|
- **滑块手柄**: 两个白色圆形手柄,带有黑色边框和阴影
|
||||||
|
- **轨道填充**: 黑色填充条,显示当前选择的范围
|
||||||
|
- **标记点**: 四个浅灰色圆点,均匀分布在轨道上
|
||||||
|
|
||||||
|
## 样式定制
|
||||||
|
|
||||||
|
组件使用 BEM 命名规范,可以通过 CSS 变量或覆盖样式来自定义外观:
|
||||||
|
|
||||||
|
```scss
|
||||||
|
.ntrp-range {
|
||||||
|
// 自定义样式
|
||||||
|
&__track {
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-color: #d0d0d0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__handle {
|
||||||
|
background: #007bff;
|
||||||
|
border-color: #0056b3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. 确保 `min < max`,否则组件可能无法正常工作
|
||||||
|
2. `step` 值应该能够整除 `max - min` 的差值
|
||||||
|
3. 组件内部会确保左右滑块不会重叠,最小间距为 `step` 值
|
||||||
|
4. 拖拽时会自动吸附到最近的步长值
|
||||||
|
|
||||||
|
## 示例
|
||||||
|
|
||||||
|
查看 `example.tsx` 文件获取更多使用示例。
|
||||||
85
src/components/Range/example.tsx
Normal file
85
src/components/Range/example.tsx
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* @Author: juguohong juguohong@flashhold.com
|
||||||
|
* @Date: 2025-08-16 17:59:28
|
||||||
|
* @LastEditors: juguohong juguohong@flashhold.com
|
||||||
|
* @LastEditTime: 2025-08-16 23:48:25
|
||||||
|
* @FilePath: /mini-programs/src/components/Range/example.tsx
|
||||||
|
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
||||||
|
*/
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import NtrpRange from './index';
|
||||||
|
|
||||||
|
const RangeExample: React.FC = () => {
|
||||||
|
const [ntrpRange, setNtrpRange] = useState<[number, number]>([2.0, 4.0]);
|
||||||
|
const [customRange, setCustomRange] = useState<[number, number]>([0, 100]);
|
||||||
|
|
||||||
|
const handleNtrpChange = (value: [number, number]) => {
|
||||||
|
console.log('NTRP range changed:', value);
|
||||||
|
setNtrpRange(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCustomChange = (value: [number, number]) => {
|
||||||
|
console.log('Custom range changed:', value);
|
||||||
|
setCustomRange(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div >
|
||||||
|
<h1>Range 组件示例</h1>
|
||||||
|
|
||||||
|
<div style={{ marginBottom: '40px' }}>
|
||||||
|
<h2>NTRP 水平区间选择器</h2>
|
||||||
|
<NtrpRange
|
||||||
|
min={1.0}
|
||||||
|
max={5.0}
|
||||||
|
step={0.5}
|
||||||
|
value={ntrpRange}
|
||||||
|
onChange={handleNtrpChange}
|
||||||
|
/>
|
||||||
|
<div >
|
||||||
|
当前选择范围: {ntrpRange[0]} - {ntrpRange[1]}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ marginBottom: '40px' }}>
|
||||||
|
<h2>自定义范围选择器</h2>
|
||||||
|
<NtrpRange
|
||||||
|
min={0}
|
||||||
|
max={100}
|
||||||
|
step={10}
|
||||||
|
value={customRange}
|
||||||
|
onChange={handleCustomChange}
|
||||||
|
/>
|
||||||
|
<div style={{ marginTop: '16px', fontSize: '14px', color: '#666' }}>
|
||||||
|
当前选择范围: {customRange[0]} - {customRange[1]}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ marginBottom: '40px' }}>
|
||||||
|
<h2>禁用状态</h2>
|
||||||
|
<NtrpRange
|
||||||
|
min={1}
|
||||||
|
max={10}
|
||||||
|
step={1}
|
||||||
|
value={[3, 7]}
|
||||||
|
disabled={true}
|
||||||
|
/>
|
||||||
|
<div style={{ marginTop: '16px', fontSize: '14px', color: '#999' }}>
|
||||||
|
此选择器已被禁用
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ marginBottom: '40px' }}>
|
||||||
|
<h2>测试说明</h2>
|
||||||
|
<div style={{ fontSize: '14px', color: '#666', lineHeight: '1.6' }}>
|
||||||
|
<p>1. 点击并拖拽左右滑块手柄</p>
|
||||||
|
<p>2. 查看控制台日志确认拖拽事件</p>
|
||||||
|
<p>3. 观察滑块位置和值的实时变化</p>
|
||||||
|
<p>4. 检查调试信息显示</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RangeExample;
|
||||||
84
src/components/Range/index.module.scss
Normal file
84
src/components/Range/index.module.scss
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
|
||||||
|
|
||||||
|
// 全局NutUI样式覆盖
|
||||||
|
.nutRange {
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
|
||||||
|
// .nut-range__bar-box {
|
||||||
|
// margin: 0 !important;
|
||||||
|
// padding: 0 !important;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// .nut-range__bar {
|
||||||
|
// margin: 0 !important;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
.nutRangeHeader {
|
||||||
|
line-height: 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
.nutRangeHeaderLeft {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.nutRangeHeaderTitle {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nutRangeHeaderContent {
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #3c3c34;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rangeWrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 5px;
|
||||||
|
height: 44px;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
padding: 0 10px;
|
||||||
|
.rangeHandle {
|
||||||
|
:global(.nut-range-mark) {
|
||||||
|
padding-top: 28px;
|
||||||
|
left: 8px;
|
||||||
|
}
|
||||||
|
:global(.nut-range-bar) {
|
||||||
|
background: #000000;
|
||||||
|
height: 6px;
|
||||||
|
}
|
||||||
|
:global(.nut-range-button) {
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0 6px 13px 0 rgba(0, 0, 0, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.nut-range-tick) {
|
||||||
|
background: rgba(60, 60, 67, 0.18);
|
||||||
|
height: 4px !important;
|
||||||
|
width: 4px !important;
|
||||||
|
}
|
||||||
|
:global(.nut-range) {
|
||||||
|
background-color: rgba(120, 120, 120, 0.20) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
.rangeWrapperMin,
|
||||||
|
.rangeWrapperMax {
|
||||||
|
flex-shrink: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
93
src/components/Range/index.tsx
Normal file
93
src/components/Range/index.tsx
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
import React, { useState, useEffect, useMemo } from "react";
|
||||||
|
import { Range } from "@nutui/nutui-react-taro";
|
||||||
|
import styles from "./index.module.scss";
|
||||||
|
import TitleComponent from "../Title";
|
||||||
|
|
||||||
|
interface RangeProps {
|
||||||
|
min?: number;
|
||||||
|
max?: number;
|
||||||
|
step?: number;
|
||||||
|
value?: [number, number];
|
||||||
|
onChange?: (value: [number, number]) => void;
|
||||||
|
disabled?: boolean;
|
||||||
|
className?: string;
|
||||||
|
showTitle?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NtrpRange: React.FC<RangeProps> = ({
|
||||||
|
min = 1.0,
|
||||||
|
max = 5.0,
|
||||||
|
step = 0.5,
|
||||||
|
value = [min, max],
|
||||||
|
onChange,
|
||||||
|
disabled = false,
|
||||||
|
className,
|
||||||
|
showTitle = true,
|
||||||
|
}) => {
|
||||||
|
const [currentValue, setCurrentValue] = useState<[number, number]>(value);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
value && setCurrentValue(value);
|
||||||
|
}, [JSON.stringify(value || [])]);
|
||||||
|
|
||||||
|
const handleChange = (val: [number, number]) => {
|
||||||
|
setCurrentValue(val);
|
||||||
|
onChange?.(val);
|
||||||
|
};
|
||||||
|
|
||||||
|
const marks = useMemo(() => {
|
||||||
|
let marksMap = {};
|
||||||
|
for (let i = min + step; i < max; i += step) {
|
||||||
|
marksMap[i] = "";
|
||||||
|
}
|
||||||
|
return marksMap;
|
||||||
|
}, [min, max, step]);
|
||||||
|
|
||||||
|
const rangContent = useMemo(() => {
|
||||||
|
const [start, end] = currentValue || [];
|
||||||
|
if (Number(start) === Number(min) && Number(end) === Number(max)) {
|
||||||
|
return "不限";
|
||||||
|
}
|
||||||
|
return `${start.toFixed(1)} - ${end.toFixed(1)}之间`;
|
||||||
|
}, [JSON.stringify(currentValue || []), min, max]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`${styles.nutRange} ${className ? className : ''} `}>
|
||||||
|
{ showTitle && (
|
||||||
|
<div className={styles.nutRangeHeader}>
|
||||||
|
{/* <div className={styles.nutRangeHeaderLeft}>
|
||||||
|
<div className="ntrp-range__icon">icon</div>
|
||||||
|
<h3 className={styles.nutRangeHeaderTitle}>NTRP水平区间</h3>
|
||||||
|
</div> */}
|
||||||
|
<TitleComponent title='NTRP水平区间'/>
|
||||||
|
<p className={styles.nutRangeHeaderContent}>{rangContent}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div className={styles.rangeWrapper}>
|
||||||
|
<span className={styles.rangeWrapperMin}>{min.toFixed(1)}</span>
|
||||||
|
<Range
|
||||||
|
range
|
||||||
|
min={min}
|
||||||
|
max={max}
|
||||||
|
step={step}
|
||||||
|
// value={currentValue}
|
||||||
|
onEnd={handleChange}
|
||||||
|
disabled={disabled}
|
||||||
|
defaultValue={[min, max]}
|
||||||
|
className={styles.rangeHandle}
|
||||||
|
maxDescription={null}
|
||||||
|
minDescription={null}
|
||||||
|
currentDescription={null}
|
||||||
|
marks={marks}
|
||||||
|
/>
|
||||||
|
<span className={styles.rangeWrapperMax}>{max.toFixed(1)}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NtrpRange;
|
||||||
22
src/components/Range/simple-test.tsx
Normal file
22
src/components/Range/simple-test.tsx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import NtrpRange from './index';
|
||||||
|
|
||||||
|
const SimpleTest: React.FC = () => {
|
||||||
|
const [value, setValue] = useState<[number, number]>([2.0, 4.0]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ padding: '20px' }}>
|
||||||
|
<h2>简单测试</h2>
|
||||||
|
<NtrpRange
|
||||||
|
min={2.0}
|
||||||
|
max={4.0}
|
||||||
|
step={0.5}
|
||||||
|
value={value}
|
||||||
|
onChange={setValue}
|
||||||
|
/>
|
||||||
|
<p>当前值: {value[0]} - {value[1]}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SimpleTest;
|
||||||
68
src/components/Range/style-test.tsx
Normal file
68
src/components/Range/style-test.tsx
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import NtrpRange from './index';
|
||||||
|
|
||||||
|
const StyleTest: React.FC = () => {
|
||||||
|
const [value, setValue] = useState<[number, number]>([2.0, 4.0]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ padding: '20px', maxWidth: '600px', margin: '0 auto' }}>
|
||||||
|
<h1>NtrpRange 样式测试</h1>
|
||||||
|
|
||||||
|
<div style={{ marginBottom: '30px' }}>
|
||||||
|
<h2>NTRP 水平区间选择器</h2>
|
||||||
|
<NtrpRange
|
||||||
|
min={2.0}
|
||||||
|
max={4.0}
|
||||||
|
step={0.5}
|
||||||
|
value={value}
|
||||||
|
onChange={setValue}
|
||||||
|
/>
|
||||||
|
<div style={{ marginTop: '16px', fontSize: '14px', color: '#666' }}>
|
||||||
|
当前选择范围: {value[0]} - {value[1]}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ marginBottom: '30px' }}>
|
||||||
|
<h2>自定义范围选择器</h2>
|
||||||
|
<NtrpRange
|
||||||
|
min={0}
|
||||||
|
max={100}
|
||||||
|
step={1}
|
||||||
|
value={[20, 80]}
|
||||||
|
onChange={(val) => console.log('Custom range:', val)}
|
||||||
|
/>
|
||||||
|
<div style={{ marginTop: '16px', fontSize: '14px', color: '#666' }}>
|
||||||
|
固定范围: 20 - 80
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ marginBottom: '30px' }}>
|
||||||
|
<h2>禁用状态</h2>
|
||||||
|
<NtrpRange
|
||||||
|
min={1}
|
||||||
|
max={10}
|
||||||
|
step={1}
|
||||||
|
value={[3, 7]}
|
||||||
|
disabled={true}
|
||||||
|
/>
|
||||||
|
<div style={{ marginTop: '16px', fontSize: '14px', color: '#999' }}>
|
||||||
|
此选择器已被禁用
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ marginBottom: '30px' }}>
|
||||||
|
<h2>样式说明</h2>
|
||||||
|
<div style={{ fontSize: '14px', color: '#666', lineHeight: '1.6' }}>
|
||||||
|
<p>✅ 网球图标 + "NTRP水平区间"标题</p>
|
||||||
|
<p>✅ 左右范围标签("2.0及以下"、"4.0及以上")</p>
|
||||||
|
<p>✅ 圆角矩形轨道容器,带有边框和阴影</p>
|
||||||
|
<p>✅ 白色圆形滑块手柄,黑色边框和阴影</p>
|
||||||
|
<p>✅ 黑色轨道填充条</p>
|
||||||
|
<p>✅ 五个浅灰色标记点</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default StyleTest;
|
||||||
35
src/components/Range/test.tsx
Normal file
35
src/components/Range/test.tsx
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import NtrpRange from './index';
|
||||||
|
|
||||||
|
const TestPage: React.FC = () => {
|
||||||
|
const [value, setValue] = useState<[number, number]>([2.0, 4.0]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ padding: '20px' }}>
|
||||||
|
<h1>NtrpRange 组件测试</h1>
|
||||||
|
|
||||||
|
<div style={{ marginBottom: '20px' }}>
|
||||||
|
<NtrpRange
|
||||||
|
min={2.0}
|
||||||
|
max={4.0}
|
||||||
|
step={0.5}
|
||||||
|
value={value}
|
||||||
|
onChange={setValue}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ fontSize: '14px', color: '#666' }}>
|
||||||
|
当前值: {value[0].toFixed(1)} - {value[1].toFixed(1)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ marginTop: '20px', fontSize: '12px', color: '#999' }}>
|
||||||
|
<p>测试说明:</p>
|
||||||
|
<p>1. 拖拽左右滑块手柄</p>
|
||||||
|
<p>2. 观察值的变化</p>
|
||||||
|
<p>3. 检查样式是否正确</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TestPage;
|
||||||
11
src/components/Title/index.module.scss
Normal file
11
src/components/Title/index.module.scss
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
.titleContainer {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
19
src/components/Title/index.tsx
Normal file
19
src/components/Title/index.tsx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import styles from "./index.module.scss";
|
||||||
|
interface IProps {
|
||||||
|
title: string;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
const TitleComponent = (props: IProps) => {
|
||||||
|
const { title, className } = props;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
className={`${styles.titleContainer} ${className ? className : ""} `}
|
||||||
|
>
|
||||||
|
<div>图</div>
|
||||||
|
<h1 className={styles.title}>{title}</h1>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default TitleComponent;
|
||||||
@@ -3,7 +3,7 @@ import TextareaTag from './TextareaTag'
|
|||||||
import FormSwitch from './FormSwitch'
|
import FormSwitch from './FormSwitch'
|
||||||
import CoverImageUpload from './CoverImageUpload'
|
import CoverImageUpload from './CoverImageUpload'
|
||||||
import FormBasicInfo from './FormBasicInfo'
|
import FormBasicInfo from './FormBasicInfo'
|
||||||
import NTRPSlider from './NTRPSlider'
|
import Range from './Range'
|
||||||
import ParticipantsControl from './ParticipantsControl'
|
import ParticipantsControl from './ParticipantsControl'
|
||||||
import { SelectStadium, StadiumDetail } from './SelectStadium'
|
import { SelectStadium, StadiumDetail } from './SelectStadium'
|
||||||
import TimeSelector from './TimeSelector'
|
import TimeSelector from './TimeSelector'
|
||||||
@@ -15,7 +15,7 @@ export {
|
|||||||
FormSwitch,
|
FormSwitch,
|
||||||
CoverImageUpload,
|
CoverImageUpload,
|
||||||
FormBasicInfo,
|
FormBasicInfo,
|
||||||
NTRPSlider,
|
Range,
|
||||||
ParticipantsControl,
|
ParticipantsControl,
|
||||||
SelectStadium,
|
SelectStadium,
|
||||||
TimeSelector,
|
TimeSelector,
|
||||||
|
|||||||
@@ -143,8 +143,11 @@ export const publishBallFormSchema: FormFieldConfig[] = [
|
|||||||
placeholder: '请选择开始时间',
|
placeholder: '请选择开始时间',
|
||||||
required: true,
|
required: true,
|
||||||
props: {
|
props: {
|
||||||
min: 2.0,
|
showTitle: false,
|
||||||
max: 4.0,
|
className: 'ntrp-range',
|
||||||
|
step: 0.5,
|
||||||
|
min: 1.0,
|
||||||
|
max: 5.0,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -165,11 +168,15 @@ export const publishBallFormSchema: FormFieldConfig[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'autoDegrade',
|
key: 'autoDegrade',
|
||||||
label: '开启自动候补逻辑',
|
label: '',
|
||||||
type: FieldType.CHECKBOX,
|
type: FieldType.CHECKBOX,
|
||||||
placeholder: '开启自动候补逻辑',
|
placeholder: '开启自动候补逻辑',
|
||||||
required: true,
|
required: true,
|
||||||
description: '开启后,当活动人数不足时,系统会自动将活动状态改为“候补”,并通知用户。',
|
props:{
|
||||||
|
subTitle: '开启自动候补逻辑',
|
||||||
|
showToast: true,
|
||||||
|
description: '开启后,当活动人数不足时,系统会自动将活动状态改为“候补”,并通知用户。',
|
||||||
|
},
|
||||||
rules: [
|
rules: [
|
||||||
{ required: true, message: '请选择开启自动候补逻辑' }
|
{ required: true, message: '请选择开启自动候补逻辑' }
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__content {
|
&__content {
|
||||||
padding-bottom: 40px;
|
padding-bottom: 94px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -24,7 +24,11 @@
|
|||||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||||
box-shadow: 0px 4px 36px 0px rgba(0, 0, 0, 0.06);
|
box-shadow: 0px 4px 36px 0px rgba(0, 0, 0, 0.06);
|
||||||
display: flex;
|
display: flex;
|
||||||
|
.ntrp-range{
|
||||||
|
.rangeWrapper{
|
||||||
|
border: none!important;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -81,49 +85,6 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 自动降分选择 - 白色块
|
|
||||||
.auto-degrade-section {
|
|
||||||
background: white;
|
|
||||||
border-radius: 16px;
|
|
||||||
padding: 20px 16px;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
|
|
||||||
.auto-degrade-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
.auto-degrade-content {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
.auto-degrade-text {
|
|
||||||
font-size: 16px;
|
|
||||||
color: #333;
|
|
||||||
font-weight: 500;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-icon {
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
border: 1px solid #999;
|
|
||||||
border-radius: 50%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 12px;
|
|
||||||
color: #999;
|
|
||||||
margin-left: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.auto-degrade-checkbox {
|
|
||||||
margin-left: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 提交区域
|
// 提交区域
|
||||||
.submit-section {
|
.submit-section {
|
||||||
|
|||||||
@@ -4,27 +4,12 @@ import Taro from '@tarojs/taro'
|
|||||||
import ActivityTypeSwitch, { type ActivityType } from '../../components/ActivityTypeSwitch'
|
import ActivityTypeSwitch, { type ActivityType } from '../../components/ActivityTypeSwitch'
|
||||||
import PublishForm from './publishForm'
|
import PublishForm from './publishForm'
|
||||||
import { publishBallFormSchema } from '../../config/formSchema/publishBallFormSchema';
|
import { publishBallFormSchema } from '../../config/formSchema/publishBallFormSchema';
|
||||||
|
import { PublishBallFormData } from '../../../types/publishBall';
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
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 PublishBall: React.FC = () => {
|
||||||
const [coverImages, setCoverImages] = useState<CoverImage[]>([])
|
const [formData, setFormData] = useState<PublishBallFormData>({
|
||||||
const [showStadiumSelector, setShowStadiumSelector] = useState(false)
|
|
||||||
const [selectedStadium, setSelectedStadium] = useState<Stadium | null>(null)
|
|
||||||
const [formData, setFormData] = useState<FormData>({
|
|
||||||
activityType: 'individual', // 默认值
|
activityType: 'individual', // 默认值
|
||||||
title: '',
|
title: '',
|
||||||
timeRange: {
|
timeRange: {
|
||||||
@@ -37,15 +22,12 @@ const PublishBall: React.FC = () => {
|
|||||||
gameplay: '',
|
gameplay: '',
|
||||||
minParticipants: 1,
|
minParticipants: 1,
|
||||||
maxParticipants: 4,
|
maxParticipants: 4,
|
||||||
ntpLevel: { min: 2.0, max: 4.0 },
|
ntpLevel: [2.0, 4.0],
|
||||||
additionalRequirements: '',
|
additionalRequirements: '',
|
||||||
autoDegrade: false
|
autoDegrade: false
|
||||||
})
|
})
|
||||||
|
|
||||||
// 处理封面图片变化
|
|
||||||
const handleCoverImagesChange = (images: CoverImage[]) => {
|
|
||||||
setCoverImages(images)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新表单数据
|
// 更新表单数据
|
||||||
const updateFormData = (key: keyof FormData, value: any) => {
|
const updateFormData = (key: keyof FormData, value: any) => {
|
||||||
@@ -57,35 +39,6 @@ const PublishBall: React.FC = () => {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 获取人数要求显示文本
|
|
||||||
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) => {
|
const handleActivityTypeChange = (type: ActivityType) => {
|
||||||
updateFormData('activityType', type)
|
updateFormData('activityType', type)
|
||||||
@@ -104,17 +57,7 @@ const PublishBall: React.FC = () => {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (coverImages.length === 0) {
|
|
||||||
Taro.showToast({
|
|
||||||
title: '请至少上传一张活动封面',
|
|
||||||
icon: 'none'
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: 实现提交逻辑
|
// TODO: 实现提交逻辑
|
||||||
console.log('提交数据:', { coverImages, formData })
|
|
||||||
|
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '发布成功',
|
title: '发布成功',
|
||||||
|
|||||||
@@ -2,30 +2,18 @@ import React, { useState } from 'react'
|
|||||||
import { View, Text } from '@tarojs/components'
|
import { View, Text } from '@tarojs/components'
|
||||||
|
|
||||||
import Taro from '@tarojs/taro'
|
import Taro from '@tarojs/taro'
|
||||||
import { CoverImageUpload, NTRPSlider, TimeSelector, TextareaTag, SelectStadium, ParticipantsControl, TitleInput, FormBasicInfo, FormSwitch } from '../../components'
|
import { CoverImageUpload, Range, TimeSelector, TextareaTag, SelectStadium, ParticipantsControl, TitleInput, FormBasicInfo, FormSwitch } from '../../components'
|
||||||
import { type NTRPRange, type TimeRange, type Stadium, type ActivityType, type CoverImage } from '../../components/index.types'
|
import { type Stadium, type CoverImage } from '../../components/index.types'
|
||||||
import { FormFieldConfig, FieldType } from '../../config/formSchema/publishBallFormSchema'
|
import { FormFieldConfig, FieldType } from '../../config/formSchema/publishBallFormSchema'
|
||||||
import './index.scss'
|
import { PublishBallFormData } from '../../../types/publishBall';
|
||||||
|
|
||||||
interface FormData {
|
import './index.scss'
|
||||||
activityType: ActivityType
|
|
||||||
title: string
|
|
||||||
timeRange: TimeRange
|
|
||||||
fee: string
|
|
||||||
location: string
|
|
||||||
gameplay: string
|
|
||||||
minParticipants: number
|
|
||||||
maxParticipants: number
|
|
||||||
ntpLevel: NTRPRange
|
|
||||||
TextareaTag: string
|
|
||||||
autoDegrade: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
// 组件映射器
|
// 组件映射器
|
||||||
const componentMap = {
|
const componentMap = {
|
||||||
[FieldType.TEXT]: TitleInput,
|
[FieldType.TEXT]: TitleInput,
|
||||||
[FieldType.TIMEINTERVAL]: TimeSelector,
|
[FieldType.TIMEINTERVAL]: TimeSelector,
|
||||||
[FieldType.RANGE]: NTRPSlider,
|
[FieldType.RANGE]: Range,
|
||||||
[FieldType.TEXTAREATAG]: TextareaTag,
|
[FieldType.TEXTAREATAG]: TextareaTag,
|
||||||
[FieldType.NUMBERINTERVAL]: ParticipantsControl,
|
[FieldType.NUMBERINTERVAL]: ParticipantsControl,
|
||||||
[FieldType.UPLOADIMAGE]: CoverImageUpload,
|
[FieldType.UPLOADIMAGE]: CoverImageUpload,
|
||||||
@@ -34,8 +22,8 @@ const componentMap = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const PublishForm: React.FC<{
|
const PublishForm: React.FC<{
|
||||||
formData: FormData,
|
formData: PublishBallFormData,
|
||||||
onChange: (key: keyof FormData, value: any) => void,
|
onChange: (key: keyof PublishBallFormData, value: any) => void,
|
||||||
optionsConfig: FormFieldConfig[] }> = ({ formData, onChange, optionsConfig }) => {
|
optionsConfig: FormFieldConfig[] }> = ({ formData, onChange, optionsConfig }) => {
|
||||||
const [coverImages, setCoverImages] = useState<CoverImage[]>([])
|
const [coverImages, setCoverImages] = useState<CoverImage[]>([])
|
||||||
const [showStadiumSelector, setShowStadiumSelector] = useState(false)
|
const [showStadiumSelector, setShowStadiumSelector] = useState(false)
|
||||||
@@ -53,26 +41,6 @@ const PublishForm: React.FC<{
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 获取人数要求显示文本
|
|
||||||
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) => {
|
const handleStadiumSelect = (stadium: Stadium | null) => {
|
||||||
setSelectedStadium(stadium)
|
setSelectedStadium(stadium)
|
||||||
@@ -82,10 +50,6 @@ const PublishForm: React.FC<{
|
|||||||
setShowStadiumSelector(false)
|
setShowStadiumSelector(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理活动类型变化
|
|
||||||
const handleActivityTypeChange = (type: ActivityType) => {
|
|
||||||
updateFormData('activityType', type)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -128,7 +92,7 @@ const PublishForm: React.FC<{
|
|||||||
...item.props,
|
...item.props,
|
||||||
...(item.key === 'additionalRequirements' ? { options: item.options } : {})
|
...(item.key === 'additionalRequirements' ? { options: item.options } : {})
|
||||||
}
|
}
|
||||||
console.log(optionProps, item.label);
|
console.log(optionProps, item.label, formData[item.key]);
|
||||||
if (item.type === FieldType.UPLOADIMAGE) {
|
if (item.type === FieldType.UPLOADIMAGE) {
|
||||||
/* 活动封面 */
|
/* 活动封面 */
|
||||||
return <CoverImageUpload
|
return <CoverImageUpload
|
||||||
@@ -172,6 +136,7 @@ const PublishForm: React.FC<{
|
|||||||
}
|
}
|
||||||
<View className='bg-section'>
|
<View className='bg-section'>
|
||||||
<Component
|
<Component
|
||||||
|
label={item.label}
|
||||||
value={formData[item.key]}
|
value={formData[item.key]}
|
||||||
onChange={(value) => updateFormData(item.key as keyof FormData, value)}
|
onChange={(value) => updateFormData(item.key as keyof FormData, value)}
|
||||||
{...optionProps}
|
{...optionProps}
|
||||||
|
|||||||
16
types/publishBall.ts
Normal file
16
types/publishBall.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { ActivityType, TimeRange } from "@/components/index.types"
|
||||||
|
|
||||||
|
|
||||||
|
export interface PublishBallFormData {
|
||||||
|
activityType: ActivityType
|
||||||
|
title: string
|
||||||
|
timeRange: TimeRange
|
||||||
|
fee: string
|
||||||
|
location: string
|
||||||
|
gameplay: string
|
||||||
|
minParticipants: number
|
||||||
|
maxParticipants: number
|
||||||
|
ntpLevel: number[]
|
||||||
|
additionalRequirements: string
|
||||||
|
autoDegrade: boolean
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user