You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

481 lines
13 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view class="fulled tm-slider " :class="[step > 0 ? 'pb-36' : 'pb-24']"
:style="{ height: vertical ? heightpx + 'px' : 'auto', width: vertical ? '5px' : '100%'}">
<view class=" tm-slider-id fulled " :class="[vertical ? 'vertical' : 'flex-between']">
<!-- 左边label -->
<view v-if="showLeft" class="label_slider left flex-col text-size-xs text-grey-darken-1"
:class="[vertical ? '' : 'flex-center']">
<slot name="left" :data="{ value: value, color: color_tmeme, icon: leftIcon,max:max }">
<tm-icons v-if="leftIcon && !vertical" size="28" :name="leftIcon" :color="color_tmeme"></tm-icons>
<view class="text-size-xs text-grey-darken-1 ">{{ value }}</view>
</slot>
</view>
<!-- 条子内容 -->
<view class="slider_id "
:style="{ width: vertical ? 'auto' : sliderWidth + 'px', height: vertical ? sliderWidth + 'px' : '5px' }">
<view class="slider_id_bg round-10" :class="[bgColor,black_tmeme?'bk':'']"></view>
<view
:style="{ width: vertical ? '100%' : active_width + '%', height: vertical ? active_width + '%' : '100%' }"
class="slider_id_active round-10 " :class="[color_tmeme,black_tmeme?'bk':'']"></view>
<view :style="{ left: vertical ? 0 : barLeft + 'px', top: vertical ? barLeft + 'px' : 0 }"
@touchcancel="barEnd" @touchstart="barStart" @touchend="barEnd" @touchmove.stop.prevent="barMove"
@mouseleave="barEnd" @mousedown="barStart" @mouseup="barEnd" @mousemove.stop.prevent="barMove"
class="slider_bar border-white-a-2 rounded" :class="[color_tmeme,black_tmeme?'bk':'', ` shadow-${color_tmeme}-10`]">
<view v-if="showTopTips||showTip" class="slider_bar_showbg border-white-a-1" :class="[color_tmeme,black_tmeme?'bk':'']"></view>
<view v-if="showTopTips||showTip" class="slider_bar_num text-size-xs flex-center" :class="[color_tmeme,black_tmeme?'bk':'']">
<slot name="tips" :data="value">{{ value }}</slot>
</view>
</view>
<!-- 步长刻度尺 -->
<view class="kdc_wk ">
<view v-for="(item, index) in kdcNum" :key="index"
:style="{ left: vertical ? 0 : item.left, top: vertical ? item.left : 0 }" :class="[color_tmeme,black_tmeme?'bk':'']"
class="kdc_wk_item rounded border-white-a-1 ">
<view class="kdc_wk_item_label pl-0" :class="[vertical ? 'pl-24' : 'pa-24']">
<text class="text-size-xs text-grey-darken-1 ">{{ item.kedu }}</text>
</view>
</view>
</view>
</view>
<!-- 右边label -->
<view v-if="showRight" class="label_slider right flex-col text-size-xs text-grey-darken-1"
:class="[vertical ? 'flex-end' : 'flex-center']">
<slot name="right" :data="{ value: value, color: color_tmeme, icon: rightIcon,max:max }">
<tm-icons v-if="rightIcon && !vertical" size="28" :name="rightIcon" :color="color_tmeme"></tm-icons>
<view class="text-size-xs text-grey-darken-1 ">{{ max }}</view>
</slot>
</view>
</view>
</view>
</template>
<script>
/**
* 滑块
* @property {Number} value = [0] 赋值如果需要同步使用value.sync推荐使用v-model绑定。
* @property {Boolean} vertical = [true|false] 默认false,是否启用竖向模式。
* @property {Number} height = [200] 默认200,竖向模式时才有作用。
* @property {Number} width = [] 默认0,横向时起作用如果为0自动获取外层宽度。
* @property {Boolean} show-left = [true|false] 默认false,显示左边数据。
* @property {Boolean} show-right = [true|false] 默认false,显示右边数据。
* @property {Number} max = [] 默认100,显示的最大刻度。
* @property {Number} value-diog = [] 默认0,取值小数点后几位
* @property {Boolean} disabled = [true|false] 默认false, 是否禁用。
* @property {Boolean} showTip = [true|false] 默认false, 始终显示进度标签。
* @property {Number} step = [10|20] 默认0, 步长,设置请尽量大于20.太小滑动容易过小出问题。
* @property {String} color = [primary] 默认primary, 主题颜色名称。
* @property {String} bg-color = [grey-lighten-2] 默认grey-lighten-2, 底部不活动的背景色,颜色名称。
* @property {String} left-icon = [] 默认icon-minus, 左边图标
* @property {String} right-icon = [] 默认icon-plus, 右边图标
* @property {String} name = [] 默认:'',提交表单时的的字段名称标识
* @property {Function} change 同v-model和value相等的参数变动时触发。
* @example <tm-slider v-model="checked" ></tm-slider>
*
*/
import tmIcons from "@/tm-vuetify/components/tm-icons/tm-icons.vue"
export default {
name:'tm-slider',
components:{tmIcons},
model:{
prop:'value',
event:'input'
},
props: {
//提交表单时的的字段名称
name:{
type:String,
default:''
},
vertical: Boolean, //是否启用竖向模式。需要和height配合使用。
// 单位upx.
height: {
type: Number,
default: 200
},
width: {
type: Number,
default: 0
},
showLeft: Boolean, //显示左边
showRight: Boolean, //显示右边
// 最大刻度。
max: {
type: Number,
default: 100
},
// 默认的数据。不能大于max.,使用.sync修饰。双向绑定数据。
value: {
type: Number,
default: 0
},
// 取值小数点后几位。默认为0
valueDiog: {
type: Number,
default: 0
},
// 是否禁用。
disabled: Boolean,
//步长。默认为0,设置请尽量大于0.太小滑动容易过小出问题。
step: {
type: Number,
default: 0
},
// 主题颜色名称。
color: {
type: String,
default: 'primary'
},
// 底部不活动的背景色,颜色名称。。
bgColor: {
type: String,
default: 'grey-lighten-2'
},
// 左边图标名称。
leftIcon: {
type: String | Boolean,
default: 'icon-minus'
},
// 右边图标名称。
rightIcon: {
type: String | Boolean,
default: 'icon-plus'
},
// 始终显示进度提示窗
showTip: {
type: Boolean,
default: false
},
// 跟随主题色的改变而改变。
fllowTheme:{
type:Boolean|String,
default:true
},
black: {
type: Boolean,
default: null
},
},
watch: {
value: function(val) {
let rdl = this.sliderWidth * (Math.abs(val) / Math.abs(this.max));
this.barLeft = rdl >= this.sliderWidth || rdl < 0 ? this.sliderWidth : rdl;
},
barLeft: function() {
let value = (this.barLeft / this.sliderWidth) * this.max;
if (!isNaN(value)) {
if (this.valueDiog > 0) {
let s = value.toString();
let st = s.split('.');
let ruslt = 0;
if (st.length > 1) {
ruslt = parseFloat(st[0] + '.' + st[1].substring(0, this.valueDiog));
} else {
ruslt = parseFloat(s);
}
this.$emit('update:value', ruslt);
this.$emit('input', ruslt);
this.$emit('change', ruslt);
} else {
this.$emit('update:value', parseInt(value));
this.$emit('input', parseInt(value));
this.$emit('change', parseInt(value));
}
}
}
},
computed: {
black_tmeme: function() {
if (this.black !== null) return this.black;
return this.$tm.vx.state().tmVuetify.black;
},
// 计算刻度尺的数量
kdcNum: function() {
if (Math.abs(this.step) <= 0) return [];
let jlv = Math.abs(this.step) / Math.abs(this.max);
let left_width = jlv * (this.sliderWidth - 0);
let kd_num = parseInt(this.sliderWidth / left_width);
let ar = [];
for (let i = 0; i <= kd_num; i++) {
ar.push({
index: i, //顺序
left: i * left_width + 'px', //距离左边距离
kedu: this.step * i //当前刻度数量。
});
}
return ar;
},
//比例
active_width: function() {
return (this.barLeft / this.sliderWidth) * 100;
},
heightpx: function() {
return uni.upx2px(this.height);
},
color_tmeme:function(){
if(this.$tm.vx.state().tmVuetify.color!==null&&this.$tm.vx.state().tmVuetify.color && this.fllowTheme){
return this.$tm.vx.state().tmVuetify.color;
}
return this.color;
},
},
data() {
return {
sliderWidth: 0,
x: 0,
startMove: false,
barLeft: 0,
isError: false,
showTopTips: false
};
},
async mounted() {
this.$nextTick(async function() {
await this.getwidth();
if (Math.abs(this.value) > Math.abs(this.max)) {
this.isError = true;
return;
}
let rdl = this.sliderWidth * (Math.abs(this.value) / Math.abs(this.max));
this.barLeft = rdl >= this.sliderWidth || rdl < 0 ? this.sliderWidth : rdl;
});
},
async updated() {
this.$nextTick(async function() {
await this.getwidth();
if (Math.abs(this.value) > Math.abs(this.max)) {
this.isError = true;
return;
}
let rdl = this.sliderWidth * (Math.abs(this.value) / Math.abs(this.max));
this.barLeft = rdl >= this.sliderWidth || rdl < 0 ? this.sliderWidth : rdl;
});
},
methods: {
barStart(e) {
if (this.disabled || this.isError) return;
if(e.type.indexOf('mouse')>-1&&e.changedTouches.length==0){
this.x = (this.vertical ? e.pageY : e.pageX) - this.barLeft;
this.startMove = true;
this.showTopTips = true;
}else{
if (e.changedTouches.length > 0) {
this.x = (this.vertical ? e.changedTouches[0].pageY : e.changedTouches[0].pageX) - this.barLeft;
this.startMove = true;
this.showTopTips = true;
}
}
this.$nextTick(function(){
this.$emit('start',this.value)
})
},
barEnd(e) {
if (this.disabled || this.isError) return;
this.startMove = false;
this.showTopTips = false;
this.$nextTick(function(){
this.$emit('end',this.value)
})
},
barMove(e) {
if (this.disabled || this.isError) return;
if (!this.startMove) return;
let left = 0;
if(e.type.indexOf('mouse')>-1&&e.changedTouches.length==0){
left = (this.vertical ? e.pageY : e.pageX) - this.x;
}else{
left = (this.vertical ? e.changedTouches[0].pageY : e.changedTouches[0].pageX) - this.x;
}
if (left <= 0) {
this.barLeft = 0;
return;
}
if (left >= this.sliderWidth) {
this.barLeft = this.sliderWidth;
return;
}
let nowStep = parseInt(Math.abs(left - this.barLeft));
let bdi = parseInt((this.step / this.max) * this.sliderWidth);
if (nowStep >= bdi) {
// 每一小段的值。
if(this.step!==0){
let kedud = this.sliderWidth / (this.max / this.step);
this.barLeft = Math.round(left / kedud) * kedud;
}else{
this.barLeft = left;
}
}
},
async getwidth() {
let res = await this.$Querey('.tm-slider-id', this,0).catch(e=>{});
res[0].width = res[0].width||uni.upx2px(this.width);
res[0].height = res[0].height||uni.upx2px(this.height);
if (this.showLeft === false && this.showRight === false) {
this.sliderWidth = this.vertical ? res[0].height : res[0].width;
return;
}
if (this.showLeft !== false && this.showRight !== false) {
this.sliderWidth = (this.vertical ? res[0].height : res[0].width) - uni.upx2px(this.vertical ? 50 :
100) * 2;
return;
}
if (this.showLeft === true || this.showRight === true) {
this.sliderWidth = (this.vertical ? res[0].height : res[0].width) - uni.upx2px(this.vertical ? 50 :
100);
}
}
}
};
</script>
<style lang="scss" scoped>
.label_slider {
flex-shrink: 0;
&.left,
&.right {
width: 100upx;
}
}
.tm-slider-id {
width: 100%;
.slider_id {
position: relative;
height: 4px;
.slider_id_bg {
width: 100%;
height: 100%;
}
.slider_id_active {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
z-index: 5;
}
.slider_bar {
position: absolute;
width: 40upx;
height: 40upx;
left: 0;
top: 0;
margin-top: -22upx;
z-index: 10;
.slider_bar_showbg {
position: absolute;
width: 50upx;
height: 50upx;
border-radius: 30upx;
border-bottom-left-radius: 5upx;
transform: rotate(-45deg);
bottom: 60upx;
left: -9upx;
animation: roteScaleTop_BG 0.3s ease-in-out;
}
.slider_bar_num {
position: absolute;
width: 50upx;
height: 50upx;
border-radius: 50upx;
left: -8upx;
bottom: 60upx;
line-height: 50upx;
text-align: center;
background: transparent !important;
animation: roteScaleTop 0.3s ease-in-out;
}
}
.kdc_wk {
width: 100%;
height: 100%;
position: absolute;
z-index: 6;
left: 0;
top: -2upx;
.kdc_wk_item {
width: 10upx;
height: 10upx;
position: absolute;
z-index: 7;
text-align: center;
.kdc_wk_item_label {
margin-left: -100%;
}
}
}
}
&.vertical {
height: 100%;
.label_slider {
flex-shrink: 0;
&.left,
&.right {
width: auto;
height: 50upx;
}
}
.slider_bar {
margin-left: -18upx;
}
.kdc_wk {
top: 0upx;
.kdc_wk_item {
.kdc_wk_item_label {
margin-left: 100%;
margin-top: -15upx;
}
}
}
}
}
@keyframes roteScaleTop_BG{
0%{
transform: scale(0.9) translateY(20rpx) rotate(-45deg);
opacity: 0;
}
100%{
transform: scale(1) translateY(0rpx) rotate(-45deg);
opacity: 1;
}
}
@keyframes roteScaleTop{
0%{
transform: scale(0.9) translateY(20rpx);
opacity: 0;
}
100%{
transform: scale(1) translateY(0rpx);
opacity: 1;
}
}
</style>