车联网项目中,大家想用只输入车牌号的键盘,但是默认的都不行,那怎么办呢?我最近写了一个出来,分享一下~
1、实例截图
2、原理讲解
上面两个图分别是选车牌登记地和车牌号码的,其实界面很简单,就是最外层一个遮盖层,里面分为两部分,上面操作框,下面键盘框,键盘框主要使用flex布局,点击的键盘触发emit,将值传到以上输入框中,下面的删除键,将是触发emit,将值进行处理。
然后大家估计感兴趣的是,如何将省市按钮和删除按钮插入到这个 v-for 循环的(这里面的按钮都是v-for循环渲染的),那么就要说到 flex布局的 order属性了,默认为0,值越大,就越往后面排,核心代码如下:
先检索到当前是哪种键盘类型,然后观察我们要进行order修改的值的位置在整个数组的哪里,然后修改order,最后在进行style动态修改order值
watch(() => prop.type, (val) => {
if (val === 'word') {
curList.value = wordList.value.map((item, index) => {
let order = 0
if (index >= 30) {
order = index - 30 + 2 // 为什么 + 2? 因为要给第一个按钮留一个位置
}
return {
...item,
id: 'word' + index,
order: order
}
})
} else {
curList.value = numberList.value.map((item, index) => {
let order = 0
if (index >= 29) {
order = index - 29 + 2
}
return {
...item,
id: 'number' + index,
order: order
}
})
}
}, {
immediate: true
})
3、代码实例
父组件:
<template>
<view class="bind-car">
<view class="title">
<text>绑定后享受车主服务</text>
<text>支持小/大型汽车、新能源车</text>
</view>
<view class="main">
<van-field class="main-half-55" :focus="carFocus" clickable readonly :border="false" :maxlength="1" :value="carValue" label="车牌号码" placeholder="车牌选择" input-align="right" @click-input="popup('word')" />
<van-field class="main-half-45" :focus="lpnFocus" clickable readonly :border="false" :maxlength="7" :value="lpn" placeholder="车牌号输入" input-align="right" @click-input="popup('number')" />
</view>
<button class="bottom" type="default" @click="nextStep">下一步</button>
<van-overlay :show="overlayBindCar">
<view class="car-wrapper" @click="overlayBindCar=false">
<view class="car-wrapper-box" @click.stop="overlayBindCar=true">
<Keyboard :type="keyboardType" @sureEmit="sureEmit" @resetEmit="resetEmit" @deleteEmit="deleteEmit" @switchEmit="switchEmit"></Keyboard>
</view>
</view>
</van-overlay>
</view>
</template>
<script setup>
import { isCarLicense } from '@/utils'
import { ref, reactive } from 'vue'
import { debounce } from '@/utils'
import Keyboard from './keyboard.vue'
const keyboardType = ref('word')
const carValue = ref('') // 车牌归属地
const lpn = ref('') // 车牌号
const carFocus = ref(false) // 聚焦归属地
const lpnFocus = ref(false) // 聚焦车牌号
const access = ref(false) // 车牌号验证是否通过
const sureEmit = e => {
if (e.status) {
if (e.type === 'word') {
carValue.value = e.value.name ? e.value.name : ''
} else {
lpn.value = lpn.value + (e.value.name ? e.value.name : '')
}
}
overlayBindCar.value = e.status
}
const resetEmit = () => {
overlayBindCar.value = false
}
const deleteEmit = e => {
if (e.type === 'word') {
carValue.value = ''
} else {
lpn.value = lpn.value.slice(0 , -1)
}
}
const switchEmit = e => {
if (e.type === 'word') {
keyboardType.value = 'number'
lpnFocus.value = true
carFocus.value = false
} else {
keyboardType.value = 'word'
lpnFocus.value = false
carFocus.value = true
}
}
const overlayBindCar = ref(false)
const popup = debounce((type) => {
overlayBindCar.value = true
keyboardType.value = type
}, 500, true)
const nextStep = debounce(() => {
let fullLpn = carValue.value + lpn.value
console.log(fullLpn, 'fullLpn');
if (!isCarLicense(fullLpn)) {
uni.showToast({
title: '请输入正确的车牌号',
icon: 'none'
})
access.value = false
return
} else {
access.value = true
}
uni.navigateTo({
url: `./bindCarSubmit?lpn=${fullLpn}`
})
}, 500, true)
</script>
<style lang="scss" scoped>
.bind-car{
background-color: #F3F3F3;
height: 100vh;
box-sizing: border-box;
.title{
padding: 40rpx 0 30rpx 30rpx;
text{
&:first-child{
display: block;
font-size: 36rpx;
font-weight: bold;
color: #333333;
line-height: 50rpx;
}
&:last-child{
display: block;
margin-top: 10rpx;
font-size: 28rpx;
font-weight: 400;
color: #999999;
line-height: 40rpx;
}
}
}
.main{
background-color: #fff;
height: 110rpx;
display: flex;
::v-deep van-field{
.van-field__label, input{
font-size: 36rpx;
font-weight: 400;
color: #303133;
line-height: 70rpx;
}
.van-field__body {
height: 70rpx;
}
}
.main-half-55{
width: 55%;
::v-deep .van-cell__value{
color: #333333;
}
}
.main-half-45{
width: 45%;
}
&:last-child{
::v-deep .van-cell{
padding-left: 0;
}
}
}
.bottom{
margin-top: 30rpx;
text-align: center;
width: calc(100% - 60rpx);
font-size: 34rpx;
font-weight: 400;
color: #FFFFFF;
line-height: 98rpx;
height: 98rpx;
background-color: #CA2915;
border-radius: 16rpx;
}
.car-wrapper{
height: 100%;
position: relative;
.car-wrapper-box{
position: absolute;
bottom: 0;
left: 0;
width: 100%;
}
}
}
</style>
子组件:
prop.type 的值:word => 车牌前缀, number => 车牌数字
然后三个emit:
sureEmit // 键盘确认
resetEmit // 键盘取消
deleteEmit // 删除字符
<template>
<view class="keyboard">
<view class="keyboard-top">
<text @click.stop="reset">取消</text>
<text>车牌号键盘</text>
<text @click.stop="sure">确认</text>
</view>
<view class="keyboard-box">
<button class="item" hover-class="active" :style="{order: item.order, marginLeft: item.id === 'number20' ? '35rpx' : '0', marginRight: item.id === 'number28' ? '35rpx' : '0'}" v-for="item in curList" :key="item.id" @click.stop="choose(item)">
{{item.name}}
</button>
<button hover-class="active" class="item item-0" @click.stop="switchKey">
{{prop.type === 'word' ? 'ABC' : '省市'}}
</button>
<button @click.stop="remove" hover-class="active" class="item item-1" :style="{flex: prop.type !== 'word' ? '0 0 70rpx' : '0 0 143rpx'}">
<image src="/static/img/keyboard-delete.png" mode="widthFix"></image>
</button>
</view>
</view>
</template>
<script setup>
import { reactive, watch, ref } from 'vue'
import { debounce } from '@/utils'
const prop = withDefaults(defineProps(), {
type: 'word' // word => 车牌前缀, number => 车牌数字
})
const curList = ref([]) // 当前所属显示的键盘数据来源
const activeButton = ref({})
const emit = defineEmits()
/**
* 键盘确认
*/
const sure = () => {
emit('sureEmit', {
type: prop.type,
value: activeButton.value,
status: false
})
}
/**
* 键盘取消
*/
const reset = () => {
emit('resetEmit')
}
/**
* 删除字符
*/
const remove = () => {
if (prop.type === 'word') {
emit('deleteEmit', {
type: prop.type
})
} else {
emit('deleteEmit', {
type: prop.type
})
}
}
/**
* 键盘item点击
*/
const choose = (item) => {
activeButton.value = item
emit('sureEmit', {
type: prop.type,
value: item,
status: true
})
if (prop.type === 'word') {
switchKey()
}
}
const switchKey = () => {
emit('switchEmit', {
type: prop.type
})
}
const wordList = ref([
{
name: '京'
},
{
name: '沪'
},
{
name: '粤'
},
{
name: '津'
},
{
name: '冀'
},
{
name: '豫'
},
{
name: '云'
},
{
name: '辽'
},
{
name: '黑'
},
{
name: '湘'
},
{
name: '皖'
},
{
name: '鲁'
},
{
name: '苏'
},
{
name: '浙'
},
{
name: '赣'
},
{
name: '鄂'
},
{
name: '桂'
},
{
name: '甘'
},
{
name: '晋'
},
{
name: '陕'
},
{
name: '蒙'
},
{
name: '吉'
},
{
name: '闽'
},
{
name: '贵'
},
{
name: '渝'
},
{
name: '川'
},
{
name: '青'
},
{
name: '琼'
},
{
name: '宁'
},
{
name: '挂'
},
{
name: '藏'
},
{
name: '港'
},
{
name: '澳'
},
{
name: '新'
},
{
name: '使'
},
{
name: '学'
}
])
const numberList = ref([
{
name: 1
},
{
name: 2
},
{
name: 3
},
{
name: 4
},
{
name: 5
},
{
name: 6
},
{
name: 7
},
{
name: 8
},
{
name: 9
},
{
name: '0'
},
{
name: 'Q'
},
{
name: 'W'
},
{
name: 'E'
},
{
name: 'R'
},
{
name: 'T'
},
{
name: 'Y'
},
{
name: 'U'
},
{
name: 'I'
},
{
name: 'O'
},
{
name: 'P'
},
{
name: 'A'
},
{
name: 'S'
},
{
name: 'D'
},
{
name: 'F'
},
{
name: 'G'
},
{
name: 'H'
},
{
name: 'J'
},
{
name: 'K'
},
{
name: 'L'
},
{
name: 'Z'
},
{
name: 'X'
},
{
name: 'C'
},
{
name: 'V'
},
{
name: 'B'
},
{
name: 'N'
},
{
name: 'M'
}
])
watch(() => prop.type, (val) => {
if (val === 'word') {
curList.value = wordList.value.map((item, index) => {
let order = 0
if (index >= 30) {
order = index - 30 + 2 // 为什么 + 2? 因为要给第一个按钮留一个位置
}
return {
...item,
id: 'word' + index,
order: order
}
})
} else {
curList.value = numberList.value.map((item, index) => {
let order = 0
if (index >= 29) {
order = index - 29 + 2
}
return {
...item,
id: 'number' + index,
order: order
}
})
}
}, {
immediate: true
})
</script>
<style lang="scss" scoped>
.keyboard{
background-color: #E7E6E4;
font-size: 36rpx;
font-weight: 400;
color: #000000;
line-height: 50rpx;
.keyboard-top{
padding: 0 20rpx;
height: 68rpx;
line-height: 68rpx;
background-color: #fff;
color: #909399;
display: flex;
flex-flow: row nowrap;
align-items: center;
justify-content: space-between;
text{
&:last-child{
color: #CA2915;
}
}
}
.keyboard-box{
padding: 0 10rpx 10rpx;
display: flex;
flex-flow: row wrap;
justify-content: space-between;
button{
margin: 0;
padding: 0;
font-size: 36rpx;
font-weight: 400;
color: #000000;
}
.item{
flex: 0 0 70rpx;
margin-top: 10rpx;
color: #000000;
text-align: center;
height: 89rpx;
line-height: 89rpx;
background-color: #FCFCFE;
box-shadow: 0px 1px 0px 0px #898A8D;
border-radius: 5rpx;
}
.active{
background-color: #ADB3BC;
}
.item-0{
background-color: #ADB3BC;
color: #000000;
flex: 0 0 143rpx;
order: 1;
}
.item-1{
flex: 0 0 143rpx;
background-color: #ADB3BC;
position: relative;
order: 9;
image{
width: 59rpx;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
}
}
</style>
本博客所有 utils 方法都来自于另一篇博客有趣且重要的JS知识合集(12)常用基础算法