hookehuyr

feat(首页): 更新电动车列表数据结构和API调用

重构首页电动车展示逻辑,使用新的API返回数据结构:
1. 更新图片字段为front_photo
2. 使用brand和model组合显示电动车名称
3. 修改收藏功能直接操作scooter对象
4. 添加获取轮播图、精品推荐和最新上架数据的API调用
5. 更新认证状态判断逻辑为verification_status === 5
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-09 16:08:53 4 + * @LastEditTime: 2025-07-10 18:30:25
5 * @FilePath: /jgdl/src/pages/index/index.vue 5 * @FilePath: /jgdl/src/pages/index/index.vue
6 * @Description: 捡个电驴首页 6 * @Description: 捡个电驴首页
7 --> 7 -->
...@@ -73,24 +73,24 @@ ...@@ -73,24 +73,24 @@
73 <view v-for="scooter in featuredScooters" :key="scooter.id" 73 <view v-for="scooter in featuredScooters" :key="scooter.id"
74 class="bg-white rounded-lg shadow-sm overflow-hidden" @tap="() => onProductClick(scooter)"> 74 class="bg-white rounded-lg shadow-sm overflow-hidden" @tap="() => onProductClick(scooter)">
75 <view class="relative p-2"> 75 <view class="relative p-2">
76 - <image :src="scooter.imageUrl" :alt="scooter.name" mode="aspectFill" 76 + <image :src="scooter.front_photo" mode="aspectFill"
77 class="w-full h-36 object-cover rounded-lg" /> 77 class="w-full h-36 object-cover rounded-lg" />
78 - <view class="absolute top-4 right-3 w-7 h-7 rounded-full bg-white bg-opacity-80 flex items-center justify-center" @tap.stop="() => toggleFavorite(scooter.id)"> 78 + <view class="absolute top-4 right-3 w-7 h-7 rounded-full bg-white bg-opacity-80 flex items-center justify-center" @tap.stop="() => toggleFavorite(scooter)">
79 - <Heart1 v-if="!favoriteIds.includes(scooter.id)" size="22" :color="'#9ca3af'" /> 79 + <Heart1 v-if="!scooter.is_favorite" size="22" :color="'#9ca3af'" />
80 <HeartFill v-else size="22" :color="'#ef4444'" /> 80 <HeartFill v-else size="22" :color="'#ef4444'" />
81 </view> 81 </view>
82 - <view v-if="scooter.isVerified" 82 + <view v-if="scooter.verification_status === 5"
83 class="absolute bottom-4 right-4 text-white text-xs px-1.5 py-0.5 rounded flex items-center" style="background-color: #EB5305;"> 83 class="absolute bottom-4 right-4 text-white text-xs px-1.5 py-0.5 rounded flex items-center" style="background-color: #EB5305;">
84 <Check size="12" color="#ffffff" class="mr-0.5" /> 84 <Check size="12" color="#ffffff" class="mr-0.5" />
85 <text class="text-white">认证</text> 85 <text class="text-white">认证</text>
86 </view> 86 </view>
87 </view> 87 </view>
88 <view class="p-2 pl-3"> 88 <view class="p-2 pl-3">
89 - <text class="font-medium text-sm block">{{ scooter.name }}</text> 89 + <text class="font-medium text-sm block">{{ scooter.brand }} {{ scooter.model }}</text>
90 <text class="text-xs text-gray-500 block mt-1 mb-1"> 90 <text class="text-xs text-gray-500 block mt-1 mb-1">
91 - {{ scooter.year }} · {{ scooter.school }} 91 + {{ scooter.manufacture_year }}年 · {{ scooter.school_name }}
92 - <text v-if="scooter.batteryHealth">电池健康度{{ scooter.batteryHealth }}%</text> 92 + <!-- <text v-if="scooter.battery_capacity_ah">电池容量{{ scooter.battery_capacity_ah }}Ah</text>
93 - <text v-if="scooter.mileage"> 行驶{{ scooter.mileage }}公里</text> 93 + <text v-if="scooter.total_mileage_km"> 行驶{{ scooter.total_mileage_km }}公里</text> -->
94 </text> 94 </text>
95 <view class="mt-1"> 95 <view class="mt-1">
96 <text class="text-orange-500 font-bold" style="font-size: 1.25rem;"> 96 <text class="text-orange-500 font-bold" style="font-size: 1.25rem;">
...@@ -118,32 +118,32 @@ ...@@ -118,32 +118,32 @@
118 <view class="flex flex-col"> 118 <view class="flex flex-col">
119 <view v-for="scooter in latestScooters" :key="scooter.id" 119 <view v-for="scooter in latestScooters" :key="scooter.id"
120 class="bg-white rounded-lg shadow-sm overflow-hidden mb-3" @tap="() => onProductClick(scooter)"> 120 class="bg-white rounded-lg shadow-sm overflow-hidden mb-3" @tap="() => onProductClick(scooter)">
121 - <view class="flex"> 121 + <view class="flex min-h-32">
122 - <view class="w-32 h-24 relative p-2"> 122 + <view class="w-32 relative p-2 flex flex-col">
123 - <image :src="scooter.imageUrl" :alt="scooter.name" mode="aspectFill" 123 + <image :src="scooter.front_photo" :alt="scooter.name" mode="aspectFill"
124 - class="w-full h-full object-cover rounded-lg" /> 124 + class="w-full flex-1 object-cover rounded-lg" />
125 - <view v-if="scooter.isVerified" 125 + <view v-if="scooter.verification_status === 5"
126 class="absolute bottom-3 right-3 text-white text-xs px-1 rounded flex items-center" style="background-color: #EB5305;"> 126 class="absolute bottom-3 right-3 text-white text-xs px-1 rounded flex items-center" style="background-color: #EB5305;">
127 <Check size="12" color="#ffffff" class="mr-0.5" /> 127 <Check size="12" color="#ffffff" class="mr-0.5" />
128 <text class="text-white">认证</text> 128 <text class="text-white">认证</text>
129 </view> 129 </view>
130 </view> 130 </view>
131 - <view class="flex-1 p-3 relative"> 131 + <view class="flex-1 p-3 relative flex flex-col">
132 - <view class="absolute top-2 right-2" @tap.stop="() => toggleFavorite(scooter.id)"> 132 + <view class="absolute top-2 right-2" @tap.stop="() => toggleFavorite(scooter)">
133 - <Heart1 v-if="!favoriteIds.includes(scooter.id)" size="22" :color="'#9ca3af'" /> 133 + <Heart1 v-if="!scooter.is_favorite" size="22" :color="'#9ca3af'" />
134 <HeartFill v-else size="22" :color="'#ef4444'" /> 134 <HeartFill v-else size="22" :color="'#ef4444'" />
135 </view> 135 </view>
136 - <text class="font-medium text-sm block">{{ scooter.name }}</text> 136 + <text class="font-medium text-sm block">{{ scooter.brand }} {{ scooter.model }}</text>
137 - <text class="text-xs text-gray-600 mt-1 block"> 137 + <text class="text-xs text-gray-600 mt-1 block" style="word-break: break-all;">
138 - {{ scooter.year }} · 138 + {{ scooter.manufacture_year }}年
139 - <text v-if="scooter.batteryHealth">电池健康度{{ scooter.batteryHealth }}%</text> 139 + <text v-if="scooter.range_km">续航{{ scooter.range_km }}km</text>
140 - <text v-if="scooter.mileage"> 行驶{{ scooter.mileage }}公里</text> 140 + <text v-if="scooter.max_speed_kmh"> 最高时速{{ scooter.max_speed_kmh }}km/h</text>
141 </text> 141 </text>
142 - <view class="mt-2"> 142 + <view class="mt-auto">
143 <text class="text-orange-500 font-bold" style="font-size: 1.25rem;"> 143 <text class="text-orange-500 font-bold" style="font-size: 1.25rem;">
144 ¥{{ scooter.price.toLocaleString() }} 144 ¥{{ scooter.price.toLocaleString() }}
145 </text> 145 </text>
146 - <text class="text-xs text-gray-500 mt-1 block">{{ scooter.school }}</text> 146 + <text class="text-xs text-gray-500 mt-1 block">{{ scooter.school_name }}</text>
147 </view> 147 </view>
148 </view> 148 </view>
149 </view> 149 </view>
...@@ -168,7 +168,7 @@ import TabBar from '@/components/TabBar.vue' ...@@ -168,7 +168,7 @@ import TabBar from '@/components/TabBar.vue'
168 import SearchPopup from '@/components/SearchPopup.vue' 168 import SearchPopup from '@/components/SearchPopup.vue'
169 import "./index.less"; 169 import "./index.less";
170 // 导入接口 170 // 导入接口
171 -import { getRecommendVehicleAPI } from '@/api/car'; 171 +import { getRecommendVehicleAPI, getVehicleListAPI } from '@/api/car';
172 172
173 // 响应式数据 173 // 响应式数据
174 const searchValue = ref('') 174 const searchValue = ref('')
...@@ -181,11 +181,7 @@ const onSearchHandle = () => { ...@@ -181,11 +181,7 @@ const onSearchHandle = () => {
181 } 181 }
182 182
183 // Banner图片 183 // Banner图片
184 -const bannerImages = ref([ 184 +const bannerImages = ref([])
185 - 'https://images.unsplash.com/photo-1558981806-ec527fa84c39?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60',
186 - 'https://images.unsplash.com/photo-1558981285-6f0c94958bb6?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60',
187 - 'https://images.unsplash.com/photo-1558981403-c5f9899a28bc?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60'
188 -])
189 185
190 // 精品推荐数据 186 // 精品推荐数据
191 const featuredScooters = ref([ 187 const featuredScooters = ref([
...@@ -276,20 +272,18 @@ const latestScooters = ref([ ...@@ -276,20 +272,18 @@ const latestScooters = ref([
276 * 切换收藏状态 272 * 切换收藏状态
277 * @param {string} scooterId - 电动车ID 273 * @param {string} scooterId - 电动车ID
278 */ 274 */
279 -const toggleFavorite = (scooterId) => { 275 +const toggleFavorite = async (scooter) => {
280 - const index = favoriteIds.value.indexOf(scooterId) 276 + scooter.is_favorite = !scooter.is_favorite
281 - if (index > -1) { 277 + if (scooter.is_favorite) {
282 - favoriteIds.value.splice(index, 1)
283 Taro.showToast({ 278 Taro.showToast({
284 - title: '取消收藏', 279 + title: '收藏成功',
285 - icon: 'none', 280 + icon: 'success',
286 duration: 2000 281 duration: 2000
287 }) 282 })
288 } else { 283 } else {
289 - favoriteIds.value.push(scooterId)
290 Taro.showToast({ 284 Taro.showToast({
291 - title: '收藏成功', 285 + title: '取消收藏',
292 - icon: 'success', 286 + icon: 'none',
293 duration: 2000 287 duration: 2000
294 }) 288 })
295 } 289 }
...@@ -383,12 +377,21 @@ useReady(async () => { ...@@ -383,12 +377,21 @@ useReady(async () => {
383 }) 377 })
384 378
385 onMounted(async () => { 379 onMounted(async () => {
386 - console.warn('index mounted') 380 + // 获取首页轮播
387 - // 获取推荐车源 381 + const res1 = await getRecommendVehicleAPI({ section: 1 })
388 - // const res = await getRecommendVehicleAPI() 382 + if (res1.code) {
389 - // if (res.code) { 383 + bannerImages.value = res1.data.map(item => item.front_photo);
390 - // recommendVehicleList.value = res.data 384 + }
391 - // } 385 + // 获取精品推荐
386 + const res2 = await getRecommendVehicleAPI({ section: 3 })
387 + if (res2.code) {
388 + featuredScooters.value = res2.data
389 + }
390 + // 获取最新上架
391 + const res3 = await getVehicleListAPI({ page: 0, page_size: 5 })
392 + if (res3.code) {
393 + latestScooters.value = res3.data.list
394 + }
392 }) 395 })
393 396
394 // 分享功能 397 // 分享功能
......
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:40:19 4 + * @LastEditTime: 2025-07-10 17:47:49
5 * @FilePath: /jgdl/src/pages/myCar/index.vue 5 * @FilePath: /jgdl/src/pages/myCar/index.vue
6 * @Description: 文件描述 6 * @Description: 文件描述
7 --> 7 -->
...@@ -212,8 +212,9 @@ const confirmOffline = async () => { ...@@ -212,8 +212,9 @@ const confirmOffline = async () => {
212 } 212 }
213 213
214 // 调用API更新车源状态 214 // 调用API更新车源状态
215 - const { code, msg } = await changeVehicleStatusAPI({ id: car.id, status: status }) 215 + const { code, msg, data } = await changeVehicleStatusAPI({ id: car.id, status: status })
216 if (code) { 216 if (code) {
217 + car.status = data.status;
217 Taro.showToast({ 218 Taro.showToast({
218 title: status === 5 ? '下架成功' : msg, 219 title: status === 5 ? '下架成功' : msg,
219 icon: 'none', 220 icon: 'none',
......