feat(页面): 重构发布页并添加NutUI组件
- 使用NutUI组件重构发布页界面,包括搜索栏、菜单筛选和按钮 - 替换Right图标为RectRight以保持一致性 - 添加加载更多功能和模拟数据生成逻辑 - 更新车辆卡片布局和样式,增加认证标识
Showing
3 changed files
with
372 additions
and
280 deletions
| ... | @@ -8,6 +8,10 @@ export {} | ... | @@ -8,6 +8,10 @@ export {} |
| 8 | declare module 'vue' { | 8 | declare module 'vue' { |
| 9 | export interface GlobalComponents { | 9 | export interface GlobalComponents { |
| 10 | NavBar: typeof import('./src/components/navBar.vue')['default'] | 10 | NavBar: typeof import('./src/components/navBar.vue')['default'] |
| 11 | + NutButton: typeof import('@nutui/nutui-taro')['Button'] | ||
| 12 | + NutLoading: typeof import('@nutui/nutui-taro')['Loading'] | ||
| 13 | + NutMenu: typeof import('@nutui/nutui-taro')['Menu'] | ||
| 14 | + NutMenuItem: typeof import('@nutui/nutui-taro')['MenuItem'] | ||
| 11 | NutSearchbar: typeof import('@nutui/nutui-taro')['Searchbar'] | 15 | NutSearchbar: typeof import('@nutui/nutui-taro')['Searchbar'] |
| 12 | NutSwiper: typeof import('@nutui/nutui-taro')['Swiper'] | 16 | NutSwiper: typeof import('@nutui/nutui-taro')['Swiper'] |
| 13 | NutSwiperItem: typeof import('@nutui/nutui-taro')['SwiperItem'] | 17 | NutSwiperItem: typeof import('@nutui/nutui-taro')['SwiperItem'] | ... | ... |
| 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-01 21:50:54 | 4 | + * @LastEditTime: 2025-07-01 22:57:26 |
| 5 | * @FilePath: /jgdl/src/pages/index/index.vue | 5 | * @FilePath: /jgdl/src/pages/index/index.vue |
| 6 | * @Description: 捡个电驴首页 | 6 | * @Description: 捡个电驴首页 |
| 7 | --> | 7 | --> |
| ... | @@ -59,7 +59,7 @@ | ... | @@ -59,7 +59,7 @@ |
| 59 | <text class="text-lg font-medium">精品推荐</text> | 59 | <text class="text-lg font-medium">精品推荐</text> |
| 60 | <view class="text-sm text-gray-500 flex items-center"> | 60 | <view class="text-sm text-gray-500 flex items-center"> |
| 61 | <text>更多</text> | 61 | <text>更多</text> |
| 62 | - <Right size="16" /> | 62 | + <RectRight size="12" /> |
| 63 | </view> | 63 | </view> |
| 64 | </view> | 64 | </view> |
| 65 | <view class="grid grid-cols-2 gap-3"> | 65 | <view class="grid grid-cols-2 gap-3"> |
| ... | @@ -105,7 +105,7 @@ | ... | @@ -105,7 +105,7 @@ |
| 105 | <text class="text-lg font-medium">最新上架</text> | 105 | <text class="text-lg font-medium">最新上架</text> |
| 106 | <view class="text-sm text-gray-500 flex items-center"> | 106 | <view class="text-sm text-gray-500 flex items-center"> |
| 107 | <text>更多</text> | 107 | <text>更多</text> |
| 108 | - <Right size="16" /> | 108 | + <RectRight size="12" /> |
| 109 | </view> | 109 | </view> |
| 110 | </view> | 110 | </view> |
| 111 | <view class="flex flex-col"> | 111 | <view class="flex flex-col"> |
| ... | @@ -143,7 +143,7 @@ | ... | @@ -143,7 +143,7 @@ |
| 143 | </view> | 143 | </view> |
| 144 | </view> | 144 | </view> |
| 145 | </view> | 145 | </view> |
| 146 | - | 146 | + |
| 147 | <!-- 自定义TabBar --> | 147 | <!-- 自定义TabBar --> |
| 148 | <TabBar /> | 148 | <TabBar /> |
| 149 | </view> | 149 | </view> |
| ... | @@ -154,7 +154,7 @@ import Taro from '@tarojs/taro' | ... | @@ -154,7 +154,7 @@ import Taro from '@tarojs/taro' |
| 154 | // import '@tarojs/taro/html.css' 和 nutui组件居然有冲突? | 154 | // import '@tarojs/taro/html.css' 和 nutui组件居然有冲突? |
| 155 | import { ref, onMounted } from 'vue' | 155 | import { ref, onMounted } from 'vue' |
| 156 | import { useDidShow, useReady } from '@tarojs/taro' | 156 | import { useDidShow, useReady } from '@tarojs/taro' |
| 157 | -import { Clock, Star, Right, Addfollow, HeartFill, Check, Search2, Shop } from '@nutui/icons-vue-taro' | 157 | +import { Clock, Star, RectRight, Addfollow, HeartFill, Check, Search2, Shop } from '@nutui/icons-vue-taro' |
| 158 | import TabBar from '@/components/TabBar.vue' | 158 | import TabBar from '@/components/TabBar.vue' |
| 159 | import "./index.less"; | 159 | import "./index.less"; |
| 160 | 160 | ... | ... |
| 1 | <template> | 1 | <template> |
| 2 | - <view class="post-page"> | 2 | + <view> |
| 3 | - <!-- 顶部搜索栏 --> | 3 | + <view class="flex flex-col bg-white min-h-screen"> |
| 4 | - <view class="search-container"> | 4 | + <!-- Header --> |
| 5 | - <view class="search-box"> | 5 | + <view class="bg-orange-400 p-4 pt-8 pb-6"> |
| 6 | - <Search size="18" color="#9ca3af" /> | 6 | + <view class="text-2xl font-bold text-white mb-3">捡个电驴</view> |
| 7 | - <input | 7 | + <!-- Search Bar --> |
| 8 | - v-model="searchValue" | 8 | + <nut-searchbar v-model="searchValue" placeholder="搜索品牌型号" shape="round" background="transparent" |
| 9 | - placeholder="搜索电动车..." | 9 | + input-background="#ffffff"> |
| 10 | - class="search-input" | 10 | + <template #leftin> |
| 11 | - /> | 11 | + <Search2 /> |
| 12 | + </template> | ||
| 13 | + </nut-searchbar> | ||
| 12 | </view> | 14 | </view> |
| 13 | - </view> | ||
| 14 | - | ||
| 15 | - <!-- 分类网格 --> | ||
| 16 | - <view class="categories-grid"> | ||
| 17 | - <view | ||
| 18 | - v-for="category in categories" | ||
| 19 | - :key="category.id" | ||
| 20 | - class="category-item" | ||
| 21 | - @click="onCategoryClick(category)" | ||
| 22 | - > | ||
| 23 | - <view class="category-icon"> | ||
| 24 | - <component :is="category.icon" size="32" color="#f97316" /> | ||
| 25 | - </view> | ||
| 26 | - <text class="category-name">{{ category.name }}</text> | ||
| 27 | - <text class="category-count">{{ category.count }}辆</text> | ||
| 28 | - </view> | ||
| 29 | - </view> | ||
| 30 | 15 | ||
| 31 | - <!-- 热门推荐 --> | 16 | + <!-- Filter options --> |
| 32 | - <view class="section"> | 17 | + <nut-menu> |
| 33 | - <view class="section-header"> | 18 | + <nut-menu-item v-model="selectedBrand" :options="brandOptions" @change="onBrandChange" /> |
| 34 | - <text class="section-title">热门推荐</text> | 19 | + <nut-menu-item v-model="selectedYear" :options="yearOptions" @change="onYearChange" /> |
| 35 | - <view class="section-more" @click="onViewMore('hot')"> | 20 | + <nut-menu-item v-model="selectedSchool" :options="schoolOptions" @change="onSchoolChange" /> |
| 36 | - <text class="more-text">查看更多</text> | 21 | + </nut-menu> |
| 37 | - <Right size="16" color="#9ca3af" /> | 22 | + |
| 38 | - </view> | 23 | + <!-- Scooter listings - 参考PostPage.tsx --> |
| 39 | - </view> | 24 | + <view class="flex-1 p-4 vehicle-list"> |
| 40 | - <view class="scooter-list"> | 25 | + <view class="space-y-4"> |
| 41 | - <view | 26 | + <view v-for="scooter in scooters" :key="scooter.id" |
| 42 | - v-for="scooter in hotScooters" | 27 | + class="bg-white rounded-lg shadow-sm overflow-hidden mb-3" @tap="() => onProductClick(scooter)"> |
| 43 | - :key="scooter.id" | 28 | + <view class="flex"> |
| 44 | - class="scooter-card" | 29 | + <view class="w-32 h-24 relative p-2"> |
| 45 | - @click="onProductClick(scooter)" | 30 | + <image :src="scooter.imageUrl" :alt="scooter.name" mode="aspectFill" |
| 46 | - > | 31 | + class="w-full h-full object-cover rounded-lg" /> |
| 47 | - <image :src="scooter.image" class="scooter-image" mode="aspectFill" /> | 32 | + <view v-if="scooter.isVerified" |
| 48 | - <view class="scooter-info"> | 33 | + class="absolute bottom-3 right-3 bg-orange-500 text-white text-xs px-1 rounded flex items-center"> |
| 49 | - <text class="scooter-name">{{ scooter.name }}</text> | 34 | + <Check size="12" color="#ffffff" class="mr-0.5" /> |
| 50 | - <text class="scooter-year">{{ scooter.year }}年</text> | 35 | + <text class="text-white">认证</text> |
| 51 | - <text class="scooter-school">{{ scooter.school }}</text> | 36 | + </view> |
| 52 | - <view class="scooter-footer"> | 37 | + </view> |
| 53 | - <text class="scooter-price">¥{{ scooter.price }}</text> | 38 | + <view class="flex-1 p-3 relative"> |
| 54 | - <view class="favorite-btn" @click.stop="toggleFavorite(scooter.id)"> | 39 | + <view class="absolute top-2 right-2" @tap.stop="() => toggleFavorite(scooter.id)"> |
| 55 | - <Heart | 40 | + <Addfollow v-if="!favoriteIds.includes(scooter.id)" size="16" color="#9ca3af" /> |
| 56 | - size="20" | 41 | + <HeartFill v-else size="16" color="#ef4444" /> |
| 57 | - :color="favoriteIds.includes(scooter.id) ? '#ef4444' : '#9ca3af'" | 42 | + </view> |
| 58 | - :fill="favoriteIds.includes(scooter.id) ? '#ef4444' : 'none'" | 43 | + <text class="font-medium text-sm block">{{ scooter.name }}</text> |
| 59 | - /> | 44 | + <text class="text-xs text-gray-600 mt-1 block"> |
| 45 | + {{ scooter.year }} · | ||
| 46 | + <text v-if="scooter.batteryHealth">电池健康度{{ scooter.batteryHealth }}%</text> | ||
| 47 | + <text v-if="scooter.mileage"> 行驶{{ scooter.mileage }}公里</text> | ||
| 48 | + </text> | ||
| 49 | + <view class="mt-2"> | ||
| 50 | + <text class="text-orange-500 font-bold"> | ||
| 51 | + ¥{{ scooter.price.toLocaleString() }} | ||
| 52 | + </text> | ||
| 53 | + <text class="text-xs text-gray-500 mt-1 block">{{ scooter.school }}</text> | ||
| 54 | + </view> | ||
| 60 | </view> | 55 | </view> |
| 61 | </view> | 56 | </view> |
| 62 | </view> | 57 | </view> |
| 63 | </view> | 58 | </view> |
| 64 | - </view> | ||
| 65 | - </view> | ||
| 66 | 59 | ||
| 67 | - <!-- 最新发布 --> | 60 | + <!-- 加载更多按钮 --> |
| 68 | - <view class="section"> | 61 | + <view class="load-more-container"> |
| 69 | - <view class="section-header"> | 62 | + <view v-if="loading" class="loading-container"> |
| 70 | - <text class="section-title">最新发布</text> | 63 | + <view class="loading-spinner"></view> |
| 71 | - <view class="section-more" @click="onViewMore('latest')"> | 64 | + <text class="loading-text">加载中...</text> |
| 72 | - <text class="more-text">查看更多</text> | 65 | + </view> |
| 73 | - <Right size="16" color="#9ca3af" /> | 66 | + |
| 67 | + <view v-else-if="noMoreData" class="no-more-data"> | ||
| 68 | + <text>没有更多数据了</text> | ||
| 69 | + </view> | ||
| 70 | + | ||
| 71 | + <nut-button v-else shape="round" type="info" @click="loadMoreData">点击加载更多</nut-button> | ||
| 74 | </view> | 72 | </view> |
| 75 | </view> | 73 | </view> |
| 76 | - <view class="scooter-list"> | 74 | + |
| 77 | - <view | 75 | + <!-- Featured recommendations section --> |
| 78 | - v-for="scooter in latestScooters" | 76 | + <view class="mt-6 mb-20 ml-4 mr-4"> |
| 79 | - :key="scooter.id" | 77 | + <view class="flex justify-between items-center mb-3"> |
| 80 | - class="scooter-card" | 78 | + <text class="text-lg font-medium">精品推荐</text> |
| 81 | - @click="onProductClick(scooter)" | 79 | + <view class="text-sm text-gray-500 flex items-center" @tap="onViewMore"> |
| 82 | - > | 80 | + <text>更多</text> |
| 83 | - <image :src="scooter.image" class="scooter-image" mode="aspectFill" /> | 81 | + <RectRight size="12" /> |
| 84 | - <view class="scooter-info"> | 82 | + </view> |
| 85 | - <text class="scooter-name">{{ scooter.name }}</text> | 83 | + </view> |
| 86 | - <text class="scooter-year">{{ scooter.year }}年</text> | 84 | + <view class="grid grid-cols-2 gap-3"> |
| 87 | - <text class="scooter-school">{{ scooter.school }}</text> | 85 | + <view v-for="scooter in featuredScooters" :key="scooter.id" |
| 88 | - <view class="scooter-footer"> | 86 | + class="bg-white rounded-lg shadow-sm overflow-hidden" @tap="() => onProductClick(scooter)"> |
| 89 | - <text class="scooter-price">¥{{ scooter.price }}</text> | 87 | + <view class="relative p-2"> |
| 90 | - <view class="favorite-btn" @click.stop="toggleFavorite(scooter.id)"> | 88 | + <image :src="scooter.imageUrl" :alt="scooter.name" mode="aspectFill" |
| 91 | - <Heart | 89 | + class="w-full h-36 object-cover rounded-lg" /> |
| 92 | - size="20" | 90 | + <view class="absolute top-4 right-4 p-1" @tap.stop="() => toggleFavorite(scooter.id)"> |
| 93 | - :color="favoriteIds.includes(scooter.id) ? '#ef4444' : '#9ca3af'" | 91 | + <Addfollow v-if="!favoriteIds.includes(scooter.id)" size="20" color="#ffffff" /> |
| 94 | - :fill="favoriteIds.includes(scooter.id) ? '#ef4444' : 'none'" | 92 | + <HeartFill v-else size="20" color="#ef4444" /> |
| 95 | - /> | 93 | + </view> |
| 94 | + <view v-if="scooter.isVerified" | ||
| 95 | + class="absolute bottom-4 right-4 bg-orange-500 text-white text-xs px-1.5 py-0.5 rounded flex items-center"> | ||
| 96 | + <Check size="12" color="#ffffff" class="mr-0.5" /> | ||
| 97 | + <text class="text-white">认证</text> | ||
| 98 | + </view> | ||
| 99 | + </view> | ||
| 100 | + <view class="p-2"> | ||
| 101 | + <text class="font-medium text-sm block">{{ scooter.name }}</text> | ||
| 102 | + <text class="text-xs text-gray-500 block"> | ||
| 103 | + {{ scooter.year }} · | ||
| 104 | + <text v-if="scooter.batteryHealth">电池健康度{{ scooter.batteryHealth }}%</text> | ||
| 105 | + <text v-if="scooter.mileage"> 行驶{{ scooter.mileage }}公里</text> | ||
| 106 | + </text> | ||
| 107 | + <view class="mt-1"> | ||
| 108 | + <text class="text-orange-500 font-bold"> | ||
| 109 | + ¥{{ scooter.price.toLocaleString() }} | ||
| 110 | + </text> | ||
| 111 | + <text class="text-xs text-gray-500 mt-1 block">{{ scooter.school }}</text> | ||
| 96 | </view> | 112 | </view> |
| 97 | </view> | 113 | </view> |
| 98 | </view> | 114 | </view> |
| 99 | </view> | 115 | </view> |
| 100 | </view> | 116 | </view> |
| 101 | </view> | 117 | </view> |
| 118 | + | ||
| 119 | + <!-- 自定义TabBar --> | ||
| 120 | + <TabBar /> | ||
| 102 | </view> | 121 | </view> |
| 103 | - | ||
| 104 | - <!-- 自定义TabBar --> | ||
| 105 | - <TabBar /> | ||
| 106 | </template> | 122 | </template> |
| 107 | 123 | ||
| 108 | <script setup> | 124 | <script setup> |
| 109 | import { ref } from 'vue' | 125 | import { ref } from 'vue' |
| 110 | import Taro from '@tarojs/taro' | 126 | import Taro from '@tarojs/taro' |
| 111 | -import { Search, Right, Cart, Star, Cart2, Category, Heart } from '@nutui/icons-vue-taro' | 127 | +import { Search2, Right, RectRight, Check, Addfollow, HeartFill } from '@nutui/icons-vue-taro' |
| 112 | import TabBar from '@/components/TabBar.vue' | 128 | import TabBar from '@/components/TabBar.vue' |
| 113 | 129 | ||
| 114 | // 响应式数据 | 130 | // 响应式数据 |
| 115 | const searchValue = ref('') | 131 | const searchValue = ref('') |
| 116 | -const favoriteIds = ref([1, 3, 5]) | 132 | +const favoriteIds = ref(['5', '7', '1']) |
| 117 | - | 133 | + |
| 118 | -// 分类数据 | 134 | +// 无限滚动相关状态 |
| 119 | -const categories = ref([ | 135 | +const loading = ref(false) |
| 120 | - { id: 1, name: '电动自行车', icon: Cart2, count: 128 }, | 136 | +const noMoreData = ref(false) |
| 121 | - { id: 2, name: '电动摩托车', icon: Cart2, count: 86 }, | 137 | +const currentPage = ref(1) |
| 122 | - { id: 3, name: '电动汽车', icon: Star, count: 45 }, | 138 | +const pageSize = ref(4) |
| 123 | - { id: 4, name: '电动货车', icon: Cart, count: 23 }, | 139 | + |
| 124 | - { id: 5, name: '平衡车', icon: Category, count: 67 }, | 140 | +// Filter states - 使用NutUI Menu组件 |
| 125 | - { id: 6, name: '滑板车', icon: Category, count: 92 } | 141 | +const selectedBrand = ref('全部品牌') |
| 142 | +const selectedYear = ref('出厂年份') | ||
| 143 | +const selectedSchool = ref('所在学校') | ||
| 144 | + | ||
| 145 | +// Menu选项数据 | ||
| 146 | +const brandOptions = ref([ | ||
| 147 | + { text: '全部品牌', value: '全部品牌' }, | ||
| 148 | + { text: '雅迪', value: '雅迪' }, | ||
| 149 | + { text: '台铃', value: '台铃' }, | ||
| 150 | + { text: '小鸟', value: '小鸟' }, | ||
| 151 | + { text: '新日', value: '新日' }, | ||
| 152 | + { text: '爱玛', value: '爱玛' }, | ||
| 153 | + { text: '小牛', value: '小牛' } | ||
| 154 | +]) | ||
| 155 | + | ||
| 156 | +const yearOptions = ref([ | ||
| 157 | + { text: '出厂年份', value: '出厂年份' }, | ||
| 158 | + { text: '2024年', value: '2024年' }, | ||
| 159 | + { text: '2023年', value: '2023年' }, | ||
| 160 | + { text: '2022年', value: '2022年' }, | ||
| 161 | + { text: '2021年', value: '2021年' }, | ||
| 162 | + { text: '2020年', value: '2020年' } | ||
| 163 | +]) | ||
| 164 | + | ||
| 165 | +const schoolOptions = ref([ | ||
| 166 | + { text: '所在学校', value: '所在学校' }, | ||
| 167 | + { text: '上海理工大学', value: '上海理工大学' }, | ||
| 168 | + { text: '上海复旦大学', value: '上海复旦大学' }, | ||
| 169 | + { text: '上海同济大学', value: '上海同济大学' }, | ||
| 170 | + { text: '上海交通大学', value: '上海交通大学' } | ||
| 126 | ]) | 171 | ]) |
| 127 | 172 | ||
| 128 | -// 热门推荐数据 | 173 | +// 主要车辆列表数据 - 参考PostPage.tsx |
| 129 | -const hotScooters = ref([ | 174 | +const scooters = ref([ |
| 130 | { | 175 | { |
| 131 | - id: 1, | 176 | + id: '5', |
| 132 | - name: '小牛电动 NGT', | 177 | + name: '雅迪 豪华版', |
| 133 | - year: 2023, | 178 | + year: '2023年', |
| 134 | - school: '清华大学', | 179 | + school: '上海理工大学', |
| 135 | price: 3200, | 180 | price: 3200, |
| 136 | - image: 'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=300&h=200&fit=crop' | 181 | + imageUrl: 'https://images.unsplash.com/photo-1567922045116-2a00fae2ed03?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60', |
| 182 | + batteryHealth: 98, | ||
| 183 | + mileage: 1200, | ||
| 184 | + brand: '雅迪', | ||
| 185 | + isVerified: true | ||
| 186 | + }, | ||
| 187 | + { | ||
| 188 | + id: '6', | ||
| 189 | + name: '台铃 战速', | ||
| 190 | + year: '2022年', | ||
| 191 | + school: '上海理工大学', | ||
| 192 | + price: 3800, | ||
| 193 | + imageUrl: 'https://images.unsplash.com/photo-1567922045116-2a00fae2ed03?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60', | ||
| 194 | + batteryHealth: 92, | ||
| 195 | + mileage: 2500, | ||
| 196 | + brand: '台铃' | ||
| 137 | }, | 197 | }, |
| 138 | { | 198 | { |
| 139 | - id: 2, | 199 | + id: '7', |
| 140 | - name: '雅迪 DE2', | 200 | + name: '小鸟电车', |
| 141 | - year: 2022, | 201 | + year: '2023年', |
| 142 | - school: '北京大学', | 202 | + school: '上海复旦大学', |
| 143 | - price: 2800, | 203 | + price: 3100, |
| 144 | - image: 'https://images.unsplash.com/photo-1571068316344-75bc76f77890?w=300&h=200&fit=crop' | 204 | + imageUrl: 'https://images.unsplash.com/photo-1567922045116-2a00fae2ed03?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60', |
| 205 | + batteryHealth: 92, | ||
| 206 | + mileage: 2000, | ||
| 207 | + brand: '小鸟' | ||
| 208 | + }, | ||
| 209 | + { | ||
| 210 | + id: '8', | ||
| 211 | + name: '新日电动车', | ||
| 212 | + year: '2024年', | ||
| 213 | + school: '上海同济大学', | ||
| 214 | + price: 6700, | ||
| 215 | + imageUrl: 'https://images.unsplash.com/photo-1595941069915-4ebc5197c14a?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60', | ||
| 216 | + batteryHealth: 96, | ||
| 217 | + mileage: 500, | ||
| 218 | + brand: '新日', | ||
| 219 | + isVerified: true | ||
| 145 | } | 220 | } |
| 146 | ]) | 221 | ]) |
| 147 | 222 | ||
| 148 | -// 最新发布数据 | 223 | +// 精品推荐数据 - 参考PostPage.tsx |
| 149 | -const latestScooters = ref([ | 224 | +const featuredScooters = ref([ |
| 150 | { | 225 | { |
| 151 | - id: 3, | 226 | + id: '1', |
| 152 | - name: '爱玛 A500', | 227 | + name: '小龟电动车', |
| 153 | - year: 2024, | 228 | + year: '2023年', |
| 154 | - school: '人民大学', | 229 | + school: '上海理工大学', |
| 155 | - price: 2600, | 230 | + price: 3880, |
| 156 | - image: 'https://images.unsplash.com/photo-1544191696-15693072e0d8?w=300&h=200&fit=crop' | 231 | + batteryHealth: 92, |
| 232 | + mileage: 2000, | ||
| 233 | + imageUrl: 'https://images.unsplash.com/photo-1558981285-6f0c94958bb6?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60', | ||
| 234 | + isVerified: true | ||
| 157 | }, | 235 | }, |
| 158 | { | 236 | { |
| 159 | - id: 4, | 237 | + id: '2', |
| 160 | - name: '台铃 TDR', | 238 | + name: '立马电动车', |
| 161 | - year: 2023, | 239 | + year: '2022年', |
| 162 | - school: '北京理工', | 240 | + school: '上海复旦大学', |
| 163 | - price: 3500, | 241 | + price: 2999, |
| 164 | - image: 'https://images.unsplash.com/photo-1558618047-3c8c76ca7d13?w=300&h=200&fit=crop' | 242 | + batteryHealth: 95, |
| 243 | + mileage: 1500, | ||
| 244 | + imageUrl: 'https://images.unsplash.com/photo-1558981403-c5f9899a28bc?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60' | ||
| 165 | } | 245 | } |
| 166 | ]) | 246 | ]) |
| 167 | 247 | ||
| 168 | /** | 248 | /** |
| 169 | * 切换收藏状态 | 249 | * 切换收藏状态 |
| 170 | - * @param {number} id - 电动车ID | 250 | + * @param {string} scooterId - 电动车ID |
| 171 | */ | 251 | */ |
| 172 | -const toggleFavorite = (id) => { | 252 | +const toggleFavorite = (scooterId) => { |
| 173 | - const index = favoriteIds.value.indexOf(id) | 253 | + const index = favoriteIds.value.indexOf(scooterId) |
| 174 | if (index > -1) { | 254 | if (index > -1) { |
| 175 | favoriteIds.value.splice(index, 1) | 255 | favoriteIds.value.splice(index, 1) |
| 176 | } else { | 256 | } else { |
| 177 | - favoriteIds.value.push(id) | 257 | + favoriteIds.value.push(scooterId) |
| 178 | } | 258 | } |
| 179 | } | 259 | } |
| 180 | 260 | ||
| 181 | -// 事件处理函数 | 261 | +/** |
| 182 | -const onCategoryClick = () => { | 262 | + * 点击产品卡片 |
| 263 | + * @param {Object} scooter - 电动车信息 | ||
| 264 | + */ | ||
| 265 | +const onProductClick = (scooter) => { | ||
| 183 | Taro.showToast({ | 266 | Taro.showToast({ |
| 184 | - title: '选择了分类', | 267 | + title: `查看${scooter.name}`, |
| 185 | icon: 'none' | 268 | icon: 'none' |
| 186 | }) | 269 | }) |
| 187 | } | 270 | } |
| 188 | 271 | ||
| 189 | -const onProductClick = () => { | ||
| 190 | - Taro.navigateTo({ | ||
| 191 | - url: '/pages/detail/index' | ||
| 192 | - }) | ||
| 193 | -} | ||
| 194 | - | ||
| 195 | /** | 272 | /** |
| 196 | * 查看更多点击事件 | 273 | * 查看更多点击事件 |
| 197 | */ | 274 | */ |
| 198 | const onViewMore = () => { | 275 | const onViewMore = () => { |
| 199 | - // 跳转到列表页面 | 276 | + Taro.showToast({ |
| 200 | -} | 277 | + title: '查看更多精品推荐', |
| 201 | -</script> | 278 | + icon: 'none' |
| 202 | - | 279 | + }) |
| 203 | -<style lang="less"> | ||
| 204 | -.post-page { | ||
| 205 | - min-height: 100vh; | ||
| 206 | - background-color: #fef7ed; | ||
| 207 | - padding-bottom: 100px; | ||
| 208 | } | 280 | } |
| 209 | 281 | ||
| 210 | -.search-container { | 282 | +// Menu组件事件处理方法 |
| 211 | - padding: 16px; | 283 | +/** |
| 212 | - background-color: #ffffff; | 284 | + * 品牌选择变化事件 |
| 213 | - border-bottom: 1px solid #f3f4f6; | 285 | + * @param {string} value - 选中的品牌值 |
| 286 | + */ | ||
| 287 | +const onBrandChange = (value) => { | ||
| 288 | + selectedBrand.value = value | ||
| 289 | + // 这里可以添加过滤逻辑 | ||
| 214 | } | 290 | } |
| 215 | 291 | ||
| 216 | -.search-box { | 292 | +/** |
| 217 | - display: flex; | 293 | + * 年份选择变化事件 |
| 218 | - align-items: center; | 294 | + * @param {string} value - 选中的年份值 |
| 219 | - background-color: #f9fafb; | 295 | + */ |
| 220 | - border-radius: 24px; | 296 | +const onYearChange = (value) => { |
| 221 | - padding: 12px 16px; | 297 | + selectedYear.value = value |
| 222 | - gap: 8px; | 298 | + // 这里可以添加过滤逻辑 |
| 223 | } | 299 | } |
| 224 | 300 | ||
| 225 | -.search-input { | 301 | +/** |
| 226 | - flex: 1; | 302 | + * 学校选择变化事件 |
| 227 | - border: none; | 303 | + * @param {string} value - 选中的学校值 |
| 228 | - outline: none; | 304 | + */ |
| 229 | - background: transparent; | 305 | +const onSchoolChange = (value) => { |
| 230 | - font-size: 14px; | 306 | + selectedSchool.value = value |
| 231 | - color: #374151; | 307 | + // 这里可以添加过滤逻辑 |
| 232 | } | 308 | } |
| 233 | 309 | ||
| 234 | -.categories-grid { | 310 | +/** |
| 235 | - display: grid; | 311 | + * 生成模拟车辆数据 |
| 236 | - grid-template-columns: repeat(3, 1fr); | 312 | + * @param {number} page - 页码 |
| 237 | - gap: 16px; | 313 | + * @param {number} size - 每页数量 |
| 238 | - padding: 20px 16px; | 314 | + * @returns {Array} 车辆数据数组 |
| 239 | - background-color: #ffffff; | 315 | + */ |
| 240 | - margin-bottom: 12px; | 316 | +const generateMockData = (page, size) => { |
| 317 | + const brands = ['雅迪', '台铃', '小鸟', '新日', '爱玛', '小牛', '绿源', '立马'] | ||
| 318 | + const schools = ['上海理工大学', '上海复旦大学', '上海同济大学', '上海交通大学', '华东师范大学', '上海大学'] | ||
| 319 | + const years = ['2024年', '2023年', '2022年', '2021年', '2020年'] | ||
| 320 | + const images = [ | ||
| 321 | + 'https://images.unsplash.com/photo-1567922045116-2a00fae2ed03?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60', | ||
| 322 | + 'https://images.unsplash.com/photo-1573981368236-719bbb6f70f7?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60', | ||
| 323 | + 'https://images.unsplash.com/photo-1583568671741-c70dafa8e8e7?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60', | ||
| 324 | + 'https://images.unsplash.com/photo-1595941069915-4ebc5197c14a?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60', | ||
| 325 | + 'https://images.unsplash.com/photo-1558981285-6f0c94958bb6?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60', | ||
| 326 | + 'https://images.unsplash.com/photo-1558981403-c5f9899a28bc?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60' | ||
| 327 | + ] | ||
| 328 | + | ||
| 329 | + const data = [] | ||
| 330 | + for (let i = 0; i < size; i++) { | ||
| 331 | + const index = (page - 1) * size + i | ||
| 332 | + const brand = brands[Math.floor(Math.random() * brands.length)] | ||
| 333 | + const school = schools[Math.floor(Math.random() * schools.length)] | ||
| 334 | + const year = years[Math.floor(Math.random() * years.length)] | ||
| 335 | + const image = images[Math.floor(Math.random() * images.length)] | ||
| 336 | + | ||
| 337 | + data.push({ | ||
| 338 | + id: `mock_${index + 100}`, | ||
| 339 | + name: `${brand} ${['豪华版', '标准版', '运动版', '经典版'][Math.floor(Math.random() * 4)]}`, | ||
| 340 | + year: year, | ||
| 341 | + school: school, | ||
| 342 | + price: Math.floor(Math.random() * 5000) + 2000, | ||
| 343 | + imageUrl: image, | ||
| 344 | + batteryHealth: Math.floor(Math.random() * 20) + 80, | ||
| 345 | + mileage: Math.floor(Math.random() * 3000) + 500, | ||
| 346 | + brand: brand, | ||
| 347 | + isVerified: Math.random() > 0.7 | ||
| 348 | + }) | ||
| 349 | + } | ||
| 350 | + return data | ||
| 241 | } | 351 | } |
| 242 | 352 | ||
| 243 | -.category-item { | 353 | +/** |
| 244 | - display: flex; | 354 | + * 模拟异步请求加载更多数据 |
| 245 | - flex-direction: column; | 355 | + */ |
| 246 | - align-items: center; | 356 | +const loadMoreData = async () => { |
| 247 | - padding: 20px 12px; | 357 | + if (loading.value || noMoreData.value) return |
| 248 | - background-color: #fef7ed; | 358 | + |
| 249 | - border-radius: 12px; | 359 | + loading.value = true |
| 250 | - border: 1px solid #fed7aa; | 360 | + |
| 251 | - transition: all 0.2s; | 361 | + try { |
| 362 | + // 模拟网络请求延迟 | ||
| 363 | + await new Promise(resolve => setTimeout(resolve, 1000 + Math.random() * 1000)) | ||
| 364 | + | ||
| 365 | + // 模拟最多加载5页数据 | ||
| 366 | + if (currentPage.value >= 5) { | ||
| 367 | + noMoreData.value = true | ||
| 368 | + loading.value = false | ||
| 369 | + return | ||
| 370 | + } | ||
| 371 | + | ||
| 372 | + currentPage.value++ | ||
| 373 | + const newData = generateMockData(currentPage.value, pageSize.value) | ||
| 374 | + scooters.value.push(...newData) | ||
| 375 | + | ||
| 376 | + } catch (error) { | ||
| 377 | + Taro.showToast({ | ||
| 378 | + title: '加载失败,请重试', | ||
| 379 | + icon: 'none' | ||
| 380 | + }) | ||
| 381 | + } finally { | ||
| 382 | + loading.value = false | ||
| 383 | + } | ||
| 252 | } | 384 | } |
| 385 | +</script> | ||
| 253 | 386 | ||
| 254 | -.category-item:active { | 387 | +<style lang="less"> |
| 255 | - transform: scale(0.95); | 388 | +// 使用Tailwind CSS类,只保留必要的自定义样式 |
| 256 | - background-color: #fef3e2; | 389 | +.space-y-4>view:not(:first-child) { |
| 390 | + margin-top: 1rem; | ||
| 257 | } | 391 | } |
| 258 | 392 | ||
| 259 | -.category-icon { | 393 | +.grid { |
| 260 | - margin-bottom: 8px; | 394 | + display: grid; |
| 261 | } | 395 | } |
| 262 | 396 | ||
| 263 | -.category-name { | 397 | +.grid-cols-2 { |
| 264 | - font-size: 14px; | 398 | + grid-template-columns: repeat(2, 1fr); |
| 265 | - font-weight: 500; | ||
| 266 | - color: #374151; | ||
| 267 | - margin-bottom: 4px; | ||
| 268 | } | 399 | } |
| 269 | 400 | ||
| 270 | -.category-count { | 401 | +.gap-3 { |
| 271 | - font-size: 12px; | 402 | + gap: 0.75rem; |
| 272 | - color: #9ca3af; | ||
| 273 | } | 403 | } |
| 274 | 404 | ||
| 275 | -.section { | 405 | +// 确保图片正确显示 |
| 276 | - background-color: #ffffff; | 406 | +image { |
| 277 | - margin-bottom: 12px; | 407 | + display: block; |
| 278 | - padding: 16px; | ||
| 279 | } | 408 | } |
| 280 | 409 | ||
| 281 | -.section-header { | 410 | +// 加载动画 |
| 411 | +.load-more-container { | ||
| 282 | display: flex; | 412 | display: flex; |
| 283 | - justify-content: space-between; | 413 | + justify-content: center; |
| 284 | - align-items: center; | 414 | + padding: 2rem 0; |
| 285 | - margin-bottom: 16px; | ||
| 286 | -} | ||
| 287 | - | ||
| 288 | -.section-title { | ||
| 289 | - font-size: 18px; | ||
| 290 | - font-weight: 600; | ||
| 291 | - color: #111827; | ||
| 292 | } | 415 | } |
| 293 | 416 | ||
| 294 | -.section-more { | 417 | +.loading-container { |
| 295 | display: flex; | 418 | display: flex; |
| 296 | align-items: center; | 419 | align-items: center; |
| 297 | - gap: 4px; | 420 | + color: #666; |
| 298 | -} | ||
| 299 | - | ||
| 300 | -.more-text { | ||
| 301 | - font-size: 14px; | ||
| 302 | - color: #9ca3af; | ||
| 303 | } | 421 | } |
| 304 | 422 | ||
| 305 | -.scooter-list { | 423 | +.loading-spinner { |
| 306 | - display: grid; | 424 | + width: 20px; |
| 307 | - grid-template-columns: repeat(2, 1fr); | 425 | + height: 20px; |
| 308 | - gap: 12px; | 426 | + border: 2px solid #f3f3f3; |
| 309 | -} | 427 | + border-top: 2px solid #3498db; |
| 310 | - | 428 | + border-radius: 50%; |
| 311 | -.scooter-card { | 429 | + animation: spin 1s linear infinite; |
| 312 | - background-color: #ffffff; | 430 | + margin-right: 8px; |
| 313 | - border-radius: 12px; | ||
| 314 | - overflow: hidden; | ||
| 315 | - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); | ||
| 316 | - transition: all 0.2s; | ||
| 317 | -} | ||
| 318 | - | ||
| 319 | -.scooter-card:active { | ||
| 320 | - transform: scale(0.98); | ||
| 321 | - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); | ||
| 322 | } | 431 | } |
| 323 | 432 | ||
| 324 | -.scooter-image { | 433 | +.loading-text { |
| 325 | - width: 100%; | 434 | + font-size: 0.85rem; |
| 326 | - height: 120px; | ||
| 327 | - object-fit: cover; | ||
| 328 | } | 435 | } |
| 329 | 436 | ||
| 330 | -.scooter-info { | 437 | +.no-more-data { |
| 331 | - padding: 12px; | 438 | + color: #999; |
| 439 | + font-size: 0.85rem; | ||
| 332 | } | 440 | } |
| 333 | 441 | ||
| 334 | -.scooter-name { | 442 | +.load-more-btn { |
| 443 | + background-color: #007aff; | ||
| 444 | + color: white; | ||
| 445 | + border: none; | ||
| 446 | + border-radius: 20px; | ||
| 447 | + padding: 10px 20px; | ||
| 335 | font-size: 14px; | 448 | font-size: 14px; |
| 336 | - font-weight: 600; | 449 | + cursor: pointer; |
| 337 | - color: #111827; | ||
| 338 | - margin-bottom: 4px; | ||
| 339 | - display: block; | ||
| 340 | } | 450 | } |
| 341 | 451 | ||
| 342 | -.scooter-year { | 452 | +.load-more-btn:hover { |
| 343 | - font-size: 12px; | 453 | + background-color: #0056cc; |
| 344 | - color: #6b7280; | ||
| 345 | - margin-bottom: 2px; | ||
| 346 | - display: block; | ||
| 347 | } | 454 | } |
| 348 | 455 | ||
| 349 | -.scooter-school { | 456 | +@keyframes spin { |
| 350 | - font-size: 12px; | 457 | + 0% { |
| 351 | - color: #9ca3af; | 458 | + transform: rotate(0deg); |
| 352 | - margin-bottom: 8px; | 459 | + } |
| 353 | - display: block; | ||
| 354 | -} | ||
| 355 | - | ||
| 356 | -.scooter-footer { | ||
| 357 | - display: flex; | ||
| 358 | - justify-content: space-between; | ||
| 359 | - align-items: center; | ||
| 360 | -} | ||
| 361 | - | ||
| 362 | -.scooter-price { | ||
| 363 | - font-size: 16px; | ||
| 364 | - font-weight: 700; | ||
| 365 | - color: #f97316; | ||
| 366 | -} | ||
| 367 | - | ||
| 368 | -.favorite-btn { | ||
| 369 | - padding: 4px; | ||
| 370 | - border-radius: 50%; | ||
| 371 | - transition: all 0.2s; | ||
| 372 | -} | ||
| 373 | 460 | ||
| 374 | -.favorite-btn:active { | 461 | + 100% { |
| 375 | - transform: scale(0.9); | 462 | + transform: rotate(360deg); |
| 463 | + } | ||
| 376 | } | 464 | } |
| 377 | </style> | 465 | </style> | ... | ... |
-
Please register or login to post a comment