feat(收款设置): 新增收款设置页面及相关功能
添加收款设置页面,包括银行账号和身份信息设置功能 扩展权限检查功能,支持返回未通过校验的字段信息 更新用户个人中心,添加收款设置入口
Showing
9 changed files
with
631 additions
and
31 deletions
| ... | @@ -44,6 +44,7 @@ declare module 'vue' { | ... | @@ -44,6 +44,7 @@ declare module 'vue' { |
| 44 | Picker: typeof import('./src/components/time-picker-data/picker.vue')['default'] | 44 | Picker: typeof import('./src/components/time-picker-data/picker.vue')['default'] |
| 45 | PosterBuilder: typeof import('./src/components/PosterBuilder/index.vue')['default'] | 45 | PosterBuilder: typeof import('./src/components/PosterBuilder/index.vue')['default'] |
| 46 | PrivacyAgreementModal: typeof import('./src/components/PrivacyAgreementModal.vue')['default'] | 46 | PrivacyAgreementModal: typeof import('./src/components/PrivacyAgreementModal.vue')['default'] |
| 47 | + QrcodePay: typeof import('./src/components/qrcodePay.vue')['default'] | ||
| 47 | RouterLink: typeof import('vue-router')['RouterLink'] | 48 | RouterLink: typeof import('vue-router')['RouterLink'] |
| 48 | RouterView: typeof import('vue-router')['RouterView'] | 49 | RouterView: typeof import('vue-router')['RouterView'] |
| 49 | SearchPopup: typeof import('./src/components/SearchPopup.vue')['default'] | 50 | SearchPopup: typeof import('./src/components/SearchPopup.vue')['default'] | ... | ... |
| 1 | /* | 1 | /* |
| 2 | * @Date: 2025-06-28 10:33:00 | 2 | * @Date: 2025-06-28 10:33:00 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-07-15 15:42:47 | 4 | + * @LastEditTime: 2025-08-05 15:12:01 |
| 5 | * @FilePath: /jgdl/src/app.config.js | 5 | * @FilePath: /jgdl/src/app.config.js |
| 6 | * @Description: 配置文件 | 6 | * @Description: 配置文件 |
| 7 | */ | 7 | */ |
| ... | @@ -29,6 +29,7 @@ export default { | ... | @@ -29,6 +29,7 @@ export default { |
| 29 | 'pages/helpCenter/index', | 29 | 'pages/helpCenter/index', |
| 30 | 'pages/search/index', | 30 | 'pages/search/index', |
| 31 | 'pages/recommendCarList/index', | 31 | 'pages/recommendCarList/index', |
| 32 | + 'pages/collectionSettings/index', | ||
| 32 | ], | 33 | ], |
| 33 | subpackages: [ // 配置在tabBar中的页面不能分包写到subpackages中去 | 34 | subpackages: [ // 配置在tabBar中的页面不能分包写到subpackages中去 |
| 34 | { | 35 | { | ... | ... |
src/pages/collectionSettings/index.config.js
0 → 100755
| 1 | +/* | ||
| 2 | + * @Date: 2025-08-05 15:10:51 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2025-08-05 15:12:21 | ||
| 5 | + * @FilePath: /jgdl/src/pages/collectionSettings/index.config.js | ||
| 6 | + * @Description: 收款设置 | ||
| 7 | + */ | ||
| 8 | +export default { | ||
| 9 | + navigationBarTitleText: '收款设置', | ||
| 10 | + usingComponents: { | ||
| 11 | + }, | ||
| 12 | +} |
src/pages/collectionSettings/index.less
0 → 100644
| 1 | +.collection-settings { | ||
| 2 | + min-height: 100vh; | ||
| 3 | + background-color: #f5f5f5; | ||
| 4 | + padding: 0; | ||
| 5 | + | ||
| 6 | + .page-title { | ||
| 7 | + background-color: #fff; | ||
| 8 | + padding: 32rpx; | ||
| 9 | + font-size: 36rpx; | ||
| 10 | + font-weight: 600; | ||
| 11 | + color: #333; | ||
| 12 | + text-align: center; | ||
| 13 | + border-bottom: 1rpx solid #eee; | ||
| 14 | + } | ||
| 15 | + | ||
| 16 | + .settings-list { | ||
| 17 | + margin-top: 24rpx; | ||
| 18 | + background-color: #fff; | ||
| 19 | + // border-radius: 16rpx; | ||
| 20 | + // margin: 24rpx 32rpx; | ||
| 21 | + overflow: hidden; | ||
| 22 | + | ||
| 23 | + .setting-item { | ||
| 24 | + display: flex; | ||
| 25 | + align-items: center; | ||
| 26 | + justify-content: space-between; | ||
| 27 | + padding: 32rpx 24rpx; | ||
| 28 | + border-bottom: 1rpx solid #f0f0f0; | ||
| 29 | + transition: background-color 0.2s; | ||
| 30 | + | ||
| 31 | + &:last-child { | ||
| 32 | + border-bottom: none; | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + &:active { | ||
| 36 | + background-color: #f8f8f8; | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + .setting-left { | ||
| 40 | + .setting-label { | ||
| 41 | + font-size: 32rpx; | ||
| 42 | + color: #333; | ||
| 43 | + // font-weight: 500; | ||
| 44 | + } | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + .setting-right { | ||
| 48 | + display: flex; | ||
| 49 | + align-items: center; | ||
| 50 | + gap: 16rpx; | ||
| 51 | + | ||
| 52 | + .setting-status { | ||
| 53 | + font-size: 28rpx; | ||
| 54 | + color: #999; | ||
| 55 | + | ||
| 56 | + &.status-set { | ||
| 57 | + color: #52c41a; | ||
| 58 | + } | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + .arrow { | ||
| 62 | + font-size: 24rpx; | ||
| 63 | + color: #ccc; | ||
| 64 | + } | ||
| 65 | + } | ||
| 66 | + } | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + // 弹窗样式 | ||
| 70 | + .modal-content { | ||
| 71 | + height: 100%; | ||
| 72 | + display: flex; | ||
| 73 | + flex-direction: column; | ||
| 74 | + background-color: #fff; | ||
| 75 | + | ||
| 76 | + .modal-header { | ||
| 77 | + padding: 32rpx; | ||
| 78 | + border-bottom: 1rpx solid #eee; | ||
| 79 | + background-color: #fff; | ||
| 80 | + position: sticky; | ||
| 81 | + top: 0; | ||
| 82 | + z-index: 10; | ||
| 83 | + | ||
| 84 | + .modal-title { | ||
| 85 | + font-size: 36rpx; | ||
| 86 | + font-weight: 600; | ||
| 87 | + color: #333; | ||
| 88 | + text-align: center; | ||
| 89 | + } | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + .form-content { | ||
| 93 | + flex: 1; | ||
| 94 | + padding: 32rpx; | ||
| 95 | + overflow-y: auto; | ||
| 96 | + | ||
| 97 | + .form-item { | ||
| 98 | + margin-bottom: 48rpx; | ||
| 99 | + | ||
| 100 | + .form-label { | ||
| 101 | + display: block; | ||
| 102 | + font-size: 28rpx; | ||
| 103 | + color: #333; | ||
| 104 | + margin-bottom: 16rpx; | ||
| 105 | + font-weight: 500; | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + .form-input { | ||
| 109 | + width: 100%; | ||
| 110 | + padding: 24rpx 0; | ||
| 111 | + font-size: 32rpx; | ||
| 112 | + border: none; | ||
| 113 | + // border-bottom: 2rpx solid #eee; | ||
| 114 | + background: transparent; | ||
| 115 | + transition: border-color 0.3s; | ||
| 116 | + | ||
| 117 | + &:focus { | ||
| 118 | + border-bottom-color: #1890ff; | ||
| 119 | + } | ||
| 120 | + } | ||
| 121 | + | ||
| 122 | + .error-text { | ||
| 123 | + display: block; | ||
| 124 | + font-size: 24rpx; | ||
| 125 | + color: #ff4d4f; | ||
| 126 | + margin-top: 8rpx; | ||
| 127 | + } | ||
| 128 | + } | ||
| 129 | + } | ||
| 130 | + | ||
| 131 | + .modal-footer { | ||
| 132 | + padding: 32rpx; | ||
| 133 | + border-top: 1rpx solid #eee; | ||
| 134 | + background-color: #fff; | ||
| 135 | + display: flex; | ||
| 136 | + gap: 24rpx; | ||
| 137 | + | ||
| 138 | + .footer-btn { | ||
| 139 | + flex: 1; | ||
| 140 | + height: 88rpx; | ||
| 141 | + font-size: 32rpx; | ||
| 142 | + border-radius: 44rpx; | ||
| 143 | + } | ||
| 144 | + | ||
| 145 | + .footer-btn-cancel { | ||
| 146 | + // background-color: #f5f5f5; | ||
| 147 | + color: #666; | ||
| 148 | + // border: 1rpx solid #d9d9d9; | ||
| 149 | + } | ||
| 150 | + | ||
| 151 | + .footer-btn-save { | ||
| 152 | + background-color: #ffa500; | ||
| 153 | + color: #fff; | ||
| 154 | + border: 1rpx solid #ffa500; | ||
| 155 | + } | ||
| 156 | + } | ||
| 157 | + } | ||
| 158 | +} | ||
| 159 | + | ||
| 160 | +// NutUI 组件样式覆盖 | ||
| 161 | +:deep(.nut-popup) { | ||
| 162 | + .nut-icon { | ||
| 163 | + font-size: 32rpx; | ||
| 164 | + color: #666; | ||
| 165 | + } | ||
| 166 | +} | ||
| 167 | + | ||
| 168 | +:deep(.nut-input) { | ||
| 169 | + .nut-input-value { | ||
| 170 | + font-size: 32rpx; | ||
| 171 | + color: #333; | ||
| 172 | + } | ||
| 173 | + | ||
| 174 | + .nut-input-placeholder { | ||
| 175 | + color: #ccc; | ||
| 176 | + } | ||
| 177 | +} | ||
| 178 | + | ||
| 179 | +:deep(.nut-button) { | ||
| 180 | + &.nut-button--disabled { | ||
| 181 | + background-color: #f5f5f5 !important; | ||
| 182 | + color: #ccc !important; | ||
| 183 | + border-color: #f5f5f5 !important; | ||
| 184 | + } | ||
| 185 | +} |
src/pages/collectionSettings/index.vue
0 → 100644
| 1 | +<!-- | ||
| 2 | + * @Date: 2022-09-19 14:11:06 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2025-08-05 15:29:14 | ||
| 5 | + * @FilePath: /jgdl/src/pages/collectionSettings/index.vue | ||
| 6 | + * @Description: 收款设置 | ||
| 7 | +--> | ||
| 8 | +<template> | ||
| 9 | + <view class="collection-settings"> | ||
| 10 | + | ||
| 11 | + <!-- 设置列表 --> | ||
| 12 | + <view class="settings-list"> | ||
| 13 | + <!-- 收款账号 --> | ||
| 14 | + <view class="setting-item" @click="openAccountModal"> | ||
| 15 | + <view class="setting-left"> | ||
| 16 | + <text class="setting-label">收款账号</text> | ||
| 17 | + </view> | ||
| 18 | + <view class="setting-right"> | ||
| 19 | + <text class="setting-status" :class="{ 'status-set': accountInfo.bankName }"> | ||
| 20 | + {{ accountInfo.bankName ? '已设置' : '未设置' }} | ||
| 21 | + </text> | ||
| 22 | + <text class="arrow">></text> | ||
| 23 | + </view> | ||
| 24 | + </view> | ||
| 25 | + | ||
| 26 | + <!-- 身份信息 --> | ||
| 27 | + <view class="setting-item" @click="openIdentityModal"> | ||
| 28 | + <view class="setting-left"> | ||
| 29 | + <text class="setting-label">身份信息</text> | ||
| 30 | + </view> | ||
| 31 | + <view class="setting-right"> | ||
| 32 | + <text class="setting-status" :class="{ 'status-set': identityInfo.userName }"> | ||
| 33 | + {{ identityInfo.userName ? '已设置' : '未设置' }} | ||
| 34 | + </text> | ||
| 35 | + <text class="arrow">></text> | ||
| 36 | + </view> | ||
| 37 | + </view> | ||
| 38 | + </view> | ||
| 39 | + | ||
| 40 | + <!-- 收款账号弹窗 --> | ||
| 41 | + <nut-popup | ||
| 42 | + v-model:visible="showAccountModal" | ||
| 43 | + position="bottom" | ||
| 44 | + :style="{ width: '100%', height: '80%' }" | ||
| 45 | + closeable | ||
| 46 | + close-icon-position="top-right" | ||
| 47 | + @close="closeAccountModal" | ||
| 48 | + > | ||
| 49 | + <view class="modal-content"> | ||
| 50 | + <view class="modal-header"> | ||
| 51 | + <text class="modal-title">收款账号设置</text> | ||
| 52 | + </view> | ||
| 53 | + | ||
| 54 | + <view class="form-content"> | ||
| 55 | + <view class="form-item"> | ||
| 56 | + <text class="form-label">银行名称</text> | ||
| 57 | + <nut-input | ||
| 58 | + v-model="tempAccountInfo.bankName" | ||
| 59 | + placeholder="请输入银行名称" | ||
| 60 | + class="form-input" | ||
| 61 | + /> | ||
| 62 | + </view> | ||
| 63 | + | ||
| 64 | + <view class="form-item"> | ||
| 65 | + <text class="form-label">银行账号</text> | ||
| 66 | + <nut-input | ||
| 67 | + v-model="tempAccountInfo.bankAccount" | ||
| 68 | + placeholder="请输入银行账号" | ||
| 69 | + class="form-input" | ||
| 70 | + type="number" | ||
| 71 | + /> | ||
| 72 | + </view> | ||
| 73 | + </view> | ||
| 74 | + | ||
| 75 | + <view class="modal-footer"> | ||
| 76 | + <nut-button | ||
| 77 | + type="default" | ||
| 78 | + @click="closeAccountModal" | ||
| 79 | + class="footer-btn footer-btn-cancel" | ||
| 80 | + > | ||
| 81 | + 关闭 | ||
| 82 | + </nut-button> | ||
| 83 | + <nut-button | ||
| 84 | + type="primary" | ||
| 85 | + @click="saveAccountInfo" | ||
| 86 | + color="#ffa500" | ||
| 87 | + :disabled="!tempAccountInfo.bankName || !tempAccountInfo.bankAccount" | ||
| 88 | + class="footer-btn footer-btn-save" | ||
| 89 | + > | ||
| 90 | + 保存 | ||
| 91 | + </nut-button> | ||
| 92 | + </view> | ||
| 93 | + </view> | ||
| 94 | + </nut-popup> | ||
| 95 | + | ||
| 96 | + <!-- 身份信息弹窗 --> | ||
| 97 | + <nut-popup | ||
| 98 | + v-model:visible="showIdentityModal" | ||
| 99 | + position="bottom" | ||
| 100 | + :style="{ width: '100%', height: '80%' }" | ||
| 101 | + closeable | ||
| 102 | + close-icon-position="top-right" | ||
| 103 | + @close="closeIdentityModal" | ||
| 104 | + > | ||
| 105 | + <view class="modal-content"> | ||
| 106 | + <view class="modal-header"> | ||
| 107 | + <text class="modal-title">身份信息设置</text> | ||
| 108 | + </view> | ||
| 109 | + | ||
| 110 | + <view class="form-content"> | ||
| 111 | + <view class="form-item"> | ||
| 112 | + <text class="form-label">用户名称</text> | ||
| 113 | + <nut-input | ||
| 114 | + v-model="tempIdentityInfo.userName" | ||
| 115 | + placeholder="请输入真实姓名" | ||
| 116 | + class="form-input" | ||
| 117 | + /> | ||
| 118 | + </view> | ||
| 119 | + | ||
| 120 | + <view class="form-item"> | ||
| 121 | + <text class="form-label">身份证号码</text> | ||
| 122 | + <nut-input | ||
| 123 | + v-model="tempIdentityInfo.idCard" | ||
| 124 | + placeholder="请输入身份证号码" | ||
| 125 | + class="form-input" | ||
| 126 | + maxlength="18" | ||
| 127 | + @blur="handleIdCardBlur" | ||
| 128 | + /> | ||
| 129 | + <text v-if="idCardError" class="error-text">{{ idCardError }}</text> | ||
| 130 | + </view> | ||
| 131 | + </view> | ||
| 132 | + | ||
| 133 | + <view class="modal-footer"> | ||
| 134 | + <nut-button | ||
| 135 | + type="default" | ||
| 136 | + @click="closeIdentityModal" | ||
| 137 | + class="footer-btn footer-btn-cancel" | ||
| 138 | + > | ||
| 139 | + 关闭 | ||
| 140 | + </nut-button> | ||
| 141 | + <nut-button | ||
| 142 | + type="primary" | ||
| 143 | + @click="saveIdentityInfo" | ||
| 144 | + color="#ffa500" | ||
| 145 | + :disabled="!tempIdentityInfo.userName || !tempIdentityInfo.idCard || !!idCardError" | ||
| 146 | + class="footer-btn footer-btn-save" | ||
| 147 | + > | ||
| 148 | + 保存 | ||
| 149 | + </nut-button> | ||
| 150 | + </view> | ||
| 151 | + </view> | ||
| 152 | + </nut-popup> | ||
| 153 | + </view> | ||
| 154 | +</template> | ||
| 155 | + | ||
| 156 | +<script setup> | ||
| 157 | +import { ref } from "vue"; | ||
| 158 | +import Taro from "@tarojs/taro"; | ||
| 159 | +import "./index.less"; | ||
| 160 | + | ||
| 161 | +/** | ||
| 162 | + * 收款账号信息 | ||
| 163 | + */ | ||
| 164 | +const accountInfo = ref({ | ||
| 165 | + bankName: '', | ||
| 166 | + bankAccount: '' | ||
| 167 | +}); | ||
| 168 | + | ||
| 169 | +/** | ||
| 170 | + * 身份信息 | ||
| 171 | + */ | ||
| 172 | +const identityInfo = ref({ | ||
| 173 | + userName: '', | ||
| 174 | + idCard: '' | ||
| 175 | +}); | ||
| 176 | + | ||
| 177 | +/** | ||
| 178 | + * 临时收款账号信息(用于弹窗编辑) | ||
| 179 | + */ | ||
| 180 | +const tempAccountInfo = ref({ | ||
| 181 | + bankName: '', | ||
| 182 | + bankAccount: '' | ||
| 183 | +}); | ||
| 184 | + | ||
| 185 | +/** | ||
| 186 | + * 临时身份信息(用于弹窗编辑) | ||
| 187 | + */ | ||
| 188 | +const tempIdentityInfo = ref({ | ||
| 189 | + userName: '', | ||
| 190 | + idCard: '' | ||
| 191 | +}); | ||
| 192 | + | ||
| 193 | +/** | ||
| 194 | + * 弹窗显示状态 | ||
| 195 | + */ | ||
| 196 | +const showAccountModal = ref(false); | ||
| 197 | +const showIdentityModal = ref(false); | ||
| 198 | + | ||
| 199 | +/** | ||
| 200 | + * 身份证号码错误信息 | ||
| 201 | + */ | ||
| 202 | +const idCardError = ref(''); | ||
| 203 | + | ||
| 204 | +/** | ||
| 205 | + * 身份证号码校验 | ||
| 206 | + * @param {string} idCard - 身份证号码 | ||
| 207 | + * @returns {boolean} - 是否有效 | ||
| 208 | + */ | ||
| 209 | +const validateIdCard = (idCard) => { | ||
| 210 | + if (!idCard) { | ||
| 211 | + return { valid: false, error: '请输入身份证号码' }; | ||
| 212 | + } | ||
| 213 | + | ||
| 214 | + // 身份证号码正则表达式 | ||
| 215 | + const idCardRegex = /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/; | ||
| 216 | + | ||
| 217 | + if (!idCardRegex.test(idCard)) { | ||
| 218 | + return { valid: false, error: '身份证号码格式不正确' }; | ||
| 219 | + } | ||
| 220 | + | ||
| 221 | + // 校验码验证 | ||
| 222 | + const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]; | ||
| 223 | + const checkCodes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2']; | ||
| 224 | + | ||
| 225 | + let sum = 0; | ||
| 226 | + for (let i = 0; i < 17; i++) { | ||
| 227 | + sum += parseInt(idCard[i]) * weights[i]; | ||
| 228 | + } | ||
| 229 | + | ||
| 230 | + const checkCode = checkCodes[sum % 11]; | ||
| 231 | + const lastChar = idCard[17].toUpperCase(); | ||
| 232 | + | ||
| 233 | + if (checkCode !== lastChar) { | ||
| 234 | + return { valid: false, error: '身份证号码校验失败' }; | ||
| 235 | + } | ||
| 236 | + | ||
| 237 | + return { valid: true, error: '' }; | ||
| 238 | +}; | ||
| 239 | + | ||
| 240 | +/** | ||
| 241 | + * 处理身份证号码失焦事件 | ||
| 242 | + */ | ||
| 243 | +const handleIdCardBlur = () => { | ||
| 244 | + const idCard = tempIdentityInfo.value.idCard; | ||
| 245 | + if (idCard) { | ||
| 246 | + const result = validateIdCard(idCard); | ||
| 247 | + idCardError.value = result.error; | ||
| 248 | + } else { | ||
| 249 | + idCardError.value = ''; | ||
| 250 | + } | ||
| 251 | +}; | ||
| 252 | + | ||
| 253 | +/** | ||
| 254 | + * 打开收款账号弹窗 | ||
| 255 | + */ | ||
| 256 | +const openAccountModal = () => { | ||
| 257 | + tempAccountInfo.value = { ...accountInfo.value }; | ||
| 258 | + showAccountModal.value = true; | ||
| 259 | +}; | ||
| 260 | + | ||
| 261 | +/** | ||
| 262 | + * 关闭收款账号弹窗 | ||
| 263 | + */ | ||
| 264 | +const closeAccountModal = () => { | ||
| 265 | + showAccountModal.value = false; | ||
| 266 | + tempAccountInfo.value = { bankName: '', bankAccount: '' }; | ||
| 267 | +}; | ||
| 268 | + | ||
| 269 | +/** | ||
| 270 | + * 保存收款账号信息 | ||
| 271 | + */ | ||
| 272 | +const saveAccountInfo = () => { | ||
| 273 | + if (!tempAccountInfo.value.bankName || !tempAccountInfo.value.bankAccount) { | ||
| 274 | + Taro.showToast({ | ||
| 275 | + title: '请填写完整信息', | ||
| 276 | + icon: 'none' | ||
| 277 | + }); | ||
| 278 | + return; | ||
| 279 | + } | ||
| 280 | + | ||
| 281 | + accountInfo.value = { ...tempAccountInfo.value }; | ||
| 282 | + closeAccountModal(); | ||
| 283 | + | ||
| 284 | + Taro.showToast({ | ||
| 285 | + title: '保存成功', | ||
| 286 | + icon: 'success' | ||
| 287 | + }); | ||
| 288 | +}; | ||
| 289 | + | ||
| 290 | +/** | ||
| 291 | + * 打开身份信息弹窗 | ||
| 292 | + */ | ||
| 293 | +const openIdentityModal = () => { | ||
| 294 | + tempIdentityInfo.value = { ...identityInfo.value }; | ||
| 295 | + idCardError.value = ''; | ||
| 296 | + showIdentityModal.value = true; | ||
| 297 | +}; | ||
| 298 | + | ||
| 299 | +/** | ||
| 300 | + * 关闭身份信息弹窗 | ||
| 301 | + */ | ||
| 302 | +const closeIdentityModal = () => { | ||
| 303 | + showIdentityModal.value = false; | ||
| 304 | + tempIdentityInfo.value = { userName: '', idCard: '' }; | ||
| 305 | + idCardError.value = ''; | ||
| 306 | +}; | ||
| 307 | + | ||
| 308 | +/** | ||
| 309 | + * 保存身份信息 | ||
| 310 | + */ | ||
| 311 | +const saveIdentityInfo = () => { | ||
| 312 | + if (!tempIdentityInfo.value.userName || !tempIdentityInfo.value.idCard) { | ||
| 313 | + Taro.showToast({ | ||
| 314 | + title: '请填写完整信息', | ||
| 315 | + icon: 'none' | ||
| 316 | + }); | ||
| 317 | + return; | ||
| 318 | + } | ||
| 319 | + | ||
| 320 | + const validation = validateIdCard(tempIdentityInfo.value.idCard); | ||
| 321 | + if (!validation.valid) { | ||
| 322 | + Taro.showToast({ | ||
| 323 | + title: validation.error, | ||
| 324 | + icon: 'none' | ||
| 325 | + }); | ||
| 326 | + return; | ||
| 327 | + } | ||
| 328 | + | ||
| 329 | + identityInfo.value = { ...tempIdentityInfo.value }; | ||
| 330 | + closeIdentityModal(); | ||
| 331 | + | ||
| 332 | + Taro.showToast({ | ||
| 333 | + title: '保存成功', | ||
| 334 | + icon: 'success' | ||
| 335 | + }); | ||
| 336 | +}; | ||
| 337 | +</script> | ||
| 338 | + | ||
| 339 | +<script> | ||
| 340 | +export default { | ||
| 341 | + name: "collectionSettings", | ||
| 342 | +}; | ||
| 343 | +</script> |
| ... | @@ -357,13 +357,15 @@ const copyWechat = () => { | ... | @@ -357,13 +357,15 @@ const copyWechat = () => { |
| 357 | */ | 357 | */ |
| 358 | const conversation_id = ref('') | 358 | const conversation_id = ref('') |
| 359 | const handleContactSeller = async () => { | 359 | const handleContactSeller = async () => { |
| 360 | - const hasPermission = await checkPermission(PERMISSION_TYPES.CONTACT_SELLER, { | 360 | + const permissionResult = await checkPermission(PERMISSION_TYPES.CONTACT_SELLER, { |
| 361 | showToast: false, | 361 | showToast: false, |
| 362 | autoRedirect: false | 362 | autoRedirect: false |
| 363 | }) | 363 | }) |
| 364 | 364 | ||
| 365 | // 如果没有权限,显示确认弹窗 | 365 | // 如果没有权限,显示确认弹窗 |
| 366 | - if (!hasPermission) { | 366 | + if (!permissionResult.hasPermission) { |
| 367 | + // 可以通过 permissionResult.missingFields 获取未通过校验的字段 | ||
| 368 | + // 可以通过 permissionResult.validFields 获取已通过校验的字段 | ||
| 367 | Taro.showModal({ | 369 | Taro.showModal({ |
| 368 | title: '提示', | 370 | title: '提示', |
| 369 | content: '联系卖家需要先完善个人信息', | 371 | content: '联系卖家需要先完善个人信息', |
| ... | @@ -441,13 +443,13 @@ const sendMessageToSeller = async () => { | ... | @@ -441,13 +443,13 @@ const sendMessageToSeller = async () => { |
| 441 | * 购买商品 | 443 | * 购买商品 |
| 442 | */ | 444 | */ |
| 443 | const handlePurchase = async () => { | 445 | const handlePurchase = async () => { |
| 444 | - const hasPermission = await checkPermission(PERMISSION_TYPES.BUY_CAR, { | 446 | + const permissionResult = await checkPermission(PERMISSION_TYPES.BUY_CAR, { |
| 445 | showToast: false, | 447 | showToast: false, |
| 446 | autoRedirect: false | 448 | autoRedirect: false |
| 447 | }) | 449 | }) |
| 448 | 450 | ||
| 449 | // 如果没有权限,显示确认弹窗 | 451 | // 如果没有权限,显示确认弹窗 |
| 450 | - if (!hasPermission) { | 452 | + if (!permissionResult.hasPermission) { |
| 451 | Taro.showModal({ | 453 | Taro.showModal({ |
| 452 | title: '提示', | 454 | title: '提示', |
| 453 | content: '购买车辆需要先完善个人信息', | 455 | content: '购买车辆需要先完善个人信息', | ... | ... |
| ... | @@ -58,6 +58,12 @@ | ... | @@ -58,6 +58,12 @@ |
| 58 | <Right size="18" color="#9ca3af" /> | 58 | <Right size="18" color="#9ca3af" /> |
| 59 | </view> | 59 | </view> |
| 60 | 60 | ||
| 61 | + <view class="menu-item" @click="onCollectionSettings"> | ||
| 62 | + <Scan size="20" color="#6b7280" /> | ||
| 63 | + <text class="menu-text">收款设置</text> | ||
| 64 | + <Right size="18" color="#9ca3af" /> | ||
| 65 | + </view> | ||
| 66 | + | ||
| 61 | <view class="menu-item" @click="onFeedback"> | 67 | <view class="menu-item" @click="onFeedback"> |
| 62 | <Message size="20" color="#6b7280" /> | 68 | <Message size="20" color="#6b7280" /> |
| 63 | <text class="menu-text">意见反馈</text> | 69 | <text class="menu-text">意见反馈</text> |
| ... | @@ -74,10 +80,10 @@ | ... | @@ -74,10 +80,10 @@ |
| 74 | 80 | ||
| 75 | <!-- 自定义TabBar --> | 81 | <!-- 自定义TabBar --> |
| 76 | <TabBar /> | 82 | <TabBar /> |
| 77 | - | 83 | + |
| 78 | <!-- 隐私政策同意弹框 --> | 84 | <!-- 隐私政策同意弹框 --> |
| 79 | - <PrivacyAgreementModal | 85 | + <PrivacyAgreementModal |
| 80 | - v-model:visible="showPrivacyModal" | 86 | + v-model:visible="showPrivacyModal" |
| 81 | @confirm="onPrivacyConfirm" | 87 | @confirm="onPrivacyConfirm" |
| 82 | @cancel="onPrivacyCancel" | 88 | @cancel="onPrivacyCancel" |
| 83 | /> | 89 | /> |
| ... | @@ -86,7 +92,7 @@ | ... | @@ -86,7 +92,7 @@ |
| 86 | 92 | ||
| 87 | <script setup> | 93 | <script setup> |
| 88 | import { computed, ref } from 'vue' | 94 | import { computed, ref } from 'vue' |
| 89 | -import { Heart, Clock, Notice, Cart, Message, Tips, Right, StarN } from '@nutui/icons-vue-taro' | 95 | +import { Heart, Clock, Notice, Cart, Message, Tips, Right, StarN, Scan } from '@nutui/icons-vue-taro' |
| 90 | import Taro, { useDidShow } from '@tarojs/taro' | 96 | import Taro, { useDidShow } from '@tarojs/taro' |
| 91 | import TabBar from '@/components/TabBar.vue' | 97 | import TabBar from '@/components/TabBar.vue' |
| 92 | import PrivacyAgreementModal from '@/components/PrivacyAgreementModal.vue' | 98 | import PrivacyAgreementModal from '@/components/PrivacyAgreementModal.vue' |
| ... | @@ -114,7 +120,7 @@ useDidShow(async () => { | ... | @@ -114,7 +120,7 @@ useDidShow(async () => { |
| 114 | const onEditProfile = async () => { | 120 | const onEditProfile = async () => { |
| 115 | // 检查用户是否已完善资料 | 121 | // 检查用户是否已完善资料 |
| 116 | const hasCompleteProfile = userStore.hasCompleteProfile | 122 | const hasCompleteProfile = userStore.hasCompleteProfile |
| 117 | - | 123 | + |
| 118 | if (hasCompleteProfile) { | 124 | if (hasCompleteProfile) { |
| 119 | // 已完善资料,直接进入编辑页面 | 125 | // 已完善资料,直接进入编辑页面 |
| 120 | Taro.navigateTo({ | 126 | Taro.navigateTo({ |
| ... | @@ -207,6 +213,15 @@ const onFeedback = () => { | ... | @@ -207,6 +213,15 @@ const onFeedback = () => { |
| 207 | } | 213 | } |
| 208 | 214 | ||
| 209 | /** | 215 | /** |
| 216 | + * 收款设置 | ||
| 217 | + */ | ||
| 218 | +const onCollectionSettings = () => { | ||
| 219 | + Taro.navigateTo({ | ||
| 220 | + url: '/pages/collectionSettings/index' | ||
| 221 | + }) | ||
| 222 | +} | ||
| 223 | + | ||
| 224 | +/** | ||
| 210 | * 我的认证车 | 225 | * 我的认证车 |
| 211 | */ | 226 | */ |
| 212 | const onMyAuthCar = () => { | 227 | const onMyAuthCar = () => { | ... | ... |
| ... | @@ -1130,24 +1130,33 @@ const loadBrandsModels = async () => { | ... | @@ -1130,24 +1130,33 @@ const loadBrandsModels = async () => { |
| 1130 | // 页面加载时执行 | 1130 | // 页面加载时执行 |
| 1131 | onMounted(async () => { | 1131 | onMounted(async () => { |
| 1132 | // 检查卖车权限 | 1132 | // 检查卖车权限 |
| 1133 | - const hasPermission = await checkPermission(PERMISSION_TYPES.SELL_CAR, { | 1133 | + const permissionResult = await checkPermission(PERMISSION_TYPES.SELL_CAR, { |
| 1134 | showToast: false, | 1134 | showToast: false, |
| 1135 | autoRedirect: false | 1135 | autoRedirect: false |
| 1136 | }) | 1136 | }) |
| 1137 | 1137 | ||
| 1138 | // 如果没有权限,显示确认弹窗 | 1138 | // 如果没有权限,显示确认弹窗 |
| 1139 | - if (!hasPermission) { | 1139 | + if (!permissionResult.hasPermission) { |
| 1140 | + // 可以通过 permissionResult.missingFields 获取未通过校验的字段 | ||
| 1141 | + // 可以通过 permissionResult.validFields 获取已通过校验的字段 | ||
| 1140 | Taro.showModal({ | 1142 | Taro.showModal({ |
| 1141 | - title: '提示', | 1143 | + title: '温馨提示', |
| 1142 | - content: '发布车源需要先完善个人信息', | 1144 | + content: `您还未填写${permissionResult.missingFields.includes('phone') ? '个人资料' : '收款信息'},展示无法发布`, |
| 1143 | - cancelText: '关闭', | 1145 | + cancelText: '稍后设置', |
| 1144 | - confirmText: '前往完善', | 1146 | + confirmText: '立即设置', |
| 1145 | success: (res) => { | 1147 | success: (res) => { |
| 1146 | if (res.confirm) { | 1148 | if (res.confirm) { |
| 1147 | - // 用户点击前往完善,跳转到注册页面 | 1149 | + if (permissionResult.missingFields.includes('phone')) { |
| 1148 | - Taro.navigateTo({ | 1150 | + // 用户信息未填写 |
| 1149 | - url: '/pages/register/index' | 1151 | + Taro.navigateTo({ |
| 1150 | - }) | 1152 | + url: '/pages/register/index' |
| 1153 | + }) | ||
| 1154 | + } else if (permissionResult.missingFields.includes('paymentInfo')) { | ||
| 1155 | + // 收款信息未填写 | ||
| 1156 | + Taro.navigateTo({ | ||
| 1157 | + url: '/pages/collectionSettings/index' | ||
| 1158 | + }) | ||
| 1159 | + } | ||
| 1151 | } else { | 1160 | } else { |
| 1152 | // 用户点击关闭,返回上一页 | 1161 | // 用户点击关闭,返回上一页 |
| 1153 | // Taro.navigateBack() | 1162 | // Taro.navigateBack() | ... | ... |
| ... | @@ -76,10 +76,25 @@ const PERMISSION_CONFIG = { | ... | @@ -76,10 +76,25 @@ const PERMISSION_CONFIG = { |
| 76 | * @returns {boolean} 是否满足要求 | 76 | * @returns {boolean} 是否满足要求 |
| 77 | */ | 77 | */ |
| 78 | function checkUserFields(userInfo, checkFields) { | 78 | function checkUserFields(userInfo, checkFields) { |
| 79 | - return checkFields.every(field => { | 79 | + const result = { |
| 80 | + isValid: true, | ||
| 81 | + missingFields: [], | ||
| 82 | + validFields: [] | ||
| 83 | + } | ||
| 84 | + | ||
| 85 | + checkFields.forEach(field => { | ||
| 80 | const value = userInfo[field] | 86 | const value = userInfo[field] |
| 81 | - return value && value.toString().trim() !== '' | 87 | + const isFieldValid = value && value.toString().trim() !== '' |
| 88 | + | ||
| 89 | + if (isFieldValid) { | ||
| 90 | + result.validFields.push(field) | ||
| 91 | + } else { | ||
| 92 | + result.missingFields.push(field) | ||
| 93 | + result.isValid = false | ||
| 94 | + } | ||
| 82 | }) | 95 | }) |
| 96 | + | ||
| 97 | + return result | ||
| 83 | } | 98 | } |
| 84 | 99 | ||
| 85 | /** | 100 | /** |
| ... | @@ -93,7 +108,11 @@ function checkUserFields(userInfo, checkFields) { | ... | @@ -93,7 +108,11 @@ function checkUserFields(userInfo, checkFields) { |
| 93 | * @param {Function} options.onFail - 验证失败回调 | 108 | * @param {Function} options.onFail - 验证失败回调 |
| 94 | * @param {boolean} options.showToast - 是否显示提示(默认true) | 109 | * @param {boolean} options.showToast - 是否显示提示(默认true) |
| 95 | * @param {boolean} options.autoRedirect - 是否自动跳转(默认true) | 110 | * @param {boolean} options.autoRedirect - 是否自动跳转(默认true) |
| 96 | - * @returns {Promise<boolean>} 是否通过权限验证 | 111 | + * @returns {Promise<Object>} 权限检查结果 |
| 112 | + * @returns {boolean} returns.hasPermission - 是否通过权限验证 | ||
| 113 | + * @returns {Array} returns.validFields - 已通过校验的字段 | ||
| 114 | + * @returns {Array} returns.missingFields - 未通过校验的字段 | ||
| 115 | + * @returns {string} [returns.error] - 错误信息(如果有) | ||
| 97 | */ | 116 | */ |
| 98 | export async function checkPermission(permissionType, options = {}) { | 117 | export async function checkPermission(permissionType, options = {}) { |
| 99 | const userStore = useUserStore() | 118 | const userStore = useUserStore() |
| ... | @@ -119,12 +138,16 @@ export async function checkPermission(permissionType, options = {}) { | ... | @@ -119,12 +138,16 @@ export async function checkPermission(permissionType, options = {}) { |
| 119 | } | 138 | } |
| 120 | 139 | ||
| 121 | // 检查用户字段 | 140 | // 检查用户字段 |
| 122 | - const hasPermission = checkUserFields(userStore.userInfo, finalConfig.checkFields) | 141 | + const fieldCheckResult = checkUserFields(userStore.userInfo, finalConfig.checkFields) |
| 123 | 142 | ||
| 124 | - if (hasPermission) { | 143 | + if (fieldCheckResult.isValid) { |
| 125 | // 权限验证成功 | 144 | // 权限验证成功 |
| 126 | - finalConfig.onSuccess && finalConfig.onSuccess() | 145 | + finalConfig.onSuccess && finalConfig.onSuccess(fieldCheckResult) |
| 127 | - return true | 146 | + return { |
| 147 | + hasPermission: true, | ||
| 148 | + validFields: fieldCheckResult.validFields, | ||
| 149 | + missingFields: [] | ||
| 150 | + } | ||
| 128 | } else { | 151 | } else { |
| 129 | // 权限验证失败 | 152 | // 权限验证失败 |
| 130 | if (finalConfig.showToast) { | 153 | if (finalConfig.showToast) { |
| ... | @@ -148,13 +171,22 @@ export async function checkPermission(permissionType, options = {}) { | ... | @@ -148,13 +171,22 @@ export async function checkPermission(permissionType, options = {}) { |
| 148 | }) | 171 | }) |
| 149 | } | 172 | } |
| 150 | 173 | ||
| 151 | - finalConfig.onFail && finalConfig.onFail() | 174 | + finalConfig.onFail && finalConfig.onFail(fieldCheckResult) |
| 152 | - return false | 175 | + return { |
| 176 | + hasPermission: false, | ||
| 177 | + validFields: fieldCheckResult.validFields, | ||
| 178 | + missingFields: fieldCheckResult.missingFields | ||
| 179 | + } | ||
| 153 | } | 180 | } |
| 154 | } catch (error) { | 181 | } catch (error) { |
| 155 | console.error('权限检查失败:', error) | 182 | console.error('权限检查失败:', error) |
| 156 | - finalConfig.onFail && finalConfig.onFail(error) | 183 | + finalConfig.onFail && finalConfig.onFail({ error, missingFields: finalConfig.checkFields, validFields: [] }) |
| 157 | - return false | 184 | + return { |
| 185 | + hasPermission: false, | ||
| 186 | + validFields: [], | ||
| 187 | + missingFields: finalConfig.checkFields, | ||
| 188 | + error: error.message | ||
| 189 | + } | ||
| 158 | } | 190 | } |
| 159 | } | 191 | } |
| 160 | 192 | ... | ... |
-
Please register or login to post a comment