feat(认证车源): 重构认证车源页面并优化提交成功提示
重构认证车源页面,使用API获取真实数据替代模拟数据 优化表单提交成功后的提示交互,区分编辑模式和申请模式 移除模拟数据加载逻辑,改为分页加载API数据
Showing
2 changed files
with
100 additions
and
112 deletions
| 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-07-10 21:34:57 | 4 | + * @LastEditTime: 2025-07-10 22:49:42 |
| 5 | * @FilePath: /jgdl/src/pages/authCar/index.vue | 5 | * @FilePath: /jgdl/src/pages/authCar/index.vue |
| 6 | * @Description: 认证车源 | 6 | * @Description: 认证车源 |
| 7 | --> | 7 | --> |
| ... | @@ -19,13 +19,7 @@ | ... | @@ -19,13 +19,7 @@ |
| 19 | 19 | ||
| 20 | <!-- 我要认证按钮 --> | 20 | <!-- 我要认证按钮 --> |
| 21 | <view class="px-16 mt-5"> | 21 | <view class="px-16 mt-5"> |
| 22 | - <nut-button | 22 | + <nut-button color="#FFA135" size="large" block @click="handleAuth" class="auth-button"> |
| 23 | - color="#FFA135" | ||
| 24 | - size="large" | ||
| 25 | - block | ||
| 26 | - @click="handleAuth" | ||
| 27 | - class="auth-button" | ||
| 28 | - > | ||
| 29 | <view class="flex items-center justify-center"> | 23 | <view class="flex items-center justify-center"> |
| 30 | <Check class="mr-2" size="20" color="#ffffff" /> | 24 | <Check class="mr-2" size="20" color="#ffffff" /> |
| 31 | <text class="text-white font-medium">我要认证</text> | 25 | <text class="text-white font-medium">我要认证</text> |
| ... | @@ -40,23 +34,14 @@ | ... | @@ -40,23 +34,14 @@ |
| 40 | </view> | 34 | </view> |
| 41 | 35 | ||
| 42 | <!-- 滚动列表 --> | 36 | <!-- 滚动列表 --> |
| 43 | - <scroll-view | 37 | + <scroll-view class="auth-car-list" :style="scrollStyle" :scroll-y="true" @scrolltolower="loadMore" |
| 44 | - class="auth-car-list" | 38 | + @scroll="scroll" :lower-threshold="50" :enable-flex="false"> |
| 45 | - :style="scrollStyle" | ||
| 46 | - :scroll-y="true" | ||
| 47 | - @scrolltolower="loadMore" | ||
| 48 | - @scroll="scroll" | ||
| 49 | - :lower-threshold="50" | ||
| 50 | - :enable-flex="false" | ||
| 51 | - > | ||
| 52 | <view class="space-y-4"> | 39 | <view class="space-y-4"> |
| 53 | - <view v-for="car in authCars" :key="car.id" | 40 | + <view v-for="car in authCars" :key="car.id" class="bg-white rounded-lg shadow-sm overflow-hidden mb-3" |
| 54 | - class="bg-white rounded-lg shadow-sm overflow-hidden mb-3" | 41 | + @tap="() => onCarClick(car)"> |
| 55 | - @tap="() => onCarClick(car)" | ||
| 56 | - > | ||
| 57 | <view class="flex"> | 42 | <view class="flex"> |
| 58 | <view class="w-32 h-24 relative p-2"> | 43 | <view class="w-32 h-24 relative p-2"> |
| 59 | - <image :src="car.imageUrl" :alt="car.name" mode="aspectFill" | 44 | + <image :src="car.front_photo" :alt="car.name" mode="aspectFill" |
| 60 | class="w-full h-full object-cover rounded-lg" /> | 45 | class="w-full h-full object-cover rounded-lg" /> |
| 61 | </view> | 46 | </view> |
| 62 | <view class="flex-1 p-3 relative"> | 47 | <view class="flex-1 p-3 relative"> |
| ... | @@ -64,17 +49,17 @@ | ... | @@ -64,17 +49,17 @@ |
| 64 | <Heart1 v-if="!car.is_favorite" size="22" :color="'#9ca3af'" /> | 49 | <Heart1 v-if="!car.is_favorite" size="22" :color="'#9ca3af'" /> |
| 65 | <HeartFill v-else size="22" :color="'#ef4444'" /> | 50 | <HeartFill v-else size="22" :color="'#ef4444'" /> |
| 66 | </view> | 51 | </view> |
| 67 | - <text class="font-medium text-sm block">{{ car.name }}</text> | 52 | + <text class="font-medium text-sm block">{{ car.brand }} {{ car.model }}</text> |
| 68 | <text class="text-xs text-gray-600 mt-1 block"> | 53 | <text class="text-xs text-gray-600 mt-1 block"> |
| 69 | - {{ car.year }} · | 54 | + {{ car.manufacture_year }} · |
| 70 | - <text v-if="car.batteryHealth">电池健康度{{ car.batteryHealth }}%</text> | 55 | + <text v-if="car.range_km">续航{{ car.range_km }}km</text> |
| 71 | - <text v-if="car.mileage"> 行驶{{ car.mileage }}公里</text> | 56 | + <text v-if="car.max_speed_kmh"> 最高时速{{ car.max_speed_kmh }}km/h</text> |
| 72 | </text> | 57 | </text> |
| 73 | <view class="mt-2"> | 58 | <view class="mt-2"> |
| 74 | <text class="text-orange-500 font-bold" style="font-size: 1.2rem;"> | 59 | <text class="text-orange-500 font-bold" style="font-size: 1.2rem;"> |
| 75 | ¥{{ car.price.toLocaleString() }} | 60 | ¥{{ car.price.toLocaleString() }} |
| 76 | </text> | 61 | </text> |
| 77 | - <text class="text-xs text-gray-500 mt-1 block">{{ car.school }}</text> | 62 | + <text class="text-xs text-gray-500 mt-1 block">{{ car.school_name }}</text> |
| 78 | </view> | 63 | </view> |
| 79 | <!-- 认证信息 --> | 64 | <!-- 认证信息 --> |
| 80 | <!-- <view class="mt-1"> | 65 | <!-- <view class="mt-1"> |
| ... | @@ -98,11 +83,7 @@ | ... | @@ -98,11 +83,7 @@ |
| 98 | </view> | 83 | </view> |
| 99 | 84 | ||
| 100 | <!-- 成功提示 --> | 85 | <!-- 成功提示 --> |
| 101 | - <nut-toast | 86 | + <nut-toast v-model:visible="toastVisible" :msg="toastMessage" :type="toastType" /> |
| 102 | - v-model:visible="toastVisible" | ||
| 103 | - :msg="toastMessage" | ||
| 104 | - :type="toastType" | ||
| 105 | - /> | ||
| 106 | </view> | 87 | </view> |
| 107 | </template> | 88 | </template> |
| 108 | 89 | ||
| ... | @@ -112,6 +93,8 @@ import { ref, computed, onMounted } from 'vue' | ... | @@ -112,6 +93,8 @@ import { ref, computed, onMounted } from 'vue' |
| 112 | import { Check, Heart1, HeartFill } from '@nutui/icons-vue-taro' | 93 | import { Check, Heart1, HeartFill } from '@nutui/icons-vue-taro' |
| 113 | import { useFavorite } from '@/composables/useFavorite' | 94 | import { useFavorite } from '@/composables/useFavorite' |
| 114 | import './index.less' | 95 | import './index.less' |
| 96 | +// 接口导入 | ||
| 97 | +import { getVehicleListAPI } from '@/api/car'; | ||
| 115 | 98 | ||
| 116 | // Banner图片数据 | 99 | // Banner图片数据 |
| 117 | const bannerImages = ref([ | 100 | const bannerImages = ref([ |
| ... | @@ -121,52 +104,7 @@ const bannerImages = ref([ | ... | @@ -121,52 +104,7 @@ const bannerImages = ref([ |
| 121 | ]) | 104 | ]) |
| 122 | 105 | ||
| 123 | // 认证车源数据 | 106 | // 认证车源数据 |
| 124 | -const authCars = ref([ | 107 | +const authCars = ref([]) |
| 125 | - { | ||
| 126 | - id: 1, | ||
| 127 | - name: '小牛NGT 电动车', | ||
| 128 | - year: '2023年', | ||
| 129 | - batteryHealth: 95, | ||
| 130 | - mileage: 1200, | ||
| 131 | - price: 4500, | ||
| 132 | - school: '上海理工大学', | ||
| 133 | - imageUrl: 'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&h=300&fit=crop', | ||
| 134 | - authDate: '2024-01-15' | ||
| 135 | - }, | ||
| 136 | - { | ||
| 137 | - id: 2, | ||
| 138 | - name: '雅迪 DE2 电动车', | ||
| 139 | - year: '2023年', | ||
| 140 | - batteryHealth: 88, | ||
| 141 | - mileage: 2800, | ||
| 142 | - price: 3200, | ||
| 143 | - school: '上海大学', | ||
| 144 | - imageUrl: 'https://images.unsplash.com/photo-1571068316344-75bc76f77890?w=400&h=300&fit=crop', | ||
| 145 | - authDate: '2024-01-10' | ||
| 146 | - }, | ||
| 147 | - { | ||
| 148 | - id: 3, | ||
| 149 | - name: '爱玛 A500 电动车', | ||
| 150 | - year: '2022年', | ||
| 151 | - batteryHealth: 92, | ||
| 152 | - mileage: 1800, | ||
| 153 | - price: 2800, | ||
| 154 | - school: '华东理工大学', | ||
| 155 | - imageUrl: 'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&h=300&fit=crop', | ||
| 156 | - authDate: '2024-01-08' | ||
| 157 | - }, | ||
| 158 | - { | ||
| 159 | - id: 4, | ||
| 160 | - name: '台铃 TDR-2023 电动车', | ||
| 161 | - year: '2023年', | ||
| 162 | - batteryHealth: 90, | ||
| 163 | - mileage: 2200, | ||
| 164 | - price: 3800, | ||
| 165 | - school: '上海交通大学', | ||
| 166 | - imageUrl: 'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&h=300&fit=crop', | ||
| 167 | - authDate: '2024-01-05' | ||
| 168 | - } | ||
| 169 | -]) | ||
| 170 | 108 | ||
| 171 | // 收藏状态 | 109 | // 收藏状态 |
| 172 | // 收藏功能现在使用基于对象属性的模式 | 110 | // 收藏功能现在使用基于对象属性的模式 |
| ... | @@ -174,6 +112,8 @@ const authCars = ref([ | ... | @@ -174,6 +112,8 @@ const authCars = ref([ |
| 174 | // 加载状态 | 112 | // 加载状态 |
| 175 | const loading = ref(false) | 113 | const loading = ref(false) |
| 176 | const hasMore = ref(true) | 114 | const hasMore = ref(true) |
| 115 | +const currentPage = ref(0) | ||
| 116 | +const pageSize = ref(10) | ||
| 177 | 117 | ||
| 178 | // Toast提示 | 118 | // Toast提示 |
| 179 | const toastVisible = ref(false) | 119 | const toastVisible = ref(false) |
| ... | @@ -210,37 +150,61 @@ const onCarClick = (car) => { | ... | @@ -210,37 +150,61 @@ const onCarClick = (car) => { |
| 210 | const { toggleFavorite } = useFavorite() | 150 | const { toggleFavorite } = useFavorite() |
| 211 | 151 | ||
| 212 | /** | 152 | /** |
| 213 | - * 加载更多数据 | 153 | + * 加载认证车辆数据 |
| 154 | + * @param {boolean} isLoadMore - 是否为加载更多 | ||
| 214 | */ | 155 | */ |
| 215 | -const loadMore = () => { | 156 | +const loadAuthCarData = async (isLoadMore = false) => { |
| 216 | - if (loading.value || !hasMore.value) return | 157 | + if (loading.value) return |
| 217 | 158 | ||
| 218 | loading.value = true | 159 | loading.value = true |
| 219 | 160 | ||
| 220 | - // 模拟加载更多数据 | 161 | + try { |
| 221 | - setTimeout(() => { | 162 | + // 构建请求参数 - 只获取认证车辆 |
| 222 | - const newCars = [ | 163 | + const params = { |
| 223 | - { | 164 | + page: currentPage.value, |
| 224 | - id: authCars.value.length + 1, | 165 | + limit: pageSize.value, |
| 225 | - name: '新增认证车辆', | 166 | + verification_status: 1 // 只获取已认证的车辆 |
| 226 | - year: '2023年', | ||
| 227 | - batteryHealth: 85, | ||
| 228 | - mileage: 3000, | ||
| 229 | - price: 2500, | ||
| 230 | - school: '复旦大学', | ||
| 231 | - imageUrl: 'https://images.unsplash.com/photo-1571068316344-75bc76f77890?w=400&h=300&fit=crop', | ||
| 232 | - authDate: '2024-01-01' | ||
| 233 | } | 167 | } |
| 234 | - ] | ||
| 235 | 168 | ||
| 236 | - authCars.value.push(...newCars) | 169 | + const response = await getVehicleListAPI(params) |
| 237 | - loading.value = false | ||
| 238 | 170 | ||
| 239 | - // 模拟没有更多数据 | 171 | + if (response && response.code === 1 && response.data) { |
| 240 | - if (authCars.value.length >= 10) { | 172 | + const vehicleList = response.data.list || [] |
| 241 | - hasMore.value = false | 173 | + |
| 174 | + // 处理图片数据 | ||
| 175 | + const processedData = vehicleList.map(item => ({ | ||
| 176 | + ...item, | ||
| 177 | + })) | ||
| 178 | + | ||
| 179 | + if (isLoadMore) { | ||
| 180 | + authCars.value.push(...processedData) | ||
| 181 | + } else { | ||
| 182 | + authCars.value = processedData | ||
| 183 | + } | ||
| 184 | + | ||
| 185 | + // 检查是否还有更多数据 - 基于总数和当前已加载数量 | ||
| 186 | + const totalLoaded = (currentPage.value + 1) * pageSize.value | ||
| 187 | + hasMore.value = totalLoaded < response.data.total | ||
| 188 | + } else { | ||
| 189 | + console.error('API返回错误:', response) | ||
| 190 | + showToast(response?.msg || '获取数据失败', 'error') | ||
| 191 | + } | ||
| 192 | + } catch (error) { | ||
| 193 | + console.error('加载认证车辆数据失败:', error) | ||
| 194 | + showToast('网络错误,请稍后重试', 'error') | ||
| 195 | + } finally { | ||
| 196 | + loading.value = false | ||
| 242 | } | 197 | } |
| 243 | - }, 1000) | 198 | +} |
| 199 | + | ||
| 200 | +/** | ||
| 201 | + * 加载更多数据 | ||
| 202 | + */ | ||
| 203 | +const loadMore = async () => { | ||
| 204 | + if (loading.value || !hasMore.value) return | ||
| 205 | + | ||
| 206 | + currentPage.value++ | ||
| 207 | + await loadAuthCarData(true) | ||
| 244 | } | 208 | } |
| 245 | 209 | ||
| 246 | /** | 210 | /** |
| ... | @@ -253,16 +217,21 @@ const scroll = (e) => { | ... | @@ -253,16 +217,21 @@ const scroll = (e) => { |
| 253 | /** | 217 | /** |
| 254 | * 显示提示信息 | 218 | * 显示提示信息 |
| 255 | */ | 219 | */ |
| 256 | -// showToast 功能现在由 useFavorite composable 处理 | 220 | +const showToast = (message, type = 'success') => { |
| 221 | + toastMessage.value = message | ||
| 222 | + toastType.value = type | ||
| 223 | + toastVisible.value = true | ||
| 224 | +} | ||
| 257 | 225 | ||
| 258 | // 初始化 | 226 | // 初始化 |
| 259 | -onMounted(() => { | 227 | +onMounted(async () => { |
| 260 | - // 可以在这里加载初始数据 | 228 | + // 加载初始认证车辆数据 |
| 229 | + await loadAuthCarData() | ||
| 261 | }) | 230 | }) |
| 262 | </script> | 231 | </script> |
| 263 | 232 | ||
| 264 | <script> | 233 | <script> |
| 265 | export default { | 234 | export default { |
| 266 | -以在这里加载: 'AuthCarPage' | 235 | + name: 'AuthCarPage' |
| 267 | } | 236 | } |
| 268 | </script> | 237 | </script> | ... | ... |
| 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-07-10 17:24:11 | 4 | + * @LastEditTime: 2025-07-10 22:59:16 |
| 5 | * @FilePath: /jgdl/src/pages/setAuthCar/index.vue | 5 | * @FilePath: /jgdl/src/pages/setAuthCar/index.vue |
| 6 | * @Description: 申请认证 | 6 | * @Description: 申请认证 |
| 7 | --> | 7 | --> |
| ... | @@ -424,16 +424,35 @@ const onSubmit = async () => { | ... | @@ -424,16 +424,35 @@ const onSubmit = async () => { |
| 424 | } | 424 | } |
| 425 | 425 | ||
| 426 | Taro.hideLoading() | 426 | Taro.hideLoading() |
| 427 | - const successTitle = isEditMode.value ? '保存成功' : '申请提交成功' | 427 | + // 提交成功提示 |
| 428 | + if (isEditMode.value) { | ||
| 428 | Taro.showToast({ | 429 | Taro.showToast({ |
| 429 | - title: successTitle, | 430 | + title: '保存成功', |
| 430 | - icon: 'success' | 431 | + icon: 'success', |
| 432 | + duration: 2000, | ||
| 433 | + complete: () => { | ||
| 434 | + // 保存成功后返回上一页 | ||
| 435 | + Taro.navigateBack() | ||
| 436 | + } | ||
| 437 | + }) | ||
| 438 | + } else { | ||
| 439 | + Taro.showModal({ | ||
| 440 | + title: '温馨提示', | ||
| 441 | + content: '认证提交成功,我们会尽快审核', | ||
| 442 | + showCancel: false, | ||
| 443 | + confirmText: '知道了', | ||
| 444 | + success: function (res) { | ||
| 445 | + if (res.confirm) { | ||
| 446 | + Taro.navigateBack() | ||
| 447 | + } | ||
| 448 | + } | ||
| 431 | }) | 449 | }) |
| 450 | + } | ||
| 432 | 451 | ||
| 433 | // 返回上一页 | 452 | // 返回上一页 |
| 434 | - setTimeout(() => { | 453 | + // setTimeout(() => { |
| 435 | - Taro.navigateBack() | 454 | + // Taro.navigateBack() |
| 436 | - }, 1500) | 455 | + // }, 1500) |
| 437 | } catch (error) { | 456 | } catch (error) { |
| 438 | console.error('提交失败:', error) | 457 | console.error('提交失败:', error) |
| 439 | Taro.hideLoading() | 458 | Taro.hideLoading() | ... | ... |
-
Please register or login to post a comment