zarm-mobile
Version:
UI for react.js
565 lines (489 loc) • 13.8 kB
JSX
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;