mand-mobile/components/captcha/index.vue

471 lines
11 KiB
Vue
Raw Normal View History

2018-03-26 16:04:04 +08:00
<template>
<div class="md-captcha" v-show="isInline || value || visible">
<template v-if="isInline">
2018-03-26 16:04:04 +08:00
<div class="md-captcha-content">
<h2 class="md-captcha-title" v-if="title" v-text="title"></h2>
2018-04-11 15:22:40 +08:00
<div class="md-captcha-message">
2018-03-26 16:04:04 +08:00
<slot></slot>
</div>
</div>
<md-codebox
ref="codebox"
v-model="code"
:maxlength="maxlength"
:system="system"
:mask="mask"
@submit="$_onSubmit"
2018-08-27 19:17:20 +08:00
:closable="false"
:isView="true"
:justify="true"
:autofocus="false"
2021-10-11 14:37:36 +08:00
:input-type="inputType"
2018-09-14 16:03:24 +08:00
>
<footer class="md-captcha-footer">
2018-11-15 15:47:44 +08:00
<div class="md-captcha-error" v-if="errorMsg" v-text="errorMsg"></div>
<div class="md-captcha-brief" v-else v-text="brief"></div>
2018-09-14 16:03:24 +08:00
<button
class="md-captcha-btn"
v-if="count"
v-text="countBtnText"
:disabled="this.isCounting"
@click="$_onResend"
></button>
</footer>
</md-codebox>
2018-03-26 16:04:04 +08:00
</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'">
2018-03-26 16:04:04 +08:00
<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>
2018-04-11 15:22:40 +08:00
<div class="md-captcha-message">
2018-03-26 16:04:04 +08:00
<slot></slot>
</div>
</div>
<md-codebox
ref="codebox"
v-model="code"
:maxlength="maxlength"
:system="system"
:closable="false"
:mask="mask"
2018-08-27 19:17:20 +08:00
:justify="true"
2018-03-26 16:04:04 +08:00
:autofocus="false"
2021-10-11 14:37:36 +08:00
:input-type="inputType"
2018-03-26 16:04:04 +08:00
@submit="$_onSubmit"
2018-09-14 16:03:24 +08:00
>
<footer class="md-captcha-footer">
2018-11-15 15:47:44 +08:00
<div class="md-captcha-error" v-if="errorMsg" v-text="errorMsg"></div>
<div class="md-captcha-brief" v-else v-text="brief"></div>
2018-09-14 16:03:24 +08:00
<button
class="md-captcha-btn"
v-if="count"
v-text="countBtnText"
:disabled="this.isCounting"
@click="$_onResend"
></button>
</footer>
</md-codebox>
2018-03-26 16:04:04 +08:00
</md-dialog>
</template>
</div>
</template>
<script>
import Dialog from '../dialog'
import Popup from "../popup";
import PopupTitlebar from "../popup/title-bar";
2018-03-26 16:04:04 +08:00
import Codebox from '../codebox'
import Button from '../button'
import {mdDocument} from '../_util'
import {t} from '../_locale'
2018-03-26 16:04:04 +08:00
export default {
name: 'md-captcha',
components: {
[Dialog.name]: Dialog,
[Popup.name]: Popup,
[PopupTitlebar.name]: PopupTitlebar,
2018-03-26 16:04:04 +08:00
[Codebox.name]: Codebox,
[Button.name]: Button,
},
props: {
title: {
type: String,
},
subtitle: {
type: String
},
2018-11-15 15:47:44 +08:00
brief: {
type: String,
default: '',
},
2018-03-26 16:04:04 +08:00
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,
},
2018-03-26 16:04:04 +08:00
appendTo: {
default: () => mdDocument.body,
2018-03-26 16:04:04 +08:00
},
count: {
type: Number,
default: 60,
},
countNormalText: {
type: String,
default: t('md.captcha.sendCaptcha'),
},
countActiveText: {
type: String,
default: t('md.captcha.countdown'),
},
2018-03-26 16:04:04 +08:00
isView: {
type: Boolean,
default: false,
},
type: {
type: String,
default: "dialog"
},
2021-10-11 14:37:36 +08:00
inputType: {
type: String,
default: 'tel',
},
disableSend: {
type: Boolean,
default: false
}
2018-03-26 16:04:04 +08:00
},
data() {
return {
code: '',
visible: false,
2018-04-11 15:22:40 +08:00
errorMsg: '',
2018-03-26 16:04:04 +08:00
isCounting: false,
firstShown: false,
countBtnText: this.countNormalText,
isKeyboard: false,
originHeight: 0
};
2018-03-26 16:04:04 +08:00
},
watch: {
value(val) {
if (val) {
this.code = ''
if (!this.firstShown) {
this.firstShown = true
this.$_emitSend()
2018-03-26 16:04:04 +08:00
}
}
},
2018-04-11 15:22:40 +08:00
code(val) {
if (val && this.errorMsg) {
this.errorMsg = ''
}
},
2018-03-26 16:04:04 +08:00
},
computed: {
isInline() {
return this.isView || this.type === "inline";
},
isShowErrorStyle() {
return this.errorMsg !== "" && !this.disableSend;
}
},
2018-03-26 16:04:04 +08:00
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) {
2018-03-26 16:04:04 +08:00
this.appendTo.appendChild(this.$el)
}
if (this.value || this.isInline) {
2018-03-26 16:04:04 +08:00
this.firstShown = true
this.$_emitSend()
2018-03-26 16:04:04 +08:00
}
},
2018-04-18 15:05:47 +08:00
beforeDestroy() {
if (this.judgeDeviceType().isAndroid && this.type === "halfScreen") {
window.removeEventListener("resize", this.listenResize);
}
if (this.appendTo && !this.isInline) {
2018-04-18 15:05:47 +08:00
this.appendTo.removeChild(this.$el)
}
},
2018-03-26 16:04:04 +08:00
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)
2018-03-26 16:04:04 +08:00
},
$_emitSend() {
this.autoSend && this.$_onResend()
},
2018-03-26 16:04:04 +08:00
// 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
};
},
2018-03-26 16:04:04 +08:00
countdown() {
2018-04-24 22:49:00 +08:00
if (!this.count) {
return
}
2018-03-26 16:04:04 +08:00
clearInterval(this.__counter__)
const timestamp = Date.now()
let i = this.count
2018-03-26 16:04:04 +08:00
this.isCounting = true
this.countBtnText = this.countActiveText.replace('{$1}', i)
2018-04-23 17:33:32 +08:00
/* istanbul ignore next */
2018-03-26 16:04:04 +08:00
this.__counter__ = setInterval(() => {
if (i <= 1) {
2018-03-26 16:04:04 +08:00
this.resetcount()
} else {
i = this.count - Math.floor((Date.now() - timestamp) / 1000)
this.countBtnText = this.countActiveText.replace('{$1}', i)
2018-03-26 16:04:04 +08:00
}
}, 1000)
},
resetcount() {
this.isCounting = false
this.countBtnText = this.countNormalText
2018-03-26 16:04:04 +08:00
clearInterval(this.__counter__)
},
2018-04-11 15:22:40 +08:00
setError(msg) {
this.$nextTick(() => {
this.errorMsg = msg
// this.code = ''
2018-04-11 15:22:40 +08:00
})
},
close() {
this.$emit("input", false)
}
2018-03-26 16:04:04 +08:00
},
}
</script>
2018-03-26 16:04:04 +08:00
<style lang="stylus">
2018-08-27 19:17:20 +08:00
.md-captcha
.md-dialog
2018-11-11 12:17:35 +08:00
.md-popup
z-index captcha-zindex
2018-08-27 19:17:20 +08:00
.md-dialog-body
2018-09-14 16:03:24 +08:00
padding 60px 60px 30px 60px
2018-08-27 19:17:20 +08:00
.md-dialog-content
margin-bottom number-keyboard-height
2018-09-14 16:03:24 +08:00
.md-codebox
margin-bottom 28px
2018-08-27 19:17:20 +08:00
.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
2018-09-14 16:03:24 +08:00
margin 28px 0
2018-08-27 19:17:20 +08:00
display flex
font-size captcha-footer-font-size
justify-content space-between
align-items center
overflow hidden
2018-11-15 15:47:44 +08:00
.md-captcha-error, .md-captcha-brief
2018-08-27 19:17:20 +08:00
flex 1 1 0%
2018-11-15 15:47:44 +08:00
.md-captcha-error
2018-08-27 19:17:20 +08:00
color captcha-error-color
2018-11-15 15:47:44 +08:00
.md-captcha-brief
color captcha-brief-color
2018-08-27 19:17:20 +08:00
.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
2018-03-26 16:04:04 +08:00
</style>