261 lines
5.5 KiB
Vue
261 lines
5.5 KiB
Vue
<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"
|
|
:size="contentLength"
|
|
:value="currentNum"
|
|
:readOnly="readOnly"
|
|
@input="$_onInput"
|
|
@blur="$_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, String],
|
|
default: 0,
|
|
},
|
|
value: {
|
|
type: [Number, String],
|
|
default: 0,
|
|
},
|
|
step: {
|
|
type: [Number, String],
|
|
default: 1,
|
|
},
|
|
min: {
|
|
type: [Number, String],
|
|
default: -Number.MAX_VALUE,
|
|
},
|
|
max: {
|
|
type: [Number, String],
|
|
default: Number.MAX_VALUE,
|
|
},
|
|
disabled: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
readOnly: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
isInteger: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
isMin: false,
|
|
isMax: false,
|
|
currentNum: 0,
|
|
}
|
|
},
|
|
|
|
computed: {
|
|
contentLength() {
|
|
if (!this.value) {
|
|
return 2
|
|
}
|
|
const length = this.value.toString().length
|
|
return length > 2 ? length : 2
|
|
},
|
|
},
|
|
|
|
watch: {
|
|
defaultValue(val) {
|
|
this.currentNum = this.$_getCurrentNum(val)
|
|
},
|
|
value(val) {
|
|
this.currentNum = this.$_getCurrentNum(val)
|
|
},
|
|
min(val) {
|
|
if (this.currentNum < val) {
|
|
this.currentNum = val
|
|
}
|
|
this.$_checkStatus()
|
|
},
|
|
max(val) {
|
|
if (this.currentNum > val) {
|
|
this.currentNum = val
|
|
}
|
|
this.$_checkStatus()
|
|
},
|
|
currentNum(val) {
|
|
this.$_checkStatus()
|
|
this.$emit('input', +val)
|
|
this.$emit('change', +val)
|
|
},
|
|
},
|
|
|
|
mounted() {
|
|
// verify that the minimum value is less than the maximum value
|
|
this.$_checkMinMax()
|
|
this.currentNum = this.$_getCurrentNum(this.value || this.defaultValue)
|
|
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()
|
|
},
|
|
$_formatNum(value) {
|
|
// @elist
|
|
value = String(value).replace(/[^0-9.-]/g, '')
|
|
return value === '' ? 0 : this.isInteger ? Math.floor(value) : +value
|
|
},
|
|
$_getCurrentNum(value) {
|
|
return Math.max(Math.min(this.max, this.$_formatNum(value)), this.min)
|
|
},
|
|
$_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
|
|
},
|
|
|
|
// MARK: 监听事件方法, 如 $_onButtonClick
|
|
$_onInput(event) {
|
|
const {value} = event.target
|
|
const formatted = this.$_formatNum(value)
|
|
if (+value !== formatted) {
|
|
event.target.value = formatted
|
|
}
|
|
this.currentNum = formatted
|
|
},
|
|
$_onChange() {
|
|
this.currentNum = this.$_getCurrentNum(this.currentNum)
|
|
},
|
|
},
|
|
}
|
|
|
|
</script>
|
|
|
|
<style lang="stylus">
|
|
.md-stepper
|
|
color stepper-color
|
|
-webkit-font-smoothing antialiased
|
|
font-size stepper-font-size
|
|
height stepper-height
|
|
display flex
|
|
&.disabled
|
|
.md-stepper-button
|
|
&:before,
|
|
&:after
|
|
opacity stepper-disabled-opacity
|
|
input
|
|
opacity stepper-disabled-opacity
|
|
|
|
.md-stepper-button
|
|
position relative
|
|
width stepper-width-button
|
|
height stepper-height
|
|
background-color stepper-fill
|
|
border-radius 2px
|
|
&:after
|
|
content ""
|
|
position absolute
|
|
width 24px
|
|
height 2px
|
|
top 50%
|
|
left 50%
|
|
background stepper-color
|
|
transform translate(-50%, -50%)
|
|
&.md-stepper-button-add
|
|
&:before
|
|
content ""
|
|
position absolute
|
|
width 2px
|
|
height 24px
|
|
top 50%
|
|
left 50%
|
|
background stepper-color
|
|
transform translate(-50%, -50%)
|
|
&.disabled
|
|
&:before,
|
|
&:after
|
|
opacity stepper-disabled-opacity
|
|
|
|
.md-stepper-number
|
|
margin 0 4px
|
|
min-width stepper-width-input
|
|
height stepper-height
|
|
padding 0 4px
|
|
text-align center
|
|
border-radius stepper-radius-button
|
|
background-color stepper-fill
|
|
input
|
|
width 100%
|
|
height stepper-height
|
|
border none
|
|
outline none
|
|
font-size stepper-input-font-size
|
|
line-height stepper-height
|
|
background-color transparent
|
|
box-sizing border-box
|
|
text-align center
|
|
color stepper-color
|
|
border-radius stepper-radius-input
|
|
</style>
|