| <template> | 
|   <div | 
|     class="el-slider" | 
|     :class="{ 'is-vertical': vertical, 'el-slider--with-input': showInput }" | 
|     role="slider" | 
|     :aria-valuemin="min" | 
|     :aria-valuemax="max" | 
|     :aria-orientation="vertical ? 'vertical': 'horizontal'" | 
|     :aria-disabled="sliderDisabled" | 
|   > | 
|     <el-input-number | 
|       v-model="firstValue" | 
|       v-if="showInput && !range" | 
|       class="el-slider__input" | 
|       ref="input" | 
|       @change="emitChange" | 
|       :step="step" | 
|       :disabled="sliderDisabled" | 
|       :controls="showInputControls" | 
|       :min="min" | 
|       :max="max" | 
|       :debounce="debounce" | 
|       :size="inputSize"> | 
|     </el-input-number> | 
|     <div | 
|       class="el-slider__runway" | 
|       :class="{ 'show-input': showInput, 'disabled': sliderDisabled }" | 
|       :style="runwayStyle" | 
|       @click="onSliderClick" | 
|       ref="slider"> | 
|       <div | 
|         class="el-slider__bar" | 
|         :style="barStyle"> | 
|       </div> | 
|       <slider-button | 
|         :vertical="vertical" | 
|         v-model="firstValue" | 
|         :tooltip-class="tooltipClass" | 
|         ref="button1"> | 
|       </slider-button> | 
|       <slider-button | 
|         :vertical="vertical" | 
|         v-model="secondValue" | 
|         :tooltip-class="tooltipClass" | 
|         ref="button2" | 
|         v-if="range"> | 
|       </slider-button> | 
|       <div | 
|         class="el-slider__stop" | 
|         v-for="(item, key) in stops" | 
|         :key="key" | 
|         :style="getStopStyle(item)" | 
|         v-if="showStops"> | 
|       </div> | 
|       <template v-if="markList.length > 0"> | 
|         <div> | 
|           <div | 
|             v-for="(item, key) in markList" | 
|             :style="getStopStyle(item.position)" | 
|             class="el-slider__stop el-slider__marks-stop" | 
|             :key="key"> | 
|           </div> | 
|         </div> | 
|         <div class="el-slider__marks"> | 
|           <slider-marker | 
|             :mark="item.mark" v-for="(item, key) in markList" | 
|             :key="key" | 
|             :style="getStopStyle(item.position)"> | 
|           </slider-marker> | 
|         </div> | 
|       </template> | 
|     </div> | 
|   </div> | 
| </template> | 
|   | 
| <script type="text/babel"> | 
|   import ElInputNumber from 'element-ui/packages/input-number'; | 
|   import SliderButton from './button.vue'; | 
|   import SliderMarker from './marker'; | 
|   import Emitter from 'element-ui/src/mixins/emitter'; | 
|   | 
|   export default { | 
|     name: 'ElSlider', | 
|   | 
|     mixins: [Emitter], | 
|   | 
|     inject: { | 
|       elForm: { | 
|         default: '' | 
|       } | 
|     }, | 
|   | 
|     props: { | 
|       min: { | 
|         type: Number, | 
|         default: 0 | 
|       }, | 
|       max: { | 
|         type: Number, | 
|         default: 100 | 
|       }, | 
|       step: { | 
|         type: Number, | 
|         default: 1 | 
|       }, | 
|       value: { | 
|         type: [Number, Array], | 
|         default: 0 | 
|       }, | 
|       showInput: { | 
|         type: Boolean, | 
|         default: false | 
|       }, | 
|       showInputControls: { | 
|         type: Boolean, | 
|         default: true | 
|       }, | 
|       inputSize: { | 
|         type: String, | 
|         default: 'small' | 
|       }, | 
|       showStops: { | 
|         type: Boolean, | 
|         default: false | 
|       }, | 
|       showTooltip: { | 
|         type: Boolean, | 
|         default: true | 
|       }, | 
|       formatTooltip: Function, | 
|       disabled: { | 
|         type: Boolean, | 
|         default: false | 
|       }, | 
|       range: { | 
|         type: Boolean, | 
|         default: false | 
|       }, | 
|       vertical: { | 
|         type: Boolean, | 
|         default: false | 
|       }, | 
|       height: { | 
|         type: String | 
|       }, | 
|       debounce: { | 
|         type: Number, | 
|         default: 300 | 
|       }, | 
|       label: { | 
|         type: String | 
|       }, | 
|       tooltipClass: String, | 
|       marks: Object | 
|     }, | 
|   | 
|     components: { | 
|       ElInputNumber, | 
|       SliderButton, | 
|       SliderMarker | 
|     }, | 
|   | 
|     data() { | 
|       return { | 
|         firstValue: null, | 
|         secondValue: null, | 
|         oldValue: null, | 
|         dragging: false, | 
|         sliderSize: 1 | 
|       }; | 
|     }, | 
|   | 
|     watch: { | 
|       value(val, oldVal) { | 
|         if (this.dragging || | 
|           Array.isArray(val) && | 
|           Array.isArray(oldVal) && | 
|           val.every((item, index) => item === oldVal[index])) { | 
|           return; | 
|         } | 
|         this.setValues(); | 
|       }, | 
|   | 
|       dragging(val) { | 
|         if (!val) { | 
|           this.setValues(); | 
|         } | 
|       }, | 
|   | 
|       firstValue(val) { | 
|         if (this.range) { | 
|           this.$emit('input', [this.minValue, this.maxValue]); | 
|         } else { | 
|           this.$emit('input', val); | 
|         } | 
|       }, | 
|   | 
|       secondValue() { | 
|         if (this.range) { | 
|           this.$emit('input', [this.minValue, this.maxValue]); | 
|         } | 
|       }, | 
|   | 
|       min() { | 
|         this.setValues(); | 
|       }, | 
|   | 
|       max() { | 
|         this.setValues(); | 
|       } | 
|     }, | 
|   | 
|     methods: { | 
|       valueChanged() { | 
|         if (this.range) { | 
|           return ![this.minValue, this.maxValue] | 
|             .every((item, index) => item === this.oldValue[index]); | 
|         } else { | 
|           return this.value !== this.oldValue; | 
|         } | 
|       }, | 
|       setValues() { | 
|         if (this.min > this.max) { | 
|           console.error('[Element Error][Slider]min should not be greater than max.'); | 
|           return; | 
|         } | 
|         const val = this.value; | 
|         if (this.range && Array.isArray(val)) { | 
|           if (val[1] < this.min) { | 
|             this.$emit('input', [this.min, this.min]); | 
|           } else if (val[0] > this.max) { | 
|             this.$emit('input', [this.max, this.max]); | 
|           } else if (val[0] < this.min) { | 
|             this.$emit('input', [this.min, val[1]]); | 
|           } else if (val[1] > this.max) { | 
|             this.$emit('input', [val[0], this.max]); | 
|           } else { | 
|             this.firstValue = val[0]; | 
|             this.secondValue = val[1]; | 
|             if (this.valueChanged()) { | 
|               this.dispatch('ElFormItem', 'el.form.change', [this.minValue, this.maxValue]); | 
|               this.oldValue = val.slice(); | 
|             } | 
|           } | 
|         } else if (!this.range && typeof val === 'number' && !isNaN(val)) { | 
|           if (val < this.min) { | 
|             this.$emit('input', this.min); | 
|           } else if (val > this.max) { | 
|             this.$emit('input', this.max); | 
|           } else { | 
|             this.firstValue = val; | 
|             if (this.valueChanged()) { | 
|               this.dispatch('ElFormItem', 'el.form.change', val); | 
|               this.oldValue = val; | 
|             } | 
|           } | 
|         } | 
|       }, | 
|   | 
|       setPosition(percent) { | 
|         const targetValue = this.min + percent * (this.max - this.min) / 100; | 
|         if (!this.range) { | 
|           this.$refs.button1.setPosition(percent); | 
|           return; | 
|         } | 
|         let button; | 
|         if (Math.abs(this.minValue - targetValue) < Math.abs(this.maxValue - targetValue)) { | 
|           button = this.firstValue < this.secondValue ? 'button1' : 'button2'; | 
|         } else { | 
|           button = this.firstValue > this.secondValue ? 'button1' : 'button2'; | 
|         } | 
|         this.$refs[button].setPosition(percent); | 
|       }, | 
|   | 
|       onSliderClick(event) { | 
|         if (this.sliderDisabled || this.dragging) return; | 
|         this.resetSize(); | 
|         if (this.vertical) { | 
|           const sliderOffsetBottom = this.$refs.slider.getBoundingClientRect().bottom; | 
|           this.setPosition((sliderOffsetBottom - event.clientY) / this.sliderSize * 100); | 
|         } else { | 
|           const sliderOffsetLeft = this.$refs.slider.getBoundingClientRect().left; | 
|           this.setPosition((event.clientX - sliderOffsetLeft) / this.sliderSize * 100); | 
|         } | 
|         this.emitChange(); | 
|       }, | 
|   | 
|       resetSize() { | 
|         if (this.$refs.slider) { | 
|           this.sliderSize = this.$refs.slider[`client${ this.vertical ? 'Height' : 'Width' }`]; | 
|         } | 
|       }, | 
|   | 
|       emitChange() { | 
|         this.$nextTick(() => { | 
|           this.$emit('change', this.range ? [this.minValue, this.maxValue] : this.value); | 
|         }); | 
|       }, | 
|   | 
|       getStopStyle(position) { | 
|         return this.vertical ? { 'bottom': position + '%' } : { 'left': position + '%' }; | 
|       } | 
|     }, | 
|   | 
|     computed: { | 
|       stops() { | 
|         if (!this.showStops || this.min > this.max) return []; | 
|         if (this.step === 0) { | 
|           process.env.NODE_ENV !== 'production' && | 
|           console.warn('[Element Warn][Slider]step should not be 0.'); | 
|           return []; | 
|         } | 
|         const stopCount = (this.max - this.min) / this.step; | 
|         const stepWidth = 100 * this.step / (this.max - this.min); | 
|         const result = []; | 
|         for (let i = 1; i < stopCount; i++) { | 
|           result.push(i * stepWidth); | 
|         } | 
|         if (this.range) { | 
|           return result.filter(step => { | 
|             return step < 100 * (this.minValue - this.min) / (this.max - this.min) || | 
|               step > 100 * (this.maxValue - this.min) / (this.max - this.min); | 
|           }); | 
|         } else { | 
|           return result.filter(step => step > 100 * (this.firstValue - this.min) / (this.max - this.min)); | 
|         } | 
|       }, | 
|   | 
|       markList() { | 
|         if (!this.marks) { | 
|           return []; | 
|         } | 
|   | 
|         const marksKeys = Object.keys(this.marks); | 
|         return marksKeys.map(parseFloat) | 
|           .sort((a, b) => a - b) | 
|           .filter(point => point <= this.max && point >= this.min) | 
|           .map(point => ({ | 
|             point, | 
|             position: (point - this.min) * 100 / (this.max - this.min), | 
|             mark: this.marks[point] | 
|           })); | 
|       }, | 
|   | 
|       minValue() { | 
|         return Math.min(this.firstValue, this.secondValue); | 
|       }, | 
|   | 
|       maxValue() { | 
|         return Math.max(this.firstValue, this.secondValue); | 
|       }, | 
|   | 
|       barSize() { | 
|         return this.range | 
|           ? `${ 100 * (this.maxValue - this.minValue) / (this.max - this.min) }%` | 
|           : `${ 100 * (this.firstValue - this.min) / (this.max - this.min) }%`; | 
|       }, | 
|   | 
|       barStart() { | 
|         return this.range | 
|           ? `${ 100 * (this.minValue - this.min) / (this.max - this.min) }%` | 
|           : '0%'; | 
|       }, | 
|   | 
|       precision() { | 
|         let precisions = [this.min, this.max, this.step].map(item => { | 
|           let decimal = ('' + item).split('.')[1]; | 
|           return decimal ? decimal.length : 0; | 
|         }); | 
|         return Math.max.apply(null, precisions); | 
|       }, | 
|   | 
|       runwayStyle() { | 
|         return this.vertical ? { height: this.height } : {}; | 
|       }, | 
|   | 
|       barStyle() { | 
|         return this.vertical | 
|           ? { | 
|             height: this.barSize, | 
|             bottom: this.barStart | 
|           } : { | 
|             width: this.barSize, | 
|             left: this.barStart | 
|           }; | 
|       }, | 
|   | 
|       sliderDisabled() { | 
|         return this.disabled || (this.elForm || {}).disabled; | 
|       } | 
|     }, | 
|   | 
|     mounted() { | 
|       let valuetext; | 
|       if (this.range) { | 
|         if (Array.isArray(this.value)) { | 
|           this.firstValue = Math.max(this.min, this.value[0]); | 
|           this.secondValue = Math.min(this.max, this.value[1]); | 
|         } else { | 
|           this.firstValue = this.min; | 
|           this.secondValue = this.max; | 
|         } | 
|         this.oldValue = [this.firstValue, this.secondValue]; | 
|         valuetext = `${this.firstValue}-${this.secondValue}`; | 
|       } else { | 
|         if (typeof this.value !== 'number' || isNaN(this.value)) { | 
|           this.firstValue = this.min; | 
|         } else { | 
|           this.firstValue = Math.min(this.max, Math.max(this.min, this.value)); | 
|         } | 
|         this.oldValue = this.firstValue; | 
|         valuetext = this.firstValue; | 
|       } | 
|       this.$el.setAttribute('aria-valuetext', valuetext); | 
|   | 
|       // label screen reader | 
|       this.$el.setAttribute('aria-label', this.label ? this.label : `slider between ${this.min} and ${this.max}`); | 
|   | 
|       this.resetSize(); | 
|       window.addEventListener('resize', this.resetSize); | 
|     }, | 
|   | 
|     beforeDestroy() { | 
|       window.removeEventListener('resize', this.resetSize); | 
|     } | 
|   }; | 
| </script> |