zhengkaixin 3 years ago
parent
commit
849ce3d238

+ 260 - 0
components/mpvue-agepicker/mpvueAgePicker.vue

@@ -0,0 +1,260 @@
+<template>
+  <div class="mpvue-picker">
+    <div :class="{'pickerMask':showPicker}" @click="maskClick" catchtouchmove="true"></div>
+    <div class="mpvue-picker-content " :class="{'mpvue-picker-view-show':showPicker}">
+      <div class="mpvue-picker__hd" catchtouchmove="true">
+        <div class="mpvue-picker__action" @click="pickerCancel">取消</div>
+        <div class="mpvue-picker__action" :style="{color:themeColor}" @click="pickerConfirm">确定</div>
+      </div>
+      <picker-view indicator-style="height: 40px;" class="mpvue-picker-view" :value="pickerValue" @change="pickerChange">
+        <block>
+          <picker-view-column>
+            <div class="picker-item" v-for="(item,index) in ageDownLimitDataList" :key="index">{{item.label}}</div>
+          </picker-view-column>
+          <picker-view-column>
+            <div class="picker-item" v-for="(item,index) in ageUpLimitDataList" :key="index">{{item.label}}</div>
+          </picker-view-column> 
+        </block>
+      </picker-view>
+    </div>
+  </div>
+</template>
+
+<script>
+//import provinceData from './city-data/province.js';
+//import cityData from './city-data/city.js';
+//import areaData from './city-data/area.js';
+export default {
+  data() {
+    return {
+	  ageData :[],
+      pickerValue: [0, 0],
+      ageDownLimitDataList: [],
+      ageUpLimitDataList: [], 
+		/* 是否显示控件 */
+		showPicker: false,
+    };
+  },
+  created() {
+	console.log('mpvueCityPicker created')
+	for(let i = 18;i<=99;i++)
+	{
+		this.ageData.push(i);
+	}
+    this.init()
+  },
+  props: {
+    /* 默认值 */
+    pickerValueDefault: {
+      type: Array,
+      default(){
+				return [0, 0]
+			}
+    },
+    /* 主题色 */
+    themeColor: String
+  },
+	watch:{
+		pickerValueDefault(){
+			this.init();
+		}
+	},
+  methods: {
+		init() {
+			console.log('mpvueCityPicker init')
+			if(this.pickerValueDefault.length == 0)
+				this.pickerValueDefault  = [0,0];
+			if(this.ageDownLimitDataList.length<=0)
+			{
+				this.ageDownLimitDataList = [];
+				for(let i = 0;i< this.ageData.length-1;i++)
+				{
+					let data = {value : i,label:this.ageData[i]};
+					this.ageDownLimitDataList.push(data);
+				}
+				
+			}
+			if(this.ageUpLimitDataList.length<=0)
+			{
+				this.ageUpLimitDataList = [];
+				for(let i = 1;i< this.ageData.length;i++)
+				{
+					let data = {value : i,label:this.ageData[i]};
+					this.ageUpLimitDataList.push(data);
+				}
+				
+			}
+			//console.log('ageDownLimit'+JSON.stringify(this.ageDownLimitDataList))
+			//console.log('ageUpLimit'+JSON.stringify(this.ageUpLimitDataList))
+			//this.handPickValueDefault(); // 对 pickerValueDefault 做兼容处理
+			
+			 
+			this.pickerValue = this.pickerValueDefault;
+		},
+    show() {
+      setTimeout(() => {
+        this.showPicker = true;
+      }, 0);
+    },
+    maskClick() {
+      this.pickerCancel();
+    },
+    pickerCancel() {
+      this.showPicker = false;
+      this._$emit('onCancel');
+    },
+    pickerConfirm(e) {
+      this.showPicker = false;
+      this._$emit('onConfirm');
+    },
+    showPickerView() {
+      this.showPicker = true;
+    },
+    handPickValueDefault() {
+	
+      //console.log('mpvueCityPicker handPickValueDefault')
+	  console.log('默认树'+JSON.stringify(this.pickerValueDefault))
+	  //console.log('省'+JSON.stringify(this.pickerValueDefault))
+	  	  
+       
+        if (this.pickerValueDefault[0] > this.ageDownLimitDataList.length - 1) {
+          this.pickerValueDefault[0] = this.ageDownLimitDataList.length - 1;
+		  
+		  console.log('省索引'+JSON.stringify(this.pickerValueDefault[0]))
+        }
+		let ageDown = this.ageDownLimitDataList[this.pickerValueDefault[0]];		
+		if (this.pickerValueDefault[1] > this.ageUpLimitDataList.length - 1) {
+		  this.pickerValueDefault[1] = this.ageUpLimitDataList.length - 1;
+		  
+		  console.log('省索引'+JSON.stringify(this.pickerValueDefault[0]))
+		}
+		let ageUp = this.ageUpLimitDataList[this.pickerValueDefault[1]];
+		this.ageUpLimitDataList = [];
+		for(let i = 0;i< this.ageData.length;i++){
+			if(this.ageData[i]<=ageDown)
+				continue;
+			this.ageUpLimitDataList.push(this.ageData[i]);
+			if(ageUp == this.ageData[i]){
+				this.pickerValueDefault[1] = i;
+			}
+		}
+		  
+    },
+    pickerChange(e) {
+      let changePickerValue = e.mp.detail.value;
+      if (this.pickerValue[0] !== changePickerValue[0]) {
+        // 第一级发生滚动
+        let ageDown = this.ageDownLimitDataList[changePickerValue[0]];        
+		 console.log('ageDown'+JSON.stringify(ageDown))
+        //let ageUp = this.ageUpLimitDataList[this.pickerValueDefault[1]];
+        this.ageUpLimitDataList = [];
+		//this.pickerValueDefault[1] 
+		
+        for(let i = 0;i< this.ageData.length;i++){
+        	if(this.ageData[i]<=ageDown.label)
+        		continue;
+			let data = {value:i,label:this.ageData[i]};
+        	this.ageUpLimitDataList.push(data);
+        	 
+        }
+		//this.cityDataList = cityData[changePickerValue[0]];
+        //this.areaDataList = areaData[changePickerValue[0]][0];
+        changePickerValue[1] = 0; 
+      } 
+	  /*else if (this.pickerValue[1] !== changePickerValue[1]) {
+        // 第二级滚动
+        this.areaDataList =
+          areaData[changePickerValue[0]][changePickerValue[1]];
+        changePickerValue[2] = 0;
+      }*/
+      this.pickerValue = changePickerValue;
+      this._$emit('onChange');
+    },
+    _$emit(emitName) {
+      let pickObj = {
+        label: this._getLabel(),
+        value: this.pickerValue
+      };
+      this.$emit(emitName, pickObj);
+    },
+    _getLabel() {
+      let pcikerLabel = 
+        this.ageDownLimitDataList[this.pickerValue[0]].label +
+        '-' +
+        this.ageUpLimitDataList[this.pickerValue[1]].label+'岁';
+      return pcikerLabel;
+    } 
+  }
+};
+</script>
+
+<style>
+.pickerMask {
+  position: fixed;
+  z-index: 1000;
+  top: 0;
+  right: 0;
+  left: 0;
+  bottom: 0;
+  background: rgba(0, 0, 0, 0.6);
+}
+.mpvue-picker-content {
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  width: 100%;
+  transition: all 0.3s ease;
+  transform: translateY(100%);
+  z-index: 3000;
+}
+.mpvue-picker-view-show {
+  transform: translateY(0);
+}
+.mpvue-picker__hd {
+  display: flex;
+  padding: 9px 15px;
+  background-color: #fff;
+  position: relative;
+  text-align: center;
+  font-size: 17px;
+}
+.mpvue-picker__hd:after {
+  content: ' ';
+  position: absolute;
+  left: 0;
+  bottom: 0;
+  right: 0;
+  height: 1px;
+  border-bottom: 1px solid #e5e5e5;
+  color: #e5e5e5;
+  transform-origin: 0 100%;
+  transform: scaleY(0.5);
+}
+.mpvue-picker__action {
+  display: block;
+  flex: 1;
+  color: #1aad19;
+}
+.mpvue-picker__action:first-child {
+  text-align: left;
+  color: #888;
+}
+.mpvue-picker__action:last-child {
+  text-align: right;
+}
+.picker-item {
+  text-align: center;
+  line-height: 40px;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  font-size: 16px;
+}
+.mpvue-picker-view {
+  position: relative;
+  bottom: 0;
+  left: 0;
+  width: 100%;
+  height: 238px;
+  background-color: rgba(255, 255, 255, 1);
+}
+</style>

+ 281 - 0
components/mpvue-datepicker/mpvue-datepicker.vue

@@ -0,0 +1,281 @@
+<template>
+  <div class="mpvue-picker">
+    <div :class="{'pickerMask':showPicker}" @click="maskClick" catchtouchmove="true"></div>
+    <div class="mpvue-picker-content " :class="{'mpvue-picker-view-show':showPicker}">
+      <div class="mpvue-picker__hd" catchtouchmove="true">
+        <div class="mpvue-picker__action" @click="pickerCancel">取消</div>
+        <div class="mpvue-picker__action" :style="{color:themeColor}" @click="pickerConfirm">确定</div>
+      </div>
+      <picker-view indicator-style="height: 40px;" class="mpvue-picker-view" :value="pickerValue" @change="pickerChange">
+        <block>
+			<picker-view-column>
+			    <view class="picker-item" v-for="(item,index) in years" :key="index">{{item.label}}年</view>
+			</picker-view-column>
+			<picker-view-column>
+			    <view class="picker-item" v-for="(item,index) in months" :key="index">{{item.label}}月</view>
+			</picker-view-column>
+			<picker-view-column>
+			    <view class="picker-item" v-for="(item,index) in days" :key="index">{{item.label}}日</view>
+			</picker-view-column>
+        </block>
+      </picker-view>
+    </div>
+  </div>
+</template>
+
+<script>
+//import provinceData from './city-data/province.js';
+//import cityData from './city-data/city.js';
+//import areaData from './city-data/area.js';
+
+var addMonth=function(date, months) {
+	let y = date.getFullYear();
+	let m = date.getMonth();
+	var d = date.getDate();
+
+//		console.log('add month y' + y + ' m:' + m + ' d: ' + d + ' month: ' + months);
+	y += Math.floor((m + 1 + months) / 12); //计算年
+	m = Math.floor((m + 1 + months) % 12) - 1; //计算月
+	let _date = new Date(y,m,d);
+//		console.log('result y:' + y + ' m: ' + m + '_date:' + _date);
+	return _date;
+};
+var addDay=function(date, days) {
+	var _date = new Date(date.getTime() + 24*60*60*1000*days) 
+	return _date;
+};
+export default {
+  data() {
+	  const date = new Date()
+	  const years = []
+	  const year = date.getFullYear()
+	  const months = []
+	  const month = date.getMonth() + 1
+	  const days = []
+	  const day = date.getDate()
+	  
+	  let index = 0;
+	  for (let i = 1990; i <= date.getFullYear(); i++) {
+		  let data = { value: index++,label:i};
+	      years.push(data)
+	  }
+	  index = 0;
+	  for (let i = 1; i <= 12; i++) {
+		  let data = { value: index++,label:i};
+	      months.push(data)
+	  }
+	  index = 0;
+	  for (let i = 1; i <= 31; i++) {
+		  let data = { value: index++,label:i};
+	      days.push(data)
+	  }
+    return { 
+      pickerValue: [0, 0, 0],
+		 years,
+		 year,
+		 months,
+		 month,
+		 days,
+		 day,
+		/* 是否显示控件 */
+		showPicker: false,
+    };
+  },
+  created() {
+	console.log('mpvueCityPicker created')
+	 
+    this.init()
+  },
+  props: {
+    /* 默认值 */
+    pickerValueDefault: {
+      type: Array,
+      default(){
+				return [0, 0 , 0]
+			}
+    },
+    /* 主题色 */
+    themeColor: String
+  },
+	watch:{
+		pickerValueDefault(){
+			this.init();
+		}
+	},
+  methods: {
+		init() {
+			console.log('mpvueCityPicker init')
+			if(this.pickerValueDefault.length == 0)
+				this.pickerValueDefault  = [0,0, 0];
+			 
+			//console.log('ageDownLimit'+JSON.stringify(this.ageDownLimitDataList))
+			//console.log('ageUpLimit'+JSON.stringify(this.ageUpLimitDataList))
+			//this.handPickValueDefault(); // 对 pickerValueDefault 做兼容处理
+			
+			 
+			this.pickerValue = this.pickerValueDefault;
+		},
+    show() {
+      setTimeout(() => {
+        this.showPicker = true;
+      }, 0);
+    },
+    maskClick() {
+      this.pickerCancel();
+    },
+    pickerCancel() {
+      this.showPicker = false;
+      this._$emit('onCancel');
+    },
+    pickerConfirm(e) {
+      this.showPicker = false;
+      this._$emit('onConfirm');
+    },
+    showPickerView() {
+      this.showPicker = true;
+    },
+    handPickValueDefault() {
+	
+      //console.log('mpvueCityPicker handPickValueDefault')
+	  console.log('默认树'+JSON.stringify(this.pickerValueDefault))
+	  //console.log('省'+JSON.stringify(this.pickerValueDefault))
+	  	  
+       
+        if (this.pickerValueDefault[0] > this.years.length - 1) {
+          this.pickerValueDefault[0] = this.years.length - 1;
+		   
+        } 	
+		if (this.pickerValueDefault[1] > this.months.length - 1) {
+		  this.pickerValueDefault[1] = this.months.length - 1;
+		   
+		} 
+		if (this.pickerValueDefault[2] > this.days.length - 1) {
+		  this.pickerValueDefault[2] = this.days.length - 1;		   
+		} 
+    },
+    pickerChange(e) {
+      let changePickerValue = e.mp.detail.value;
+      if (this.pickerValue[0] !== changePickerValue[0]) {
+        // 第一级发生滚动   
+		 
+		//this.cityDataList = cityData[changePickerValue[0]];
+        //this.areaDataList = areaData[changePickerValue[0]][0];
+        changePickerValue[1] = 0; 
+		console.log('第一级滚动')
+        console.log('date'+JSON.stringify(this.years[changePickerValue[0]]) +'-'+JSON.stringify(this.months[changePickerValue[1]]))
+      } 
+	  else if (this.pickerValue[1] !== changePickerValue[1]) {
+        // 第二级滚动
+		console.log('第二级滚动')
+		let year = this.years[changePickerValue[0]].label;
+		let month = this.months[changePickerValue[1]].label; 
+        console.log('date'+JSON.stringify(this.years[changePickerValue[0]]) +'-'+JSON.stringify(this.months[changePickerValue[1]]))
+		let date = new Date(year,month-1,1);
+		let nextMonth = addMonth(date,1);
+		let currentMonth = addDay(nextMonth,-1);
+		
+		console.log('下月日期'+JSON.stringify(nextMonth))
+		console.log('当前日期'+JSON.stringify(currentMonth))
+		const day = currentMonth.getDate()
+		console.log('day'+JSON.stringify(day))
+		let index = 0;
+		this.days =[];
+		for (let i = 1; i <= day; i++) {
+				  let data = { value: index++,label:i};
+		    this.days.push(data)
+		}
+        changePickerValue[2] = 0;
+      } 
+      this.pickerValue = changePickerValue;
+      this._$emit('onChange');
+    },
+    _$emit(emitName) {
+      let pickObj = {
+        label: this._getLabel(),
+        value: this.pickerValue
+      };
+      this.$emit(emitName, pickObj);
+    },
+    _getLabel() {
+      let pcikerLabel = 
+        this.years[this.pickerValue[0]].label +
+        '-' +
+        this.months[this.pickerValue[1]].label+
+		'-'+
+		this.days[this.pickerValue[2]].label;
+      return pcikerLabel;
+    } 
+  }
+};
+</script>
+
+<style>
+.pickerMask {
+  position: fixed;
+  z-index: 1000;
+  top: 0;
+  right: 0;
+  left: 0;
+  bottom: 0;
+  background: rgba(0, 0, 0, 0.6);
+}
+.mpvue-picker-content {
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  width: 100%;
+  transition: all 0.3s ease;
+  transform: translateY(100%);
+  z-index: 3000;
+}
+.mpvue-picker-view-show {
+  transform: translateY(0);
+}
+.mpvue-picker__hd {
+  display: flex;
+  padding: 9px 15px;
+  background-color: #fff;
+  position: relative;
+  text-align: center;
+  font-size: 17px;
+}
+.mpvue-picker__hd:after {
+  content: ' ';
+  position: absolute;
+  left: 0;
+  bottom: 0;
+  right: 0;
+  height: 1px;
+  border-bottom: 1px solid #e5e5e5;
+  color: #e5e5e5;
+  transform-origin: 0 100%;
+  transform: scaleY(0.5);
+}
+.mpvue-picker__action {
+  display: block;
+  flex: 1;
+  color: #1aad19;
+}
+.mpvue-picker__action:first-child {
+  text-align: left;
+  color: #888;
+}
+.mpvue-picker__action:last-child {
+  text-align: right;
+}
+.picker-item {
+  text-align: center;
+  line-height: 40px;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  font-size: 16px;
+}
+.mpvue-picker-view {
+  position: relative;
+  bottom: 0;
+  left: 0;
+  width: 100%;
+  height: 238px;
+  background-color: rgba(255, 255, 255, 1);
+}
+</style>

+ 74 - 0
components/primewind-sliderrange/README.md

@@ -0,0 +1,74 @@
+# slider-range
+uni-app 滑块区间选择组件  
+
+可根据具体需求,修改、自定义其他内容。
+
+## 属性说明
+
+|属性名|类型|默认值|说明|
+| -- | -- | --|--|
+| value | Array<Number, Number> | [0,100] |滑块已选中区间的值|
+| min | Number|  0 | 滑块区间最小值 |
+| max | Number | 100 | 滑块区间最大值 | 
+| step | Number | 1 | 拖动时的步长 |
+| disabled | Boolean | false | 是否为禁用状态 |
+| height | Number | 50 | 滑块容器高度 |
+| barHeight | Number | 5 | 滑块进度条高度 |
+| backgroundColor | String | #e9e9e9| 滑块进度条背景色|
+| activeColor | String | #1aad19 | 已选中区间进度条颜色|
+| blockSize | Number | 20 | 滑块大小 |
+| blockColor | String | #fff | 滑块颜色 |
+| decorationVisible | Boolean | false | 是否显示滑块内装饰元素|
+| tipVisible | Boolean | true | 是否显示滑块值提示文本 |
+| fomat| Function | | 滑块值提示文本格式化函数,**注意**:小程序中此属性必传,否则会报错,如果无需格式化,可以简单返回原始值: `format(val) {  return val    }`;H5中可以不传。|
+
+
+## 使用示例
+
+```html
+
+<slider-range
+  :value="rangeValue"
+  :min="rangeMin"
+  :max="rangMax"
+  :step="5"
+  :bar-height="3"
+  :block-size="26"
+  background-color="#EEEEF6"
+  active-color="#FF6B00"
+  :format="format"
+  :decorationVisible="true"
+  @change="handleRangeChange"
+></slider-range>
+
+
+```
+
+```javascript
+
+import SliderRange from '../components/slider-range/index.vue'
+export default {
+  components: {
+	SliderRange
+  },
+  data() {
+	return {
+	  rangeMin: 5,
+	  rangMax: 200,
+	  rangeValue: [10, 50]
+	}
+  },
+  methods: {
+	format(val) {
+	  return val + '%'
+	},
+	handleRangeChange(e) {
+	  this.rangeValue = e
+	}
+  }
+}
+```
+
+效果图
+
+![](http://images.alisali.cn/img_20190715175325.png)

+ 378 - 0
components/primewind-sliderrange/index.vue

@@ -0,0 +1,378 @@
+<template>
+  <view
+    class="slider-range"
+    :class="{ disabled: disabled }"
+    :style="{ paddingLeft: blockSize / 2 + 'px', paddingRight: blockSize / 2 + 'px' }"
+  >
+    <view class="slider-range-inner" :style="{ height: height + 'px' }">
+      <view
+        class="slider-bar"
+        :style="{
+          height: barHeight + 'px',
+        }"
+      >
+        <!-- 背景条 -->
+        <view
+          class="slider-bar-bg"
+          :style="{
+            backgroundColor: backgroundColor,
+          }"
+        ></view>
+
+        <!-- 滑块实际区间 -->
+        <view
+          class="slider-bar-inner"
+          :style="{
+            width: ((values[1] - values[0]) / (max - min)) * 100 + '%',
+            left: lowerHandlePosition + '%',
+            backgroundColor: activeColor,
+          }"
+        ></view>
+      </view>
+
+      <!-- 滑动块-左 -->
+      <view
+        class="slider-handle-block"
+        :class="{ decoration: decorationVisible }"
+        :style="{
+          backgroundColor: blockColor,
+          width: blockSize + 'px',
+          height: blockSize + 'px',
+          left: lowerHandlePosition + '%',
+        }"
+        @touchstart="_onTouchStart"
+        @touchmove="_onBlockTouchMove"
+        @touchend="_onBlockTouchEnd"
+        data-tag="lowerBlock"
+      ></view>
+
+      <!-- 滑动块-右 -->
+      <view
+        class="slider-handle-block"
+        :class="{ decoration: decorationVisible }"
+        :style="{
+          backgroundColor: blockColor,
+          width: blockSize + 'px',
+          height: blockSize + 'px',
+          left: higherHandlePosition + '%',
+        }"
+        @touchstart="_onTouchStart"
+        @touchmove="_onBlockTouchMove"
+        @touchend="_onBlockTouchEnd"
+        data-tag="higherBlock"
+      ></view>
+
+      <!-- 滑块值提示 -->
+      <view v-if="tipVisible" class="range-tip" :style="lowerTipStyle">{{ format(values[0]) }}</view>
+      <view v-if="tipVisible" class="range-tip" :style="higherTipStyle">{{ format(values[1]) }}</view>
+    </view>
+  </view>
+</template>
+<script>
+export default {
+  components: {},
+  props: {
+    //滑块区间当前取值
+    value: {
+      type: Array,
+      default: function() {
+        return [0, 100]
+      },
+    },
+    //最小值
+    min: {
+      type: Number,
+      default: 0,
+    },
+    //最大值
+    max: {
+      type: Number,
+      default: 100,
+    },
+    step: {
+      type: Number,
+      default: 1,
+    },
+    format: {
+      type: Function,
+      default: function(val) {
+        return val
+      },
+    },
+    disabled: {
+      type: Boolean,
+      default: false,
+    },
+    //滑块容器高度
+    height: {
+      height: Number,
+      default: 50,
+    },
+    //区间进度条高度
+    barHeight: {
+      type: Number,
+      default: 5,
+    },
+    //背景条颜色
+    backgroundColor: {
+      type: String,
+      default: '#e9e9e9',
+    },
+    //已选择的颜色
+    activeColor: {
+      type: String,
+      default: '#1aad19',
+    },
+    //滑块大小
+    blockSize: {
+      type: Number,
+      default: 20,
+    },
+    blockColor: {
+      type: String,
+      default: '#fff',
+    },
+    tipVisible: {
+      type: Boolean,
+      default: true,
+    },
+    decorationVisible: {
+      type: Boolean,
+      default: false,
+    },
+  },
+  data() {
+    return {
+      values: [this.min, this.max],
+      startDragPos: 0, // 开始拖动时的坐标位置
+      startVal: 0, //开始拖动时较小点的值
+    }
+  },
+  computed: {
+    // 较小点滑块的坐标
+    lowerHandlePosition() {
+      return ((this.values[0] - this.min) / (this.max - this.min)) * 100
+    },
+    // 较大点滑块的坐标
+    higherHandlePosition() {
+      return ((this.values[1] - this.min) / (this.max - this.min)) * 100
+    },
+    lowerTipStyle() {
+      if (this.lowerHandlePosition < 90) {
+        return `left: ${this.lowerHandlePosition}%;`
+      }
+      return `right: ${100 - this.lowerHandlePosition}%;transform: translate(50%, -100%);`
+    },
+    higherTipStyle() {
+      if (this.higherHandlePosition < 90) {
+        return `left: ${this.higherHandlePosition}%;`
+      }
+      return `right: ${100 - this.higherHandlePosition}%;transform: translate(50%, -100%);`
+    },
+  },
+  created: function() {},
+  onLoad: function(option) {},
+  watch: {
+    //滑块当前值
+    value: {
+      immediate: true,
+      handler(newVal, oldVal) {
+        if (this._isValuesValid(newVal) && (newVal[0] !== this.values[0] || newVal[1] !== this.values[1])) {
+          this._updateValue(newVal)
+        }
+      },
+    },
+  },
+  methods: {
+    _updateValue(newVal) {
+      // 步长大于区间差,或者区间最大值和最小值相等情况
+      if (this.step >= this.max - this.min) {
+        throw new RangeError('Invalid slider step or slider range')
+      }
+
+      let newValues = []
+      if (Array.isArray(newVal)) {
+        newValues = [newVal[0], newVal[1]]
+      }
+      if (typeof newValues[0] !== 'number') {
+        newValues[0] = this.values[0]
+      } else {
+        newValues[0] = Math.round((newValues[0] - this.min) / this.step) * this.step + this.min
+      }
+      if (typeof newValues[1] !== 'number') {
+        newValues[1] = this.values[1]
+      } else {
+        newValues[1] = Math.round((newValues[1] - this.min) / this.step) * this.step + this.min
+      }
+
+      // 新值与原值相等,不做处理
+      if (this.values[0] === newValues[0] && this.values[1] === newValues[1]) {
+        return
+      }
+      // 左侧滑块值小于最小值时,设置为最小值
+      if (newValues[0] < this.min) {
+        newValues[0] = this.min
+      }
+      // 右侧滑块值大于最大值时,设置为最大值
+      if (newValues[1] > this.max) {
+        newValues[1] = this.max
+      }
+      // 两个滑块重叠或左右交错,使两个滑块保持最小步长的间距
+      if (newValues[0] >= newValues[1]) {
+        // 左侧未动,右侧滑块滑到左侧滑块之左
+        if (newValues[0] === this.values[0]) {
+          newValues[1] = newValues[0] + this.step
+        } else {
+          // 右侧未动, 左侧滑块滑到右侧之右
+          newValues[0] = newValues[1] - this.step
+        }
+      }
+
+      this.values = newValues
+      this.$emit('change', this.values)
+    },
+    _onTouchStart: function(event) {
+      if (this.disabled) {
+        return
+      }
+
+      this.isDragging = true
+      let tag = event.target.dataset.tag
+
+      //兼容h5平台及某版本微信
+      let e = event.changedTouches ? event.changedTouches[0] : event
+      this.startDragPos = e.pageX
+
+      this.startVal = tag === 'lowerBlock' ? this.values[0] : this.values[1]
+    },
+    _onBlockTouchMove: function(e) {
+      if (this.disabled) {
+        return
+      }
+      this._onDrag(e)
+    },
+    _onBlockTouchEnd: function(e) {
+      if (this.disabled) {
+        return
+      }
+      this.isDragging = false
+      this._onDrag(e)
+    },
+    _onDrag(event) {
+      if (!this.isDragging) {
+        return
+      }
+      let view = uni
+        .createSelectorQuery()
+        .in(this)
+        .select('.slider-range-inner')
+
+      view
+        .boundingClientRect(data => {
+          let sliderWidth = data.width
+          const tag = event.target.dataset.tag
+          let e = event.changedTouches ? event.changedTouches[0] : event
+          let diff = ((e.pageX - this.startDragPos) / sliderWidth) * (this.max - this.min)
+          let nextVal = this.startVal + diff
+
+          if (tag === 'lowerBlock') {
+            this._updateValue([nextVal, null])
+          } else {
+            this._updateValue([null, nextVal])
+          }
+        })
+        .exec()
+    },
+    _isValuesValid: function(values) {
+      return Array.isArray(values) && values.length == 2
+    },
+  },
+}
+</script>
+
+<style scoped>
+.slider-range {
+  position: relative;
+  padding-top: 40rpx;
+}
+
+.slider-range-inner {
+  position: relative;
+  width: 100%;
+}
+
+.slider-range.disabled .slider-bar-inner {
+  opacity: 0.35;
+}
+
+.slider-range.disabled .slider-handle-block {
+  cursor: not-allowed;
+}
+
+.slider-bar {
+  position: absolute;
+  top: 50%;
+  left: 0;
+  right: 0;
+  transform: translateY(-50%);
+}
+
+.slider-bar-bg {
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  border-radius: 10000px;
+  z-index: 10;
+}
+
+.slider-bar-inner {
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  border-radius: 10000px;
+  z-index: 11;
+}
+
+.slider-handle-block {
+  position: absolute;
+  top: 50%;
+  transform: translate(-50%, -50%);
+  border-radius: 50%;
+  box-shadow: 0 0 3px 2px rgba(227, 229, 241, 0.5);
+  z-index: 12;
+}
+
+.slider-handle-block.decoration::before {
+  position: absolute;
+  content: '';
+  width: 6upx;
+  height: 24upx;
+  top: 50%;
+  left: 29%;
+  transform: translateY(-50%);
+  background: #eeedf2;
+  border-radius: 3upx;
+  z-index: 13;
+}
+
+.slider-handle-block.decoration::after {
+  position: absolute;
+  content: '';
+  width: 6upx;
+  height: 24upx;
+  top: 50%;
+  right: 29%;
+  transform: translateY(-50%);
+  background: #eeedf2;
+  border-radius: 3upx;
+  z-index: 13;
+}
+
+.range-tip {
+  position: absolute;
+  top: 0;
+  font-size: 24upx;
+  color: #666;
+  transform: translate(-50%, -100%);
+}
+</style>

+ 90 - 0
components/primewind-sliderrange/pages/slider-range/index.vue

@@ -0,0 +1,90 @@
+<template>
+  <view class="demo-slider-range">
+    <view class="section-title">普通用法</view>
+    <view class="slider-item">
+      <slider-range
+        :value="slider1.rangeValue"
+        :min="slider1.min"
+        :max="slider1.max"
+        :step="slider1.step"
+        :bar-height="barHeight"
+        :block-size="blockSize"
+        :background-color="backgroundColor"
+        :format="format1"
+        @change="handleRangeChange"
+      ></slider-range>
+    </view>
+    <view class="section-title">自定义</view>
+    <view class="slider-item">
+      <slider-range
+        :value="slider2.rangeValue"
+        :min="slider2.min"
+        :max="slider2.max"
+        :step="slider2.step"
+        :bar-height="barHeight"
+        :block-size="blockSize"
+        :background-color="backgroundColor"
+        :active-color="slider2.activeColor"
+        :format="format2"
+        :decorationVisible="slider2.decorationVisible"
+        @change="handleRangeChange"
+      ></slider-range>
+    </view>
+  </view>
+</template>
+
+<script>
+import SliderRange from '../../components/slider-range/index.vue'
+export default {
+  components: {
+    SliderRange,
+  },
+  data() {
+    return {
+      barHeight: 3,
+      blockSize: 26,
+      backgroundColor: '#EEEEF6',
+      slider1: {
+        min: 50,
+        max: 200,
+        step: 10,
+        rangeValue: [50, 150],
+      },
+      slider2: {
+        rangeMin: 0,
+        rangMax: 100,
+        rangeValue: [8, 80],
+        step: 1,
+        activeColor: '#FF6B00',
+        decorationVisible: true,
+      },
+    }
+  },
+  methods: {
+    format1(val) {
+      return val
+    },
+    format2(val) {
+      return `${val}%`
+    },
+    handleRangeChange(e) {
+      this.rangeValue = e
+    },
+  },
+}
+</script>
+<style>
+.demo-slider-range {
+  background-color: #fff;
+  padding: 100rpx 40rpx 0;
+}
+
+.section-title {
+  padding: 0 0 20rpx;
+  color: #666;
+}
+
+.slider-item:not(:last-child) {
+  margin-bottom: 100rpx;
+}
+</style>

+ 75 - 0
components/range-slider/README.md

@@ -0,0 +1,75 @@
+# uni-app-range-slider   
+uni-app 区间选择滑块   
+   
+说明:目前支持平台有APP、微信小程序、H5,其他平台理论上支持。   
+   
+
+## 效果图:   
+![效果图](https://zhangdaren.github.io/uni-app-range-slider/static/preview.png)   
+   
+## range-slider属性   
+   
+| 属性名 | 类型 | 默认值 | 说明 |
+|---|---|---|---|
+| width | Number | 750 | 组件宽度(rpx)|
+| height |Number |100 | 组件高度(rpx) |
+| block-size | Number | 50 | 滑块大小(rpx) |
+| bar-height | Number | 10 | 进度条高度(rpx) |
+| background-color | String | #e9e9e9 | 进度条背景色 |
+| active-color | String | #1aad19 | 已选择的颜色 |
+| min | Number | 0 | 最小值 |
+| max |Number | 100 | 最大值 |
+| values |Array| [0,100] | 当前区间值 |
+| step |Number| 1 | 步长值 |
+| liveMode |Boolean| true | 是否即时刷新数值,默认true |
+| @rangechange | EventHandle | |完成一次拖动后触发的事件,event.detail = {minValue: value1,maxValue:value2, originalValue:value3} |
+   
+---
+## tips:   
+修改自:https://github.com/Money888/wechat-rangeslider   
+   
+---
+## 更新历史说明:   
+## v1.0.11(20200110)   
+* [修复] step为10或更大时,数值计算出问题的bug   
+* 
+## v1.0.10(20200102)   
+* [优化] 增加拖动时圆点变大的效果   
+* [优化] 将所有upx替换为rpx   
+* [修复] 因为设置step,导致拖动会有偏移的bug   
+* 
+## v1.0.9(20190823)   
+* [修复] 修复使用step时,可能会遇到精度问题,达不到max值   
+* [修复] 修复使用min大于0时,会跳动的bug   
+
+## v1.0.8(20190719)   
+* [新增] liveMode模式,默认为true,即拖动就会刷新数值;设为false时,每次拖动只在touchEnd派发一次change事件   
+* [建议] 示例里之前step参数直接写为数值,在h5上会报错,现修改为data下面的变量   
+   
+### v1.0.7(20190604)   
+* [新增] 支持设置步长,如显示值为0.5, 1, 1.5……这类的值   
+* [新增] 增加一个选择时间区间的示例   
+* 回调函数里默认输出的是格式化后的值,为了取值方便,也并原始值一并给出,方便操作   
+   
+### v1.0.6(20190521)   
+* 适配最新自定义组件编译模式,如遇小程序/app不能正常使用时,请下载最新版   
+   
+### v1.0.5(20190417)   
+* 解决某种微信环境内,touchstart回调的e参数不完整的问题   
+   
+### v1.0.4(20190326)   
+* 解决手动赋值不生效的问题   
+   
+### v1.0.3(20190322)   
+* 解决h5平台上,无法设置初始值的问题   
+   
+### v1.0.2(20190306)   
+* 兼容H5,支持发布到浏览器   
+   
+### v1.0.1   
+* 增加拖动过程中,自动更新滑块值功能。   
+   
+### v1.0.0   
+* 初次提交   
+
+

+ 219 - 0
components/range-slider/pages/index/index.vue

@@ -0,0 +1,219 @@
+<template>
+	<view class="content">
+		<view class="text-center mrg50T"><text class="title">区间选择滑块/范围选择滑块</text></view>
+
+		<view class="part part1 mrg50T">
+			<view class="title">1. 默认示例:</view>
+			<view class="text-center mrg10T">
+				<text>区间:</text>
+				<text>{{ rangeValues[0] }}</text>
+				<text>~</text>
+				<text>{{ rangeValues[1] }}</text>
+			</view>
+			<view class="rowBox mrg50T">
+				<view class="sliderBox">
+					<RangeSlider
+						:width="slideWidth"
+						:height="slideHeight"
+						:blockSize="slideBlockSize"
+						:min="slideMin"
+						:max="slideMax"
+						:values="rangeValues"
+						:step="step"
+						:liveMode="isLiveMode"
+						@rangechange="onRangeChange"
+					>
+						<view slot="minBlock" class="range-slider-block"></view>
+						<!-- 左边滑块的内容 -->
+						<view slot="maxBlock" class="range-slider-block"></view>
+						<!-- 右边滑块的内容 -->
+					</RangeSlider>
+				</view>
+			</view>
+
+			<button @tap="test" class="testBtn">手动设置为10~60</button>
+		</view>
+
+		<view class="part part2 mrg50T">
+			<view class="title">2. 示例:将原始数据格式化为时间显示</view>
+			<view class="text-center mrg50T">
+				<RangeSlider
+					:width="slideWidth"
+					:height="slideHeight"
+					:blockSize="slideBlockSize"
+					:min="timeMinValue"
+					:max="timeMaxValue"
+					:activeColor="'#E68B28'"
+					:values="rangeValues2"
+					@rangechange="onRangeChange2"
+				>
+					<view slot="minBlock" class="range-slider-block"></view>
+					<!-- 左边滑块的内容 -->
+					<view slot="maxBlock" class="range-slider-block"></view>
+					<!-- 右边滑块的内容 -->
+				</RangeSlider>
+				<text class="plan-unit">{{ serTime }}</text>
+			</view>
+		</view>
+
+		<view class="part part3 mrg50T">
+			<view class="title">3. 示例:页面第三个滑块</view>
+
+			<view class="text-center mrg10T">
+				<text>区间:</text>
+				<text>{{ rangeValues3[0] }}</text>
+				<text>~</text>
+				<text>{{ rangeValues3[1] }}</text>
+			</view>
+
+			<view class="text-center mrg50T">
+				<RangeSlider
+					:width="slideWidth"
+					:height="slideHeight"
+					:blockSize="slideBlockSize"
+					:min="slideMin"
+					:max="slideMax"
+					:values="rangeValues3"
+					:step="step"
+					:liveMode="isLiveMode"
+					@rangechange="onRangeChange3"
+				>
+					<view slot="minBlock" class="range-slider-block"></view>
+					<!-- 左边滑块的内容 -->
+					<view slot="maxBlock" class="range-slider-block"></view>
+					<!-- 右边滑块的内容 -->
+				</RangeSlider>
+			</view>
+		</view>
+
+		<text class="tips">修改自:https://github.com/Money888/wechat-rangeslider</text>
+	</view>
+</template>
+
+<script>
+import RangeSlider from '../../components/range-slider/range-slider.vue';
+
+export default {
+	data() {
+		return {
+			// rangeValues: [2, 5], //当前区间值
+			// slideWidth: 350, //宽度
+			// slideHeight: 80,  //高度
+			// slideBlockSize: 56, //圆形按钮大小
+			// slideMin: 0,  //slider最小值
+			// slideMax: 12,  //slider最大值
+
+			rangeValues: [4, 5.2],
+			slideWidth: 350,
+			slideHeight: 80,
+			slideBlockSize: 30,
+			slideMin: 0,
+			slideMax: 10,
+			isLiveMode: true,
+			step: 0.1,
+			//
+			timeMinValue: 0,
+			timeMaxValue: 10,
+
+			rangeValues2: [1, 6],
+			serTime: '02:24:00-14:24:00',
+
+			rangeValues3: [3, 5]
+		};
+	},
+	components: {
+		RangeSlider
+	},
+	onLoad() {
+		console.log('index onload');
+	},
+	methods: {
+		pad: function(num, n) {
+			return Array(n - ('' + num).length + 1).join(0) + num;
+		},
+		onRangeChange: function(e) {
+			this.rangeValues = [e.minValue, e.maxValue];
+
+			console.log(this.rangeValues);
+			console.log(JSON.stringify(e));
+		},
+		test: function() {
+			this.rangeValues = [4.2, 6.6];
+		},
+		onRangeChange2: function(e) {
+			let startTime = this.formatTimeBySliderValue(e.originalValue.minValue);
+			let endTime = this.formatTimeBySliderValue(e.originalValue.maxValue);
+			this.serTime = startTime + '-' + endTime;
+		},
+		onRangeChange3: function(e) {
+			this.rangeValues3 = [e.minValue, e.maxValue];
+		},
+		formatTimeBySliderValue(value) {
+			//按比例,将滑块上面的数值进行转换为时间形式
+			//转换为分钟数
+			let minutes = (24 * 60 * value) / this.slideMax;
+			//转换为小时数
+			let hours = parseInt(minutes / 60);
+			//剩余分钟数
+			let minutes_min = parseInt(minutes % 60);
+			return '' + this.pad(hours, 2) + ':' + this.pad(minutes_min, 2) + ':' + '00';
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+view {
+	display: flex;
+}
+
+.content {
+	justify-content: center;
+	flex-direction: column;
+}
+
+.sliderBox {
+	justify-content: center;
+	margin-right: 50rpx;
+}
+
+.text-center {
+	justify-content: center;
+}
+
+.rowBox {
+	flex-direction: row;
+	align-items: center;
+	justify-content: center;
+}
+
+.mrg10T {
+	margin-top: 10rpx;
+}
+
+.tips {
+	color: #999;
+	font-size: 24rpx;
+	text-align: center;
+	margin-top: 100rpx;
+}
+
+.testBtn {
+	margin-top: 50rpx;
+}
+
+.part {
+	flex-direction: column;
+	justify-content: center;
+	border-top: 1rpx solid #ccc;
+	padding-top: 50rpx;
+	.title {
+		font-size: 32rpx;
+		padding: 0 30rpx;
+	}
+}
+
+.part2 {
+	margin-top: 100rpx;
+}
+</style>

+ 391 - 0
components/range-slider/range-slider.vue

@@ -0,0 +1,391 @@
+<template>
+	<view class="range-slider" :style="'width:' + width + 'rpx;height:' + height + 'rpx'">
+		<view class="range-bar" :style="'width:100%;height:' + barHeight + 'rpx'">
+			<view class="range-bar-bg" :style="'background-color:' + backgroundColor + ''"></view>
+			<view class="range-bar-progress" :style="'margin-left:' + progressBarLeft + 'rpx;width:' + progressBarWidth + 'rpx;background-color:' + activeColor + ''"></view>
+		</view>
+
+		<view
+			class="block"
+			:class="{ active: isMinActive }"
+			:style="'width:' + blockSize + 'rpx;height:' + blockSize + 'rpx;margin-left:' + minBlockLeft + 'rpx;'"
+			@touchstart="_onBlockTouchStart"
+			@touchmove.stop="_onBlockTouchMove"
+			@touchend="_onBlockTouchEnd"
+			:data-left="minBlockLeft"
+			data-tag="minBlock"
+		>
+			<slot name="minBlock"></slot>
+		</view>
+		<view
+			class="block"
+			:class="{ active: isMaxActive }"
+			:style="'width:' + blockSize + 'rpx;height:' + blockSize + 'rpx;margin-left:' + maxBlockLeft + 'rpx;'"
+			@touchstart="_onBlockTouchStart"
+			@touchmove.stop="_onBlockTouchMove"
+			@touchend="_onBlockTouchEnd"
+			:data-left="maxBlockLeft"
+			data-tag="maxBlock"
+		>
+			<slot name="maxBlock"></slot>
+		</view>
+	</view>
+</template>
+<script>
+/**
+ * range-slider v1.0.6
+ */
+const _windowWidth = uni.getSystemInfoSync().windowWidth;
+
+export default {
+	data() {
+		return {
+			isMinActive: false,
+			isMaxActive: false,
+
+			//#ifdef H5
+			MAX_LENGTH: 294,
+			maxBlockLeft: 300,
+			//#endif
+
+			// #ifndef H5
+			MAX_LENGTH: 700,
+			maxBlockLeft: 350,
+			// #endif
+
+			minBlockLeft: 0,
+			progressBarLeft: 0,
+			progressBarWidth: 350,
+
+			originalMinValue: 0,
+			originalMaxValue: 0
+		};
+	},
+	components: {},
+	props: {
+		//组件宽度
+		width: {
+			type: Number,
+			default: 750
+		},
+		//组件高度
+		height: {
+			type: Number,
+			default: 100
+		},
+		//滑块大小
+		blockSize: {
+			type: Number,
+			default: 50
+		},
+		//区间进度条高度
+		barHeight: {
+			type: Number,
+			default: 5
+		},
+		//背景条颜色
+		backgroundColor: {
+			type: String,
+			default: '#e9e9e9'
+		},
+		//已选择的颜色
+		activeColor: {
+			type: String,
+			default: '#1aad19'
+		},
+		//最小值
+		min: {
+			type: Number,
+			default: 0
+		},
+		//最大值
+		max: {
+			type: Number,
+			default: 100
+		},
+		//设置初始值
+		values: {
+			type: Array,
+			default: function() {
+				return [0, 100];
+			}
+		},
+		//步长值
+		step: {
+			type: Number,
+			default: 1
+		},
+		//live模式,是否动态更新
+		liveMode: {
+			type: Boolean,
+			default: true
+		}
+	},
+	created: function() {
+		//使用自定义组件编译模式时,支持生命周期为:created
+		this._refresh();
+	},
+	onLoad: function(option) {
+		//不使用自定义组件编译模式时,支持生命周期为:onload
+		this._refresh();
+	},
+	onUnload: function() {},
+	watch: {
+		//组件宽度
+		width: function(newVal, oldVal, changedPath) {
+			var that = this;
+			if (newVal != that.width) {
+				this._refresh();
+			}
+		},
+		//滑块大小
+		blockSize: function(newVal, oldVal, changedPath) {
+			var that = this;
+			if (newVal != that.blockSize) {
+				this._refresh();
+			}
+		},
+		//最小值
+		min: function(newVal, oldVal, changedPath) {
+			var that = this;
+			if (newVal != that.min) {
+				that._refresh();
+			}
+		},
+		//最大值
+		max: function(newVal, oldVal, changedPath) {
+			var that = this;
+			if (newVal != that.max) {
+				that._refresh();
+			}
+		},
+		//设置初始值
+		values: function(newVal, oldVal, changedPath) {
+			var that = this;
+			var values = that.values;
+			console.log('refresh', newVal, oldVal);
+			if (that._isValuesValid(newVal) && that._isValuesValid(values)) {
+				if (values[0] != oldVal[0] || values[1] != oldVal[1]) that._refresh();
+			}
+		}
+	},
+	methods: {
+		//补0
+		_pad: function(num, n) {
+			return Array(n - ('' + num).length + 1).join(0) + num;
+		},
+		_pxToRpx: function(px) {
+			return (750 * px) / _windowWidth;
+		},
+		_onBlockTouchStart: function(e) {
+			let tag = e.target.dataset.tag;
+			if (tag == 'minBlock' || tag == 'maxBlock') {
+				this.isMinActive = tag == 'minBlock';
+				this.isMaxActive = tag == 'maxBlock';
+
+				//兼容h5平台及某版本微信
+				if (e.hasOwnProperty('changedTouches')) {
+					this._blockDownX = e.changedTouches[0].pageX;
+				} else {
+					this._blockDownX = e.pageX;
+				}
+
+				//#ifdef H5
+				this._blockLeft = parseFloat(e.target.dataset.left);
+				//#endif
+				// #ifndef H5
+				this._blockLeft = e.target.dataset.left;
+				// #endif
+
+				this._curBlock = e.target.dataset.tag;
+			}
+		},
+		_onBlockTouchMove: function(e) {
+			let tag = e.target.dataset.tag;
+			if (tag == 'minBlock' || tag == 'maxBlock') {
+				var that = this;
+				var values = that._calculateValues(e);
+				that._refreshProgressBar(values[2], values[3]);
+				that._refreshBlock(values[2], values[3]);
+				//拖动时也触发事件
+				var eventDetail = {
+					minValue: this.formatNumber(values[0], this.step),
+					maxValue: this.formatNumber(values[1], this.step),
+					fromUser: true,
+					originalValue: {
+						minValue: values[0],
+						maxValue: values[1]
+					}
+				};
+
+				this.originalMinValue = values[0];
+				this.originalMaxValue = values[1];
+				var eventOption = {};
+				//
+				if (this.liveMode) that.$emit('rangechange', eventDetail, eventOption);
+			}
+		},
+		_onBlockTouchEnd: function(e) {
+			let tag = e.target.dataset.tag;
+			this.isMinActive = false;
+			this.isMaxActive = false;
+			if (tag == 'minBlock' || tag == 'maxBlock') {
+				var that = this;
+				var values = that._calculateValues(e.mp.changedTouches[0]);
+				that._refreshProgressBar(values[2], values[3]);
+				that._refreshBlock(values[2], values[3]);
+				var eventDetail = {
+					minValue: this.formatNumber(values[0], this.step),
+					maxValue: this.formatNumber(values[1], this.step),
+					fromUser: true,
+					originalValue: {
+						minValue: values[0],
+						maxValue: values[1]
+					}
+				};
+				this.originalMinValue = values[0];
+				this.originalMaxValue = values[1];
+				var eventOption = {};
+				that.$emit('rangechange', eventDetail, eventOption);
+			}
+		},
+		_isValuesValid: function(values) {
+			return values != null && values != undefined && values.length == 2;
+		},
+		/**
+		 * 根据手势计算相关数据
+		 */
+		_calculateValues: function(e) {
+			var pageX = e.pageX;
+			//兼容h5平台
+			if (e.hasOwnProperty('changedTouches')) {
+				pageX = e.changedTouches[0].pageX;
+			}
+
+			var that = this;
+			var moveLength = pageX - that._blockDownX;
+			var left = that._blockLeft + that._pxToRpx(moveLength);
+			left = Math.max(0, left);
+			left = Math.min(left, that.MAX_LENGTH);
+			var minBlockLeft = that.minBlockLeft;
+			var maxBlockLeft = that.maxBlockLeft;
+			if (that._curBlock == 'minBlock') {
+				minBlockLeft = left;
+			} else {
+				maxBlockLeft = left;
+			}
+			var range = that.max - that.min;
+			var minLeft = Math.min(minBlockLeft, maxBlockLeft);
+			var maxLeft = Math.max(minBlockLeft, maxBlockLeft);
+			var minValue = (minLeft / that.MAX_LENGTH) * range + that.min;
+			var maxValue = (maxLeft / that.MAX_LENGTH) * range + that.min;
+			return [minValue, maxValue, minLeft, maxLeft];
+		},
+		/**
+		 * 计算滑块坐标
+		 */
+		_calculateBlockLeft: function(minValue, maxValue) {
+			var that = this;
+			var blockSize = that.blockSize;
+			var range = that.max - that.min;
+			var minLeft = ((minValue - that.min) / range) * that.MAX_LENGTH;
+			var maxLeft = ((maxValue - that.min) / range) * that.MAX_LENGTH;
+			return [minLeft, maxLeft];
+		},
+		/**
+		 * 刷新进度条视图
+		 */
+		_refreshProgressBar: function(minBlockLeft, maxBlockLeft) {
+			var that = this;
+			var blockSize = that.blockSize;
+			that.progressBarLeft = minBlockLeft + blockSize / 2;
+			that.progressBarWidth = Math.abs(maxBlockLeft - minBlockLeft);
+		},
+		/**
+		 * 刷新滑块视图
+		 */
+		_refreshBlock: function(minBlockLeft, maxBlockLeft) {
+			var that = this;
+			that.minBlockLeft = minBlockLeft;
+			that.maxBlockLeft = maxBlockLeft;
+		},
+		/**
+		 * 刷新整个视图
+		 */
+		_refresh: function() {
+			var that = this;
+			var MAX_LENGTH = that.width - that.blockSize;
+			that.MAX_LENGTH = MAX_LENGTH;
+			that.maxBlockLeft = MAX_LENGTH;
+			that.progressBarWidth = MAX_LENGTH;
+			var values = that.values;
+			if (this.originalMinValue && this.originalMinValue) {
+				values = [this.originalMinValue || values[0], this.originalMaxValue || values[1]];
+			}
+
+			if (that._isValuesValid(values)) {
+				values[0] = Math.max(that.min, values[0]);
+				values[0] = Math.min(values[0], that.max);
+				values[1] = Math.max(that.min, values[1]);
+				values[1] = Math.min(values[1], that.max);
+				var leftValues = that._calculateBlockLeft(values[0], values[1]);
+				that._refreshProgressBar(leftValues[0], leftValues[1]);
+				that._refreshBlock(leftValues[0], leftValues[1]);
+			}
+		},
+		formatNumber(num, step) {
+			//格式化数字,保留几位小数
+			let stepStr = '' + step;
+			let index = stepStr.indexOf('.');
+			let len = index > -1 ? stepStr.length - index - 1 : 0;
+			let offestNum = parseInt(1 + Array(('' + len).length + 1).join(0)) * 0.1;
+			let tmpNum = num * offestNum;
+			return ((parseInt(tmpNum / step + (step > 1 ? 1 : step) * 0.5) * step) / offestNum).toFixed(len);
+		}
+	}
+};
+</script>
+
+<style>
+.range-slider {
+	position: relative;
+}
+
+.range-bar {
+	position: absolute;
+}
+
+.range-bar {
+	position: absolute;
+	top: 50%;
+	transform: translate(0, -50%);
+	border-radius: 10000rpx;
+}
+
+.range-bar-bg {
+	position: absolute;
+	width: 100%;
+	height: 100%;
+	border-radius: 10000rpx;
+}
+
+.range-bar-progress {
+	position: absolute;
+	width: 100%;
+	height: 100%;
+	background-color: blueviolet;
+}
+
+.block {
+	position: absolute;
+	top: 50%;
+	transform: translate(0, -50%);
+	background: #fff;
+	border-radius: 50%;
+	box-shadow: 0rpx 0rpx 6rpx #ccc;
+}
+
+.block.active {
+	transform: translate(0, -50%) scale(1.5);
+}
+</style>

+ 75 - 0
range-slider_1.0.11/README.md

@@ -0,0 +1,75 @@
+# uni-app-range-slider   
+uni-app 区间选择滑块   
+   
+说明:目前支持平台有APP、微信小程序、H5,其他平台理论上支持。   
+   
+
+## 效果图:   
+![效果图](https://zhangdaren.github.io/uni-app-range-slider/static/preview.png)   
+   
+## range-slider属性   
+   
+| 属性名 | 类型 | 默认值 | 说明 |
+|---|---|---|---|
+| width | Number | 750 | 组件宽度(rpx)|
+| height |Number |100 | 组件高度(rpx) |
+| block-size | Number | 50 | 滑块大小(rpx) |
+| bar-height | Number | 10 | 进度条高度(rpx) |
+| background-color | String | #e9e9e9 | 进度条背景色 |
+| active-color | String | #1aad19 | 已选择的颜色 |
+| min | Number | 0 | 最小值 |
+| max |Number | 100 | 最大值 |
+| values |Array| [0,100] | 当前区间值 |
+| step |Number| 1 | 步长值 |
+| liveMode |Boolean| true | 是否即时刷新数值,默认true |
+| @rangechange | EventHandle | |完成一次拖动后触发的事件,event.detail = {minValue: value1,maxValue:value2, originalValue:value3} |
+   
+---
+## tips:   
+修改自:https://github.com/Money888/wechat-rangeslider   
+   
+---
+## 更新历史说明:   
+## v1.0.11(20200110)   
+* [修复] step为10或更大时,数值计算出问题的bug   
+* 
+## v1.0.10(20200102)   
+* [优化] 增加拖动时圆点变大的效果   
+* [优化] 将所有upx替换为rpx   
+* [修复] 因为设置step,导致拖动会有偏移的bug   
+* 
+## v1.0.9(20190823)   
+* [修复] 修复使用step时,可能会遇到精度问题,达不到max值   
+* [修复] 修复使用min大于0时,会跳动的bug   
+
+## v1.0.8(20190719)   
+* [新增] liveMode模式,默认为true,即拖动就会刷新数值;设为false时,每次拖动只在touchEnd派发一次change事件   
+* [建议] 示例里之前step参数直接写为数值,在h5上会报错,现修改为data下面的变量   
+   
+### v1.0.7(20190604)   
+* [新增] 支持设置步长,如显示值为0.5, 1, 1.5……这类的值   
+* [新增] 增加一个选择时间区间的示例   
+* 回调函数里默认输出的是格式化后的值,为了取值方便,也并原始值一并给出,方便操作   
+   
+### v1.0.6(20190521)   
+* 适配最新自定义组件编译模式,如遇小程序/app不能正常使用时,请下载最新版   
+   
+### v1.0.5(20190417)   
+* 解决某种微信环境内,touchstart回调的e参数不完整的问题   
+   
+### v1.0.4(20190326)   
+* 解决手动赋值不生效的问题   
+   
+### v1.0.3(20190322)   
+* 解决h5平台上,无法设置初始值的问题   
+   
+### v1.0.2(20190306)   
+* 兼容H5,支持发布到浏览器   
+   
+### v1.0.1   
+* 增加拖动过程中,自动更新滑块值功能。   
+   
+### v1.0.0   
+* 初次提交   
+
+

+ 391 - 0
range-slider_1.0.11/components/range-slider/range-slider.vue

@@ -0,0 +1,391 @@
+<template>
+	<view class="range-slider" :style="'width:' + width + 'rpx;height:' + height + 'rpx'">
+		<view class="range-bar" :style="'width:100%;height:' + barHeight + 'rpx'">
+			<view class="range-bar-bg" :style="'background-color:' + backgroundColor + ''"></view>
+			<view class="range-bar-progress" :style="'margin-left:' + progressBarLeft + 'rpx;width:' + progressBarWidth + 'rpx;background-color:' + activeColor + ''"></view>
+		</view>
+
+		<view
+			class="block"
+			:class="{ active: isMinActive }"
+			:style="'width:' + blockSize + 'rpx;height:' + blockSize + 'rpx;margin-left:' + minBlockLeft + 'rpx;'"
+			@touchstart="_onBlockTouchStart"
+			@touchmove.stop="_onBlockTouchMove"
+			@touchend="_onBlockTouchEnd"
+			:data-left="minBlockLeft"
+			data-tag="minBlock"
+		>
+			<slot name="minBlock"></slot>
+		</view>
+		<view
+			class="block"
+			:class="{ active: isMaxActive }"
+			:style="'width:' + blockSize + 'rpx;height:' + blockSize + 'rpx;margin-left:' + maxBlockLeft + 'rpx;'"
+			@touchstart="_onBlockTouchStart"
+			@touchmove.stop="_onBlockTouchMove"
+			@touchend="_onBlockTouchEnd"
+			:data-left="maxBlockLeft"
+			data-tag="maxBlock"
+		>
+			<slot name="maxBlock"></slot>
+		</view>
+	</view>
+</template>
+<script>
+/**
+ * range-slider v1.0.6
+ */
+const _windowWidth = uni.getSystemInfoSync().windowWidth;
+
+export default {
+	data() {
+		return {
+			isMinActive: false,
+			isMaxActive: false,
+
+			//#ifdef H5
+			MAX_LENGTH: 294,
+			maxBlockLeft: 300,
+			//#endif
+
+			// #ifndef H5
+			MAX_LENGTH: 700,
+			maxBlockLeft: 350,
+			// #endif
+
+			minBlockLeft: 0,
+			progressBarLeft: 0,
+			progressBarWidth: 350,
+
+			originalMinValue: 0,
+			originalMaxValue: 0
+		};
+	},
+	components: {},
+	props: {
+		//组件宽度
+		width: {
+			type: Number,
+			default: 750
+		},
+		//组件高度
+		height: {
+			type: Number,
+			default: 100
+		},
+		//滑块大小
+		blockSize: {
+			type: Number,
+			default: 50
+		},
+		//区间进度条高度
+		barHeight: {
+			type: Number,
+			default: 5
+		},
+		//背景条颜色
+		backgroundColor: {
+			type: String,
+			default: '#e9e9e9'
+		},
+		//已选择的颜色
+		activeColor: {
+			type: String,
+			default: '#1aad19'
+		},
+		//最小值
+		min: {
+			type: Number,
+			default: 0
+		},
+		//最大值
+		max: {
+			type: Number,
+			default: 100
+		},
+		//设置初始值
+		values: {
+			type: Array,
+			default: function() {
+				return [0, 100];
+			}
+		},
+		//步长值
+		step: {
+			type: Number,
+			default: 1
+		},
+		//live模式,是否动态更新
+		liveMode: {
+			type: Boolean,
+			default: true
+		}
+	},
+	created: function() {
+		//使用自定义组件编译模式时,支持生命周期为:created
+		this._refresh();
+	},
+	onLoad: function(option) {
+		//不使用自定义组件编译模式时,支持生命周期为:onload
+		this._refresh();
+	},
+	onUnload: function() {},
+	watch: {
+		//组件宽度
+		width: function(newVal, oldVal, changedPath) {
+			var that = this;
+			if (newVal != that.width) {
+				this._refresh();
+			}
+		},
+		//滑块大小
+		blockSize: function(newVal, oldVal, changedPath) {
+			var that = this;
+			if (newVal != that.blockSize) {
+				this._refresh();
+			}
+		},
+		//最小值
+		min: function(newVal, oldVal, changedPath) {
+			var that = this;
+			if (newVal != that.min) {
+				that._refresh();
+			}
+		},
+		//最大值
+		max: function(newVal, oldVal, changedPath) {
+			var that = this;
+			if (newVal != that.max) {
+				that._refresh();
+			}
+		},
+		//设置初始值
+		values: function(newVal, oldVal, changedPath) {
+			var that = this;
+			var values = that.values;
+			console.log('refresh', newVal, oldVal);
+			if (that._isValuesValid(newVal) && that._isValuesValid(values)) {
+				if (values[0] != oldVal[0] || values[1] != oldVal[1]) that._refresh();
+			}
+		}
+	},
+	methods: {
+		//补0
+		_pad: function(num, n) {
+			return Array(n - ('' + num).length + 1).join(0) + num;
+		},
+		_pxToRpx: function(px) {
+			return (750 * px) / _windowWidth;
+		},
+		_onBlockTouchStart: function(e) {
+			let tag = e.target.dataset.tag;
+			if (tag == 'minBlock' || tag == 'maxBlock') {
+				this.isMinActive = tag == 'minBlock';
+				this.isMaxActive = tag == 'maxBlock';
+
+				//兼容h5平台及某版本微信
+				if (e.hasOwnProperty('changedTouches')) {
+					this._blockDownX = e.changedTouches[0].pageX;
+				} else {
+					this._blockDownX = e.pageX;
+				}
+
+				//#ifdef H5
+				this._blockLeft = parseFloat(e.target.dataset.left);
+				//#endif
+				// #ifndef H5
+				this._blockLeft = e.target.dataset.left;
+				// #endif
+
+				this._curBlock = e.target.dataset.tag;
+			}
+		},
+		_onBlockTouchMove: function(e) {
+			let tag = e.target.dataset.tag;
+			if (tag == 'minBlock' || tag == 'maxBlock') {
+				var that = this;
+				var values = that._calculateValues(e);
+				that._refreshProgressBar(values[2], values[3]);
+				that._refreshBlock(values[2], values[3]);
+				//拖动时也触发事件
+				var eventDetail = {
+					minValue: this.formatNumber(values[0], this.step),
+					maxValue: this.formatNumber(values[1], this.step),
+					fromUser: true,
+					originalValue: {
+						minValue: values[0],
+						maxValue: values[1]
+					}
+				};
+
+				this.originalMinValue = values[0];
+				this.originalMaxValue = values[1];
+				var eventOption = {};
+				//
+				if (this.liveMode) that.$emit('rangechange', eventDetail, eventOption);
+			}
+		},
+		_onBlockTouchEnd: function(e) {
+			let tag = e.target.dataset.tag;
+			this.isMinActive = false;
+			this.isMaxActive = false;
+			if (tag == 'minBlock' || tag == 'maxBlock') {
+				var that = this;
+				var values = that._calculateValues(e.mp.changedTouches[0]);
+				that._refreshProgressBar(values[2], values[3]);
+				that._refreshBlock(values[2], values[3]);
+				var eventDetail = {
+					minValue: this.formatNumber(values[0], this.step),
+					maxValue: this.formatNumber(values[1], this.step),
+					fromUser: true,
+					originalValue: {
+						minValue: values[0],
+						maxValue: values[1]
+					}
+				};
+				this.originalMinValue = values[0];
+				this.originalMaxValue = values[1];
+				var eventOption = {};
+				that.$emit('rangechange', eventDetail, eventOption);
+			}
+		},
+		_isValuesValid: function(values) {
+			return values != null && values != undefined && values.length == 2;
+		},
+		/**
+		 * 根据手势计算相关数据
+		 */
+		_calculateValues: function(e) {
+			var pageX = e.pageX;
+			//兼容h5平台
+			if (e.hasOwnProperty('changedTouches')) {
+				pageX = e.changedTouches[0].pageX;
+			}
+
+			var that = this;
+			var moveLength = pageX - that._blockDownX;
+			var left = that._blockLeft + that._pxToRpx(moveLength);
+			left = Math.max(0, left);
+			left = Math.min(left, that.MAX_LENGTH);
+			var minBlockLeft = that.minBlockLeft;
+			var maxBlockLeft = that.maxBlockLeft;
+			if (that._curBlock == 'minBlock') {
+				minBlockLeft = left;
+			} else {
+				maxBlockLeft = left;
+			}
+			var range = that.max - that.min;
+			var minLeft = Math.min(minBlockLeft, maxBlockLeft);
+			var maxLeft = Math.max(minBlockLeft, maxBlockLeft);
+			var minValue = (minLeft / that.MAX_LENGTH) * range + that.min;
+			var maxValue = (maxLeft / that.MAX_LENGTH) * range + that.min;
+			return [minValue, maxValue, minLeft, maxLeft];
+		},
+		/**
+		 * 计算滑块坐标
+		 */
+		_calculateBlockLeft: function(minValue, maxValue) {
+			var that = this;
+			var blockSize = that.blockSize;
+			var range = that.max - that.min;
+			var minLeft = ((minValue - that.min) / range) * that.MAX_LENGTH;
+			var maxLeft = ((maxValue - that.min) / range) * that.MAX_LENGTH;
+			return [minLeft, maxLeft];
+		},
+		/**
+		 * 刷新进度条视图
+		 */
+		_refreshProgressBar: function(minBlockLeft, maxBlockLeft) {
+			var that = this;
+			var blockSize = that.blockSize;
+			that.progressBarLeft = minBlockLeft + blockSize / 2;
+			that.progressBarWidth = Math.abs(maxBlockLeft - minBlockLeft);
+		},
+		/**
+		 * 刷新滑块视图
+		 */
+		_refreshBlock: function(minBlockLeft, maxBlockLeft) {
+			var that = this;
+			that.minBlockLeft = minBlockLeft;
+			that.maxBlockLeft = maxBlockLeft;
+		},
+		/**
+		 * 刷新整个视图
+		 */
+		_refresh: function() {
+			var that = this;
+			var MAX_LENGTH = that.width - that.blockSize;
+			that.MAX_LENGTH = MAX_LENGTH;
+			that.maxBlockLeft = MAX_LENGTH;
+			that.progressBarWidth = MAX_LENGTH;
+			var values = that.values;
+			if (this.originalMinValue && this.originalMinValue) {
+				values = [this.originalMinValue || values[0], this.originalMaxValue || values[1]];
+			}
+
+			if (that._isValuesValid(values)) {
+				values[0] = Math.max(that.min, values[0]);
+				values[0] = Math.min(values[0], that.max);
+				values[1] = Math.max(that.min, values[1]);
+				values[1] = Math.min(values[1], that.max);
+				var leftValues = that._calculateBlockLeft(values[0], values[1]);
+				that._refreshProgressBar(leftValues[0], leftValues[1]);
+				that._refreshBlock(leftValues[0], leftValues[1]);
+			}
+		},
+		formatNumber(num, step) {
+			//格式化数字,保留几位小数
+			let stepStr = '' + step;
+			let index = stepStr.indexOf('.');
+			let len = index > -1 ? stepStr.length - index - 1 : 0;
+			let offestNum = parseInt(1 + Array(('' + len).length + 1).join(0)) * 0.1;
+			let tmpNum = num * offestNum;
+			return ((parseInt(tmpNum / step + (step > 1 ? 1 : step) * 0.5) * step) / offestNum).toFixed(len);
+		}
+	}
+};
+</script>
+
+<style>
+.range-slider {
+	position: relative;
+}
+
+.range-bar {
+	position: absolute;
+}
+
+.range-bar {
+	position: absolute;
+	top: 50%;
+	transform: translate(0, -50%);
+	border-radius: 10000rpx;
+}
+
+.range-bar-bg {
+	position: absolute;
+	width: 100%;
+	height: 100%;
+	border-radius: 10000rpx;
+}
+
+.range-bar-progress {
+	position: absolute;
+	width: 100%;
+	height: 100%;
+	background-color: blueviolet;
+}
+
+.block {
+	position: absolute;
+	top: 50%;
+	transform: translate(0, -50%);
+	background: #fff;
+	border-radius: 50%;
+	box-shadow: 0rpx 0rpx 6rpx #ccc;
+}
+
+.block.active {
+	transform: translate(0, -50%) scale(1.5);
+}
+</style>

+ 219 - 0
range-slider_1.0.11/pages/index/index.vue

@@ -0,0 +1,219 @@
+<template>
+	<view class="content">
+		<view class="text-center mrg50T"><text class="title">区间选择滑块/范围选择滑块</text></view>
+
+		<view class="part part1 mrg50T">
+			<view class="title">1. 默认示例:</view>
+			<view class="text-center mrg10T">
+				<text>区间:</text>
+				<text>{{ rangeValues[0] }}</text>
+				<text>~</text>
+				<text>{{ rangeValues[1] }}</text>
+			</view>
+			<view class="rowBox mrg50T">
+				<view class="sliderBox">
+					<RangeSlider
+						:width="slideWidth"
+						:height="slideHeight"
+						:blockSize="slideBlockSize"
+						:min="slideMin"
+						:max="slideMax"
+						:values="rangeValues"
+						:step="step"
+						:liveMode="isLiveMode"
+						@rangechange="onRangeChange"
+					>
+						<view slot="minBlock" class="range-slider-block"></view>
+						<!-- 左边滑块的内容 -->
+						<view slot="maxBlock" class="range-slider-block"></view>
+						<!-- 右边滑块的内容 -->
+					</RangeSlider>
+				</view>
+			</view>
+
+			<button @tap="test" class="testBtn">手动设置为10~60</button>
+		</view>
+
+		<view class="part part2 mrg50T">
+			<view class="title">2. 示例:将原始数据格式化为时间显示</view>
+			<view class="text-center mrg50T">
+				<RangeSlider
+					:width="slideWidth"
+					:height="slideHeight"
+					:blockSize="slideBlockSize"
+					:min="timeMinValue"
+					:max="timeMaxValue"
+					:activeColor="'#E68B28'"
+					:values="rangeValues2"
+					@rangechange="onRangeChange2"
+				>
+					<view slot="minBlock" class="range-slider-block"></view>
+					<!-- 左边滑块的内容 -->
+					<view slot="maxBlock" class="range-slider-block"></view>
+					<!-- 右边滑块的内容 -->
+				</RangeSlider>
+				<text class="plan-unit">{{ serTime }}</text>
+			</view>
+		</view>
+
+		<view class="part part3 mrg50T">
+			<view class="title">3. 示例:页面第三个滑块</view>
+
+			<view class="text-center mrg10T">
+				<text>区间:</text>
+				<text>{{ rangeValues3[0] }}</text>
+				<text>~</text>
+				<text>{{ rangeValues3[1] }}</text>
+			</view>
+
+			<view class="text-center mrg50T">
+				<RangeSlider
+					:width="slideWidth"
+					:height="slideHeight"
+					:blockSize="slideBlockSize"
+					:min="slideMin"
+					:max="slideMax"
+					:values="rangeValues3"
+					:step="step"
+					:liveMode="isLiveMode"
+					@rangechange="onRangeChange3"
+				>
+					<view slot="minBlock" class="range-slider-block"></view>
+					<!-- 左边滑块的内容 -->
+					<view slot="maxBlock" class="range-slider-block"></view>
+					<!-- 右边滑块的内容 -->
+				</RangeSlider>
+			</view>
+		</view>
+
+		<text class="tips">修改自:https://github.com/Money888/wechat-rangeslider</text>
+	</view>
+</template>
+
+<script>
+import RangeSlider from '../../components/range-slider/range-slider.vue';
+
+export default {
+	data() {
+		return {
+			// rangeValues: [2, 5], //当前区间值
+			// slideWidth: 350, //宽度
+			// slideHeight: 80,  //高度
+			// slideBlockSize: 56, //圆形按钮大小
+			// slideMin: 0,  //slider最小值
+			// slideMax: 12,  //slider最大值
+
+			rangeValues: [4, 5.2],
+			slideWidth: 350,
+			slideHeight: 80,
+			slideBlockSize: 30,
+			slideMin: 0,
+			slideMax: 10,
+			isLiveMode: true,
+			step: 0.1,
+			//
+			timeMinValue: 0,
+			timeMaxValue: 10,
+
+			rangeValues2: [1, 6],
+			serTime: '02:24:00-14:24:00',
+
+			rangeValues3: [3, 5]
+		};
+	},
+	components: {
+		RangeSlider
+	},
+	onLoad() {
+		console.log('index onload');
+	},
+	methods: {
+		pad: function(num, n) {
+			return Array(n - ('' + num).length + 1).join(0) + num;
+		},
+		onRangeChange: function(e) {
+			this.rangeValues = [e.minValue, e.maxValue];
+
+			console.log(this.rangeValues);
+			console.log(JSON.stringify(e));
+		},
+		test: function() {
+			this.rangeValues = [4.2, 6.6];
+		},
+		onRangeChange2: function(e) {
+			let startTime = this.formatTimeBySliderValue(e.originalValue.minValue);
+			let endTime = this.formatTimeBySliderValue(e.originalValue.maxValue);
+			this.serTime = startTime + '-' + endTime;
+		},
+		onRangeChange3: function(e) {
+			this.rangeValues3 = [e.minValue, e.maxValue];
+		},
+		formatTimeBySliderValue(value) {
+			//按比例,将滑块上面的数值进行转换为时间形式
+			//转换为分钟数
+			let minutes = (24 * 60 * value) / this.slideMax;
+			//转换为小时数
+			let hours = parseInt(minutes / 60);
+			//剩余分钟数
+			let minutes_min = parseInt(minutes % 60);
+			return '' + this.pad(hours, 2) + ':' + this.pad(minutes_min, 2) + ':' + '00';
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+view {
+	display: flex;
+}
+
+.content {
+	justify-content: center;
+	flex-direction: column;
+}
+
+.sliderBox {
+	justify-content: center;
+	margin-right: 50rpx;
+}
+
+.text-center {
+	justify-content: center;
+}
+
+.rowBox {
+	flex-direction: row;
+	align-items: center;
+	justify-content: center;
+}
+
+.mrg10T {
+	margin-top: 10rpx;
+}
+
+.tips {
+	color: #999;
+	font-size: 24rpx;
+	text-align: center;
+	margin-top: 100rpx;
+}
+
+.testBtn {
+	margin-top: 50rpx;
+}
+
+.part {
+	flex-direction: column;
+	justify-content: center;
+	border-top: 1rpx solid #ccc;
+	padding-top: 50rpx;
+	.title {
+		font-size: 32rpx;
+		padding: 0 30rpx;
+	}
+}
+
+.part2 {
+	margin-top: 100rpx;
+}
+</style>