2016-10-28 17:35:28 +08:00
|
|
|
<template>
|
|
|
|
<div :class="classes">
|
2016-11-11 15:08:12 +08:00
|
|
|
<Input-number
|
|
|
|
v-if="!range && showInput"
|
|
|
|
:min="min"
|
|
|
|
:max="max"
|
|
|
|
:step="step"
|
2017-12-22 14:46:44 +01:00
|
|
|
:value="currentValue[0]"
|
2016-11-11 15:08:12 +08:00
|
|
|
:disabled="disabled"
|
|
|
|
@on-change="handleInputChange"></Input-number>
|
2017-03-03 15:52:19 +08:00
|
|
|
<div :class="[prefixCls + '-wrap']" ref="slider" @click.self="sliderClick">
|
2017-09-19 16:45:02 +08:00
|
|
|
<input type="hidden" :name="name" :value="currentValue">
|
2016-10-28 17:35:28 +08:00
|
|
|
<template v-if="showStops">
|
2016-11-11 17:40:27 +08:00
|
|
|
<div :class="[prefixCls + '-stop']" v-for="item in stops" :style="{ 'left': item + '%' }" @click.self="sliderClick"></div>
|
2016-10-28 17:35:28 +08:00
|
|
|
</template>
|
2016-11-11 17:40:27 +08:00
|
|
|
<div :class="[prefixCls + '-bar']" :style="barStyle" @click.self="sliderClick"></div>
|
2017-11-11 10:16:43 +01:00
|
|
|
<div
|
|
|
|
:class="[prefixCls + '-button-wrap']"
|
|
|
|
:style="{left: minPosition + '%'}"
|
|
|
|
@touchstart="onPointerDown($event, 'min')"
|
|
|
|
@mousedown="onPointerDown($event, 'min')">
|
|
|
|
<Tooltip :controlled="pointerDown === 'min'" placement="top" :content="tipFormat(currentValue[0])"
|
|
|
|
:disabled="tipDisabled" :always="showTip === 'always'" ref="minTooltip">
|
|
|
|
<div :class="minButtonClasses"></div>
|
|
|
|
</Tooltip>
|
|
|
|
</div>
|
|
|
|
<div v-if="range"
|
|
|
|
:class="[prefixCls + '-button-wrap']"
|
|
|
|
:style="{left: maxPosition + '%'}"
|
|
|
|
@touchstart="onPointerDown($event, 'max')"
|
|
|
|
@mousedown="onPointerDown($event, 'max')">
|
|
|
|
<Tooltip :controlled="pointerDown === 'max'" placement="top" :content="tipFormat(currentValue[1])"
|
|
|
|
:disabled="tipDisabled" :always="showTip === 'always'" ref="maxTooltip">
|
|
|
|
<div :class="maxButtonClasses"></div>
|
|
|
|
</Tooltip>
|
|
|
|
</div>
|
2016-10-28 17:35:28 +08:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
<script>
|
|
|
|
import InputNumber from '../../components/input-number/input-number.vue';
|
|
|
|
import Tooltip from '../../components/tooltip/tooltip.vue';
|
2017-02-21 11:41:28 -06:00
|
|
|
import { getStyle, oneOf } from '../../utils/assist';
|
2017-07-10 13:52:53 +08:00
|
|
|
import { on, off } from '../../utils/dom';
|
2017-03-09 11:14:40 +08:00
|
|
|
import Emitter from '../../mixins/emitter';
|
2016-10-28 17:35:28 +08:00
|
|
|
|
|
|
|
const prefixCls = 'ivu-slider';
|
|
|
|
|
|
|
|
export default {
|
2017-03-03 17:46:09 +08:00
|
|
|
name: 'Slider',
|
2017-03-09 11:14:40 +08:00
|
|
|
mixins: [ Emitter ],
|
2016-10-28 17:35:28 +08:00
|
|
|
components: { InputNumber, Tooltip },
|
|
|
|
props: {
|
|
|
|
min: {
|
|
|
|
type: Number,
|
|
|
|
default: 0
|
|
|
|
},
|
|
|
|
max: {
|
|
|
|
type: Number,
|
|
|
|
default: 100
|
|
|
|
},
|
|
|
|
step: {
|
|
|
|
type: Number,
|
|
|
|
default: 1
|
|
|
|
},
|
|
|
|
range: {
|
|
|
|
type: Boolean,
|
|
|
|
default: false
|
|
|
|
},
|
|
|
|
value: {
|
|
|
|
type: [Number, Array],
|
|
|
|
default: 0
|
|
|
|
},
|
|
|
|
disabled: {
|
|
|
|
type: Boolean,
|
|
|
|
default: false
|
|
|
|
},
|
|
|
|
showInput: {
|
|
|
|
type: Boolean,
|
|
|
|
default: false
|
|
|
|
},
|
|
|
|
showStops: {
|
|
|
|
type: Boolean,
|
|
|
|
default: false
|
|
|
|
},
|
|
|
|
tipFormat: {
|
|
|
|
type: Function,
|
|
|
|
default (val) {
|
|
|
|
return val;
|
|
|
|
}
|
2017-02-21 11:41:28 -06:00
|
|
|
},
|
|
|
|
showTip: {
|
|
|
|
type: String,
|
|
|
|
default: 'hover',
|
|
|
|
validator (value) {
|
|
|
|
return oneOf(value, ['hover', 'always', 'never']);
|
|
|
|
}
|
2017-09-19 16:45:02 +08:00
|
|
|
},
|
|
|
|
name: {
|
|
|
|
type: String
|
2016-10-28 17:35:28 +08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
data () {
|
2017-11-11 10:16:43 +01:00
|
|
|
const val = this.checkLimits(Array.isArray(this.value) ? this.value : [this.value]);
|
2016-10-28 17:35:28 +08:00
|
|
|
return {
|
2016-11-11 15:08:12 +08:00
|
|
|
prefixCls: prefixCls,
|
2017-11-11 10:16:43 +01:00
|
|
|
currentValue: val,
|
2016-11-11 15:08:12 +08:00
|
|
|
dragging: false,
|
2017-11-11 10:16:43 +01:00
|
|
|
pointerDown: '',
|
2016-11-11 15:08:12 +08:00
|
|
|
startX: 0,
|
|
|
|
currentX: 0,
|
|
|
|
startPos: 0,
|
2017-11-11 10:16:43 +01:00
|
|
|
oldValue: val
|
2016-12-25 22:49:42 +08:00
|
|
|
};
|
2016-10-28 17:35:28 +08:00
|
|
|
},
|
2017-03-03 15:52:19 +08:00
|
|
|
watch: {
|
|
|
|
value (val) {
|
2017-11-11 10:16:43 +01:00
|
|
|
val = this.checkLimits(Array.isArray(val) ? val : [val]);
|
|
|
|
if (val[0] !== this.currentValue[0] || val[1] !== this.currentValue[1]) {
|
|
|
|
this.currentValue = val;
|
|
|
|
}
|
2017-03-03 15:52:19 +08:00
|
|
|
},
|
|
|
|
currentValue (val) {
|
|
|
|
this.$nextTick(() => {
|
2017-11-11 10:16:43 +01:00
|
|
|
this.$refs.minTooltip.updatePopper();
|
2017-03-03 15:52:19 +08:00
|
|
|
if (this.range) {
|
2017-11-11 10:16:43 +01:00
|
|
|
this.$refs.maxTooltip.updatePopper();
|
2017-03-03 15:52:19 +08:00
|
|
|
}
|
|
|
|
});
|
2017-11-11 10:16:43 +01:00
|
|
|
const exportValue = this.range ? val : val[0];
|
|
|
|
this.$emit('input', exportValue);
|
|
|
|
this.$emit('on-input', exportValue);
|
2017-03-03 15:52:19 +08:00
|
|
|
}
|
|
|
|
},
|
2016-10-28 17:35:28 +08:00
|
|
|
computed: {
|
|
|
|
classes () {
|
|
|
|
return [
|
|
|
|
`${prefixCls}`,
|
|
|
|
{
|
2016-11-11 15:08:12 +08:00
|
|
|
[`${prefixCls}-input`]: this.showInput && !this.range,
|
2016-10-28 17:35:28 +08:00
|
|
|
[`${prefixCls}-range`]: this.range,
|
|
|
|
[`${prefixCls}-disabled`]: this.disabled
|
|
|
|
}
|
2016-12-25 22:49:42 +08:00
|
|
|
];
|
2016-10-28 17:35:28 +08:00
|
|
|
},
|
2017-11-11 10:16:43 +01:00
|
|
|
minButtonClasses () {
|
2016-11-11 15:08:12 +08:00
|
|
|
return [
|
|
|
|
`${prefixCls}-button`,
|
|
|
|
{
|
2017-11-11 10:16:43 +01:00
|
|
|
[`${prefixCls}-button-dragging`]: this.pointerDown === 'min'
|
2016-11-11 15:08:12 +08:00
|
|
|
}
|
|
|
|
];
|
|
|
|
},
|
2017-11-11 10:16:43 +01:00
|
|
|
maxButtonClasses () {
|
2016-11-11 15:08:12 +08:00
|
|
|
return [
|
|
|
|
`${prefixCls}-button`,
|
|
|
|
{
|
2017-11-11 10:16:43 +01:00
|
|
|
[`${prefixCls}-button-dragging`]: this.pointerDown === 'max'
|
2016-11-11 15:08:12 +08:00
|
|
|
}
|
|
|
|
];
|
|
|
|
},
|
2017-11-11 10:16:43 +01:00
|
|
|
minPosition () {
|
|
|
|
const val = this.currentValue;
|
2018-01-30 08:24:39 +01:00
|
|
|
return (val[0] - this.min) / this.valueRange * 100;
|
2017-11-11 10:16:43 +01:00
|
|
|
},
|
|
|
|
maxPosition: function () {
|
|
|
|
const val = this.currentValue;
|
|
|
|
|
2018-01-30 08:24:39 +01:00
|
|
|
return (val[1] - this.min) / this.valueRange * 100;
|
2016-11-11 15:08:12 +08:00
|
|
|
},
|
2016-10-28 17:35:28 +08:00
|
|
|
barStyle () {
|
2018-01-09 06:07:23 +01:00
|
|
|
|
2017-11-11 10:16:43 +01:00
|
|
|
const style = {
|
2018-01-30 08:24:39 +01:00
|
|
|
width: (this.currentValue[0] - this.min) / this.valueRange * 100 + '%'
|
2017-11-11 10:16:43 +01:00
|
|
|
};
|
2016-10-28 17:35:28 +08:00
|
|
|
|
|
|
|
if (this.range) {
|
2018-01-30 08:24:39 +01:00
|
|
|
style.left = (this.currentValue[0] - this.min) / this.valueRange * 100 + '%';
|
|
|
|
style.width = (this.currentValue[1] - this.currentValue[0]) / this.valueRange * 100 + '%';
|
2016-10-28 17:35:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return style;
|
|
|
|
},
|
2017-11-11 10:16:43 +01:00
|
|
|
stops () {
|
2018-01-30 08:24:39 +01:00
|
|
|
let stopCount = this.valueRange / this.step;
|
2016-11-11 16:14:25 +08:00
|
|
|
let result = [];
|
2018-01-30 08:24:39 +01:00
|
|
|
let stepWidth = 100 * this.step / this.valueRange;
|
2016-11-11 16:14:25 +08:00
|
|
|
for (let i = 1; i < stopCount; i++) {
|
|
|
|
result.push(i * stepWidth);
|
|
|
|
}
|
|
|
|
return result;
|
2016-11-11 15:08:12 +08:00
|
|
|
},
|
|
|
|
sliderWidth () {
|
2017-03-03 15:52:19 +08:00
|
|
|
return parseInt(getStyle(this.$refs.slider, 'width'), 10);
|
2017-02-21 11:41:28 -06:00
|
|
|
},
|
|
|
|
tipDisabled () {
|
2017-03-03 15:52:19 +08:00
|
|
|
return this.tipFormat(this.currentValue[0]) === null || this.showTip === 'never';
|
2018-01-30 08:24:39 +01:00
|
|
|
},
|
|
|
|
valueRange(){
|
|
|
|
return this.max - this.min;
|
2016-10-28 17:35:28 +08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
methods: {
|
2017-11-11 10:16:43 +01:00
|
|
|
getPointerX (e) {
|
|
|
|
return e.type.indexOf('touch') !== -1 ? e.touches[0].clientX : e.clientX;
|
2016-11-11 15:08:12 +08:00
|
|
|
},
|
2017-11-11 10:16:43 +01:00
|
|
|
checkLimits ([min, max]) {
|
2018-01-30 08:24:39 +01:00
|
|
|
min = Math.max(this.min, min);
|
|
|
|
min = Math.min(this.max, min);
|
2016-10-28 17:35:28 +08:00
|
|
|
|
2018-01-30 08:24:39 +01:00
|
|
|
max = Math.max(this.min, min, max);
|
|
|
|
max = Math.min(this.max, max);
|
2017-11-11 10:16:43 +01:00
|
|
|
return [min, max];
|
2016-11-11 15:08:12 +08:00
|
|
|
},
|
2017-11-11 10:16:43 +01:00
|
|
|
onPointerDown (event, type) {
|
2016-11-11 15:08:12 +08:00
|
|
|
if (this.disabled) return;
|
2016-11-11 18:28:13 +08:00
|
|
|
event.preventDefault();
|
2017-11-11 10:16:43 +01:00
|
|
|
this.pointerDown = type;
|
|
|
|
|
|
|
|
this.onPointerDragStart(event);
|
|
|
|
on(window, 'mousemove', this.onPointerDrag);
|
|
|
|
on(window, 'touchmove', this.onPointerDrag);
|
|
|
|
on(window, 'mouseup', this.onPointerDragEnd);
|
|
|
|
on(window, 'touchend', this.onPointerDragEnd);
|
2016-11-11 15:08:12 +08:00
|
|
|
},
|
2017-11-11 10:16:43 +01:00
|
|
|
onPointerDragStart (event) {
|
2017-07-14 11:32:44 +08:00
|
|
|
this.dragging = false;
|
2017-11-11 10:16:43 +01:00
|
|
|
this.startX = this.getPointerX(event);
|
2018-01-30 08:24:39 +01:00
|
|
|
this.startPos = (this[`${this.pointerDown}Position`] * this.valueRange / 100) + this.min;
|
2016-11-11 15:08:12 +08:00
|
|
|
},
|
2017-11-11 10:16:43 +01:00
|
|
|
onPointerDrag (event) {
|
2017-07-14 11:32:44 +08:00
|
|
|
this.dragging = true;
|
2017-11-11 10:16:43 +01:00
|
|
|
this.$refs[`${this.pointerDown}Tooltip`].visible = true;
|
|
|
|
this.currentX = this.getPointerX(event);
|
2018-01-30 08:24:39 +01:00
|
|
|
const diff = (this.currentX - this.startX) / this.sliderWidth * this.valueRange;
|
2017-11-11 10:16:43 +01:00
|
|
|
|
2018-01-30 08:24:39 +01:00
|
|
|
this.changeButtonPosition(this.startPos + diff);
|
2016-11-11 15:08:12 +08:00
|
|
|
},
|
2017-11-11 10:16:43 +01:00
|
|
|
onPointerDragEnd () {
|
2016-11-11 15:08:12 +08:00
|
|
|
if (this.dragging) {
|
|
|
|
this.dragging = false;
|
2017-11-11 10:16:43 +01:00
|
|
|
this.$refs[`${this.pointerDown}Tooltip`].visible = false;
|
2016-11-11 15:08:12 +08:00
|
|
|
}
|
2017-11-11 10:16:43 +01:00
|
|
|
|
|
|
|
this.pointerDown = '';
|
|
|
|
off(window, 'mousemove', this.onPointerDrag);
|
|
|
|
off(window, 'touchmove', this.onPointerDrag);
|
|
|
|
off(window, 'mouseup', this.onPointerDragEnd);
|
|
|
|
off(window, 'touchend', this.onPointerDragEnd);
|
2016-11-11 15:08:12 +08:00
|
|
|
},
|
2017-11-11 10:16:43 +01:00
|
|
|
changeButtonPosition (newPos, forceType) {
|
|
|
|
const type = forceType || this.pointerDown;
|
|
|
|
const index = type === 'min' ? 0 : 1;
|
|
|
|
if (type === 'min') newPos = this.checkLimits([newPos, this.maxPosition])[0];
|
|
|
|
else newPos = this.checkLimits([this.minPosition, newPos])[1];
|
|
|
|
|
2018-01-30 10:24:29 +01:00
|
|
|
const modulus = newPos % this.step;
|
2017-11-11 10:16:43 +01:00
|
|
|
const value = this.currentValue;
|
2018-01-30 10:24:29 +01:00
|
|
|
value[index] = newPos - modulus;
|
2017-11-11 10:16:43 +01:00
|
|
|
this.currentValue = [...value];
|
|
|
|
|
2017-04-05 12:31:49 +08:00
|
|
|
if (!this.dragging) {
|
2017-11-11 10:16:43 +01:00
|
|
|
if (this.currentValue[index] !== this.oldValue[index]) {
|
|
|
|
const exportValue = this.range ? this.currentValue : this.currentValue[0];
|
|
|
|
this.$emit('on-change', exportValue);
|
|
|
|
this.dispatch('FormItem', 'on-form-change', exportValue);
|
|
|
|
this.oldValue[index] = this.currentValue[index];
|
2016-11-11 15:08:12 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2018-01-23 18:14:23 +08:00
|
|
|
sliderClick (event) {
|
2016-11-11 15:08:12 +08:00
|
|
|
if (this.disabled) return;
|
2017-11-11 10:16:43 +01:00
|
|
|
const currentX = this.getPointerX(event);
|
|
|
|
const sliderOffsetLeft = this.$refs.slider.getBoundingClientRect().left;
|
2018-01-30 08:24:39 +01:00
|
|
|
let newPos = ((currentX - sliderOffsetLeft) / this.sliderWidth * this.valueRange) + this.min;
|
2017-11-11 10:16:43 +01:00
|
|
|
|
|
|
|
if (!this.range || newPos <= this.minPosition) this.changeButtonPosition(newPos, 'min');
|
|
|
|
else if (newPos >= this.maxPosition) this.changeButtonPosition(newPos, 'max');
|
|
|
|
else this.changeButtonPosition(newPos, ((newPos - this.firstPosition) <= (this.secondPosition - newPos)) ? 'min' : 'max');
|
2016-11-11 15:08:12 +08:00
|
|
|
},
|
|
|
|
|
2017-11-11 10:16:43 +01:00
|
|
|
handleInputChange (val) {
|
|
|
|
this.currentValue = [val, this.currentValue[1]];
|
|
|
|
const exportValue = this.range ? this.currentValue : this.currentValue[0];
|
|
|
|
this.$emit('on-change', exportValue);
|
|
|
|
this.dispatch('FormItem', 'on-form-change', exportValue);
|
2016-11-11 15:08:12 +08:00
|
|
|
},
|
2018-01-23 18:14:23 +08:00
|
|
|
},
|
|
|
|
mounted () {
|
|
|
|
// #2852
|
|
|
|
this.$on('on-visible-change', (val) => {
|
|
|
|
if (val && this.showTip === 'always') {
|
|
|
|
this.$refs.minTooltip.doDestroy();
|
|
|
|
if (this.range) {
|
|
|
|
this.$refs.maxTooltip.doDestroy();
|
|
|
|
}
|
|
|
|
this.$nextTick(() => {
|
|
|
|
this.$refs.minTooltip.updatePopper();
|
|
|
|
if (this.range) {
|
|
|
|
this.$refs.maxTooltip.updatePopper();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
2016-10-28 17:35:28 +08:00
|
|
|
}
|
2016-12-25 22:49:42 +08:00
|
|
|
};
|
|
|
|
</script>
|