import bind from 'bind-decorator' import classnames from 'classnames' import dayjs, { Dayjs } from 'dayjs' import _pick from 'lodash/pick' import _isObject from 'lodash/isObject' import _isFunction from 'lodash/isFunction' import Taro from '@tarojs/taro' import { View } from '@tarojs/components' import { BaseEvent } from '@tarojs/components/types/common' import Calendar from './types' import AtCalendarBody from './body/index' import AtCalendarController from './controller/index' import { DefaultProps, Props, State, PropsWithDefaults } from './interface' const defaultProps: DefaultProps = { marks: [], isSwiper: true, hideArrow: true, time: '', timeStep: 15, hideTime: true, isVertical: false, selectedDates: [], isMultiSelect: false, format: 'YYYY-MM-DD', currentDate: Date.now(), monthFormat: 'YYYY年MM月', collapse: false, collapsible: false, } export default class AtCalendar extends Taro.Component> { static options = { addGlobalClass: true } static defaultProps: DefaultProps = defaultProps constructor (props: Props) { super(...arguments) const { currentDate, isMultiSelect, collapse, time } = props as PropsWithDefaults this.state = this.getInitializeState(currentDate, isMultiSelect, collapse, time) } componentWillReceiveProps (nextProps: Props) { const { currentDate, isMultiSelect, collapse, } = nextProps if(collapse !== this.state.collapse) { this.handleCollapse(collapse); } if (!currentDate || currentDate === this.props.currentDate) return if (isMultiSelect && this.props.isMultiSelect) { const { start, end } = currentDate as Calendar.SelectedDate const { start: preStart, end: preEnd } = this.props .currentDate as Calendar.SelectedDate if (start === preStart && preEnd === end) { return } } const stateValue: State = this.getInitializeState( currentDate, isMultiSelect, collapse, nextProps.time || this.state.time ) this.setState(stateValue) } @bind private getSingleSelectdState (value: Dayjs): Partial { const { generateDate, collapse } = this.state const stateValue: Partial = { selectedDate: this.getSelectedDate(value.valueOf()) } const dayjsGenerateDate: Dayjs = collapse ? value.startOf('week') : value.startOf('month') const generateDateValue: number = dayjsGenerateDate.valueOf() if (generateDateValue !== generateDate) { this.triggerChangeDate(dayjsGenerateDate) stateValue.generateDate = generateDateValue } return stateValue } @bind private getMultiSelectedState (value: Dayjs): Pick { const { selectedDate } = this.state const { end, start } = selectedDate const valueUnix: number = value.valueOf() const state: Pick = { selectedDate } if (end) { state.selectedDate = this.getSelectedDate(valueUnix, 0) } else { state.selectedDate.end = Math.max(valueUnix, +start) state.selectedDate.start = Math.min(valueUnix, +start) } return state } private getSelectedDate (start: number, end?: number): Calendar.SelectedDate { const stateValue: Calendar.SelectedDate = { start, end: start } if (typeof end !== 'undefined') { stateValue.end = end } return stateValue } private getInitializeState ( currentDate: Calendar.DateArg | Calendar.SelectedDate, isMultiSelect = false, collapse = false, time: string ): State { let end: number let start: number let generateDateValue: number if (isMultiSelect) { const { start: cStart, end: cEnd } = currentDate as Calendar.SelectedDate const dayjsStart = dayjs(cStart) start = dayjsStart.startOf('day').valueOf() generateDateValue = dayjsStart.startOf('month').valueOf() end = cEnd ? dayjs(cEnd) .startOf('day') .valueOf() : start } else { const dayjsStart = dayjs(currentDate as Calendar.DateArg) start = dayjsStart.startOf('day').valueOf() generateDateValue = collapse ? dayjsStart.valueOf() : dayjsStart.startOf('month').valueOf() end = start } return { pickerKey: 0, generateDate: generateDateValue, selectedDate: this.getSelectedDate(start, end), collapse, time, } } @bind private triggerChangeDate (value: Dayjs) { const { format } = this.props if (!_isFunction(this.props.onMonthChange)) return this.props.onMonthChange(value.format(format)) } @bind setMonth (vectorCount: number) { const { format } = this.props const { generateDate, collapse } = this.state const _generateDate: Dayjs = dayjs(generateDate).add(vectorCount, collapse ? 'week' : 'month') this.setState({ generateDate: _generateDate.valueOf() }) if (vectorCount && _isFunction(this.props.onMonthChange)) { this.props.onMonthChange(_generateDate.format(format)) } } @bind handleClickPreMonth (isMinMonth?: boolean): void { if (isMinMonth === true) { return } this.setMonth(-1) if (_isFunction(this.props.onClickPreMonth)) { this.props.onClickPreMonth() } } @bind handleClickNextMonth (isMaxMonth?: boolean): void { if (isMaxMonth === true) { return } this.setMonth(1) if (_isFunction(this.props.onClickNextMonth)) { this.props.onClickNextMonth() } } // picker 选择时间改变时触发 @bind handleSelectDate (e: BaseEvent & { detail: { value: string } }) { const { value } = e.detail const _generateDate: Dayjs = dayjs(value) let _generateDateValue: number = _generateDate.valueOf() // if(this.state.collapse && dayjs(this.state.selectedDate.start).month() === _generateDate.month()) { // _generateDateValue = dayjs(this.state.selectedDate.start).valueOf(); // } if (this.state.generateDate === _generateDateValue) return this.triggerChangeDate(_generateDate) this.setState({ pickerKey: this.state.pickerKey + 1, generateDate: _generateDateValue }) } @bind handleCancel () { this.setState({ pickerKey: this.state.pickerKey - 1, }) } @bind handleSelectTime (value:string) { this.setState({ time: value, }, () => { this.handleSelectedDate() }) if (!_isFunction(this.props.onTimeChange)) return this.props.onTimeChange(value) } @bind handleDayClick (item: Calendar.Item) { const { isMultiSelect } = this.props const { isDisabled, value } = item if (isDisabled) return const dayjsDate: Dayjs = dayjs(value) let stateValue: Partial = {} if (isMultiSelect) { stateValue = this.getMultiSelectedState(dayjsDate) } else { stateValue = this.getSingleSelectdState(dayjsDate) } this.setState(stateValue as State, () => { this.handleSelectedDate() }) if (_isFunction(this.props.onDayClick)) { this.props.onDayClick({ value: item.value }) } } handleSelectedDate () { const selectDate = this.state.selectedDate const time = this.state.time; if (_isFunction(this.props.onSelectDate)) { const info: Calendar.SelectedDate = { start: dayjs(selectDate.start).format(this.props.format) } if (selectDate.end) { info.end = dayjs(selectDate.end).format(this.props.format) } this.props.onSelectDate({ value: info, time, }) } } @bind handleDayLongClick (item: Calendar.Item) { if (_isFunction(this.props.onDayLongClick)) { this.props.onDayLongClick({ value: item.value }) } } handleCollapse (collapse) { const { selectedDate, generateDate, time, } = this.state; const { isMultiSelect, } = this.props; // 选中日期不在当前月,则直接关闭 if(dayjs(generateDate).month() !== dayjs(selectedDate.start).month()) { return this.setState({ collapse, }) } // 选中日期在当前月,则重新 generateDate = selectedDate.start const stateValue: State = this.getInitializeState( selectedDate.start, isMultiSelect, collapse, time ) this.setState(stateValue) } startX: number; startY: number; handleTouchStart = e => { const { clientX, clientY } = e.touches[0] this.startX = clientX; this.startY = clientY; } handleTouchEnd = e => { console.log(this.props.collapsible) // 不可折叠 if(!this.props.collapsible) return; const { clientY } = e.changedTouches[0] if(clientY - this.startY > 40 ) { this.handleCollapse(false) } if(clientY - this.startY < -40) { this.handleCollapse(true) } } render () { const { generateDate, selectedDate, collapse, time, pickerKey } = this.state const { marks, format, minDate, maxDate, isSwiper, className, hideArrow, isVertical, monthFormat, selectedDates, hideTime, timeStep, collapseBtn, } = this.props as PropsWithDefaults return ( {collapseBtn && {collapse? '展开' :'收起'} } ) } }