feat(收藏): 重构收藏功能为基于对象属性的模式并添加API支持
添加收藏相关API接口 创建useFavorite composable统一管理收藏状态 移除各页面中的favoriteIds数组,改用对象is_favorite属性 更新所有页面使用新的收藏逻辑
Showing
11 changed files
with
147 additions
and
203 deletions
| ... | @@ -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 | * 商品点击事件 | ... | ... |
src/composables/useFavorite.js
0 → 100644
| 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 | * 商品点击事件 | ... | ... |
-
Please register or login to post a comment