LatestScooters.vue
5.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
<template>
<view class="latest-scooters">
<!-- 最新上架 -->
<view class="px-4 py-3">
<view class="flex items-center justify-between mb-3">
<text class="text-lg font-medium">最新上架</text>
<view class="text-sm text-gray-500 flex items-center" @tap="onNewCarClick">
<text>更多</text>
<RectRight size="12" />
</view>
</view>
<view class="space-y-4">
<view v-for="scooter in latestScooters" :key="scooter.id"
class="bg-white rounded-lg shadow-sm overflow-hidden" @tap="() => onProductClick(scooter)">
<view class="flex min-h-32">
<view class="w-32 relative p-2 flex flex-col">
<image :src="scooter.front_photo" 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 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.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-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_name }}</text>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import Taro from '@tarojs/taro'
import { ref, onMounted } from 'vue'
import { RectRight, Check, Heart1, HeartFill } from '@nutui/icons-vue-taro'
import { getVehicleListAPI } from '@/api/car'
import { useFavorite } from '@/composables/useFavorite'
import { DEFAULT_COVER_IMG } from '@/utils/config'
// 最新上架数据
const latestScooters = ref([])
// 使用收藏功能composables
const { toggleFavorite } = useFavorite()
/**
* 点击产品卡片
* @param {Object} scooter - 电动车信息
*/
const onProductClick = (scooter) => {
Taro.navigateTo({
url: `/pages/productDetail/index?id=${scooter.id}`
})
}
/**
* 点击查看更多
*/
const onNewCarClick = () => {
Taro.navigateTo({
url: '/pages/newCarList/index'
})
}
/**
* 加载最新上架数据
* 循环查询直到获取到数据或确认没有数据为止
*/
const loadLatestData = async () => {
try {
let page = 0
let hasData = false
let allData = []
const limit = 5
// 循环查询直到获取到足够数据或没有更多数据
while (!hasData && page < 10) { // 最多查询10页防止无限循环
const res = await getVehicleListAPI({ page, limit })
if (res.code && res.data && res.data.list && res.data.list.length > 0) {
// 处理图片数据
const processedData = res.data.list.map(item => ({
...item,
front_photo: item.front_photo || DEFAULT_COVER_IMG,
// 确保价格为数字类型
price: Number(item.price) || 0,
market_price: Number(item.market_price) || 0,
}))
allData = [...allData, ...processedData]
// 如果已经获取到足够数据,停止查询
if (allData.length >= limit) {
hasData = true
latestScooters.value = allData.slice(0, limit)
} else if (res.data.list.length < limit) {
// 如果返回的数据少于请求的数量,说明没有更多数据了
hasData = true
latestScooters.value = allData
}
} else {
// 如果当前页没有数据,检查是否还有下一页
if (res.data && res.data.total !== undefined) {
const totalPages = Math.ceil(res.data.total / limit)
if (page >= totalPages - 1) {
// 已经是最后一页,停止查询
break
}
} else {
// 没有总数信息,如果连续没有数据就停止
break
}
}
page++
}
// 如果最终没有获取到任何数据
if (allData.length === 0) {
latestScooters.value = []
}
} catch (error) {
console.error('加载最新上架数据失败:', error)
latestScooters.value = []
}
}
onMounted(() => {
loadLatestData()
})
// 暴露刷新方法供父组件调用
defineExpose({
refresh: loadLatestData
})
</script>
<style lang="less" scoped>
.latest-scooters {
// 组件样式可以根据需要添加
}
</style>