mand-mobile/components/captcha/index.vue

467 lines
11 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="md-captcha" v-show="isInline || value || visible">
<template v-if="isInline">
<div class="md-captcha-content">
<h2 class="md-captcha-title" v-if="title" v-text="title"></h2>
<div class="md-captcha-message">
<slot></slot>
</div>
</div>
<md-codebox
ref="codebox"
v-model="code"
:maxlength="maxlength"
:system="system"
:mask="mask"
@submit="$_onSubmit"
:closable="false"
:isView="true"
:justify="true"
:autofocus="false"
:input-type="inputType"
>
<footer class="md-captcha-footer">
<div class="md-captcha-error" v-if="errorMsg" v-text="errorMsg"></div>
<div class="md-captcha-brief" v-else v-text="brief"></div>
<button
class="md-captcha-btn"
v-if="count"
v-text="countBtnText"
:disabled="this.isCounting"
@click="$_onResend"
></button>
</footer>
</md-codebox>
</template>
<template v-if="type === 'halfScreen'">
<md-popup
:value="value"
:hasMask="true"
position="bottom"
:maskClosable="false"
@input="$_onInput"
@show="$_onShow"
@hide="$_onHide"
>
<div class="md-captcha-half-container">
<md-popup-title-bar
only-close
large-radius
:title="title"
:describe="subtitle"
title-align="left"
@cancel="close"
></md-popup-title-bar>
<div class="md-captcha-half-content">
<slot></slot>
</div>
<md-codebox
ref="codebox"
v-model="code"
:maxlength="maxlength"
:system="system"
:mask="mask"
@submit="$_onSubmit"
:disabled="disableSend"
:closable="false"
:isView="true"
:justify="true"
:autofocus="false"
:input-type="inputType"
:isErrorStyle="isShowErrorStyle"
>
<footer
class="md-captcha-footer"
:class="{ halfStyle: isKeyboard }"
>
<div
class="md-captcha-error"
v-if="errorMsg"
v-text="errorMsg"
></div>
<div class="md-captcha-brief" v-else v-text="brief"></div>
<button
class="md-captcha-btn"
:class="[disableSend && 'is-disabled-send']"
v-if="count"
v-text="countBtnText"
:disabled="this.isCounting"
@click="$_onResend"
></button>
</footer>
</md-codebox>
</div>
</md-popup>
</template>
<template v-if="type === 'dialog'">
<md-dialog
:value="value"
:closable="true"
:appendTo="false"
position="center"
@input="$_onInput"
@show="$_onShow"
@hide="$_onHide"
>
<div class="md-captcha-content">
<h2 class="md-captcha-title" v-if="title" v-text="title"></h2>
<div class="md-captcha-message">
<slot></slot>
</div>
</div>
<md-codebox
ref="codebox"
v-model="code"
:maxlength="maxlength"
:system="system"
:closable="false"
:mask="mask"
:justify="true"
:autofocus="false"
:input-type="inputType"
@submit="$_onSubmit"
>
<footer class="md-captcha-footer">
<div class="md-captcha-error" v-if="errorMsg" v-text="errorMsg"></div>
<div class="md-captcha-brief" v-else v-text="brief"></div>
<button
class="md-captcha-btn"
v-if="count"
v-text="countBtnText"
:disabled="this.isCounting"
@click="$_onResend"
></button>
</footer>
</md-codebox>
</md-dialog>
</template>
</div>
</template>
<script>
import Dialog from '../dialog'
import Popup from '../popup'
import PopupTitlebar from '../popup/title-bar'
import Codebox from '../codebox'
import Button from '../button'
import {mdDocument} from '../_util'
import {t} from '../_locale'
export default {
name: 'md-captcha',
components: {
[Dialog.name]: Dialog,
[Popup.name]: Popup,
[PopupTitlebar.name]: PopupTitlebar,
[Codebox.name]: Codebox,
[Button.name]: Button,
},
props: {
title: {
type: String,
},
subtitle: {
type: String,
},
brief: {
type: String,
default: '',
},
value: {
type: Boolean,
default: false,
},
maxlength: {
type: [Number, String],
default: 4,
},
mask: {
type: Boolean,
default: false,
},
system: {
type: Boolean,
default: false,
},
autoSend: {
type: Boolean,
default: true,
},
autoCountdown: {
type: Boolean,
default: true,
},
appendTo: {
default: () => mdDocument.body,
},
count: {
type: Number,
default: 60,
},
countNormalText: {
type: String,
default: t('md.captcha.sendCaptcha'),
},
countActiveText: {
type: String,
default: t('md.captcha.countdown'),
},
isView: {
type: Boolean,
default: false,
},
type: {
type: String,
default: 'dialog',
},
inputType: {
type: String,
default: 'tel',
},
disableSend: {
type: Boolean,
default: false,
},
},
data() {
return {
code: '',
visible: false,
errorMsg: '',
isCounting: false,
firstShown: false,
countBtnText: this.countNormalText,
isKeyboard: false,
originHeight: 0,
}
},
watch: {
value(val) {
if (val) {
this.code = ''
if (!this.firstShown) {
this.firstShown = true
this.$_emitSend()
}
}
},
code(val) {
if (val && this.errorMsg) {
this.errorMsg = ''
}
},
},
computed: {
isInline() {
return this.isView || this.type === 'inline'
},
isShowErrorStyle() {
return this.errorMsg !== '' && !this.disableSend
},
},
mounted() {
// Andriod 键盘收起Andriod 键盘弹起或收起页面高度会发生变化,以此为依据获知键盘收起
if (this.judgeDeviceType().isAndroid && this.type === 'halfScreen') {
// 记录初始高度
this.originHeight = document.documentElement.clientHeight || document.body.clientHeight
window.addEventListener('resize', this.listenResize, false)
}
if (this.appendTo && !this.isInline) {
this.appendTo.appendChild(this.$el)
}
if (this.value || this.isInline) {
this.firstShown = true
this.$_emitSend()
}
},
beforeDestroy() {
if (this.judgeDeviceType().isAndroid && this.type === 'halfScreen') {
window.removeEventListener('resize', this.listenResize)
}
if (this.appendTo && !this.isInline) {
this.appendTo.removeChild(this.$el)
}
},
methods: {
// MARK: events handler, 如 $_onButtonClick
$_onInput(val) {
this.$emit('input', val)
},
$_onShow() {
this.visible = true
this.$refs.codebox.focus()
this.$emit('show')
},
$_onHide() {
this.visible = false
this.$refs.codebox.blur()
this.$emit('hide')
},
$_onSubmit(code) {
this.$emit('submit', code)
},
$_onResend() {
if (this.autoCountdown) {
this.countdown()
}
this.$emit('send', this.countdown)
},
$_emitSend() {
this.autoSend && this.$_onResend()
},
// MARK: public methods
listenResize() {
let resizeHeight = document.documentElement.clientHeight || document.body.clientHeight
if (this.originHeight < resizeHeight) {
this.isKeyboard = false
} else {
this.isKeyboard = true
}
this.originHeight = resizeHeight
},
// 判断设备类型
judgeDeviceType() {
let ua = window.navigator.userAgent.toLocaleLowerCase()
let isIOS = /iphone|ipad|ipod/.test(ua)
let isAndroid = /android/.test(ua)
return {
isIOS: isIOS,
isAndroid: isAndroid,
}
},
countdown() {
if (!this.count) {
return
}
clearInterval(this.__counter__)
const timestamp = Date.now()
let i = this.count
this.isCounting = true
this.countBtnText = this.countActiveText.replace('{$1}', i)
/* istanbul ignore next */
this.__counter__ = setInterval(() => {
if (i <= 1) {
this.resetcount()
} else {
i = this.count - Math.floor((Date.now() - timestamp) / 1000)
this.countBtnText = this.countActiveText.replace('{$1}', i)
}
}, 1000)
},
resetcount() {
this.isCounting = false
this.countBtnText = this.countNormalText
clearInterval(this.__counter__)
},
setError(msg) {
this.$nextTick(() => {
this.errorMsg = msg
// this.code = ''
})
},
close() {
this.$emit('input', false)
},
},
}
</script>
<style lang="stylus">
.md-captcha
.md-dialog
.md-popup
z-index captcha-zindex
.md-dialog-body
padding 60px 60px 30px 60px
.md-dialog-content
margin-bottom number-keyboard-height
.md-codebox
margin-bottom 28px
.md-captcha-content
font-size captcha-font-size
color captcha-color
text-align center
line-height 1.2
margin-bottom 50px
.md-captcha-title
color captcha-title-color
font-size captcha-title-font-size
font-weight normal
line-height 1.15
margin 0 0 16px 0
.md-captcha-footer
margin 28px 0
display flex
font-size captcha-footer-font-size
justify-content space-between
align-items center
overflow hidden
.md-captcha-error, .md-captcha-brief
flex 1 1 0%
.md-captcha-error
color captcha-error-color
.md-captcha-brief
color captcha-brief-color
.md-captcha-btn
display inline-block
color captcha-btn-color
font-size captcha-footer-font-size
padding 0
margin 0 0 0 12px
border 0
border-radius 0
background none
&:disabled
color color-text-disabled
// 半屏样式
.md-captcha-half-container
background-color #fff
border-radius 40px 40px 0 0
.md-popup-title-bar
height auto !important
margin-bottom 24px
.title-bar-title
.title
font-weight bold
line-height 56px
p.describe
font-size 24px !important
color #91989F !important
line-height 33px !important
margin-top: 4px !important
.md-captcha-half-content
padding 0px 40px
.md-codebox-wrapper
.md-codebox
padding 75px 40px 0px
margin 0px
.md-captcha-footer
padding 32px 40px 745px
margin 0px
&.halfStyle
padding-bottom 40px
.md-captcha-btn
padding 5px 16px
background-color color-primary
border-radius radius-medium
color color-text-base-inverse
&:disabled
color #61686F
background none
border-radius inherit
padding 10px 0px
&.is-disabled-send
background rgba(145,152,159,0.1)
color #61686F
</style>