feat(myAuthCar): 添加我的认证车页面功能
添加我的认证车页面,包括页面配置、路由注册、样式和主要功能实现 - 创建认证车页面组件和样式文件 - 在app.config中注册新页面路由 - 修改个人中心页面跳转逻辑 - 实现认证车列表展示、加载更多和空状态 - 添加模拟数据和API服务占位
Showing
5 changed files
with
437 additions
and
5 deletions
| 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-03 12:59:10 | 4 | + * @LastEditTime: 2025-07-03 18:09:07 |
| 5 | * @FilePath: /jgdl/src/app.config.js | 5 | * @FilePath: /jgdl/src/app.config.js |
| 6 | * @Description: 文件描述 | 6 | * @Description: 文件描述 |
| 7 | */ | 7 | */ |
| ... | @@ -23,6 +23,7 @@ export default { | ... | @@ -23,6 +23,7 @@ export default { |
| 23 | 'pages/myFavorites/index', | 23 | 'pages/myFavorites/index', |
| 24 | 'pages/myCar/index', | 24 | 'pages/myCar/index', |
| 25 | 'pages/myOrders/index', | 25 | 'pages/myOrders/index', |
| 26 | + 'pages/myAuthCar/index', | ||
| 26 | ], | 27 | ], |
| 27 | subpackages: [ // 配置在tabBar中的页面不能分包写到subpackages中去 | 28 | subpackages: [ // 配置在tabBar中的页面不能分包写到subpackages中去 |
| 28 | { | 29 | { | ... | ... |
src/pages/myAuthCar/index.config.js
0 → 100755
| 1 | +/* | ||
| 2 | + * @Date: 2025-07-03 18:08:31 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2025-07-03 18:08:57 | ||
| 5 | + * @FilePath: /jgdl/src/pages/myAuthCar/index.config.js | ||
| 6 | + * @Description: 文件描述 | ||
| 7 | + */ | ||
| 8 | +export default { | ||
| 9 | + navigationBarTitleText: '我的认证车', | ||
| 10 | + usingComponents: { | ||
| 11 | + }, | ||
| 12 | +} |
src/pages/myAuthCar/index.less
0 → 100644
| 1 | +/* 我的认证车页面样式 */ | ||
| 2 | +.auth-car-list { | ||
| 3 | + background-color: #f8f9fa; | ||
| 4 | +} | ||
| 5 | + | ||
| 6 | +/* 车辆卡片样式 */ | ||
| 7 | +.car-item { | ||
| 8 | + background: white; | ||
| 9 | + border-radius: 12rpx; | ||
| 10 | + margin: 16rpx; | ||
| 11 | + padding: 24rpx; | ||
| 12 | + box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); | ||
| 13 | +} | ||
| 14 | + | ||
| 15 | +/* 按钮样式优化 */ | ||
| 16 | +.sell-button { | ||
| 17 | + background: linear-gradient(135deg, #ff6b6b, #ff8e8e); | ||
| 18 | + border: none; | ||
| 19 | + color: white; | ||
| 20 | + font-weight: 500; | ||
| 21 | +} | ||
| 22 | + | ||
| 23 | +/* 价格样式 */ | ||
| 24 | +.price-text { | ||
| 25 | + font-size: 32rpx; | ||
| 26 | + font-weight: bold; | ||
| 27 | + color: #ff6b35; | ||
| 28 | +} | ||
| 29 | + | ||
| 30 | +.original-price { | ||
| 31 | + font-size: 24rpx; | ||
| 32 | + color: #999; | ||
| 33 | + text-decoration: line-through; | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | +/* 空状态样式 */ | ||
| 37 | +.empty-state { | ||
| 38 | + padding: 120rpx 0; | ||
| 39 | + text-align: center; | ||
| 40 | + color: #999; | ||
| 41 | + font-size: 28rpx; | ||
| 42 | +} |
src/pages/myAuthCar/index.vue
0 → 100644
| 1 | +<!-- | ||
| 2 | + * @Date: 2022-09-19 14:11:06 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2025-07-03 13:25:46 | ||
| 5 | + * @FilePath: /jgdl/src/pages/myAuthCar/index.vue | ||
| 6 | + * @Description: 我的认证车页面 | ||
| 7 | +--> | ||
| 8 | +<template> | ||
| 9 | + <view class="flex flex-col bg-white min-h-screen"> | ||
| 10 | + <!-- Auth Car List --> | ||
| 11 | + <view class="flex-1"> | ||
| 12 | + <!-- 滚动列表 --> | ||
| 13 | + <scroll-view | ||
| 14 | + class="auth-car-list" | ||
| 15 | + :style="scrollStyle" | ||
| 16 | + :scroll-y="true" | ||
| 17 | + @scrolltolower="loadMore" | ||
| 18 | + @scroll="scroll" | ||
| 19 | + :lower-threshold="50" | ||
| 20 | + :enable-flex="false" | ||
| 21 | + > | ||
| 22 | + <view class="space-y-4"> | ||
| 23 | + <view | ||
| 24 | + v-for="item in authCars" | ||
| 25 | + :key="item.id" | ||
| 26 | + @tap="() => onItemClick(item)" | ||
| 27 | + style="border-bottom: 1px solid #e5e7eb" | ||
| 28 | + > | ||
| 29 | + <view class="flex p-4"> | ||
| 30 | + <view class="w-24 h-24 relative"> | ||
| 31 | + <image | ||
| 32 | + :src="item.imageUrl" | ||
| 33 | + :alt="item.name" | ||
| 34 | + mode="aspectFill" | ||
| 35 | + class="w-full h-full object-cover rounded-lg" | ||
| 36 | + /> | ||
| 37 | + </view> | ||
| 38 | + <view class="flex-1 ml-4"> | ||
| 39 | + <text class="font-medium text-base block">{{ item.name }}</text> | ||
| 40 | + <text class="text-sm text-gray-500 mt-1 block">{{ item.details }}</text> | ||
| 41 | + <view class="mt-2 flex justify-between items-center"> | ||
| 42 | + <view> | ||
| 43 | + <text class="text-orange-500 font-bold"> | ||
| 44 | + ¥{{ item.price.toLocaleString() }} | ||
| 45 | + </text> | ||
| 46 | + <text class="text-gray-400 text-xs line-through ml-2"> | ||
| 47 | + ¥{{ item.originalPrice.toLocaleString() }} | ||
| 48 | + </text> | ||
| 49 | + </view> | ||
| 50 | + <nut-button | ||
| 51 | + @click.stop="handleSellClick(item.id)" | ||
| 52 | + size="small" | ||
| 53 | + type="primary" | ||
| 54 | + class="px-3 py-1 rounded-full text-sm" | ||
| 55 | + > | ||
| 56 | + 我要卖车 | ||
| 57 | + </nut-button> | ||
| 58 | + </view> | ||
| 59 | + </view> | ||
| 60 | + </view> | ||
| 61 | + </view> | ||
| 62 | + </view> | ||
| 63 | + | ||
| 64 | + <!-- Loading indicator --> | ||
| 65 | + <view v-if="loading" class="loading-container py-4 text-center"> | ||
| 66 | + <text class="loading-text text-gray-500">加载中...</text> | ||
| 67 | + </view> | ||
| 68 | + | ||
| 69 | + <!-- 没有更多数据 --> | ||
| 70 | + <view v-if="!hasMore && authCars.length > 0" class="no-more-container py-4 text-center"> | ||
| 71 | + <text class="text-gray-400 text-sm">没有更多数据了</text> | ||
| 72 | + </view> | ||
| 73 | + | ||
| 74 | + <!-- Empty State --> | ||
| 75 | + <view | ||
| 76 | + v-if="authCars.length === 0 && !loading" | ||
| 77 | + class="flex flex-col items-center justify-center h-64" | ||
| 78 | + > | ||
| 79 | + <text class="text-gray-500">暂无认证车辆</text> | ||
| 80 | + </view> | ||
| 81 | + </scroll-view> | ||
| 82 | + </view> | ||
| 83 | + | ||
| 84 | + <!-- 成功提示 --> | ||
| 85 | + <nut-toast | ||
| 86 | + v-model:visible="toastVisible" | ||
| 87 | + :msg="toastMessage" | ||
| 88 | + :type="toastType" | ||
| 89 | + /> | ||
| 90 | + </view> | ||
| 91 | +</template> | ||
| 92 | + | ||
| 93 | +<script setup> | ||
| 94 | +import { ref, computed, onMounted } from 'vue' | ||
| 95 | +import Taro from '@tarojs/taro' | ||
| 96 | +import './index.less' | ||
| 97 | + | ||
| 98 | +// ==================== API相关 ==================== | ||
| 99 | +/** | ||
| 100 | + * API服务 - 为真实API预留空间 | ||
| 101 | + */ | ||
| 102 | +const apiService = { | ||
| 103 | + /** | ||
| 104 | + * 获取我的认证车列表 | ||
| 105 | + * @param {number} page - 页码 | ||
| 106 | + * @param {number} pageSize - 每页数量 | ||
| 107 | + * @returns {Promise} API响应 | ||
| 108 | + */ | ||
| 109 | + async getAuthCarsList(page = 1, pageSize = 10) { | ||
| 110 | + // TODO: 替换为真实API调用 | ||
| 111 | + // return await request.get('/api/auth-cars', { page, pageSize }) | ||
| 112 | + | ||
| 113 | + // 模拟API延迟 | ||
| 114 | + await new Promise(resolve => setTimeout(resolve, 800 + Math.random() * 400)) | ||
| 115 | + | ||
| 116 | + // 模拟API响应数据 | ||
| 117 | + return { | ||
| 118 | + code: 200, | ||
| 119 | + data: { | ||
| 120 | + list: generateMockData(page, pageSize), | ||
| 121 | + total: 50, // 模拟总数 | ||
| 122 | + hasMore: page < 5 // 模拟是否还有更多数据 | ||
| 123 | + }, | ||
| 124 | + message: 'success' | ||
| 125 | + } | ||
| 126 | + } | ||
| 127 | +} | ||
| 128 | + | ||
| 129 | +// ==================== 响应式数据 ==================== | ||
| 130 | +/** | ||
| 131 | + * 我的认证车列表数据 | ||
| 132 | + */ | ||
| 133 | +const authCars = ref([]) | ||
| 134 | + | ||
| 135 | +/** | ||
| 136 | + * 加载状态 | ||
| 137 | + */ | ||
| 138 | +const loading = ref(false) | ||
| 139 | +const hasMore = ref(true) | ||
| 140 | +const currentPage = ref(1) | ||
| 141 | +const pageSize = ref(10) | ||
| 142 | + | ||
| 143 | +/** | ||
| 144 | + * Toast提示 | ||
| 145 | + */ | ||
| 146 | +const toastVisible = ref(false) | ||
| 147 | +const toastMessage = ref('') | ||
| 148 | +const toastType = ref('success') | ||
| 149 | + | ||
| 150 | +/** | ||
| 151 | + * 滚动样式 - 考虑header和TabBar的高度 | ||
| 152 | + */ | ||
| 153 | +const scrollStyle = computed(() => { | ||
| 154 | + return { | ||
| 155 | + height: 'calc(100vh)' // 减去header和TabBar的高度 | ||
| 156 | + } | ||
| 157 | +}) | ||
| 158 | + | ||
| 159 | +// ==================== 数据处理方法 ==================== | ||
| 160 | +/** | ||
| 161 | + * 生成模拟数据 | ||
| 162 | + * @param {number} page - 页码 | ||
| 163 | + * @param {number} size - 每页数量 | ||
| 164 | + * @returns {Array} 模拟数据数组 | ||
| 165 | + */ | ||
| 166 | +const generateMockData = (page, size) => { | ||
| 167 | + const brands = ['小牛', '雅迪', '绿源', '爱玛', '台铃', '新日', '立马', '小鸟'] | ||
| 168 | + const models = ['豪华版', '标准版', '运动版', '经典版', '智能版', '动力版'] | ||
| 169 | + const images = [ | ||
| 170 | + 'https://images.unsplash.com/photo-1558981285-6f0c94958bb6?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60', | ||
| 171 | + 'https://images.unsplash.com/photo-1558981403-c5f9899a28bc?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60', | ||
| 172 | + 'https://images.unsplash.com/photo-1591637333184-19aa84b3e01f?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60', | ||
| 173 | + 'https://images.unsplash.com/photo-1558980664-3a031cf67ea8?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60', | ||
| 174 | + 'https://images.unsplash.com/photo-1567922045116-2a00fae2ed03?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60', | ||
| 175 | + 'https://images.unsplash.com/photo-1573981368236-719bbb6f70f7?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60' | ||
| 176 | + ] | ||
| 177 | + | ||
| 178 | + const data = [] | ||
| 179 | + for (let i = 0; i < size; i++) { | ||
| 180 | + const index = (page - 1) * size + i | ||
| 181 | + const brand = brands[Math.floor(Math.random() * brands.length)] | ||
| 182 | + const model = models[Math.floor(Math.random() * models.length)] | ||
| 183 | + const image = images[Math.floor(Math.random() * images.length)] | ||
| 184 | + const originalPrice = Math.floor(Math.random() * 3000) + 3000 | ||
| 185 | + const price = Math.floor(originalPrice * (0.7 + Math.random() * 0.2)) // 7-9折 | ||
| 186 | + const usageTime = Math.floor(Math.random() * 24) + 1 // 1-24个月 | ||
| 187 | + const range = Math.floor(Math.random() * 100) + 60 // 60-160km续航 | ||
| 188 | + | ||
| 189 | + data.push({ | ||
| 190 | + id: `auth_${index + 100}`, | ||
| 191 | + name: `${brand} ${model}`, | ||
| 192 | + details: `续航${range}km | 使用${usageTime}个月`, | ||
| 193 | + price: price, | ||
| 194 | + originalPrice: originalPrice, | ||
| 195 | + imageUrl: image, | ||
| 196 | + brand: brand, | ||
| 197 | + authTime: new Date(Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000).toISOString() // 最近30天内认证 | ||
| 198 | + }) | ||
| 199 | + } | ||
| 200 | + return data | ||
| 201 | +} | ||
| 202 | + | ||
| 203 | +/** | ||
| 204 | + * 初始化加载数据 | ||
| 205 | + */ | ||
| 206 | +const initData = async () => { | ||
| 207 | + loading.value = true | ||
| 208 | + try { | ||
| 209 | + const response = await apiService.getAuthCarsList(1, pageSize.value) | ||
| 210 | + if (response.code === 200) { | ||
| 211 | + authCars.value = response.data.list | ||
| 212 | + hasMore.value = response.data.hasMore | ||
| 213 | + currentPage.value = 1 | ||
| 214 | + } else { | ||
| 215 | + showToast('加载失败,请重试', 'error') | ||
| 216 | + } | ||
| 217 | + } catch (error) { | ||
| 218 | + console.error('加载我的认证车列表失败:', error) | ||
| 219 | + showToast('网络错误,请重试', 'error') | ||
| 220 | + } finally { | ||
| 221 | + loading.value = false | ||
| 222 | + } | ||
| 223 | +} | ||
| 224 | + | ||
| 225 | +/** | ||
| 226 | + * 加载更多数据 | ||
| 227 | + */ | ||
| 228 | +const loadMore = async () => { | ||
| 229 | + if (loading.value || !hasMore.value) return | ||
| 230 | + | ||
| 231 | + loading.value = true | ||
| 232 | + try { | ||
| 233 | + const nextPage = currentPage.value + 1 | ||
| 234 | + const response = await apiService.getAuthCarsList(nextPage, pageSize.value) | ||
| 235 | + | ||
| 236 | + if (response.code === 200) { | ||
| 237 | + authCars.value.push(...response.data.list) | ||
| 238 | + hasMore.value = response.data.hasMore | ||
| 239 | + currentPage.value = nextPage | ||
| 240 | + } else { | ||
| 241 | + showToast('加载失败,请重试', 'error') | ||
| 242 | + } | ||
| 243 | + } catch (error) { | ||
| 244 | + console.error('加载更多数据失败:', error) | ||
| 245 | + showToast('网络错误,请重试', 'error') | ||
| 246 | + } finally { | ||
| 247 | + loading.value = false | ||
| 248 | + } | ||
| 249 | +} | ||
| 250 | + | ||
| 251 | +// ==================== 事件处理方法 ==================== | ||
| 252 | +/** | ||
| 253 | + * 点击车辆项目 | ||
| 254 | + * @param {Object} item - 车辆信息 | ||
| 255 | + */ | ||
| 256 | +const onItemClick = (item) => { | ||
| 257 | + // TODO: 跳转到车辆详情页 | ||
| 258 | + Taro.navigateTo({ | ||
| 259 | + url: `/pages/productDetail/index?id=${item.id}` | ||
| 260 | + }) | ||
| 261 | +} | ||
| 262 | + | ||
| 263 | +/** | ||
| 264 | + * 处理我要卖车点击事件 | ||
| 265 | + * @param {string} carId - 车辆ID | ||
| 266 | + */ | ||
| 267 | +const handleSellClick = (carId) => { | ||
| 268 | + Taro.navigateTo({ | ||
| 269 | + url: `/pages/sell/index?id=${carId}&mode=edit` | ||
| 270 | + }) | ||
| 271 | +} | ||
| 272 | + | ||
| 273 | +/** | ||
| 274 | + * 滚动事件处理 | ||
| 275 | + */ | ||
| 276 | +const scroll = (e) => { | ||
| 277 | + // 可以在这里处理滚动事件,比如记录滚动位置 | ||
| 278 | +} | ||
| 279 | + | ||
| 280 | +/** | ||
| 281 | + * 显示提示信息 | ||
| 282 | + */ | ||
| 283 | +const showToast = (message, type = 'success') => { | ||
| 284 | + toastMessage.value = message | ||
| 285 | + toastType.value = type | ||
| 286 | + toastVisible.value = true | ||
| 287 | +} | ||
| 288 | + | ||
| 289 | +// ==================== 生命周期 ==================== | ||
| 290 | +onMounted(() => { | ||
| 291 | + initData() | ||
| 292 | +}) | ||
| 293 | +</script> | ||
| 294 | + | ||
| 295 | +<script> | ||
| 296 | +export default { | ||
| 297 | + name: "MyAuthCarPage", | ||
| 298 | +}; | ||
| 299 | +</script> | ||
| 300 | + | ||
| 301 | +<style lang="less" scoped> | ||
| 302 | +// 自定义样式 | ||
| 303 | +.object-cover { | ||
| 304 | + object-fit: cover; | ||
| 305 | +} | ||
| 306 | + | ||
| 307 | +.line-through { | ||
| 308 | + text-decoration: line-through; | ||
| 309 | +} | ||
| 310 | + | ||
| 311 | +.auth-car-list { | ||
| 312 | + .loading-container { | ||
| 313 | + display: flex; | ||
| 314 | + justify-content: center; | ||
| 315 | + align-items: center; | ||
| 316 | + | ||
| 317 | + .loading-text { | ||
| 318 | + font-size: 28rpx; | ||
| 319 | + color: #999; | ||
| 320 | + } | ||
| 321 | + } | ||
| 322 | + | ||
| 323 | + .no-more-container { | ||
| 324 | + display: flex; | ||
| 325 | + justify-content: center; | ||
| 326 | + align-items: center; | ||
| 327 | + padding: 32rpx 0; | ||
| 328 | + | ||
| 329 | + text { | ||
| 330 | + font-size: 24rpx; | ||
| 331 | + color: #ccc; | ||
| 332 | + } | ||
| 333 | + } | ||
| 334 | +} | ||
| 335 | + | ||
| 336 | +// 认证标签样式 | ||
| 337 | +.absolute { | ||
| 338 | + position: absolute; | ||
| 339 | +} | ||
| 340 | + | ||
| 341 | +.top-1 { | ||
| 342 | + top: 4rpx; | ||
| 343 | +} | ||
| 344 | + | ||
| 345 | +.right-1 { | ||
| 346 | + right: 4rpx; | ||
| 347 | +} | ||
| 348 | + | ||
| 349 | +.bg-green-500 { | ||
| 350 | + background-color: #10b981; | ||
| 351 | +} | ||
| 352 | + | ||
| 353 | +.text-white { | ||
| 354 | + color: white; | ||
| 355 | +} | ||
| 356 | + | ||
| 357 | +.text-xs { | ||
| 358 | + font-size: 20rpx; | ||
| 359 | +} | ||
| 360 | + | ||
| 361 | +.px-1 { | ||
| 362 | + padding-left: 4rpx; | ||
| 363 | + padding-right: 4rpx; | ||
| 364 | +} | ||
| 365 | + | ||
| 366 | +.rounded { | ||
| 367 | + border-radius: 8rpx; | ||
| 368 | +} | ||
| 369 | + | ||
| 370 | +.flex { | ||
| 371 | + display: flex; | ||
| 372 | +} | ||
| 373 | + | ||
| 374 | +.items-center { | ||
| 375 | + align-items: center; | ||
| 376 | +} | ||
| 377 | +</style> |
| ... | @@ -52,7 +52,7 @@ | ... | @@ -52,7 +52,7 @@ |
| 52 | <Right size="18" color="#9ca3af" /> | 52 | <Right size="18" color="#9ca3af" /> |
| 53 | </view> | 53 | </view> |
| 54 | 54 | ||
| 55 | - <view class="menu-item" @click="onSettings"> | 55 | + <view class="menu-item" @click="onMyAuthCar"> |
| 56 | <StarN size="20" color="#6b7280" /> | 56 | <StarN size="20" color="#6b7280" /> |
| 57 | <text class="menu-text">我的认证</text> | 57 | <text class="menu-text">我的认证</text> |
| 58 | <Right size="18" color="#9ca3af" /> | 58 | <Right size="18" color="#9ca3af" /> |
| ... | @@ -163,11 +163,11 @@ const onFeedback = () => { | ... | @@ -163,11 +163,11 @@ const onFeedback = () => { |
| 163 | } | 163 | } |
| 164 | 164 | ||
| 165 | /** | 165 | /** |
| 166 | - * 设置 | 166 | + * 我的认证车 |
| 167 | */ | 167 | */ |
| 168 | -const onSettings = () => { | 168 | +const onMyAuthCar = () => { |
| 169 | Taro.navigateTo({ | 169 | Taro.navigateTo({ |
| 170 | - url: '/pages/settings/index' | 170 | + url: '/pages/myAuthCar/index' |
| 171 | }) | 171 | }) |
| 172 | } | 172 | } |
| 173 | </script> | 173 | </script> | ... | ... |
-
Please register or login to post a comment