Files
workspace/code/allapp/allapp-rn/src/components/common/ao/DatePicker.tsx
T
2026-02-03 22:19:45 +08:00

173 lines
4.9 KiB
TypeScript

import type { IDatePickerProps } from '@yz1311/react-native-wheel-picker'
import SegmentedControl from '@react-native-segmented-control/segmented-control'
import { DatePicker } from '@yz1311/react-native-wheel-picker'
import dayjs from 'dayjs'
import React, { useEffect, useRef, useState } from 'react'
import { Animated, Modal, StyleSheet, Text, TouchableOpacity, View } from 'react-native'
import { toDate } from '@/utils/time'
// 定义 mode 类型
export type DatePickerMode = 'year' | 'month' | 'date' | 'time' | 'datetime'
// 定义 format 类型
export type DateFormatType = 'YYYY' | 'YYYY-MM' | 'YYYY-MM-DD' | 'HH:mm:ss' | 'YYYY-MM-DD HH:mm:ss'
interface ADatePickerProps extends Omit<IDatePickerProps, 'date'> {
date?: Date | string
show?: boolean
onClose?: () => void
onConfirm?: (value: string) => void
mode?: DatePickerMode
format?: DateFormatType
}
const defaultFormatMap: Record<DatePickerMode, DateFormatType> = {
year: 'YYYY',
month: 'YYYY-MM',
date: 'YYYY-MM-DD',
time: 'HH:mm:ss',
datetime: 'YYYY-MM-DD HH:mm:ss',
}
export default function ADatePicker({
date,
show = false,
onClose,
onConfirm,
format,
...restProps
}: ADatePickerProps) {
const fadeAnim = useRef(new Animated.Value(0)).current
const slideAnim = useRef(new Animated.Value(300)).current
const [visible, setVisible] = useState(show)
const [index, setIndex] = useState(0)
const [mode, setMode] = useState<DatePickerMode>('month')
// 使用传入 format 或默认 format
const finalFormat = format || defaultFormatMap[mode]
useEffect(() => {
if (show) {
setVisible(true)
fadeAnim.setValue(0)
slideAnim.setValue(300)
Animated.parallel([
Animated.timing(fadeAnim, { toValue: 1, duration: 250, useNativeDriver: true }),
Animated.timing(slideAnim, { toValue: 0, duration: 250, useNativeDriver: true }),
]).start()
}
else {
Animated.parallel([
Animated.timing(fadeAnim, { toValue: 0, duration: 250, useNativeDriver: true }),
Animated.timing(slideAnim, { toValue: 280, duration: 250, useNativeDriver: true }),
]).start(() => setVisible(false))
}
}, [show])
const handleCancel = () => onClose?.()
const handleConfirm = (date: Date) => {
const value = dayjs(date).format(finalFormat)
onConfirm?.(value)
}
if (!visible)
return null
return (
<Modal transparent visible={visible} animationType="none">
<Animated.View style={[styles.overlay, { opacity: fadeAnim }]}>
<TouchableOpacity style={{ flex: 1 }} activeOpacity={1} onPress={handleCancel} />
</Animated.View>
<Animated.View
style={[styles.pickerContainer, { transform: [{ translateY: slideAnim }] }]}
>
<View className="px-3 pt-2 flex flex-col gap-2">
<View className="w-full">
<Text>快速选择</Text>
{/* <ATab
data={[
{
label: '自定义',
key: '自定义',
},
{
label: '不限',
key: '不限',
},
{
label: '今天',
key: '今天',
},
{
label: '本月',
key: '本月',
},
{
label: '本年',
key: '本年',
},
{
label: '范围',
key: '范围',
},
]}
onChange={(key) => {
setMode('datetime')
}}
/> */}
</View>
<View className="w-full">
<Text>选择模式</Text>
<SegmentedControl
values={['日', '月', '年', '时间', '日期']}
selectedIndex={index}
style={{ borderRadius: 2, width: '100%' }}
sliderStyle={{
borderRadius: 2,
}}
onChange={(event: any) => setIndex(event.nativeEvent.selectedSegmentIndex)}
/>
</View>
</View>
<DatePicker
{...restProps}
date={toDate(date)}
mode={mode}
isModal={false}
onPickerCancel={handleCancel}
onPickerConfirm={handleConfirm}
showHeader={false}
onDateChange={(value) => {
console.log(value)
}}
/>
<View className="flex flex-row gap-4 px-3 pb-1 v">
{/* <AButton variant="outline" className="flex-1">取消</AButton>
<AButton className="flex-1">确认</AButton> */}
</View>
</Animated.View>
</Modal>
)
}
const styles = StyleSheet.create({
overlay: {
...StyleSheet.absoluteFillObject,
backgroundColor: 'rgba(0,0,0,0.5)',
},
pickerContainer: {
backgroundColor: '#fff',
overflow: 'hidden',
position: 'absolute',
bottom: 0,
width: '100%',
},
})