删除多余文件
This commit is contained in:
@@ -1,77 +0,0 @@
|
|||||||
# TimePicker 时间选择器组件
|
|
||||||
|
|
||||||
## 功能特性
|
|
||||||
|
|
||||||
- 使用自定义样式重写PickerViewColumn功能
|
|
||||||
- 完全还原原生PickerView的样式和动画效果
|
|
||||||
- 支持年份和月份选择
|
|
||||||
- 平滑的滚动动画和切换效果
|
|
||||||
- 响应式设计,支持触摸滚动
|
|
||||||
- 渐变遮罩效果增强视觉层次
|
|
||||||
|
|
||||||
## 技术实现
|
|
||||||
|
|
||||||
### 核心特性
|
|
||||||
- 使用ScrollView替代PickerViewColumn
|
|
||||||
- 自定义滚动逻辑实现选项对齐
|
|
||||||
- CSS动画和过渡效果还原原生体验
|
|
||||||
- 智能滚动位置计算和自动对齐
|
|
||||||
|
|
||||||
### 样式还原
|
|
||||||
- 选中项指示器(高亮背景)
|
|
||||||
- 渐变遮罩效果(顶部和底部)
|
|
||||||
- 平滑的过渡动画
|
|
||||||
- 精确的尺寸和间距
|
|
||||||
|
|
||||||
## 使用方法
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
import { TimePicker } from '@/components/TimePicker'
|
|
||||||
|
|
||||||
const [visible, setVisible] = useState(false)
|
|
||||||
|
|
||||||
<TimePicker
|
|
||||||
visible={visible}
|
|
||||||
onClose={() => setVisible(false)}
|
|
||||||
onConfirm={(year, month) => {
|
|
||||||
console.log('选择的时间:', year, month)
|
|
||||||
setVisible(false)
|
|
||||||
}}
|
|
||||||
defaultYear={2024}
|
|
||||||
defaultMonth={6}
|
|
||||||
minYear={2020}
|
|
||||||
maxYear={2030}
|
|
||||||
/>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Props
|
|
||||||
|
|
||||||
| 属性 | 类型 | 默认值 | 说明 |
|
|
||||||
|------|------|--------|------|
|
|
||||||
| visible | boolean | - | 是否显示选择器 |
|
|
||||||
| 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`文件来自定义样式:
|
|
||||||
|
|
||||||
- `.time-picker-popup`: 弹出层容器
|
|
||||||
- `.picker-container`: 选择器容器
|
|
||||||
- `.custom-picker`: 自定义选择器
|
|
||||||
- `.picker-indicator`: 选中项指示器
|
|
||||||
- `.picker-column`: 选择列
|
|
||||||
- `.picker-item`: 选择项
|
|
||||||
- `.picker-item-active`: 激活状态的选择项
|
|
||||||
|
|
||||||
## 测试
|
|
||||||
|
|
||||||
运行测试页面:
|
|
||||||
```tsx
|
|
||||||
import TimePickerTest from '@/components/TimePicker/test'
|
|
||||||
```
|
|
||||||
@@ -1,233 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
.demoContainer {
|
|
||||||
padding: 20px;
|
|
||||||
text-align: center;
|
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
||||||
min-height: 100vh;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 700;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
display: block;
|
|
||||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
|
||||||
font-size: 16px;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
display: block;
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.demoButton {
|
|
||||||
margin: 20px 0;
|
|
||||||
width: 250px;
|
|
||||||
height: 50px;
|
|
||||||
border-radius: 25px;
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 600;
|
|
||||||
background: rgba(255, 255, 255, 0.2);
|
|
||||||
border: 2px solid rgba(255, 255, 255, 0.3);
|
|
||||||
color: white;
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
background: rgba(255, 255, 255, 0.3);
|
|
||||||
transform: scale(0.98);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.demoResult {
|
|
||||||
margin: 30px 0;
|
|
||||||
padding: 20px;
|
|
||||||
background: rgba(255, 255, 255, 0.1);
|
|
||||||
border-radius: 16px;
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
||||||
|
|
||||||
text {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.demoFeatures {
|
|
||||||
margin-top: 40px;
|
|
||||||
padding: 20px;
|
|
||||||
background: rgba(255, 255, 255, 0.1);
|
|
||||||
opacity: 0.9;
|
|
||||||
border-radius: 16px;
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.featureTitle {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
display: block;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.featureItem {
|
|
||||||
font-size: 14px;
|
|
||||||
margin: 8px 0;
|
|
||||||
display: block;
|
|
||||||
color: rgba(255, 255, 255, 0.9);
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
import React, { useState } from 'react'
|
|
||||||
import { View, Button, Text } from '@tarojs/components'
|
|
||||||
import TimePicker from './TimePicker'
|
|
||||||
import styles from './demo.module.scss'
|
|
||||||
|
|
||||||
const TimePickerDemo: React.FC = () => {
|
|
||||||
const [visible, setVisible] = useState(false)
|
|
||||||
const [selectedTime, setSelectedTime] = useState('')
|
|
||||||
|
|
||||||
const handleConfirm = (year: number, month: number) => {
|
|
||||||
setSelectedTime(`${year}年${month}月`)
|
|
||||||
setVisible(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View className={styles.demoContainer}>
|
|
||||||
<Text className={styles.title}>TimePicker 演示</Text>
|
|
||||||
<Text className={styles.subtitle}>体验"一个一个往下翻"的效果</Text>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
onClick={() => setVisible(true)}
|
|
||||||
className={styles.demoButton}
|
|
||||||
>
|
|
||||||
打开时间选择器
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
{selectedTime && (
|
|
||||||
<View className={styles.demoResult}>
|
|
||||||
<Text>已选择: {selectedTime}</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<View className={styles.demoFeatures}>
|
|
||||||
<Text className={styles.featureTitle}>特性说明:</Text>
|
|
||||||
<Text className={styles.featureItem}>• 每次只显示一个选项</Text>
|
|
||||||
<Text className={styles.featureItem}>• 完美居中对齐</Text>
|
|
||||||
<Text className={styles.featureItem}>• 平滑滚动动画</Text>
|
|
||||||
<Text className={styles.featureItem}>• 触摸结束后自动对齐</Text>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<TimePicker
|
|
||||||
visible={visible}
|
|
||||||
onClose={() => setVisible(false)}
|
|
||||||
onConfirm={handleConfirm}
|
|
||||||
defaultYear={2024}
|
|
||||||
defaultMonth={6}
|
|
||||||
minYear={2020}
|
|
||||||
maxYear={2030}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TimePickerDemo
|
|
||||||
@@ -1,187 +0,0 @@
|
|||||||
/* 时间选择器弹出层样式 */
|
|
||||||
.time-picker-popup {
|
|
||||||
.common-popup-content {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.popup-handle {
|
|
||||||
width: 32px;
|
|
||||||
height: 4px;
|
|
||||||
background: #e0e0e0;
|
|
||||||
border-radius: 2px;
|
|
||||||
margin: 12px auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.picker-container {
|
|
||||||
padding: 26px 16px 0 16px;
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.picker-wrapper {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-picker {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 216px;
|
|
||||||
background: #fff;
|
|
||||||
border-radius: 8px;
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
/* 确保只显示一个选项 */
|
|
||||||
perspective: 1000px;
|
|
||||||
/* 水平布局 */
|
|
||||||
flex-direction: row;
|
|
||||||
/* 确保列之间有适当间距 */
|
|
||||||
gap: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 选中项指示器 */
|
|
||||||
.picker-indicator {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 48px;
|
|
||||||
background: rgba(22, 24, 35, 0.05);
|
|
||||||
transform: translateY(-50%);
|
|
||||||
pointer-events: none;
|
|
||||||
z-index: 1;
|
|
||||||
border-radius: 4px;
|
|
||||||
box-shadow: inset 0 0 0 1px rgba(22, 24, 35, 0.1);
|
|
||||||
/* 确保指示器完美覆盖选中项 */
|
|
||||||
margin: 0 20px;
|
|
||||||
width: calc(100% - 40px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.picker-column {
|
|
||||||
flex: 1;
|
|
||||||
height: 100%;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
/* 水平居中布局 */
|
|
||||||
min-width: 0;
|
|
||||||
/* 确保列之间有适当间距 */
|
|
||||||
padding: 0 8px;
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
border-right: 1px solid rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 确保滚动容器正确显示 */
|
|
||||||
&::before,
|
|
||||||
&::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 84px;
|
|
||||||
pointer-events: none;
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
top: 0;
|
|
||||||
background: linear-gradient(to bottom, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0.8) 50%, rgba(255, 255, 255, 0) 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
bottom: 0;
|
|
||||||
background: linear-gradient(to top, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0.8) 50%, rgba(255, 255, 255, 0) 100%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.picker-scroll {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
will-change: scroll-position;
|
|
||||||
-webkit-overflow-scrolling: touch;
|
|
||||||
/* 确保滚动行为 */
|
|
||||||
scroll-snap-type: y mandatory;
|
|
||||||
/* 优化滚动性能 */
|
|
||||||
overscroll-behavior: contain;
|
|
||||||
}
|
|
||||||
|
|
||||||
.picker-padding {
|
|
||||||
height: 84px; /* (216 - 48) / 2 = 84px,用于居中对齐 */
|
|
||||||
/* 确保padding区域不可见 */
|
|
||||||
opacity: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.picker-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
height: 48px;
|
|
||||||
width: 100%;
|
|
||||||
font-size: 16px;
|
|
||||||
color: #161823;
|
|
||||||
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
|
||||||
position: relative;
|
|
||||||
will-change: transform, color;
|
|
||||||
/* 确保每个选项都能正确对齐 */
|
|
||||||
scroll-snap-align: center;
|
|
||||||
/* 水平居中 */
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
&.picker-item-active {
|
|
||||||
color: #161823;
|
|
||||||
font-weight: 600;
|
|
||||||
transform: scale(1.02);
|
|
||||||
|
|
||||||
.picker-item-text {
|
|
||||||
color: #161823;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(.picker-item-active) {
|
|
||||||
color: rgba(22, 24, 35, 0.6);
|
|
||||||
|
|
||||||
.picker-item-text {
|
|
||||||
color: rgba(22, 24, 35, 0.6);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.picker-item-text {
|
|
||||||
font-size: 16px;
|
|
||||||
color: inherit;
|
|
||||||
text-align: center;
|
|
||||||
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
|
||||||
user-select: none;
|
|
||||||
width: 100%;
|
|
||||||
line-height: 48px;
|
|
||||||
white-space: nowrap;
|
|
||||||
/* 确保文字完美居中 */
|
|
||||||
display: block;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
/* 强制居中对齐 */
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
top: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 滚动条隐藏 */
|
|
||||||
.picker-scroll {
|
|
||||||
::-webkit-scrollbar {
|
|
||||||
width: 0;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 移除重复的渐变遮罩代码,已在.picker-column中定义 */
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
export { default } from './TimePicker'
|
|
||||||
export type { TimePickerProps } from './TimePicker'
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
.testContainer {
|
|
||||||
padding: 20px;
|
|
||||||
text-align: center;
|
|
||||||
background: #f8f9fa;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.testTitle {
|
|
||||||
font-size: 22px;
|
|
||||||
font-weight: 700;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
display: block;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.testSubtitle {
|
|
||||||
font-size: 16px;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
display: block;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.testInfo {
|
|
||||||
margin: 20px 0;
|
|
||||||
padding: 20px;
|
|
||||||
background: #fff;
|
|
||||||
border-radius: 12px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
text-align: left;
|
|
||||||
|
|
||||||
text {
|
|
||||||
font-size: 14px;
|
|
||||||
margin: 8px 0;
|
|
||||||
display: block;
|
|
||||||
color: #555;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.testButton {
|
|
||||||
margin: 20px 0;
|
|
||||||
width: 200px;
|
|
||||||
height: 44px;
|
|
||||||
border-radius: 22px;
|
|
||||||
font-size: 16px;
|
|
||||||
background: #007bff;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.testResult {
|
|
||||||
margin: 20px 0;
|
|
||||||
color: white;
|
|
||||||
border-radius: 8px;
|
|
||||||
|
|
||||||
text {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
import React, { useState } from 'react'
|
|
||||||
import { View, Text, Button } from '@tarojs/components'
|
|
||||||
import TimePicker from './TimePicker'
|
|
||||||
import styles from './layout-test.module.scss'
|
|
||||||
|
|
||||||
const LayoutTest: React.FC = () => {
|
|
||||||
const [visible, setVisible] = useState(false)
|
|
||||||
const [selectedTime, setSelectedTime] = useState('')
|
|
||||||
|
|
||||||
const handleConfirm = (year: number, month: number) => {
|
|
||||||
setSelectedTime(`${year}年${month}月`)
|
|
||||||
setVisible(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View className={styles.testContainer}>
|
|
||||||
<Text className={styles.testTitle}>布局测试</Text>
|
|
||||||
<Text className={styles.testSubtitle}>验证年份和月份的水平居中对齐</Text>
|
|
||||||
|
|
||||||
<View className={styles.testInfo}>
|
|
||||||
<Text>• 年份和月份应该在同一行显示</Text>
|
|
||||||
<Text>• 两个列应该水平居中对齐</Text>
|
|
||||||
<Text>• 选中项指示器应该完美覆盖两个列</Text>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
onClick={() => setVisible(true)}
|
|
||||||
className={styles.testButton}
|
|
||||||
>
|
|
||||||
测试布局
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
{selectedTime && (
|
|
||||||
<View className={styles.testResult}>
|
|
||||||
<Text>选择结果: {selectedTime}</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<TimePicker
|
|
||||||
visible={visible}
|
|
||||||
onClose={() => setVisible(false)}
|
|
||||||
onConfirm={handleConfirm}
|
|
||||||
defaultYear={2024}
|
|
||||||
defaultMonth={6}
|
|
||||||
minYear={2020}
|
|
||||||
maxYear={2030}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
.container {
|
|
||||||
padding: 20px;
|
|
||||||
text-align: center;
|
|
||||||
background: #f5f5f5;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: 20px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
margin: 20px 0;
|
|
||||||
width: 200px;
|
|
||||||
height: 44px;
|
|
||||||
border-radius: 22px;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result {
|
|
||||||
margin-top: 30px;
|
|
||||||
padding: 20px;
|
|
||||||
background: #fff;
|
|
||||||
border-radius: 12px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
|
|
||||||
text {
|
|
||||||
font-size: 16px;
|
|
||||||
color: #333;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
import React, { useState } from 'react'
|
|
||||||
import { View, Button, Text } from '@tarojs/components'
|
|
||||||
import TimePicker from './TimePicker'
|
|
||||||
import styles from './test.module.scss'
|
|
||||||
|
|
||||||
const TimePickerTest: React.FC = () => {
|
|
||||||
const [visible, setVisible] = useState(false)
|
|
||||||
const [selectedTime, setSelectedTime] = useState('')
|
|
||||||
|
|
||||||
const handleConfirm = (year: number, month: number) => {
|
|
||||||
setSelectedTime(`${year}年${month}月`)
|
|
||||||
setVisible(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View className={styles.container}>
|
|
||||||
<Text className={styles.title}>TimePicker 组件测试</Text>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
onClick={() => setVisible(true)}
|
|
||||||
className={styles.button}
|
|
||||||
>
|
|
||||||
打开时间选择器
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
{selectedTime && (
|
|
||||||
<View className={styles.result}>
|
|
||||||
<Text>已选择: {selectedTime}</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<TimePicker
|
|
||||||
visible={visible}
|
|
||||||
onClose={() => setVisible(false)}
|
|
||||||
onConfirm={handleConfirm}
|
|
||||||
defaultYear={2024}
|
|
||||||
defaultMonth={6}
|
|
||||||
minYear={2020}
|
|
||||||
maxYear={2030}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TimePickerTest
|
|
||||||
@@ -8,7 +8,6 @@ import NumberInterval from "./NumberInterval";
|
|||||||
import TimeSelector from "./TimeSelector";
|
import TimeSelector from "./TimeSelector";
|
||||||
import TitleTextarea from "./TitleTextarea";
|
import TitleTextarea from "./TitleTextarea";
|
||||||
import CommonPopup from "./CommonPopup";
|
import CommonPopup from "./CommonPopup";
|
||||||
import TimePicker from "./TimePicker/TimePicker";
|
|
||||||
import { CalendarUI, DialogCalendarCard } from "./Picker";
|
import { CalendarUI, DialogCalendarCard } from "./Picker";
|
||||||
import CommonDialog from "./CommonDialog";
|
import CommonDialog from "./CommonDialog";
|
||||||
import PublishMenu from "./PublishMenu/PublishMenu";
|
import PublishMenu from "./PublishMenu/PublishMenu";
|
||||||
@@ -28,7 +27,6 @@ export {
|
|||||||
TimeSelector,
|
TimeSelector,
|
||||||
TitleTextarea,
|
TitleTextarea,
|
||||||
CommonPopup,
|
CommonPopup,
|
||||||
TimePicker,
|
|
||||||
DialogCalendarCard,
|
DialogCalendarCard,
|
||||||
CalendarUI,
|
CalendarUI,
|
||||||
CommonDialog,
|
CommonDialog,
|
||||||
|
|||||||
Reference in New Issue
Block a user