uniapp + vue3微信小程序开发(7)键盘开发

车联网项目中,大家想用只输入车牌号的键盘,但是默认的都不行,那怎么办呢?我最近写了一个出来,分享一下~

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)常用基础算法

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值