修改发布日历
This commit is contained in:
233
src/components/TimePicker/TimePicker.tsx
Normal file
233
src/components/TimePicker/TimePicker.tsx
Normal file
@@ -0,0 +1,233 @@
|
||||
import React, { useState, useEffect, useRef } from 'react'
|
||||
import { View, Text, ScrollView } from '@tarojs/components'
|
||||
import { CommonPopup } from '../index'
|
||||
import styles from './index.module.scss'
|
||||
|
||||
export interface TimePickerProps {
|
||||
visible: boolean
|
||||
onClose: () => void
|
||||
onConfirm: (year: number, month: number) => void
|
||||
defaultYear?: number
|
||||
defaultMonth?: number
|
||||
minYear?: number
|
||||
maxYear?: number
|
||||
}
|
||||
|
||||
const TimePicker: React.FC<TimePickerProps> = ({
|
||||
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 [yearScrollTop, setYearScrollTop] = useState(0)
|
||||
const [monthScrollTop, setMonthScrollTop] = useState(0)
|
||||
|
||||
const yearScrollRef = useRef<any>(null)
|
||||
const monthScrollRef = useRef<any>(null)
|
||||
|
||||
// 计算当前选项在数组中的索引
|
||||
const getYearIndex = (year: number) => year - minYear
|
||||
const getMonthIndex = (month: number) => month - 1
|
||||
|
||||
// 生成选择器的选项数据
|
||||
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
|
||||
}))
|
||||
|
||||
// 计算滚动位置 - 确保每次只显示一个选项
|
||||
const calculateScrollTop = (index: number) => {
|
||||
const itemHeight = 48 // 每个选项的高度
|
||||
const containerHeight = 216 // 容器高度
|
||||
const centerOffset = (containerHeight - itemHeight) / 2
|
||||
return index * itemHeight - centerOffset
|
||||
}
|
||||
|
||||
// 获取当前可见的选项数量
|
||||
const getVisibleItemCount = () => {
|
||||
const containerHeight = 216
|
||||
const itemHeight = 48
|
||||
return Math.floor(containerHeight / itemHeight)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
setSelectedYear(defaultYear)
|
||||
setSelectedMonth(defaultMonth)
|
||||
|
||||
// 设置初始滚动位置
|
||||
const yearScrollTop = calculateScrollTop(getYearIndex(defaultYear))
|
||||
const monthScrollTop = calculateScrollTop(getMonthIndex(defaultMonth))
|
||||
setYearScrollTop(yearScrollTop)
|
||||
setMonthScrollTop(monthScrollTop)
|
||||
}
|
||||
}, [visible, defaultYear, defaultMonth])
|
||||
|
||||
// 处理年份滚动
|
||||
const handleYearScroll = (event: any) => {
|
||||
const scrollTop = event.detail.scrollTop
|
||||
const itemHeight = 48
|
||||
const containerHeight = 216
|
||||
const centerOffset = (containerHeight - itemHeight) / 2
|
||||
|
||||
// 计算当前选中的年份索引
|
||||
const currentIndex = Math.round((scrollTop + centerOffset) / itemHeight)
|
||||
const clampedIndex = Math.max(0, Math.min(currentIndex, yearOptions.length - 1))
|
||||
const newYear = minYear + clampedIndex
|
||||
|
||||
if (newYear !== selectedYear) {
|
||||
setSelectedYear(newYear)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理年份滚动结束,自动对齐
|
||||
const handleYearScrollEnd = () => {
|
||||
const yearIndex = getYearIndex(selectedYear)
|
||||
const alignedScrollTop = calculateScrollTop(yearIndex)
|
||||
// 使用setTimeout确保滚动动画完成后再对齐
|
||||
setTimeout(() => {
|
||||
setYearScrollTop(alignedScrollTop)
|
||||
}, 100)
|
||||
}
|
||||
|
||||
// 处理月份滚动
|
||||
const handleMonthScroll = (event: any) => {
|
||||
const scrollTop = event.detail.scrollTop
|
||||
const itemHeight = 48
|
||||
const containerHeight = 216
|
||||
const centerOffset = (containerHeight - itemHeight) / 2
|
||||
|
||||
// 计算当前选中的月份索引
|
||||
const currentIndex = Math.round((scrollTop + centerOffset) / itemHeight)
|
||||
const clampedIndex = Math.max(0, Math.min(currentIndex, monthOptions.length - 1))
|
||||
const newMonth = clampedIndex + 1
|
||||
|
||||
if (newMonth !== selectedMonth) {
|
||||
setSelectedMonth(newMonth)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理月份滚动结束,自动对齐
|
||||
const handleMonthScrollEnd = () => {
|
||||
const monthIndex = getMonthIndex(selectedMonth)
|
||||
const alignedScrollTop = calculateScrollTop(monthIndex)
|
||||
// 使用setTimeout确保滚动动画完成后再对齐
|
||||
setTimeout(() => {
|
||||
setMonthScrollTop(alignedScrollTop)
|
||||
}, 100)
|
||||
}
|
||||
|
||||
const handleConfirm = () => {
|
||||
onConfirm(selectedYear, selectedMonth)
|
||||
onClose()
|
||||
}
|
||||
|
||||
if (!visible) return null
|
||||
|
||||
return (
|
||||
<CommonPopup
|
||||
visible={visible}
|
||||
onClose={onClose}
|
||||
onConfirm={handleConfirm}
|
||||
showHeader={false}
|
||||
hideFooter={false}
|
||||
cancelText="返回"
|
||||
confirmText="完成"
|
||||
position="bottom"
|
||||
round={true}
|
||||
className={styles['time-picker-popup']}
|
||||
>
|
||||
{/* 拖拽手柄 */}
|
||||
<View className={styles['popup-handle']} />
|
||||
|
||||
{/* 时间选择器 */}
|
||||
<View className={styles['picker-container']}>
|
||||
{/* 自定义多列选择器 */}
|
||||
<View className={styles['picker-wrapper']}>
|
||||
<View className={styles['custom-picker']}>
|
||||
{/* 选中项指示器 */}
|
||||
<View className={styles['picker-indicator']} />
|
||||
|
||||
{/* 年份列 */}
|
||||
<View className={styles['picker-column']}>
|
||||
<ScrollView
|
||||
ref={yearScrollRef}
|
||||
scrollY
|
||||
scrollTop={yearScrollTop}
|
||||
onScroll={handleYearScroll}
|
||||
onTouchEnd={handleYearScrollEnd}
|
||||
onScrollToLower={handleYearScrollEnd}
|
||||
onScrollToUpper={handleYearScrollEnd}
|
||||
className={styles['picker-scroll']}
|
||||
scrollWithAnimation={true}
|
||||
enhanced={true}
|
||||
showScrollbar={false}
|
||||
bounces={false}
|
||||
fastDeceleration={true}
|
||||
>
|
||||
<View className={styles['picker-padding']} />
|
||||
{yearOptions.map((option, index) => (
|
||||
<View
|
||||
key={option.value}
|
||||
className={`${styles['picker-item']} ${
|
||||
option.value === selectedYear ? styles['picker-item-active'] : ''
|
||||
}`}
|
||||
data-value={option.value}
|
||||
>
|
||||
<Text className={styles['picker-item-text']}>{option.text}</Text>
|
||||
</View>
|
||||
))}
|
||||
<View className={styles['picker-padding']} />
|
||||
</ScrollView>
|
||||
</View>
|
||||
|
||||
{/* 月份列 */}
|
||||
<View className={styles['picker-column']}>
|
||||
<ScrollView
|
||||
ref={monthScrollRef}
|
||||
scrollY
|
||||
scrollTop={monthScrollTop}
|
||||
onScroll={handleMonthScroll}
|
||||
onTouchEnd={handleMonthScrollEnd}
|
||||
onScrollToLower={handleMonthScrollEnd}
|
||||
onScrollToUpper={handleMonthScrollEnd}
|
||||
className={styles['picker-scroll']}
|
||||
scrollWithAnimation={true}
|
||||
enhanced={true}
|
||||
showScrollbar={false}
|
||||
bounces={false}
|
||||
fastDeceleration={true}
|
||||
>
|
||||
<View className={styles['picker-padding']} />
|
||||
{monthOptions.map((option, index) => (
|
||||
<View
|
||||
key={option.value}
|
||||
className={`${styles['picker-item']} ${
|
||||
option.value === selectedMonth ? styles['picker-item-active'] : ''
|
||||
}`}
|
||||
data-value={option.value}
|
||||
>
|
||||
<Text className={styles['picker-item-text']}>{option.text}</Text>
|
||||
</View>
|
||||
))}
|
||||
<View className={styles['picker-padding']} />
|
||||
</ScrollView>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</CommonPopup>
|
||||
)
|
||||
}
|
||||
|
||||
export default TimePicker
|
||||
Reference in New Issue
Block a user