/**
 * 注意点:
 * 1.需要保证每一列的数据不会有重复的(重复了也会兼容)
 */ import React , { Component } from 'react' ; import { StyleSheet , View , } from 'react-native' ; import WheelCurvedPicker from '../WheelCurvedPicker' ; import PickerHeader from './PickerHeader' ; import { ICommonPickerProps as IProps } from '../types' ; import { isIPhoneX } from './utils' ; const Picker = WheelCurvedPicker ; let PickerItem = Picker . Item ; export interface IState { style ? : any ; pickerElevation ? : number ; selectedValue : any ; wheelSelectedIndexes : Array < number > ; pickerData : Array < any > | { [ key ] : Array < any > ; } | { [ key ] : Array < { [ key ] : Array < any > ; } > ; } ; wheelDatas : Array < Array < string | number > > ; } export default class CommonPicker extends Component < IProps , IState > { static defaultProps = { showHeader : true , wheelStyles : [ ] , pickerConfirmBtnText : '确定' , pickerCancelBtnText : '取消' , selectedValue : [ ] } ; private pickerStyle : 'parallel' | 'cascade' ; private pickedValue : any ; private wheelRefs : Array < any > = [ ] ; private wheelSelectedIndexes : Array < number > ; constructor ( props , context ) { super ( props , context ) ; this . state = this . _getStateFromProps ( props ) ; } componentDidMount ( ) { if ( this . pickerStyle == 'cascade' ) { this . _getCascadeData ( this . props . pickerData , true , this . props . selectedValue as Array < any > ) ; } } UNSAFE_componentWillReceiveProps ( newProps , nextContext ) { let newState = this . _getStateFromProps ( newProps ) ; this . setState ( newState , ( ) => { if ( this . pickerStyle == 'cascade' ) { this . _getCascadeData ( newProps . pickerData , true , newProps . selectedValue ) ; } } ) ; } _getDeepLength = ( node , length = 0 ) => { if ( Array . isArray ( node ) ) { return length + ( node . length > 0 ? 1 : 0 ) ; } length ++ ; let keys = Object . keys ( node ) ; let tempLengths = [ ] ; for ( let key of keys ) { tempLengths . push ( this . _getDeepLength ( node [ key ] , length ) ) ; } return Math . max ( ... tempLengths ) ; } ; _getStateFromProps ( props ) { let { pickerData , selectedValue : tempSelectedValue } = props ; if ( pickerData == undefined || Object . keys ( pickerData ) . length == 0 || pickerData . length == 0 ) { pickerData = [ [ '' ] ] ; } let selectedValue = [ ] ; if ( tempSelectedValue && tempSelectedValue . constructor !== Array ) { selectedValue = [ tempSelectedValue ] ; } else { selectedValue = [ ... tempSelectedValue ] ; } let pickerStyle = pickerData . constructor === Array ? 'parallel' : 'cascade' ; let wheelSelectedIndexes ; let cascadeData = { wheelDatas : undefined } ; if ( pickerStyle === 'parallel' ) { wheelSelectedIndexes = Array . from ( { length : pickerData . length } ) ; let isSingleWheel = false ; if ( ! pickerData . some ( x => Array . isArray ( x ) ) ) { let index = 0 ; wheelSelectedIndexes [ index ] = 0 ; let findIndex = pickerData . findIndex ( x => x == selectedValue [ 0 ] ) ; if ( findIndex > 0 ) { wheelSelectedIndexes [ index ] = findIndex ; } selectedValue [ index ] = pickerData [ wheelSelectedIndexes [ index ] ] ; pickerData = [ pickerData ] ; isSingleWheel = true ; } else { if ( pickerData . some ( x => ! Array . isArray ( x ) ) ) { console . warn ( 'parallel模式，数组内部要么全部数组，要么全部字符串/数字' ) ; pickerData = pickerData . map ( x => { if ( Array . isArray ( x ) ) { return x ; } return [ x ] ; } ) ; } for ( let index in pickerData ) { let wheelData = pickerData [ index ] ; wheelSelectedIndexes [ index ] = 0 ; let findIndex = wheelData . findIndex ( x => x == selectedValue [ index ] ) ; if ( findIndex > 0 ) { wheelSelectedIndexes [ index ] = findIndex ; } selectedValue [ index ] = wheelData [ wheelSelectedIndexes [ index ] ] ; } } this . wheelSelectedIndexes = wheelSelectedIndexes ; } else if ( pickerStyle === 'cascade' ) { let maxDeepLength = this . _getDeepLength ( pickerData , 0 ) ; wheelSelectedIndexes = Array . from ( { length : maxDeepLength } ) ; this . wheelSelectedIndexes = wheelSelectedIndexes ; } this . pickedValue = JSON . parse ( JSON . stringify ( selectedValue ) ) ; this . pickerStyle = pickerStyle as 'parallel' | 'cascade' ; let result = Object . assign ( Object . assign ( { } , props ) , { pickerData , selectedValue , wheelDatas : cascadeData . wheelDatas , wheelSelectedIndexes : wheelSelectedIndexes } ) ; return result ; } _pickerCancel = ( ) => { this . props . onPickerCancel && this . props . onPickerCancel ( this . pickedValue ) ; } ; _pickerConfirm = ( ) => { this . props . onPickerConfirm && this . props . onPickerConfirm ( this . pickedValue ) ; } ; _onValueChange = ( pickerValue = this . pickedValue , wheelIndex ) => { this . props . onValueChange && this . props . onValueChange ( JSON . parse ( JSON . stringify ( pickerValue ) ) , wheelIndex ) ; } ; _updateSelectedIndex = ( wheelIndex , valueIndex ) => { let wheelSelectedIndexes = this . state . wheelSelectedIndexes . slice ( 0 , wheelIndex ) . concat ( [ parseInt ( valueIndex + '' ) ] ) . concat ( this . state . wheelSelectedIndexes . slice ( wheelIndex + 1 ) ) ; this . setState ( { wheelSelectedIndexes : wheelSelectedIndexes } ) ; return wheelSelectedIndexes ; } ; oaram param onInit param pickedValueArray param wheelIndex param valueIndex _getCascadeData = ( pickerData , onInit = false , pickedValueArray = [ ] , wheelIndex = 0 , valueIndex = 0 ) => { let wheelDatas = [ ] ; let maxDeepLength = this . wheelSelectedIndexes . length ; let wheelSelectedIndexes = Array . from ( { length : maxDeepLength } ) ; let validDeepLength = 1 ; let node = pickerData ; for ( let i = 0 ; i < maxDeepLength ; i ++ ) { if ( wheelIndex > 0 && i < wheelIndex ) { wheelDatas [ i ] = this . state . wheelDatas [ i ] ; wheelSelectedIndexes [ i ] = this . state . wheelSelectedIndexes [ i ] ; if ( node . constructor === Array ) { node = null ; } else { let keys = Object . keys ( node ) ; node = this . state . wheelSelectedIndexes [ i ] >= 0 ? node [ keys [ this . state . wheelSelectedIndexes [ i ] ] ] : null ; } validDeepLength ++ ; continue ; } if ( node == null ) { break ; } if ( node . constructor === Array ) { wheelDatas [ i ] = [ ... node ] ; if ( onInit ) { wheelSelectedIndexes [ i ] = node . findIndex ( x => x == pickedValueArray [ i ] ) ; if ( wheelSelectedIndexes [ i ] < 0 ) { wheelSelectedIndexes [ i ] = 0 ; } } else { if ( i == wheelIndex ) { wheelSelectedIndexes [ i ] = valueIndex ; } else { let prevSelectedIndex = this . state . wheelSelectedIndexes [ i ] ; if ( prevSelectedIndex >= 0 && this . state . wheelDatas [ i ] != undefined && wheelDatas [ i ] != undefined && this . state . wheelDatas [ i ] [ prevSelectedIndex ] == wheelDatas [ i ] [ prevSelectedIndex ] ) { wheelSelectedIndexes [ i ] = prevSelectedIndex ; } else { wheelSelectedIndexes [ i ] = 0 ; } } } node = null ; } else { let keys = Object . keys ( node ) ; wheelDatas [ i ] = [ ... keys ] ; if ( onInit ) { wheelSelectedIndexes [ i ] = keys . findIndex ( x => x == pickedValueArray [ i ] ) ; if ( wheelSelectedIndexes [ i ] < 0 ) { wheelSelectedIndexes [ i ] = 0 ; } } else { if ( i == wheelIndex ) { wheelSelectedIndexes [ i ] = valueIndex ; } else { let prevSelectedIndex = this . state . wheelSelectedIndexes [ i ] ; if ( prevSelectedIndex >= 0 && this . state . wheelDatas [ i ] != undefined && wheelDatas [ i ] != undefined && this . state . wheelDatas [ i ] [ prevSelectedIndex ] == wheelDatas [ i ] [ prevSelectedIndex ] ) { wheelSelectedIndexes [ i ] = prevSelectedIndex ; } else { wheelSelectedIndexes [ i ] = 0 ; } } } node = wheelSelectedIndexes [ i ] >= 0 ? node [ keys [ wheelSelectedIndexes [ i ] + '' ] ] : null ; } this . _updateSelectedIndex ( i , wheelSelectedIndexes [ i ] ) ; validDeepLength ++ ; } let returnPickerData = Array . from ( { length : wheelSelectedIndexes . length } , ( ) => [ ] ) ; for ( let i = 0 ; i < wheelSelectedIndexes . length ; i ++ ) { returnPickerData [ i ] = wheelDatas [ i ] ? wheelDatas [ i ] [ wheelSelectedIndexes [ i ] + '' ] : undefined ; } this . setState ( { wheelDatas : wheelDatas , wheelSelectedIndexes : wheelSelectedIndexes , } ) ; this . pickedValue = returnPickerData ; return { wheelDatas , wheelSelectedIndexes : wheelSelectedIndexes , pickerData : returnPickerData } ; } ; _renderParallelWheel ( pickerData ) { return pickerData . map ( ( item , index ) => { const wheelStyle = ( this . props . wheelStyles || [ ] ) [ index ] || { } ; return ( < View key = { index } style = { [ styles . pickerWheel , wheelStyle ] } > < Picker selectedValue = { this . state . wheelSelectedIndexes [ index ] } itemStyle = { this . props . itemStyle } indicator = { this . props . indicator } indicatorColor = { this . props . indicatorColor } onValueChange = { valueIndex => { this . pickedValue . splice ( index , 1 , pickerData [ index ] [ valueIndex ] ) ; this . _updateSelectedIndex ( index , valueIndex ) ; this . _onValueChange ( this . pickedValue , index ) ; } } > { item . map ( ( value , itemIndex ) => ( < PickerItem key = { itemIndex } value = { itemIndex } label = { value . toString ( ) } / > ) ) } < /Picker> < /View>); } ) ; } _renderCascadeWheel ( pickerData ) { return ( < View style = { [ styles . pickerWrap ] } > { this . state . wheelDatas && ( this . state . wheelSelectedIndexes || [ ] ) . map ( ( x , index ) => { const wheelStyle = ( this . props . wheelStyles || [ ] ) [ index ] || { } ; return ( < View key = { index } style = { [ styles . pickerWheel , wheelStyle ] } > < Picker ref = { ref => this . wheelRefs [ index ] = ref } selectedValue = { this . state . wheelSelectedIndexes [ index ] } itemStyle = { this . props . itemStyle } indicator = { this . props . indicator } indicatorColor = { this . props . indicatorColor } onValueChange = { valueIndex => { let cascadeData = this . _getCascadeData ( pickerData , false , [ ] , index , parseInt ( valueIndex + '' ) ) ; this . pickedValue = cascadeData . pickerData ; this . _onValueChange ( cascadeData . pickerData , index ) ; } } > { } { ( this . state . wheelDatas [ index ] || [ ] ) . map ( ( value , index ) => ( < PickerItem key = { index } value = { index } label = { value . toString ( ) } / > ) ) } < /Picker> < /View>); } ) } < /View>); } _renderWheel ( pickerData ) { let wheel = null ; if ( this . pickerStyle === 'parallel' ) { wheel = this . _renderParallelWheel ( pickerData ) ; } else if ( this . pickerStyle === 'cascade' ) { wheel = this . _renderCascadeWheel ( pickerData ) ; } return wheel ; } render ( ) { const { isModal , modalProps , modalVisible , onModalVisibleChange } = this . props ; const pickerView = ( < View style = { [ styles . pickerBox , { minHeight : 240 + ( this . props . showHeader ? 40 : 0 ) } , this . props . style ] } > { this . props . showHeader ? < PickerHeader { ... this . props } onPickerCancel = { ( ) => this . _pickerCancel ( ) } onPickerConfirm = { ( ) => this . _pickerConfirm ( ) } /> : null } < View style = { [ styles . pickerWrap , this . props . pickerWrapperStyle ] } > { this . _renderWheel ( this . state . pickerData ) } < /View> { isModal && isIPhoneX ? < View style = { { height : 34 } } /> : null} < /View>); if ( isModal ) { const Modal = require ( 'react-native-modal' ) . default ; if ( Modal ) { return ( < Modal onBackdropPress = { ( ) => { onModalVisibleChange && onModalVisibleChange ( false ) ; } } onBackButtonPress = { ( ) => { onModalVisibleChange && onModalVisibleChange ( false ) ; } } { ... ( modalProps || { } ) } style = { [ { flex : 1 , justifyContent : 'flex-end' , margin : 0 } , ( modalProps || { style : undefined } ) . style ] } isVisible = { modalVisible } > { pickerView } < /Modal>); } } return pickerView ; } } let styles = StyleSheet . create ( { pickerBox : { backgroundColor : 'white' } , pickerWrap : { flexDirection : 'row' , flex : 1 , alignItems : 'center' , minHeight : 240 , } , pickerWheel : { flex : 1 } , } ) ;