本文采用需求导向阐述,需求->实现效果->实现思路->核心细节->详细代码以及调用方法
1.需求:需要实现一个安全认证的键盘,如下图效果图
2.实现效果:
3.实现思路
首先要考虑的是直接用input控件,让手机系统弹出输入法是否可行,我原本是做android的,从android的角度来看,涉及到键盘弹出需要再WebView所在Activity设置android:windowSoftInputMode="stateHidden|adjustResize"属性,否则可能会发生键盘把底部一些布局弹起来的问题,例如:
看上面的效果感觉不行,一是样式没有保持一致,还不干净;二是依赖android系统默认设置,三是还要依赖android那边的实现,所以最后决定自己写。
4.实现细节
- 直接按照普通的样式来写,最后保证键盘控件在父布局的最底部,使用
position fixed
left 0
bottom 0
代码即可实现类似键盘在最底部弹出
- 父布局需要传递一个值isShowKeyBoard给这个键盘子组件,让父布局根据业务控制是否显示这个键盘组件;
- 子组件需要在点击确定后取消后告诉父布局,用户输入的密码是什么,或者优化成在键盘子组件进行密码验证,最后返回一个验证结果给父组件,我上传的代码没有调用服务进行验证,而是返回了密码给父组件。
5.详细代码以及调用方法
子组件实现代码(基于pug和style模板语言,不懂的可看我之前的博客):
- 键盘子组件代码
<!--密码键盘,只能输入6位数字-->
<template lang="pug">
.keyboard(:class="{isIos : isIOS}" v-show="isShowChild")
.safe
.tip 安全验证
.password {{sercet}}
.operator
.btn.sure(@click="clickSure" :class="{sure_canClick: isCanClick}") 确定
.btn.cancel(@click="clickCancel") 取消
.num_board
.row
button(@click="clickNum('1')") 1
button.center(@click="clickNum('2')") 2
button(@click="clickNum('3')") 3
.row
button(@click="clickNum('4')") 4
button(class = "center" @click="clickNum('5')") 5
button(@click="clickNum('6')") 6
.row
button(@click="clickNum('7')") 7
button(class = "center" @click="clickNum('8')") 8
button(@click="clickNum('9')") 9
.row
button(class= "num_null")
button(class = "center" @click="clickNum('0')") 0
button(class = "num_delete" @click="clickNum('X')")
img(src="../../assets/delete.png" class="icon")
</template>
<script>
export default {
props:{// 接受调用处传进来的值
isShowKeyBoard:{// 是否显示键盘
required: true,
default() {
return false// 默认不显示
}
},
},
data() {
return {
isIOS: false,// 判断当前页面是否跑在ios平台,因为ios平台兼容性问题,要特殊处理
isCanClick: false,// 如果不可点击'确定'的按钮样式更改
password: '',//(真正的密码)
sercet: '',// 密文密码(******)
// 因为父组件传递进来的属性不支持进行双向绑定,需要创建一个副本isShowChild来进行双向绑定操作。
isShowChild: this.isShowKeyBoard,
}
},
created(){// 在html渲染前先拿isIOS的值
let u = navigator.userAgent
this.isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)
this.clickNum('X') // 清空数据,恢复默认值
},
watch: {// 动态监听父布局是否有改动了传进来的这个值
isShowKeyBoard: {
handler(newValue) {
this.isShowChild = newValue// 有改动则重新赋值给副本
},
deep: true,
immediate: true
},
},
methods: {
clickCancel() {
// 因为子组件直接改了副本后 第二次父组件再改 子组件的watch监听不到了,所以这里直接改父控件调用处的原变量,让那边去触发watch,这样第二次还能触发。
this.$emit('onShowChange', false)
this.clickNum('X')// 清空密码
},
clickSure() {
if(this.password.length === 6) {
// 密码长度对了,返回密码,这里也可以调用服务进行验证,最后返回验证的值。
console.log(this.password)
this.$emit('onCheckPassword', this.password)
} else {
this.$toast('请输入6位数密码')// 请输入6位数密码
}
},
clickNum(num) {
console.log(num)
if(num != 'X') {
if( this.sercet === '请输入交易密码' ) {
this.password = ''
this.password +=num
this.sercet = ''
this.sercet += '*'
} else if(this.password.length === 6){
this.$toast('已经输入6位数了')
} else{
this.password +=num
this.sercet += '*'
if(this.password.length === 6) {
this.isCanClick = true
}
}
} else { // 清空密码和相关属性恢复
this.isCanClick = false
this.password = ''
this.sercet= '请输入交易密码'
}
},
},
}
</script>
<style scoped lang="stylus">
.keyboard
background white
width 100%
height 355px
border-radius 8px 8px 0px 0px
position fixed
left 0
bottom 0
&.isIos
bottom 48px
.safe
height:52px;
background #E5EBF7
border-radius:8px;
display flex
justify-content space-between
margin 20px 10px 12px
padding 16px 14px
.tip
font-size 14px
line-height:20px;
text-align left
color #333333
.password
color #999999
font-size 14px
line-height:20px;
text-align right
opacity:1;
.operator
display flex
flex-direction row-reverse
margin-bottom 20px
.btn
width:80px;
height:32px;
font-size 14px
line-height 32px
display flex
justify-content center
&.cancel
border-radius:4px;
border:1px solid rgba(68,114,197,1);
color #4472C5
&.sure
background:rgba(144,171,220,1);
border-radius:4px;
color #D2DDF1
margin-right 10px
margin-left 20px
&.sure_canClick
background #4472C5
color white
.num_board
background #D2D5DB
height 100%
padding 6px
.row
margin-bottom 7px
display flex
flex-direction row
button
width:117px;
height:46px;
background:rgba(255,255,255,1);
box-shadow:0px 0 0px 0px rgba(132,134,136,1);
border-radius:5px;
text-align center
font-size 25px
color #000000
display flex
justify-content center
line-height:46px;
&.center
margin 0 6px
.num_null
width:117px;
height:46px;
float: right;
margin: 0;
padding: 0;
background-color: #D2D5DB;
border: none
outline:none
.num_delete
width:117px;
height:46px;
background #D2D5DB
position relative
.icon
position absolute
width 23px
height 18px
top 30%
right 40%
</style>
- 父布局调用代码:
<template lang="pug">
Keyboard(:isShowKeyBoard="" @onShowChange="showKeyBoard" @onCheckPassword="onCheckPassword")
</template>
<script>
import Keyboard from '_components/keyboard'
data(){
return {
isShowKeyBoard: false,// 是否展示数字键盘
}
},
methods: {
// 显示或关闭按钮
showKeyBoard(flag) {
this.isShowKeyBoard = flag
},
// 点击确定的回调
onCheckPassword(password) {
this.submitData(password)
},
},
</script>