访客信息增加车牌号
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
// plate-input.js
|
||||
// 车牌号输入组件:先选省份简称 → 再选城市代码 → 最后键盘输入号码
|
||||
Component({
|
||||
properties: {
|
||||
value: { type: String, value: '' }
|
||||
},
|
||||
|
||||
data: {
|
||||
plateChars: [],
|
||||
numValue: '',
|
||||
inputFocus: false,
|
||||
showProvince: false,
|
||||
showCity: false,
|
||||
hasValue: false,
|
||||
provinces: [
|
||||
'京', '津', '沪', '渝', '冀', '豫', '云', '辽', '黑', '湘',
|
||||
'皖', '鲁', '新', '苏', '浙', '赣', '鄂', '桂', '甘', '晋',
|
||||
'蒙', '陕', '吉', '闽', '贵', '粤', '川', '青', '藏', '琼', '宁'
|
||||
],
|
||||
cityLetters: [
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G',
|
||||
'H', 'J', 'K', 'L', 'M', 'N', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
||||
'X', 'Y', 'Z'
|
||||
],
|
||||
numSlots: [0, 1, 2, 3, 4, 5]
|
||||
},
|
||||
|
||||
observers: {
|
||||
'value': function (val) {
|
||||
if (val && val !== this._lastEmitted) {
|
||||
this._parseValue(val)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
lifetimes: {
|
||||
attached() {
|
||||
if (this.data.value) {
|
||||
this._parseValue(this.data.value)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
_parseValue(val) {
|
||||
const chars = val.split('')
|
||||
const numValue = chars.slice(2).join('')
|
||||
this.setData({
|
||||
plateChars: chars,
|
||||
numValue: numValue,
|
||||
hasValue: chars.length > 0
|
||||
})
|
||||
},
|
||||
|
||||
_emit(chars) {
|
||||
const value = chars.filter(Boolean).join('')
|
||||
this._lastEmitted = value
|
||||
this.triggerEvent('change', { value })
|
||||
},
|
||||
|
||||
// 点击省份格
|
||||
onProvinceTap() {
|
||||
this.setData({ showProvince: true, showCity: false, inputFocus: false })
|
||||
},
|
||||
|
||||
// 选择省份
|
||||
selectProvince(e) {
|
||||
const code = e.currentTarget.dataset.value
|
||||
const chars = [code, this.data.plateChars[1]].filter(Boolean)
|
||||
this.setData({ plateChars: chars, showProvince: false, showCity: true, hasValue: true })
|
||||
this._emit(chars)
|
||||
},
|
||||
|
||||
// 点击城市格
|
||||
onCityTap() {
|
||||
if (!this.data.plateChars[0]) {
|
||||
this.setData({ showProvince: true })
|
||||
return
|
||||
}
|
||||
this.setData({ showCity: true, showProvince: false, inputFocus: false })
|
||||
},
|
||||
|
||||
// 选择城市代码
|
||||
selectCity(e) {
|
||||
const letter = e.currentTarget.dataset.value
|
||||
const numPart = this.data.plateChars.slice(2).join('')
|
||||
const chars = [this.data.plateChars[0], letter].concat(numPart.split(''))
|
||||
this.setData({
|
||||
plateChars: chars,
|
||||
showCity: false,
|
||||
numValue: numPart,
|
||||
inputFocus: true,
|
||||
hasValue: true
|
||||
})
|
||||
this._emit(chars)
|
||||
},
|
||||
|
||||
// 点击号码格 → 弹出键盘
|
||||
onNumTap() {
|
||||
if (!this.data.plateChars[0] || !this.data.plateChars[1]) {
|
||||
this.setData({ showProvince: true })
|
||||
return
|
||||
}
|
||||
this.setData({ inputFocus: true, showProvince: false, showCity: false })
|
||||
},
|
||||
|
||||
// 键盘输入号码
|
||||
onNumInput(e) {
|
||||
const raw = e.detail.value.toUpperCase().replace(/[^A-Z0-9]/g, '').slice(0, 6)
|
||||
const chars = [this.data.plateChars[0], this.data.plateChars[1]].concat(raw.split(''))
|
||||
this.setData({ plateChars: chars, numValue: raw, hasValue: true })
|
||||
this._emit(chars)
|
||||
},
|
||||
|
||||
// 关闭弹窗
|
||||
hidePicker() {
|
||||
this.setData({ showProvince: false, showCity: false })
|
||||
},
|
||||
|
||||
noop() {},
|
||||
|
||||
// 清除车牌号
|
||||
clearPlate() {
|
||||
this.setData({
|
||||
plateChars: [],
|
||||
numValue: '',
|
||||
inputFocus: false,
|
||||
hasValue: false
|
||||
})
|
||||
this._emit([])
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"component": true
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<!--plate-input.wxml-->
|
||||
<view class="plate-container">
|
||||
<!-- 车牌号格子显示 -->
|
||||
<view class="plate-body">
|
||||
<view class="plate-cell {{plateChars[0] ? 'filled' : 'empty'}}" bindtap="onProvinceTap">
|
||||
{{plateChars[0] || '省'}}
|
||||
</view>
|
||||
<view class="plate-cell {{plateChars[1] ? 'filled' : 'empty'}}" bindtap="onCityTap">
|
||||
{{plateChars[1] || '市'}}
|
||||
</view>
|
||||
<view class="plate-sep">·</view>
|
||||
<view
|
||||
wx:for="{{numSlots}}" wx:key="*this"
|
||||
class="plate-cell num {{plateChars[item + 2] ? 'filled' : 'empty'}} {{item >= 5 ? 'extra' : ''}}"
|
||||
bindtap="onNumTap"
|
||||
>
|
||||
{{plateChars[item + 2] || (item < 5 ? '' : '新')}}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 隐藏输入框(唤起键盘) -->
|
||||
<input
|
||||
class="plate-hidden-input"
|
||||
type="text"
|
||||
focus="{{inputFocus}}"
|
||||
value="{{numValue}}"
|
||||
maxlength="6"
|
||||
bindinput="onNumInput"
|
||||
adjust-position="{{true}}"
|
||||
confirm-type="done"
|
||||
/>
|
||||
|
||||
<!-- 清除按钮 -->
|
||||
<view class="plate-clear" wx:if="{{hasValue}}" bindtap="clearPlate">清除</view>
|
||||
|
||||
<!-- 省份选择弹窗 -->
|
||||
<view class="popup-mask" wx:if="{{showProvince}}" bindtap="hidePicker">
|
||||
<view class="popup-panel" catchtap="noop">
|
||||
<view class="popup-title">选择省份简称</view>
|
||||
<scroll-view scroll-y class="popup-scroll">
|
||||
<view class="popup-grid">
|
||||
<view
|
||||
class="popup-item"
|
||||
wx:for="{{provinces}}" wx:key="*this"
|
||||
data-value="{{item}}" bindtap="selectProvince"
|
||||
>{{item}}</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="popup-cancel" bindtap="hidePicker">取消</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 城市选择弹窗 -->
|
||||
<view class="popup-mask" wx:if="{{showCity}}" bindtap="hidePicker">
|
||||
<view class="popup-panel" catchtap="noop">
|
||||
<view class="popup-title">选择城市代码</view>
|
||||
<scroll-view scroll-y class="popup-scroll">
|
||||
<view class="popup-grid">
|
||||
<view
|
||||
class="popup-item"
|
||||
wx:for="{{cityLetters}}" wx:key="*this"
|
||||
data-value="{{item}}" bindtap="selectCity"
|
||||
>{{item}}</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="popup-cancel" bindtap="hidePicker">取消</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1,155 @@
|
||||
/* plate-input.wxss */
|
||||
|
||||
.plate-container {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* 车牌格子区域 */
|
||||
.plate-body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #f5f7fa;
|
||||
border: 2rpx solid #dce3ec;
|
||||
border-radius: 12rpx;
|
||||
padding: 8rpx 16rpx;
|
||||
height: 72rpx;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.plate-cell {
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 30rpx;
|
||||
border-radius: 8rpx;
|
||||
margin: 0 4rpx;
|
||||
flex-shrink: 0;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.plate-cell.empty {
|
||||
color: #b8c9db;
|
||||
background: #eaf0f7;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.plate-cell.filled {
|
||||
color: #2c3e50;
|
||||
background: #ffffff;
|
||||
border: 1rpx solid #dce3ec;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.plate-cell.num {
|
||||
width: 44rpx;
|
||||
}
|
||||
|
||||
.plate-cell.num.extra {
|
||||
border-style: dashed;
|
||||
border-color: #c8d6e5;
|
||||
}
|
||||
|
||||
.plate-sep {
|
||||
font-size: 32rpx;
|
||||
color: #b8c9db;
|
||||
margin: 0 6rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* 隐藏输入框(用于唤起键盘) */
|
||||
.plate-hidden-input {
|
||||
position: absolute;
|
||||
left: -9999rpx;
|
||||
width: 0;
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* 清除按钮 */
|
||||
.plate-clear {
|
||||
position: absolute;
|
||||
right: -60rpx;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
font-size: 24rpx;
|
||||
color: #ff4d4f;
|
||||
padding: 8rpx 12rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 弹窗遮罩 */
|
||||
.popup-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 999;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
/* 弹窗面板 */
|
||||
.popup-panel {
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
border-radius: 24rpx 24rpx 0 0;
|
||||
padding: 32rpx;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.popup-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 24rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.popup-scroll {
|
||||
max-height: 400rpx;
|
||||
padding-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.popup-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16rpx;
|
||||
padding: 0 8rpx;
|
||||
}
|
||||
|
||||
.popup-item {
|
||||
width: calc((100% - 96rpx) / 7);
|
||||
height: 72rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #f5f7fa;
|
||||
border-radius: 12rpx;
|
||||
font-size: 30rpx;
|
||||
color: #2c3e50;
|
||||
border: 1rpx solid #dce3ec;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.popup-item:active {
|
||||
background: #5b9bd5;
|
||||
color: #fff;
|
||||
border-color: #5b9bd5;
|
||||
}
|
||||
|
||||
.popup-cancel {
|
||||
text-align: center;
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
padding: 24rpx 0;
|
||||
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
Reference in New Issue
Block a user