<div class="input-box-wrap">
<input
type="text"
pattern="[0-9]*"
id="single-factor-code-text-field"
autocomplete="one-time-code"
inputMode="numeric"
v-maxlength="4"
ref="hideInputRef"
@input="handleHideInput"
/>
</div>
<div class="vcode-inputs" v-if="vcodeSended" @click="handleClickVCode">
<input v-for="(item, index) in 4" :key="item" type="number"
pattern="\d*" v-maxlength="1" ref="vcode"
@keyup.prevent="vcodekeydown($event, index)"
>
<script>
export default {
data () {
return {
ac: null
}
},
methods: {
handleClickVCode () {
this.$refs.hideInputRef.focus()
},
handleHideInput () {
const code = this.$refs.hideInputRef.value
if (code && code.length >= 4) {
this.fillcode(code)
}
},
getCodeFrom (success) {
if ('OTPCredential' in window) {
this.ac = new AbortController()
navigator.credentials.get({
otp: { transport: ['sms'] },
signal: this.ac.signal
}).then(otp => {
success && success(otp.code)
}).catch(err => {
this.showToast(JSON.stringify(err))
})
}
},
async sendCode () {
try {
this.showLoading = true
const { $api, phone } = this
await $api.vcode(phone, this.verifyParams)
this.vcode = ''
this.phoneLocked = true
this.vcodeSended = true
this.countdown()
this.$nextTick(() => {
const vcodeInputs = this.$refs.vcode
vcodeInputs.forEach(item => {
item.value = ''
})
this.$refs.hideInputRef.focus()
this.getCodeFrom((code) => {
this.fillcode(code)
})
})
} catch (error) {
this.showToast(error.error)
} finally {
this.showLoading = false
}
},
fillcode (code) {
if (code && code.length >= 4) {
this.$refs.hideInputRef.value = code
const codeArr = code.split('')
const codeArr4 = codeArr.slice(0, 4)
const vcodeInputs = this.$refs.vcode
vcodeInputs.forEach((item, index) => {
if (codeArr4[index]) {
item.value = codeArr4[index]
this.$$(vcodeInputs[index]).addClass('on')
}
})
vcodeInputs[0].focus()
this.vcode = code
if (code && code.length >= 4) {
this.signIn()
}
if (this.ac) this.ac.abort()
}
},
async signIn () {
const { vcode, phone, $api, $account, backPath, $listeners, $f7router } = this
if (this.verifyPhone() && this.verifyCode()) {
try {
this.showLoading = true
await $api.login({
phone,
code: vcode
})
this.$stat('all_login_succ')
$account.retrieveAccount().then(res => {
if (backPath) {
$f7router.navigate(backPath, { clearPreviousHistory: true })
} else if ($listeners.success) {
this.$app.ready(app => {
if (app.router.currentRoute.url) {
app.router.refreshPage()
} else {
window.location.reload()
}
this.$emit('success')
})
} else {
$f7router.navigate('/', { clearPreviousHistory: true })
}
})
} catch (error) {
this.showToast(error.error || error.msg)
} finally {
this.showLoading = false
}
}
},
verifyCode () {
return this.vcode.length > 3
},
verifyPhone () {
const isValid = /^1([38][0-9]|4[5-9]|5[0-3,5-9]|66|7[0-8]|9[89])[0-9]{8}$/.test(this.phone)
if (!isValid) {
this.showToast('手机号码格式错误')
}
return isValid
},
modifyPhone () {
this.phoneLocked = false
this.$nextTick(() => {
this.$$(this.$refs.phoneInput).focus()
})
},
vcodekeydown (event, index) {
const vcodeInputs = this.$refs.vcode
if ((event.which >= 48 && event.which <= 57) || event.which === 229) {
let value = event.key
if (Number.isNaN(parseInt(value))) {
value = event.target.value
}
vcodeInputs[index].value = value
if (index < 3 && value > -1) {
this.$$(vcodeInputs[index + 1]).focus()
} else {
this.vcode = vcodeInputs.map(item => item.value).join('')
if (this.vcode.length > 3) {
this.signIn()
}
}
this.$$(vcodeInputs[index]).addClass('on')
} else if (event.which === 8) {
vcodeInputs[index].value = ''
if (index > 0) {
this.$$(vcodeInputs[index - 1]).focus()
}
this.vcode = ''
this.$$(vcodeInputs[index]).removeClass('on')
}
},
showToast (text) {
this.$f7.toast.create({
text,
position: 'center',
cssClass: 'alk-login-toast',
closeTimeout: 2000
}).open()
},
countdown () {
const timer = new this.$utils.Timer(1000, {
start: true,
loop: true
}, () => {
this.countdownSeconds = this.countdownSeconds - 1
if (this.countdownSeconds === 0) {
timer.stop()
this.countdownSeconds = 60
}
})
},
reset () {
this.phone = ''
this.vcode = ''
this.showLoading = false
this.agreementChecked = false
this.phoneLocked = false
this.vcodeSended = false
this.countdownSeconds = 60
this.verifyParams = {}
if (this.ac) {
this.ac.abort()
this.ac = null
}
},
useVerify () {
if (!this.verifyPhone() || this.vcodeDisabled) {
return false
}
}
}
}
</script>