绩效考核手机版
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

257 lines
7.7 KiB

<template>
<view :id="$sUid" class="s-slider" :class="c_class" :style="c_style" @click="onClick">
<view class="s-slider__bar" :style="bar_style">
<template v-if="isRange">
<view
class="s-slider__button-wrap-start"
@touchstart="onStartButtonTouchStart"
@touchmove.prevent="onTouchMove"
@touchend="onTouchEnd"
@touchcancel="onTouchEnd"
@click.stop
>
<slot name="startButton">
<view class="s-slider__button"></view>
</slot>
</view>
<view
class="s-slider__button-wrap-end"
@touchstart="onEndButtonTouchStart"
@touchmove.prevent="onTouchMove"
@touchend="onTouchEnd"
@touchcancel="onTouchEnd"
@click.stop
>
<slot name="endButton">
<view class="s-slider__button"></view>
</slot>
</view>
</template>
<view
v-else
class="s-slider__button-wrap"
@touchstart="onTouchStart"
@touchmove.prevent="onTouchMove"
@touchend="onTouchEnd"
@touchcancel="onTouchEnd"
@click.stop
>
<slot name="button">
<view class="s-slider__button" :style="button_style"></view>
</slot>
</view>
</view>
</view>
</template>
<script>
import componentMixin from '../../mixins/componentMixin';
import useTouch from '../../common/useTouch';
/**
* s-slider 滑块
* @description 该组件一般用于表单中,手动选择一个区间范围的场景。
* @property {Number|Array<Number>} value 双向绑定滑块选择值,为数组时为双滑块
* @property {Boolean} disabled 是否禁用滑块
* @property {Boolean} readonly 是否只读滑块
* @property {Boolean} vertical 是否垂直展示
* @property {Number|String} barHeight 进度条高度
* @property {String} inactiveColor 进度条非激活态颜色
* @property {String} activeColor 进度条激活态颜色
* @property {Number|String} min 最小值
* @property {Number|String} max 最大值
* @property {Number|String} step 步长,默认 1
* @property {Number|String} buttonSize 滑块按钮大小
* @property {String|Object} buttonStyle 滑块按钮样式
* @event {Function} change (value) 改变后的值
* @event {Function} start (event) 开始触发滑块移动
* @event {Function} move (event) 正在滑动中
* @event {Function} end (event) 滑动结束
* @example <s-slider :v-model="50">
*/
export default {
name: 'SSlider',
mixins: [componentMixin],
props: {
value: {
type: [Number, Array],
default: 0,
},
disabled: Boolean,
readonly: Boolean,
vertical: Boolean,
barHeight: [Number, String],
inactiveColor: String,
activeColor: String,
min: {
type: [Number, String],
default: 0,
},
max: {
type: [Number, String],
default: 100,
},
step: {
type: [Number, String],
default: 1,
},
buttonSize: [Number, String],
buttonStyle: [String, Object],
},
data: () => ({
buttonIndex: 0,
startValue: 0,
currentValue: 0,
dragStatus: '',
}),
computed: {
c_class() {
return this.$mergeClass({
's-slider--vertical': this.vertical,
's-slider--readonly': this.readonly,
's-slider--disabled': this.disabled,
}, this.custom_class);
},
c_style() {
return this.$mergeStyle({
[this.vertical ? 'width' : 'height']: this.$addUnit(this.barHeight),
background: this.inactiveColor,
}, this.custom_style);
},
scope() {
return Number(this.max) - Number(this.min);
},
isRange() {
return Array.isArray(this.value);
},
bar_style() {
const { value, min, scope } = this;
let progress = 0;
let offset = 0;
if (this.isRange) {
progress = ((value[1] - value[0]) * 100) / scope;
offset = ((value[0] - Number(min)) * 100) / scope;
} else {
progress = ((value - Number(min)) * 100) / scope;
}
return this.$mergeStyle({
[this.vertical ? 'height' : 'width']: `${progress}%`,
left: this.vertical ? '' : `${offset}%`,
bottom: this.vertical ? `${offset}%` : '',
background: this.activeColor,
transition: this.dragStatus ? 'none' : '',
});
},
button_style() {
return this.$mergeStyle({
width: this.$addUnit(this.buttonSize),
height: this.$addUnit(this.buttonSize),
}, this.buttonStyle);
},
},
created() {
this.touch = useTouch();
this.updateValue(this.value);
},
methods: {
onStartButtonTouchStart(event) {
this.buttonIndex = 0;
this.onTouchStart(event);
},
onEndButtonTouchStart(event) {
this.buttonIndex = 1;
this.onTouchStart(event);
},
onTouchStart(event) {
if (this.disabled || this.readonly) return;
this.touch.start(event);
this.currentValue = this.value;
if (this.isRange) {
this.startValue = this.currentValue.map(this.format);
} else {
this.startValue = this.format(this.currentValue);
}
this.dragStatus = 'start';
},
onTouchMove(event) {
if (this.disabled || this.readonly) return;
if (this.dragStatus === 'start') {
this.$emit('start');
} else if (this.dragStatus === 'draging') {
this.$emit('move');
}
this.touch.move(event);
this.dragStatus = 'draging';
this.$getRect(`#${this.$sUid}`).then(rect => {
const delta = this.vertical ? -this.touch.deltaY : this.touch.deltaX;
const total = this.vertical ? rect.height : rect.width;
const diff = (delta / total) * this.scope;
if (this.isRange) {
this.currentValue[this.buttonIndex] = this.startValue[this.buttonIndex] + diff;
} else {
this.currentValue = this.startValue + diff;
}
this.updateValue(this.currentValue);
});
},
onTouchEnd() {
if (this.disabled || this.readonly) return;
if (this.dragStatus === 'draging') {
this.updateValue(this.currentValue, true);
this.$emit('end');
}
this.dragStatus = '';
},
onClick(event) {
if (this.disabled || this.readonly) return;
const { min, vertical, scope } = this;
this.$getRect(`#${this.$sUid}`).then(rect => {
const { clientX, clientY } = this.touch.getTouch(event);
const delta = vertical ? rect.top + rect.height - clientY : clientX - rect.left;
const total = vertical ? rect.height : rect.width;
const value = Number(min) + (delta / total) * scope;
if (this.isRange) {
const [left, right] = this.value;
const middle = (left + right) / 2;
if (value <= middle) {
this.updateValue([value, right], true);
} else {
this.updateValue([left, value], true);
}
} else {
this.updateValue(value, true);
}
});
},
updateValue(value, end) {
if (this.isRange) {
value = this.overlap(value).map(this.format);
} else {
value = this.format(value);
}
if (!this.isSameValue(value, this.value)) {
this.$emit('input', value);
}
if (end && !this.isSameValue(value, this.startValue)) {
this.$emit('change', value);
}
},
isSameValue(newValue, oldValue) {
return JSON.stringify(newValue) === JSON.stringify(oldValue);
},
format(value) {
const { min, max, step } = this;
value = Math.max(+min, Math.min(value, +max));
return Math.round(value / +step) * +step;
},
overlap(value) {
if (value[0] > value[1]) {
return value.slice(0).reverse();
}
return value;
},
},
};
</script>
<style lang="scss" src="./index.scss"></style>