hookehuyr

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

- 移除本地模拟数据,改为调用getVehicleListAPI获取真实车辆数据
- 更新车辆卡片显示字段以匹配API返回数据结构
- 实现分页加载和筛选功能
- 添加数据加载状态管理和错误处理
<!--
* @Date: 2022-09-19 14:11:06
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-07-10 21:19:50
* @LastEditTime: 2025-07-10 22:39:18
* @FilePath: /jgdl/src/pages/newCarList/index.vue
* @Description: 最新上架页面
-->
......@@ -53,7 +53,7 @@
>
<view class="flex">
<view class="w-32 h-24 relative p-2">
<image :src="car.imageUrl" :alt="car.name" mode="aspectFill"
<image :src="car.front_photo" mode="aspectFill"
class="w-full h-full object-cover rounded-lg" />
<view v-if="car.isNew"
class="absolute bottom-3 right-3 bg-red-500 text-white text-xs px-1 rounded flex items-center">
......@@ -65,22 +65,22 @@
<Heart1 v-if="!car.is_favorite" size="22" color="#9ca3af" />
<HeartFill v-else size="22" color="#ef4444" />
</view>
<text class="font-medium text-sm block">{{ car.name }}</text>
<text class="font-medium text-sm block">{{ car.brand }} {{ car.model }}</text>
<text class="text-xs text-gray-600 mt-1 block">
{{ car.year }} ·
<text v-if="car.batteryHealth">电池健康度{{ car.batteryHealth }}%</text>
<text v-if="car.mileage"> 行驶{{ car.mileage }}公里</text>
{{ car.manufacture_year }} ·
<text v-if="car.range_km">续航{{ car.range_km }}km</text>
<text v-if="car.max_speed_kmh"> 最高时速{{ car.max_speed_kmh }}km/h</text>
</text>
<view class="mt-2">
<text class="text-orange-500 font-bold" style="font-size: 1.2rem;">
¥{{ car.price.toLocaleString() }}
</text>
<text class="text-xs text-gray-500 mt-1 block">{{ car.school }}</text>
<text class="text-xs text-gray-500 mt-1 block">{{ car.school_name }}</text>
</view>
<!-- 上架时间 -->
<view class="mt-1">
<!-- <view class="mt-1">
<text class="text-xs text-green-600">{{ car.listingTime }}</text>
</view>
</view> -->
</view>
</view>
</view>
......@@ -118,15 +118,21 @@ import { Search2, Heart1, HeartFill } from '@nutui/icons-vue-taro'
// TabBar 组件已移除
import { useFavorite } from '@/composables/useFavorite'
import './index.less'
// 接口导入已移除,现在使用模拟数据;
// 接口导入
import { getVehicleBrandsAPI, getSchoolsAPI } from '@/api/other';
import { getVehicleListAPI } from '@/api/car';
// 响应式数据
const searchValue = ref('')
const onBlurSearch = () => {
console.warn(searchValue.value)
/**
* 搜索框失焦事件
*/
const onBlurSearch = async () => {
// 重置分页并重新加载数据
currentPage.value = 0
hasMore.value = true
await loadVehicleData()
}
// 收藏功能现在使用基于对象属性的模式
// Filter states - 使用NutUI Menu组件
const selectedBrand = ref('')
......@@ -141,66 +147,13 @@ const yearOptions = ref([])
const schoolOptions = ref([])
// 最新上架车辆数据
const newCars = ref([
{
id: 1,
name: '小牛NGT 电动车',
year: '2024年',
batteryHealth: 100,
mileage: 0,
price: 5200,
school: '上海理工大学',
imageUrl: 'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&h=300&fit=crop',
listingTime: '刚刚上架',
isNew: true,
brand: '小牛'
},
{
id: 2,
name: '雅迪 DE3 电动车',
year: '2024年',
batteryHealth: 98,
mileage: 200,
price: 4800,
school: '上海大学',
imageUrl: 'https://images.unsplash.com/photo-1571068316344-75bc76f77890?w=400&h=300&fit=crop',
listingTime: '5分钟前上架',
isNew: true,
brand: '雅迪'
},
{
id: 3,
name: '爱玛 A600 电动车',
year: '2024年',
batteryHealth: 95,
mileage: 500,
price: 3800,
school: '华东理工大学',
imageUrl: 'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&h=300&fit=crop',
listingTime: '10分钟前上架',
isNew: true,
brand: '爱玛'
},
{
id: 4,
name: '台铃 TDR-2024 电动车',
year: '2024年',
batteryHealth: 92,
mileage: 800,
price: 4200,
school: '上海交通大学',
imageUrl: 'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&h=300&fit=crop',
listingTime: '15分钟前上架',
isNew: true,
brand: '台铃'
}
])
const newCars = ref([])
// 加载状态
const loading = ref(false)
const hasMore = ref(true)
const currentPage = ref(1)
const pageSize = ref(4)
const currentPage = ref(0)
const pageSize = ref(10)
// Toast提示
const toastVisible = ref(false)
......@@ -266,79 +219,88 @@ const onSchoolChange = (value) => {
/**
* 过滤车辆数据
*/
const filterCars = () => {
// TODO: 实现过滤逻辑
const filterCars = async () => {
// 重置数据
newCars.value = []
currentPage.value = 0
hasMore.value = true
// 重新加载数据
await loadVehicleData()
showToast('筛选条件已更新', 'success')
}
/**
* 生成模拟车辆数据
* @param {number} page - 页码
* @param {number} size - 每页数量
* @returns {Array} 车辆数据数组
* 加载车辆数据
* @param {boolean} isLoadMore - 是否为加载更多
*/
const generateMockData = (page, size) => {
const brands = ['雅迪', '台铃', '小鸟', '新日', '爱玛', '小牛', '绿源', '立马']
const schools = ['上海理工大学', '上海复旦大学', '上海同济大学', '上海交通大学', '华东师范大学', '上海大学']
const years = ['2024年', '2023年', '2022年', '2021年', '2020年']
const images = [
'https://images.unsplash.com/photo-1567922045116-2a00fae2ed03?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60',
'https://images.unsplash.com/photo-1573981368236-719bbb6f70f7?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60',
'https://images.unsplash.com/photo-1583568671741-c70dafa8e8e7?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60',
'https://images.unsplash.com/photo-1595941069915-4ebc5197c14a?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 listingTimes = ['刚刚上架', '5分钟前上架', '10分钟前上架', '15分钟前上架', '30分钟前上架', '1小时前上架']
const data = []
for (let i = 0; i < size; i++) {
const index = (page - 1) * size + i
const brand = brands[Math.floor(Math.random() * brands.length)]
const school = schools[Math.floor(Math.random() * schools.length)]
const year = years[Math.floor(Math.random() * years.length)]
const image = images[Math.floor(Math.random() * images.length)]
const listingTime = listingTimes[Math.floor(Math.random() * listingTimes.length)]
data.push({
id: `new_${index + 100}`,
name: `${brand} ${['豪华版', '标准版', '运动版', '经典版'][Math.floor(Math.random() * 4)]}`,
year: year,
school: school,
price: Math.floor(Math.random() * 3000) + 3000, // 新车价格相对较高
imageUrl: image,
batteryHealth: Math.floor(Math.random() * 10) + 90, // 新车电池健康度较高
mileage: Math.floor(Math.random() * 1000), // 新车里程较少
brand: brand,
listingTime: listingTime,
isNew: Math.random() > 0.3 // 70%概率显示新标签
})
const loadVehicleData = async (isLoadMore = false) => {
if (loading.value) return
loading.value = true
try {
// 构建请求参数
const params = {
page: currentPage.value,
limit: pageSize.value
}
// 添加筛选条件
if (selectedSchool.value) {
params.school_id = selectedSchool.value
}
if (selectedBrand.value) {
params.brand = selectedBrand.value
}
if (selectedYear.value) {
params.manufacture_year = selectedYear.value
}
if (searchValue.value.trim()) {
params.keyword = searchValue.value.trim()
}
const response = await getVehicleListAPI(params)
if (response && response.code === 1 && response.data) {
const vehicleList = response.data.list || []
// 处理图片数据
const processedData = vehicleList.map(item => ({
...item,
// 添加新车标签(7天内发布的车辆)
isNew: new Date() - new Date(item.created_time) < 7 * 24 * 60 * 60 * 1000
}))
if (isLoadMore) {
newCars.value.push(...processedData)
} else {
newCars.value = processedData
}
// 检查是否还有更多数据 - 基于总数和当前已加载数量
const totalLoaded = (currentPage.value + 1) * pageSize.value
hasMore.value = totalLoaded < response.data.total
} else {
console.error('API返回错误:', response)
showToast(response?.msg || '获取数据失败', 'error')
}
} catch (error) {
console.error('加载车辆数据失败:', error)
showToast('网络错误,请稍后重试', 'error')
} finally {
loading.value = false
}
return data
}
/**
* 加载更多数据
*/
const loadMore = () => {
const loadMore = async () => {
if (loading.value || !hasMore.value) return
loading.value = true
// 模拟网络请求延迟
setTimeout(() => {
// 模拟最多加载5页数据
if (currentPage.value >= 5) {
hasMore.value = false
loading.value = false
return
}
currentPage.value++
const newData = generateMockData(currentPage.value, pageSize.value)
newCars.value.push(...newData)
loading.value = false
}, 1000 + Math.random() * 1000)
await loadVehicleData(true)
}
/**
......@@ -359,7 +321,6 @@ const showToast = (message, type = 'success') => {
// 初始化
onMounted(async () => {
// 可以在这里加载初始数据
// 获取全部品牌数据
const vBrands = await getVehicleBrandsAPI()
if (vBrands.code) {
......@@ -393,6 +354,9 @@ onMounted(async () => {
text: '全部学校',
value: ''
}, ...schoolOptions.value]
// 加载初始车辆数据
await loadVehicleData()
})
</script>
......