📌个人主页:个人主页
🧀 推荐专栏:小程序开发成神之路 --【这是一个为想要入门和进阶小程序开发专门开启的精品专栏
!从个人到商业的全套开发教程
,实打实的干货分享,确定不来看看? 😻😻】
📝作者简介:从web开发,再到大数据算法,踩过了无数的坑,用心总结经验教训,助你在技术生涯一臂之力!若想获取更多精彩内容,敬请订阅专栏或者关注😁😂🤣😃😆😉😊😋😍😘🥰
⭐️您的小小关注是我持续输出的动力!
⭐️
干货内容推荐
🥇入门和进阶小程序开发,不可错误的精彩内容🥇 :
文章目录
一、需求背景
在小程序流量的刺激下,广大商超也逐步建立起了自己的私域流量。小程序中除了入驻广大商家的商业服务外,围绕商业服务而展开的基础服务也必不可少。
其中停车服务便是其中的一个大头,一套智能的停车场缴费系统同样是必不可少。在设计该系统时,我们要兼容各类汽车拍照的型号,比如说燃油车的拍照和电动车和混动车的拍照规则不同。将牌照识别系统和在车辆进场后的车位编号相绑定这是后续智能化的基础。
二、效果预览
2.1 不带动画直接输入型
2.2 输入框带动画型
三、完整源码
3.【样式2.1】源码
(1)界面元素
<template>
<div>
<div class="p-i" :style="bgheight">
<div class="column">
<img class="p-i__img" :src="LOGO" mode="aspectFit">
<keyboard
@keyboard="keyboardChange"
:show.sync="show"
active-border="#0deafe"
base-border="38f8f8f"
></keyboard>
</div>
<div class="p-i__con column text-center">
<div class="p-i__info text-center">提示:请确保您填写车牌号的正确性,以免后续</div>
<div class="p-i__info text-center">误交费给您造成不必要的麻烦。</div>
<div class="p-i__ben text-center" @click="TapScale">停车缴费标准</div>
<div class="p-i__over">
<div class="text-center">
<div class="p-i__over-name">剩余停车位</div>
<div class="p-i__over-value">{{space}}</div>
</div>
</div>
</div>
<div class="p-i__footer flex-center" @click="call()">
<div class="p-i__footer-text">客服电话:{{tel}}</div>
</div>
</div>
</div>
</template>
(2)脚本逻辑
import keyboard from "@/components/mpvue-keyboard/mpvue-keyboard";
import { obj2style } from "@/components/mpvue-keyboard/index";
import LOGO from "@/components/mpvue-keyboard/parking-logo.png";
export default {
data() {
return {
tel: 888888,
space: 999,
show: true,
LOGO: LOGO
};
},
components: { keyboard },
computed: {
bgheight() {
let style = {};
const { screenHeight } = uni.getSystemInfoSync();
style["height"] = screenHeight - uni.getStorageSync("CustomBar") + "px";
return obj2style(style);
}
},
methods: {
keyboardChange(e) {
uni.showToast({
icon: "none",
title: e,
mask: true,
duration: 2000
});
},
call() {
uni.makePhoneCall({
phoneNumber: this.tel
});
},
TapScale() {}
},
mounted() {}
};
(3)界面样式
$gfrom: #04010e; // 渐变色起始颜色
$gmiddle: #091149; // 渐变色过度颜色
$gend: #0c2c5c; // 渐变色结束颜色
.p-i {
background: -webkit-gradient(
linear,
left top,
left bottom,
from($gfrom),
color-stop(0.5, $gmiddle),
to($gend)
);
width: 100%;
overflow: hidden;
&__img {
position: relative;
margin: 0 auto;
margin-top: 130rpx;
width: 323rpx;
height: 58rpx;
}
&__con {
width: 100%;
padding: 10rpx 30rpx;
margin-top: 130rpx;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
}
&__info {
width: 100%;
height: 48rpx;
font-size: 30rpx;
font-weight: normal;
font-stretch: normal;
letter-spacing: 0rpx;
color: #a0c0ff;
}
&__ben {
width: 100%;
margin-top: 40rpx;
height: 38rpx;
text-decoration: underline;
font-size: 36rpx;
font-weight: normal;
font-stretch: normal;
line-height: 48rpx;
letter-spacing: 0px;
color: #ffffff;
}
&__over {
position: relative;
width: 100%;
top: 60rpx;
}
&__over-img {
position: absolute;
left: 50%;
transform: translate(-50%, 0);
-webkit-transform: translate(-50%, 0);
width: 380rpx;
height: 380rpx;
}
&__over-name {
position: relative;
top: 140rpx;
height: 29rpx;
font-size: 30rpx;
font-weight: normal;
font-stretch: normal;
line-height: 48rpx;
letter-spacing: 0px;
color: #a99dc6;
z-index: 10;
}
&__over-value {
position: relative;
top: 170rpx;
height: 45rpx;
font-size: 60rpx;
font-weight: normal;
font-stretch: normal;
line-height: 48rpx;
letter-spacing: 0px;
color: #ffffff;
}
&__footer {
position: absolute;
width: 100%;
bottom: 60rpx;
left: 50%;
transform: translate(-50%, 0);
}
&__footer-img {
width: 26rpx;
height: 27rpx;
}
&__footer-text {
margin-left: 10rpx;
font-size: 28rpx;
font-weight: normal;
font-stretch: normal;
letter-spacing: 0px;
color: #ffffff;
}
}
.text-center {
text-align: center;
}
.column {
display: flex;
display: -webkit-flex;
flex-direction: column;
-webkit-flex-direction: column;
}
3.【样式2.2】源码
(1)界面元素
<view class="so-mask">
<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>
<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>
(2)脚本逻辑
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) {
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);
}
};
(3)界面样式
.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: 10upx
}
.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)
}
}