123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680 |
- <template>
- <transition name="el-zoom-in-top" @after-leave="$emit('dodestroy')">
- <div
- v-show="visible"
- class="el-picker-panel el-date-range-picker el-popper"
- :class="[{
- 'has-sidebar': $slots.sidebar || shortcuts,
- 'has-time': showTime
- }, popperClass]">
- <div class="el-picker-panel__body-wrapper">
- <slot name="sidebar" class="el-picker-panel__sidebar"></slot>
- <div class="el-picker-panel__sidebar" v-if="shortcuts">
- <button
- type="button"
- class="el-picker-panel__shortcut"
- v-for="(shortcut, key) in shortcuts"
- :key="key"
- @click="handleShortcutClick(shortcut)">{{shortcut.text}}</button>
- </div>
- <div class="el-picker-panel__body">
- <div class="el-date-range-picker__time-header" v-if="showTime">
- <span class="el-date-range-picker__editors-wrap">
- <span class="el-date-range-picker__time-picker-wrap">
- <el-input
- size="small"
- :disabled="rangeState.selecting"
- ref="minInput"
- :placeholder="t('el.datepicker.startDate')"
- class="el-date-range-picker__editor"
- :value="minVisibleDate"
- @input="val => handleDateInput(val, 'min')"
- @change="val => handleDateChange(val, 'min')" />
- </span>
- <span class="el-date-range-picker__time-picker-wrap" v-clickoutside="handleMinTimeClose">
- <el-input
- size="small"
- class="el-date-range-picker__editor"
- :disabled="rangeState.selecting"
- :placeholder="t('el.datepicker.startTime')"
- :value="minVisibleTime"
- @focus="minTimePickerVisible = true"
- @input="val => handleTimeInput(val, 'min')"
- @change="val => handleTimeChange(val, 'min')" />
- <time-picker
- ref="minTimePicker"
- @pick="handleMinTimePick"
- :time-arrow-control="arrowControl"
- :visible="minTimePickerVisible"
- @mounted="$refs.minTimePicker.format=timeFormat">
- </time-picker>
- </span>
- </span>
- <span class="el-icon-arrow-right"></span>
- <span class="el-date-range-picker__editors-wrap is-right">
- <span class="el-date-range-picker__time-picker-wrap">
- <el-input
- size="small"
- class="el-date-range-picker__editor"
- :disabled="rangeState.selecting"
- :placeholder="t('el.datepicker.endDate')"
- :value="maxVisibleDate"
- :readonly="!minDate"
- @input="val => handleDateInput(val, 'max')"
- @change="val => handleDateChange(val, 'max')" />
- </span>
- <span class="el-date-range-picker__time-picker-wrap" v-clickoutside="handleMaxTimeClose">
- <el-input
- size="small"
- class="el-date-range-picker__editor"
- :disabled="rangeState.selecting"
- :placeholder="t('el.datepicker.endTime')"
- :value="maxVisibleTime"
- :readonly="!minDate"
- @focus="minDate && (maxTimePickerVisible = true)"
- @input="val => handleTimeInput(val, 'max')"
- @change="val => handleTimeChange(val, 'max')" />
- <time-picker
- ref="maxTimePicker"
- @pick="handleMaxTimePick"
- :time-arrow-control="arrowControl"
- :visible="maxTimePickerVisible"
- @mounted="$refs.maxTimePicker.format=timeFormat">
- </time-picker>
- </span>
- </span>
- </div>
- <div class="el-picker-panel__content el-date-range-picker__content is-left">
- <div class="el-date-range-picker__header">
- <button
- type="button"
- @click="leftPrevYear"
- class="el-picker-panel__icon-btn el-icon-d-arrow-left"></button>
- <button
- type="button"
- @click="leftPrevMonth"
- class="el-picker-panel__icon-btn el-icon-arrow-left"></button>
- <button
- type="button"
- @click="leftNextYear"
- v-if="unlinkPanels"
- :disabled="!enableYearArrow"
- :class="{ 'is-disabled': !enableYearArrow }"
- class="el-picker-panel__icon-btn el-icon-d-arrow-right"></button>
- <button
- type="button"
- @click="leftNextMonth"
- v-if="unlinkPanels"
- :disabled="!enableMonthArrow"
- :class="{ 'is-disabled': !enableMonthArrow }"
- class="el-picker-panel__icon-btn el-icon-arrow-right"></button>
- <div>{{ leftLabel }}</div>
- </div>
- <date-table
- selection-mode="range"
- :date="leftDate"
- :default-value="defaultValue"
- :min-date="minDate"
- :max-date="maxDate"
- :range-state="rangeState"
- :disabled-date="disabledDate"
- :cell-class-name="cellClassName"
- @changerange="handleChangeRange"
- :first-day-of-week="firstDayOfWeek"
- @pick="handleRangePick">
- </date-table>
- </div>
- <div class="el-picker-panel__content el-date-range-picker__content is-right">
- <div class="el-date-range-picker__header">
- <button
- type="button"
- @click="rightPrevYear"
- v-if="unlinkPanels"
- :disabled="!enableYearArrow"
- :class="{ 'is-disabled': !enableYearArrow }"
- class="el-picker-panel__icon-btn el-icon-d-arrow-left"></button>
- <button
- type="button"
- @click="rightPrevMonth"
- v-if="unlinkPanels"
- :disabled="!enableMonthArrow"
- :class="{ 'is-disabled': !enableMonthArrow }"
- class="el-picker-panel__icon-btn el-icon-arrow-left"></button>
- <button
- type="button"
- @click="rightNextYear"
- class="el-picker-panel__icon-btn el-icon-d-arrow-right"></button>
- <button
- type="button"
- @click="rightNextMonth"
- class="el-picker-panel__icon-btn el-icon-arrow-right"></button>
- <div>{{ rightLabel }}</div>
- </div>
- <date-table
- selection-mode="range"
- :date="rightDate"
- :default-value="defaultValue"
- :min-date="minDate"
- :max-date="maxDate"
- :range-state="rangeState"
- :disabled-date="disabledDate"
- :cell-class-name="cellClassName"
- @changerange="handleChangeRange"
- :first-day-of-week="firstDayOfWeek"
- @pick="handleRangePick">
- </date-table>
- </div>
- </div>
- </div>
- <div class="el-picker-panel__footer" v-if="showTime">
- <el-button
- size="mini"
- type="text"
- class="el-picker-panel__link-btn"
- @click="handleClear">
- {{ t('el.datepicker.clear') }}
- </el-button>
- <el-button
- plain
- size="mini"
- class="el-picker-panel__link-btn"
- :disabled="btnDisabled"
- @click="handleConfirm(false)">
- {{ t('el.datepicker.confirm') }}
- </el-button>
- </div>
- </div>
- </transition>
- </template>
- <script type="text/babel">
- import {
- formatDate,
- parseDate,
- isDate,
- modifyDate,
- modifyTime,
- modifyWithTimeString,
- prevYear,
- nextYear,
- prevMonth,
- nextMonth,
- nextDate,
- extractDateFormat,
- extractTimeFormat
- } from 'element-ui/src/utils/date-util';
- import Clickoutside from 'element-ui/src/utils/clickoutside';
- import Locale from 'element-ui/src/mixins/locale';
- import TimePicker from './time';
- import DateTable from '../basic/date-table';
- import ElInput from 'element-ui/packages/input';
- import ElButton from 'element-ui/packages/button';
- const calcDefaultValue = (defaultValue) => {
- if (Array.isArray(defaultValue)) {
- return [new Date(defaultValue[0]), new Date(defaultValue[1])];
- } else if (defaultValue) {
- return [new Date(defaultValue), nextDate(new Date(defaultValue), 1)];
- } else {
- return [new Date(), nextDate(new Date(), 1)];
- }
- };
- export default {
- mixins: [Locale],
- directives: { Clickoutside },
- computed: {
- btnDisabled() {
- return !(this.minDate && this.maxDate && !this.selecting && this.isValidValue([this.minDate, this.maxDate]));
- },
- leftLabel() {
- return this.leftDate.getFullYear() + ' ' + this.t('el.datepicker.year') + ' ' + this.t(`el.datepicker.month${ this.leftDate.getMonth() + 1 }`);
- },
- rightLabel() {
- return this.rightDate.getFullYear() + ' ' + this.t('el.datepicker.year') + ' ' + this.t(`el.datepicker.month${ this.rightDate.getMonth() + 1 }`);
- },
- leftYear() {
- return this.leftDate.getFullYear();
- },
- leftMonth() {
- return this.leftDate.getMonth();
- },
- leftMonthDate() {
- return this.leftDate.getDate();
- },
- rightYear() {
- return this.rightDate.getFullYear();
- },
- rightMonth() {
- return this.rightDate.getMonth();
- },
- rightMonthDate() {
- return this.rightDate.getDate();
- },
- minVisibleDate() {
- if (this.dateUserInput.min !== null) return this.dateUserInput.min;
- if (this.minDate) return formatDate(this.minDate, this.dateFormat);
- return '';
- },
- maxVisibleDate() {
- if (this.dateUserInput.max !== null) return this.dateUserInput.max;
- if (this.maxDate || this.minDate) return formatDate(this.maxDate || this.minDate, this.dateFormat);
- return '';
- },
- minVisibleTime() {
- if (this.timeUserInput.min !== null) return this.timeUserInput.min;
- if (this.minDate) return formatDate(this.minDate, this.timeFormat);
- return '';
- },
- maxVisibleTime() {
- if (this.timeUserInput.max !== null) return this.timeUserInput.max;
- if (this.maxDate || this.minDate) return formatDate(this.maxDate || this.minDate, this.timeFormat);
- return '';
- },
- timeFormat() {
- if (this.format) {
- return extractTimeFormat(this.format);
- } else {
- return 'HH:mm:ss';
- }
- },
- dateFormat() {
- if (this.format) {
- return extractDateFormat(this.format);
- } else {
- return 'yyyy-MM-dd';
- }
- },
- enableMonthArrow() {
- const nextMonth = (this.leftMonth + 1) % 12;
- const yearOffset = this.leftMonth + 1 >= 12 ? 1 : 0;
- return this.unlinkPanels && new Date(this.leftYear + yearOffset, nextMonth) < new Date(this.rightYear, this.rightMonth);
- },
- enableYearArrow() {
- return this.unlinkPanels && this.rightYear * 12 + this.rightMonth - (this.leftYear * 12 + this.leftMonth + 1) >= 12;
- }
- },
- data() {
- return {
- popperClass: '',
- value: [],
- defaultValue: null,
- defaultTime: null,
- minDate: '',
- maxDate: '',
- leftDate: new Date(),
- rightDate: nextMonth(new Date()),
- rangeState: {
- endDate: null,
- selecting: false,
- row: null,
- column: null
- },
- showTime: false,
- shortcuts: '',
- visible: '',
- disabledDate: '',
- cellClassName: '',
- firstDayOfWeek: 7,
- minTimePickerVisible: false,
- maxTimePickerVisible: false,
- format: '',
- arrowControl: false,
- unlinkPanels: false,
- dateUserInput: {
- min: null,
- max: null
- },
- timeUserInput: {
- min: null,
- max: null
- }
- };
- },
- watch: {
- minDate(val) {
- this.dateUserInput.min = null;
- this.timeUserInput.min = null;
- this.$nextTick(() => {
- if (this.$refs.maxTimePicker && this.maxDate && this.maxDate < this.minDate) {
- const format = 'HH:mm:ss';
- this.$refs.maxTimePicker.selectableRange = [
- [
- parseDate(formatDate(this.minDate, format), format),
- parseDate('23:59:59', format)
- ]
- ];
- }
- });
- if (val && this.$refs.minTimePicker) {
- this.$refs.minTimePicker.date = val;
- this.$refs.minTimePicker.value = val;
- }
- },
- maxDate(val) {
- this.dateUserInput.max = null;
- this.timeUserInput.max = null;
- if (val && this.$refs.maxTimePicker) {
- this.$refs.maxTimePicker.date = val;
- this.$refs.maxTimePicker.value = val;
- }
- },
- minTimePickerVisible(val) {
- if (val) {
- this.$nextTick(() => {
- this.$refs.minTimePicker.date = this.minDate;
- this.$refs.minTimePicker.value = this.minDate;
- this.$refs.minTimePicker.adjustSpinners();
- });
- }
- },
- maxTimePickerVisible(val) {
- if (val) {
- this.$nextTick(() => {
- this.$refs.maxTimePicker.date = this.maxDate;
- this.$refs.maxTimePicker.value = this.maxDate;
- this.$refs.maxTimePicker.adjustSpinners();
- });
- }
- },
- value(newVal) {
- if (!newVal) {
- this.minDate = null;
- this.maxDate = null;
- } else if (Array.isArray(newVal)) {
- this.minDate = isDate(newVal[0]) ? new Date(newVal[0]) : null;
- this.maxDate = isDate(newVal[1]) ? new Date(newVal[1]) : null;
- if (this.minDate) {
- this.leftDate = this.minDate;
- if (this.unlinkPanels && this.maxDate) {
- const minDateYear = this.minDate.getFullYear();
- const minDateMonth = this.minDate.getMonth();
- const maxDateYear = this.maxDate.getFullYear();
- const maxDateMonth = this.maxDate.getMonth();
- this.rightDate = minDateYear === maxDateYear && minDateMonth === maxDateMonth
- ? nextMonth(this.maxDate)
- : this.maxDate;
- } else {
- this.rightDate = nextMonth(this.leftDate);
- }
- } else {
- this.leftDate = calcDefaultValue(this.defaultValue)[0];
- this.rightDate = nextMonth(this.leftDate);
- }
- }
- },
- defaultValue(val) {
- if (!Array.isArray(this.value)) {
- const [left, right] = calcDefaultValue(val);
- this.leftDate = left;
- this.rightDate = val && val[1] && this.unlinkPanels
- ? right
- : nextMonth(this.leftDate);
- }
- }
- },
- methods: {
- handleClear() {
- this.minDate = null;
- this.maxDate = null;
- this.leftDate = calcDefaultValue(this.defaultValue)[0];
- this.rightDate = nextMonth(this.leftDate);
- this.$emit('pick', null);
- },
- handleChangeRange(val) {
- this.minDate = val.minDate;
- this.maxDate = val.maxDate;
- this.rangeState = val.rangeState;
- },
- handleDateInput(value, type) {
- this.dateUserInput[type] = value;
- if (value.length !== this.dateFormat.length) return;
- const parsedValue = parseDate(value, this.dateFormat);
- if (parsedValue) {
- if (typeof this.disabledDate === 'function' &&
- this.disabledDate(new Date(parsedValue))) {
- return;
- }
- if (type === 'min') {
- this.minDate = modifyDate(this.minDate || new Date(), parsedValue.getFullYear(), parsedValue.getMonth(), parsedValue.getDate());
- this.leftDate = new Date(parsedValue);
- if (!this.unlinkPanels) {
- this.rightDate = nextMonth(this.leftDate);
- }
- } else {
- this.maxDate = modifyDate(this.maxDate || new Date(), parsedValue.getFullYear(), parsedValue.getMonth(), parsedValue.getDate());
- this.rightDate = new Date(parsedValue);
- if (!this.unlinkPanels) {
- this.leftDate = prevMonth(parsedValue);
- }
- }
- }
- },
- handleDateChange(value, type) {
- const parsedValue = parseDate(value, this.dateFormat);
- if (parsedValue) {
- if (type === 'min') {
- this.minDate = modifyDate(this.minDate, parsedValue.getFullYear(), parsedValue.getMonth(), parsedValue.getDate());
- if (this.minDate > this.maxDate) {
- this.maxDate = this.minDate;
- }
- } else {
- this.maxDate = modifyDate(this.maxDate, parsedValue.getFullYear(), parsedValue.getMonth(), parsedValue.getDate());
- if (this.maxDate < this.minDate) {
- this.minDate = this.maxDate;
- }
- }
- }
- },
- handleTimeInput(value, type) {
- this.timeUserInput[type] = value;
- if (value.length !== this.timeFormat.length) return;
- const parsedValue = parseDate(value, this.timeFormat);
- if (parsedValue) {
- if (type === 'min') {
- this.minDate = modifyTime(this.minDate, parsedValue.getHours(), parsedValue.getMinutes(), parsedValue.getSeconds());
- this.$nextTick(_ => this.$refs.minTimePicker.adjustSpinners());
- } else {
- this.maxDate = modifyTime(this.maxDate, parsedValue.getHours(), parsedValue.getMinutes(), parsedValue.getSeconds());
- this.$nextTick(_ => this.$refs.maxTimePicker.adjustSpinners());
- }
- }
- },
- handleTimeChange(value, type) {
- const parsedValue = parseDate(value, this.timeFormat);
- if (parsedValue) {
- if (type === 'min') {
- this.minDate = modifyTime(this.minDate, parsedValue.getHours(), parsedValue.getMinutes(), parsedValue.getSeconds());
- if (this.minDate > this.maxDate) {
- this.maxDate = this.minDate;
- }
- this.$refs.minTimePicker.value = this.minDate;
- this.minTimePickerVisible = false;
- } else {
- this.maxDate = modifyTime(this.maxDate, parsedValue.getHours(), parsedValue.getMinutes(), parsedValue.getSeconds());
- if (this.maxDate < this.minDate) {
- this.minDate = this.maxDate;
- }
- this.$refs.maxTimePicker.value = this.minDate;
- this.maxTimePickerVisible = false;
- }
- }
- },
- handleRangePick(val, close = true) {
- const defaultTime = this.defaultTime || [];
- const minDate = modifyWithTimeString(val.minDate, defaultTime[0]);
- const maxDate = modifyWithTimeString(val.maxDate, defaultTime[1]);
- if (this.maxDate === maxDate && this.minDate === minDate) {
- return;
- }
- this.onPick && this.onPick(val);
- this.maxDate = maxDate;
- this.minDate = minDate;
- // workaround for https://github.com/ElemeFE/element/issues/7539, should remove this block when we don't have to care about Chromium 55 - 57
- setTimeout(() => {
- this.maxDate = maxDate;
- this.minDate = minDate;
- }, 10);
- if (!close || this.showTime) return;
- this.handleConfirm();
- },
- handleShortcutClick(shortcut) {
- if (shortcut.onClick) {
- shortcut.onClick(this);
- }
- },
- handleMinTimePick(value, visible, first) {
- this.minDate = this.minDate || new Date();
- if (value) {
- this.minDate = modifyTime(this.minDate, value.getHours(), value.getMinutes(), value.getSeconds());
- }
- if (!first) {
- this.minTimePickerVisible = visible;
- }
- if (!this.maxDate || this.maxDate && this.maxDate.getTime() < this.minDate.getTime()) {
- this.maxDate = new Date(this.minDate);
- }
- },
- handleMinTimeClose() {
- this.minTimePickerVisible = false;
- },
- handleMaxTimePick(value, visible, first) {
- if (this.maxDate && value) {
- this.maxDate = modifyTime(this.maxDate, value.getHours(), value.getMinutes(), value.getSeconds());
- }
- if (!first) {
- this.maxTimePickerVisible = visible;
- }
- if (this.maxDate && this.minDate && this.minDate.getTime() > this.maxDate.getTime()) {
- this.minDate = new Date(this.maxDate);
- }
- },
- handleMaxTimeClose() {
- this.maxTimePickerVisible = false;
- },
- // leftPrev*, rightNext* need to take care of `unlinkPanels`
- leftPrevYear() {
- this.leftDate = prevYear(this.leftDate);
- if (!this.unlinkPanels) {
- this.rightDate = nextMonth(this.leftDate);
- }
- },
- leftPrevMonth() {
- this.leftDate = prevMonth(this.leftDate);
- if (!this.unlinkPanels) {
- this.rightDate = nextMonth(this.leftDate);
- }
- },
- rightNextYear() {
- if (!this.unlinkPanels) {
- this.leftDate = nextYear(this.leftDate);
- this.rightDate = nextMonth(this.leftDate);
- } else {
- this.rightDate = nextYear(this.rightDate);
- }
- },
- rightNextMonth() {
- if (!this.unlinkPanels) {
- this.leftDate = nextMonth(this.leftDate);
- this.rightDate = nextMonth(this.leftDate);
- } else {
- this.rightDate = nextMonth(this.rightDate);
- }
- },
- // leftNext*, rightPrev* are called when `unlinkPanels` is true
- leftNextYear() {
- this.leftDate = nextYear(this.leftDate);
- },
- leftNextMonth() {
- this.leftDate = nextMonth(this.leftDate);
- },
- rightPrevYear() {
- this.rightDate = prevYear(this.rightDate);
- },
- rightPrevMonth() {
- this.rightDate = prevMonth(this.rightDate);
- },
- handleConfirm(visible = false) {
- if (this.isValidValue([this.minDate, this.maxDate])) {
- this.$emit('pick', [this.minDate, this.maxDate], visible);
- }
- },
- isValidValue(value) {
- return Array.isArray(value) &&
- value && value[0] && value[1] &&
- isDate(value[0]) && isDate(value[1]) &&
- value[0].getTime() <= value[1].getTime() && (
- typeof this.disabledDate === 'function'
- ? !this.disabledDate(value[0]) && !this.disabledDate(value[1])
- : true
- );
- },
- resetView() {
- // NOTE: this is a hack to reset {min, max}Date on picker open.
- // TODO: correct way of doing so is to refactor {min, max}Date to be dependent on value and internal selection state
- // an alternative would be resetView whenever picker becomes visible, should also investigate date-panel's resetView
- if (this.minDate && this.maxDate == null) this.rangeState.selecting = false;
- this.minDate = this.value && isDate(this.value[0]) ? new Date(this.value[0]) : null;
- this.maxDate = this.value && isDate(this.value[0]) ? new Date(this.value[1]) : null;
- }
- },
- components: { TimePicker, DateTable, ElInput, ElButton }
- };
- </script>
|