Showing
8 changed files
with
582 additions
and
17 deletions
| ... | @@ -7,6 +7,7 @@ export {} | ... | @@ -7,6 +7,7 @@ export {} |
| 7 | 7 | ||
| 8 | declare module 'vue' { | 8 | declare module 'vue' { |
| 9 | export interface GlobalComponents { | 9 | export interface GlobalComponents { |
| 10 | + AddressSelector: typeof import('./src/components/AddressSelector.vue')['default'] | ||
| 10 | BannerSwiper: typeof import('./src/components/BannerSwiper.vue')['default'] | 11 | BannerSwiper: typeof import('./src/components/BannerSwiper.vue')['default'] |
| 11 | BrandModelPicker: typeof import('./src/components/BrandModelPicker.vue')['default'] | 12 | BrandModelPicker: typeof import('./src/components/BrandModelPicker.vue')['default'] |
| 12 | FeaturedRecommendations: typeof import('./src/components/FeaturedRecommendations.vue')['default'] | 13 | FeaturedRecommendations: typeof import('./src/components/FeaturedRecommendations.vue')['default'] |
| ... | @@ -15,6 +16,7 @@ declare module 'vue' { | ... | @@ -15,6 +16,7 @@ declare module 'vue' { |
| 15 | NavBar: typeof import('./src/components/navBar.vue')['default'] | 16 | NavBar: typeof import('./src/components/navBar.vue')['default'] |
| 16 | NutActionSheet: typeof import('@nutui/nutui-taro')['ActionSheet'] | 17 | NutActionSheet: typeof import('@nutui/nutui-taro')['ActionSheet'] |
| 17 | NutButton: typeof import('@nutui/nutui-taro')['Button'] | 18 | NutButton: typeof import('@nutui/nutui-taro')['Button'] |
| 19 | + NutCascader: typeof import('@nutui/nutui-taro')['Cascader'] | ||
| 18 | NutCheckbox: typeof import('@nutui/nutui-taro')['Checkbox'] | 20 | NutCheckbox: typeof import('@nutui/nutui-taro')['Checkbox'] |
| 19 | NutCol: typeof import('@nutui/nutui-taro')['Col'] | 21 | NutCol: typeof import('@nutui/nutui-taro')['Col'] |
| 20 | NutConfigProvider: typeof import('@nutui/nutui-taro')['ConfigProvider'] | 22 | NutConfigProvider: typeof import('@nutui/nutui-taro')['ConfigProvider'] | ... | ... |
| ... | @@ -37,6 +37,7 @@ | ... | @@ -37,6 +37,7 @@ |
| 37 | "license": "MIT", | 37 | "license": "MIT", |
| 38 | "dependencies": { | 38 | "dependencies": { |
| 39 | "@babel/runtime": "^7.7.7", | 39 | "@babel/runtime": "^7.7.7", |
| 40 | + "@nutui/icons-vue": "^0.1.1", | ||
| 40 | "@nutui/icons-vue-taro": "^0.0.9", | 41 | "@nutui/icons-vue-taro": "^0.0.9", |
| 41 | "@nutui/nutui-taro": "^4.3.13", | 42 | "@nutui/nutui-taro": "^4.3.13", |
| 42 | "@tarojs/components": "4.1.2", | 43 | "@tarojs/components": "4.1.2", |
| ... | @@ -55,6 +56,7 @@ | ... | @@ -55,6 +56,7 @@ |
| 55 | "@tarojs/shared": "4.1.2", | 56 | "@tarojs/shared": "4.1.2", |
| 56 | "@tarojs/taro": "4.1.2", | 57 | "@tarojs/taro": "4.1.2", |
| 57 | "axios-miniprogram": "^2.7.2", | 58 | "axios-miniprogram": "^2.7.2", |
| 59 | + "element-china-area-data": "^6.1.0", | ||
| 58 | "pinia": "^3.0.3", | 60 | "pinia": "^3.0.3", |
| 59 | "taro-plugin-pinia": "^1.0.0", | 61 | "taro-plugin-pinia": "^1.0.0", |
| 60 | "vue": "^3.3.0" | 62 | "vue": "^3.3.0" | ... | ... |
pnpm-lock.yaml
0 → 100644
This diff could not be displayed because it is too large.
| ... | @@ -47,6 +47,12 @@ button::after { | ... | @@ -47,6 +47,12 @@ button::after { |
| 47 | color: #9ca3af; | 47 | color: #9ca3af; |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | +/* 覆盖 NutUI Tabs 组件的 width: 0 样式 */ | ||
| 51 | +.nut-tabs.horizontal .nut-sticky__box > .nut-tabs__titles .nut-tabs__titles-item, | ||
| 52 | +.nut-tabs.horizontal > .nut-tabs__titles .nut-tabs__titles-item { | ||
| 53 | + width: auto !important; | ||
| 54 | +} | ||
| 55 | + | ||
| 50 | .bg-orange-400 { | 56 | .bg-orange-400 { |
| 51 | background-color: #fb923c; | 57 | background-color: #fb923c; |
| 52 | } | 58 | } | ... | ... |
src/components/AddressSelector.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <view> | ||
| 3 | + <!-- 地址选择弹窗 --> | ||
| 4 | + <nut-popup | ||
| 5 | + :visible="visible" | ||
| 6 | + position="bottom" | ||
| 7 | + :style="{ height: '85%' }" | ||
| 8 | + close-icon-position="top-right" | ||
| 9 | + @close="closeModal" | ||
| 10 | + :z-index="9999" | ||
| 11 | + > | ||
| 12 | + <view class="address-modal"> | ||
| 13 | + <view class="address-modal-header"> | ||
| 14 | + <text class="address-modal-title">选择地址</text> | ||
| 15 | + </view> | ||
| 16 | + | ||
| 17 | + <view class="address-modal-content"> | ||
| 18 | + <!-- 省市县选择 --> | ||
| 19 | + <view class="address-section"> | ||
| 20 | + <text class="address-section-title">选择省市县</text> | ||
| 21 | + <nut-cascader | ||
| 22 | + v-model="selectedAreaCodes" | ||
| 23 | + v-model:visible="showAreaPicker" | ||
| 24 | + :options="areaData" | ||
| 25 | + @change="onAreaChange" | ||
| 26 | + @path-change="onAreaPathChange" | ||
| 27 | + title="请选择省市县" | ||
| 28 | + /> | ||
| 29 | + <view | ||
| 30 | + class="area-selector" | ||
| 31 | + @click="showAreaPicker = true" | ||
| 32 | + > | ||
| 33 | + <text class="area-text" :class="{ 'area-selected': selectedAreaText }"> | ||
| 34 | + {{ selectedAreaText || '请选择省市县' }} | ||
| 35 | + </text> | ||
| 36 | + <ArrowRight color="#9ca3af" size="12" /> | ||
| 37 | + </view> | ||
| 38 | + </view> | ||
| 39 | + | ||
| 40 | + <!-- 详细地址输入 --> | ||
| 41 | + <view class="address-section"> | ||
| 42 | + <text class="address-section-title">详细地址</text> | ||
| 43 | + <nut-textarea | ||
| 44 | + v-model="detailAddress" | ||
| 45 | + placeholder="请输入详细地址(街道、门牌号等)" | ||
| 46 | + rows="3" | ||
| 47 | + class="detail-address-input" | ||
| 48 | + /> | ||
| 49 | + </view> | ||
| 50 | + </view> | ||
| 51 | + | ||
| 52 | + <view class="address-modal-footer"> | ||
| 53 | + <nut-button | ||
| 54 | + type="primary" | ||
| 55 | + color="orange" | ||
| 56 | + block | ||
| 57 | + @click="confirmAddress" | ||
| 58 | + :disabled="!selectedAreaText || !detailAddress.trim()" | ||
| 59 | + > | ||
| 60 | + 确定 | ||
| 61 | + </nut-button> | ||
| 62 | + </view> | ||
| 63 | + </view> | ||
| 64 | + </nut-popup> | ||
| 65 | + </view> | ||
| 66 | +</template> | ||
| 67 | + | ||
| 68 | +<script setup> | ||
| 69 | +import { ref, computed, watch } from 'vue' | ||
| 70 | +import { RectRight, ArrowRight } from '@nutui/icons-vue-taro' | ||
| 71 | +import { regionData } from 'element-china-area-data' | ||
| 72 | + | ||
| 73 | +/** | ||
| 74 | + * 转换地区数据格式以适配NutUI Cascader组件 | ||
| 75 | + * @param {Array} data - element-china-area-data的原始数据 | ||
| 76 | + * @returns {Array} - 转换后的数据格式 | ||
| 77 | + */ | ||
| 78 | +const transformAreaData = (data) => { | ||
| 79 | + return data.map(item => ({ | ||
| 80 | + value: item.value, | ||
| 81 | + text: item.label, | ||
| 82 | + children: item.children ? transformAreaData(item.children) : undefined | ||
| 83 | + })) | ||
| 84 | +} | ||
| 85 | + | ||
| 86 | +/** | ||
| 87 | + * 组件属性定义 | ||
| 88 | + */ | ||
| 89 | +const props = defineProps({ | ||
| 90 | + // 当前选中的地址信息 | ||
| 91 | + modelValue: { | ||
| 92 | + type: Object, | ||
| 93 | + default: () => ({ | ||
| 94 | + province: '', | ||
| 95 | + city: '', | ||
| 96 | + county: '', | ||
| 97 | + detail_address: '', | ||
| 98 | + full_address: '' | ||
| 99 | + }) | ||
| 100 | + }, | ||
| 101 | + // 占位符文本 | ||
| 102 | + placeholder: { | ||
| 103 | + type: String, | ||
| 104 | + default: '请选择省市县并填写详细地址' | ||
| 105 | + }, | ||
| 106 | + // 控制弹窗显示状态 | ||
| 107 | + visible: { | ||
| 108 | + type: Boolean, | ||
| 109 | + default: false | ||
| 110 | + } | ||
| 111 | +}) | ||
| 112 | + | ||
| 113 | +/** | ||
| 114 | + * 组件事件定义 | ||
| 115 | + */ | ||
| 116 | +const emit = defineEmits(['update:modelValue', 'change', 'update:visible']) | ||
| 117 | + | ||
| 118 | +/** | ||
| 119 | + * 地址选择相关状态 | ||
| 120 | + */ | ||
| 121 | +const showAreaPicker = ref(false) | ||
| 122 | +const selectedAreaCodes = ref([]) | ||
| 123 | +const selectedAreaText = ref('') | ||
| 124 | +const detailAddress = ref('') | ||
| 125 | +const areaData = ref(transformAreaData(regionData)) | ||
| 126 | + | ||
| 127 | +/** | ||
| 128 | + * 计算完整地址 | ||
| 129 | + */ | ||
| 130 | +const fullAddress = computed(() => { | ||
| 131 | + if (selectedAreaText.value && detailAddress.value.trim()) { | ||
| 132 | + return `${selectedAreaText.value} ${detailAddress.value.trim()}` | ||
| 133 | + } | ||
| 134 | + return '' | ||
| 135 | +}) | ||
| 136 | + | ||
| 137 | +/** | ||
| 138 | + * 初始化地址数据 | ||
| 139 | + * @param {Object} addressData - 地址数据对象 | ||
| 140 | + */ | ||
| 141 | +const initAddressData = (addressData) => { | ||
| 142 | + if (addressData.province && addressData.city && addressData.county) { | ||
| 143 | + selectedAreaText.value = `${addressData.province}${addressData.city}${addressData.county}` | ||
| 144 | + detailAddress.value = addressData.detail_address || '' | ||
| 145 | + } else { | ||
| 146 | + selectedAreaText.value = '' | ||
| 147 | + detailAddress.value = '' | ||
| 148 | + } | ||
| 149 | +} | ||
| 150 | + | ||
| 151 | +/** | ||
| 152 | + * 监听props变化,初始化组件数据 | ||
| 153 | + */ | ||
| 154 | +watch(() => props.modelValue, (newValue) => { | ||
| 155 | + if (newValue) { | ||
| 156 | + initAddressData(newValue) | ||
| 157 | + } | ||
| 158 | +}, { immediate: true, deep: true }) | ||
| 159 | + | ||
| 160 | +/** | ||
| 161 | + * 地区选择变化回调(选中值改变时触发) | ||
| 162 | + * @param {Array} value - 选中的值数组 | ||
| 163 | + * @param {Array} pathNodes - 选中的路径节点数组 | ||
| 164 | + */ | ||
| 165 | +const onAreaChange = (value, pathNodes) => { | ||
| 166 | + if (pathNodes && Array.isArray(pathNodes) && pathNodes.length === 3) { | ||
| 167 | + selectedAreaCodes.value = value | ||
| 168 | + selectedAreaText.value = pathNodes | ||
| 169 | + .filter(node => node && node.text) | ||
| 170 | + .map(node => node.text) | ||
| 171 | + .join('') | ||
| 172 | + // 选择完三级地址后自动关闭选择器 | ||
| 173 | + showAreaPicker.value = false | ||
| 174 | + } | ||
| 175 | +} | ||
| 176 | + | ||
| 177 | +/** | ||
| 178 | + * 地区路径选择变化回调(选中项改变时触发) | ||
| 179 | + * @param {Array} pathNodes - 选中的路径节点数组 | ||
| 180 | + */ | ||
| 181 | +const onAreaPathChange = (pathNodes) => { | ||
| 182 | + if (pathNodes && Array.isArray(pathNodes)) { | ||
| 183 | + selectedAreaText.value = pathNodes | ||
| 184 | + .filter(node => node && node.text) | ||
| 185 | + .map(node => node.text) | ||
| 186 | + .join('') | ||
| 187 | + } | ||
| 188 | +} | ||
| 189 | + | ||
| 190 | + | ||
| 191 | + | ||
| 192 | +/** | ||
| 193 | + * 关闭地址选择弹窗 | ||
| 194 | + */ | ||
| 195 | +const closeModal = () => { | ||
| 196 | + emit('update:visible', false) | ||
| 197 | +} | ||
| 198 | + | ||
| 199 | +/** | ||
| 200 | + * 确认地址选择 | ||
| 201 | + */ | ||
| 202 | +const confirmAddress = () => { | ||
| 203 | + if (selectedAreaText.value && detailAddress.value.trim()) { | ||
| 204 | + const codes = selectedAreaCodes.value | ||
| 205 | + const addressData = { | ||
| 206 | + province: getProvinceFromText(selectedAreaText.value), | ||
| 207 | + city: getCityFromText(selectedAreaText.value), | ||
| 208 | + county: getCountyFromText(selectedAreaText.value), | ||
| 209 | + province_code: codes[0] || '', | ||
| 210 | + city_code: codes[1] || '', | ||
| 211 | + county_code: codes[2] || '', | ||
| 212 | + detail_address: detailAddress.value.trim(), | ||
| 213 | + full_address: fullAddress.value | ||
| 214 | + } | ||
| 215 | + | ||
| 216 | + // 触发更新事件 | ||
| 217 | + emit('update:modelValue', addressData) | ||
| 218 | + emit('change', addressData) | ||
| 219 | + | ||
| 220 | + // 关闭弹窗 | ||
| 221 | + emit('update:visible', false) | ||
| 222 | + } | ||
| 223 | +} | ||
| 224 | + | ||
| 225 | +/** | ||
| 226 | + * 从选中文本中提取省份 | ||
| 227 | + * @param {String} text - 选中的地区文本 | ||
| 228 | + * @returns {String} - 省份名称 | ||
| 229 | + */ | ||
| 230 | +const getProvinceFromText = (text) => { | ||
| 231 | + // 这里可以根据实际需要实现更精确的解析逻辑 | ||
| 232 | + // 暂时使用简单的方式 | ||
| 233 | + const codes = selectedAreaCodes.value | ||
| 234 | + if (codes.length >= 1) { | ||
| 235 | + const provinceNode = findNodeByCode(areaData.value, codes[0]) | ||
| 236 | + return provinceNode ? provinceNode.text : '' | ||
| 237 | + } | ||
| 238 | + return '' | ||
| 239 | +} | ||
| 240 | + | ||
| 241 | +/** | ||
| 242 | + * 从选中文本中提取城市 | ||
| 243 | + * @param {String} text - 选中的地区文本 | ||
| 244 | + * @returns {String} - 城市名称 | ||
| 245 | + */ | ||
| 246 | +const getCityFromText = (text) => { | ||
| 247 | + const codes = selectedAreaCodes.value | ||
| 248 | + if (codes.length >= 2) { | ||
| 249 | + const provinceNode = findNodeByCode(areaData.value, codes[0]) | ||
| 250 | + if (provinceNode && provinceNode.children) { | ||
| 251 | + const cityNode = findNodeByCode(provinceNode.children, codes[1]) | ||
| 252 | + return cityNode ? cityNode.text : '' | ||
| 253 | + } | ||
| 254 | + } | ||
| 255 | + return '' | ||
| 256 | +} | ||
| 257 | + | ||
| 258 | +/** | ||
| 259 | + * 从选中文本中提取县区 | ||
| 260 | + * @param {String} text - 选中的地区文本 | ||
| 261 | + * @returns {String} - 县区名称 | ||
| 262 | + */ | ||
| 263 | +const getCountyFromText = (text) => { | ||
| 264 | + const codes = selectedAreaCodes.value | ||
| 265 | + if (codes.length >= 3) { | ||
| 266 | + const provinceNode = findNodeByCode(areaData.value, codes[0]) | ||
| 267 | + if (provinceNode && provinceNode.children) { | ||
| 268 | + const cityNode = findNodeByCode(provinceNode.children, codes[1]) | ||
| 269 | + if (cityNode && cityNode.children) { | ||
| 270 | + const countyNode = findNodeByCode(cityNode.children, codes[2]) | ||
| 271 | + return countyNode ? countyNode.text : '' | ||
| 272 | + } | ||
| 273 | + } | ||
| 274 | + } | ||
| 275 | + return '' | ||
| 276 | +} | ||
| 277 | + | ||
| 278 | +/** | ||
| 279 | + * 根据编码查找节点 | ||
| 280 | + * @param {Array} nodes - 节点数组 | ||
| 281 | + * @param {String} code - 编码 | ||
| 282 | + * @returns {Object|null} - 找到的节点或null | ||
| 283 | + */ | ||
| 284 | +const findNodeByCode = (nodes, code) => { | ||
| 285 | + return nodes.find(node => node.value === code) || null | ||
| 286 | +} | ||
| 287 | +</script> | ||
| 288 | + | ||
| 289 | +<style lang="less"> | ||
| 290 | +// 地址选择器样式 | ||
| 291 | +.address-selector { | ||
| 292 | + display: flex; | ||
| 293 | + align-items: center; | ||
| 294 | + justify-content: space-between; | ||
| 295 | + padding: 24rpx 32rpx; | ||
| 296 | + border: 1px solid #e5e7eb; | ||
| 297 | + border-radius: 24rpx; | ||
| 298 | + background-color: white; | ||
| 299 | + min-height: 96rpx; | ||
| 300 | + transition: all 0.2s ease; | ||
| 301 | + | ||
| 302 | + &:active { | ||
| 303 | + background-color: #f9fafb; | ||
| 304 | + } | ||
| 305 | + | ||
| 306 | + .address-text { | ||
| 307 | + font-size: 28rpx; | ||
| 308 | + color: #9ca3af; | ||
| 309 | + flex: 1; | ||
| 310 | + line-height: 1.6; | ||
| 311 | + word-break: break-all; | ||
| 312 | + | ||
| 313 | + &.address-selected { | ||
| 314 | + color: #111827; | ||
| 315 | + } | ||
| 316 | + } | ||
| 317 | +} | ||
| 318 | + | ||
| 319 | +// 地址选择弹窗样式 | ||
| 320 | +.address-modal { | ||
| 321 | + display: flex; | ||
| 322 | + flex-direction: column; | ||
| 323 | + height: 100%; | ||
| 324 | + background-color: white; | ||
| 325 | + | ||
| 326 | + .address-modal-header { | ||
| 327 | + display: flex; | ||
| 328 | + align-items: center; | ||
| 329 | + justify-content: center; | ||
| 330 | + padding: 32rpx; | ||
| 331 | + border-bottom: 1px solid #f3f4f6; | ||
| 332 | + background-color: white; | ||
| 333 | + position: sticky; | ||
| 334 | + top: 0; | ||
| 335 | + z-index: 10; | ||
| 336 | + | ||
| 337 | + .address-modal-title { | ||
| 338 | + font-size: 36rpx; | ||
| 339 | + font-weight: 600; | ||
| 340 | + color: #111827; | ||
| 341 | + } | ||
| 342 | + } | ||
| 343 | + | ||
| 344 | + .address-modal-content { | ||
| 345 | + flex: 1; | ||
| 346 | + padding: 32rpx; | ||
| 347 | + overflow-y: auto; | ||
| 348 | + | ||
| 349 | + .address-section { | ||
| 350 | + margin-bottom: 48rpx; | ||
| 351 | + | ||
| 352 | + .address-section-title { | ||
| 353 | + display: block; | ||
| 354 | + font-size: 28rpx; | ||
| 355 | + font-weight: 500; | ||
| 356 | + color: #374151; | ||
| 357 | + margin-bottom: 24rpx; | ||
| 358 | + } | ||
| 359 | + | ||
| 360 | + .area-selector { | ||
| 361 | + display: flex; | ||
| 362 | + align-items: center; | ||
| 363 | + justify-content: space-between; | ||
| 364 | + padding: 24rpx 32rpx; | ||
| 365 | + border: 1px solid #e5e7eb; | ||
| 366 | + border-radius: 24rpx; | ||
| 367 | + background-color: white; | ||
| 368 | + transition: all 0.2s ease; | ||
| 369 | + | ||
| 370 | + &:active { | ||
| 371 | + background-color: #f9fafb; | ||
| 372 | + } | ||
| 373 | + | ||
| 374 | + .area-text { | ||
| 375 | + font-size: 28rpx; | ||
| 376 | + color: #9ca3af; | ||
| 377 | + flex: 1; | ||
| 378 | + line-height: 1.6; | ||
| 379 | + word-break: break-all; | ||
| 380 | + | ||
| 381 | + &.area-selected { | ||
| 382 | + color: #111827; | ||
| 383 | + } | ||
| 384 | + } | ||
| 385 | + } | ||
| 386 | + | ||
| 387 | + .detail-address-input { | ||
| 388 | + width: 100%; | ||
| 389 | + | ||
| 390 | + :deep(.nut-textarea) { | ||
| 391 | + border: 1px solid #e5e7eb; | ||
| 392 | + border-radius: 24rpx; | ||
| 393 | + | ||
| 394 | + .nut-textarea__textarea { | ||
| 395 | + padding: 24rpx 32rpx; | ||
| 396 | + font-size: 28rpx; | ||
| 397 | + line-height: 1.6; | ||
| 398 | + min-height: 160rpx; | ||
| 399 | + } | ||
| 400 | + } | ||
| 401 | + } | ||
| 402 | + } | ||
| 403 | + } | ||
| 404 | + | ||
| 405 | + .address-modal-footer { | ||
| 406 | + padding: 32rpx; | ||
| 407 | + border-top: 1px solid #f3f4f6; | ||
| 408 | + background-color: white; | ||
| 409 | + position: sticky; | ||
| 410 | + bottom: 0; | ||
| 411 | + z-index: 10; | ||
| 412 | + | ||
| 413 | + :deep(.nut-button) { | ||
| 414 | + border-radius: 24rpx; | ||
| 415 | + font-weight: 500; | ||
| 416 | + height: 88rpx; | ||
| 417 | + } | ||
| 418 | + } | ||
| 419 | +} | ||
| 420 | +</style> |
| ... | @@ -474,3 +474,32 @@ | ... | @@ -474,3 +474,32 @@ |
| 474 | font-size: 28rpx; | 474 | font-size: 28rpx; |
| 475 | margin-right: 8rpx; | 475 | margin-right: 8rpx; |
| 476 | } | 476 | } |
| 477 | + | ||
| 478 | +// 地址选择器触发器样式 | ||
| 479 | +.address-selector { | ||
| 480 | + display: flex; | ||
| 481 | + align-items: center; | ||
| 482 | + justify-content: space-between; | ||
| 483 | + padding: 24rpx 32rpx; | ||
| 484 | + border: 1px solid #e5e7eb; | ||
| 485 | + border-radius: 24rpx; | ||
| 486 | + background-color: white; | ||
| 487 | + min-height: 96rpx; | ||
| 488 | + transition: all 0.2s ease; | ||
| 489 | + | ||
| 490 | + &:active { | ||
| 491 | + background-color: #f9fafb; | ||
| 492 | + } | ||
| 493 | + | ||
| 494 | + .address-text { | ||
| 495 | + font-size: 28rpx; | ||
| 496 | + color: #9ca3af; | ||
| 497 | + flex: 1; | ||
| 498 | + line-height: 1.6; | ||
| 499 | + word-break: break-all; | ||
| 500 | + | ||
| 501 | + &.address-selected { | ||
| 502 | + color: #111827; | ||
| 503 | + } | ||
| 504 | + } | ||
| 505 | +} | ... | ... |
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2022-09-19 14:11:06 | 2 | * @Date: 2022-09-19 14:11:06 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-08-08 13:58:13 | 4 | + * @LastEditTime: 2025-08-12 17:05:50 |
| 5 | * @FilePath: /jgdl/src/pages/collectionSettings/index.vue | 5 | * @FilePath: /jgdl/src/pages/collectionSettings/index.vue |
| 6 | * @Description: 收款设置 | 6 | * @Description: 收款设置 |
| 7 | --> | 7 | --> |
| ... | @@ -32,9 +32,9 @@ | ... | @@ -32,9 +32,9 @@ |
| 32 | </view> | 32 | </view> |
| 33 | <view class="setting-right"> | 33 | <view class="setting-right"> |
| 34 | <text class="setting-status" | 34 | <text class="setting-status" |
| 35 | - :class="{ 'status-set': identityInfo.userName && identityInfo.idCard && identityInfo.idcard_1_img && identityInfo.idcard_2_img && identityInfo.idcard_effect_begin && identityInfo.idcard_effect_end && identityInfo.idcard_address }"> | 35 | + :class="{ 'status-set': identityInfo.userName && identityInfo.idCard && identityInfo.idcard_1_img && identityInfo.idcard_2_img && identityInfo.idcard_effect_begin && identityInfo.idcard_effect_end && (identityInfo.idcard_province && identityInfo.idcard_city && identityInfo.idcard_district && identityInfo.idcard_address) }"> |
| 36 | {{ identityInfo.userName && identityInfo.idCard && identityInfo.idcard_1_img && identityInfo.idcard_2_img && | 36 | {{ identityInfo.userName && identityInfo.idCard && identityInfo.idcard_1_img && identityInfo.idcard_2_img && |
| 37 | - identityInfo.idcard_effect_begin && identityInfo.idcard_effect_end && identityInfo.idcard_address ? '已设置' : | 37 | + identityInfo.idcard_effect_begin && identityInfo.idcard_effect_end && (identityInfo.idcard_province && identityInfo.idcard_city && identityInfo.idcard_district && identityInfo.idcard_address) ? '已设置' : |
| 38 | '未设置' }} | 38 | '未设置' }} |
| 39 | </text> | 39 | </text> |
| 40 | <!-- <text class="arrow">></text> --> | 40 | <!-- <text class="arrow">></text> --> |
| ... | @@ -194,8 +194,16 @@ | ... | @@ -194,8 +194,16 @@ |
| 194 | <!-- 身份证地址 --> | 194 | <!-- 身份证地址 --> |
| 195 | <view class="form-item"> | 195 | <view class="form-item"> |
| 196 | <text class="form-label"><text class="required-mark">*</text>身份证地址</text> | 196 | <text class="form-label"><text class="required-mark">*</text>身份证地址</text> |
| 197 | - <textarea v-model="tempIdentityInfo.idcard_address" placeholder="请输入身份证地址" class="form-input native-input" | 197 | + <!-- 地址选择器触发器 --> |
| 198 | - :cursor-spacing="50" /> | 198 | + <view |
| 199 | + class="address-selector" | ||
| 200 | + @click="showAddressSelector = true" | ||
| 201 | + > | ||
| 202 | + <text class="address-text" :class="{ 'address-selected': addressData.full_address }"> | ||
| 203 | + {{ addressData.full_address || '请选择省市县并填写详细地址' }} | ||
| 204 | + </text> | ||
| 205 | + <RectRight color="#999" size="10" /> | ||
| 206 | + </view> | ||
| 199 | </view> | 207 | </view> |
| 200 | </view> | 208 | </view> |
| 201 | 209 | ||
| ... | @@ -204,7 +212,7 @@ | ... | @@ -204,7 +212,7 @@ |
| 204 | 关闭 | 212 | 关闭 |
| 205 | </nut-button> | 213 | </nut-button> |
| 206 | <nut-button type="primary" @click="saveIdentityInfo" color="#ffa500" | 214 | <nut-button type="primary" @click="saveIdentityInfo" color="#ffa500" |
| 207 | - :disabled="!tempIdentityInfo.userName || !tempIdentityInfo.idCard || !tempIdentityInfo.idcard_1_img || !tempIdentityInfo.idcard_2_img || !tempIdentityInfo.idcard_effect_begin || !tempIdentityInfo.idcard_effect_end || !tempIdentityInfo.idcard_address || !!idCardError" | 215 | + :disabled="!tempIdentityInfo.userName || !tempIdentityInfo.idCard || !tempIdentityInfo.idcard_1_img || !tempIdentityInfo.idcard_2_img || !tempIdentityInfo.idcard_effect_begin || !tempIdentityInfo.idcard_effect_end || !tempIdentityInfo.idcard_province || !tempIdentityInfo.idcard_city || !tempIdentityInfo.idcard_district || !tempIdentityInfo.idcard_address || !!idCardError" |
| 208 | class="footer-btn footer-btn-save"> | 216 | class="footer-btn footer-btn-save"> |
| 209 | 保存 | 217 | 保存 |
| 210 | </nut-button> | 218 | </nut-button> |
| ... | @@ -234,6 +242,15 @@ | ... | @@ -234,6 +242,15 @@ |
| 234 | <view v-if="showBackButton" class="fixed-back-btn" @click="goBack"> | 242 | <view v-if="showBackButton" class="fixed-back-btn" @click="goBack"> |
| 235 | <text class="back-text">返回</text> | 243 | <text class="back-text">返回</text> |
| 236 | </view> | 244 | </view> |
| 245 | + | ||
| 246 | + <!-- 地址选择器组件 --> | ||
| 247 | + <AddressSelector | ||
| 248 | + v-model:visible="showAddressSelector" | ||
| 249 | + v-model="addressData" | ||
| 250 | + @change="onAddressChange" | ||
| 251 | + placeholder="请选择省市县并填写详细地址" | ||
| 252 | + /> | ||
| 253 | + | ||
| 237 | </view> | 254 | </view> |
| 238 | </template> | 255 | </template> |
| 239 | 256 | ||
| ... | @@ -252,6 +269,9 @@ import BASE_URL from '@/utils/config' | ... | @@ -252,6 +269,9 @@ import BASE_URL from '@/utils/config' |
| 252 | // 导入用户状态管理 | 269 | // 导入用户状态管理 |
| 253 | import { useUserStore } from '@/stores/user' | 270 | import { useUserStore } from '@/stores/user' |
| 254 | 271 | ||
| 272 | +// 导入地址选择组件 | ||
| 273 | +import AddressSelector from '@/components/AddressSelector.vue' | ||
| 274 | + | ||
| 255 | // 获取页面参数 | 275 | // 获取页面参数 |
| 256 | const instance = Taro.getCurrentInstance() | 276 | const instance = Taro.getCurrentInstance() |
| 257 | const { target } = instance.router?.params || {} | 277 | const { target } = instance.router?.params || {} |
| ... | @@ -308,6 +328,14 @@ const tempIdentityInfo = ref({ | ... | @@ -308,6 +328,14 @@ const tempIdentityInfo = ref({ |
| 308 | idcard_2_img: '', | 328 | idcard_2_img: '', |
| 309 | idcard_effect_begin: '', | 329 | idcard_effect_begin: '', |
| 310 | idcard_effect_end: '', | 330 | idcard_effect_end: '', |
| 331 | + // 身份证地址相关字段(显示用中文名称) | ||
| 332 | + province: '', | ||
| 333 | + city: '', | ||
| 334 | + county: '', | ||
| 335 | + // 身份证地址相关字段(提交用真实字段名) | ||
| 336 | + idcard_province: '', | ||
| 337 | + idcard_city: '', | ||
| 338 | + idcard_district: '', | ||
| 311 | idcard_address: '' | 339 | idcard_address: '' |
| 312 | }); | 340 | }); |
| 313 | 341 | ||
| ... | @@ -320,6 +348,7 @@ const showBankModal = ref(false); | ... | @@ -320,6 +348,7 @@ const showBankModal = ref(false); |
| 320 | const showIdcardBeginDate = ref(false); | 348 | const showIdcardBeginDate = ref(false); |
| 321 | const showIdcardEndDate = ref(false); | 349 | const showIdcardEndDate = ref(false); |
| 322 | const showEndDateTypeModal = ref(false); | 350 | const showEndDateTypeModal = ref(false); |
| 351 | +const showAddressSelector = ref(false); | ||
| 323 | 352 | ||
| 324 | /** | 353 | /** |
| 325 | * 身份证号码错误信息 | 354 | * 身份证号码错误信息 |
| ... | @@ -418,6 +447,14 @@ const getUserInfo = async () => { | ... | @@ -418,6 +447,14 @@ const getUserInfo = async () => { |
| 418 | idcard_2_img: userInfo.idcard_2_img || '', | 447 | idcard_2_img: userInfo.idcard_2_img || '', |
| 419 | idcard_effect_begin: userInfo.idcard_effect_begin || '', | 448 | idcard_effect_begin: userInfo.idcard_effect_begin || '', |
| 420 | idcard_effect_end: userInfo.idcard_effect_end || '', | 449 | idcard_effect_end: userInfo.idcard_effect_end || '', |
| 450 | + // 地址字段(用于显示的中文名称) | ||
| 451 | + province: userInfo.province_name || '', | ||
| 452 | + city: userInfo.city_name || '', | ||
| 453 | + county: userInfo.county_name || userInfo.district_name || '', | ||
| 454 | + // 身份证地址字段(真实字段名) | ||
| 455 | + idcard_province: userInfo.idcard_province || '', | ||
| 456 | + idcard_city: userInfo.idcard_city || '', | ||
| 457 | + idcard_district: userInfo.idcard_district || '', | ||
| 421 | idcard_address: userInfo.idcard_address || '' | 458 | idcard_address: userInfo.idcard_address || '' |
| 422 | }; | 459 | }; |
| 423 | } | 460 | } |
| ... | @@ -620,10 +657,28 @@ const saveAccountInfo = async () => { | ... | @@ -620,10 +657,28 @@ const saveAccountInfo = async () => { |
| 620 | const openIdentityModal = () => { | 657 | const openIdentityModal = () => { |
| 621 | tempIdentityInfo.value = { ...identityInfo.value }; | 658 | tempIdentityInfo.value = { ...identityInfo.value }; |
| 622 | idCardError.value = ''; | 659 | idCardError.value = ''; |
| 660 | + // 初始化地址选择器数据 | ||
| 661 | + initAddressData(); | ||
| 623 | showIdentityModal.value = true; | 662 | showIdentityModal.value = true; |
| 624 | }; | 663 | }; |
| 625 | 664 | ||
| 626 | /** | 665 | /** |
| 666 | + * 初始化地址选择器数据 | ||
| 667 | + */ | ||
| 668 | +const initAddressData = () => { | ||
| 669 | + addressData.value = { | ||
| 670 | + province: tempIdentityInfo.value.province || '', | ||
| 671 | + city: tempIdentityInfo.value.city || '', | ||
| 672 | + county: tempIdentityInfo.value.county || '', | ||
| 673 | + province_code: tempIdentityInfo.value.idcard_province || '', | ||
| 674 | + city_code: tempIdentityInfo.value.idcard_city || '', | ||
| 675 | + county_code: tempIdentityInfo.value.idcard_district || '', | ||
| 676 | + detail_address: tempIdentityInfo.value.idcard_address || '', | ||
| 677 | + full_address: (tempIdentityInfo.value.province + tempIdentityInfo.value.city + tempIdentityInfo.value.county + tempIdentityInfo.value.idcard_address) || '' | ||
| 678 | + } | ||
| 679 | +}; | ||
| 680 | + | ||
| 681 | +/** | ||
| 627 | * 关闭身份信息弹窗 | 682 | * 关闭身份信息弹窗 |
| 628 | */ | 683 | */ |
| 629 | const closeIdentityModal = () => { | 684 | const closeIdentityModal = () => { |
| ... | @@ -635,6 +690,12 @@ const closeIdentityModal = () => { | ... | @@ -635,6 +690,12 @@ const closeIdentityModal = () => { |
| 635 | idcard_2_img: '', | 690 | idcard_2_img: '', |
| 636 | idcard_effect_begin: '', | 691 | idcard_effect_begin: '', |
| 637 | idcard_effect_end: '', | 692 | idcard_effect_end: '', |
| 693 | + province: '', | ||
| 694 | + city: '', | ||
| 695 | + county: '', | ||
| 696 | + idcard_province: '', | ||
| 697 | + idcard_city: '', | ||
| 698 | + idcard_district: '', | ||
| 638 | idcard_address: '' | 699 | idcard_address: '' |
| 639 | }; | 700 | }; |
| 640 | idCardError.value = ''; | 701 | idCardError.value = ''; |
| ... | @@ -995,6 +1056,10 @@ const saveIdentityInfo = async () => { | ... | @@ -995,6 +1056,10 @@ const saveIdentityInfo = async () => { |
| 995 | idcard_2_img: tempIdentityInfo.value.idcard_2_img, | 1056 | idcard_2_img: tempIdentityInfo.value.idcard_2_img, |
| 996 | idcard_effect_begin: tempIdentityInfo.value.idcard_effect_begin, | 1057 | idcard_effect_begin: tempIdentityInfo.value.idcard_effect_begin, |
| 997 | idcard_effect_end: tempIdentityInfo.value.idcard_effect_end, | 1058 | idcard_effect_end: tempIdentityInfo.value.idcard_effect_end, |
| 1059 | + // 使用真实的身份证地址字段名 | ||
| 1060 | + idcard_province: tempIdentityInfo.value.idcard_province, | ||
| 1061 | + idcard_city: tempIdentityInfo.value.idcard_city, | ||
| 1062 | + idcard_district: tempIdentityInfo.value.idcard_district, | ||
| 998 | idcard_address: tempIdentityInfo.value.idcard_address | 1063 | idcard_address: tempIdentityInfo.value.idcard_address |
| 999 | }); | 1064 | }); |
| 1000 | 1065 | ||
| ... | @@ -1009,6 +1074,10 @@ const saveIdentityInfo = async () => { | ... | @@ -1009,6 +1074,10 @@ const saveIdentityInfo = async () => { |
| 1009 | idcard_2_img: tempIdentityInfo.value.idcard_2_img, | 1074 | idcard_2_img: tempIdentityInfo.value.idcard_2_img, |
| 1010 | idcard_effect_begin: tempIdentityInfo.value.idcard_effect_begin, | 1075 | idcard_effect_begin: tempIdentityInfo.value.idcard_effect_begin, |
| 1011 | idcard_effect_end: tempIdentityInfo.value.idcard_effect_end, | 1076 | idcard_effect_end: tempIdentityInfo.value.idcard_effect_end, |
| 1077 | + // 身份证地址字段 | ||
| 1078 | + idcard_province: tempIdentityInfo.value.idcard_province, | ||
| 1079 | + idcard_city: tempIdentityInfo.value.idcard_city, | ||
| 1080 | + idcard_district: tempIdentityInfo.value.idcard_district, | ||
| 1012 | idcard_address: tempIdentityInfo.value.idcard_address | 1081 | idcard_address: tempIdentityInfo.value.idcard_address |
| 1013 | }); | 1082 | }); |
| 1014 | 1083 | ||
| ... | @@ -1041,6 +1110,32 @@ const goBack = () => { | ... | @@ -1041,6 +1110,32 @@ const goBack = () => { |
| 1041 | url: '/pages/sell/index' | 1110 | url: '/pages/sell/index' |
| 1042 | }); | 1111 | }); |
| 1043 | }; | 1112 | }; |
| 1113 | + | ||
| 1114 | +// 地址选择相关状态 | ||
| 1115 | +const addressData = ref({ | ||
| 1116 | + province: '', | ||
| 1117 | + city: '', | ||
| 1118 | + county: '', | ||
| 1119 | + detail_address: '', | ||
| 1120 | + full_address: '' | ||
| 1121 | +}) | ||
| 1122 | + | ||
| 1123 | +/** | ||
| 1124 | + * 地址变化处理回调 | ||
| 1125 | + * @param {Object} address - 地址数据对象 | ||
| 1126 | + */ | ||
| 1127 | +const onAddressChange = (address) => { | ||
| 1128 | + // 更新临时身份信息中的地址字段(中文名称,用于显示) | ||
| 1129 | + tempIdentityInfo.value.province = address.province | ||
| 1130 | + tempIdentityInfo.value.city = address.city | ||
| 1131 | + tempIdentityInfo.value.county = address.county | ||
| 1132 | + | ||
| 1133 | + // 更新身份证地址字段(真实字段名,用于提交) | ||
| 1134 | + tempIdentityInfo.value.idcard_province = address.province_code || address.province | ||
| 1135 | + tempIdentityInfo.value.idcard_city = address.city_code || address.city | ||
| 1136 | + tempIdentityInfo.value.idcard_district = address.county_code || address.county | ||
| 1137 | + tempIdentityInfo.value.idcard_address = address.detail_address | ||
| 1138 | +} | ||
| 1044 | </script> | 1139 | </script> |
| 1045 | 1140 | ||
| 1046 | <script> | 1141 | <script> | ... | ... |
| 1 | /* | 1 | /* |
| 2 | * @Date: 2025-01-08 18:00:00 | 2 | * @Date: 2025-01-08 18:00:00 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-08-07 21:50:44 | 4 | + * @LastEditTime: 2025-08-12 16:36:02 |
| 5 | * @FilePath: /jgdl/src/stores/user.js | 5 | * @FilePath: /jgdl/src/stores/user.js |
| 6 | * @Description: 用户状态管理 | 6 | * @Description: 用户状态管理 |
| 7 | */ | 7 | */ |
| ... | @@ -34,6 +34,10 @@ export const useUserStore = defineStore('user', { | ... | @@ -34,6 +34,10 @@ export const useUserStore = defineStore('user', { |
| 34 | idcard_2_img: '', | 34 | idcard_2_img: '', |
| 35 | idcard_effect_begin: '', | 35 | idcard_effect_begin: '', |
| 36 | idcard_effect_end: '', | 36 | idcard_effect_end: '', |
| 37 | + // 身份证地址字段 | ||
| 38 | + idcard_province: '', | ||
| 39 | + idcard_city: '', | ||
| 40 | + idcard_district: '', | ||
| 37 | idcard_address: '', | 41 | idcard_address: '', |
| 38 | division_agree_pic: '', | 42 | division_agree_pic: '', |
| 39 | }, | 43 | }, |
| ... | @@ -55,16 +59,19 @@ export const useUserStore = defineStore('user', { | ... | @@ -55,16 +59,19 @@ export const useUserStore = defineStore('user', { |
| 55 | */ | 59 | */ |
| 56 | hasCompleteCollectionInfo: (state) => { | 60 | hasCompleteCollectionInfo: (state) => { |
| 57 | return !!( | 61 | return !!( |
| 58 | - state.userInfo.bank_id && state.userInfo.bank_id && | 62 | + state.userInfo.bank_id && |
| 59 | - state.userInfo.bank_no && state.userInfo.bank_no && | 63 | + state.userInfo.bank_no && |
| 60 | - state.userInfo.bank_img && state.userInfo.bank_img && | 64 | + state.userInfo.bank_img && |
| 61 | - state.userInfo.idcard_1_img && state.userInfo.idcard_1_img && | 65 | + state.userInfo.idcard_1_img && |
| 62 | - state.userInfo.idcard_2_img && state.userInfo.idcard_2_img && | 66 | + state.userInfo.idcard_2_img && |
| 63 | - state.userInfo.name && state.userInfo.name && | 67 | + state.userInfo.name && |
| 64 | - state.userInfo.idcard && state.userInfo.idcard && | 68 | + state.userInfo.idcard && |
| 65 | - state.userInfo.idcard_address && state.userInfo.idcard_address && | 69 | + state.userInfo.idcard_province && |
| 66 | - state.userInfo.idcard_effect_begin && state.userInfo.idcard_effect_begin && | 70 | + state.userInfo.idcard_city && |
| 67 | - state.userInfo.idcard_effect_end && state.userInfo.idcard_effect_end | 71 | + state.userInfo.idcard_district && |
| 72 | + state.userInfo.idcard_address && | ||
| 73 | + state.userInfo.idcard_effect_begin && | ||
| 74 | + state.userInfo.idcard_effect_end | ||
| 68 | ) | 75 | ) |
| 69 | }, | 76 | }, |
| 70 | 77 | ||
| ... | @@ -142,6 +149,10 @@ export const useUserStore = defineStore('user', { | ... | @@ -142,6 +149,10 @@ export const useUserStore = defineStore('user', { |
| 142 | idcard_2_img: '', | 149 | idcard_2_img: '', |
| 143 | idcard_effect_begin: '', | 150 | idcard_effect_begin: '', |
| 144 | idcard_effect_end: '', | 151 | idcard_effect_end: '', |
| 152 | + // 身份证地址字段 | ||
| 153 | + idcard_province: '', | ||
| 154 | + idcard_city: '', | ||
| 155 | + idcard_district: '', | ||
| 145 | idcard_address: '', | 156 | idcard_address: '', |
| 146 | division_agree_pic: '', | 157 | division_agree_pic: '', |
| 147 | } | 158 | } | ... | ... |
-
Please register or login to post a comment