123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609 |
- <template>
- <transition name="el-zoom-in-top" @after-enter="handleEnter" @after-leave="handleLeave">
- <div
- v-show="visible"
- class="el-picker-panel el-date-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-picker__time-header" v-if="showTime">
- <span class="el-date-picker__editor-wrap">
- <el-input
- :placeholder="t('el.datepicker.selectDate')"
- :value="visibleDate"
- size="small"
- @input="val => userInputDate = val"
- @change="handleVisibleDateChange" />
- </span>
- <span class="el-date-picker__editor-wrap" v-clickoutside="handleTimePickClose">
- <el-input
- ref="input"
- @focus="timePickerVisible = true"
- :placeholder="t('el.datepicker.selectTime')"
- :value="visibleTime"
- size="small"
- @input="val => userInputTime = val"
- @change="handleVisibleTimeChange" />
- <time-picker
- ref="timepicker"
- :time-arrow-control="arrowControl"
- @pick="handleTimePick"
- :visible="timePickerVisible"
- @mounted="proxyTimePickerDataProperties">
- </time-picker>
- </span>
- </div>
- <div
- class="el-date-picker__header"
- :class="{ 'el-date-picker__header--bordered': currentView === 'year' || currentView === 'month' }"
- v-show="currentView !== 'time'">
- <button
- type="button"
- @click="prevYear"
- :aria-label="t(`el.datepicker.prevYear`)"
- class="el-picker-panel__icon-btn el-date-picker__prev-btn el-icon-d-arrow-left">
- </button>
- <button
- type="button"
- @click="prevMonth"
- v-show="currentView === 'date'"
- :aria-label="t(`el.datepicker.prevMonth`)"
- class="el-picker-panel__icon-btn el-date-picker__prev-btn el-icon-arrow-left">
- </button>
- <span
- @click="showYearPicker"
- role="button"
- class="el-date-picker__header-label">{{ yearLabel }}</span>
- <span
- @click="showMonthPicker"
- v-show="currentView === 'date'"
- role="button"
- class="el-date-picker__header-label"
- :class="{ active: currentView === 'month' }">{{t(`el.datepicker.month${ month + 1 }`)}}</span>
- <button
- type="button"
- @click="nextYear"
- :aria-label="t(`el.datepicker.nextYear`)"
- class="el-picker-panel__icon-btn el-date-picker__next-btn el-icon-d-arrow-right">
- </button>
- <button
- type="button"
- @click="nextMonth"
- v-show="currentView === 'date'"
- :aria-label="t(`el.datepicker.nextMonth`)"
- class="el-picker-panel__icon-btn el-date-picker__next-btn el-icon-arrow-right">
- </button>
- </div>
- <div class="el-picker-panel__content">
- <date-table
- v-show="currentView === 'date'"
- @pick="handleDatePick"
- :selection-mode="selectionMode"
- :first-day-of-week="firstDayOfWeek"
- :value="value"
- :default-value="defaultValue ? new Date(defaultValue) : null"
- :date="date"
- :cell-class-name="cellClassName"
- :disabled-date="disabledDate">
- </date-table>
- <year-table
- v-show="currentView === 'year'"
- @pick="handleYearPick"
- :selection-mode="selectionMode"
- :value="value"
- :default-value="defaultValue ? new Date(defaultValue) : null"
- :date="date"
- :disabled-date="disabledDate">
- </year-table>
- <month-table
- v-show="currentView === 'month'"
- @pick="handleMonthPick"
- :selection-mode="selectionMode"
- :value="value"
- :default-value="defaultValue ? new Date(defaultValue) : null"
- :date="date"
- :disabled-date="disabledDate">
- </month-table>
- </div>
- </div>
- </div>
- <div
- class="el-picker-panel__footer"
- v-show="footerVisible && (currentView === 'date' || currentView === 'month' || currentView === 'year')">
- <el-button
- size="mini"
- type="text"
- class="el-picker-panel__link-btn"
- @click="changeToNow"
- v-show="selectionMode !== 'dates' && selectionMode !== 'months' && selectionMode !== 'years'">
- {{ t('el.datepicker.now') }}
- </el-button>
- <el-button
- plain
- size="mini"
- class="el-picker-panel__link-btn"
- @click="confirm">
- {{ t('el.datepicker.confirm') }}
- </el-button>
- </div>
- </div>
- </transition>
- </template>
- <script type="text/babel">
- import {
- formatDate,
- parseDate,
- getWeekNumber,
- isDate,
- modifyDate,
- modifyTime,
- modifyWithTimeString,
- clearMilliseconds,
- clearTime,
- prevYear,
- nextYear,
- prevMonth,
- nextMonth,
- changeYearMonthAndClampDate,
- extractDateFormat,
- extractTimeFormat,
- timeWithinRange
- } from 'element-ui/src/utils/date-util';
- import Clickoutside from 'element-ui/src/utils/clickoutside';
- import Locale from 'element-ui/src/mixins/locale';
- import ElInput from 'element-ui/packages/input';
- import ElButton from 'element-ui/packages/button';
- import TimePicker from './time';
- import YearTable from '../basic/year-table';
- import MonthTable from '../basic/month-table';
- import DateTable from '../basic/date-table';
- export default {
- mixins: [Locale],
- directives: { Clickoutside },
- watch: {
- showTime(val) {
- /* istanbul ignore if */
- if (!val) return;
- this.$nextTick(_ => {
- const inputElm = this.$refs.input.$el;
- if (inputElm) {
- this.pickerWidth = inputElm.getBoundingClientRect().width + 10;
- }
- });
- },
- value(val) {
- if (this.selectionMode === 'dates' && this.value) return;
- if (this.selectionMode === 'months' && this.value) return;
- if (this.selectionMode === 'years' && this.value) return;
- if (isDate(val)) {
- this.date = new Date(val);
- } else {
- this.date = this.getDefaultValue();
- }
- },
- defaultValue(val) {
- if (!isDate(this.value)) {
- this.date = val ? new Date(val) : new Date();
- }
- },
- timePickerVisible(val) {
- if (val) this.$nextTick(() => this.$refs.timepicker.adjustSpinners());
- },
- selectionMode(newVal) {
- if (newVal === 'month') {
- /* istanbul ignore next */
- if (this.currentView !== 'year' || this.currentView !== 'month') {
- this.currentView = 'month';
- }
- } else if (newVal === 'dates') {
- this.currentView = 'date';
- } else if (newVal === 'years') {
- this.currentView = 'year';
- } else if (newVal === 'months') {
- this.currentView = 'month';
- }
- }
- },
- methods: {
- proxyTimePickerDataProperties() {
- const format = timeFormat => {this.$refs.timepicker.format = timeFormat;};
- const value = value => {this.$refs.timepicker.value = value;};
- const date = date => {this.$refs.timepicker.date = date;};
- const selectableRange = selectableRange => {this.$refs.timepicker.selectableRange = selectableRange;};
- this.$watch('value', value);
- this.$watch('date', date);
- this.$watch('selectableRange', selectableRange);
- format(this.timeFormat);
- value(this.value);
- date(this.date);
- selectableRange(this.selectableRange);
- },
- handleClear() {
- this.date = this.getDefaultValue();
- this.$emit('pick', null);
- },
- emit(value, ...args) {
- if (!value) {
- this.$emit('pick', value, ...args);
- } else if (Array.isArray(value)) {
- const dates = value.map(date => this.showTime ? clearMilliseconds(date) : clearTime(date));
- this.$emit('pick', dates, ...args);
- } else {
- this.$emit('pick', this.showTime ? clearMilliseconds(value) : clearTime(value), ...args);
- }
- this.userInputDate = null;
- this.userInputTime = null;
- },
- // resetDate() {
- // this.date = new Date(this.date);
- // },
- showMonthPicker() {
- this.currentView = 'month';
- },
- showYearPicker() {
- this.currentView = 'year';
- },
- // XXX: 没用到
- // handleLabelClick() {
- // if (this.currentView === 'date') {
- // this.showMonthPicker();
- // } else if (this.currentView === 'month') {
- // this.showYearPicker();
- // }
- // },
- prevMonth() {
- this.date = prevMonth(this.date);
- },
- nextMonth() {
- this.date = nextMonth(this.date);
- },
- prevYear() {
- if (this.currentView === 'year') {
- this.date = prevYear(this.date, 10);
- } else {
- this.date = prevYear(this.date);
- }
- },
- nextYear() {
- if (this.currentView === 'year') {
- this.date = nextYear(this.date, 10);
- } else {
- this.date = nextYear(this.date);
- }
- },
- handleShortcutClick(shortcut) {
- if (shortcut.onClick) {
- shortcut.onClick(this);
- }
- },
- handleTimePick(value, visible, first) {
- if (isDate(value)) {
- const newDate = this.value
- ? modifyTime(this.value, value.getHours(), value.getMinutes(), value.getSeconds())
- : modifyWithTimeString(this.getDefaultValue(), this.defaultTime);
- this.date = newDate;
- this.emit(this.date, true);
- } else {
- this.emit(value, true);
- }
- if (!first) {
- this.timePickerVisible = visible;
- }
- },
- handleTimePickClose() {
- this.timePickerVisible = false;
- },
- handleMonthPick(month) {
- if (this.selectionMode === 'month') {
- this.date = modifyDate(this.date, this.year, month, 1);
- this.emit(this.date);
- } else if (this.selectionMode === 'months') {
- this.emit(month, true);
- } else {
- this.date = changeYearMonthAndClampDate(this.date, this.year, month);
- // TODO: should emit intermediate value ??
- // this.emit(this.date);
- this.currentView = 'date';
- }
- },
- handleDatePick(value) {
- if (this.selectionMode === 'day') {
- let newDate = this.value
- ? modifyDate(this.value, value.getFullYear(), value.getMonth(), value.getDate())
- : modifyWithTimeString(value, this.defaultTime);
- // change default time while out of selectableRange
- if (!this.checkDateWithinRange(newDate)) {
- newDate = modifyDate(this.selectableRange[0][0], value.getFullYear(), value.getMonth(), value.getDate());
- }
- this.date = newDate;
- this.emit(this.date, this.showTime);
- } else if (this.selectionMode === 'week') {
- this.emit(value.date);
- } else if (this.selectionMode === 'dates') {
- this.emit(value, true); // set false to keep panel open
- }
- },
- handleYearPick(year) {
- if (this.selectionMode === 'year') {
- this.date = modifyDate(this.date, year, 0, 1);
- this.emit(this.date);
- } else if (this.selectionMode === 'years') {
- this.emit(year, true);
- } else {
- this.date = changeYearMonthAndClampDate(this.date, year, this.month);
- // TODO: should emit intermediate value ??
- // this.emit(this.date, true);
- this.currentView = 'month';
- }
- },
- changeToNow() {
- // NOTE: not a permanent solution
- // consider disable "now" button in the future
- if ((!this.disabledDate || !this.disabledDate(new Date())) && this.checkDateWithinRange(new Date())) {
- this.date = new Date();
- this.emit(this.date);
- }
- },
- confirm() {
- if (this.selectionMode === 'dates' || this.selectionMode === 'months' || this.selectionMode === 'years') {
- this.emit(this.value);
- } else {
- // value were emitted in handle{Date,Time}Pick, nothing to update here
- // deal with the scenario where: user opens the picker, then confirm without doing anything
- const value = this.value
- ? this.value
- : modifyWithTimeString(this.getDefaultValue(), this.defaultTime);
- this.date = new Date(value); // refresh date
- this.emit(value);
- }
- },
- resetView() {
- if (this.selectionMode === 'month' || this.selectionMode === 'months') {
- this.currentView = 'month';
- } else if (this.selectionMode === 'year' || this.selectionMode === 'years') {
- this.currentView = 'year';
- } else {
- this.currentView = 'date';
- }
- },
- handleEnter() {
- document.body.addEventListener('keydown', this.handleKeydown);
- },
- handleLeave() {
- this.$emit('dodestroy');
- document.body.removeEventListener('keydown', this.handleKeydown);
- },
- handleKeydown(event) {
- const keyCode = event.keyCode;
- const list = [38, 40, 37, 39];
- if (this.visible && !this.timePickerVisible) {
- if (list.indexOf(keyCode) !== -1) {
- this.handleKeyControl(keyCode);
- event.stopPropagation();
- event.preventDefault();
- }
- if (keyCode === 13 && this.userInputDate === null && this.userInputTime === null) { // Enter
- this.emit(this.date, false);
- }
- }
- },
- handleKeyControl(keyCode) {
- const mapping = {
- 'year': {
- 38: -4, 40: 4, 37: -1, 39: 1, offset: (date, step) => date.setFullYear(date.getFullYear() + step)
- },
- 'month': {
- 38: -4, 40: 4, 37: -1, 39: 1, offset: (date, step) => date.setMonth(date.getMonth() + step)
- },
- 'week': {
- 38: -1, 40: 1, 37: -1, 39: 1, offset: (date, step) => date.setDate(date.getDate() + step * 7)
- },
- 'day': {
- 38: -7, 40: 7, 37: -1, 39: 1, offset: (date, step) => date.setDate(date.getDate() + step)
- }
- };
- const mode = this.selectionMode;
- const year = 3.1536e10;
- const now = this.date.getTime();
- const newDate = new Date(this.date.getTime());
- while (Math.abs(now - newDate.getTime()) <= year) {
- const map = mapping[mode];
- map.offset(newDate, map[keyCode]);
- if (typeof this.disabledDate === 'function' && this.disabledDate(newDate)) {
- continue;
- }
- this.date = newDate;
- this.$emit('pick', newDate, true);
- break;
- }
- },
- handleVisibleTimeChange(value) {
- const time = parseDate(value, this.timeFormat);
- if (time && this.checkDateWithinRange(time)) {
- this.date = modifyDate(time, this.year, this.month, this.monthDate);
- this.userInputTime = null;
- this.$refs.timepicker.value = this.date;
- this.timePickerVisible = false;
- this.emit(this.date, true);
- }
- },
- handleVisibleDateChange(value) {
- const date = parseDate(value, this.dateFormat);
- if (date) {
- if (typeof this.disabledDate === 'function' && this.disabledDate(date)) {
- return;
- }
- this.date = modifyTime(date, this.date.getHours(), this.date.getMinutes(), this.date.getSeconds());
- this.userInputDate = null;
- this.resetView();
- this.emit(this.date, true);
- }
- },
- isValidValue(value) {
- return value && !isNaN(value) && (
- typeof this.disabledDate === 'function'
- ? !this.disabledDate(value)
- : true
- ) && this.checkDateWithinRange(value);
- },
- getDefaultValue() {
- // if default-value is set, return it
- // otherwise, return now (the moment this method gets called)
- return this.defaultValue ? new Date(this.defaultValue) : new Date();
- },
- checkDateWithinRange(date) {
- return this.selectableRange.length > 0
- ? timeWithinRange(date, this.selectableRange, this.format || 'HH:mm:ss')
- : true;
- }
- },
- components: {
- TimePicker, YearTable, MonthTable, DateTable, ElInput, ElButton
- },
- data() {
- return {
- popperClass: '',
- date: new Date(),
- value: '',
- defaultValue: null, // use getDefaultValue() for time computation
- defaultTime: null,
- showTime: false,
- selectionMode: 'day',
- shortcuts: '',
- visible: false,
- currentView: 'date',
- disabledDate: '',
- cellClassName: '',
- selectableRange: [],
- firstDayOfWeek: 7,
- showWeekNumber: false,
- timePickerVisible: false,
- format: '',
- arrowControl: false,
- userInputDate: null,
- userInputTime: null
- };
- },
- computed: {
- year() {
- return this.date.getFullYear();
- },
- month() {
- return this.date.getMonth();
- },
- week() {
- return getWeekNumber(this.date);
- },
- monthDate() {
- return this.date.getDate();
- },
- footerVisible() {
- return this.showTime || this.selectionMode === 'dates' || this.selectionMode === 'months' || this.selectionMode === 'years';
- },
- visibleTime() {
- if (this.userInputTime !== null) {
- return this.userInputTime;
- } else {
- return formatDate(this.value || this.defaultValue, this.timeFormat);
- }
- },
- visibleDate() {
- if (this.userInputDate !== null) {
- return this.userInputDate;
- } else {
- return formatDate(this.value || this.defaultValue, this.dateFormat);
- }
- },
- yearLabel() {
- const yearTranslation = this.t('el.datepicker.year');
- if (this.currentView === 'year') {
- const startYear = Math.floor(this.year / 10) * 10;
- if (yearTranslation) {
- return startYear + ' ' + yearTranslation + ' - ' + (startYear + 9) + ' ' + yearTranslation;
- }
- return startYear + ' - ' + (startYear + 9);
- }
- return this.year + ' ' + yearTranslation;
- },
- 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';
- }
- }
- }
- };
- </script>
|