uniapp自定义车牌号查询输入框

29 篇文章 1 订阅

 查询效果图

 直接上代码:

<template>
	<view class="query-vehicle">
		<!-- <page-navbar :is-slot="true" title="车辆查询"></page-navbar> -->
		<view class="title">请输入查询的车牌号</view>
		<!-- 车辆查询 -->
		<view>
			<view class="car" @click="bFocus = true">
				<input :focus="bFocus" type="text" v-model="sCar" disabled class="ipt-hide" :maxlength="cCarNum" />
				<view class="car-item" v-for="(nIndex, k) in cCarNum" :class="{active: k === sCar.length }"
					:key="nIndex">
					{{sCar.split('')[k]?sCar.split('')[k].toUpperCase():''}}
				</view>
				<view class="car-change" v-if="bIsUnit">
					<u-image width="50rpx" height="50rpx" :src="newEnergyPath" />
					<text>新能源</text>
				</view>
			</view>
			<!-- 自定义键盘组件 -->
			<keyboard-input @typeChange="typeChange" v-if="bFocus" :plate="sCar" @export="setPlate"
				@close="bFocus = false" />
		</view>
		<submit-btn text="确认查询" @handleSubmit="handleSubmit" :isOpacity="isOpacity"></submit-btn>
	</view>
</template>
<script>
	import KeyboardInput from '@/components/keyboard-input/keyboard-input.vue'
	export default {
		components: { KeyboardInput },
		mixins: [],
		data() {
			return {
				newEnergyPath: '/pages/home/static/new-energy.png',
				sCar: '', // 车牌号
				bIsUnit: true, // 是否新能源
				bFocus: false, // 输入车键盘触发
				queryRecord: []
			}
		},
		mounted() {},
		computed: {
			cCarNum() {
				return this.bIsUnit ? 7 : 8; //车牌位数(新能源8位)
			},
			isOpacity() {
				return this.sCar.length >= 7
			}
		},
		onShow() {},
		watch: {},
		methods: {
			// 车牌号完成触发
			setPlate(plate) {
				if (plate.length >= 7) this.sCar = plate;
				this.bFocus = false;
			},
			// 切换新能源触发
			typeChange(e) {
				// 2新能源
				if (Number(e) === 2) {
					this.bIsUnit = false
				} else {
					this.bIsUnit = true
				}
				this.sCar = ''
			},
			// 查询车辆信息
			parkingGet(sCarNumber) {
				uni.showLoading({
					title: '正在查询...',
					mask: true
				})
				this.$u.api.parkingGet({
					carLicense: sCarNumber
				}).then(res => {
					const {
						cardUuid
					} = res.data
					if (cardUuid && cardUuid !== '' && cardUuid !== '0') {
						this.$u.route({
							url: 'pages/home/parkingFee/view/vehicleInformation',
							params: res.data
						})
					} else {
						uni.showToast({
							title: '未查询到停车记录',
							icon: 'none'
						})
					}
				}).catch(err => {
					throw err
				}).finally(() => {
					uni.hideLoading();
				})
			},
			handleSubmit() {
				if (this.sCar.length >= 7) {
					this.parkingGet(this.sCar)
				} else {
					uni.showToast({
						title: '请输入正确车牌号查询',
						icon: 'none'
					})
				}
			}
		}
	}
</script>
<style lang='scss'>
	.query-vehicle {
		padding: 60rpx 0;
		background: #FFFFFF;

		.title {
			padding: 0 30rpx 50rpx;
			font-size: 40rpx;
			font-weight: bold;
			color: #262626;
		}

		.car {
			display: flex;
			flex-direction: row;
			justify-content: center;
			align-items: center;
			margin-bottom: 270rpx;
			width: 100%;
			box-sizing: border-box;

			.car-item {
				height: 106rpx;
				width: 72rpx;
				margin: 0 6rpx;
				text-align: center;
				line-height: 106rpx;
				color: #262626;
				font-size: 50rpx;
				border: 2rpx solid #D8D8D8;
				border-radius: 6rpx;
			}

			.active {
				border: 2rpx solid #0683FF;
			}

			.ipt-hide {
				position: absolute;
				z-index: -1;
				left: -100%;
				opacity: 0
			}

			.car-change {
				height: 106rpx;
				display: flex;
				flex-direction: column;
				align-items: center;
				justify-content: center;
				font-size: 20rpx;
				padding: 4rpx;
				margin: 0 6rpx;
				line-height: 2;
				color: #BBBBBB;
				border: 2rpx dotted #D8D8D8;
				border-radius: 6rpx;
			}
		}
	}
</style>

键盘组件部分:

自定义车键盘组件:keyboard-input.vue      uni-plate-input.css       uni-plate-input.less

新建三个文件夹同级:

keyboard-input.vue 

<template>
	<view class="so-mask">
		<view style="height: 100%" @tap="$emit('close')" />
		<view class="so-plate animation-scale-up">
			<view class="so-plate-head">
				<view class="so-plate-type">
					<radio-group @change="typeChange">
						<label>
							<radio value="1" :checked="type===1" />
							普通车牌
						</label>
						<label>
							<radio value="2" :checked="type===2" />
							新能源车牌
						</label>
					</radio-group>
				</view>
			</view>
			<view class="so-plate-body">
				<view class="so-plate-word" :class="{ active: currentInputIndex == 0 }" @tap="inputSwitch"
					data-index="0">
					<text>{{ currentInputValue[0] }}</text>
				</view>
				<view class="so-plate-word" :class="{ active: currentInputIndex == 1 }" @tap="inputSwitch"
					data-index="1">
					<text>{{ currentInputValue[1] }}</text>
				</view>
				<view class="so-plate-dot"></view>
				<view class="so-plate-word" :class="{ active: currentInputIndex == 2 }" @tap="inputSwitch"
					data-index="2">
					<text>{{ currentInputValue[2] }}</text>
				</view>
				<view class="so-plate-word" :class="{ active: currentInputIndex == 3 }" @tap="inputSwitch"
					data-index="3">
					<text>{{ currentInputValue[3] }}</text>
				</view>
				<view class="so-plate-word" :class="{ active: currentInputIndex == 4 }" @tap="inputSwitch"
					data-index="4">
					<text>{{ currentInputValue[4] }}</text>
				</view>
				<view class="so-plate-word" :class="{ active: currentInputIndex == 5 }" @tap="inputSwitch"
					data-index="5">
					<text>{{ currentInputValue[5] }}</text>
				</view>
				<view class="so-plate-word" :class="{ active: currentInputIndex == 6 }" @tap="inputSwitch"
					data-index="6">
					<text>{{ currentInputValue[6] }}</text>
				</view>
				<view class="so-plate-word" :class="{ active: currentInputIndex == 7 }" @tap="inputSwitch"
					v-if="type == 2" data-index="7">
					<text>{{ currentInputValue[7] }}</text>
				</view>
			</view>
			<view class="so-plate-foot">
				<view class="so-plate-keyboard" :style="{height:keyboardHeight}">
					<view id="keyboard">
						<block v-if="inputType == 1">
							<view hover-class="hover" class="so-plate-key" v-for="el of provinceText" :key="el"
								:data-value="el" @tap="chooseKey">{{ el }}</view>
						</block>
						<block v-if="inputType == 1">
							<text class="so-plate-key fill-block"></text>
						</block>
						<block v-if="inputType >= 3">
							<view hover-class="hover" class="so-plate-key" v-for="el of numberText" :key="el"
								:data-value="el" @tap="chooseKey">{{ el }}</view>
						</block>
						<block v-if="inputType >= 2">
							<view hover-class="hover" class="so-plate-key" v-for="el of wordText" :key="el"
								:data-value="el" @tap="chooseKey">{{ el }}</view>
						</block>
						<block v-if="inputType == 3">
							<text v-for="el of fillBlock" :key="el.num" class="so-plate-key fill-block"></text>
						</block>
						<block v-if="inputType == 4">
							<view hover-class="hover" class="so-plate-key" v-for="el of lastWordText" :key="el"
								:data-value="el" @tap="chooseKey">{{ el }}</view>
						</block>
						<text v-if="inputType == 4" class="so-plate-key fill-block"></text>
					</view>
				</view>
				<view class="so-plate-btn-group">
					<view>
						<button class="so-plate-btn so-plate-btn--cancel" @tap="$emit('close')">取消</button>
					</view>
					<view>
						<button class="so-plate-btn so-plate-btn--delete" @tap="deleteKey">删除</button>
						<button class="so-plate-btn so-plate-btn--submit" @tap="exportPlate">完成</button>

					</view>

				</view>
			</view>
		</view>
	</view>
</template>
<script>
	export default {
		name: 'uni-plate-input',
		data() {
			return {
				type: 1, //车牌类型
				currentInputIndex: 0, //当前编辑的输入框
				currentInputValue: ['', '', '', '', '', '', ''],
				fillBlock: [{
					num: 11
				}, {
					num: 12
				}, {
					num: 13
				}, {
					num: 14
				}, {
					num: 15
				}, {
					num: 16
				}], //避免:key报错
				keyboardHeightInit: false,
				keyboardHeight: 'auto',
				provinceText: [
					'粤',
					'京',
					'冀',
					'沪',
					'津',
					'晋',
					'蒙',
					'辽',
					'吉',
					'黑',
					'苏',
					'浙',
					'皖',
					'闽',
					'赣',
					'鲁',
					'豫',
					'鄂',
					'湘',
					'桂',
					'琼',
					'渝',
					'川',
					'贵',
					'云',
					'藏',
					'陕',
					'甘',
					'青',
					'宁',
					'新'
				],
				numberText: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'],
				wordText: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U',
					'V', 'W', 'X', 'Y', 'Z'
				],
				lastWordText: ['挂', '港', '学', '领', '警']
			};
		},
		props: {
			plate: {
				type: String
			}
		},
		computed: {
			//输入框类型
			inputType() {
				switch (this.currentInputIndex) {
					case 0:
						return 1;
						break;
					case 1:
						return 2;
						break;
					case 2:
						return 3;
						break;
					case 3:
						return 3;
						break;
					case 4:
						return 3;
						break;
					case 5:
						return 3;
						break;
					case 6:
						return this.type == 2 ? 3 : 4;
						break;
					case 7:
						return 4;
						break;
					default:
						return 1;
						break;
				}
			}
		},
		watch: {
			currentInputIndex: function(n, o) {
				if (!this.keyboardHeightInit) return
				this.$nextTick(() => {
					this.changeKeyboardHeight()
				})
			}
		},
		methods: {
			//车牌类型切换
			typeChange(e) {
				this.$emit("typeChange", e.detail.value);
				const {
					value
				} = e.detail;
				this.type = parseInt(value)
				this.currentInputIndex = 0
				if (value == 1) {
					this.currentInputValue = ['', '', '', '', '', '', '']
				} else {
					this.currentInputValue = ['', '', '', '', '', '', '', '']
				}
			},
			inputSwitch(e) {
				const {
					index
				} = e.currentTarget.dataset;
				this.currentInputIndex = parseInt(index);
			},
			chooseKey(e) {
				const {
					value
				} = e.currentTarget.dataset;
				this.$set(this.currentInputValue, this.currentInputIndex, value);
				if (this.type == 1 && this.currentInputIndex < 6) {
					this.currentInputIndex++
				}
				if (this.type == 2 && this.currentInputIndex < 7) {
					this.currentInputIndex++
				}
			},
			deleteKey() {
				this.$set(this.currentInputValue, this.currentInputIndex, '')
				if (this.currentInputIndex != 0) this.currentInputIndex--
			},
			exportPlate() {
				const plate = this.currentInputValue.join('')
				let err = false
				if (this.type === 1 && plate.length != 7) {
					err = true
				} else if (this.type === 2 && plate.length != 8) {
					err = true
				}
				if (err) return uni.showToast({
					title: '请输入完整的车牌号码',
					icon: 'none'
				})

				this.$emit('export', plate)
			},
			changeKeyboardHeight() {
				const that = this
				const query = uni.createSelectorQuery().in(this);
				query.select('#keyboard').boundingClientRect();
				query.exec(function(res) {
					if (res && res[0]) {
						that.keyboardHeight = res[0].height + uni.upx2px(30) + 'px'
						that.keyboardHeightInit = true
					}
				});
			}
		},
		mounted() {
			// console.log(this.plate);
			const plateKey = this.plate.split('')
			if (plateKey.length === 7) {
				this.type = 1
			} else if (plateKey.length === 8) {
				this.type = 2
			}
			if (plateKey.length === 7 || plateKey.length === 8) {
				this.currentInputValue = plateKey
				this.currentInputIndex = plateKey.length - 1
			}

			setTimeout(() => { //在动画结束之后才开始获取
				this.$nextTick(() => {
					this.changeKeyboardHeight()
				})
			}, 500);
		}
	};
</script>
<style scoped lang="less">
	@import './uni-plate-input';
</style>

关于uni-plate-input.css部分:

.so-mask {
	position: fixed;
	top: 0;
	bottom: 0;
	right: 0;
	left: 0;
	background: rgba(0, 0, 0, 0.5);
	z-index: 998
}

.so-plate {
	box-sizing: border-box;
	position: absolute;
	bottom: 0;
	width: 100%;
	left: 0;
	background: #fff;
	padding: 25upx 25upx 0 25upx
}

.so-plate-head {
	display: -webkit-box;
	display: flex;
	-webkit-box-pack: justify;
	justify-content: space-between;
	-webkit-box-align: center;
	align-items: center
}

.so-plate-type {
	-webkit-box-flex: 1;
	flex: 1;
	display: block
}

.so-plate-type label {
	display: inline-block;
	min-height: 32upx;
	font-size: 26upx;
	margin-right: 20upx
}

.so-plate-body {
	box-sizing: border-box;
	padding: 30upx 0;
	display: -webkit-box;
	display: flex;
	-webkit-box-pack: justify;
	justify-content: space-between;
	-webkit-box-align: center;
	align-items: center
}

.so-plate-word {
	border: 1upx solid #ccc;
	border-radius: 10upx;
	height: 0;
	margin: 0 5upx;
	box-sizing: border-box;
	padding-bottom: calc(4.28571429%);
	width: calc(4.28571429%);
	position: relative
}

.so-plate-word.active {
	border-color: #007aff;
	box-shadow: 0 0 15upx 0 #007aff
}

.so-plate-word text {
	position: absolute;
	top: 50%;
	left: 50%;
	-webkit-transform: translateX(-50%) translateY(-50%);
	transform: translateX(-50%) translateY(-50%);
	font-weight: 700;
	font-size: 32upx
}

.so-plate-dot {
	width: 15upx;
	height: 15upx;
	background: #ccc;
	border-radius: 50%;
	margin: 0 5upx
}

.so-plate-keyboard {
	background: #eee;
	margin-left: -25upx;
	margin-right: -25upx;
	padding: 20upx 25upx 10upx 25upx;
	box-sizing: border-box;
	-webkit-transition: all .3s;
	transition: all .3s
}

.so-plate-keyboard>view {
	display: -webkit-box;
	display: flex;
	flex-wrap: wrap;
	-webkit-box-pack: justify;
	justify-content: space-between
}

.so-plate-key {
	display: block;
	background: #fff;
	border-radius: 10upx;
	box-shadow: 0 0 8upx 0 #bbb;
	width: 80upx;
	height: 80upx;
	margin: 5upx 0;
	font-size: 32upx;
	text-align: center;
	display: -webkit-box;
	display: flex;
	-webkit-box-align: center;
	align-items: center;
	-webkit-box-pack: center;
	justify-content: center;
	position: relative
}

.so-plate-key.hover {
	background: #efefef
}

.so-plate-key.fill-block {
	width: 80upx;
	height: 80upx;
	background: none;
	box-shadow: none
}

.so-plate-btn {
	display: inline-block;
	background: #fff;
	border-radius: 10upx;
	box-shadow: 0 0 10upx 0 #bbb;
	font-size: 28upx;
	text-align: center;
	margin: 0 0 0 10upx
}

.so-plate-btn-group {
	display: -webkit-box;
	display: flex;
	-webkit-box-pack: justify;
	justify-content: space-between;
	background: #eee;
	margin-left: -25upx;
	margin-right: -25upx;
	box-sizing: border-box;
	padding: 0 25upx 10upx 25upx
}

.so-plate-btn--cancel {
	margin: 0
}

.so-plate-btn--submit {
	background: #5773f9;
	color: #fff
}

.so-plate-btn--delete {
	color: #fd6b6d
}

.animation-scale-up {
	-webkit-animation-duration: .2s;
	animation-duration: .2s;
	-webkit-animation-timing-function: ease-out;
	animation-timing-function: ease-out;
	-webkit-animation-fill-mode: both;
	animation-fill-mode: both;
	-webkit-animation-name: scale-up;
	animation-name: scale-up
}

@-webkit-keyframes scale-up {
	0% {
		opacity: .8;
		-webkit-transform: scale(.8);
		transform: scale(.8)
	}

	100% {
		opacity: 1;
		-webkit-transform: scale(1);
		transform: scale(1)
	}
}

@keyframes scale-up {
	0% {
		opacity: .8;
		-webkit-transform: scale(.8);
		transform: scale(.8)
	}

	100% {
		opacity: 1;
		-webkit-transform: scale(1);
		transform: scale(1)
	}
}

关于uni-plate-input.less部分:

.so-mask {
	position: fixed;
	top: 0;
	bottom: 0;
	right: 0;
	left: 0;
	background: rgba(0, 0, 0, 0.5);
	z-index: 998;
}
.so-plate {
	box-sizing: border-box;
	position: absolute;
	bottom: 0;
	width: 100%;
	left: 0;
	background: #fff;
	padding: 25upx 25upx 0 25upx;
	&-head {
		display: flex;
		justify-content: space-between;
		align-items: center;
	}
	&-type {			
		flex:1;
		display:block;
		label {
			display: inline-block;
			min-height: 32upx;
			font-size: 26upx;
			margin-right: 10upx;
		}
	}
	&-body {
		box-sizing: border-box;
		padding: 30upx 0;
		display: flex;
		justify-content: space-between;
		align-items: center;
	}
	&-word {
		border: 1upx solid #ccc;
		border-radius: 10upx;
		height: 0;
		margin: 0 5upx;
		box-sizing: border-box;
		padding-bottom: calc((100% - 70upx) / 7);
		width: calc((100% - 70upx) / 7);
		position: relative;
		&.active {
			border-color: #007aff;
			box-shadow: 0 0 15upx 0 #007aff;
		}
		text {
			position: absolute;
			top: 50%;
			left: 50%;
			transform: translateX(-50%) translateY(-50%);
			font-weight: 700;
			font-size: 32upx;
		}
	}
	&-dot {
		width: 15upx;
		height: 15upx;
		background: #ccc;
		border-radius: 50%;
		margin: 0 5upx;
	}
	&-keyboard {
		background: #eee;
		margin-left: -25upx;
		margin-right: -25upx;
		padding: 20upx 25upx 10upx 25upx;
		box-sizing: border-box;
		transition: all .3s;
		&>view{
			display: flex;
			flex-wrap: wrap;
			justify-content: space-between;
		}
	}
	&-key {
		display: block;
		background: #fff;
		border-radius: 10upx;
		box-shadow: 0 0 8upx 0 #bbb;
		width: 80upx;
		height: 80upx;
		margin: 5upx 0;
		font-size: 32upx;
		text-align: center;
		display: flex;
		align-items: center;
		justify-content: center;
		position: relative;
		&.hover {
			background: #efefef;
		}
		&.fill-block {
			width: 80upx;
			height: 80upx;
			background: none;
			box-shadow: none;
		}
	}
	&-btn {
		display: inline-block;
		background: #fff;
		border-radius: 10upx;
		box-shadow: 0 0 10upx 0 #bbb;
		font-size: 28upx;
		text-align: center;
		margin:0 0 0 10upx;
		padding:0 25upx;
		&-group{
			display: flex;
			justify-content: space-between;
			background: #eee;
			margin-left: -25upx;
			margin-right: -25upx;
			box-sizing: border-box;
			padding: 0 25upx 10upx 25upx;
		}
		&--cancel{
			margin:0;
		}
		&--submit{
			background:#5773f9;
			color:#fff;
		}
		&--delete{
			color:#fd6b6d;
		}
	}
}


.animation-scale-up {
	animation-duration: .2s;
	animation-timing-function: ease-out;
	animation-fill-mode: both;
    animation-name: scale-up
}
@keyframes scale-up {
    0% {
        opacity: .8;
        transform: scale(.8)
    }

    100% {
        opacity: 1;
        transform: scale(1)
    }
}

三个文件目录结构:

按照上面所写就完成了自定义车牌号键盘输入了,喜欢点个赞点个关注

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值