diff --git a/components/plate-input/plate-input.js b/components/plate-input/plate-input.js new file mode 100644 index 0000000..4a8a01e --- /dev/null +++ b/components/plate-input/plate-input.js @@ -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([]) + } + } +}) diff --git a/components/plate-input/plate-input.json b/components/plate-input/plate-input.json new file mode 100644 index 0000000..467ce29 --- /dev/null +++ b/components/plate-input/plate-input.json @@ -0,0 +1,3 @@ +{ + "component": true +} diff --git a/components/plate-input/plate-input.wxml b/components/plate-input/plate-input.wxml new file mode 100644 index 0000000..966f6ef --- /dev/null +++ b/components/plate-input/plate-input.wxml @@ -0,0 +1,69 @@ + + + + + + {{plateChars[0] || '省'}} + + + {{plateChars[1] || '市'}} + + · + + {{plateChars[item + 2] || (item < 5 ? '' : '新')}} + + + + + + + + 清除 + + + + + 选择省份简称 + + + {{item}} + + + 取消 + + + + + + + 选择城市代码 + + + {{item}} + + + 取消 + + + diff --git a/components/plate-input/plate-input.wxss b/components/plate-input/plate-input.wxss new file mode 100644 index 0000000..49a92a1 --- /dev/null +++ b/components/plate-input/plate-input.wxss @@ -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; +} diff --git a/pages/appointment/appointment.js b/pages/appointment/appointment.js index 207180c..ba00d04 100644 --- a/pages/appointment/appointment.js +++ b/pages/appointment/appointment.js @@ -80,7 +80,7 @@ Page({ onHostNameInput(e) { this.setData({ 'form.hostName': e.detail.value }) }, - onPlateNumberInput(e) { + onPlateNumberChange(e) { this.setData({ 'form.plateNumber': e.detail.value }) }, diff --git a/pages/appointment/appointment.json b/pages/appointment/appointment.json index c69f2e7..e78918d 100644 --- a/pages/appointment/appointment.json +++ b/pages/appointment/appointment.json @@ -1,4 +1,6 @@ { - "usingComponents": {}, + "usingComponents": { + "plate-input": "/components/plate-input/plate-input" + }, "navigationBarTitleText": "访客预约" } diff --git a/pages/appointment/appointment.wxml b/pages/appointment/appointment.wxml index eb7203a..b9dfd8e 100644 --- a/pages/appointment/appointment.wxml +++ b/pages/appointment/appointment.wxml @@ -4,32 +4,32 @@ 预约人信息 - 姓名 + 姓名* - 手机号 + 手机号* - 公司 + 公司* - 来访事由 + 来访事由* 车牌号 - + - + - 预约时间 + 来访时间 - 来访日期 + 来访日期* {{form.date || '请选择日期'}} @@ -38,7 +38,7 @@ - 来访时间 + 来访时段* {{form.time || '请选择时间'}} @@ -52,7 +52,7 @@ 被访人信息 - 拜访区域 + 拜访区域* {{areaIndex >= 0 ? areas[areaIndex] : '请选择拜访区域'}} @@ -61,7 +61,7 @@ - 被访人 + 被访人* {{personIndex >= 0 ? personNames[personIndex] : '请选择被访人'}} diff --git a/pages/appointment/appointment.wxss b/pages/appointment/appointment.wxss index 89ac4b4..67c8387 100644 --- a/pages/appointment/appointment.wxss +++ b/pages/appointment/appointment.wxss @@ -44,6 +44,11 @@ page { flex-shrink: 0; } +.required { + color: #ff4d4f; + margin-left: 4rpx; +} + .form-input { flex: 1; font-size: 28rpx; diff --git a/pages/index/index.wxml b/pages/index/index.wxml index f6852ff..73309b2 100644 --- a/pages/index/index.wxml +++ b/pages/index/index.wxml @@ -26,7 +26,7 @@ 访客预约 - 选择预约时间和预约人,提交预约信息 + 选择来访时间和预约人,提交预约信息 diff --git a/pages/records/records.wxml b/pages/records/records.wxml index 780e029..1679e12 100644 --- a/pages/records/records.wxml +++ b/pages/records/records.wxml @@ -45,7 +45,7 @@ {{item.reason}} - 预约时间 + 来访时间 {{item.date}} {{item.time}} diff --git a/pages/scan/result/index.wxml b/pages/scan/result/index.wxml index 1d4c36a..f75d0d4 100644 --- a/pages/scan/result/index.wxml +++ b/pages/scan/result/index.wxml @@ -36,6 +36,10 @@ 来访事由 {{record.reason}} + + 车牌号 + {{record.plateNumber}} +