/**
 * 日期/时间选择组件
 */ import React , { FC , PureComponent } from "react" ; import { ColorPropType , StyleProp , View , ViewPropTypes , ViewStyle , Dimensions , Text } from "react-native" ; import moment from "moment" ; import CommonPicker from "./CommonPicker" ; import { IDatePickerProps as IProps } from '../types' ; export interface IState { pickerData ? : Array < any > ; selectedDateArray : Array < string > ; } export default class DatePicker extends PureComponent < IProps , IState > { static defaultProps = { showHeader : true , labelUnit : { year : '年' , month : '月' , date : '日' , hour : '时' , minute : '分' , second : '秒' } , mode : 'date' , maxDate : moment ( ) . add ( 10 , 'years' ) . toDate ( ) , minDate : moment ( ) . add ( - 30 , 'years' ) . toDate ( ) , date : new Date ( ) , style : null , textColor : '#333' , textSize : 26 , itemSpace : 20 , } ; readonly state : IState = { pickerData : [ ] , selectedDateArray : [ ] } ; private targetDate : Date ; componentDidMount ( ) { this . _setDefaultValue ( ) ; this . setState ( { pickerData : this . _genData ( ) } ) ; } componentDidUpdate ( prevProps , prevState , snapshot ? ) { if ( this . props . date !== prevProps . date || this . props . minDate !== prevProps . minDate || this . props . maxDate !== prevProps . maxDate ) { this . _setDefaultValue ( ) ; } } _setDefaultValue = ( props = this . props ) => { if ( ! props . date || ! moment ( props . date ) . isValid ( ) ) { this . targetDate = moment ( ) . second ( 0 ) . toDate ( ) ; } else { this . targetDate = props . date ; } if ( moment ( this . targetDate ) . isBefore ( props . minDate ) || moment ( this . targetDate ) . isAfter ( props . maxDate ) ) { this . targetDate = moment ( props . minDate ) . toDate ( ) ; } const { labelUnit , mode } = props ; let dataArray = [ ] ; let yearWithUnit = moment ( this . targetDate ) . year ( ) + labelUnit . year ; let monthWithUnit = ( moment ( this . targetDate ) . month ( ) + 1 ) + labelUnit . month ; let dateWithUnit = moment ( this . targetDate ) . date ( ) + labelUnit . date ; let hourWithUnit = moment ( this . targetDate ) . hour ( ) + labelUnit . hour ; let minuteWithUnit = moment ( this . targetDate ) . minute ( ) + labelUnit . minute ; switch ( props . mode ) { case 'year' : dataArray = [ yearWithUnit ] ; break ; case 'month' : dataArray = [ yearWithUnit , monthWithUnit ] ; break ; case 'date' : dataArray = [ yearWithUnit , monthWithUnit , dateWithUnit ] ; break ; case 'time' : dataArray = [ hourWithUnit , minuteWithUnit ] ; break ; case 'datetime' : dataArray = [ yearWithUnit , monthWithUnit , dateWithUnit , hourWithUnit , minuteWithUnit ] ; break ; } this . setState ( { selectedDateArray : dataArray } ) ; } ; _genData = ( props = this . props ) => { if ( moment ( props . maxDate ) . isBefore ( moment ( props . minDate ) ) || ! moment ( props . minDate ) . isValid ( ) || ! moment ( props . maxDate ) . isValid ( ) ) { console . log ( 'maxDate不能小于minDate' ) ; return [ ] ; } let years = new Set ( ) ; for ( let i = moment ( props . minDate ) . year ( ) ; i <= moment ( props . maxDate ) . year ( ) ; i ++ ) { years . add ( i + props . labelUnit . year ) ; } let currentYear = moment ( this . targetDate ) . year ( ) ; let currentMonth = moment ( this . targetDate ) . month ( ) ; let monthes = this . _getMonthesByYear ( props , currentYear ) ; let days = this . _getDaysByYearAndMonth ( props , currentYear , currentMonth ) ; switch ( props . mode ) { case 'year' : return [ Array . from ( years ) ] ; case 'month' : return [ Array . from ( years ) , Array . from ( monthes ) ] ; case 'date' : return [ Array . from ( years ) , Array . from ( monthes ) , Array . from ( days ) ] ; case 'time' : return this . _genTimeData ( props ) ; case 'datetime' : return [ Array . from ( years ) , Array . from ( monthes ) , Array . from ( days ) , ... this . _genTimeData ( props ) ] ; default : return [ ] ; } } ; _getMonthesByYear = ( props = this . props , currentYear ) => { let monthes = new Set ( ) ; if ( props . mode !== 'year' && props . mode !== 'time' ) { for ( let i = 1 ; i <= 12 ; i ++ ) { monthes . add ( i + props . labelUnit . month ) ; } if ( moment ( props . minDate ) . year ( ) === currentYear ) { for ( let i = 1 ; i < moment ( props . minDate ) . month ( ) + 1 ; i ++ ) { monthes . delete ( i + props . labelUnit . month ) ; } } if ( moment ( props . maxDate ) . year ( ) === currentYear ) { for ( let i = moment ( props . maxDate ) . month ( ) + 2 ; i <= 12 ; i ++ ) { monthes . delete ( i + props . labelUnit . month ) ; } } } return monthes ; } ; _getDaysByYearAndMonth = ( props = this . props , currentYear , currentMonth ) => { let days = new Set ( ) ; if ( props . mode !== 'year' && props . mode !== 'month' && props . mode !== 'time' ) { let daysInMonth = moment ( ) . year ( currentYear ) . month ( currentMonth ) . daysInMonth ( ) ; for ( let i = 1 ; i <= daysInMonth ; i ++ ) { days . add ( i + props . labelUnit . date ) ; } if ( moment ( props . minDate ) . year ( ) === currentYear && moment ( props . minDate ) . month ( ) === currentMonth ) { for ( let i = 1 ; i < moment ( props . minDate ) . date ( ) ; i ++ ) { days . delete ( i + props . labelUnit . date ) ; } } if ( moment ( props . maxDate ) . year ( ) === currentYear && moment ( props . maxDate ) . month ( ) === currentMonth ) { for ( let i = moment ( props . maxDate ) . date ( ) + 1 ; i <= 31 ; i ++ ) { days . delete ( i + props . labelUnit . date ) ; } } } return days ; } ; _genTimeData = ( prop ) => { let pickerData = { } ; const [ hours , minutes ] = [ [ ] , [ ] ] ; for ( let i = 0 ; i < 24 ; i += 1 ) { hours . push ( `${ i }${ this . props . labelUnit . hour }` ) ; } for ( let i = 0 ; i <= 59 ; i += 1 ) { minutes . push ( `${ i }${ this . props . labelUnit . minute }` ) ; } pickerData = [ hours , minutes ] ; return pickerData ; } ; _getNewMonthesAndSelectedMonthByYear = ( yearWithUnit , monthWithUnit ) => { const { mode , labelUnit } = this . props ; let nextMonthes = this . _getMonthesByYear ( this . props , parseInt ( yearWithUnit . replace ( labelUnit . year , '' ) ) ) ; let nextMonthArray = Array . from ( nextMonthes ) ; if ( nextMonthes . has ( monthWithUnit ) ) { return [ nextMonthArray , monthWithUnit ] ; } else { return [ nextMonthArray , nextMonthArray [ 0 ] ] ; } } ; _getNewDaysAndSelectedDayByYearAndMonth = ( yearWithUnit , monthWithUnit , dayWithUnit ) => { const { mode , labelUnit } = this . props ; let nextDays = this . _getDaysByYearAndMonth ( this . props , parseInt ( yearWithUnit . replace ( labelUnit . year , '' ) ) , parseInt ( monthWithUnit . replace ( labelUnit . month , '' ) ) - 1 ) ; let nextDayArray = Array . from ( nextDays ) ; if ( nextDays . has ( dayWithUnit ) ) { return [ nextDayArray , dayWithUnit ] ; } else { let lastDayInt = parseInt ( dayWithUnit . replace ( labelUnit . date , '' ) ) ; if ( lastDayInt > 28 && nextDayArray . length < lastDayInt ) { return [ nextDayArray , nextDayArray [ nextDayArray . length - 1 ] ] ; } return [ nextDayArray , nextDayArray [ 0 ] ] ; } } ; _dataArrayToMoment = ( dataArray ) => { const { mode , labelUnit } = this . props ; let date = moment ( moment ( ) . format ( 'YYYY-MM-DD' ) , 'YYYY-MM-DD' ) ; switch ( mode ) { case 'year' : date . year ( dataArray [ 0 ] . replace ( labelUnit . year , '' ) ) ; break ; case 'month' : date . year ( dataArray [ 0 ] . replace ( labelUnit . year , '' ) ) . month ( parseInt ( dataArray [ 1 ] . replace ( labelUnit . month , '' ) ) - 1 ) ; break ; case 'date' : date . year ( dataArray [ 0 ] . replace ( labelUnit . year , '' ) ) . month ( parseInt ( dataArray [ 1 ] . replace ( labelUnit . month , '' ) ) - 1 ) . date ( dataArray [ 2 ] . replace ( labelUnit . date , '' ) ) ; break ; case 'time' : date . hour ( dataArray [ 0 ] . replace ( labelUnit . hour , '' ) ) . minute ( dataArray [ 1 ] . replace ( labelUnit . minute , '' ) ) ; break ; case 'datetime' : date . year ( dataArray [ 0 ] . replace ( labelUnit . year , '' ) ) . month ( parseInt ( dataArray [ 1 ] . replace ( labelUnit . month , '' ) ) - 1 ) . date ( dataArray [ 2 ] . replace ( labelUnit . date , '' ) ) . hour ( dataArray [ 3 ] . replace ( labelUnit . hour , '' ) ) . minute ( dataArray [ 4 ] . replace ( labelUnit . minute , '' ) ) ; break ; } return date ; } ; _onDateChange = ( value , wheelIndex ) => { const { mode , labelUnit } = this . props ; let nextValue = [ ... value ] ; let nextPickerData = [ ... this . state . pickerData ] ; switch ( mode ) { case 'year' : break ; case 'month' : if ( wheelIndex == 0 ) { const [ monthes , newMonthWithUnit ] = this . _getNewMonthesAndSelectedMonthByYear ( value [ 0 ] , value [ 1 ] ) ; nextValue [ 1 ] = newMonthWithUnit ; nextPickerData [ 1 ] = monthes ; } else { } break ; case 'date' : case 'datetime' : if ( wheelIndex === 0 ) { const [ monthes , newMonthWithUnit ] = this . _getNewMonthesAndSelectedMonthByYear ( value [ 0 ] , value [ 1 ] ) ; const [ days , newDayWithUnit ] = this . _getNewDaysAndSelectedDayByYearAndMonth ( value [ 0 ] , newMonthWithUnit , value [ 2 ] ) ; nextValue [ 1 ] = newMonthWithUnit ; nextValue [ 2 ] = newDayWithUnit ; nextPickerData [ 1 ] = monthes ; nextPickerData [ 2 ] = days ; } else if ( wheelIndex === 1 ) { const [ days , newDayWithUnit ] = this . _getNewDaysAndSelectedDayByYearAndMonth ( value [ 0 ] , value [ 1 ] , value [ 2 ] ) ; nextValue [ 2 ] = newDayWithUnit ; nextPickerData [ 2 ] = days ; } else if ( wheelIndex === 2 ) { } break ; } let nextDate = this . _dataArrayToMoment ( nextValue ) ; this . setState ( { selectedDateArray : nextValue , pickerData : nextPickerData } ) ; this . targetDate = nextDate . toDate ( ) ; this . props . onDateChange && this . props . onDateChange ( nextDate . toDate ( ) ) ; } ; render ( ) { const { width : deviceWidth } = Dimensions . get ( 'window' ) ; return ( < CommonPicker { ... this . props } style = { [ { width : deviceWidth } , this . props . style ] } wheelStyles = { this . props . mode === 'datetime' ? [ { minWidth : 20 } ] : [ ] } pickerData = { this . state . pickerData } selectedValue = { this . state . selectedDateArray } onValueChange = { ( value , wheelIndex ) => { this . _onDateChange ( value , wheelIndex ) ; } } onPickerConfirm = { ( ) => { this . props . onPickerConfirm && this . props . onPickerConfirm ( this . targetDate ) ; } } />); } }