|
|
@@ -1,126 +1,386 @@
|
|
|
<template>
|
|
|
- <uv-list>
|
|
|
- <uv-list-item title="车牌号"
|
|
|
- :show-arrow="true"
|
|
|
- clickable :rightText="plateNo"
|
|
|
- @click="handleChoosePlate"></uv-list-item>
|
|
|
- </uv-list>
|
|
|
-
|
|
|
-<!-- <view class="logout-btn">
|
|
|
- <uv-button :custom-style="customStyle" type="error" @click="logoutUser">退出登录</uv-button>
|
|
|
- </view>-->
|
|
|
+ <view class="plate-page">
|
|
|
+ <!-- 车牌输入区域 -->
|
|
|
+ <view class="plate-input-section">
|
|
|
+ <view class="plate-input-header">
|
|
|
+ <text class="plate-label">车牌号码:</text>
|
|
|
+ <view class="default-checkbox">
|
|
|
+ <uv-checkbox v-model="isDefault" shape="circle"></uv-checkbox>
|
|
|
+ <text class="default-text">设为默认车辆</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="plate-input-hint">
|
|
|
+ <text>添加车牌后部分站点可享停车减免权益,临牌减免权益请查看站点详细减免规则</text>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 车牌输入框 -->
|
|
|
+ <view class="plate-input-container">
|
|
|
+ <!-- 省份输入 -->
|
|
|
+ <input class="plate-item plate-region"
|
|
|
+ v-model="plateChars[0]"
|
|
|
+ maxlength="1"
|
|
|
+ type="text"
|
|
|
+ inputmode="none"
|
|
|
+ readonly
|
|
|
+ @focus="onPlateFocus(0); $event.preventDefault();"
|
|
|
+ @click="toggleRegionPicker"
|
|
|
+ @touchstart.prevent="toggleRegionPicker"/>
|
|
|
+
|
|
|
+ <!-- 城市输入 -->
|
|
|
+ <input class="plate-item plate-region"
|
|
|
+ v-model="plateChars[1]"
|
|
|
+ maxlength="1"
|
|
|
+ type="text"
|
|
|
+ inputmode="text"
|
|
|
+ @input="onPlateInput"
|
|
|
+ @focus="onPlateFocus(1)"/>
|
|
|
+
|
|
|
+ <!-- 分隔符 -->
|
|
|
+ <view class="plate-divider">
|
|
|
+ <text>·</text>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 字母数字输入框 -->
|
|
|
+ <input v-for="(char, index) in plateChars.slice(2)"
|
|
|
+ :key="index + 2"
|
|
|
+ class="plate-item"
|
|
|
+ :class="{ 'new-energy': index === 5 }"
|
|
|
+ v-model="plateChars[index + 2]"
|
|
|
+ maxlength="1"
|
|
|
+ placeholder-class="placeholder"
|
|
|
+ :placeholder="index === 5 ? '新能源' : ''"
|
|
|
+ type="text"
|
|
|
+ inputmode="text"
|
|
|
+ @input="onPlateInput"
|
|
|
+ @focus="onPlateFocus(index + 2)"/>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 保存按钮 -->
|
|
|
+ <view class="save-btn-container">
|
|
|
+ <uv-button type="primary" color="#C6171E" shape="circle" :disabled="!isPlateValid" @click="savePlate">保存</uv-button>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 地区选择器 -->
|
|
|
+ <view v-if="showRegionPicker" class="region-picker-container">
|
|
|
+ <!-- 完成按钮 -->
|
|
|
+ <view class="region-picker-header">
|
|
|
+ <uv-button type="default" size="small" @click="toggleRegionPicker">完成</uv-button>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 地区网格 -->
|
|
|
+ <view class="region-grid">
|
|
|
+ <view v-for="region in regions" :key="region"
|
|
|
+ class="region-item"
|
|
|
+ @click="selectRegion(region)">
|
|
|
+ <text>{{ region }}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
</template>
|
|
|
|
|
|
-<script setup lang="ts">
|
|
|
+<script setup>
|
|
|
import {onLoad, onShow} from "@dcloudio/uni-app";
|
|
|
-import {ref} from "vue";
|
|
|
-import {body, get, upload} from "@/utils/https";
|
|
|
-import {clearToken} from "@/utils/auth"
|
|
|
-
|
|
|
-const plateNo = ref("未绑定")
|
|
|
-
|
|
|
-const customStyle = () => {
|
|
|
- return {
|
|
|
- height: '80rpx',
|
|
|
- borderRadius: '40rpx',
|
|
|
- fontSize: '30rpx',
|
|
|
- lineHeight: '80rpx',
|
|
|
- background: '#FF6D00',
|
|
|
- color: '#fff',
|
|
|
- fontWeight: '500',
|
|
|
- width: '60%',
|
|
|
+import {ref, computed} from "vue";
|
|
|
+import {body} from "@/utils/https";
|
|
|
+
|
|
|
+// 页面状态
|
|
|
+const plateNo = ref("未绑定");
|
|
|
+const plateChars = ref(['粤', 'B', '', '', '', '', '', '']);
|
|
|
+const isDefault = ref(false);
|
|
|
+const currentInputIndex = ref(2);
|
|
|
+const showRegionPicker = ref(false);
|
|
|
+
|
|
|
+// 地区列表
|
|
|
+const regions = ['京', '津', '渝', '沪', '冀', '晋', '辽', '吉', '黑', '苏', '浙', '皖', '闽', '赣', '鲁', '豫', '鄂', '湘', '粤', '琼', '川', '贵', '云', '陕', '甘', '青', '蒙', '桂', '宁', '新', '藏', '使', '领', '警', '学', '港', '澳'];
|
|
|
+
|
|
|
+// 计算属性
|
|
|
+const isPlateValid = computed(() => {
|
|
|
+ const validChars = plateChars.value.filter(char => char !== '');
|
|
|
+ return validChars.length >= 7;
|
|
|
+});
|
|
|
+
|
|
|
+// 输入事件处理
|
|
|
+const onPlateInput = () => {
|
|
|
+ // 自动大写输入内容
|
|
|
+ for (let i = 0; i < plateChars.value.length; i++) {
|
|
|
+ plateChars.value[i] = plateChars.value[i].toUpperCase();
|
|
|
}
|
|
|
-}
|
|
|
+};
|
|
|
|
|
|
+// 焦点事件处理
|
|
|
+const onPlateFocus = (index) => {
|
|
|
+ currentInputIndex.value = index;
|
|
|
+};
|
|
|
|
|
|
-const handleChoosePlate = () => {
|
|
|
- uni.chooseLicensePlate({
|
|
|
- success: (res) => {
|
|
|
- plateNo.value = res.plateNumber;
|
|
|
- save({
|
|
|
- defaultPlateNo: res.plateNumber,
|
|
|
- });
|
|
|
- },
|
|
|
- fail: (err) => {
|
|
|
- console.log(err);
|
|
|
- },
|
|
|
- });
|
|
|
-}
|
|
|
+// 地区选择器方法
|
|
|
+const toggleRegionPicker = () => {
|
|
|
+ showRegionPicker.value = !showRegionPicker.value;
|
|
|
+};
|
|
|
|
|
|
+const selectRegion = (region) => {
|
|
|
+ plateChars.value[0] = region;
|
|
|
+ toggleRegionPicker();
|
|
|
+ if (plateChars.value[1] === '') {
|
|
|
+ currentInputIndex.value = 1;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const savePlate = () => {
|
|
|
+ const newPlateNo = plateChars.value.filter(char => char !== '').join('');
|
|
|
+ if (newPlateNo.length < 7) {
|
|
|
+ uni.showToast({
|
|
|
+ title: '请输入完整车牌号',
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 保存到服务器
|
|
|
+ save({
|
|
|
+ defaultPlateNo: newPlateNo,
|
|
|
+ });
|
|
|
+};
|
|
|
|
|
|
-const save = (form: Record<string, any>) => {
|
|
|
- console.log(form)
|
|
|
+const save = (form) => {
|
|
|
+ console.log(form);
|
|
|
uni.showLoading({
|
|
|
title: "保存中",
|
|
|
});
|
|
|
body(`/user/updateUser`, form).then(() => {
|
|
|
uni.hideLoading();
|
|
|
- getApp<any>().globalData.user.defaultPlateNo = form.defaultPlateNo;
|
|
|
+ getApp().globalData.user.defaultPlateNo = form.defaultPlateNo;
|
|
|
+ plateNo.value = form.defaultPlateNo;
|
|
|
uni.showToast({
|
|
|
icon: "success",
|
|
|
title: "保存成功",
|
|
|
});
|
|
|
- }).catch((err: any) => {
|
|
|
+ setTimeout(() => {
|
|
|
+ uni.navigateBack();
|
|
|
+ }, 1500);
|
|
|
+ }).catch((err) => {
|
|
|
uni.hideLoading();
|
|
|
- })
|
|
|
-};
|
|
|
-
|
|
|
-
|
|
|
-const logoutUser = () => {
|
|
|
- uni.showModal({
|
|
|
- title: "温馨提示",
|
|
|
- content: "确定退出登录吗?",
|
|
|
- confirmColor: "#2d9e95",
|
|
|
- confirmText: "确定退出",
|
|
|
- cancelText: "手滑了",
|
|
|
- success: (res) => {
|
|
|
- if (res.confirm) {
|
|
|
- uni.showLoading({
|
|
|
- title: "退出中",
|
|
|
- });
|
|
|
-
|
|
|
- get(`/user/logout`).then(() => {
|
|
|
- uni.hideLoading();
|
|
|
- uni.showToast({
|
|
|
- icon: "success",
|
|
|
- title: "已退出",
|
|
|
- });
|
|
|
-
|
|
|
- clearToken();
|
|
|
- setTimeout(() => {
|
|
|
- uni.reLaunch({
|
|
|
- url: "/pages/index/index",
|
|
|
- });
|
|
|
- }, 1500);
|
|
|
-
|
|
|
- })
|
|
|
- }
|
|
|
- },
|
|
|
+ uni.showToast({
|
|
|
+ title: "保存失败",
|
|
|
+ icon: "none"
|
|
|
+ });
|
|
|
});
|
|
|
};
|
|
|
|
|
|
-
|
|
|
onLoad(() => {
|
|
|
- if (getApp<any>().globalData.user) {
|
|
|
- const user = getApp<any>().globalData.user;
|
|
|
+ if (getApp().globalData.user) {
|
|
|
+ const user = getApp().globalData.user;
|
|
|
plateNo.value = user.defaultPlateNo || "未绑定";
|
|
|
+ if (user.defaultPlateNo) {
|
|
|
+ // 初始化已有车牌号
|
|
|
+ const plate = user.defaultPlateNo;
|
|
|
+ plateChars.value = ['粤', 'B', '', '', '', '', '', ''];
|
|
|
+ for (let i = 0; i < plate.length && i < plateChars.value.length; i++) {
|
|
|
+ plateChars.value[i] = plate[i];
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
});
|
|
|
|
|
|
-
|
|
|
onShow(() => {
|
|
|
- if (getApp<any>().globalData.user) {
|
|
|
- const user = getApp<any>().globalData.user;
|
|
|
+ if (getApp().globalData.user) {
|
|
|
+ const user = getApp().globalData.user;
|
|
|
plateNo.value = user.defaultPlateNo || "未绑定";
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
});
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss">
|
|
|
-.logout-btn{
|
|
|
- width: calc(100vw - 60rpx);
|
|
|
- margin: 60rpx auto;
|
|
|
+.plate-page {
|
|
|
+ background-color: #f5f5f5;
|
|
|
+ min-height: 100vh;
|
|
|
+ box-sizing: border-box;
|
|
|
+ padding-bottom: 200rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.plate-input-section {
|
|
|
+ background-color: #fff;
|
|
|
+ padding: 30rpx;
|
|
|
+ box-sizing: border-box;
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.plate-input-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ margin-bottom: 10rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.plate-label {
|
|
|
+ font-size: 32rpx;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+
|
|
|
+.default-checkbox {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.default-text {
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #666;
|
|
|
+}
|
|
|
+
|
|
|
+.plate-input-hint {
|
|
|
+ margin-bottom: 30rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.plate-input-hint text {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #999;
|
|
|
+ line-height: 1.5;
|
|
|
+}
|
|
|
+
|
|
|
+.plate-input-container {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ margin-bottom: 40rpx;
|
|
|
+ gap: 4rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.plate-item {
|
|
|
+ width: 64rpx;
|
|
|
+ height: 88rpx;
|
|
|
+ border: 2rpx solid #ddd;
|
|
|
+ border-radius: 4rpx;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ font-size: 36rpx;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #333;
|
|
|
+ background-color: #fff;
|
|
|
+ transition: all 0.2s ease;
|
|
|
+ box-sizing: border-box;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+
|
|
|
+.plate-item.active {
|
|
|
+ border-color: #ff6d00;
|
|
|
+ background-color: #fff9f5;
|
|
|
+}
|
|
|
+
|
|
|
+.plate-item.new-energy {
|
|
|
+ border-color: #00c853;
|
|
|
+ background-color: #f1f8e9;
|
|
|
+}
|
|
|
+
|
|
|
+.plate-item.new-energy .placeholder {
|
|
|
+ color: #00c853;
|
|
|
+ font-size: 18rpx;
|
|
|
+ text-align: center;
|
|
|
+ line-height: 20rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.plate-region {
|
|
|
+ background-color: #fafafa;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+/* 确保占位符文本也居中 */
|
|
|
+.placeholder {
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+
|
|
|
+.plate-divider {
|
|
|
+ width: 24rpx;
|
|
|
+ height: 88rpx;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ font-size: 32rpx;
|
|
|
+ color: #666;
|
|
|
+}
|
|
|
+
|
|
|
+.save-btn-container {
|
|
|
+ margin-top: 20rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.save-btn-container :deep(.uv-button) {
|
|
|
+ height: 80rpx;
|
|
|
+ font-size: 30rpx;
|
|
|
+ border-radius: 40rpx;
|
|
|
+ background-color: #C6171E;
|
|
|
+}
|
|
|
+
|
|
|
+.save-btn-container :deep(.uv-button--disabled) {
|
|
|
+ background-color: #e0e0e0;
|
|
|
+ color: #9e9e9e;
|
|
|
+}
|
|
|
+
|
|
|
+/* 地区选择器样式 */
|
|
|
+.region-picker-container {
|
|
|
+ position: fixed;
|
|
|
+ bottom: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ background-color: #fff;
|
|
|
+ border-radius: 20rpx 20rpx 0 0;
|
|
|
+ padding: 15rpx;
|
|
|
+ box-sizing: border-box;
|
|
|
+ box-shadow: 0 -4rpx 12rpx rgba(0, 0, 0, 0.05);
|
|
|
+ z-index: 1000;
|
|
|
+ height: 38vh;
|
|
|
+ overflow-y: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.region-picker-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ margin-bottom: 5rpx;
|
|
|
+ padding: 0 5rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.region-picker-header :deep(.uv-button) {
|
|
|
+ font-size: 30rpx;
|
|
|
+ color: #ff6d00;
|
|
|
+ background-color: transparent;
|
|
|
+ border: none;
|
|
|
+ padding: 5rpx 15rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.region-grid {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 6rpx;
|
|
|
+ padding: 5rpx;
|
|
|
+ height: calc(38vh - 60rpx);
|
|
|
+ overflow-y: hidden;
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+
|
|
|
+.region-item {
|
|
|
+ width: 85rpx;
|
|
|
+ height: 65rpx;
|
|
|
+ background-color: #f8f8f8;
|
|
|
+ border-radius: 8rpx;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ font-size: 40rpx;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #333;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.2s ease;
|
|
|
+ margin: 0;
|
|
|
+ flex-shrink: 0;
|
|
|
+ flex-basis: calc(16.66% - 8rpx);
|
|
|
+}
|
|
|
+
|
|
|
+.region-item:active {
|
|
|
+ background-color: #ff6d00;
|
|
|
+ color: #fff;
|
|
|
+ transform: scale(0.95);
|
|
|
}
|
|
|
-</style>
|
|
|
+</style>
|