hookehuyr

feat(页面): 重构发布页并添加NutUI组件

- 使用NutUI组件重构发布页界面,包括搜索栏、菜单筛选和按钮
- 替换Right图标为RectRight以保持一致性
- 添加加载更多功能和模拟数据生成逻辑
- 更新车辆卡片布局和样式,增加认证标识
...@@ -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>
......