| <template> | 
|   <el-input | 
|     class="el-date-editor" | 
|     :class="'el-date-editor--' + type" | 
|     :readonly="!editable || readonly || type === 'dates' || type === 'week' || type === 'years' || type === 'months'" | 
|     :disabled="pickerDisabled" | 
|     :size="pickerSize" | 
|     :name="name" | 
|     v-bind="firstInputId" | 
|     v-if="!ranged" | 
|     v-clickoutside="handleClose" | 
|     :placeholder="placeholder" | 
|     @focus="handleFocus" | 
|     @keydown.native="handleKeydown" | 
|     :value="displayValue" | 
|     @input="value => userInput = value" | 
|     @change="handleChange" | 
|     @mouseenter.native="handleMouseEnter" | 
|     @mouseleave.native="showClose = false" | 
|     :validateEvent="false" | 
|     ref="reference"> | 
|     <i slot="prefix" | 
|       class="el-input__icon" | 
|       :class="triggerClass" | 
|       @click="handleFocus"> | 
|     </i> | 
|     <i slot="suffix" | 
|       class="el-input__icon" | 
|       @click="handleClickIcon" | 
|       :class="[showClose ? '' + clearIcon : '']" | 
|       v-if="haveTrigger"> | 
|     </i> | 
|   </el-input> | 
|   <div | 
|     class="el-date-editor el-range-editor el-input__inner" | 
|     :class="[ | 
|       'el-date-editor--' + type, | 
|       pickerSize ? `el-range-editor--${ pickerSize }` : '', | 
|       pickerDisabled ? 'is-disabled' : '', | 
|       pickerVisible ? 'is-active' : '' | 
|     ]" | 
|     @click="handleRangeClick" | 
|     @mouseenter="handleMouseEnter" | 
|     @mouseleave="showClose = false" | 
|     @keydown="handleKeydown" | 
|     ref="reference" | 
|     v-clickoutside="handleClose" | 
|     v-else> | 
|     <i :class="['el-input__icon', 'el-range__icon', triggerClass]"></i> | 
|     <input | 
|       autocomplete="off" | 
|       :placeholder="startPlaceholder" | 
|       :value="displayValue && displayValue[0]" | 
|       :disabled="pickerDisabled" | 
|       v-bind="firstInputId" | 
|       :readonly="!editable || readonly" | 
|       :name="name && name[0]" | 
|       @input="handleStartInput" | 
|       @change="handleStartChange" | 
|       @focus="handleFocus" | 
|       class="el-range-input"> | 
|     <slot name="range-separator"> | 
|       <span class="el-range-separator">{{ rangeSeparator }}</span> | 
|     </slot> | 
|     <input | 
|       autocomplete="off" | 
|       :placeholder="endPlaceholder" | 
|       :value="displayValue && displayValue[1]" | 
|       :disabled="pickerDisabled" | 
|       v-bind="secondInputId" | 
|       :readonly="!editable || readonly" | 
|       :name="name && name[1]" | 
|       @input="handleEndInput" | 
|       @change="handleEndChange" | 
|       @focus="handleFocus" | 
|       class="el-range-input"> | 
|     <i | 
|       @click="handleClickIcon" | 
|       v-if="haveTrigger" | 
|       :class="[showClose ? '' + clearIcon : '']" | 
|       class="el-input__icon el-range__close-icon"> | 
|     </i> | 
|   </div> | 
| </template> | 
|   | 
| <script> | 
| import Vue from 'vue'; | 
| import Clickoutside from 'element-ui/src/utils/clickoutside'; | 
| import { formatDate, parseDate, isDateObject, getWeekNumber } from 'element-ui/src/utils/date-util'; | 
| import Popper from 'element-ui/src/utils/vue-popper'; | 
| import Emitter from 'element-ui/src/mixins/emitter'; | 
| import ElInput from 'element-ui/packages/input'; | 
| import merge from 'element-ui/src/utils/merge'; | 
|   | 
| const NewPopper = { | 
|   props: { | 
|     appendToBody: Popper.props.appendToBody, | 
|     offset: Popper.props.offset, | 
|     boundariesPadding: Popper.props.boundariesPadding, | 
|     arrowOffset: Popper.props.arrowOffset, | 
|     transformOrigin: Popper.props.transformOrigin | 
|   }, | 
|   methods: Popper.methods, | 
|   data() { | 
|     return merge({ visibleArrow: true }, Popper.data); | 
|   }, | 
|   beforeDestroy: Popper.beforeDestroy | 
| }; | 
|   | 
| const DEFAULT_FORMATS = { | 
|   date: 'yyyy-MM-dd', | 
|   month: 'yyyy-MM', | 
|   months: 'yyyy-MM', | 
|   datetime: 'yyyy-MM-dd HH:mm:ss', | 
|   time: 'HH:mm:ss', | 
|   week: 'yyyywWW', | 
|   timerange: 'HH:mm:ss', | 
|   daterange: 'yyyy-MM-dd', | 
|   monthrange: 'yyyy-MM', | 
|   datetimerange: 'yyyy-MM-dd HH:mm:ss', | 
|   year: 'yyyy', | 
|   years: 'yyyy' | 
| }; | 
| const HAVE_TRIGGER_TYPES = [ | 
|   'date', | 
|   'datetime', | 
|   'time', | 
|   'time-select', | 
|   'week', | 
|   'month', | 
|   'year', | 
|   'daterange', | 
|   'monthrange', | 
|   'timerange', | 
|   'datetimerange', | 
|   'dates', | 
|   'months', | 
|   'years' | 
| ]; | 
| const DATE_FORMATTER = function(value, format) { | 
|   if (format === 'timestamp') return value.getTime(); | 
|   return formatDate(value, format); | 
| }; | 
| const DATE_PARSER = function(text, format) { | 
|   if (format === 'timestamp') return new Date(Number(text)); | 
|   return parseDate(text, format); | 
| }; | 
| const RANGE_FORMATTER = function(value, format) { | 
|   if (Array.isArray(value) && value.length === 2) { | 
|     const start = value[0]; | 
|     const end = value[1]; | 
|   | 
|     if (start && end) { | 
|       return [DATE_FORMATTER(start, format), DATE_FORMATTER(end, format)]; | 
|     } | 
|   } | 
|   return ''; | 
| }; | 
| const RANGE_PARSER = function(array, format, separator) { | 
|   if (!Array.isArray(array)) { | 
|     array = array.split(separator); | 
|   } | 
|   if (array.length === 2) { | 
|     const range1 = array[0]; | 
|     const range2 = array[1]; | 
|   | 
|     return [DATE_PARSER(range1, format), DATE_PARSER(range2, format)]; | 
|   } | 
|   return []; | 
| }; | 
| const TYPE_VALUE_RESOLVER_MAP = { | 
|   default: { | 
|     formatter(value) { | 
|       if (!value) return ''; | 
|       return '' + value; | 
|     }, | 
|     parser(text) { | 
|       if (text === undefined || text === '') return null; | 
|       return text; | 
|     } | 
|   }, | 
|   week: { | 
|     formatter(value, format) { | 
|       let week = getWeekNumber(value); | 
|       let month = value.getMonth(); | 
|       const trueDate = new Date(value); | 
|       if (week === 1 && month === 11) { | 
|         trueDate.setHours(0, 0, 0, 0); | 
|         trueDate.setDate(trueDate.getDate() + 3 - (trueDate.getDay() + 6) % 7); | 
|       } | 
|       let date = formatDate(trueDate, format); | 
|   | 
|       date = /WW/.test(date) | 
|         ? date.replace(/WW/, week < 10 ? '0' + week : week) | 
|         : date.replace(/W/, week); | 
|       return date; | 
|     }, | 
|     parser(text, format) { | 
|       // parse as if a normal date | 
|       return TYPE_VALUE_RESOLVER_MAP.date.parser(text, format); | 
|     } | 
|   }, | 
|   date: { | 
|     formatter: DATE_FORMATTER, | 
|     parser: DATE_PARSER | 
|   }, | 
|   datetime: { | 
|     formatter: DATE_FORMATTER, | 
|     parser: DATE_PARSER | 
|   }, | 
|   daterange: { | 
|     formatter: RANGE_FORMATTER, | 
|     parser: RANGE_PARSER | 
|   }, | 
|   monthrange: { | 
|     formatter: RANGE_FORMATTER, | 
|     parser: RANGE_PARSER | 
|   }, | 
|   datetimerange: { | 
|     formatter: RANGE_FORMATTER, | 
|     parser: RANGE_PARSER | 
|   }, | 
|   timerange: { | 
|     formatter: RANGE_FORMATTER, | 
|     parser: RANGE_PARSER | 
|   }, | 
|   time: { | 
|     formatter: DATE_FORMATTER, | 
|     parser: DATE_PARSER | 
|   }, | 
|   month: { | 
|     formatter: DATE_FORMATTER, | 
|     parser: DATE_PARSER | 
|   }, | 
|   year: { | 
|     formatter: DATE_FORMATTER, | 
|     parser: DATE_PARSER | 
|   }, | 
|   number: { | 
|     formatter(value) { | 
|       if (!value) return ''; | 
|       return '' + value; | 
|     }, | 
|     parser(text) { | 
|       let result = Number(text); | 
|   | 
|       if (!isNaN(text)) { | 
|         return result; | 
|       } else { | 
|         return null; | 
|       } | 
|     } | 
|   }, | 
|   dates: { | 
|     formatter(value, format) { | 
|       return value.map(date => DATE_FORMATTER(date, format)); | 
|     }, | 
|     parser(value, format) { | 
|       return (typeof value === 'string' ? value.split(', ') : value) | 
|         .map(date => date instanceof Date ? date : DATE_PARSER(date, format)); | 
|     } | 
|   }, | 
|   months: { | 
|     formatter(value, format) { | 
|       return value.map(date => DATE_FORMATTER(date, format)); | 
|     }, | 
|     parser(value, format) { | 
|       return (typeof value === 'string' ? value.split(', ') : value) | 
|         .map(date => date instanceof Date ? date : DATE_PARSER(date, format)); | 
|     } | 
|   }, | 
|   years: { | 
|     formatter(value, format) { | 
|       return value.map(date => DATE_FORMATTER(date, format)); | 
|     }, | 
|     parser(value, format) { | 
|       return (typeof value === 'string' ? value.split(', ') : value) | 
|         .map(date => date instanceof Date ? date : DATE_PARSER(date, format)); | 
|     } | 
|   } | 
| }; | 
| const PLACEMENT_MAP = { | 
|   left: 'bottom-start', | 
|   center: 'bottom', | 
|   right: 'bottom-end' | 
| }; | 
|   | 
| const parseAsFormatAndType = (value, customFormat, type, rangeSeparator = '-') => { | 
|   if (!value) return null; | 
|   const parser = ( | 
|     TYPE_VALUE_RESOLVER_MAP[type] || | 
|     TYPE_VALUE_RESOLVER_MAP['default'] | 
|   ).parser; | 
|   const format = customFormat || DEFAULT_FORMATS[type]; | 
|   return parser(value, format, rangeSeparator); | 
| }; | 
|   | 
| const formatAsFormatAndType = (value, customFormat, type) => { | 
|   if (!value) return null; | 
|   const formatter = ( | 
|     TYPE_VALUE_RESOLVER_MAP[type] || | 
|     TYPE_VALUE_RESOLVER_MAP['default'] | 
|   ).formatter; | 
|   const format = customFormat || DEFAULT_FORMATS[type]; | 
|   return formatter(value, format); | 
| }; | 
|   | 
| /* | 
|  * Considers: | 
|  *   1. Date object | 
|  *   2. date string | 
|  *   3. array of 1 or 2 | 
|  */ | 
| const valueEquals = function(a, b) { | 
|   // considers Date object and string | 
|   const dateEquals = function(a, b) { | 
|     const aIsDate = a instanceof Date; | 
|     const bIsDate = b instanceof Date; | 
|     if (aIsDate && bIsDate) { | 
|       return a.getTime() === b.getTime(); | 
|     } | 
|     if (!aIsDate && !bIsDate) { | 
|       return a === b; | 
|     } | 
|     return false; | 
|   }; | 
|   | 
|   const aIsArray = a instanceof Array; | 
|   const bIsArray = b instanceof Array; | 
|   if (aIsArray && bIsArray) { | 
|     if (a.length !== b.length) { | 
|       return false; | 
|     } | 
|     return a.every((item, index) => dateEquals(item, b[index])); | 
|   } | 
|   if (!aIsArray && !bIsArray) { | 
|     return dateEquals(a, b); | 
|   } | 
|   return false; | 
| }; | 
|   | 
| const isString = function(val) { | 
|   return typeof val === 'string' || val instanceof String; | 
| }; | 
|   | 
| const validator = function(val) { | 
|   // either: String, Array of String, null / undefined | 
|   return ( | 
|     val === null || | 
|     val === undefined || | 
|     isString(val) || | 
|     (Array.isArray(val) && val.length === 2 && val.every(isString)) | 
|   ); | 
| }; | 
|   | 
| export default { | 
|   mixins: [Emitter, NewPopper], | 
|   | 
|   inject: { | 
|     elForm: { | 
|       default: '' | 
|     }, | 
|     elFormItem: { | 
|       default: '' | 
|     } | 
|   }, | 
|   | 
|   props: { | 
|     size: String, | 
|     format: String, | 
|     valueFormat: String, | 
|     readonly: Boolean, | 
|     placeholder: String, | 
|     startPlaceholder: String, | 
|     endPlaceholder: String, | 
|     prefixIcon: String, | 
|     clearIcon: { | 
|       type: String, | 
|       default: 'el-icon-circle-close' | 
|     }, | 
|     name: { | 
|       default: '', | 
|       validator | 
|     }, | 
|     disabled: Boolean, | 
|     clearable: { | 
|       type: Boolean, | 
|       default: true | 
|     }, | 
|     id: { | 
|       default: '', | 
|       validator | 
|     }, | 
|     popperClass: String, | 
|     editable: { | 
|       type: Boolean, | 
|       default: true | 
|     }, | 
|     align: { | 
|       type: String, | 
|       default: 'left' | 
|     }, | 
|     value: {}, | 
|     defaultValue: {}, | 
|     defaultTime: {}, | 
|     rangeSeparator: { | 
|       default: '-' | 
|     }, | 
|     pickerOptions: {}, | 
|     unlinkPanels: Boolean, | 
|     validateEvent: { | 
|       type: Boolean, | 
|       default: true | 
|     } | 
|   }, | 
|   | 
|   components: { ElInput }, | 
|   | 
|   directives: { Clickoutside }, | 
|   | 
|   data() { | 
|     return { | 
|       pickerVisible: false, | 
|       showClose: false, | 
|       userInput: null, | 
|       valueOnOpen: null, // value when picker opens, used to determine whether to emit change | 
|       unwatchPickerOptions: null | 
|     }; | 
|   }, | 
|   | 
|   watch: { | 
|     pickerVisible(val) { | 
|       if (this.readonly || this.pickerDisabled) return; | 
|       if (val) { | 
|         this.showPicker(); | 
|         this.valueOnOpen = Array.isArray(this.value) ? [...this.value] : this.value; | 
|       } else { | 
|         this.hidePicker(); | 
|         this.emitChange(this.value); | 
|         this.userInput = null; | 
|         if (this.validateEvent) { | 
|           this.dispatch('ElFormItem', 'el.form.blur'); | 
|         } | 
|         this.$emit('blur', this); | 
|         this.blur(); | 
|       } | 
|     }, | 
|     parsedValue: { | 
|       immediate: true, | 
|       handler(val) { | 
|         if (this.picker) { | 
|           this.picker.value = val; | 
|         } | 
|       } | 
|     }, | 
|     defaultValue(val) { | 
|       // NOTE: should eventually move to jsx style picker + panel ? | 
|       if (this.picker) { | 
|         this.picker.defaultValue = val; | 
|       } | 
|     }, | 
|     value(val, oldVal) { | 
|       if (!valueEquals(val, oldVal) && !this.pickerVisible && this.validateEvent) { | 
|         this.dispatch('ElFormItem', 'el.form.change', val); | 
|       } | 
|     } | 
|   }, | 
|   | 
|   computed: { | 
|     ranged() { | 
|       return this.type.indexOf('range') > -1; | 
|     }, | 
|   | 
|     reference() { | 
|       const reference = this.$refs.reference; | 
|       return reference.$el || reference; | 
|     }, | 
|   | 
|     refInput() { | 
|       if (this.reference) { | 
|         return [].slice.call(this.reference.querySelectorAll('input')); | 
|       } | 
|       return []; | 
|     }, | 
|   | 
|     valueIsEmpty() { | 
|       const val = this.value; | 
|       if (Array.isArray(val)) { | 
|         for (let i = 0, len = val.length; i < len; i++) { | 
|           if (val[i]) { | 
|             return false; | 
|           } | 
|         } | 
|       } else { | 
|         if (val) { | 
|           return false; | 
|         } | 
|       } | 
|       return true; | 
|     }, | 
|   | 
|     triggerClass() { | 
|       return this.prefixIcon || (this.type.indexOf('time') !== -1 ? 'el-icon-time' : 'el-icon-date'); | 
|     }, | 
|   | 
|     selectionMode() { | 
|       if (this.type === 'week') { | 
|         return 'week'; | 
|       } else if (this.type === 'month') { | 
|         return 'month'; | 
|       } else if (this.type === 'year') { | 
|         return 'year'; | 
|       } else if (this.type === 'dates') { | 
|         return 'dates'; | 
|       } else if (this.type === 'months') { | 
|         return 'months'; | 
|       } else if (this.type === 'years') { | 
|         return 'years'; | 
|       } | 
|   | 
|       return 'day'; | 
|     }, | 
|   | 
|     haveTrigger() { | 
|       if (typeof this.showTrigger !== 'undefined') { | 
|         return this.showTrigger; | 
|       } | 
|       return HAVE_TRIGGER_TYPES.indexOf(this.type) !== -1; | 
|     }, | 
|   | 
|     displayValue() { | 
|       const formattedValue = formatAsFormatAndType(this.parsedValue, this.format, this.type, this.rangeSeparator); | 
|       if (Array.isArray(this.userInput)) { | 
|         return [ | 
|           this.userInput[0] || (formattedValue && formattedValue[0]) || '', | 
|           this.userInput[1] || (formattedValue && formattedValue[1]) || '' | 
|         ]; | 
|       } else if (this.userInput !== null) { | 
|         return this.userInput; | 
|       } else if (formattedValue) { | 
|         return (this.type === 'dates' || this.type === 'years' || this.type === 'months') | 
|           ? formattedValue.join(', ') | 
|           : formattedValue; | 
|       } else { | 
|         return ''; | 
|       } | 
|     }, | 
|   | 
|     parsedValue() { | 
|       if (!this.value) return this.value; // component value is not set | 
|       if (this.type === 'time-select') return this.value; // time-select does not require parsing, this might change in next major version | 
|   | 
|       const valueIsDateObject = isDateObject(this.value) || (Array.isArray(this.value) && this.value.every(isDateObject)); | 
|       if (valueIsDateObject) { | 
|         return this.value; | 
|       } | 
|   | 
|       if (this.valueFormat) { | 
|         return parseAsFormatAndType(this.value, this.valueFormat, this.type, this.rangeSeparator) || this.value; | 
|       } | 
|   | 
|       // NOTE: deal with common but incorrect usage, should remove in next major version | 
|       // user might provide string / timestamp without value-format, coerce them into date (or array of date) | 
|       return Array.isArray(this.value) ? this.value.map(val => new Date(val)) : new Date(this.value); | 
|     }, | 
|   | 
|     _elFormItemSize() { | 
|       return (this.elFormItem || {}).elFormItemSize; | 
|     }, | 
|   | 
|     pickerSize() { | 
|       return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size; | 
|     }, | 
|   | 
|     pickerDisabled() { | 
|       return this.disabled || (this.elForm || {}).disabled; | 
|     }, | 
|   | 
|     firstInputId() { | 
|       const obj = {}; | 
|       let id; | 
|       if (this.ranged) { | 
|         id = this.id && this.id[0]; | 
|       } else { | 
|         id = this.id; | 
|       } | 
|       if (id) obj.id = id; | 
|       return obj; | 
|     }, | 
|   | 
|     secondInputId() { | 
|       const obj = {}; | 
|       let id; | 
|       if (this.ranged) { | 
|         id = this.id && this.id[1]; | 
|       } | 
|       if (id) obj.id = id; | 
|       return obj; | 
|     } | 
|   }, | 
|   | 
|   created() { | 
|     // vue-popper | 
|     this.popperOptions = { | 
|       boundariesPadding: 0, | 
|       gpuAcceleration: false | 
|     }; | 
|     this.placement = PLACEMENT_MAP[this.align] || PLACEMENT_MAP.left; | 
|   | 
|     this.$on('fieldReset', this.handleFieldReset); | 
|   }, | 
|   | 
|   methods: { | 
|     focus() { | 
|       if (!this.ranged) { | 
|         this.$refs.reference.focus(); | 
|       } else { | 
|         this.handleFocus(); | 
|       } | 
|     }, | 
|   | 
|     blur() { | 
|       this.refInput.forEach(input => input.blur()); | 
|     }, | 
|   | 
|     // {parse, formatTo} Value deals maps component value with internal Date | 
|     parseValue(value) { | 
|       const isParsed = isDateObject(value) || (Array.isArray(value) && value.every(isDateObject)); | 
|       if (this.valueFormat && !isParsed) { | 
|         return parseAsFormatAndType(value, this.valueFormat, this.type, this.rangeSeparator) || value; | 
|       } else { | 
|         return value; | 
|       } | 
|     }, | 
|   | 
|     formatToValue(date) { | 
|       const isFormattable = isDateObject(date) || (Array.isArray(date) && date.every(isDateObject)); | 
|       if (this.valueFormat && isFormattable) { | 
|         return formatAsFormatAndType(date, this.valueFormat, this.type, this.rangeSeparator); | 
|       } else { | 
|         return date; | 
|       } | 
|     }, | 
|   | 
|     // {parse, formatTo} String deals with user input | 
|     parseString(value) { | 
|       const type = Array.isArray(value) ? this.type : this.type.replace('range', ''); | 
|       return parseAsFormatAndType(value, this.format, type); | 
|     }, | 
|   | 
|     formatToString(value) { | 
|       const type = Array.isArray(value) ? this.type : this.type.replace('range', ''); | 
|       return formatAsFormatAndType(value, this.format, type); | 
|     }, | 
|   | 
|     handleMouseEnter() { | 
|       if (this.readonly || this.pickerDisabled) return; | 
|       if (!this.valueIsEmpty && this.clearable) { | 
|         this.showClose = true; | 
|       } | 
|     }, | 
|   | 
|     handleChange() { | 
|       if (this.userInput) { | 
|         const value = this.parseString(this.displayValue); | 
|         if (value) { | 
|           this.picker.value = value; | 
|           if (this.isValidValue(value)) { | 
|             this.emitInput(value); | 
|             this.userInput = null; | 
|           } | 
|         } | 
|       } | 
|       if (this.userInput === '') { | 
|         this.emitInput(null); | 
|         this.emitChange(null); | 
|         this.userInput = null; | 
|       } | 
|     }, | 
|   | 
|     handleStartInput(event) { | 
|       if (this.userInput) { | 
|         this.userInput = [event.target.value, this.userInput[1]]; | 
|       } else { | 
|         this.userInput = [event.target.value, null]; | 
|       } | 
|     }, | 
|   | 
|     handleEndInput(event) { | 
|       if (this.userInput) { | 
|         this.userInput = [this.userInput[0], event.target.value]; | 
|       } else { | 
|         this.userInput = [null, event.target.value]; | 
|       } | 
|     }, | 
|   | 
|     handleStartChange(event) { | 
|       const value = this.parseString(this.userInput && this.userInput[0]); | 
|       if (value) { | 
|         this.userInput = [this.formatToString(value), this.displayValue[1]]; | 
|         const newValue = [value, this.picker.value && this.picker.value[1]]; | 
|         this.picker.value = newValue; | 
|         if (this.isValidValue(newValue)) { | 
|           this.emitInput(newValue); | 
|           this.userInput = null; | 
|         } | 
|       } | 
|     }, | 
|   | 
|     handleEndChange(event) { | 
|       const value = this.parseString(this.userInput && this.userInput[1]); | 
|       if (value) { | 
|         this.userInput = [this.displayValue[0], this.formatToString(value)]; | 
|         const newValue = [this.picker.value && this.picker.value[0], value]; | 
|         this.picker.value = newValue; | 
|         if (this.isValidValue(newValue)) { | 
|           this.emitInput(newValue); | 
|           this.userInput = null; | 
|         } | 
|       } | 
|     }, | 
|   | 
|     handleClickIcon(event) { | 
|       if (this.readonly || this.pickerDisabled) return; | 
|       if (this.showClose) { | 
|         this.valueOnOpen = this.value; | 
|         event.stopPropagation(); | 
|         this.emitInput(null); | 
|         this.emitChange(null); | 
|         this.showClose = false; | 
|         if (this.picker && typeof this.picker.handleClear === 'function') { | 
|           this.picker.handleClear(); | 
|         } | 
|       } else { | 
|         this.pickerVisible = !this.pickerVisible; | 
|       } | 
|     }, | 
|   | 
|     handleClose() { | 
|       if (!this.pickerVisible) return; | 
|       this.pickerVisible = false; | 
|   | 
|       if (this.type === 'dates' || this.type === 'years' || this.type === 'months') { | 
|         // restore to former value | 
|         const oldValue = parseAsFormatAndType(this.valueOnOpen, this.valueFormat, this.type, this.rangeSeparator) || this.valueOnOpen; | 
|         this.emitInput(oldValue); | 
|       } | 
|     }, | 
|   | 
|     handleFieldReset(initialValue) { | 
|       this.userInput = initialValue === '' ? null : initialValue; | 
|     }, | 
|   | 
|     handleFocus() { | 
|       const type = this.type; | 
|   | 
|       if (HAVE_TRIGGER_TYPES.indexOf(type) !== -1 && !this.pickerVisible) { | 
|         this.pickerVisible = true; | 
|       } | 
|       this.$emit('focus', this); | 
|     }, | 
|   | 
|     handleKeydown(event) { | 
|       const keyCode = event.keyCode; | 
|   | 
|       // ESC | 
|       if (keyCode === 27) { | 
|         this.pickerVisible = false; | 
|         event.stopPropagation(); | 
|         return; | 
|       } | 
|   | 
|       // Tab | 
|       if (keyCode === 9) { | 
|         if (!this.ranged) { | 
|           this.handleChange(); | 
|           this.pickerVisible = this.picker.visible = false; | 
|           this.blur(); | 
|           event.stopPropagation(); | 
|         } else { | 
|           // user may change focus between two input | 
|           setTimeout(() => { | 
|             if (this.refInput.indexOf(document.activeElement) === -1) { | 
|               this.pickerVisible = false; | 
|               this.blur(); | 
|               event.stopPropagation(); | 
|             } | 
|           }, 0); | 
|         } | 
|         return; | 
|       } | 
|   | 
|       // Enter | 
|       if (keyCode === 13) { | 
|         if (this.userInput === '' || this.isValidValue(this.parseString(this.displayValue))) { | 
|           this.handleChange(); | 
|           this.pickerVisible = this.picker.visible = false; | 
|           this.blur(); | 
|         } | 
|         event.stopPropagation(); | 
|         return; | 
|       } | 
|   | 
|       // if user is typing, do not let picker handle key input | 
|       if (this.userInput) { | 
|         event.stopPropagation(); | 
|         return; | 
|       } | 
|   | 
|       // delegate other keys to panel | 
|       if (this.picker && this.picker.handleKeydown) { | 
|         this.picker.handleKeydown(event); | 
|       } | 
|     }, | 
|   | 
|     handleRangeClick() { | 
|       const type = this.type; | 
|   | 
|       if (HAVE_TRIGGER_TYPES.indexOf(type) !== -1 && !this.pickerVisible) { | 
|         this.pickerVisible = true; | 
|       } | 
|       this.$emit('focus', this); | 
|     }, | 
|   | 
|     hidePicker() { | 
|       if (this.picker) { | 
|         this.picker.resetView && this.picker.resetView(); | 
|         this.pickerVisible = this.picker.visible = false; | 
|         this.destroyPopper(); | 
|       } | 
|     }, | 
|   | 
|     showPicker() { | 
|       if (this.$isServer) return; | 
|       if (!this.picker) { | 
|         this.mountPicker(); | 
|       } | 
|       this.pickerVisible = this.picker.visible = true; | 
|   | 
|       this.updatePopper(); | 
|   | 
|       this.picker.value = this.parsedValue; | 
|       this.picker.resetView && this.picker.resetView(); | 
|   | 
|       this.$nextTick(() => { | 
|         this.picker.adjustSpinners && this.picker.adjustSpinners(); | 
|       }); | 
|     }, | 
|   | 
|     mountPicker() { | 
|       this.picker = new Vue(this.panel).$mount(); | 
|       this.picker.defaultValue = this.defaultValue; | 
|       this.picker.defaultTime = this.defaultTime; | 
|       this.picker.popperClass = this.popperClass; | 
|       this.popperElm = this.picker.$el; | 
|       this.picker.width = this.reference.getBoundingClientRect().width; | 
|       this.picker.showTime = this.type === 'datetime' || this.type === 'datetimerange'; | 
|       this.picker.selectionMode = this.selectionMode; | 
|       this.picker.unlinkPanels = this.unlinkPanels; | 
|       this.picker.arrowControl = this.arrowControl || this.timeArrowControl || false; | 
|       this.$watch('format', (format) => { | 
|         this.picker.format = format; | 
|       }); | 
|   | 
|       const updateOptions = () => { | 
|         const options = this.pickerOptions; | 
|   | 
|         if (options && options.selectableRange) { | 
|           let ranges = options.selectableRange; | 
|           const parser = TYPE_VALUE_RESOLVER_MAP.datetimerange.parser; | 
|           const format = DEFAULT_FORMATS.timerange; | 
|   | 
|           ranges = Array.isArray(ranges) ? ranges : [ranges]; | 
|           this.picker.selectableRange = ranges.map(range => parser(range, format, this.rangeSeparator)); | 
|         } | 
|   | 
|         for (const option in options) { | 
|           if (options.hasOwnProperty(option) && | 
|               // 忽略 time-picker 的该配置项 | 
|               option !== 'selectableRange') { | 
|             this.picker[option] = options[option]; | 
|           } | 
|         } | 
|   | 
|         // main format must prevail over undocumented pickerOptions.format | 
|         if (this.format) { | 
|           this.picker.format = this.format; | 
|         } | 
|       }; | 
|       updateOptions(); | 
|       this.unwatchPickerOptions = this.$watch('pickerOptions', () => updateOptions(), { deep: true }); | 
|       this.$el.appendChild(this.picker.$el); | 
|       this.picker.resetView && this.picker.resetView(); | 
|   | 
|       this.picker.$on('dodestroy', this.doDestroy); | 
|       this.picker.$on('pick', (date = '', visible = false) => { | 
|         this.userInput = null; | 
|         this.pickerVisible = this.picker.visible = visible; | 
|         this.emitInput(date); | 
|         this.picker.resetView && this.picker.resetView(); | 
|       }); | 
|   | 
|       this.picker.$on('select-range', (start, end, pos) => { | 
|         if (this.refInput.length === 0) return; | 
|         if (!pos || pos === 'min') { | 
|           this.refInput[0].setSelectionRange(start, end); | 
|           this.refInput[0].focus(); | 
|         } else if (pos === 'max') { | 
|           this.refInput[1].setSelectionRange(start, end); | 
|           this.refInput[1].focus(); | 
|         } | 
|       }); | 
|     }, | 
|   | 
|     unmountPicker() { | 
|       if (this.picker) { | 
|         this.picker.$destroy(); | 
|         this.picker.$off(); | 
|         if (typeof this.unwatchPickerOptions === 'function') { | 
|           this.unwatchPickerOptions(); | 
|         } | 
|         this.picker.$el.parentNode.removeChild(this.picker.$el); | 
|       } | 
|     }, | 
|   | 
|     emitChange(val) { | 
|       // determine user real change only | 
|       if (!valueEquals(val, this.valueOnOpen)) { | 
|         this.$emit('change', val); | 
|         this.valueOnOpen = val; | 
|         if (this.validateEvent) { | 
|           this.dispatch('ElFormItem', 'el.form.change', val); | 
|         } | 
|       } | 
|     }, | 
|   | 
|     emitInput(val) { | 
|       const formatted = this.formatToValue(val); | 
|       if (!valueEquals(this.value, formatted)) { | 
|         this.$emit('input', formatted); | 
|       } | 
|     }, | 
|   | 
|     isValidValue(value) { | 
|       if (!this.picker) { | 
|         this.mountPicker(); | 
|       } | 
|       if (this.picker.isValidValue) { | 
|         return value && this.picker.isValidValue(value); | 
|       } else { | 
|         return true; | 
|       } | 
|     } | 
|   } | 
| }; | 
| </script> |