hookehuyr

feat(myAuthCar): 添加我的认证车页面功能

添加我的认证车页面,包括页面配置、路由注册、样式和主要功能实现
- 创建认证车页面组件和样式文件
- 在app.config中注册新页面路由
- 修改个人中心页面跳转逻辑
- 实现认证车列表展示、加载更多和空状态
- 添加模拟数据和API服务占位
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 {
......
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 +}
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 +}
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>
......