mirror of https://github.com/ElemeFE/element
				
				
				
			Slider: add range support (#2751)
							parent
							
								
									7f6d698f72
								
							
						
					
					
						commit
						450cf81ded
					
				|  | @ -7,7 +7,8 @@ | |||
|         value3: 42, | ||||
|         value4: 0, | ||||
|         value5: 0, | ||||
|         value6: 0 | ||||
|         value6: 0, | ||||
|         value7: [4, 8] | ||||
|       }; | ||||
|     } | ||||
|   } | ||||
|  | @ -119,6 +120,35 @@ Set value via a input box. | |||
| ``` | ||||
| ::: | ||||
| 
 | ||||
| ### Range selection | ||||
| 
 | ||||
| Selecting a range of values is supported. | ||||
| 
 | ||||
| :::demo Setting the `range` attribute activates range mode, where the binding value is an array made up of two boundary values. | ||||
| ```html | ||||
| <template> | ||||
|   <div class="block"> | ||||
|     <el-slider | ||||
|       v-model="value7" | ||||
|       range | ||||
|       show-stops | ||||
|       :max="10"> | ||||
|     </el-slider> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
|   export default { | ||||
|     data() { | ||||
|       return { | ||||
|         value7: [4, 8] | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
| ``` | ||||
| ::: | ||||
| 
 | ||||
| ## Attributes | ||||
| | Attribute      | Description          | Type      | Accepted Values       | Default  | | ||||
| |---------- |-------------- |---------- |--------------------------------  |-------- | | ||||
|  | @ -126,9 +156,10 @@ Set value via a input box. | |||
| | max | maximum value | number | — | 100 | | ||||
| | disabled | whether Slider is disabled | boolean | — | false | | ||||
| | step | step size | number | — | 1 | | ||||
| | show-input | whether to display an input box | boolean | — | false | | ||||
| | show-input | whether to display an input box, works when `range` is false | boolean | — | false | | ||||
| | show-input-controls | whether to display control buttons when `show-input` is true | boolean | — | true | | ||||
| | show-stops | whether to display breakpoints | boolean | — | false | | ||||
| | range | whether to select a range | boolean | — | false | | ||||
| 
 | ||||
| ## Events | ||||
| | Event Name | Description | Parameters | | ||||
|  |  | |||
|  | @ -7,7 +7,8 @@ | |||
|         value3: 42, | ||||
|         value4: 0, | ||||
|         value5: 0, | ||||
|         value6: 0 | ||||
|         value6: 0, | ||||
|         value7: [4, 8] | ||||
|       }; | ||||
|     } | ||||
|   } | ||||
|  | @ -143,6 +144,35 @@ | |||
| ``` | ||||
| ::: | ||||
| 
 | ||||
| ### 范围选择 | ||||
| 
 | ||||
| 支持选择某一数值范围 | ||||
| 
 | ||||
| :::demo 设置`range`即可开启范围选择,此时绑定值是一个数组,其元素分别为最小边界值和最大边界值 | ||||
| ```html | ||||
| <template> | ||||
|   <div class="block"> | ||||
|     <el-slider | ||||
|       v-model="value7" | ||||
|       range | ||||
|       show-stops | ||||
|       :max="10"> | ||||
|     </el-slider> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
|   export default { | ||||
|     data() { | ||||
|       return { | ||||
|         value7: [4, 8] | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
| ``` | ||||
| ::: | ||||
| 
 | ||||
| ### Attributes | ||||
| | 参数      | 说明          | 类型      | 可选值                           | 默认值  | | ||||
| |---------- |-------------- |---------- |--------------------------------  |-------- | | ||||
|  | @ -150,9 +180,10 @@ | |||
| | max | 最大值 | number | — | 100 | | ||||
| | disabled | 是否禁用 | boolean | — | false | | ||||
| | step | 步长 | number | — | 1 | | ||||
| | show-input | 是否显示输入框 | boolean | — | false | | ||||
| | show-input | 是否显示输入框,仅在非范围选择时有效 | boolean | — | false | | ||||
| | show-input-controls | 在显示输入框的情况下,是否显示输入框的控制按钮 | boolean | — | true| | ||||
| | show-stops | 是否显示间断点 | boolean | — | false | | ||||
| | range | 是否为范围选择 | boolean | — | false | | ||||
| 
 | ||||
| ### Events | ||||
| | 事件名称      | 说明    | 回调参数      | | ||||
|  |  | |||
|  | @ -0,0 +1,156 @@ | |||
| <template> | ||||
|   <div | ||||
|     class="el-slider__button-wrapper" | ||||
|     @mouseenter="handleMouseEnter" | ||||
|     @mouseleave="handleMouseLeave" | ||||
|     @mousedown="onButtonDown" | ||||
|     :class="{ 'hover': hovering, 'dragging': dragging }" | ||||
|     :style="{ left: currentPosition }" | ||||
|     ref="button"> | ||||
|     <el-tooltip placement="top" ref="tooltip"> | ||||
|       <span slot="content">{{ value }}</span> | ||||
|       <div class="el-slider__button" :class="{ 'hover': hovering, 'dragging': dragging }"></div> | ||||
|     </el-tooltip> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
|   import ElTooltip from 'element-ui/packages/tooltip'; | ||||
| 
 | ||||
|   export default { | ||||
|     name: 'ElSliderButton', | ||||
| 
 | ||||
|     components: { | ||||
|       ElTooltip | ||||
|     }, | ||||
| 
 | ||||
|     props: { | ||||
|       value: { | ||||
|         type: Number, | ||||
|         default: 0 | ||||
|       } | ||||
|     }, | ||||
| 
 | ||||
|     data() { | ||||
|       return { | ||||
|         hovering: false, | ||||
|         dragging: false, | ||||
|         startX: 0, | ||||
|         currentX: 0, | ||||
|         startPosition: 0, | ||||
|         newPosition: null, | ||||
|         oldValue: this.value | ||||
|       }; | ||||
|     }, | ||||
| 
 | ||||
|     computed: { | ||||
|       disabled() { | ||||
|         return this.$parent.disabled; | ||||
|       }, | ||||
| 
 | ||||
|       max() { | ||||
|         return this.$parent.max; | ||||
|       }, | ||||
| 
 | ||||
|       min() { | ||||
|         return this.$parent.min; | ||||
|       }, | ||||
| 
 | ||||
|       step() { | ||||
|         return this.$parent.step; | ||||
|       }, | ||||
| 
 | ||||
|       precision() { | ||||
|         return this.$parent.precision; | ||||
|       }, | ||||
| 
 | ||||
|       currentPosition() { | ||||
|         return `${ (this.value - this.min) / (this.max - this.min) * 100 }%`; | ||||
|       } | ||||
|     }, | ||||
| 
 | ||||
|     watch: { | ||||
|       dragging(val) { | ||||
|         this.$parent.dragging = val; | ||||
|       } | ||||
|     }, | ||||
| 
 | ||||
|     methods: { | ||||
|       showTooltip() { | ||||
|         this.$refs.tooltip && (this.$refs.tooltip.showPopper = true); | ||||
|       }, | ||||
| 
 | ||||
|       hideTooltip() { | ||||
|         this.$refs.tooltip && (this.$refs.tooltip.showPopper = false); | ||||
|       }, | ||||
| 
 | ||||
|       handleMouseEnter() { | ||||
|         this.hovering = true; | ||||
|         this.showTooltip(); | ||||
|       }, | ||||
|    | ||||
|       handleMouseLeave() { | ||||
|         this.hovering = false; | ||||
|         this.hideTooltip(); | ||||
|       }, | ||||
| 
 | ||||
|       onButtonDown(event) { | ||||
|         if (this.disabled) return; | ||||
|         this.onDragStart(event); | ||||
|         window.addEventListener('mousemove', this.onDragging); | ||||
|         window.addEventListener('mouseup', this.onDragEnd); | ||||
|         window.addEventListener('contextmenu', this.onDragEnd); | ||||
|       }, | ||||
| 
 | ||||
|       onDragStart(event) { | ||||
|         this.dragging = true; | ||||
|         this.startX = event.clientX; | ||||
|         this.startPosition = parseInt(this.currentPosition, 10); | ||||
|       }, | ||||
| 
 | ||||
|       onDragging(event) { | ||||
|         if (this.dragging) { | ||||
|           this.showTooltip(); | ||||
|           this.currentX = event.clientX; | ||||
|           const diff = (this.currentX - this.startX) / this.$parent.$sliderWidth * 100; | ||||
|           this.newPosition = this.startPosition + diff; | ||||
|           this.setPosition(this.newPosition); | ||||
|         } | ||||
|       }, | ||||
| 
 | ||||
|       onDragEnd() { | ||||
|         if (this.dragging) { | ||||
|           /* | ||||
|            * 防止在 mouseup 后立即触发 click,导致滑块有几率产生一小段位移 | ||||
|            * 不使用 preventDefault 是因为 mouseup 和 click 没有注册在同一个 DOM 上 | ||||
|            */ | ||||
|           setTimeout(() => { | ||||
|             this.dragging = false; | ||||
|             this.hideTooltip(); | ||||
|             this.setPosition(this.newPosition); | ||||
|           }, 0); | ||||
|           window.removeEventListener('mousemove', this.onDragging); | ||||
|           window.removeEventListener('mouseup', this.onDragEnd); | ||||
|           window.removeEventListener('contextmenu', this.onDragEnd); | ||||
|         } | ||||
|       }, | ||||
| 
 | ||||
|       setPosition(newPosition) { | ||||
|         if (newPosition < 0) { | ||||
|           newPosition = 0; | ||||
|         } else if (newPosition > 100) { | ||||
|           newPosition = 100; | ||||
|         } | ||||
|         const lengthPerStep = 100 / ((this.max - this.min) / this.step); | ||||
|         const steps = Math.round(newPosition / lengthPerStep); | ||||
|         let value = steps * lengthPerStep * (this.max - this.min) * 0.01 + this.min; | ||||
|         value = parseFloat(value.toFixed(this.precision)); | ||||
|         this.$emit('input', value); | ||||
|         this.$refs.tooltip && this.$refs.tooltip.updatePopper(); | ||||
|         if (!this.dragging && this.value !== this.oldValue) { | ||||
|           this.oldValue = this.value; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   }; | ||||
| </script> | ||||
|  | @ -2,9 +2,8 @@ | |||
|   <div class="el-slider"> | ||||
|     <el-input-number | ||||
|       v-model="inputValue" | ||||
|       v-if="showInput" | ||||
|       v-if="showInput && !range" | ||||
|       class="el-slider__input" | ||||
|       @keyup.native="onInputChange" | ||||
|       ref="input" | ||||
|       :step="step" | ||||
|       :disabled="disabled" | ||||
|  | @ -15,29 +14,37 @@ | |||
|     </el-input-number> | ||||
|     <div class="el-slider__runway" | ||||
|       :class="{ 'show-input': showInput, 'disabled': disabled }" | ||||
|       @click="onSliderClick" ref="slider"> | ||||
|       <div class="el-slider__bar" :style="{ width: currentPosition }"></div> | ||||
|       @click="onSliderClick" | ||||
|       ref="slider"> | ||||
|       <div | ||||
|         class="el-slider__button-wrapper" | ||||
|         @mouseenter="handleMouseEnter" | ||||
|         @mouseleave="handleMouseLeave" | ||||
|         @mousedown="onButtonDown" | ||||
|         :class="{ 'hover': hovering, 'dragging': dragging }" | ||||
|         :style="{left: currentPosition}" | ||||
|         ref="button"> | ||||
|         <el-tooltip placement="top" ref="tooltip"> | ||||
|           <span slot="content">{{ value }}</span> | ||||
|           <div class="el-slider__button" :class="{ 'hover': hovering, 'dragging': dragging }"></div> | ||||
|         </el-tooltip> | ||||
|         class="el-slider__bar" | ||||
|         :style="{ | ||||
|           width: barWidth, | ||||
|           left: barLeft | ||||
|         }"> | ||||
|       </div> | ||||
|       <slider-button | ||||
|         v-model="firstValue" | ||||
|         ref="button1"> | ||||
|       </slider-button> | ||||
|       <slider-button | ||||
|         v-model="secondValue" | ||||
|         ref="button2" | ||||
|         v-if="range"> | ||||
|       </slider-button> | ||||
|       <div | ||||
|         class="el-slider__stop" | ||||
|         v-for="item in stops" | ||||
|         :style="{ 'left': item + '%' }" | ||||
|         v-if="showStops"> | ||||
|       </div> | ||||
|       <div class="el-slider__stop" v-for="item in stops" :style="{ 'left': item + '%' }" v-if="showStops"></div> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script type="text/babel"> | ||||
|   import ElInputNumber from 'element-ui/packages/input-number'; | ||||
|   import ElTooltip from 'element-ui/packages/tooltip'; | ||||
|   import SliderButton from './button.vue'; | ||||
|   import { getStyle } from 'element-ui/src/utils/dom'; | ||||
| 
 | ||||
|   export default { | ||||
|  | @ -56,12 +63,8 @@ | |||
|         type: Number, | ||||
|         default: 1 | ||||
|       }, | ||||
|       defaultValue: { | ||||
|         type: Number, | ||||
|         default: 0 | ||||
|       }, | ||||
|       value: { | ||||
|         type: Number, | ||||
|         type: [Number, Array], | ||||
|         default: 0 | ||||
|       }, | ||||
|       showInput: { | ||||
|  | @ -79,142 +82,136 @@ | |||
|       disabled: { | ||||
|         type: Boolean, | ||||
|         default: false | ||||
|       }, | ||||
|       range: { | ||||
|         type: Boolean, | ||||
|         default: false | ||||
|       } | ||||
|     }, | ||||
| 
 | ||||
|     components: { | ||||
|       ElInputNumber, | ||||
|       ElTooltip | ||||
|       SliderButton | ||||
|     }, | ||||
| 
 | ||||
|     data() { | ||||
|       return { | ||||
|         firstValue: null, | ||||
|         secondValue: null, | ||||
|         oldValue: null, | ||||
|         precision: 0, | ||||
|         inputValue: null, | ||||
|         timeout: null, | ||||
|         hovering: false, | ||||
|         dragging: false, | ||||
|         startX: 0, | ||||
|         currentX: 0, | ||||
|         startPos: 0, | ||||
|         newPos: null, | ||||
|         oldValue: this.value, | ||||
|         currentPosition: (this.value - this.min) / (this.max - this.min) * 100 + '%' | ||||
|         dragging: false | ||||
|       }; | ||||
|     }, | ||||
| 
 | ||||
|     watch: { | ||||
|       inputValue(val) { | ||||
|         this.$emit('input', Number(val)); | ||||
|         this.firstValue = val; | ||||
|       }, | ||||
| 
 | ||||
|       value(val) { | ||||
|         this.$nextTick(() => { | ||||
|           this.updatePopper(); | ||||
|         }); | ||||
|         if (typeof val !== 'number' || isNaN(val) || val < this.min) { | ||||
|           this.$emit('input', this.min); | ||||
|       value(val, oldVal) { | ||||
|         if (this.dragging || | ||||
|           Array.isArray(val) && | ||||
|           Array.isArray(oldVal) && | ||||
|           val.every((item, index) => item === oldVal[index])) { | ||||
|           return; | ||||
|         } | ||||
|         if (val > this.max) { | ||||
|           this.$emit('input', this.max); | ||||
|           return; | ||||
|         this.setValues(); | ||||
|       }, | ||||
| 
 | ||||
|       dragging(val) { | ||||
|         if (!val) { | ||||
|           this.setValues(); | ||||
|         } | ||||
|         this.inputValue = val; | ||||
|         this.setPosition((val - this.min) * 100 / (this.max - this.min)); | ||||
|       }, | ||||
| 
 | ||||
|       firstValue(val) { | ||||
|         if (this.range) { | ||||
|           this.$emit('input', [this.minValue, this.maxValue]); | ||||
|         } else { | ||||
|           this.inputValue = val; | ||||
|           this.$emit('input', val); | ||||
|         } | ||||
|       }, | ||||
| 
 | ||||
|       secondValue() { | ||||
|         if (this.range) { | ||||
|           this.$emit('input', [this.minValue, this.maxValue]); | ||||
|         } | ||||
|       }, | ||||
| 
 | ||||
|       min() { | ||||
|         this.setValues(); | ||||
|       }, | ||||
| 
 | ||||
|       max() { | ||||
|         this.setValues(); | ||||
|       } | ||||
|     }, | ||||
| 
 | ||||
|     methods: { | ||||
|       handleMouseEnter() { | ||||
|         this.hovering = true; | ||||
|         this.$refs.tooltip.showPopper = true; | ||||
|       }, | ||||
| 
 | ||||
|       handleMouseLeave() { | ||||
|         this.hovering = false; | ||||
|         this.$refs.tooltip.showPopper = false; | ||||
|       }, | ||||
| 
 | ||||
|       updatePopper() { | ||||
|         this.$refs.tooltip.updatePopper(); | ||||
|       }, | ||||
| 
 | ||||
|       setPosition(newPos) { | ||||
|         if (newPos < 0) { | ||||
|           newPos = 0; | ||||
|         } else if (newPos > 100) { | ||||
|           newPos = 100; | ||||
|       valueChanged() { | ||||
|         if (this.range) { | ||||
|           return ![this.minValue, this.maxValue] | ||||
|             .every((item, index) => item === this.oldValue[index]); | ||||
|         } else { | ||||
|           return this.value !== this.oldValue; | ||||
|         } | ||||
| 
 | ||||
|         const lengthPerStep = 100 / ((this.max - this.min) / this.step); | ||||
|         const steps = Math.round(newPos / lengthPerStep); | ||||
|         let value = steps * lengthPerStep * (this.max - this.min) * 0.01 + this.min; | ||||
|         value = parseFloat(value.toFixed(this.precision)); | ||||
|         this.$emit('input', value); | ||||
|         this.currentPosition = (this.value - this.min) / (this.max - this.min) * 100 + '%'; | ||||
|         if (!this.dragging) { | ||||
|           if (this.value !== this.oldValue) { | ||||
|             this.$emit('change', this.value); | ||||
|             this.oldValue = this.value; | ||||
|       }, | ||||
|       setValues() { | ||||
|         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.$emit('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.$emit('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.disabled || this.dragging) return; | ||||
|         const sliderOffsetLeft = this.$refs.slider.getBoundingClientRect().left; | ||||
|         this.setPosition((event.clientX - sliderOffsetLeft) / this.$sliderWidth * 100); | ||||
|       }, | ||||
| 
 | ||||
|       onInputChange() { | ||||
|         if (this.value === '') { | ||||
|           return; | ||||
|         } | ||||
|         if (!isNaN(this.value)) { | ||||
|           this.setPosition((this.value - this.min) * 100 / (this.max - this.min)); | ||||
|         } | ||||
|       }, | ||||
| 
 | ||||
|       onDragStart(event) { | ||||
|         this.dragging = true; | ||||
|         this.startX = event.clientX; | ||||
|         this.startPos = parseInt(this.currentPosition, 10); | ||||
|       }, | ||||
| 
 | ||||
|       onDragging(event) { | ||||
|         if (this.dragging) { | ||||
|           this.$refs.tooltip.showPopper = true; | ||||
|           this.currentX = event.clientX; | ||||
|           const diff = (this.currentX - this.startX) / this.$sliderWidth * 100; | ||||
|           this.newPos = this.startPos + diff; | ||||
|           this.setPosition(this.newPos); | ||||
|         } | ||||
|       }, | ||||
| 
 | ||||
|       onDragEnd() { | ||||
|         if (this.dragging) { | ||||
|           /* | ||||
|            * 防止在 mouseup 后立即触发 click,导致滑块有几率产生一小段位移 | ||||
|            * 不使用 preventDefault 是因为 mouseup 和 click 没有注册在同一个 DOM 上 | ||||
|            */ | ||||
|           setTimeout(() => { | ||||
|             this.dragging = false; | ||||
|             this.$refs.tooltip.showPopper = false; | ||||
|             this.setPosition(this.newPos); | ||||
|           }, 0); | ||||
|           window.removeEventListener('mousemove', this.onDragging); | ||||
|           window.removeEventListener('mouseup', this.onDragEnd); | ||||
|           window.removeEventListener('contextmenu', this.onDragEnd); | ||||
|         } | ||||
|       }, | ||||
| 
 | ||||
|       onButtonDown(event) { | ||||
|         if (this.disabled) return; | ||||
|         this.onDragStart(event); | ||||
|         window.addEventListener('mousemove', this.onDragging); | ||||
|         window.addEventListener('mouseup', this.onDragEnd); | ||||
|         window.addEventListener('contextmenu', this.onDragEnd); | ||||
|       } | ||||
|     }, | ||||
| 
 | ||||
|  | @ -224,31 +221,67 @@ | |||
|       }, | ||||
| 
 | ||||
|       stops() { | ||||
|         const stopCount = (this.max - this.value) / this.step; | ||||
|         const currentLeft = parseFloat(this.currentPosition); | ||||
|         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(currentLeft + i * stepWidth); | ||||
|           result.push(i * stepWidth); | ||||
|         } | ||||
|         return result; | ||||
|         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)); | ||||
|         } | ||||
|       }, | ||||
| 
 | ||||
|       minValue() { | ||||
|         return Math.min(this.firstValue, this.secondValue); | ||||
|       }, | ||||
| 
 | ||||
|       maxValue() { | ||||
|         return Math.max(this.firstValue, this.secondValue); | ||||
|       }, | ||||
| 
 | ||||
|       barWidth() { | ||||
|         return this.range | ||||
|           ? `${ 100 * (this.maxValue - this.minValue) / (this.max - this.min) }%` | ||||
|           : `${ 100 * (this.firstValue - this.min) / (this.max - this.min) }%`; | ||||
|       }, | ||||
| 
 | ||||
|       barLeft() { | ||||
|         return this.range | ||||
|           ? `${ 100 * (this.minValue - this.min) / (this.max - this.min) }%` | ||||
|           : '0%'; | ||||
|       } | ||||
|     }, | ||||
| 
 | ||||
|     created() { | ||||
|       if (typeof this.value !== 'number' || | ||||
|         isNaN(this.value) || | ||||
|         this.value < this.min) { | ||||
|         this.$emit('input', this.min); | ||||
|       } else if (this.value > this.max) { | ||||
|         this.$emit('input', this.max); | ||||
|     mounted() { | ||||
|       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]; | ||||
|       } 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; | ||||
|       } | ||||
|       let precisions = [this.min, this.max, this.step].map(item => { | ||||
|         let decimal = ('' + item).split('.')[1]; | ||||
|         return decimal ? decimal.length : 0; | ||||
|       }); | ||||
|       this.precision = Math.max.apply(null, precisions); | ||||
|       this.inputValue = this.inputValue || this.value; | ||||
|       this.inputValue = this.inputValue || this.firstValue; | ||||
|     } | ||||
|   }; | ||||
| </script> | ||||
|  |  | |||
|  | @ -37,7 +37,7 @@ describe('Slider', () => { | |||
|           done(); | ||||
|         }); | ||||
|       }); | ||||
|     }, 100); | ||||
|     }, 10); | ||||
|   }); | ||||
| 
 | ||||
|   it('show tooltip', () => { | ||||
|  | @ -55,7 +55,7 @@ describe('Slider', () => { | |||
|         }; | ||||
|       } | ||||
|     }, true); | ||||
|     const slider = vm.$children[0]; | ||||
|     const slider = vm.$children[0].$children[0]; | ||||
|     slider.handleMouseEnter(); | ||||
|     expect(slider.$refs.tooltip.showPopper).to.true; | ||||
|     slider.handleMouseLeave(); | ||||
|  | @ -76,14 +76,14 @@ describe('Slider', () => { | |||
|         }; | ||||
|       } | ||||
|     }, true); | ||||
|     const slider = vm.$children[0]; | ||||
|     const slider = vm.$children[0].$children[0]; | ||||
|     slider.onButtonDown({ clientX: 0 }); | ||||
|     slider.onDragging({ clientX: 100 }); | ||||
|     slider.onDragEnd(); | ||||
|     setTimeout(() => { | ||||
|       slider.onButtonDown({ clientX: 0 }); | ||||
|       slider.onDragging({ clientX: 100 }); | ||||
|       slider.onDragEnd(); | ||||
|       expect(vm.value > 0).to.true; | ||||
|       done(); | ||||
|     }, 150); | ||||
|     }, 10); | ||||
|   }); | ||||
| 
 | ||||
|   it('step', done => { | ||||
|  | @ -100,14 +100,14 @@ describe('Slider', () => { | |||
|         }; | ||||
|       } | ||||
|     }, true); | ||||
|     const slider = vm.$children[0]; | ||||
|     const slider = vm.$children[0].$children[0]; | ||||
|     slider.onButtonDown({ clientX: 0 }); | ||||
|     slider.onDragging({ clientX: 100 }); | ||||
|     slider.onDragEnd(); | ||||
|     setTimeout(() => { | ||||
|       slider.onButtonDown({ clientX: 0 }); | ||||
|       slider.onDragging({ clientX: 100 }); | ||||
|       slider.onDragEnd(); | ||||
|       expect(vm.value > 0.4 && vm.value < 0.6).to.true; | ||||
|       done(); | ||||
|     }, 150); | ||||
|     }, 10); | ||||
|   }); | ||||
| 
 | ||||
|   it('click', done => { | ||||
|  | @ -130,8 +130,8 @@ describe('Slider', () => { | |||
|       setTimeout(() => { | ||||
|         expect(vm.value > 0).to.true; | ||||
|         done(); | ||||
|       }, 150); | ||||
|     }, 150); | ||||
|       }, 10); | ||||
|     }, 10); | ||||
|   }); | ||||
| 
 | ||||
|   it('disabled', done => { | ||||
|  | @ -148,15 +148,14 @@ describe('Slider', () => { | |||
|         }; | ||||
|       } | ||||
|     }, true); | ||||
|     const slider = vm.$children[0]; | ||||
|     const slider = vm.$children[0].$children[0]; | ||||
|     slider.onButtonDown({ clientX: 0 }); | ||||
|     slider.onDragging({ clientX: 100 }); | ||||
|     slider.onDragEnd(); | ||||
|     setTimeout(() => { | ||||
|       slider.onButtonDown({ clientX: 0 }); | ||||
|       slider.onDragging({ clientX: 100 }); | ||||
|       slider.onDragEnd(); | ||||
|       slider.onSliderClick({ clientX: 200 }); | ||||
|       expect(vm.value).to.equal(0); | ||||
|       done(); | ||||
|     }, 100); | ||||
|     }, 10); | ||||
|   }); | ||||
| 
 | ||||
|   it('show input', done => { | ||||
|  | @ -180,17 +179,145 @@ describe('Slider', () => { | |||
|       setTimeout(() => { | ||||
|         expect(vm.value).to.equal(40); | ||||
|         done(); | ||||
|       }, 150); | ||||
|     }, 150); | ||||
|       }, 10); | ||||
|     }, 10); | ||||
|   }); | ||||
| 
 | ||||
|   it('show stops', done => { | ||||
|   it('show stops', () => { | ||||
|     vm = createTest(Slider, { | ||||
|       showStops: true, | ||||
|       step: 10 | ||||
|     }, true); | ||||
|     const stops = vm.$el.querySelectorAll('.el-slider__stop'); | ||||
|     expect(stops.length).to.equal(9); | ||||
|     done(); | ||||
|   }); | ||||
| 
 | ||||
|   describe('range', () => { | ||||
|     it('basic ranged slider', () => { | ||||
|       vm = createVue({ | ||||
|         template: ` | ||||
|         <div> | ||||
|           <el-slider v-model="value" range></el-slider> | ||||
|         </div> | ||||
|       `, | ||||
| 
 | ||||
|         data() { | ||||
|           return { | ||||
|             value: [10, 20] | ||||
|           }; | ||||
|         } | ||||
|       }, true); | ||||
|       const buttons = vm.$children[0].$children; | ||||
|       expect(buttons.length).to.equal(2); | ||||
|     }); | ||||
| 
 | ||||
|     it('should not exceed min and max', done => { | ||||
|       vm = createVue({ | ||||
|         template: ` | ||||
|         <div> | ||||
|           <el-slider v-model="value" range :min="50"> | ||||
|           </el-slider> | ||||
|         </div> | ||||
|       `, | ||||
| 
 | ||||
|         data() { | ||||
|           return { | ||||
|             value: [50, 60] | ||||
|           }; | ||||
|         } | ||||
|       }, true); | ||||
|       setTimeout(() => { | ||||
|         vm.value = [40, 60]; | ||||
|         setTimeout(() => { | ||||
|           expect(vm.value).to.deep.equal([50, 60]); | ||||
|           vm.value = [50, 120]; | ||||
|           setTimeout(() => { | ||||
|             expect(vm.value).to.deep.equal([50, 100]); | ||||
|             done(); | ||||
|           }, 10); | ||||
|         }, 10); | ||||
|       }, 10); | ||||
|     }); | ||||
| 
 | ||||
|     it('click', done => { | ||||
|       vm = createVue({ | ||||
|         template: ` | ||||
|         <div style="width: 200px;"> | ||||
|           <el-slider range v-model="value"></el-slider> | ||||
|         </div> | ||||
|       `, | ||||
| 
 | ||||
|         data() { | ||||
|           return { | ||||
|             value: [0, 100] | ||||
|           }; | ||||
|         } | ||||
|       }, true); | ||||
|       const slider = vm.$children[0]; | ||||
|       setTimeout(() => { | ||||
|         slider.onSliderClick({ clientX: 100 }); | ||||
|         setTimeout(() => { | ||||
|           expect(vm.value[0] > 0).to.true; | ||||
|           expect(vm.value[1]).to.equal(100); | ||||
|           done(); | ||||
|         }, 10); | ||||
|       }, 10); | ||||
|     }); | ||||
| 
 | ||||
|     it('responsive to dynamic min and max', done => { | ||||
|       vm = createVue({ | ||||
|         template: ` | ||||
|         <div> | ||||
|           <el-slider v-model="value" range :min="min" :max="max"> | ||||
|           </el-slider> | ||||
|         </div> | ||||
|       `, | ||||
| 
 | ||||
|         data() { | ||||
|           return { | ||||
|             min: 0, | ||||
|             max: 100, | ||||
|             value: [50, 80] | ||||
|           }; | ||||
|         } | ||||
|       }, true); | ||||
|       setTimeout(() => { | ||||
|         vm.min = 60; | ||||
|         setTimeout(() => { | ||||
|           expect(vm.value).to.deep.equal([60, 80]); | ||||
|           vm.min = 30; | ||||
|           vm.max = 40; | ||||
|           setTimeout(() => { | ||||
|             expect(vm.value).to.deep.equal([40, 40]); | ||||
|             done(); | ||||
|           }, 10); | ||||
|         }, 10); | ||||
|       }, 10); | ||||
|     }); | ||||
| 
 | ||||
|     it('show stops', done => { | ||||
|       vm = createVue({ | ||||
|         template: ` | ||||
|         <div> | ||||
|           <el-slider | ||||
|             v-model="value" | ||||
|             range | ||||
|             :step="10" | ||||
|             show-stops></el-slider> | ||||
|         </div> | ||||
|       `, | ||||
| 
 | ||||
|         data() { | ||||
|           return { | ||||
|             value: [30, 60] | ||||
|           }; | ||||
|         } | ||||
|       }, true); | ||||
|       setTimeout(() => { | ||||
|         const stops = vm.$el.querySelectorAll('.el-slider__stop'); | ||||
|         expect(stops.length).to.equal(5); | ||||
|         done(); | ||||
|       }, 10); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 杨奕
						杨奕