FeaturedRecommendations.vue
4.39 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
<template>
<view v-show="featuredScooters.length" class="px-4 mt-4">
<view class="flex justify-between items-center mb-2">
<text class="text-lg font-medium">精品推荐</text>
<view class="text-sm text-gray-500 flex items-center" @tap="onMoreRecommendClick">
<text>更多</text>
<RectRight size="12" />
</view>
</view>
<view class="grid grid-cols-2 gap-3">
<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.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)">
<Heart1 v-if="!scooter.is_favorite" size="22" :color="'#9ca3af'" />
<HeartFill v-else size="22" :color="'#ef4444'" />
</view>
<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.brand }} {{ scooter.model }}</text>
<text class="text-xs text-gray-500 block mt-1 mb-1">
{{ scooter.manufacture_year }}年 · {{ scooter.school_name }}
</text>
<view class="mt-1">
<text class="text-orange-500 font-bold" style="font-size: 1.25rem;">
¥{{ scooter.price.toLocaleString() }}
</text>
</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 { getRecommendVehicleAPI } from '@/api/car'
import { useFavorite } from '@/composables/useFavorite'
import { DEFAULT_COVER_IMG } from '@/utils/config'
// 定义组件名称
defineOptions({
name: 'FeaturedRecommendations'
})
// 精品推荐数据
const featuredScooters = ref([])
// 使用收藏功能composables
const { toggleFavorite } = useFavorite()
/**
* 查看更多点击事件
*/
const onMoreRecommendClick = () => {
Taro.navigateTo({
url: '/pages/recommendCarList/index'
})
}
/**
* 点击产品卡片
* @param {Object} scooter - 电动车信息
*/
const onProductClick = (scooter) => {
Taro.navigateTo({
url: `/pages/productDetail/index?id=${scooter.id}`
})
}
/**
* 加载精品推荐数据
* 循环查询直到获取到数据或确认没有更多数据
*/
const loadFeaturedData = async () => {
try {
let page = 0
let hasData = false
let maxPages = 10 // 设置最大查询页数,避免无限循环
while (!hasData && page < maxPages) {
const res = await getRecommendVehicleAPI({ section: 3, page, limit: 4 })
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
}))
// 最多显示4个
featuredScooters.value = processedData.slice(0, 4)
hasData = true
} else if (res.code && res.data && res.data.total === 0) {
// 确认没有数据,停止查询
break
} else {
// 当前页没有数据,查询下一页
page++
}
}
if (!hasData) {
featuredScooters.value = []
}
} catch (error) {
console.error('加载精品推荐数据失败:', error)
featuredScooters.value = []
}
}
// 组件挂载时加载数据
onMounted(() => {
loadFeaturedData()
})
</script>
<style lang="less" scoped>
// 使用Tailwind CSS类,只保留必要的自定义样式
.grid {
display: grid;
}
.grid-cols-2 {
grid-template-columns: repeat(2, 1fr);
}
.gap-3 {
gap: 0.75rem;
}
// 确保图片正确显示
image {
display: block;
}
</style>