效果图:
用户移动端添加车牌号,于是我们手写了一个H5车牌号软键盘,已经封装成一个组件,调用即可。
功能如上图所示,支持 8位电车车牌和7位油车车牌。
第一位是 省份,用户点击就会弹出省份软键盘;
第二位是 字母,用户点击只能输入字母;
第三、四、五、六位是字母和数字,不包含O;
第七位是 数字、字母、或者学、警之类汉字;
第八位是 电车选项。
用户点击软键盘对应的按键时会高亮,这个是后期加的,所以上图中没有展示出来。
我用的移动端是rem布局,使用插件进行转换了(转换比是1rem=100px)
完整代码如下
主文件:
<template>
<div>
<button @click="addCare">添加车辆</button>
<div>车牌:{{ carNo }}</div>
<CarInput
:addShow="showCarInput"
@close="showCarInput = false"
@submit="onCarInputSubmit"
/>
</div>
</template>
<script>
import CarInput from "../components/CarPlateInput.vue";
export default {
name: "test",
components: {
CarInput,
},
data() {
return {
carNo: "",
showCarInput: false,
};
},
methods: {
addCare() {
this.showCarInput = true;
},
// 车牌弹窗回填
onCarInputSubmit(val) {
this.carNo = val;
this.showCarInput = false;
},
},
};
</script>
车牌录入组件:
<template>
<div class="shade-layer" v-if="addShow" @click="keyboardShow = false">
<div class="add-content">
<div class="add-header">
<p>车辆信息</p>
<span @click="$emit('close')">关闭</span>
</div>
<div class="form-list">
<p>车牌号:</p>
<div class="car-num-input">
<div
v-for="(item, index) in carNumList"
:key="index"
@click.stop="selectCarNum(index)"
:class="
(carIndex == index ? 'active' : '') +
' ' +
(item ? 'status-key' : '')
"
>
{{
index == 0 && !item
? '省'
: index == carNumList.length - 1 && !item
? '新能源'
: item
}}
</div>
</div>
</div>
<button class="submit-btn" @click="carSubmitBtn()" v-points>
提交
</button>
</div>
<!-- 车牌键盘 -->
<div class="keyboard-layer" v-if="keyboardShow" @click.stop="">
<div class="keyboard-header">
<span @click="keyboardShow = false">完成</span>
</div>
<!-- 省份键盘 -->
<div class="province-layer" v-if="carIndex == '0'">
<span
v-for="(item, index) in provinceList"
:key="index"
@click="keyboardBtn(item)"
:class="activeKey == item ? 'active-hover' : ''"
>{{ item == 'del' ? '删除' : item }}</span
>
</div>
<!-- 数字字母键盘 -->
<div class="keyboard-item" v-if="carIndex != '0'">
<div v-if="carIndex != '1'">
<span
v-for="(item, index) in keyboardList[0]"
:key="index"
@click="keyboardBtn(item)"
:class="activeKey == item ? 'active-hover' : ''"
>{{ item }}</span
>
</div>
<div>
<span
v-for="(item, index) in keyboardList[1]"
:key="index"
@click="keyboardBtn(item)"
:class="
(item == 'O' && carIndex != '1' ? 'no-btn' : '') +
' ' +
(activeKey == item ? 'active-hover' : '')
"
>{{ item }}</span
>
</div>
<div>
<span
v-for="(item, index) in keyboardList[2]"
:key="index"
@click="keyboardBtn(item)"
:class="activeKey == item ? 'active-hover' : ''"
>{{ item }}</span
>
</div>
<div>
<span
v-for="(item, index) in keyboardList[3]"
:key="index"
@click="keyboardBtn(item)"
:class="activeKey == item ? 'active-hover' : ''"
>{{ item == 'del' ? '删除' : item }}</span
>
</div>
<div v-if="carIndex == carNumList.length - 2">
<span
v-for="(item, index) in keyboardList[4]"
:key="index"
@click="keyboardBtn(item)"
:class="activeKey == item ? 'active-hover' : ''"
>{{ item }}</span
>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'CarPlateInput',
props: {
addShow: {
type: Boolean,
default: false
}
},
data() {
return {
formData: {
carNumber: ''
},
carNumList: ['', '', '', '', '', '', '', ''],
activeKey: '',
timeoutId: null,
carIndex: null,
keyboardShow: false,
provinceList: [
'京', '津', '沪', '渝', '冀', '豫', '云', '辽', '黑', '湘', '皖', '鲁', '新', '苏', '浙', '赣', '鄂', '桂', '甘', '晋', '蒙', '陕', '吉', '闽', '贵', '粤', '青', '藏', '川', '宁', '琼', '使', '领', '学', '警', '挂', 'del'
],
keyboardList: [
['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'],
['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'O', 'P'],
['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L'],
['Z', 'X', 'C', 'V', 'B', 'N', 'M', 'del'],
['学', '警', '港', '澳']
]
}
},
watch: {
addShow(val) {
if (!val) {
this.formData = { carNumber: '' }
this.carIndex = ''
this.carNumList = ['', '', '', '', '', '', '', '']
this.keyboardShow = false
}
}
},
methods: {
carSubmitBtn() {
this.formData.carNumber = ''
for (let i in this.carNumList) {
if (this.carNumList[i] == '' && i != this.carNumList.length - 1) {
this.$emit('incomplete')
return
}
this.formData.carNumber += this.carNumList[i]
}
this.$emit('submit', this.formData.carNumber)
},
selectCarNum(inx) {
this.carIndex = inx
if (!this.keyboardShow) {
this.keyboardShow = true
}
},
keyboardBtn(val) {
this.activeKey = val
this.activeKeyBtn()
this.carNumList[this.carIndex] = val == 'del' ? '' : val
if (val == 'del' && this.carIndex > 0) {
this.carIndex--
}
if (val != 'del' && this.carIndex < this.carNumList.length - 1) {
this.carIndex++
}
this.$forceUpdate()
},
activeKeyBtn() {
if (this.timeoutId) clearTimeout(this.timeoutId)
this.timeoutId = setTimeout(() => {
this.activeKey = ''
}, 300)
}
}
}
</script>
<style scoped lang="less">
.shade-layer {
position: fixed;
top: 0;
left: 0;
z-index: 5;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.4);
padding: 16px;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;
.add-content {
width: 100%;
padding: 16px 16px 24px;
box-sizing: border-box;
border-radius: 16px;
background: #fff;
.add-header {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding-bottom: 14px;
box-sizing: border-box;
p {
color: #000;
font-family: "PingFang SC";
font-size: 16px;
font-weight: 700;
line-height: 22px;
}
span {
color: #3faa73;
font-family: "PingFang SC";
font-size: 12px;
font-weight: 700;
line-height: 24px;
cursor: pointer;
}
}
.form-list {
p {
color: #6b7280;
font-family: "PingFang SC";
font-size: 14px;
font-weight: 500;
line-height: 20px;
}
input {
display: block;
font-size: 14px;
line-height: 20px;
width: 100%;
border-radius: 8px;
border: none;
background: #f5f7fa;
padding: 12px 16px;
box-sizing: border-box;
}
.car-num-input {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding: 4px 0;
box-sizing: border-box;
div {
width: 30px;
height: 36px;
background: rgba(0, 0, 0, 0.05);
border-radius: 4px;
border: 1px solid transparent;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;
color: #000;
font-size: 14px;
line-height: 18px;
&:first-child {
color: rgba(0, 0, 0, 0.5);
}
&:last-child {
border: 1px dashed rgba(27, 171, 80, 0.8);
color: rgba(0, 0, 0, 0.5);
font-size: 8px;
}
}
.active {
border: 1px solid rgba(61, 172, 120) !important;
}
.status-key {
color: #000 !important;
font-size: 14px !important;
line-height: 18px !important;
}
}
}
.submit-btn {
display: block;
width: 100%;
border: none;
padding: 6px 10px;
box-sizing: border-box;
border-radius: 52px;
background: #3faa73;
color: #fff;
text-align: center;
font-family: "PingFang SC";
font-size: 16px;
font-weight: 700;
line-height: 24px;
margin-top: 24px;
}
}
.keyboard-layer {
width: 100%;
background: #d0d5dc;
padding: 8px 4px 16px;
box-sizing: border-box;
position: absolute;
bottom: 0;
left: 0;
.keyboard-header {
width: 100%;
display: flex;
align-items: center;
justify-content: flex-end;
padding: 0 8px 8px;
box-sizing: border-box;
span {
color: #3faa73;
font-family: "PingFang SC";
font-size: 14px;
font-weight: 700;
line-height: 28px;
cursor: pointer;
}
}
.province-layer {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
span {
color: #000;
font-size: 14px;
line-height: 28px;
background: #fff;
border-radius: 6px;
padding: 6px 8px;
box-sizing: border-box;
margin: 2px;
box-shadow: 0px 2px 2px 0px rgba(0, 0, 0, 0.1);
}
}
.keyboard-item {
width: 100%;
div {
display: flex;
align-items: center;
justify-content: center;
span {
color: #000;
font-size: 14px;
line-height: 28px;
background: #fff;
border-radius: 6px;
padding: 4px 10px;
box-sizing: border-box;
margin: 4px;
box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.3);
}
}
}
.no-btn {
color: rgba(0, 0, 0, 0.4) !important;
pointer-events: none;
}
.active-hover {
background: #b3bac7 !important;
}
}
}
</style>