mand-mobile/components/stepper/index.vue

232 lines
4.8 KiB
Vue
Raw Normal View History

2018-03-26 16:04:04 +08:00
<template>
<div
class="md-stepper"
:class="{'disabled': disabled}"
>
<div
class="md-stepper-button md-stepper-button-reduce"
:class="{'disabled': isMin}"
@click="$_reduce"
>
</div>
<div class="md-stepper-number">
<input type="tel"
v-model="currentNum"
:readOnly="readOnly"
@blur="$_reset"
@change="$_onChange">
</div>
<div
class="md-stepper-button md-stepper-button-add"
:class="{'disabled': isMax}"
@click="$_add"
>
</div>
</div>
</template>
<script> import {warn} from '../_util'
function getDecimalNum(num) {
try {
return num.toString().split('.')[1].length
} catch (e) {
return 0
}
}
function accAdd(num1, num2) {
let r1 = getDecimalNum(num1)
let r2 = getDecimalNum(num2)
let m = Math.pow(10, Math.max(r1, r2))
return (num1 * m + num2 * m) / m
}
function subtr(num1, num2) {
let r1 = getDecimalNum(num1)
let r2 = getDecimalNum(num2)
let m = Math.pow(10, Math.max(r1, r2))
let n = r1 >= r2 ? r1 : r2
return ((num1 * m - num2 * m) / m).toFixed(n)
}
export default {
name: 'md-stepper',
components: {},
props: {
defaultValue: {
type: Number,
default: 0,
},
step: {
type: Number,
default: 1,
},
min: {
type: Number,
default: -Number.MAX_VALUE,
},
max: {
type: Number,
default: Number.MAX_VALUE,
},
disabled: {
type: Boolean,
default: false,
},
readOnly: {
type: Boolean,
default: false,
},
},
data() {
return {
isMin: false,
isMax: false,
currentNum: 0,
}
},
watch: {
defaultValue() {
this.currentNum = this._getCurrentNum()
},
min(val) {
if (this.currentNum < val) {
this.currentNum = val
}
this.$_checkStatus()
},
max(val) {
if (this.currentNum > val) {
this.currentNum = val
}
this.$_checkStatus()
},
},
mounted() {
// verify that the minimum value is less than the maximum value
this.$_checkMinMax()
this.currentNum = this.$_getCurrentNum()
this.$_checkStatus()
},
methods: {
// MARK: 私有方法
$_reduce() {
if (this.disabled || this.isMin) {
return
}
this.currentNum = subtr(this.currentNum, this.step)
this.$_onChange()
},
$_add() {
if (this.disabled || this.isMax) {
return
}
this.currentNum = accAdd(this.currentNum, this.step)
this.$_onChange()
},
$_getCurrentNum() {
let num = this.defaultValue
if (num < this.min) {
return this.min
} else if (num > this.max) {
return this.max
} else {
return this.defaultValue
}
},
$_checkStatus() {
this.isMin = subtr(this.currentNum, this.step) < this.min
this.isMax = accAdd(this.currentNum, this.step) > this.max
},
$_checkMinMax() {
if (this.min > this.max) {
warn('[md-vue-stepper] minNum is larger than maxNum')
}
return this.max > this.min
},
$_reset() {
if (!this.currentNum || isNaN(this.currentNum)) {
this.currentNum = this.min !== -Number.MAX_VALUE ? this.min : 0
}
},
// MARK: 监听事件方法, 如 $_onButtonClick
$_onChange() {
let currentNum = this.currentNum ? this.currentNum : this.min
if (currentNum < this.min) {
this.currentNum = this.min
} else if (currentNum > this.max) {
this.currentNum = this.max
}
this.$_checkStatus()
this.$emit('change', this.currentNum)
},
},
}
</script>
<style lang="stylus">
.md-stepper
color color-text-base
-webkit-font-smoothing antialiased
font-size font-body-large
height 50px
display flex
&.disabled
opacity opacity-disabled
.md-stepper-button
position relative
width 54px
height 50px
background-color color-primary-background
border-radius 2px
&:after
center()
content ""
position absolute
width 24px
height 2px
top 50%
left 50%
background color-text-base
transform translate(-50%, -50%)
&.md-stepper-button-add
&:before
center()
content ""
position absolute
width 2px
height 24px
top 50%
left 50%
background color-text-base
transform translate(-50%, -50%)
&.disabled
opacity opacity-disabled
.md-stepper-number
margin 0 4px
min-width 68px
height 50px
padding 0 4px
text-align center
border-radius 2px
input
width 68px
height 50px
border none
outline none
font-size font-body-normal
line-height 50px
box-sizing border-box
background-color color-primary-background
text-align center
color color-text-base
border-radius unset
</style>