增加获取场馆、字典
This commit is contained in:
115
src/components/DateTimePicker/DateTimePicker.tsx
Normal file
115
src/components/DateTimePicker/DateTimePicker.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { View, Text } from '@tarojs/components'
|
||||
import { Picker, Popup } from '@nutui/nutui-react-taro'
|
||||
import styles from './index.module.scss'
|
||||
|
||||
export interface DateTimePickerProps {
|
||||
visible: boolean
|
||||
onClose: () => void
|
||||
onConfirm: (year: number, month: number) => void
|
||||
defaultYear?: number
|
||||
defaultMonth?: number
|
||||
minYear?: number
|
||||
maxYear?: number
|
||||
}
|
||||
|
||||
const DateTimePicker: React.FC<DateTimePickerProps> = ({
|
||||
visible,
|
||||
onClose,
|
||||
onConfirm,
|
||||
defaultYear = new Date().getFullYear(),
|
||||
defaultMonth = new Date().getMonth() + 1,
|
||||
minYear = 2020,
|
||||
maxYear = 2030
|
||||
}) => {
|
||||
const [selectedYear, setSelectedYear] = useState(defaultYear)
|
||||
const [selectedMonth, setSelectedMonth] = useState(defaultMonth)
|
||||
|
||||
// 生成年份选项
|
||||
const yearOptions = Array.from({ length: maxYear - minYear + 1 }, (_, index) => ({
|
||||
text: `${minYear + index}年`,
|
||||
value: minYear + index
|
||||
}))
|
||||
|
||||
// 生成月份选项
|
||||
const monthOptions = Array.from({ length: 12 }, (_, index) => ({
|
||||
text: `${index + 1}月`,
|
||||
value: index + 1
|
||||
}))
|
||||
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
setSelectedYear(defaultYear)
|
||||
setSelectedMonth(defaultMonth)
|
||||
}
|
||||
}, [visible, defaultYear, defaultMonth])
|
||||
|
||||
const handleYearChange = (value: any) => {
|
||||
setSelectedYear(value[0])
|
||||
}
|
||||
|
||||
const handleMonthChange = (value: any) => {
|
||||
setSelectedMonth(value[0])
|
||||
}
|
||||
|
||||
const handleConfirm = () => {
|
||||
onConfirm(selectedYear, selectedMonth)
|
||||
onClose()
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
onClose()
|
||||
}
|
||||
|
||||
return (
|
||||
<Popup
|
||||
visible={visible}
|
||||
position="bottom"
|
||||
round
|
||||
onClose={onClose}
|
||||
className={styles['date-time-picker-popup']}
|
||||
>
|
||||
{/* 拖拽手柄 */}
|
||||
<View className={styles['popup-handle']} />
|
||||
|
||||
{/* 时间选择器 */}
|
||||
<View className={styles['picker-container']}>
|
||||
<View className={styles['picker-columns']}>
|
||||
{/* 年份选择 */}
|
||||
<View className={styles['picker-column']}>
|
||||
<Text className={styles['picker-label']}>年</Text>
|
||||
<Picker
|
||||
value={[selectedYear]}
|
||||
options={yearOptions}
|
||||
onChange={handleYearChange}
|
||||
className={styles['year-picker']}
|
||||
/>
|
||||
</View>
|
||||
|
||||
{/* 月份选择 */}
|
||||
<View className={styles['picker-column']}>
|
||||
<Text className={styles['picker-label']}>月</Text>
|
||||
<Picker
|
||||
value={[selectedMonth]}
|
||||
options={monthOptions}
|
||||
onChange={handleMonthChange}
|
||||
className={styles['month-picker']}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 操作按钮 */}
|
||||
<View className={styles['action-buttons']}>
|
||||
<View className={styles['cancel-btn']} onClick={handleCancel}>
|
||||
<Text className={styles['cancel-text']}>取消</Text>
|
||||
</View>
|
||||
<View className={styles['confirm-btn']} onClick={handleConfirm}>
|
||||
<Text className={styles['confirm-text']}>完成</Text>
|
||||
</View>
|
||||
</View>
|
||||
</Popup>
|
||||
)
|
||||
}
|
||||
|
||||
export default DateTimePicker
|
||||
67
src/components/DateTimePicker/README.md
Normal file
67
src/components/DateTimePicker/README.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# DateTimePicker 年月选择器
|
||||
|
||||
一个基于 NutUI 的年月切换弹窗组件,支持自定义年份范围和默认值。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- 🎯 年月分别选择,操作简单直观
|
||||
- 🎨 遵循设计稿样式,美观易用
|
||||
- 📱 支持移动端手势操作
|
||||
- ⚙️ 可自定义年份范围
|
||||
- <20><> 基于 NutUI 组件库,稳定可靠
|
||||
|
||||
## 使用方法
|
||||
|
||||
```tsx
|
||||
import { DateTimePicker } from '@/components'
|
||||
|
||||
const MyComponent = () => {
|
||||
const [visible, setVisible] = useState(false)
|
||||
|
||||
const handleConfirm = (year: number, month: number) => {
|
||||
console.log('选择的年月:', year, month)
|
||||
setVisible(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<DateTimePicker
|
||||
visible={visible}
|
||||
onClose={() => setVisible(false)}
|
||||
onConfirm={handleConfirm}
|
||||
defaultYear={2025}
|
||||
defaultMonth={11}
|
||||
minYear={2020}
|
||||
maxYear={2030}
|
||||
/>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## API 参数
|
||||
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| visible | boolean | - | 是否显示弹窗 |
|
||||
| onClose | () => void | - | 关闭弹窗的回调 |
|
||||
| onConfirm | (year: number, month: number) => void | - | 确认选择的回调 |
|
||||
| defaultYear | number | 当前年份 | 默认选中的年份 |
|
||||
| defaultMonth | number | 当前月份 | 默认选中的月份 |
|
||||
| minYear | number | 2020 | 可选择的最小年份 |
|
||||
| maxYear | number | 2030 | 可选择的最大年份 |
|
||||
|
||||
## 样式定制
|
||||
|
||||
组件使用 CSS Modules,可以通过修改 `index.module.scss` 文件来自定义样式。
|
||||
|
||||
主要样式类:
|
||||
- `.date-time-picker-popup` - 弹窗容器
|
||||
- `.picker-columns` - 选择器列容器
|
||||
- `.picker-column` - 单列选择器
|
||||
- `.action-buttons` - 操作按钮区域
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 组件基于 NutUI 的 Picker 和 Popup 组件
|
||||
2. 年份范围建议不要设置过大,以免影响性能
|
||||
3. 月份固定为 1-12 月
|
||||
4. 组件会自动处理边界情况
|
||||
45
src/components/DateTimePicker/example.tsx
Normal file
45
src/components/DateTimePicker/example.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import React, { useState } from 'react'
|
||||
import { View, Button } from '@tarojs/components'
|
||||
import DateTimePicker from './DateTimePicker'
|
||||
|
||||
const DateTimePickerExample: React.FC = () => {
|
||||
const [visible, setVisible] = useState(false)
|
||||
const [selectedDate, setSelectedDate] = useState('')
|
||||
|
||||
const handleOpen = () => {
|
||||
setVisible(true)
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
setVisible(false)
|
||||
}
|
||||
|
||||
const handleConfirm = (year: number, month: number) => {
|
||||
setSelectedDate(`${year}年${month}月`)
|
||||
console.log('选择的日期:', year, month)
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={{ padding: '20px' }}>
|
||||
<Button onClick={handleOpen}>选择年月</Button>
|
||||
|
||||
{selectedDate && (
|
||||
<View style={{ marginTop: '20px', fontSize: '16px' }}>
|
||||
已选择: {selectedDate}
|
||||
</View>
|
||||
)}
|
||||
|
||||
<DateTimePicker
|
||||
visible={visible}
|
||||
onClose={handleClose}
|
||||
onConfirm={handleConfirm}
|
||||
defaultYear={2025}
|
||||
defaultMonth={11}
|
||||
minYear={2020}
|
||||
maxYear={2030}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export default DateTimePickerExample
|
||||
102
src/components/DateTimePicker/index.module.scss
Normal file
102
src/components/DateTimePicker/index.module.scss
Normal file
@@ -0,0 +1,102 @@
|
||||
.date-time-picker-popup {
|
||||
:global(.nut-popup) {
|
||||
border-radius: 16px 16px 0 0;
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.popup-handle {
|
||||
width: 40px;
|
||||
height: 4px;
|
||||
background: #e5e5e5;
|
||||
border-radius: 2px;
|
||||
margin: 12px auto 0;
|
||||
}
|
||||
|
||||
.picker-container {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.picker-columns {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 60px;
|
||||
}
|
||||
|
||||
.picker-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.picker-label {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.year-picker,
|
||||
.month-picker {
|
||||
:global(.nut-picker) {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
:global(.nut-picker__content) {
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
:global(.nut-picker-item) {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
:global(.nut-picker-item--selected) {
|
||||
color: #000;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
:global(.nut-picker-item--disabled) {
|
||||
color: #ccc;
|
||||
}
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
padding: 0 20px 20px;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.cancel-btn,
|
||||
.confirm-btn {
|
||||
flex: 1;
|
||||
height: 44px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
background: #fff;
|
||||
border: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.cancel-text {
|
||||
color: #666;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.confirm-btn {
|
||||
background: #000;
|
||||
border: 1px solid #000;
|
||||
}
|
||||
|
||||
.confirm-text {
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
}
|
||||
2
src/components/DateTimePicker/index.ts
Normal file
2
src/components/DateTimePicker/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
import DateTimePicker from './DateTimePicker'
|
||||
export default DateTimePicker
|
||||
@@ -4,18 +4,18 @@ import './NumberInterval.scss'
|
||||
import { InputNumber } from '@nutui/nutui-react-taro'
|
||||
|
||||
interface NumberIntervalProps {
|
||||
minParticipants: number
|
||||
maxParticipants: number
|
||||
onMinParticipantsChange: (value: number) => void
|
||||
onMaxParticipantsChange: (value: number) => void
|
||||
value: [number, number]
|
||||
onChange: (value: [number, number]) => void
|
||||
}
|
||||
|
||||
const NumberInterval: React.FC<NumberIntervalProps> = ({
|
||||
minParticipants,
|
||||
maxParticipants,
|
||||
onMinParticipantsChange,
|
||||
onMaxParticipantsChange
|
||||
value,
|
||||
onChange
|
||||
}) => {
|
||||
const [minParticipants, maxParticipants] = value || [1, 4]
|
||||
const handleChange = (value: [number | string, number | string]) => {
|
||||
onChange([Number(value[0]), Number(value[1])])
|
||||
}
|
||||
return (
|
||||
<View className='participants-control-section'>
|
||||
<View className='participant-control'>
|
||||
@@ -23,9 +23,10 @@ const NumberInterval: React.FC<NumberIntervalProps> = ({
|
||||
<View className='control-buttons'>
|
||||
<InputNumber
|
||||
className="format-width"
|
||||
defaultValue={4}
|
||||
min={0}
|
||||
max={4}
|
||||
defaultValue={minParticipants}
|
||||
min={minParticipants}
|
||||
max={maxParticipants}
|
||||
onChange={(value) => handleChange([value, maxParticipants])}
|
||||
formatter={(value) => `${value}人`}
|
||||
/>
|
||||
</View>
|
||||
@@ -35,9 +36,10 @@ const NumberInterval: React.FC<NumberIntervalProps> = ({
|
||||
<View className='control-buttons'>
|
||||
<InputNumber
|
||||
className="format-width"
|
||||
defaultValue={4}
|
||||
min={0}
|
||||
max={4}
|
||||
defaultValue={maxParticipants}
|
||||
onChange={(value) => handleChange([value, maxParticipants])}
|
||||
min={minParticipants}
|
||||
max={maxParticipants}
|
||||
formatter={(value) => `${value}人`}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -6,8 +6,8 @@ import { Checkbox } from '@nutui/nutui-react-taro'
|
||||
import './TextareaTag.scss'
|
||||
|
||||
interface TextareaTagProps {
|
||||
value: string
|
||||
onChange: (value: string) => void
|
||||
value: { description: string, description_tag: string[] }
|
||||
onChange: (value: { description: string, description_tag: string[] }) => void
|
||||
title?: string
|
||||
showTitle?: boolean
|
||||
placeholder?: string
|
||||
@@ -22,27 +22,15 @@ const TextareaTag: React.FC<TextareaTagProps> = ({
|
||||
maxLength = 500,
|
||||
options = []
|
||||
}) => {
|
||||
// 处理输入框变化
|
||||
const [tags, setTags] = useState<string[]>([])
|
||||
console.log(value, 'options')
|
||||
const handleInputChange = useCallback((e: any) => {
|
||||
onChange(e.detail.value)
|
||||
// 处理文本输入变化
|
||||
const handleTextChange = useCallback((e: any) => {
|
||||
onChange({...value, description: e.detail.value})
|
||||
}, [onChange])
|
||||
|
||||
// 选择预设选项
|
||||
const handleSelectOption = useCallback((option: string) => {
|
||||
let newValue = ''
|
||||
|
||||
if (value) {
|
||||
// 如果已有内容,用分号分隔添加
|
||||
newValue = value + ';' + option
|
||||
} else {
|
||||
// 如果没有内容,直接添加
|
||||
newValue = option
|
||||
}
|
||||
|
||||
onChange(newValue)
|
||||
}, [value, onChange])
|
||||
// 处理标签选择变化
|
||||
const handleTagChange = useCallback((selectedTags: string[]) => {
|
||||
onChange({...value, description_tag: selectedTags})
|
||||
}, [onChange])
|
||||
|
||||
console.log(options, 'options')
|
||||
return (
|
||||
@@ -54,8 +42,8 @@ const TextareaTag: React.FC<TextareaTagProps> = ({
|
||||
<Checkbox.Group
|
||||
labelPosition="left"
|
||||
direction="horizontal"
|
||||
value={tags}
|
||||
onChange={(value) => setTags(value)}
|
||||
value={value.description_tag}
|
||||
onChange={handleTagChange}
|
||||
>
|
||||
{
|
||||
options?.map((option, index) => (
|
||||
@@ -76,9 +64,9 @@ const TextareaTag: React.FC<TextareaTagProps> = ({
|
||||
<Textarea
|
||||
className='additional-input'
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
value={value.description}
|
||||
placeholderClass='textarea-placeholder'
|
||||
onInput={handleInputChange}
|
||||
onInput={handleTextChange}
|
||||
maxlength={maxLength}
|
||||
autoHeight={false}
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react'
|
||||
import { View, Text, Picker } from '@tarojs/components'
|
||||
import React, { useState } from 'react'
|
||||
import { View, Text, } from '@tarojs/components'
|
||||
import { getDate, getTime } from '@/utils/timeUtils'
|
||||
import DateTimePicker from '@/components/DateTimePicker'
|
||||
import './TimeSelector.scss'
|
||||
|
||||
export interface TimeRange {
|
||||
@@ -21,8 +22,10 @@ const TimeSelector: React.FC<TimeSelectorProps> = ({
|
||||
onChange
|
||||
}) => {
|
||||
// 格式化日期显示
|
||||
|
||||
|
||||
const [visible, setVisible] = useState(false)
|
||||
const handleConfirm = (year: number, month: number) => {
|
||||
console.log('选择的日期:', year, month)
|
||||
}
|
||||
return (
|
||||
<View className='time-selector'>
|
||||
<View className='time-section'>
|
||||
@@ -31,7 +34,7 @@ const TimeSelector: React.FC<TimeSelectorProps> = ({
|
||||
<View className='time-label'>
|
||||
<View className='dot'></View>
|
||||
</View>
|
||||
<View className='time-content'>
|
||||
<View className='time-content' onClick={() => setVisible(true)}>
|
||||
<Text className='time-label'>开始时间</Text>
|
||||
<view className='time-text-wrapper'>
|
||||
<Text className='time-text'>{getDate(value.start_time)}</Text>
|
||||
@@ -53,6 +56,15 @@ const TimeSelector: React.FC<TimeSelectorProps> = ({
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<DateTimePicker
|
||||
visible={visible}
|
||||
onClose={() => setVisible(false)}
|
||||
onConfirm={handleConfirm}
|
||||
defaultYear={2025}
|
||||
defaultMonth={11}
|
||||
minYear={2020}
|
||||
maxYear={2030}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import NumberInterval from './NumberInterval'
|
||||
import TimeSelector from './TimeSelector'
|
||||
import TitleTextarea from './TitleTextarea'
|
||||
import CommonPopup from './CommonPopup'
|
||||
import DateTimePicker from './DateTimePicker/DateTimePicker'
|
||||
|
||||
export {
|
||||
ActivityTypeSwitch,
|
||||
@@ -18,6 +19,7 @@ export {
|
||||
NumberInterval,
|
||||
TimeSelector,
|
||||
TitleTextarea,
|
||||
CommonPopup
|
||||
CommonPopup,
|
||||
DateTimePicker
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user