hookehuyr

feat(收藏): 重构收藏功能为基于对象属性的模式并添加API支持

添加收藏相关API接口
创建useFavorite composable统一管理收藏状态
移除各页面中的favoriteIds数组,改用对象is_favorite属性
更新所有页面使用新的收藏逻辑
...@@ -4,6 +4,9 @@ const Api = { ...@@ -4,6 +4,9 @@ const Api = {
4 GET_SCHOOLS: '/srv/?a=common&t=get_schools', 4 GET_SCHOOLS: '/srv/?a=common&t=get_schools',
5 GET_BRANDS_MODELS: '/srv/?a=common&t=get_brands_models', 5 GET_BRANDS_MODELS: '/srv/?a=common&t=get_brands_models',
6 GET_VEHICLE_BRANDS: '/srv/?a=common&t=get_vehicle_brands', 6 GET_VEHICLE_BRANDS: '/srv/?a=common&t=get_vehicle_brands',
7 + GET_FAVORITE_LIST: '/srv/?a=favorite&t=list',
8 + TOGGLE_FAVORITE_ADD: '/srv/?a=favorite&t=add',
9 + TOGGLE_FAVORITE_DEL: '/srv/?a=favorite&t=del',
7 } 10 }
8 11
9 /** 12 /**
...@@ -26,3 +29,18 @@ export const getBrandsModelsAPI = (params) => fn(fetch.get(Api.GET_BRANDS_MODELS ...@@ -26,3 +29,18 @@ export const getBrandsModelsAPI = (params) => fn(fetch.get(Api.GET_BRANDS_MODELS
26 * @returns 29 * @returns
27 */ 30 */
28 export const getVehicleBrandsAPI = (params) => fn(fetch.get(Api.GET_VEHICLE_BRANDS, params)); 31 export const getVehicleBrandsAPI = (params) => fn(fetch.get(Api.GET_VEHICLE_BRANDS, params));
32 +
33 +/**
34 + * @description: 获取收藏列表
35 + * @param {*} params
36 + * @returns
37 + */
38 +export const getFavoriteListAPI = (params) => fn(fetch.get(Api.GET_FAVORITE_LIST, params));
39 +
40 +/**
41 + * @description: 切换收藏
42 + * @param {*} params
43 + * @returns
44 + */
45 +export const toggleFavoriteAddAPI = (params) => fn(fetch.post(Api.TOGGLE_FAVORITE_ADD, params));
46 +export const toggleFavoriteDelAPI = (params) => fn(fetch.post(Api.TOGGLE_FAVORITE_DEL, params));
......
...@@ -65,8 +65,8 @@ ...@@ -65,8 +65,8 @@
65 <view class="relative p-2"> 65 <view class="relative p-2">
66 <image :src="scooter.imageUrl" :alt="scooter.name" mode="aspectFill" 66 <image :src="scooter.imageUrl" :alt="scooter.name" mode="aspectFill"
67 class="w-full h-36 object-cover rounded-lg" /> 67 class="w-full h-36 object-cover rounded-lg" />
68 - <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)"> 68 + <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)">
69 - <Heart1 v-if="!favoriteIds.includes(scooter.id)" size="22" :color="'#9ca3af'" /> 69 + <Heart1 v-if="!scooter.is_favorite" size="22" :color="'#9ca3af'" />
70 <HeartFill v-else size="22" :color="'#ef4444'" /> 70 <HeartFill v-else size="22" :color="'#ef4444'" />
71 </view> 71 </view>
72 <view v-if="scooter.isVerified" 72 <view v-if="scooter.isVerified"
...@@ -131,6 +131,7 @@ ...@@ -131,6 +131,7 @@
131 import { ref, computed, watch } from 'vue' 131 import { ref, computed, watch } from 'vue'
132 import Taro from '@tarojs/taro' 132 import Taro from '@tarojs/taro'
133 import { Search2, Check, Heart1, HeartFill } from '@nutui/icons-vue-taro' 133 import { Search2, Check, Heart1, HeartFill } from '@nutui/icons-vue-taro'
134 +import { useFavorite } from '@/composables/useFavorite'
134 import "./SearchPopup.less" 135 import "./SearchPopup.less"
135 136
136 // Props 137 // Props
...@@ -152,7 +153,7 @@ const visible = computed({ ...@@ -152,7 +153,7 @@ const visible = computed({
152 153
153 // 响应式数据 154 // 响应式数据
154 const searchValue = ref('') 155 const searchValue = ref('')
155 -const favoriteIds = ref(['5', '7', '1']) 156 +
156 const hasSearched = ref(false) 157 const hasSearched = ref(false)
157 158
158 // 滚动相关 159 // 滚动相关
...@@ -356,28 +357,8 @@ const loadMore = () => { ...@@ -356,28 +357,8 @@ const loadMore = () => {
356 }, 500) 357 }, 500)
357 } 358 }
358 359
359 -/** 360 +// 使用收藏功能composables
360 - * 切换收藏状态 361 +const { toggleFavorite } = useFavorite()
361 - * @param {string} id 商品ID
362 - */
363 -const toggleFavorite = (id) => {
364 - const index = favoriteIds.value.indexOf(id)
365 - if (index > -1) {
366 - favoriteIds.value.splice(index, 1)
367 - Taro.showToast({
368 - title: '取消收藏',
369 - icon: 'none',
370 - duration: 2000
371 - })
372 - } else {
373 - favoriteIds.value.push(id)
374 - Taro.showToast({
375 - title: '收藏成功',
376 - icon: 'success',
377 - duration: 2000
378 - })
379 - }
380 -}
381 362
382 /** 363 /**
383 * 商品点击事件 364 * 商品点击事件
......
1 +/*
2 + * @Date: 2025-07-10 21:28:57
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2025-07-10 21:59:52
5 + * @FilePath: /jgdl/src/composables/useFavorite.js
6 + * @Description: 文件描述
7 + */
8 +import Taro from '@tarojs/taro'
9 +import { toggleFavoriteAddAPI, toggleFavoriteDelAPI } from '@/api/other';
10 +
11 +/**
12 + * 收藏功能的composables - 基于对象属性的模式
13 + * 支持对象的is_favorite属性进行收藏状态管理
14 + */
15 +export function useFavorite() {
16 + /**
17 + * 切换收藏状态
18 + * @param {Object} item - 包含is_favorite属性的对象
19 + */
20 + const toggleFavorite = async (item) => {
21 + if (!item.is_favorite) {
22 + const { code } = await toggleFavoriteAddAPI({
23 + vehicle_id: item.id,
24 + })
25 + if (code) {
26 + item.is_favorite = !item.is_favorite
27 + Taro.showToast({
28 + title: '收藏成功',
29 + icon: 'success',
30 + duration: 2000
31 + })
32 + }
33 + } else {
34 + const { code } = await toggleFavoriteDelAPI({
35 + vehicle_id: item.id,
36 + })
37 + if (code) {
38 + item.is_favorite = !item.is_favorite
39 + Taro.showToast({
40 + title: '取消收藏',
41 + icon: 'none',
42 + duration: 2000
43 + })
44 + }
45 + }
46 + }
47 +
48 + /**
49 + * 检查是否已收藏
50 + * @param {Object} item - 包含is_favorite属性的对象
51 + * @returns {boolean} 是否已收藏
52 + */
53 + const isFavorite = (item) => {
54 + return item.is_favorite || false
55 + }
56 +
57 + return {
58 + toggleFavorite,
59 + isFavorite
60 + }
61 +}
62 +
63 +// 设置useFavorite为默认导出
64 +export default useFavorite
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-06 18:13:43 4 + * @LastEditTime: 2025-07-10 21:34:57
5 * @FilePath: /jgdl/src/pages/authCar/index.vue 5 * @FilePath: /jgdl/src/pages/authCar/index.vue
6 * @Description: 认证车源 6 * @Description: 认证车源
7 --> 7 -->
...@@ -60,8 +60,8 @@ ...@@ -60,8 +60,8 @@
60 class="w-full h-full object-cover rounded-lg" /> 60 class="w-full h-full object-cover rounded-lg" />
61 </view> 61 </view>
62 <view class="flex-1 p-3 relative"> 62 <view class="flex-1 p-3 relative">
63 - <view class="absolute top-2 right-4" @tap.stop="() => toggleFavorite(car.id)"> 63 + <view class="absolute top-2 right-4" @tap.stop="() => toggleFavorite(car)">
64 - <Heart1 v-if="!favoriteIds.includes(car.id)" size="22" :color="'#9ca3af'" /> 64 + <Heart1 v-if="!car.is_favorite" size="22" :color="'#9ca3af'" />
65 <HeartFill v-else size="22" :color="'#ef4444'" /> 65 <HeartFill v-else size="22" :color="'#ef4444'" />
66 </view> 66 </view>
67 <text class="font-medium text-sm block">{{ car.name }}</text> 67 <text class="font-medium text-sm block">{{ car.name }}</text>
...@@ -109,7 +109,8 @@ ...@@ -109,7 +109,8 @@
109 <script setup> 109 <script setup>
110 import Taro from '@tarojs/taro' 110 import Taro from '@tarojs/taro'
111 import { ref, computed, onMounted } from 'vue' 111 import { ref, computed, onMounted } from 'vue'
112 -import { Check, Addfollow, Follow, Heart1, HeartFill } from '@nutui/icons-vue-taro' 112 +import { Check, Heart1, HeartFill } from '@nutui/icons-vue-taro'
113 +import { useFavorite } from '@/composables/useFavorite'
113 import './index.less' 114 import './index.less'
114 115
115 // Banner图片数据 116 // Banner图片数据
...@@ -168,7 +169,7 @@ const authCars = ref([ ...@@ -168,7 +169,7 @@ const authCars = ref([
168 ]) 169 ])
169 170
170 // 收藏状态 171 // 收藏状态
171 -const favoriteIds = ref([2, 4]) 172 +// 收藏功能现在使用基于对象属性的模式
172 173
173 // 加载状态 174 // 加载状态
174 const loading = ref(false) 175 const loading = ref(false)
...@@ -205,24 +206,8 @@ const onCarClick = (car) => { ...@@ -205,24 +206,8 @@ const onCarClick = (car) => {
205 /** 206 /**
206 * 切换收藏状态 207 * 切换收藏状态
207 */ 208 */
208 -const toggleFavorite = (carId) => { 209 +// 使用收藏功能composables
209 - const index = favoriteIds.value.indexOf(carId) 210 +const { toggleFavorite } = useFavorite()
210 - if (index > -1) {
211 - favoriteIds.value.splice(index, 1)
212 - Taro.showToast({
213 - title: '取消收藏',
214 - icon: 'none',
215 - duration: 2000
216 - })
217 - } else {
218 - favoriteIds.value.push(carId)
219 - Taro.showToast({
220 - title: '收藏成功',
221 - icon: 'success',
222 - duration: 2000
223 - })
224 - }
225 -}
226 211
227 /** 212 /**
228 * 加载更多数据 213 * 加载更多数据
...@@ -268,11 +253,7 @@ const scroll = (e) => { ...@@ -268,11 +253,7 @@ const scroll = (e) => {
268 /** 253 /**
269 * 显示提示信息 254 * 显示提示信息
270 */ 255 */
271 -const showToast = (message, type = 'success') => { 256 +// showToast 功能现在由 useFavorite composable 处理
272 - toastMessage.value = message
273 - toastType.value = type
274 - toastVisible.value = true
275 -}
276 257
277 // 初始化 258 // 初始化
278 onMounted(() => { 259 onMounted(() => {
......
...@@ -66,8 +66,8 @@ ...@@ -66,8 +66,8 @@
66 </view> 66 </view>
67 </view> 67 </view>
68 <view class="flex-1 p-3 relative"> 68 <view class="flex-1 p-3 relative">
69 - <view class="absolute top-3 right-4" @tap.stop="() => toggleFavorite(car.id)"> 69 + <view class="absolute top-3 right-4" @tap.stop="() => toggleFavorite(car)">
70 - <Heart1 v-if="!favoriteIds.includes(car.id)" size="22" color="#9ca3af" /> 70 + <Heart1 v-if="!car.is_favorite" size="22" color="#9ca3af" />
71 <HeartFill v-else size="22" color="#ef4444" /> 71 <HeartFill v-else size="22" color="#ef4444" />
72 </view> 72 </view>
73 <text class="font-medium text-sm block">{{ car.name }}</text> 73 <text class="font-medium text-sm block">{{ car.name }}</text>
...@@ -125,8 +125,8 @@ ...@@ -125,8 +125,8 @@
125 <script setup> 125 <script setup>
126 import Taro from '@tarojs/taro' 126 import Taro from '@tarojs/taro'
127 import { ref, computed, onMounted } from 'vue' 127 import { ref, computed, onMounted } from 'vue'
128 -import { Search2, Addfollow, Follow, Heart1, HeartFill } from '@nutui/icons-vue-taro' 128 +import { Search2, Heart1, HeartFill } from '@nutui/icons-vue-taro'
129 -import TabBar from '@/components/TabBar.vue' 129 +import { useFavorite } from '@/composables/useFavorite'
130 import './index.less' 130 import './index.less'
131 131
132 // 响应式数据 132 // 响应式数据
...@@ -134,7 +134,7 @@ const searchValue = ref('') ...@@ -134,7 +134,7 @@ const searchValue = ref('')
134 const onBlurSearch = () => { 134 const onBlurSearch = () => {
135 console.warn(searchValue.value) 135 console.warn(searchValue.value)
136 } 136 }
137 -const favoriteIds = ref(['2', '4', '6']) 137 +// 收藏功能现在使用基于对象属性的模式
138 138
139 // Filter states - 使用NutUI Menu组件 139 // Filter states - 使用NutUI Menu组件
140 const selectedBrand = ref('全部品牌') 140 const selectedBrand = ref('全部品牌')
...@@ -255,24 +255,8 @@ const scrollStyle = computed(() => { ...@@ -255,24 +255,8 @@ const scrollStyle = computed(() => {
255 * 切换收藏状态 255 * 切换收藏状态
256 * @param {string} carId - 车辆ID 256 * @param {string} carId - 车辆ID
257 */ 257 */
258 -const toggleFavorite = (carId) => { 258 +// 使用收藏功能composables
259 - const index = favoriteIds.value.indexOf(carId) 259 +const { toggleFavorite } = useFavorite()
260 - if (index > -1) {
261 - favoriteIds.value.splice(index, 1)
262 - Taro.showToast({
263 - title: '取消收藏',
264 - icon: 'none',
265 - duration: 2000
266 - })
267 - } else {
268 - favoriteIds.value.push(carId)
269 - Taro.showToast({
270 - title: '收藏成功',
271 - icon: 'success',
272 - duration: 2000
273 - })
274 - }
275 -}
276 260
277 /** 261 /**
278 * 点击车辆卡片 262 * 点击车辆卡片
......
1 <!-- 1 <!--
2 * @Date: 2025-06-28 10:33:00 2 * @Date: 2025-06-28 10:33:00
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-07-10 20:17:31 4 + * @LastEditTime: 2025-07-10 21:30:57
5 * @FilePath: /jgdl/src/pages/index/index.vue 5 * @FilePath: /jgdl/src/pages/index/index.vue
6 * @Description: 捡个电驴首页 6 * @Description: 捡个电驴首页
7 --> 7 -->
...@@ -169,10 +169,11 @@ import SearchPopup from '@/components/SearchPopup.vue' ...@@ -169,10 +169,11 @@ import SearchPopup from '@/components/SearchPopup.vue'
169 import "./index.less"; 169 import "./index.less";
170 // 导入接口 170 // 导入接口
171 import { getRecommendVehicleAPI, getVehicleListAPI } from '@/api/car'; 171 import { getRecommendVehicleAPI, getVehicleListAPI } from '@/api/car';
172 +import { useFavorite } from '@/composables/useFavorite'
172 173
173 // 响应式数据 174 // 响应式数据
174 const searchValue = ref('') 175 const searchValue = ref('')
175 -const favoriteIds = ref([]) 176 +// favoriteIds 已移除,现在使用基于对象属性的收藏模式
176 const showSearchPopup = ref(false) 177 const showSearchPopup = ref(false)
177 178
178 const onSearchHandle = () => { 179 const onSearchHandle = () => {
...@@ -189,26 +190,8 @@ const featuredScooters = ref([]) ...@@ -189,26 +190,8 @@ const featuredScooters = ref([])
189 // 最新上架数据 190 // 最新上架数据
190 const latestScooters = ref([]) 191 const latestScooters = ref([])
191 192
192 -/** 193 +// 使用收藏功能composables
193 - * 切换收藏状态 194 +const { toggleFavorite } = useFavorite()
194 - * @param {string} scooterId - 电动车ID
195 - */
196 -const toggleFavorite = async (scooter) => {
197 - scooter.is_favorite = !scooter.is_favorite
198 - if (scooter.is_favorite) {
199 - Taro.showToast({
200 - title: '收藏成功',
201 - icon: 'success',
202 - duration: 2000
203 - })
204 - } else {
205 - Taro.showToast({
206 - title: '取消收藏',
207 - icon: 'none',
208 - duration: 2000
209 - })
210 - }
211 -}
212 195
213 const onMoreRecommendClick = () => { 196 const onMoreRecommendClick = () => {
214 Taro.navigateTo({ 197 Taro.navigateTo({
......
...@@ -61,8 +61,8 @@ ...@@ -61,8 +61,8 @@
61 </view> 61 </view>
62 </view> 62 </view>
63 <view class="flex-1 p-3 relative"> 63 <view class="flex-1 p-3 relative">
64 - <view class="absolute top-3 right-4" @tap.stop="() => toggleFavorite(car.id)"> 64 + <view class="absolute top-3 right-4" @tap.stop="() => toggleFavorite(car)">
65 - <Heart1 v-if="!favoriteIds.includes(car.id)" 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.name }}</text>
...@@ -115,10 +115,10 @@ ...@@ -115,10 +115,10 @@
115 import Taro from '@tarojs/taro' 115 import Taro from '@tarojs/taro'
116 import { ref, computed, onMounted } from 'vue' 116 import { ref, computed, onMounted } from 'vue'
117 import { Search2, Heart1, HeartFill } from '@nutui/icons-vue-taro' 117 import { Search2, Heart1, HeartFill } from '@nutui/icons-vue-taro'
118 -import TabBar from '@/components/TabBar.vue' 118 +// TabBar 组件已移除
119 +import { useFavorite } from '@/composables/useFavorite'
119 import './index.less' 120 import './index.less'
120 -// 导入接口 121 +// 接口导入已移除,现在使用模拟数据;
121 -import { getVehicleListAPI } from '@/api/car';
122 import { getVehicleBrandsAPI, getSchoolsAPI } from '@/api/other'; 122 import { getVehicleBrandsAPI, getSchoolsAPI } from '@/api/other';
123 123
124 // 响应式数据 124 // 响应式数据
...@@ -126,7 +126,7 @@ const searchValue = ref('') ...@@ -126,7 +126,7 @@ const searchValue = ref('')
126 const onBlurSearch = () => { 126 const onBlurSearch = () => {
127 console.warn(searchValue.value) 127 console.warn(searchValue.value)
128 } 128 }
129 -const favoriteIds = ref(['2', '4', '6']) 129 +// 收藏功能现在使用基于对象属性的模式
130 130
131 // Filter states - 使用NutUI Menu组件 131 // Filter states - 使用NutUI Menu组件
132 const selectedBrand = ref('') 132 const selectedBrand = ref('')
...@@ -218,24 +218,8 @@ const scrollStyle = computed(() => { ...@@ -218,24 +218,8 @@ const scrollStyle = computed(() => {
218 * 切换收藏状态 218 * 切换收藏状态
219 * @param {string} carId - 车辆ID 219 * @param {string} carId - 车辆ID
220 */ 220 */
221 -const toggleFavorite = (carId) => { 221 +// 使用收藏功能composables
222 - const index = favoriteIds.value.indexOf(carId) 222 +const { toggleFavorite } = useFavorite()
223 - if (index > -1) {
224 - favoriteIds.value.splice(index, 1)
225 - Taro.showToast({
226 - title: '取消收藏',
227 - icon: 'none',
228 - duration: 2000
229 - })
230 - } else {
231 - favoriteIds.value.push(carId)
232 - Taro.showToast({
233 - title: '收藏成功',
234 - icon: 'success',
235 - duration: 2000
236 - })
237 - }
238 -}
239 223
240 /** 224 /**
241 * 点击车辆卡片 225 * 点击车辆卡片
......
...@@ -43,8 +43,8 @@ ...@@ -43,8 +43,8 @@
43 </view> 43 </view>
44 </view> 44 </view>
45 <view class="flex-1 p-3 relative"> 45 <view class="flex-1 p-3 relative">
46 - <view class="absolute top-3 right-4" @tap.stop="() => toggleFavorite(scooter.id)"> 46 + <view class="absolute top-3 right-4" @tap.stop="() => toggleFavorite(scooter)">
47 - <Heart1 v-if="!favoriteIds.includes(scooter.id)" size="22" color="#9ca3af" /> 47 + <Heart1 v-if="!scooter.is_favorite" size="22" color="#9ca3af" />
48 <HeartFill v-else size="22" color="#ef4444" /> 48 <HeartFill v-else size="22" color="#ef4444" />
49 </view> 49 </view>
50 <text class="font-medium text-sm block">{{ scooter.name }}</text> 50 <text class="font-medium text-sm block">{{ scooter.name }}</text>
...@@ -97,8 +97,8 @@ ...@@ -97,8 +97,8 @@
97 <view class="relative p-2"> 97 <view class="relative p-2">
98 <image :src="scooter.imageUrl" :alt="scooter.name" mode="aspectFill" 98 <image :src="scooter.imageUrl" :alt="scooter.name" mode="aspectFill"
99 class="w-full h-36 object-cover rounded-lg" /> 99 class="w-full h-36 object-cover rounded-lg" />
100 - <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)"> 100 + <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)">
101 - <Heart1 v-if="!favoriteIds.includes(scooter.id)" size="20" color="#9ca3af" /> 101 + <Heart1 v-if="!scooter.is_favorite" size="20" color="#9ca3af" />
102 <HeartFill v-else size="20" color="#ef4444" /> 102 <HeartFill v-else size="20" color="#ef4444" />
103 </view> 103 </view>
104 <view v-if="scooter.isVerified" 104 <view v-if="scooter.isVerified"
...@@ -136,13 +136,14 @@ import { ref } from 'vue' ...@@ -136,13 +136,14 @@ import { ref } from 'vue'
136 import Taro from '@tarojs/taro' 136 import Taro from '@tarojs/taro'
137 import { Search2, RectRight, Check, Heart1, HeartFill } from '@nutui/icons-vue-taro' 137 import { Search2, RectRight, Check, Heart1, HeartFill } from '@nutui/icons-vue-taro'
138 import TabBar from '@/components/TabBar.vue' 138 import TabBar from '@/components/TabBar.vue'
139 +import { useFavorite } from '@/composables/useFavorite'
139 140
140 // 响应式数据 141 // 响应式数据
141 const searchValue = ref('') 142 const searchValue = ref('')
142 const onBlurSearch = () => { 143 const onBlurSearch = () => {
143 console.warn(searchValue.value) 144 console.warn(searchValue.value)
144 } 145 }
145 -const favoriteIds = ref(['5', '7', '1']) 146 +// 收藏功能现在使用基于对象属性的模式
146 147
147 // 无限滚动相关状态 148 // 无限滚动相关状态
148 const loading = ref(false) 149 const loading = ref(false)
...@@ -282,24 +283,8 @@ const featuredScooters = ref([ ...@@ -282,24 +283,8 @@ const featuredScooters = ref([
282 * 切换收藏状态 283 * 切换收藏状态
283 * @param {string} scooterId - 电动车ID 284 * @param {string} scooterId - 电动车ID
284 */ 285 */
285 -const toggleFavorite = (scooterId) => { 286 +// 使用收藏功能composables
286 - const index = favoriteIds.value.indexOf(scooterId) 287 +const { toggleFavorite } = useFavorite()
287 - if (index > -1) {
288 - favoriteIds.value.splice(index, 1)
289 - Taro.showToast({
290 - title: '取消收藏',
291 - icon: 'none',
292 - duration: 2000
293 - })
294 - } else {
295 - favoriteIds.value.push(scooterId)
296 - Taro.showToast({
297 - title: '收藏成功',
298 - icon: 'success',
299 - duration: 2000
300 - })
301 - }
302 -}
303 288
304 /** 289 /**
305 * 点击产品卡片 290 * 点击产品卡片
......
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-09 10:42:01 4 + * @LastEditTime: 2025-07-10 22:03:48
5 * @FilePath: /jgdl/src/pages/productDetail/index.vue 5 * @FilePath: /jgdl/src/pages/productDetail/index.vue
6 * @Description: 商品详情页 6 * @Description: 商品详情页
7 --> 7 -->
...@@ -30,9 +30,9 @@ ...@@ -30,9 +30,9 @@
30 </view> 30 </view>
31 </button> 31 </button>
32 </view> 32 </view>
33 - <view @tap="toggleFavorite" 33 + <view @tap="toggleFavorite(product)"
34 class="w-8 h-8 bg-gray-100 rounded-full flex items-center justify-center"> 34 class="w-8 h-8 bg-gray-100 rounded-full flex items-center justify-center">
35 - <HeartFill v-if="isFavorite" size="18" color="#ef4444" /> 35 + <HeartFill v-if="product.is_favorite" size="18" color="#ef4444" />
36 <Heart1 v-else size="18" color="#666" /> 36 <Heart1 v-else size="18" color="#666" />
37 </view> 37 </view>
38 </view> 38 </view>
...@@ -264,6 +264,7 @@ import { ref } from 'vue' ...@@ -264,6 +264,7 @@ import { ref } from 'vue'
264 import Taro, { useShareAppMessage } from '@tarojs/taro' 264 import Taro, { useShareAppMessage } from '@tarojs/taro'
265 import { Share, Heart1, HeartFill, Message } from '@nutui/icons-vue-taro' 265 import { Share, Heart1, HeartFill, Message } from '@nutui/icons-vue-taro'
266 import payCard from '@/components/payCard.vue' 266 import payCard from '@/components/payCard.vue'
267 +import { useFavorite } from '@/composables/useFavorite'
267 import avatarImg from '@/assets/images/avatar.png' 268 import avatarImg from '@/assets/images/avatar.png'
268 import { getCurrentPageParam } from "@/utils/weapp" 269 import { getCurrentPageParam } from "@/utils/weapp"
269 import { checkPermission, PERMISSION_TYPES } from '@/utils/permission' 270 import { checkPermission, PERMISSION_TYPES } from '@/utils/permission'
...@@ -279,7 +280,7 @@ wx.showShareMenu({ ...@@ -279,7 +280,7 @@ wx.showShareMenu({
279 280
280 // 响应式数据 281 // 响应式数据
281 const currentImageIndex = ref(0) 282 const currentImageIndex = ref(0)
282 -const isFavorite = ref(false) 283 +// isFavorite 功能现在由 useFavorite composable 提供
283 const showWechat = ref(false) 284 const showWechat = ref(false)
284 const showContactModal = ref(false) 285 const showContactModal = ref(false)
285 const show_pay = ref(false) 286 const show_pay = ref(false)
...@@ -353,7 +354,8 @@ const product = ref({ ...@@ -353,7 +354,8 @@ const product = ref({
353 avatar: 'https://randomuser.me/api/portraits/men/32.jpg', 354 avatar: 'https://randomuser.me/api/portraits/men/32.jpg',
354 wechat: 'li_student_2023', 355 wechat: 'li_student_2023',
355 phone: '138****8888' 356 phone: '138****8888'
356 - } 357 + },
358 + is_favorite: false
357 }) 359 })
358 360
359 /** 361 /**
...@@ -364,16 +366,8 @@ const onSwiperChange = (index) => { ...@@ -364,16 +366,8 @@ const onSwiperChange = (index) => {
364 currentImageIndex.value = index 366 currentImageIndex.value = index
365 } 367 }
366 368
367 -/** 369 +// 使用收藏功能composables
368 - * 切换收藏状态 370 +const { toggleFavorite } = useFavorite()
369 - */
370 -const toggleFavorite = () => {
371 - isFavorite.value = !isFavorite.value
372 - Taro.showToast({
373 - title: isFavorite.value ? '已收藏' : '已取消收藏',
374 - icon: 'none'
375 - })
376 -}
377 371
378 /** 372 /**
379 * 显示微信号弹框 373 * 显示微信号弹框
......
...@@ -61,8 +61,8 @@ ...@@ -61,8 +61,8 @@
61 </view> 61 </view>
62 </view> 62 </view>
63 <view class="flex-1 p-3 relative"> 63 <view class="flex-1 p-3 relative">
64 - <view class="absolute top-3 right-4" @tap.stop="() => toggleFavorite(car.id)"> 64 + <view class="absolute top-3 right-4" @tap.stop="() => toggleFavorite(car)">
65 - <Heart1 v-if="!favoriteIds.includes(car.id)" 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.name }}</text>
...@@ -115,6 +115,7 @@ ...@@ -115,6 +115,7 @@
115 import Taro from '@tarojs/taro' 115 import Taro from '@tarojs/taro'
116 import { ref, computed, onMounted } from 'vue' 116 import { ref, computed, onMounted } from 'vue'
117 import { Search2, Heart1, HeartFill } from '@nutui/icons-vue-taro' 117 import { Search2, Heart1, HeartFill } from '@nutui/icons-vue-taro'
118 +import { useFavorite } from '@/composables/useFavorite'
118 import './index.less' 119 import './index.less'
119 120
120 // 响应式数据 121 // 响应式数据
...@@ -122,7 +123,7 @@ const searchValue = ref('') ...@@ -122,7 +123,7 @@ const searchValue = ref('')
122 const onBlurSearch = () => { 123 const onBlurSearch = () => {
123 console.warn(searchValue.value) 124 console.warn(searchValue.value)
124 } 125 }
125 -const favoriteIds = ref(['2', '4', '6']) 126 +// 收藏功能现在使用基于对象属性的模式
126 127
127 // Filter states - 使用NutUI Menu组件 128 // Filter states - 使用NutUI Menu组件
128 const selectedBrand = ref('全部品牌') 129 const selectedBrand = ref('全部品牌')
...@@ -235,24 +236,8 @@ const scrollStyle = computed(() => { ...@@ -235,24 +236,8 @@ const scrollStyle = computed(() => {
235 * 切换收藏状态 236 * 切换收藏状态
236 * @param {string} carId - 车辆ID 237 * @param {string} carId - 车辆ID
237 */ 238 */
238 -const toggleFavorite = (carId) => { 239 +// 使用收藏功能composables
239 - const index = favoriteIds.value.indexOf(carId) 240 +const { toggleFavorite } = useFavorite()
240 - if (index > -1) {
241 - favoriteIds.value.splice(index, 1)
242 - Taro.showToast({
243 - title: '取消收藏',
244 - icon: 'none',
245 - duration: 2000
246 - })
247 - } else {
248 - favoriteIds.value.push(carId)
249 - Taro.showToast({
250 - title: '收藏成功',
251 - icon: 'success',
252 - duration: 2000
253 - })
254 - }
255 -}
256 241
257 /** 242 /**
258 * 点击车辆卡片 243 * 点击车辆卡片
......
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-04 16:57:31 4 + * @LastEditTime: 2025-07-10 21:38:42
5 * @FilePath: /jgdl/src/pages/search/index.vue 5 * @FilePath: /jgdl/src/pages/search/index.vue
6 * @Description: 搜索页面 6 * @Description: 搜索页面
7 --> 7 -->
...@@ -58,8 +58,8 @@ ...@@ -58,8 +58,8 @@
58 <view class="relative p-2"> 58 <view class="relative p-2">
59 <image :src="scooter.imageUrl" :alt="scooter.name" mode="aspectFill" 59 <image :src="scooter.imageUrl" :alt="scooter.name" mode="aspectFill"
60 class="w-full h-36 object-cover rounded-lg" /> 60 class="w-full h-36 object-cover rounded-lg" />
61 - <view class="absolute top-4 right-3 w-7 h-7 rounded-full bg-white bg-opacity-90" @tap.stop="() => toggleFavorite(scooter.id)" style="padding-top: 12rpx; padding-left: 10rpx;"> 61 + <view class="absolute top-4 right-3 w-7 h-7 rounded-full bg-white bg-opacity-90" @tap.stop="() => toggleFavorite(scooter)" style="padding-top: 12rpx; padding-left: 10rpx;">
62 - <Heart1 v-if="!favoriteIds.includes(scooter.id)" size="22" :color="'#9ca3af'" /> 62 + <Heart1 v-if="!scooter.is_favorite" size="22" :color="'#9ca3af'" />
63 <HeartFill v-else size="22" :color="'#ef4444'" /> 63 <HeartFill v-else size="22" :color="'#ef4444'" />
64 </view> 64 </view>
65 <view v-if="scooter.isVerified" 65 <view v-if="scooter.isVerified"
...@@ -123,11 +123,12 @@ ...@@ -123,11 +123,12 @@
123 import { ref, computed, onMounted } from 'vue' 123 import { ref, computed, onMounted } from 'vue'
124 import Taro from '@tarojs/taro' 124 import Taro from '@tarojs/taro'
125 import { Search2, Check, Heart1, HeartFill } from '@nutui/icons-vue-taro' 125 import { Search2, Check, Heart1, HeartFill } from '@nutui/icons-vue-taro'
126 +import { useFavorite } from '@/composables/useFavorite'
126 import "./index.less"; 127 import "./index.less";
127 128
128 // 响应式数据 129 // 响应式数据
129 const searchValue = ref('') 130 const searchValue = ref('')
130 -const favoriteIds = ref(['5', '7', '1']) 131 +// 收藏功能现在使用基于对象属性的模式
131 const hasSearched = ref(false) 132 const hasSearched = ref(false)
132 133
133 // 滚动相关 134 // 滚动相关
...@@ -335,24 +336,8 @@ const loadMore = () => { ...@@ -335,24 +336,8 @@ const loadMore = () => {
335 * 切换收藏状态 336 * 切换收藏状态
336 * @param {string} id 商品ID 337 * @param {string} id 商品ID
337 */ 338 */
338 -const toggleFavorite = (id) => { 339 +// 使用收藏功能composables
339 - const index = favoriteIds.value.indexOf(id) 340 +const { toggleFavorite } = useFavorite()
340 - if (index > -1) {
341 - favoriteIds.value.splice(index, 1)
342 - Taro.showToast({
343 - title: '取消收藏',
344 - icon: 'none',
345 - duration: 2000
346 - })
347 - } else {
348 - favoriteIds.value.push(id)
349 - Taro.showToast({
350 - title: '收藏成功',
351 - icon: 'success',
352 - duration: 2000
353 - })
354 - }
355 -}
356 341
357 /** 342 /**
358 * 商品点击事件 343 * 商品点击事件
......