UNPKG

zarm-mobile

Version:
565 lines (489 loc) 13.8 kB
import React, { Component } from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import moment from 'moment'; import ColumnGroup from './ColumnGroup'; import { formatFn } from './utils'; import defaultLocale from './locale/zh_CN'; import { Popup } from '../../components'; const DATETIME = 'datetime'; const DATE = 'date'; const TIME = 'time'; const MONTH = 'month'; const YEAR = 'year'; // 获取当月天数 function getDaysInMonth(now) { return now.clone().endOf('month').date(); } // 补齐格式 function pad(n) { return n < 10 ? `0${n}` : `${n}`; } // 阻止选择器区域的默认事件 function stopClick(e) { e.stopPropagation(); } // 转成moment格式 function getGregorianCalendar(arg) { return moment(arg); } class DatePicker extends Component { constructor(props) { super(props); const date = props.value && this.isExtendMoment(props.value); const defaultDate = props.defaultValue && this.isExtendMoment(props.defaultValue); const display = props.wheelDefaultValue && this.isExtendMoment(props.wheelDefaultValue); this.initDate = props.value && this.isExtendMoment(props.value); this.state = { visible: props.visible || false, date: date || defaultDate, display, }; } componentWillReceiveProps(nextProps) { const date = nextProps.value && this.isExtendMoment(nextProps.value); const defaultDate = nextProps.defaultValue && this.isExtendMoment(nextProps.defaultValue); this.setState({ date: date || defaultDate, }); } // 点击遮罩层 onMaskClick() { const { onMaskClick } = this.props; this.onCancel(); onMaskClick && onMaskClick(); } // 点击取消 onCancel() { const { onCancel } = this.props; this.toggle(); this.setState({ date: this.initDate, }); onCancel && onCancel(); } // 点击确定 onOk() { const { onOk } = this.props; const value = this.getDate(); this.setState({ date: value, }); this.initDate = value; this.toggle(); onOk && onOk(formatFn(this, value)); } onValueChange(values, index) { const value = parseInt(values[index], 10); const props = this.props; const { mode } = props; let newValue = this.getDate().clone(); if (mode === DATETIME || mode === DATE || mode === YEAR || mode === MONTH) { switch (index) { case 0: newValue.year(value); break; case 1: newValue.month(value); break; case 2: newValue.date(value); break; case 3: newValue.hour(value); break; case 4: newValue.minute(value); break; default: break; } } else { switch (index) { case 0: newValue.hour(value); break; case 1: newValue.minute(value); break; default: break; } } newValue = this.clipDate(newValue); if (!('date' in props)) { this.setState({ date: newValue, }); } props.onChange(formatFn(this, newValue)); } getDefaultMinDate() { if (!this.defaultMinDate) { this.defaultMinDate = getGregorianCalendar([2000, 0, 1, 0, 0, 0]); } return this.defaultMinDate; } getDefaultMaxDate() { if (!this.defaultMaxDate) { this.defaultMaxDate = getGregorianCalendar([2030, 1, 1, 23, 59, 59]); } return this.defaultMaxDate; } getDate() { return this.state.date || this.state.display || this.getMinDate() || moment(new Date()); } getMinYear() { return this.getMinDate().year(); } getMaxYear() { return this.getMaxDate().year(); } getMinMonth() { return this.getMinDate().month(); } getMaxMonth() { return this.getMaxDate().month(); } getMinDay() { return this.getMinDate().date(); } getMaxDay() { return this.getMaxDate().date(); } getMinHour() { return this.getMinDate().hour(); } getMaxHour() { return this.getMaxDate().hour(); } getMinMinute() { return this.getMinDate().minute(); } getMaxMinute() { return this.getMaxDate().minute(); } getMinDate() { const minDate = this.isExtendMoment(this.props.min); return minDate || this.getDefaultMinDate(); } getMaxDate() { const maxDate = this.isExtendMoment(this.props.max); return maxDate || this.getDefaultMaxDate(); } getDateData() { const { locale, formatMonth, formatDay, mode } = this.props; const date = this.getDate(); const selYear = date.year(); const selMonth = date.month(); const minDateYear = this.getMinYear(); const maxDateYear = this.getMaxYear(); const minDateMonth = this.getMinMonth(); const maxDateMonth = this.getMaxMonth(); const minDateDay = this.getMinDay(); const maxDateDay = this.getMaxDay(); const years = []; for (let i = minDateYear; i <= maxDateYear; i += 1) { years.push({ value: `${i}`, label: `${i + locale.year}`, }); } const yearCol = { key: 'year', props: { children: years } }; if (mode === YEAR) { return [yearCol]; } const months = []; let minMonth = 0; let maxMonth = 11; if (minDateYear === selYear) { minMonth = minDateMonth; } if (maxDateYear === selYear) { maxMonth = maxDateMonth; } for (let i = minMonth; i <= maxMonth; i += 1) { const label = formatMonth ? formatMonth(i, date) : `${i + 1 + locale.month}`; months.push({ value: `${i}`, label, }); } const monthCol = { key: 'month', props: { children: months } }; if (mode === MONTH) { return [yearCol, monthCol]; } const days = []; let minDay = 1; let maxDay = getDaysInMonth(date); if (minDateYear === selYear && minDateMonth === selMonth) { minDay = minDateDay; } if (maxDateYear === selYear && maxDateMonth === selMonth) { maxDay = maxDateDay; } for (let i = minDay; i <= maxDay; i += 1) { const label = formatDay ? formatDay(i, date) : `${i + locale.day}`; days.push({ value: `${i}`, label, }); } return [ yearCol, monthCol, { key: 'day', props: { children: days } }, ]; } getTimeData() { let minHour = 0; let maxHour = 23; let minMinute = 0; let maxMinute = 59; const { mode, locale, minuteStep } = this.props; const date = this.getDate(); const minDateMinute = this.getMinMinute(); const maxDateMinute = this.getMaxMinute(); const minDateHour = this.getMinHour(); const maxDateHour = this.getMaxHour(); const hour = date.hour(); if (mode === DATETIME) { const year = date.year(); const month = date.month(); const day = date.date(); const minDateYear = this.getMinYear(); const maxDateYear = this.getMaxYear(); const minDateMonth = this.getMinMonth(); const maxDateMonth = this.getMaxMonth(); const minDateDay = this.getMinDay(); const maxDateDay = this.getMaxDay(); if (minDateYear === year && minDateMonth === month && minDateDay === day) { minHour = minDateHour; if (minDateHour === hour) { minMinute = minDateMinute; } } if (maxDateYear === year && maxDateMonth === month && maxDateDay === day) { maxHour = maxDateHour; if (maxDateHour === hour) { maxMinute = maxDateMinute; } } } else { minHour = minDateHour; if (minDateHour === hour) { minMinute = minDateMinute; } maxHour = maxDateHour; if (maxDateHour === hour) { maxMinute = maxDateMinute; } } const hours = []; for (let i = minHour; i <= maxHour; i += 1) { hours.push({ value: `${i}`, label: locale.hour ? `${i + locale.hour}` : pad(i), }); } const minutes = []; for (let i = minMinute; i <= maxMinute; i += minuteStep) { minutes.push({ value: `${i}`, label: locale.minute ? `${i + locale.minute}` : pad(i), }); } return [ { key: 'hours', props: { children: hours } }, { key: 'minutes', props: { children: minutes } }, ]; } // 获取 getValueCols() { const { mode } = this.props; const date = this.getDate(); let cols = []; let value = []; if (mode === YEAR) { return { cols: this.getDateData(), value: [`${date.year()}`], }; } if (mode === MONTH) { return { cols: this.getDateData(), value: [`${date.year()}`, `${date.month()}`], }; } if (mode === DATETIME || mode === DATE) { cols = this.getDateData(); value = [`${date.year()}`, `${date.month()}`, `${date.date()}`]; } if (mode === DATETIME || mode === TIME) { cols = cols.concat(this.getTimeData()); value = value.concat([`${date.hour()}`, `${date.minute()}`]); } return { value, cols, }; } clipDate(date) { const { mode } = this.props; const minDate = this.getMinDate(); const maxDate = this.getMaxDate(); if (mode === DATETIME) { if (date.isBefore(minDate)) { return minDate.clone(); } if (date.isAfter(maxDate)) { return maxDate.clone(); } } else if (mode === DATE) { if (date.isBefore(minDate, 'day')) { return minDate.clone(); } if (date.isAfter(maxDate, 'day')) { return maxDate.clone(); } } else { const maxHour = maxDate.hour(); const maxMinutes = maxDate.minute(); const minHour = minDate.hour(); const minMinutes = minDate.minute(); const hour = date.hour(); const minutes = date.minute(); if (hour < minHour || (hour === minHour && minutes < minMinutes)) { return minDate.clone(); } if (hour > maxHour || (hour === maxHour && minutes > maxMinutes)) { return maxDate.clone(); } } return date; } isExtendMoment(date) { const { mode } = this.props; if (date instanceof moment) { return date; } if (!date) { return ''; } if (mode === TIME) { // 如果传递参数不合法,默认转换为时:分 return moment(date).isValid() ? moment(date, 'YYYY-MM-DD HH:mm') : moment(date, 'HH:mm'); } return moment(date, 'YYYY-MM-DD HH:mm'); } // 切换显示状态 toggle() { this.setState({ visible: !this.state.visible, }); } close(key) { this.setState({ [`${key}`]: false, }); } handleClick() { this.props.onClick(); !this.props.disabled && this.toggle(); } render() { const { value, cols } = this.getValueCols(); const { prefixCls, className, disabled, cancelText, okText, title, placeholder, displayMember, valueMember } = this.props; const classes = classnames({ 'za-picker-container': true, 'za-picker-hidden': !this.state.visible, [className]: !!className, }); const inputCls = classnames({ 'za-picker-placeholder': !this.state.date, 'za-picker-disabled': !!disabled, }); return ( <div className="za-picker" onClick={() => this.handleClick()}> <div className={inputCls}> {this.state.date ? formatFn(this, this.state.date) : placeholder} </div> <div className={classes} onClick={e => stopClick(e)}> <Popup className="za-popup-inner" visible={this.state.visible} onMaskClick={() => this.close('visible')}> <div className="za-picker-wrapper"> <div className="za-picker-header"> <div className="za-picker-cancel" onClick={() => this.onCancel()}>{cancelText}</div> <div className="za-picker-title">{title}</div> <div className="za-picker-submit" onClick={() => this.onOk()}>{okText}</div> </div> <div className="za-picker-mask-top"> <div className="za-picker-mask-bottom"> <ColumnGroup className={className} prefixCls={prefixCls} disabled={disabled} displayMember={displayMember} valueMember={valueMember} selectedValue={value} onValueChange={(values, index) => this.onValueChange(values, index)}> {cols} </ColumnGroup> </div> </div> </div> </Popup> </div> </div> ); } } DatePicker.propTypes = { visible: PropTypes.bool, placeholder: PropTypes.string, title: PropTypes.string, cancelText: PropTypes.string, okText: PropTypes.string, mode: PropTypes.oneOf([YEAR, MONTH, DATE, TIME, DATETIME]), disabled: PropTypes.bool, value: PropTypes.oneOfType([ PropTypes.string, PropTypes.object, ]), defaultValue: PropTypes.oneOfType([ PropTypes.string, PropTypes.object, ]), onOk: PropTypes.func, onCancel: PropTypes.func, onMaskClick: PropTypes.func, minuteStep: PropTypes.number, prefixCls: PropTypes.string, }; DatePicker.defaultProps = { visible: false, placeholder: '请选择日期', title: '请选择日期', cancelText: '取消', okText: '确定', mode: DATE, disabled: false, value: '', defaultValue: '', onClick: () => {}, onChange: () => {}, onOk: () => {}, onCancel: () => {}, onMaskClick: () => {}, locale: defaultLocale, minuteStep: 1, prefixCls: 'za-picker', displayMember: 'value', valueMember: 'value', }; export default DatePicker;