123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- <template>
- <div class="el-calendar">
- <div class="el-calendar__header">
- <div class="el-calendar__title">
- {{ i18nDate }}
- </div>
- <div
- class="el-calendar__button-group"
- v-if="validatedRange.length === 0">
- <el-button-group>
- <el-button
- type="plain"
- size="mini"
- @click="selectDate('prev-month')">
- {{ t('el.datepicker.prevMonth') }}
- </el-button>
- <el-button
- type="plain"
- size="mini"
- @click="selectDate('today')">
- {{ t('el.datepicker.today') }}
- </el-button>
- <el-button
- type="plain"
- size="mini"
- @click="selectDate('next-month')">
- {{ t('el.datepicker.nextMonth') }}
- </el-button>
- </el-button-group>
- </div>
- </div>
- <div
- class="el-calendar__body"
- v-if="validatedRange.length === 0"
- key="no-range">
- <date-table
- :date="date"
- :selected-day="realSelectedDay"
- :first-day-of-week="realFirstDayOfWeek"
- @pick="pickDay" />
- </div>
- <div
- v-else
- class="el-calendar__body"
- key="has-range">
- <date-table
- v-for="(range, index) in validatedRange"
- :key="index"
- :date="range[0]"
- :selected-day="realSelectedDay"
- :range="range"
- :hide-header="index !== 0"
- :first-day-of-week="realFirstDayOfWeek"
- @pick="pickDay" />
- </div>
- </div>
- </template>
- <script>
- import Locale from 'element-ui/src/mixins/locale';
- import fecha from 'element-ui/src/utils/date';
- import ElButton from 'element-ui/packages/button';
- import ElButtonGroup from 'element-ui/packages/button-group';
- import DateTable from './date-table';
- import { validateRangeInOneMonth } from 'element-ui/src/utils/date-util';
- const validTypes = ['prev-month', 'today', 'next-month'];
- const weekDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
- const oneDay = 86400000;
- export default {
- name: 'ElCalendar',
- mixins: [Locale],
- components: {
- DateTable,
- ElButton,
- ElButtonGroup
- },
- props: {
- value: [Date, String, Number],
- range: {
- type: Array,
- validator(range) {
- if (Array.isArray(range)) {
- return range.length === 2 && range.every(
- item => typeof item === 'string' ||
- typeof item === 'number' ||
- item instanceof Date);
- } else {
- return true;
- }
- }
- },
- firstDayOfWeek: {
- type: Number,
- default: 1
- }
- },
- provide() {
- return {
- elCalendar: this
- };
- },
- methods: {
- pickDay(day) {
- this.realSelectedDay = day;
- },
- selectDate(type) {
- if (validTypes.indexOf(type) === -1) {
- throw new Error(`invalid type ${type}`);
- }
- let day = '';
- if (type === 'prev-month') {
- day = `${this.prevMonthDatePrefix}-01`;
- } else if (type === 'next-month') {
- day = `${this.nextMonthDatePrefix}-01`;
- } else {
- day = this.formatedToday;
- }
- if (day === this.formatedDate) return;
- this.pickDay(day);
- },
- toDate(val) {
- if (!val) {
- throw new Error('invalid val');
- }
- return val instanceof Date ? val : new Date(val);
- },
- rangeValidator(date, isStart) {
- const firstDayOfWeek = this.realFirstDayOfWeek;
- const expected = isStart ? firstDayOfWeek : (firstDayOfWeek === 0 ? 6 : firstDayOfWeek - 1);
- const message = `${isStart ? 'start' : 'end'} of range should be ${weekDays[expected]}.`;
- if (date.getDay() !== expected) {
- console.warn('[ElementCalendar]', message, 'Invalid range will be ignored.');
- return false;
- }
- return true;
- }
- },
- computed: {
- prevMonthDatePrefix() {
- const temp = new Date(this.date.getTime());
- temp.setDate(0);
- return fecha.format(temp, 'yyyy-MM');
- },
- curMonthDatePrefix() {
- return fecha.format(this.date, 'yyyy-MM');
- },
- nextMonthDatePrefix() {
- const temp = new Date(this.date.getFullYear(), this.date.getMonth() + 1, 1);
- return fecha.format(temp, 'yyyy-MM');
- },
- formatedDate() {
- return fecha.format(this.date, 'yyyy-MM-dd');
- },
- i18nDate() {
- const year = this.date.getFullYear();
- const month = this.date.getMonth() + 1;
- return `${year} ${this.t('el.datepicker.year')} ${this.t('el.datepicker.month' + month)}`;
- },
- formatedToday() {
- return fecha.format(this.now, 'yyyy-MM-dd');
- },
- realSelectedDay: {
- get() {
- if (!this.value) return this.selectedDay;
- return this.formatedDate;
- },
- set(val) {
- this.selectedDay = val;
- const date = new Date(val);
- this.$emit('input', date);
- }
- },
- date() {
- if (!this.value) {
- if (this.realSelectedDay) {
- const d = this.selectedDay.split('-');
- return new Date(d[0], d[1] - 1, d[2]);
- } else if (this.validatedRange.length) {
- return this.validatedRange[0][0];
- }
- return this.now;
- } else {
- return this.toDate(this.value);
- }
- },
- // if range is valid, we get a two-digit array
- validatedRange() {
- let range = this.range;
- if (!range) return [];
- range = range.reduce((prev, val, index) => {
- const date = this.toDate(val);
- if (this.rangeValidator(date, index === 0)) {
- prev = prev.concat(date);
- }
- return prev;
- }, []);
- if (range.length === 2) {
- const [start, end] = range;
- if (start > end) {
- console.warn('[ElementCalendar]end time should be greater than start time');
- return [];
- }
- // start time and end time in one month
- if (validateRangeInOneMonth(start, end)) {
- return [
- [start, end]
- ];
- }
- const data = [];
- let startDay = new Date(start.getFullYear(), start.getMonth() + 1, 1);
- const lastDay = this.toDate(startDay.getTime() - oneDay);
- if (!validateRangeInOneMonth(startDay, end)) {
- console.warn('[ElementCalendar]start time and end time interval must not exceed two months');
- return [];
- }
- // 第一个月的时间范围
- data.push([
- start,
- lastDay
- ]);
- // 下一月的时间范围,需要计算一下该月的第一个周起始日
- const firstDayOfWeek = this.realFirstDayOfWeek;
- const nextMontFirstDay = startDay.getDay();
- let interval = 0;
- if (nextMontFirstDay !== firstDayOfWeek) {
- if (firstDayOfWeek === 0) {
- interval = 7 - nextMontFirstDay;
- } else {
- interval = firstDayOfWeek - nextMontFirstDay;
- interval = interval > 0 ? interval : 7 + interval;
- }
- }
- startDay = this.toDate(startDay.getTime() + interval * oneDay);
- if (startDay.getDate() < end.getDate()) {
- data.push([
- startDay,
- end
- ]);
- }
- return data;
- }
- return [];
- },
- realFirstDayOfWeek() {
- if (this.firstDayOfWeek < 1 || this.firstDayOfWeek > 6) {
- return 0;
- }
- return Math.floor(this.firstDayOfWeek);
- }
- },
- data() {
- return {
- selectedDay: '',
- now: new Date()
- };
- }
- };
- </script>
|