2018-03-26 16:04:04 +08:00
|
|
|
<template>
|
|
|
|
<div class="md-codebox-wrapper">
|
2018-08-27 19:16:59 +08:00
|
|
|
<div
|
|
|
|
class="md-codebox"
|
|
|
|
:class="{
|
|
|
|
'is-disabled': disabled,
|
|
|
|
'is-justify': justify
|
|
|
|
}"
|
|
|
|
@click="focus"
|
|
|
|
>
|
2018-03-26 16:04:04 +08:00
|
|
|
<template v-if="maxlength > 0">
|
|
|
|
<span
|
|
|
|
v-for="i in num"
|
|
|
|
:key="i"
|
2018-12-03 19:08:58 +08:00
|
|
|
:class="[
|
2019-03-08 15:44:49 +08:00
|
|
|
'md-codebox-box',
|
2018-12-03 19:08:58 +08:00
|
|
|
(i === code.length + 1) && focused && 'is-active',
|
|
|
|
code.charAt(i-1) !== '' && 'is-filled'
|
|
|
|
]"
|
2018-03-26 16:04:04 +08:00
|
|
|
>
|
|
|
|
<template v-if="code.charAt(i-1)">
|
|
|
|
<template v-if="mask">
|
|
|
|
<i class="md-codebox-dot"></i>
|
|
|
|
</template>
|
|
|
|
<template v-else>
|
|
|
|
{{code.charAt(i-1)}}
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
<template v-if="i === code.length + 1 && focused">
|
|
|
|
<i class="md-codebox-blink"></i>
|
|
|
|
</template>
|
|
|
|
</span>
|
|
|
|
</template>
|
|
|
|
<template v-else>
|
|
|
|
<input
|
|
|
|
v-if="mask"
|
|
|
|
type="password"
|
|
|
|
:maxlength="maxlength"
|
|
|
|
:value="code"
|
|
|
|
readonly
|
|
|
|
disabled
|
|
|
|
:class="['md-codebox-holder', focused && 'is-active']"
|
|
|
|
/>
|
|
|
|
<input
|
|
|
|
v-else
|
|
|
|
type="text"
|
|
|
|
:maxlength="maxlength"
|
|
|
|
:value="code"
|
|
|
|
readonly
|
|
|
|
disabled
|
|
|
|
:class="['md-codebox-holder', focused && 'is-active']"
|
|
|
|
/>
|
|
|
|
</template>
|
|
|
|
</div>
|
2018-09-14 16:03:24 +08:00
|
|
|
<slot></slot>
|
2018-03-26 16:04:04 +08:00
|
|
|
<form action="" v-show="system" @submit="$_onSubmit">
|
|
|
|
<input
|
|
|
|
:value="code"
|
|
|
|
:type="mask ? 'password' : 'text'"
|
|
|
|
:maxlength="maxlength"
|
|
|
|
@input="$_onInputChange"
|
|
|
|
ref="input"
|
|
|
|
class="md-codebox-input"
|
|
|
|
/>
|
|
|
|
</form>
|
|
|
|
<md-number-keyboard
|
|
|
|
v-show="!system"
|
|
|
|
ref="keyboard"
|
2018-08-27 19:16:59 +08:00
|
|
|
class="md-codebox-keyboard"
|
2018-03-26 16:04:04 +08:00
|
|
|
:type="maxlength > 0 ? 'simple' : 'professional'"
|
|
|
|
:okText="okText"
|
|
|
|
:disorder="disorder"
|
|
|
|
:is-view="isView"
|
|
|
|
v-model="focused"
|
|
|
|
@delete="$_onDelete"
|
|
|
|
@enter="$_onEnter"
|
|
|
|
@confirm="$_onConfirm"
|
|
|
|
></md-number-keyboard>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
import NumberKeyboard from '../number-keyboard'
|
|
|
|
|
|
|
|
export default {
|
|
|
|
name: 'md-codebox',
|
|
|
|
components: {
|
|
|
|
[NumberKeyboard.name]: NumberKeyboard,
|
|
|
|
},
|
|
|
|
props: {
|
|
|
|
value: {
|
|
|
|
type: String,
|
|
|
|
default: '',
|
|
|
|
},
|
|
|
|
maxlength: {
|
|
|
|
type: [Number, String],
|
|
|
|
default: 4,
|
|
|
|
},
|
|
|
|
autofocus: {
|
|
|
|
type: Boolean,
|
|
|
|
default: false,
|
|
|
|
},
|
2018-08-27 19:16:59 +08:00
|
|
|
disabled: {
|
|
|
|
type: Boolean,
|
|
|
|
default: false,
|
|
|
|
},
|
|
|
|
justify: {
|
|
|
|
type: Boolean,
|
|
|
|
default: false,
|
|
|
|
},
|
2018-03-26 16:04:04 +08:00
|
|
|
mask: {
|
|
|
|
type: Boolean,
|
|
|
|
default: false,
|
|
|
|
},
|
|
|
|
closable: {
|
|
|
|
type: Boolean,
|
|
|
|
default: true,
|
|
|
|
},
|
|
|
|
system: {
|
|
|
|
type: Boolean,
|
|
|
|
default: false,
|
|
|
|
},
|
|
|
|
okText: {
|
|
|
|
type: String,
|
|
|
|
},
|
|
|
|
disorder: {
|
|
|
|
type: Boolean,
|
|
|
|
default: false,
|
|
|
|
},
|
|
|
|
isView: {
|
|
|
|
type: Boolean,
|
|
|
|
default: false,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
code: '',
|
|
|
|
focused: this.autofocus,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
watch: {
|
|
|
|
value: {
|
|
|
|
imediate: true,
|
|
|
|
handler(val) {
|
|
|
|
if (val !== this.code) {
|
|
|
|
this.code = val
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
computed: {
|
|
|
|
num() {
|
|
|
|
return Math.abs(parseInt(this.maxlength, 10)) || 1
|
|
|
|
},
|
|
|
|
},
|
|
|
|
mounted() {
|
|
|
|
if (this.closable) {
|
|
|
|
document.addEventListener('click', this.$_handleOutClick)
|
|
|
|
}
|
|
|
|
if (!this.system && !this.isView) {
|
|
|
|
document.body.appendChild(this.$refs.keyboard.$el)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
beforeDestroy() {
|
|
|
|
if (this.closable) {
|
2018-04-18 14:32:28 +08:00
|
|
|
document.removeEventListener('click', this.$_handleOutClick)
|
2018-03-26 16:04:04 +08:00
|
|
|
}
|
2018-04-11 15:23:07 +08:00
|
|
|
if (this.focused) {
|
|
|
|
this.blur()
|
|
|
|
}
|
2018-04-18 14:32:28 +08:00
|
|
|
if (!this.system && !this.isView) {
|
|
|
|
document.body.removeChild(this.$refs.keyboard.$el)
|
|
|
|
}
|
2018-03-26 16:04:04 +08:00
|
|
|
},
|
|
|
|
methods: {
|
|
|
|
// MARK: private methods
|
|
|
|
|
|
|
|
// MARK: events handler
|
|
|
|
$_handleOutClick(e) {
|
|
|
|
if (!this.$el.contains(e.target)) {
|
|
|
|
this.focused = false
|
|
|
|
}
|
|
|
|
},
|
|
|
|
$_onInputChange(e) {
|
|
|
|
if (this.maxlength < 0 || e.target.value.length <= this.maxlength) {
|
|
|
|
this.code = e.target.value
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.code.length === this.maxlength) {
|
|
|
|
this.$emit('submit', this.code)
|
|
|
|
}
|
|
|
|
|
|
|
|
this.$emit('input', this.code)
|
|
|
|
},
|
|
|
|
$_onSubmit(e) {
|
|
|
|
e.preventDefault()
|
|
|
|
this.$emit('submit', this.code)
|
|
|
|
},
|
|
|
|
$_onEnter(val) {
|
|
|
|
if ((this.maxlength < 0 || this.code.length < this.maxlength) && val !== '.') {
|
|
|
|
this.code += val
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.code.length === this.maxlength) {
|
|
|
|
this.$nextTick(function() {
|
|
|
|
this.$emit('submit', this.code)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
this.$emit('input', this.code)
|
|
|
|
},
|
|
|
|
$_onDelete() {
|
|
|
|
this.code = this.code.slice(0, this.code.length - 1)
|
|
|
|
this.$emit('input', this.code)
|
|
|
|
},
|
|
|
|
$_onConfirm() {
|
|
|
|
this.$emit('submit', this.code)
|
|
|
|
},
|
|
|
|
// MARK: public methods
|
|
|
|
blur() {
|
|
|
|
this.focused = false
|
|
|
|
if (this.system) {
|
|
|
|
this.$refs.input.blur()
|
|
|
|
}
|
|
|
|
},
|
|
|
|
focus() {
|
2018-08-27 19:16:59 +08:00
|
|
|
if (this.disabled) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-03-26 16:04:04 +08:00
|
|
|
this.focused = true
|
|
|
|
if (this.system) {
|
|
|
|
this.$refs.input.focus()
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<style lang="stylus">
|
2018-08-27 19:16:59 +08:00
|
|
|
.md-codebox-wrapper
|
|
|
|
.md-codebox-input
|
|
|
|
position absolute
|
|
|
|
left -9999px
|
|
|
|
opacity 0
|
|
|
|
|
|
|
|
.md-codebox
|
|
|
|
position relative
|
|
|
|
display flex
|
|
|
|
flex-wrap nowrap
|
|
|
|
justify-content center
|
|
|
|
&.is-justify
|
2018-03-26 16:04:04 +08:00
|
|
|
.md-codebox-box
|
2018-08-27 19:16:59 +08:00
|
|
|
flex 1 1 0%
|
|
|
|
|
|
|
|
.md-codebox-box
|
|
|
|
flex 0 1 codebox-width
|
|
|
|
width codebox-width
|
|
|
|
height codebox-height
|
|
|
|
display flex
|
|
|
|
align-items center
|
|
|
|
justify-content center
|
|
|
|
color codebox-color
|
|
|
|
font-family font-family-number
|
|
|
|
font-size codebox-font-size
|
|
|
|
font-weight normal
|
|
|
|
line-height 1.2
|
2019-03-08 15:44:49 +08:00
|
|
|
if codebox-gutter is a 'unit'
|
|
|
|
margin-left (codebox-gutter / 2)
|
|
|
|
margin-right (codebox-gutter / 2)
|
|
|
|
else
|
|
|
|
margin-left "calc(%s / 2)" % codebox-gutter
|
|
|
|
margin-right "calc(%s / 2)" % codebox-gutter
|
|
|
|
|
2018-08-27 19:16:59 +08:00
|
|
|
border-bottom codebox-border-width solid codebox-border-color
|
|
|
|
&:first-child
|
|
|
|
margin-left 0
|
|
|
|
&:last-child
|
|
|
|
margin-right 0
|
2018-12-03 19:08:58 +08:00
|
|
|
&.is-active, &.is-filled
|
2018-08-27 19:16:59 +08:00
|
|
|
border-color codebox-border-active-color
|
|
|
|
|
|
|
|
.md-codebox-blink
|
|
|
|
display block
|
2019-03-08 15:44:49 +08:00
|
|
|
if tab-height is a 'unit'
|
|
|
|
height (codebox-height * 0.8)
|
|
|
|
else
|
|
|
|
height "calc(%s * 0.8)" % codebox-height
|
2018-08-27 19:16:59 +08:00
|
|
|
width 2px
|
|
|
|
background-color codebox-blink-color
|
|
|
|
animation md-codebox-flash steps(2) 1s infinite
|
|
|
|
|
|
|
|
.md-codebox-dot
|
|
|
|
display block
|
|
|
|
height 10px
|
|
|
|
width 10px
|
|
|
|
border-radius 5px
|
|
|
|
background-color codebox-dot-color
|
|
|
|
|
|
|
|
.md-codebox-holder
|
|
|
|
pointer-events none
|
|
|
|
height codebox-height
|
|
|
|
line-height codebox-height
|
|
|
|
padding 0 codebox-holder-space
|
|
|
|
width 100%
|
|
|
|
text-align center
|
|
|
|
font-size codebox-font-size
|
|
|
|
outline none
|
|
|
|
color codebox-color
|
|
|
|
letter-spacing 0.1em
|
|
|
|
border-radius 0
|
|
|
|
border-style solid
|
|
|
|
border-width 0 0 codebox-border-width 0
|
|
|
|
border-color codebox-border-color
|
|
|
|
background none
|
|
|
|
box-shadow none
|
|
|
|
box-sizing border-box
|
|
|
|
-webkit-appearance none
|
2018-12-03 19:08:58 +08:00
|
|
|
-webkit-text-fill-color codebox-color
|
2018-08-27 19:16:59 +08:00
|
|
|
&[disabled],
|
|
|
|
&[readonly]
|
|
|
|
opacity 1
|
|
|
|
color codebox-color
|
|
|
|
border-color codebox-border-color
|
|
|
|
&.is-active
|
|
|
|
border-color codebox-border-active-color
|
|
|
|
|
|
|
|
.md-codebox.is-disabled
|
|
|
|
.md-codebox-box
|
|
|
|
color codebox-disabled-color
|
|
|
|
border-color codebox-disabled-color
|
|
|
|
.md-codebox-blink
|
|
|
|
display none
|
|
|
|
.md-codebox-dot
|
|
|
|
background-color codebox-disabled-color
|
|
|
|
.md-codebox-holder
|
|
|
|
color codebox-disabled-color
|
|
|
|
border-color codebox-disabled-color
|
|
|
|
|
|
|
|
@keyframes md-codebox-flash
|
|
|
|
from
|
|
|
|
opacity 0
|
|
|
|
to
|
|
|
|
opacity 1
|
2018-03-26 16:04:04 +08:00
|
|
|
</style>
|
|
|
|
|