253 lines
5.4 KiB
JavaScript
253 lines
5.4 KiB
JavaScript
import { isDate } from '../utils/validate/date';
|
|
import { getScrollTop } from '../utils/dom/scroll';
|
|
import {
|
|
t,
|
|
bem,
|
|
getNextDay,
|
|
compareDay,
|
|
compareMonth,
|
|
createComponent,
|
|
formatMonthTitle
|
|
} from './utils';
|
|
|
|
import Button from '../button';
|
|
import Month from './components/Month';
|
|
import Header from './components/Header';
|
|
|
|
export default createComponent({
|
|
props: {
|
|
title: String,
|
|
value: [Date, Array],
|
|
buttonText: String,
|
|
buttonDisabledText: String,
|
|
type: {
|
|
type: String,
|
|
default: 'single'
|
|
},
|
|
minDate: {
|
|
type: Date,
|
|
default: () => new Date(),
|
|
validator: isDate
|
|
},
|
|
maxDate: {
|
|
type: Date,
|
|
default() {
|
|
const now = new Date();
|
|
return new Date(now.getFullYear(), now.getMonth() + 6, now.getDate());
|
|
},
|
|
validator: isDate
|
|
}
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
currentMonth: this.minDate,
|
|
currentValue: this.getDefaultValue()
|
|
};
|
|
},
|
|
|
|
computed: {
|
|
months() {
|
|
const months = [];
|
|
const { minDate, maxDate } = this;
|
|
const cursor = new Date(minDate);
|
|
|
|
cursor.setDate(1);
|
|
|
|
do {
|
|
months.push({
|
|
date: new Date(cursor),
|
|
days: this.getDays(cursor),
|
|
title: formatMonthTitle(cursor)
|
|
});
|
|
|
|
cursor.setMonth(cursor.getMonth() + 1);
|
|
} while (compareMonth(cursor, maxDate) !== 1);
|
|
|
|
return months;
|
|
}
|
|
},
|
|
|
|
watch: {
|
|
value(val) {
|
|
this.currentValue = val;
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
getDefaultValue() {
|
|
const { type, value, minDate } = this;
|
|
|
|
if (type === 'single') {
|
|
return value || minDate;
|
|
}
|
|
|
|
if (type === 'range') {
|
|
const range = value || [];
|
|
return [range[0] || minDate, range[1] || getNextDay(minDate)];
|
|
}
|
|
},
|
|
|
|
getDayType(day) {
|
|
const { type, minDate, maxDate, currentValue } = this;
|
|
|
|
if (compareDay(day, minDate) < 0 || compareDay(day, maxDate) > 0) {
|
|
return 'disabled';
|
|
}
|
|
|
|
if (type === 'single') {
|
|
return compareDay(day, currentValue) === 0 ? 'selected' : '';
|
|
}
|
|
|
|
if (type === 'range') {
|
|
const [startDay, endDay] = this.currentValue;
|
|
|
|
if (!startDay) {
|
|
return;
|
|
}
|
|
|
|
const compareToStart = compareDay(day, startDay);
|
|
if (compareToStart === 0) {
|
|
return 'start';
|
|
}
|
|
|
|
if (!endDay) {
|
|
return;
|
|
}
|
|
|
|
const compareToEnd = compareDay(day, endDay);
|
|
if (compareToEnd === 0) {
|
|
return 'end';
|
|
}
|
|
|
|
if (compareToStart > 0 && compareToEnd < 0) {
|
|
return 'middle';
|
|
}
|
|
}
|
|
},
|
|
|
|
getDays(date) {
|
|
const days = [];
|
|
const placeholderCount = date.getDay() === 0 ? 6 : date.getDay() - 1;
|
|
|
|
for (let i = 1; i <= placeholderCount; i++) {
|
|
days.push({ day: '' });
|
|
}
|
|
|
|
const cursor = new Date(date);
|
|
|
|
do {
|
|
days.push({
|
|
day: cursor.getDate(),
|
|
date: new Date(cursor),
|
|
type: this.getDayType(cursor)
|
|
});
|
|
|
|
cursor.setDate(cursor.getDate() + 1);
|
|
} while (compareMonth(cursor, date) === 0);
|
|
|
|
return days;
|
|
},
|
|
|
|
onScroll() {
|
|
const scrollTop = getScrollTop(this.$refs.body);
|
|
const monthsHeight = this.$refs.month.map(item => item.height);
|
|
let height = 0;
|
|
|
|
for (let i = 0; i < this.months.length; i++) {
|
|
if (scrollTop < height) {
|
|
this.currentMonth = this.months[i - 1].date;
|
|
return;
|
|
}
|
|
|
|
height += monthsHeight[i];
|
|
}
|
|
},
|
|
|
|
onClickDay(item) {
|
|
const { date } = item;
|
|
|
|
if (this.type === 'single') {
|
|
this.$emit('input', date);
|
|
this.$emit('select', date);
|
|
}
|
|
|
|
if (this.type === 'range') {
|
|
const [startDay, endDay] = this.currentValue;
|
|
|
|
if (startDay && !endDay) {
|
|
const compareToStart = compareDay(date, startDay);
|
|
|
|
if (compareToStart === 1) {
|
|
this.$emit('input', [startDay, date]);
|
|
}
|
|
|
|
if (compareToStart === -1) {
|
|
this.$emit('input', [date, null]);
|
|
}
|
|
} else {
|
|
this.$emit('input', [date, null]);
|
|
}
|
|
}
|
|
},
|
|
|
|
onConfirmRange() {
|
|
this.$emit('input', this.currentValue);
|
|
this.$emit('select', this.currentValue);
|
|
},
|
|
|
|
genMonth(month, index) {
|
|
return (
|
|
<Month
|
|
ref="month"
|
|
refInFor
|
|
days={month.days}
|
|
date={month.date}
|
|
title={index !== 0 ? month.title : ''}
|
|
onClick={this.onClickDay}
|
|
/>
|
|
);
|
|
},
|
|
|
|
genFooter() {
|
|
if (this.type === 'range') {
|
|
const disabled = !this.currentValue[1];
|
|
const text = disabled ? this.buttonDisabledText : this.buttonText;
|
|
|
|
return (
|
|
<div class={bem('footer')}>
|
|
<Button
|
|
round
|
|
block
|
|
type="danger"
|
|
disabled={disabled}
|
|
class={bem('confirm')}
|
|
onClick={this.onConfirmRange}
|
|
>
|
|
{text || t('confirm')}
|
|
</Button>
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
},
|
|
|
|
render() {
|
|
return (
|
|
<div class={bem()}>
|
|
<Header
|
|
title={this.title}
|
|
currentMonth={this.currentMonth}
|
|
scopedSlots={{
|
|
title: () => this.slots('title')
|
|
}}
|
|
/>
|
|
<div ref="body" class={bem('body')} onScroll={this.onScroll}>
|
|
{this.months.map(this.genMonth)}
|
|
</div>
|
|
{this.genFooter()}
|
|
</div>
|
|
);
|
|
}
|
|
});
|