hookehuyr

feat(车辆列表): 替换模拟数据为真实API接口获取

- 移除本地模拟数据,改为调用getVehicleListAPI获取真实车辆数据
- 更新车辆卡片显示字段以匹配API返回数据结构
- 实现分页加载和筛选功能
- 添加数据加载状态管理和错误处理
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 21:19:50 4 + * @LastEditTime: 2025-07-10 22:39:18
5 * @FilePath: /jgdl/src/pages/newCarList/index.vue 5 * @FilePath: /jgdl/src/pages/newCarList/index.vue
6 * @Description: 最新上架页面 6 * @Description: 最新上架页面
7 --> 7 -->
...@@ -53,7 +53,7 @@ ...@@ -53,7 +53,7 @@
53 > 53 >
54 <view class="flex"> 54 <view class="flex">
55 <view class="w-32 h-24 relative p-2"> 55 <view class="w-32 h-24 relative p-2">
56 - <image :src="car.imageUrl" :alt="car.name" mode="aspectFill" 56 + <image :src="car.front_photo" mode="aspectFill"
57 class="w-full h-full object-cover rounded-lg" /> 57 class="w-full h-full object-cover rounded-lg" />
58 <view v-if="car.isNew" 58 <view v-if="car.isNew"
59 class="absolute bottom-3 right-3 bg-red-500 text-white text-xs px-1 rounded flex items-center"> 59 class="absolute bottom-3 right-3 bg-red-500 text-white text-xs px-1 rounded flex items-center">
...@@ -65,22 +65,22 @@ ...@@ -65,22 +65,22 @@
65 <Heart1 v-if="!car.is_favorite" size="22" color="#9ca3af" /> 65 <Heart1 v-if="!car.is_favorite" size="22" color="#9ca3af" />
66 <HeartFill v-else size="22" color="#ef4444" /> 66 <HeartFill v-else size="22" color="#ef4444" />
67 </view> 67 </view>
68 - <text class="font-medium text-sm block">{{ car.name }}</text> 68 + <text class="font-medium text-sm block">{{ car.brand }} {{ car.model }}</text>
69 <text class="text-xs text-gray-600 mt-1 block"> 69 <text class="text-xs text-gray-600 mt-1 block">
70 - {{ car.year }} · 70 + {{ car.manufacture_year }} ·
71 - <text v-if="car.batteryHealth">电池健康度{{ car.batteryHealth }}%</text> 71 + <text v-if="car.range_km">续航{{ car.range_km }}km</text>
72 - <text v-if="car.mileage"> 行驶{{ car.mileage }}公里</text> 72 + <text v-if="car.max_speed_kmh"> 最高时速{{ car.max_speed_kmh }}km/h</text>
73 </text> 73 </text>
74 <view class="mt-2"> 74 <view class="mt-2">
75 <text class="text-orange-500 font-bold" style="font-size: 1.2rem;"> 75 <text class="text-orange-500 font-bold" style="font-size: 1.2rem;">
76 ¥{{ car.price.toLocaleString() }} 76 ¥{{ car.price.toLocaleString() }}
77 </text> 77 </text>
78 - <text class="text-xs text-gray-500 mt-1 block">{{ car.school }}</text> 78 + <text class="text-xs text-gray-500 mt-1 block">{{ car.school_name }}</text>
79 </view> 79 </view>
80 <!-- 上架时间 --> 80 <!-- 上架时间 -->
81 - <view class="mt-1"> 81 + <!-- <view class="mt-1">
82 <text class="text-xs text-green-600">{{ car.listingTime }}</text> 82 <text class="text-xs text-green-600">{{ car.listingTime }}</text>
83 - </view> 83 + </view> -->
84 </view> 84 </view>
85 </view> 85 </view>
86 </view> 86 </view>
...@@ -118,15 +118,21 @@ import { Search2, Heart1, HeartFill } from '@nutui/icons-vue-taro' ...@@ -118,15 +118,21 @@ import { Search2, Heart1, HeartFill } from '@nutui/icons-vue-taro'
118 // TabBar 组件已移除 118 // TabBar 组件已移除
119 import { useFavorite } from '@/composables/useFavorite' 119 import { useFavorite } from '@/composables/useFavorite'
120 import './index.less' 120 import './index.less'
121 -// 接口导入已移除,现在使用模拟数据; 121 +// 接口导入
122 import { getVehicleBrandsAPI, getSchoolsAPI } from '@/api/other'; 122 import { getVehicleBrandsAPI, getSchoolsAPI } from '@/api/other';
123 +import { getVehicleListAPI } from '@/api/car';
123 124
124 // 响应式数据 125 // 响应式数据
125 const searchValue = ref('') 126 const searchValue = ref('')
126 -const onBlurSearch = () => { 127 +/**
127 - console.warn(searchValue.value) 128 + * 搜索框失焦事件
129 + */
130 +const onBlurSearch = async () => {
131 + // 重置分页并重新加载数据
132 + currentPage.value = 0
133 + hasMore.value = true
134 + await loadVehicleData()
128 } 135 }
129 -// 收藏功能现在使用基于对象属性的模式
130 136
131 // Filter states - 使用NutUI Menu组件 137 // Filter states - 使用NutUI Menu组件
132 const selectedBrand = ref('') 138 const selectedBrand = ref('')
...@@ -141,66 +147,13 @@ const yearOptions = ref([]) ...@@ -141,66 +147,13 @@ const yearOptions = ref([])
141 const schoolOptions = ref([]) 147 const schoolOptions = ref([])
142 148
143 // 最新上架车辆数据 149 // 最新上架车辆数据
144 -const newCars = ref([ 150 +const newCars = ref([])
145 - {
146 - id: 1,
147 - name: '小牛NGT 电动车',
148 - year: '2024年',
149 - batteryHealth: 100,
150 - mileage: 0,
151 - price: 5200,
152 - school: '上海理工大学',
153 - imageUrl: 'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&h=300&fit=crop',
154 - listingTime: '刚刚上架',
155 - isNew: true,
156 - brand: '小牛'
157 - },
158 - {
159 - id: 2,
160 - name: '雅迪 DE3 电动车',
161 - year: '2024年',
162 - batteryHealth: 98,
163 - mileage: 200,
164 - price: 4800,
165 - school: '上海大学',
166 - imageUrl: 'https://images.unsplash.com/photo-1571068316344-75bc76f77890?w=400&h=300&fit=crop',
167 - listingTime: '5分钟前上架',
168 - isNew: true,
169 - brand: '雅迪'
170 - },
171 - {
172 - id: 3,
173 - name: '爱玛 A600 电动车',
174 - year: '2024年',
175 - batteryHealth: 95,
176 - mileage: 500,
177 - price: 3800,
178 - school: '华东理工大学',
179 - imageUrl: 'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&h=300&fit=crop',
180 - listingTime: '10分钟前上架',
181 - isNew: true,
182 - brand: '爱玛'
183 - },
184 - {
185 - id: 4,
186 - name: '台铃 TDR-2024 电动车',
187 - year: '2024年',
188 - batteryHealth: 92,
189 - mileage: 800,
190 - price: 4200,
191 - school: '上海交通大学',
192 - imageUrl: 'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&h=300&fit=crop',
193 - listingTime: '15分钟前上架',
194 - isNew: true,
195 - brand: '台铃'
196 - }
197 -])
198 151
199 // 加载状态 152 // 加载状态
200 const loading = ref(false) 153 const loading = ref(false)
201 const hasMore = ref(true) 154 const hasMore = ref(true)
202 -const currentPage = ref(1) 155 +const currentPage = ref(0)
203 -const pageSize = ref(4) 156 +const pageSize = ref(10)
204 157
205 // Toast提示 158 // Toast提示
206 const toastVisible = ref(false) 159 const toastVisible = ref(false)
...@@ -266,79 +219,88 @@ const onSchoolChange = (value) => { ...@@ -266,79 +219,88 @@ const onSchoolChange = (value) => {
266 /** 219 /**
267 * 过滤车辆数据 220 * 过滤车辆数据
268 */ 221 */
269 -const filterCars = () => { 222 +const filterCars = async () => {
270 - // TODO: 实现过滤逻辑 223 + // 重置数据
224 + newCars.value = []
225 + currentPage.value = 0
226 + hasMore.value = true
227 +
228 + // 重新加载数据
229 + await loadVehicleData()
271 showToast('筛选条件已更新', 'success') 230 showToast('筛选条件已更新', 'success')
272 } 231 }
273 232
274 /** 233 /**
275 - * 生成模拟车辆数据 234 + * 加载车辆数据
276 - * @param {number} page - 页码 235 + * @param {boolean} isLoadMore - 是否为加载更多
277 - * @param {number} size - 每页数量
278 - * @returns {Array} 车辆数据数组
279 */ 236 */
280 -const generateMockData = (page, size) => { 237 +const loadVehicleData = async (isLoadMore = false) => {
281 - const brands = ['雅迪', '台铃', '小鸟', '新日', '爱玛', '小牛', '绿源', '立马'] 238 + if (loading.value) return
282 - const schools = ['上海理工大学', '上海复旦大学', '上海同济大学', '上海交通大学', '华东师范大学', '上海大学'] 239 +
283 - const years = ['2024年', '2023年', '2022年', '2021年', '2020年'] 240 + loading.value = true
284 - const images = [ 241 +
285 - 'https://images.unsplash.com/photo-1567922045116-2a00fae2ed03?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60', 242 + try {
286 - 'https://images.unsplash.com/photo-1573981368236-719bbb6f70f7?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60', 243 + // 构建请求参数
287 - 'https://images.unsplash.com/photo-1583568671741-c70dafa8e8e7?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60', 244 + const params = {
288 - 'https://images.unsplash.com/photo-1595941069915-4ebc5197c14a?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60', 245 + page: currentPage.value,
289 - 'https://images.unsplash.com/photo-1558981285-6f0c94958bb6?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60', 246 + limit: pageSize.value
290 - 'https://images.unsplash.com/photo-1558981403-c5f9899a28bc?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60' 247 + }
291 - ] 248 +
292 - const listingTimes = ['刚刚上架', '5分钟前上架', '10分钟前上架', '15分钟前上架', '30分钟前上架', '1小时前上架'] 249 + // 添加筛选条件
293 - 250 + if (selectedSchool.value) {
294 - const data = [] 251 + params.school_id = selectedSchool.value
295 - for (let i = 0; i < size; i++) { 252 + }
296 - const index = (page - 1) * size + i 253 + if (selectedBrand.value) {
297 - const brand = brands[Math.floor(Math.random() * brands.length)] 254 + params.brand = selectedBrand.value
298 - const school = schools[Math.floor(Math.random() * schools.length)] 255 + }
299 - const year = years[Math.floor(Math.random() * years.length)] 256 + if (selectedYear.value) {
300 - const image = images[Math.floor(Math.random() * images.length)] 257 + params.manufacture_year = selectedYear.value
301 - const listingTime = listingTimes[Math.floor(Math.random() * listingTimes.length)] 258 + }
302 - 259 + if (searchValue.value.trim()) {
303 - data.push({ 260 + params.keyword = searchValue.value.trim()
304 - id: `new_${index + 100}`, 261 + }
305 - name: `${brand} ${['豪华版', '标准版', '运动版', '经典版'][Math.floor(Math.random() * 4)]}`, 262 +
306 - year: year, 263 + const response = await getVehicleListAPI(params)
307 - school: school, 264 +
308 - price: Math.floor(Math.random() * 3000) + 3000, // 新车价格相对较高 265 + if (response && response.code === 1 && response.data) {
309 - imageUrl: image, 266 + const vehicleList = response.data.list || []
310 - batteryHealth: Math.floor(Math.random() * 10) + 90, // 新车电池健康度较高 267 +
311 - mileage: Math.floor(Math.random() * 1000), // 新车里程较少 268 + // 处理图片数据
312 - brand: brand, 269 + const processedData = vehicleList.map(item => ({
313 - listingTime: listingTime, 270 + ...item,
314 - isNew: Math.random() > 0.3 // 70%概率显示新标签 271 + // 添加新车标签(7天内发布的车辆)
315 - }) 272 + isNew: new Date() - new Date(item.created_time) < 7 * 24 * 60 * 60 * 1000
273 + }))
274 +
275 + if (isLoadMore) {
276 + newCars.value.push(...processedData)
277 + } else {
278 + newCars.value = processedData
279 + }
280 +
281 + // 检查是否还有更多数据 - 基于总数和当前已加载数量
282 + const totalLoaded = (currentPage.value + 1) * pageSize.value
283 + hasMore.value = totalLoaded < response.data.total
284 + } else {
285 + console.error('API返回错误:', response)
286 + showToast(response?.msg || '获取数据失败', 'error')
287 + }
288 + } catch (error) {
289 + console.error('加载车辆数据失败:', error)
290 + showToast('网络错误,请稍后重试', 'error')
291 + } finally {
292 + loading.value = false
316 } 293 }
317 - return data
318 } 294 }
319 295
320 /** 296 /**
321 * 加载更多数据 297 * 加载更多数据
322 */ 298 */
323 -const loadMore = () => { 299 +const loadMore = async () => {
324 if (loading.value || !hasMore.value) return 300 if (loading.value || !hasMore.value) return
325 301
326 - loading.value = true
327 -
328 - // 模拟网络请求延迟
329 - setTimeout(() => {
330 - // 模拟最多加载5页数据
331 - if (currentPage.value >= 5) {
332 - hasMore.value = false
333 - loading.value = false
334 - return
335 - }
336 -
337 currentPage.value++ 302 currentPage.value++
338 - const newData = generateMockData(currentPage.value, pageSize.value) 303 + await loadVehicleData(true)
339 - newCars.value.push(...newData)
340 - loading.value = false
341 - }, 1000 + Math.random() * 1000)
342 } 304 }
343 305
344 /** 306 /**
...@@ -359,7 +321,6 @@ const showToast = (message, type = 'success') => { ...@@ -359,7 +321,6 @@ const showToast = (message, type = 'success') => {
359 321
360 // 初始化 322 // 初始化
361 onMounted(async () => { 323 onMounted(async () => {
362 - // 可以在这里加载初始数据
363 // 获取全部品牌数据 324 // 获取全部品牌数据
364 const vBrands = await getVehicleBrandsAPI() 325 const vBrands = await getVehicleBrandsAPI()
365 if (vBrands.code) { 326 if (vBrands.code) {
...@@ -393,6 +354,9 @@ onMounted(async () => { ...@@ -393,6 +354,9 @@ onMounted(async () => {
393 text: '全部学校', 354 text: '全部学校',
394 value: '' 355 value: ''
395 }, ...schoolOptions.value] 356 }, ...schoolOptions.value]
357 +
358 + // 加载初始车辆数据
359 + await loadVehicleData()
396 }) 360 })
397 </script> 361 </script>
398 362
......