hookehuyr

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

重构首页电动车展示逻辑,使用新的API返回数据结构:
1. 更新图片字段为front_photo
2. 使用brand和model组合显示电动车名称
3. 修改收藏功能直接操作scooter对象
4. 添加获取轮播图、精品推荐和最新上架数据的API调用
5. 更新认证状态判断逻辑为verification_status === 5
<!--
* @Date: 2025-06-28 10:33:00
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-07-09 16:08:53
* @LastEditTime: 2025-07-10 18:30:25
* @FilePath: /jgdl/src/pages/index/index.vue
* @Description: 捡个电驴首页
-->
......@@ -73,24 +73,24 @@
<view v-for="scooter in featuredScooters" :key="scooter.id"
class="bg-white rounded-lg shadow-sm overflow-hidden" @tap="() => onProductClick(scooter)">
<view class="relative p-2">
<image :src="scooter.imageUrl" :alt="scooter.name" mode="aspectFill"
<image :src="scooter.front_photo" mode="aspectFill"
class="w-full h-36 object-cover rounded-lg" />
<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)">
<Heart1 v-if="!favoriteIds.includes(scooter.id)" size="22" :color="'#9ca3af'" />
<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)">
<Heart1 v-if="!scooter.is_favorite" size="22" :color="'#9ca3af'" />
<HeartFill v-else size="22" :color="'#ef4444'" />
</view>
<view v-if="scooter.isVerified"
<view v-if="scooter.verification_status === 5"
class="absolute bottom-4 right-4 text-white text-xs px-1.5 py-0.5 rounded flex items-center" style="background-color: #EB5305;">
<Check size="12" color="#ffffff" class="mr-0.5" />
<text class="text-white">认证</text>
</view>
</view>
<view class="p-2 pl-3">
<text class="font-medium text-sm block">{{ scooter.name }}</text>
<text class="font-medium text-sm block">{{ scooter.brand }} {{ scooter.model }}</text>
<text class="text-xs text-gray-500 block mt-1 mb-1">
{{ scooter.year }} · {{ scooter.school }}
<text v-if="scooter.batteryHealth">电池健康度{{ scooter.batteryHealth }}%</text>
<text v-if="scooter.mileage"> 行驶{{ scooter.mileage }}公里</text>
{{ scooter.manufacture_year }}年 · {{ scooter.school_name }}
<!-- <text v-if="scooter.battery_capacity_ah">电池容量{{ scooter.battery_capacity_ah }}Ah</text>
<text v-if="scooter.total_mileage_km"> 行驶{{ scooter.total_mileage_km }}公里</text> -->
</text>
<view class="mt-1">
<text class="text-orange-500 font-bold" style="font-size: 1.25rem;">
......@@ -118,32 +118,32 @@
<view class="flex flex-col">
<view v-for="scooter in latestScooters" :key="scooter.id"
class="bg-white rounded-lg shadow-sm overflow-hidden mb-3" @tap="() => onProductClick(scooter)">
<view class="flex">
<view class="w-32 h-24 relative p-2">
<image :src="scooter.imageUrl" :alt="scooter.name" mode="aspectFill"
class="w-full h-full object-cover rounded-lg" />
<view v-if="scooter.isVerified"
<view class="flex min-h-32">
<view class="w-32 relative p-2 flex flex-col">
<image :src="scooter.front_photo" :alt="scooter.name" mode="aspectFill"
class="w-full flex-1 object-cover rounded-lg" />
<view v-if="scooter.verification_status === 5"
class="absolute bottom-3 right-3 text-white text-xs px-1 rounded flex items-center" style="background-color: #EB5305;">
<Check size="12" color="#ffffff" class="mr-0.5" />
<text class="text-white">认证</text>
</view>
</view>
<view class="flex-1 p-3 relative">
<view class="absolute top-2 right-2" @tap.stop="() => toggleFavorite(scooter.id)">
<Heart1 v-if="!favoriteIds.includes(scooter.id)" size="22" :color="'#9ca3af'" />
<view class="flex-1 p-3 relative flex flex-col">
<view class="absolute top-2 right-2" @tap.stop="() => toggleFavorite(scooter)">
<Heart1 v-if="!scooter.is_favorite" size="22" :color="'#9ca3af'" />
<HeartFill v-else size="22" :color="'#ef4444'" />
</view>
<text class="font-medium text-sm block">{{ scooter.name }}</text>
<text class="text-xs text-gray-600 mt-1 block">
{{ scooter.year }} ·
<text v-if="scooter.batteryHealth">电池健康度{{ scooter.batteryHealth }}%</text>
<text v-if="scooter.mileage"> 行驶{{ scooter.mileage }}公里</text>
<text class="font-medium text-sm block">{{ scooter.brand }} {{ scooter.model }}</text>
<text class="text-xs text-gray-600 mt-1 block" style="word-break: break-all;">
{{ scooter.manufacture_year }}年
<text v-if="scooter.range_km">续航{{ scooter.range_km }}km</text>
<text v-if="scooter.max_speed_kmh"> 最高时速{{ scooter.max_speed_kmh }}km/h</text>
</text>
<view class="mt-2">
<view class="mt-auto">
<text class="text-orange-500 font-bold" style="font-size: 1.25rem;">
¥{{ scooter.price.toLocaleString() }}
</text>
<text class="text-xs text-gray-500 mt-1 block">{{ scooter.school }}</text>
<text class="text-xs text-gray-500 mt-1 block">{{ scooter.school_name }}</text>
</view>
</view>
</view>
......@@ -168,7 +168,7 @@ import TabBar from '@/components/TabBar.vue'
import SearchPopup from '@/components/SearchPopup.vue'
import "./index.less";
// 导入接口
import { getRecommendVehicleAPI } from '@/api/car';
import { getRecommendVehicleAPI, getVehicleListAPI } from '@/api/car';
// 响应式数据
const searchValue = ref('')
......@@ -181,11 +181,7 @@ const onSearchHandle = () => {
}
// Banner图片
const bannerImages = ref([
'https://images.unsplash.com/photo-1558981806-ec527fa84c39?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60',
'https://images.unsplash.com/photo-1558981285-6f0c94958bb6?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60',
'https://images.unsplash.com/photo-1558981403-c5f9899a28bc?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60'
])
const bannerImages = ref([])
// 精品推荐数据
const featuredScooters = ref([
......@@ -276,20 +272,18 @@ const latestScooters = ref([
* 切换收藏状态
* @param {string} scooterId - 电动车ID
*/
const toggleFavorite = (scooterId) => {
const index = favoriteIds.value.indexOf(scooterId)
if (index > -1) {
favoriteIds.value.splice(index, 1)
const toggleFavorite = async (scooter) => {
scooter.is_favorite = !scooter.is_favorite
if (scooter.is_favorite) {
Taro.showToast({
title: '取消收藏',
icon: 'none',
title: '收藏成功',
icon: 'success',
duration: 2000
})
} else {
favoriteIds.value.push(scooterId)
Taro.showToast({
title: '收藏成功',
icon: 'success',
title: '取消收藏',
icon: 'none',
duration: 2000
})
}
......@@ -383,12 +377,21 @@ useReady(async () => {
})
onMounted(async () => {
console.warn('index mounted')
// 获取推荐车源
// const res = await getRecommendVehicleAPI()
// if (res.code) {
// recommendVehicleList.value = res.data
// }
// 获取首页轮播
const res1 = await getRecommendVehicleAPI({ section: 1 })
if (res1.code) {
bannerImages.value = res1.data.map(item => item.front_photo);
}
// 获取精品推荐
const res2 = await getRecommendVehicleAPI({ section: 3 })
if (res2.code) {
featuredScooters.value = res2.data
}
// 获取最新上架
const res3 = await getVehicleListAPI({ page: 0, page_size: 5 })
if (res3.code) {
latestScooters.value = res3.data.list
}
})
// 分享功能
......
<!--
* @Date: 2022-09-19 14:11:06
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-07-10 17:40:19
* @LastEditTime: 2025-07-10 17:47:49
* @FilePath: /jgdl/src/pages/myCar/index.vue
* @Description: 文件描述
-->
......@@ -212,8 +212,9 @@ const confirmOffline = async () => {
}
// 调用API更新车源状态
const { code, msg } = await changeVehicleStatusAPI({ id: car.id, status: status })
const { code, msg, data } = await changeVehicleStatusAPI({ id: car.id, status: status })
if (code) {
car.status = data.status;
Taro.showToast({
title: status === 5 ? '下架成功' : msg,
icon: 'none',
......