feat(车辆管理): 实现我的车辆页面及编辑功能
- 新增我的车辆页面,展示用户车辆列表 - 添加车辆编辑和认证功能 - 实现车辆上下架操作 - 优化表单页面支持编辑模式 - 添加空状态和加载更多功能
Showing
6 changed files
with
736 additions
and
34 deletions
src/pages/myCar/index.less
0 → 100644
| 1 | +.my-car-page { | ||
| 2 | + min-height: 100vh; | ||
| 3 | + background-color: #f5f5f5; | ||
| 4 | + padding-bottom: 120px; | ||
| 5 | +} | ||
| 6 | + | ||
| 7 | +.car-list { | ||
| 8 | + padding: 20px; | ||
| 9 | +} | ||
| 10 | + | ||
| 11 | +/* 空状态样式 */ | ||
| 12 | +.empty-state { | ||
| 13 | + display: flex; | ||
| 14 | + flex-direction: column; | ||
| 15 | + align-items: center; | ||
| 16 | + justify-content: center; | ||
| 17 | + padding: 100px 40px; | ||
| 18 | + text-align: center; | ||
| 19 | +} | ||
| 20 | + | ||
| 21 | +.empty-image { | ||
| 22 | + width: 200px; | ||
| 23 | + height: 150px; | ||
| 24 | + margin-bottom: 30px; | ||
| 25 | + opacity: 0.6; | ||
| 26 | +} | ||
| 27 | + | ||
| 28 | +.empty-text { | ||
| 29 | + font-size: 32px; | ||
| 30 | + color: #999; | ||
| 31 | + margin-bottom: 40px; | ||
| 32 | +} | ||
| 33 | + | ||
| 34 | +/* 车辆卡片样式 */ | ||
| 35 | +.car-card { | ||
| 36 | + background: white; | ||
| 37 | + border-radius: 16px; | ||
| 38 | + margin-bottom: 20px; | ||
| 39 | + padding: 20px; | ||
| 40 | + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); | ||
| 41 | + position: relative; | ||
| 42 | +} | ||
| 43 | + | ||
| 44 | +/* 状态标识 */ | ||
| 45 | +.status-badges { | ||
| 46 | + position: absolute; | ||
| 47 | + top: 20px; | ||
| 48 | + right: 20px; | ||
| 49 | + z-index: 2; | ||
| 50 | + display: flex; | ||
| 51 | + flex-direction: column; | ||
| 52 | + gap: 8px; | ||
| 53 | +} | ||
| 54 | + | ||
| 55 | +.status-badge { | ||
| 56 | + display: flex; | ||
| 57 | + align-items: center; | ||
| 58 | + gap: 4px; | ||
| 59 | + padding: 4px 8px; | ||
| 60 | + border-radius: 12px; | ||
| 61 | + font-size: 22px; | ||
| 62 | + color: white; | ||
| 63 | + | ||
| 64 | + &.verified { | ||
| 65 | + background: linear-gradient(135deg, #10b981, #059669); | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + &.offline { | ||
| 69 | + background: linear-gradient(135deg, #ef4444, #dc2626); | ||
| 70 | + } | ||
| 71 | +} | ||
| 72 | + | ||
| 73 | +.status-icon { | ||
| 74 | + width: 16px; | ||
| 75 | + height: 16px; | ||
| 76 | +} | ||
| 77 | + | ||
| 78 | +/* 车辆图片 */ | ||
| 79 | +.car-image-container { | ||
| 80 | + width: 100%; | ||
| 81 | + height: 200px; | ||
| 82 | + border-radius: 12px; | ||
| 83 | + overflow: hidden; | ||
| 84 | + margin-bottom: 20px; | ||
| 85 | +} | ||
| 86 | + | ||
| 87 | +.car-image { | ||
| 88 | + width: 100%; | ||
| 89 | + height: 100%; | ||
| 90 | + object-fit: cover; | ||
| 91 | +} | ||
| 92 | + | ||
| 93 | +/* 车辆信息 */ | ||
| 94 | +.car-info { | ||
| 95 | + margin-bottom: 20px; | ||
| 96 | +} | ||
| 97 | + | ||
| 98 | +.car-title { | ||
| 99 | + font-size: 36px; | ||
| 100 | + font-weight: bold; | ||
| 101 | + color: #333; | ||
| 102 | + margin-bottom: 12px; | ||
| 103 | +} | ||
| 104 | + | ||
| 105 | +.car-details { | ||
| 106 | + display: flex; | ||
| 107 | + gap: 16px; | ||
| 108 | + margin-bottom: 12px; | ||
| 109 | +} | ||
| 110 | + | ||
| 111 | +.detail-item { | ||
| 112 | + font-size: 26px; | ||
| 113 | + color: #666; | ||
| 114 | + padding: 4px 12px; | ||
| 115 | + background: #f3f4f6; | ||
| 116 | + border-radius: 8px; | ||
| 117 | +} | ||
| 118 | + | ||
| 119 | +.car-description { | ||
| 120 | + font-size: 28px; | ||
| 121 | + color: #666; | ||
| 122 | + line-height: 1.5; | ||
| 123 | + margin-bottom: 16px; | ||
| 124 | +} | ||
| 125 | + | ||
| 126 | +.price-section { | ||
| 127 | + display: flex; | ||
| 128 | + align-items: baseline; | ||
| 129 | + gap: 16px; | ||
| 130 | +} | ||
| 131 | + | ||
| 132 | +.current-price { | ||
| 133 | + font-size: 40px; | ||
| 134 | + font-weight: bold; | ||
| 135 | + color: #f97316; | ||
| 136 | +} | ||
| 137 | + | ||
| 138 | +.market-price { | ||
| 139 | + font-size: 24px; | ||
| 140 | + color: #999; | ||
| 141 | + text-decoration: line-through; | ||
| 142 | +} | ||
| 143 | + | ||
| 144 | +/* 操作按钮 */ | ||
| 145 | +.action-buttons { | ||
| 146 | + display: flex; | ||
| 147 | + gap: 12px; | ||
| 148 | + justify-content: flex-end; | ||
| 149 | +} | ||
| 150 | + | ||
| 151 | +/* 加载更多 */ | ||
| 152 | +.load-more { | ||
| 153 | + display: flex; | ||
| 154 | + justify-content: center; | ||
| 155 | + padding: 40px 0; | ||
| 156 | +} | ||
| 157 | + | ||
| 158 | +.loading-text { | ||
| 159 | + display: flex; | ||
| 160 | + align-items: center; | ||
| 161 | + gap: 12px; | ||
| 162 | + font-size: 28px; | ||
| 163 | + color: #666; | ||
| 164 | +} | ||
| 165 | + | ||
| 166 | +/* 没有更多数据 */ | ||
| 167 | +.no-more { | ||
| 168 | + text-align: center; | ||
| 169 | + padding: 40px 0; | ||
| 170 | + font-size: 28px; | ||
| 171 | + color: #999; | ||
| 172 | +} | ||
| 173 | + | ||
| 174 | +/* 响应式适配 */ | ||
| 175 | +@media (max-width: 750px) { | ||
| 176 | + .car-list { | ||
| 177 | + padding: 15px; | ||
| 178 | + } | ||
| 179 | + | ||
| 180 | + .car-card { | ||
| 181 | + padding: 15px; | ||
| 182 | + margin-bottom: 15px; | ||
| 183 | + } | ||
| 184 | + | ||
| 185 | + .car-title { | ||
| 186 | + font-size: 32px; | ||
| 187 | + } | ||
| 188 | + | ||
| 189 | + .detail-item { | ||
| 190 | + font-size: 24px; | ||
| 191 | + padding: 3px 10px; | ||
| 192 | + } | ||
| 193 | + | ||
| 194 | + .car-description { | ||
| 195 | + font-size: 26px; | ||
| 196 | + } | ||
| 197 | + | ||
| 198 | + .current-price { | ||
| 199 | + font-size: 36px; | ||
| 200 | + } | ||
| 201 | + | ||
| 202 | + .market-price { | ||
| 203 | + font-size: 22px; | ||
| 204 | + } | ||
| 205 | + | ||
| 206 | + .action-buttons { | ||
| 207 | + gap: 8px; | ||
| 208 | + } | ||
| 209 | + | ||
| 210 | + .status-badge { | ||
| 211 | + font-size: 20px; | ||
| 212 | + padding: 3px 6px; | ||
| 213 | + } | ||
| 214 | + | ||
| 215 | + .status-icon { | ||
| 216 | + width: 14px; | ||
| 217 | + height: 14px; | ||
| 218 | + } | ||
| 219 | +} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 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-03 12:55:54 | 4 | + * @LastEditTime: 2025-07-03 14:06:14 |
| 5 | - * @FilePath: /jgdl/src/pages/myFavorites/index.vue | 5 | + * @FilePath: /jgdl/src/pages/myCar/index.vue |
| 6 | * @Description: 文件描述 | 6 | * @Description: 文件描述 |
| 7 | --> | 7 | --> |
| 8 | <template> | 8 | <template> |
| 9 | - <div class="red">{{ str }}</div> | 9 | + <view class="my-car-page"> |
| 10 | + <!-- 车辆列表 --> | ||
| 11 | + <view class="flex-1"> | ||
| 12 | + <!-- 滚动列表 --> | ||
| 13 | + <scroll-view | ||
| 14 | + class="car-list" | ||
| 15 | + :style="scrollStyle" | ||
| 16 | + :scroll-y="true" | ||
| 17 | + @scrolltolower="loadMore" | ||
| 18 | + @scroll="scroll" | ||
| 19 | + :lower-threshold="50" | ||
| 20 | + :enable-flex="false" | ||
| 21 | + > | ||
| 22 | + <!-- 空状态 --> | ||
| 23 | + <view v-if="!loading && carList.length === 0" class="empty-state"> | ||
| 24 | + <image src="/static/images/empty-car.png" class="empty-image" mode="aspectFit" /> | ||
| 25 | + <text class="empty-text">暂无车源信息</text> | ||
| 26 | + <nut-button color="#f97316" size="small" @click="goToSell">发布车源</nut-button> | ||
| 27 | + </view> | ||
| 28 | + | ||
| 29 | + <!-- 车辆卡片列表 --> | ||
| 30 | + <view v-else class="space-y-4"> | ||
| 31 | + <view v-for="car in carList" :key="car.id" class="car-card"> | ||
| 32 | + <!-- 状态标识 --> | ||
| 33 | + <view class="status-badges"> | ||
| 34 | + <view v-if="car.isAuthenticated" class="status-badge verified"> | ||
| 35 | + <Check class="status-icon" /> | ||
| 36 | + <text>已认证</text> | ||
| 37 | + </view> | ||
| 38 | + <view v-if="car.isOffline" class="status-badge offline"> | ||
| 39 | + <Close class="status-icon" /> | ||
| 40 | + <text>已下架</text> | ||
| 41 | + </view> | ||
| 42 | + </view> | ||
| 43 | + | ||
| 44 | + <!-- 车辆图片 --> | ||
| 45 | + <view class="car-image-container"> | ||
| 46 | + <image :src="car.image" class="car-image" mode="aspectFill" /> | ||
| 47 | + </view> | ||
| 48 | + | ||
| 49 | + <!-- 车辆信息 --> | ||
| 50 | + <view class="car-info"> | ||
| 51 | + <view class="car-title">{{ car.brand }} {{ car.model }}</view> | ||
| 52 | + <view class="car-details"> | ||
| 53 | + <text class="detail-item">{{ car.year }}</text> | ||
| 54 | + <text class="detail-item">{{ car.condition }}</text> | ||
| 55 | + <text class="detail-item">{{ car.mileage }}公里</text> | ||
| 56 | + </view> | ||
| 57 | + <view class="car-description">{{ car.description }}</view> | ||
| 58 | + <view class="price-section"> | ||
| 59 | + <view class="current-price">¥{{ car.price }}</view> | ||
| 60 | + <view class="market-price">市场价 ¥{{ car.marketPrice }}</view> | ||
| 61 | + </view> | ||
| 62 | + </view> | ||
| 63 | + | ||
| 64 | + <!-- 操作按钮 --> | ||
| 65 | + <view class="action-buttons"> | ||
| 66 | + <nut-button size="small" type="default" @click="editCar(car.id)">编辑</nut-button> | ||
| 67 | + <nut-button | ||
| 68 | + size="small" | ||
| 69 | + :type="car.isOffline ? 'success' : 'warning'" | ||
| 70 | + @click="toggleOffline(car)" | ||
| 71 | + > | ||
| 72 | + {{ car.isOffline ? '上架' : '下架' }} | ||
| 73 | + </nut-button> | ||
| 74 | + <nut-button | ||
| 75 | + v-if="!car.isAuthenticated" | ||
| 76 | + size="small" | ||
| 77 | + type="primary" | ||
| 78 | + @click="authCar(car.id)" | ||
| 79 | + > | ||
| 80 | + 认证 | ||
| 81 | + </nut-button> | ||
| 82 | + </view> | ||
| 83 | + </view> | ||
| 84 | + </view> | ||
| 85 | + | ||
| 86 | + <!-- Loading indicator --> | ||
| 87 | + <view v-if="loading" class="loading-container py-4 text-center"> | ||
| 88 | + <text class="loading-text text-gray-500">加载中...</text> | ||
| 89 | + </view> | ||
| 90 | + | ||
| 91 | + <!-- 没有更多数据 --> | ||
| 92 | + <view v-if="!hasMore && carList.length > 0" class="no-more-container py-4 text-center"> | ||
| 93 | + <text class="text-gray-400 text-sm">没有更多数据了</text> | ||
| 94 | + </view> | ||
| 95 | + </scroll-view> | ||
| 96 | + </view> | ||
| 97 | + | ||
| 98 | + <!-- 下架确认弹窗 --> | ||
| 99 | + <nut-dialog | ||
| 100 | + v-model:visible="offlineDialogVisible" | ||
| 101 | + title="确认操作" | ||
| 102 | + :content="offlineDialogContent" | ||
| 103 | + @confirm="confirmOffline" | ||
| 104 | + @cancel="cancelOffline" | ||
| 105 | + /> | ||
| 106 | + </view> | ||
| 10 | </template> | 107 | </template> |
| 11 | 108 | ||
| 12 | <script setup> | 109 | <script setup> |
| 13 | -// import '@tarojs/taro/html.css' | 110 | +import { ref, computed, onMounted } from 'vue' |
| 14 | -import { ref } from "vue"; | 111 | +import { Check, Close } from '@nutui/icons-vue-taro' |
| 112 | +import Taro from '@tarojs/taro' | ||
| 113 | +import './index.less' | ||
| 114 | + | ||
| 115 | +// 添加样式定义 | ||
| 116 | + | ||
| 117 | +/** | ||
| 118 | + * 滚动样式 - 考虑header和TabBar的高度 | ||
| 119 | + */ | ||
| 120 | +const scrollStyle = computed(() => { | ||
| 121 | + return { | ||
| 122 | + height: 'calc(100vh)' // 减去header和TabBar的高度 | ||
| 123 | + } | ||
| 124 | +}) | ||
| 125 | + | ||
| 126 | + | ||
| 127 | + | ||
| 128 | +// 页面状态 | ||
| 129 | +const loading = ref(false) | ||
| 130 | +const hasMore = ref(true) | ||
| 131 | +const currentPage = ref(1) | ||
| 132 | +const pageSize = ref(10) | ||
| 133 | + | ||
| 134 | +// 车辆列表数据 | ||
| 135 | +const carList = ref([]) | ||
| 136 | + | ||
| 137 | +// 下架确认弹窗 | ||
| 138 | +const offlineDialogVisible = ref(false) | ||
| 139 | +const offlineDialogContent = ref('') | ||
| 140 | +const currentOfflineCar = ref(null) | ||
| 141 | + | ||
| 142 | + | ||
| 143 | + | ||
| 144 | +/** | ||
| 145 | + * 跳转到发布车源页面 | ||
| 146 | + */ | ||
| 147 | +const goToSell = () => { | ||
| 148 | + Taro.navigateTo({ | ||
| 149 | + url: '/pages/sell/index' | ||
| 150 | + }) | ||
| 151 | +} | ||
| 152 | + | ||
| 153 | +/** | ||
| 154 | + * 编辑车源 | ||
| 155 | + */ | ||
| 156 | +const editCar = (carId) => { | ||
| 157 | + Taro.navigateTo({ | ||
| 158 | + url: `/pages/sell/index?id=${carId}&mode=edit` | ||
| 159 | + }) | ||
| 160 | +} | ||
| 161 | + | ||
| 162 | +/** | ||
| 163 | + * 认证车源 | ||
| 164 | + */ | ||
| 165 | +const authCar = (carId) => { | ||
| 166 | + Taro.navigateTo({ | ||
| 167 | + url: `/pages/setAuthCar/index?id=${carId}&mode=edit` | ||
| 168 | + }) | ||
| 169 | +} | ||
| 170 | + | ||
| 171 | +/** | ||
| 172 | + * 切换上下架状态 | ||
| 173 | + */ | ||
| 174 | +const toggleOffline = (car) => { | ||
| 175 | + currentOfflineCar.value = car | ||
| 176 | + offlineDialogContent.value = car.isOffline ? '确认要上架此车源吗?' : '确认要下架此车源吗?' | ||
| 177 | + offlineDialogVisible.value = true | ||
| 178 | +} | ||
| 179 | + | ||
| 180 | +/** | ||
| 181 | + * 确认上下架操作 | ||
| 182 | + */ | ||
| 183 | +const confirmOffline = () => { | ||
| 184 | + if (currentOfflineCar.value) { | ||
| 185 | + const car = currentOfflineCar.value | ||
| 186 | + car.isOffline = !car.isOffline | ||
| 187 | + | ||
| 188 | + // TODO: 调用API更新车源状态 | ||
| 189 | + // updateCarStatus(car.id, { isOffline: car.isOffline }) | ||
| 190 | + | ||
| 191 | + Taro.showToast({ | ||
| 192 | + title: car.isOffline ? '已下架' : '已上架', | ||
| 193 | + icon: 'success' | ||
| 194 | + }) | ||
| 195 | + } | ||
| 196 | + offlineDialogVisible.value = false | ||
| 197 | + currentOfflineCar.value = null | ||
| 198 | +} | ||
| 199 | + | ||
| 200 | +/** | ||
| 201 | + * 取消上下架操作 | ||
| 202 | + */ | ||
| 203 | +const cancelOffline = () => { | ||
| 204 | + offlineDialogVisible.value = false | ||
| 205 | + currentOfflineCar.value = null | ||
| 206 | +} | ||
| 207 | + | ||
| 208 | +/** | ||
| 209 | + * 获取车辆列表数据 | ||
| 210 | + */ | ||
| 211 | +const fetchCarList = async (page = 1, append = false) => { | ||
| 212 | + loading.value = true | ||
| 213 | + | ||
| 214 | + try { | ||
| 215 | + // 模拟API调用延迟 | ||
| 216 | + await new Promise(resolve => setTimeout(resolve, 800)) | ||
| 217 | + | ||
| 218 | + const mockData = generateMockCarData(page, pageSize.value) | ||
| 219 | + | ||
| 220 | + if (append) { | ||
| 221 | + carList.value.push(...mockData) | ||
| 222 | + } else { | ||
| 223 | + carList.value = mockData | ||
| 224 | + } | ||
| 225 | + | ||
| 226 | + // 模拟分页逻辑 | ||
| 227 | + if (page >= 3) { | ||
| 228 | + hasMore.value = false | ||
| 229 | + } | ||
| 230 | + | ||
| 231 | + currentPage.value = page | ||
| 232 | + } catch (error) { | ||
| 233 | + console.error('获取车辆列表失败:', error) | ||
| 234 | + showToast('加载失败,请重试', 'error') | ||
| 235 | + } finally { | ||
| 236 | + loading.value = false | ||
| 237 | + } | ||
| 238 | +} | ||
| 239 | + | ||
| 240 | +/** | ||
| 241 | + * 滚动事件处理 | ||
| 242 | + */ | ||
| 243 | +const scroll = (e) => { | ||
| 244 | + // 可以在这里处理滚动事件,比如记录滚动位置 | ||
| 245 | +} | ||
| 246 | + | ||
| 247 | +/** | ||
| 248 | + * 加载更多数据 | ||
| 249 | + */ | ||
| 250 | +const loadMore = () => { | ||
| 251 | + if (!hasMore.value || loading.value) return | ||
| 252 | + fetchCarList(currentPage.value + 1, true) | ||
| 253 | +} | ||
| 254 | + | ||
| 255 | +/** | ||
| 256 | + * 显示提示信息 | ||
| 257 | + */ | ||
| 258 | +const showToast = (message, type = 'success') => { | ||
| 259 | + Taro.showToast({ | ||
| 260 | + title: message, | ||
| 261 | + icon: type === 'success' ? 'success' : 'none' | ||
| 262 | + }) | ||
| 263 | +} | ||
| 264 | + | ||
| 265 | +/** | ||
| 266 | + * 生成模拟车辆数据 | ||
| 267 | + */ | ||
| 268 | +const generateMockCarData = (page = 1, size = 10) => { | ||
| 269 | + const brands = ['奔驰', '宝马', '奥迪', '大众', '丰田', '本田', '日产', '现代'] | ||
| 270 | + const models = ['C级', 'E级', 'S级', '3系', '5系', '7系', 'A4', 'A6', 'A8'] | ||
| 271 | + const conditions = ['准新车', '车况良好', '车况一般'] | ||
| 272 | + const images = [ | ||
| 273 | + 'https://images.unsplash.com/photo-1549924231-f129b911e442?w=400', | ||
| 274 | + 'https://images.unsplash.com/photo-1552519507-da3b142c6e3d?w=400', | ||
| 275 | + 'https://images.unsplash.com/photo-1494976388531-d1058494cdd8?w=400', | ||
| 276 | + 'https://images.unsplash.com/photo-1503376780353-7e6692767b70?w=400', | ||
| 277 | + 'https://images.unsplash.com/photo-1525609004556-c46c7d6cf023?w=400' | ||
| 278 | + ] | ||
| 279 | + | ||
| 280 | + const list = [] | ||
| 281 | + | ||
| 282 | + for (let i = 0; i < size; i++) { | ||
| 283 | + const index = (page - 1) * size + i | ||
| 284 | + const brand = brands[Math.floor(Math.random() * brands.length)] | ||
| 285 | + const model = models[Math.floor(Math.random() * models.length)] | ||
| 286 | + const condition = conditions[Math.floor(Math.random() * conditions.length)] | ||
| 287 | + const image = images[Math.floor(Math.random() * images.length)] | ||
| 288 | + const price = Math.floor(Math.random() * 200000) + 50000 | ||
| 289 | + const marketPrice = Math.floor(price * 1.2) | ||
| 290 | + const year = 2018 + Math.floor(Math.random() * 6) | ||
| 291 | + const mileage = Math.floor(Math.random() * 100000) + 10000 | ||
| 292 | + | ||
| 293 | + list.push({ | ||
| 294 | + id: `car_${index + 1}`, | ||
| 295 | + brand, | ||
| 296 | + model, | ||
| 297 | + year, | ||
| 298 | + condition, | ||
| 299 | + mileage, | ||
| 300 | + price, | ||
| 301 | + marketPrice, | ||
| 302 | + image, | ||
| 303 | + description: `${year}年${brand}${model},${condition},里程${mileage}公里`, | ||
| 304 | + isAuthenticated: Math.random() > 0.5, | ||
| 305 | + isOffline: Math.random() > 0.7, | ||
| 306 | + publishTime: new Date(Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000).toISOString() | ||
| 307 | + }) | ||
| 308 | + } | ||
| 309 | + | ||
| 310 | + return list | ||
| 311 | +} | ||
| 15 | 312 | ||
| 16 | -// 定义响应式数据 | 313 | +// 页面加载时获取数据 |
| 17 | -const str = ref('Demo页面') | 314 | +onMounted(() => { |
| 315 | + fetchCarList(1, false) | ||
| 316 | +}) | ||
| 18 | </script> | 317 | </script> |
| 19 | 318 | ||
| 20 | <script> | 319 | <script> | ... | ... |
| ... | @@ -52,6 +52,12 @@ | ... | @@ -52,6 +52,12 @@ |
| 52 | <Right size="18" color="#9ca3af" /> | 52 | <Right size="18" color="#9ca3af" /> |
| 53 | </view> | 53 | </view> |
| 54 | 54 | ||
| 55 | + <view class="menu-item" @click="onSettings"> | ||
| 56 | + <StarN size="20" color="#6b7280" /> | ||
| 57 | + <text class="menu-text">我的认证</text> | ||
| 58 | + <Right size="18" color="#9ca3af" /> | ||
| 59 | + </view> | ||
| 60 | + | ||
| 55 | <view class="menu-item" @click="onFeedback"> | 61 | <view class="menu-item" @click="onFeedback"> |
| 56 | <Message size="20" color="#6b7280" /> | 62 | <Message size="20" color="#6b7280" /> |
| 57 | <text class="menu-text">意见反馈</text> | 63 | <text class="menu-text">意见反馈</text> |
| ... | @@ -64,11 +70,6 @@ | ... | @@ -64,11 +70,6 @@ |
| 64 | <Right size="18" color="#9ca3af" /> | 70 | <Right size="18" color="#9ca3af" /> |
| 65 | </view> | 71 | </view> |
| 66 | 72 | ||
| 67 | - <view class="menu-item" @click="onSettings"> | ||
| 68 | - <Setting size="20" color="#6b7280" /> | ||
| 69 | - <text class="menu-text">设置</text> | ||
| 70 | - <Right size="18" color="#9ca3af" /> | ||
| 71 | - </view> | ||
| 72 | </view> | 73 | </view> |
| 73 | 74 | ||
| 74 | <!-- 自定义TabBar --> | 75 | <!-- 自定义TabBar --> |
| ... | @@ -79,7 +80,7 @@ | ... | @@ -79,7 +80,7 @@ |
| 79 | <script setup> | 80 | <script setup> |
| 80 | import { ref } from 'vue' | 81 | import { ref } from 'vue' |
| 81 | import { | 82 | import { |
| 82 | - Heart, Clock, Notice, Cart, Message, Tips, Setting, Right | 83 | + Heart, Clock, Notice, Cart, Message, Tips, Setting, Right, StarN |
| 83 | } from '@nutui/icons-vue-taro' | 84 | } from '@nutui/icons-vue-taro' |
| 84 | import Taro from '@tarojs/taro' | 85 | import Taro from '@tarojs/taro' |
| 85 | import TabBar from '@/components/TabBar.vue' | 86 | import TabBar from '@/components/TabBar.vue' | ... | ... |
| ... | @@ -3,7 +3,7 @@ | ... | @@ -3,7 +3,7 @@ |
| 3 | <!-- 顶部导航 --> | 3 | <!-- 顶部导航 --> |
| 4 | <nut-config-provider :theme-vars="themeVars"> | 4 | <nut-config-provider :theme-vars="themeVars"> |
| 5 | <nut-sticky top="0"> | 5 | <nut-sticky top="0"> |
| 6 | - <nut-navbar title="发布车源" left-show @on-click-back="goBack"> | 6 | + <nut-navbar :title="isEditMode ? '编辑车源' : '发布车源'" left-show @on-click-back="goBack"> |
| 7 | <template #left-show> | 7 | <template #left-show> |
| 8 | <RectLeft color="white" /> | 8 | <RectLeft color="white" /> |
| 9 | </template> | 9 | </template> |
| ... | @@ -236,7 +236,7 @@ | ... | @@ -236,7 +236,7 @@ |
| 236 | <!-- 底部按钮 --> | 236 | <!-- 底部按钮 --> |
| 237 | <view class="bottom-actions"> | 237 | <view class="bottom-actions"> |
| 238 | <nut-button color="#f97316" size="large" block @click="onPublish"> | 238 | <nut-button color="#f97316" size="large" block @click="onPublish"> |
| 239 | - 确认发布 | 239 | + {{ isEditMode ? '保存修改' : '确认发布' }} |
| 240 | </nut-button> | 240 | </nut-button> |
| 241 | </view> | 241 | </view> |
| 242 | 242 | ||
| ... | @@ -292,12 +292,18 @@ | ... | @@ -292,12 +292,18 @@ |
| 292 | </template> | 292 | </template> |
| 293 | 293 | ||
| 294 | <script setup> | 294 | <script setup> |
| 295 | -import { ref, reactive } from 'vue' | 295 | +import { ref, reactive, onMounted } from 'vue' |
| 296 | import { Plus, Right, Location, RectLeft, Close } from '@nutui/icons-vue-taro' | 296 | import { Plus, Right, Location, RectLeft, Close } from '@nutui/icons-vue-taro' |
| 297 | import Taro from '@tarojs/taro' | 297 | import Taro from '@tarojs/taro' |
| 298 | // import BASE_URL from '@/utils/config'; | 298 | // import BASE_URL from '@/utils/config'; |
| 299 | import './index.less' | 299 | import './index.less' |
| 300 | 300 | ||
| 301 | +// 获取页面参数 | ||
| 302 | +const instance = Taro.getCurrentInstance() | ||
| 303 | +const { id, mode } = instance.router?.params || {} | ||
| 304 | +const isEditMode = ref(mode === 'edit' && id) | ||
| 305 | +const carId = ref(id || '') | ||
| 306 | + | ||
| 301 | const themeVars = ref({ | 307 | const themeVars = ref({ |
| 302 | navbarBackground: '#fb923c', | 308 | navbarBackground: '#fb923c', |
| 303 | navbarColor: '#ffffff', | 309 | navbarColor: '#ffffff', |
| ... | @@ -690,12 +696,13 @@ const onTireWearConfirm = ({ selectedValue }) => { | ... | @@ -690,12 +696,13 @@ const onTireWearConfirm = ({ selectedValue }) => { |
| 690 | } | 696 | } |
| 691 | 697 | ||
| 692 | /** | 698 | /** |
| 693 | - * 发布车辆 | 699 | + * 发布/保存车辆 |
| 694 | */ | 700 | */ |
| 695 | const onPublish = () => { | 701 | const onPublish = () => { |
| 696 | if (!validateForm()) return | 702 | if (!validateForm()) return |
| 697 | 703 | ||
| 698 | - Taro.showLoading({ title: '发布中...' }) | 704 | + const loadingTitle = isEditMode.value ? '保存中...' : '发布中...' |
| 705 | + Taro.showLoading({ title: loadingTitle }) | ||
| 699 | 706 | ||
| 700 | // 收集所有上传的图片URL | 707 | // 收集所有上传的图片URL |
| 701 | const images = { | 708 | const images = { |
| ... | @@ -712,21 +719,33 @@ const onPublish = () => { | ... | @@ -712,21 +719,33 @@ const onPublish = () => { |
| 712 | imageUrls: Object.values(images).filter(url => url) // 过滤空URL | 719 | imageUrls: Object.values(images).filter(url => url) // 过滤空URL |
| 713 | } | 720 | } |
| 714 | 721 | ||
| 715 | - // 发布车辆信息数据已准备完成 | 722 | + if (isEditMode.value) { |
| 723 | + submitData.id = carId.value | ||
| 724 | + } | ||
| 725 | + | ||
| 716 | // TODO: 在此处调用实际的API接口提交数据 | 726 | // TODO: 在此处调用实际的API接口提交数据 |
| 717 | - // console.log('提交数据:', submitData) | 727 | + // if (isEditMode.value) { |
| 728 | + // updateCar(carId.value, submitData) | ||
| 729 | + // } else { | ||
| 730 | + // createCar(submitData) | ||
| 731 | + // } | ||
| 718 | 732 | ||
| 719 | - // 模拟发布请求 | 733 | + // 模拟请求 |
| 720 | setTimeout(() => { | 734 | setTimeout(() => { |
| 721 | Taro.hideLoading() | 735 | Taro.hideLoading() |
| 736 | + const successTitle = isEditMode.value ? '保存成功' : '发布成功' | ||
| 722 | Taro.showToast({ | 737 | Taro.showToast({ |
| 723 | - title: '发布成功', | 738 | + title: successTitle, |
| 724 | icon: 'success' | 739 | icon: 'success' |
| 725 | }) | 740 | }) |
| 726 | 741 | ||
| 727 | - // 发布成功后跳转到首页 | 742 | + // 成功后跳转 |
| 728 | setTimeout(() => { | 743 | setTimeout(() => { |
| 729 | - Taro.switchTab({ url: '/pages/index/index' }) | 744 | + if (isEditMode.value) { |
| 745 | + Taro.navigateBack() | ||
| 746 | + } else { | ||
| 747 | + Taro.switchTab({ url: '/pages/index/index' }) | ||
| 748 | + } | ||
| 730 | }, 1500) | 749 | }, 1500) |
| 731 | }, 2000) | 750 | }, 2000) |
| 732 | } | 751 | } |
| ... | @@ -765,4 +784,81 @@ const validateForm = () => { | ... | @@ -765,4 +784,81 @@ const validateForm = () => { |
| 765 | 784 | ||
| 766 | return true | 785 | return true |
| 767 | } | 786 | } |
| 787 | + | ||
| 788 | +/** | ||
| 789 | + * 加载车辆数据(编辑模式) | ||
| 790 | + */ | ||
| 791 | +const loadCarData = async () => { | ||
| 792 | + if (!isEditMode.value || !carId.value) return | ||
| 793 | + | ||
| 794 | + try { | ||
| 795 | + Taro.showLoading({ title: '加载中...' }) | ||
| 796 | + | ||
| 797 | + // TODO: 调用真实API获取车辆数据 | ||
| 798 | + // const carData = await getCarById(carId.value) | ||
| 799 | + | ||
| 800 | + // 模拟API响应数据 | ||
| 801 | + const mockCarData = { | ||
| 802 | + school: '上海理工大学', | ||
| 803 | + brand: '小牛电动', | ||
| 804 | + model: 'NGT', | ||
| 805 | + year: '2023年', | ||
| 806 | + condition: '9成新', | ||
| 807 | + mileage: '1200', | ||
| 808 | + range: '60', | ||
| 809 | + maxSpeed: '25', | ||
| 810 | + batteryCapacity: '20', | ||
| 811 | + batteryWear: '轻微磨损', | ||
| 812 | + brakeWear: '轻微磨损', | ||
| 813 | + tireWear: '轻微磨损', | ||
| 814 | + sellingPrice: '3,200', | ||
| 815 | + marketPrice: '6,500', | ||
| 816 | + description: '车况良好,电池续航正常,无重大事故,平时保养得当。', | ||
| 817 | + images: { | ||
| 818 | + front: 'https://picsum.photos/300/200?random=1', | ||
| 819 | + left: 'https://picsum.photos/300/200?random=2', | ||
| 820 | + right: 'https://picsum.photos/300/200?random=3', | ||
| 821 | + other: 'https://picsum.photos/300/200?random=4' | ||
| 822 | + } | ||
| 823 | + } | ||
| 824 | + | ||
| 825 | + // 填充表单数据 | ||
| 826 | + Object.assign(formData, { | ||
| 827 | + school: mockCarData.school, | ||
| 828 | + brand: mockCarData.brand, | ||
| 829 | + model: mockCarData.model, | ||
| 830 | + year: mockCarData.year, | ||
| 831 | + condition: mockCarData.condition, | ||
| 832 | + mileage: mockCarData.mileage, | ||
| 833 | + range: mockCarData.range, | ||
| 834 | + maxSpeed: mockCarData.maxSpeed, | ||
| 835 | + batteryCapacity: mockCarData.batteryCapacity, | ||
| 836 | + batteryWear: mockCarData.batteryWear, | ||
| 837 | + brakeWear: mockCarData.brakeWear, | ||
| 838 | + tireWear: mockCarData.tireWear, | ||
| 839 | + sellingPrice: mockCarData.sellingPrice, | ||
| 840 | + marketPrice: mockCarData.marketPrice, | ||
| 841 | + description: mockCarData.description | ||
| 842 | + }) | ||
| 843 | + | ||
| 844 | + // 填充图片数据 | ||
| 845 | + Object.assign(uploadedImages, mockCarData.images) | ||
| 846 | + | ||
| 847 | + Taro.hideLoading() | ||
| 848 | + } catch (error) { | ||
| 849 | + console.error('加载车辆数据失败:', error) | ||
| 850 | + Taro.hideLoading() | ||
| 851 | + Taro.showToast({ | ||
| 852 | + title: '加载数据失败', | ||
| 853 | + icon: 'none' | ||
| 854 | + }) | ||
| 855 | + } | ||
| 856 | +} | ||
| 857 | + | ||
| 858 | +// 页面加载时执行 | ||
| 859 | +onMounted(() => { | ||
| 860 | + if (isEditMode.value) { | ||
| 861 | + loadCarData() | ||
| 862 | + } | ||
| 863 | +}) | ||
| 768 | </script> | 864 | </script> | ... | ... |
| 1 | /* | 1 | /* |
| 2 | * @Date: 2025-07-02 17:52:43 | 2 | * @Date: 2025-07-02 17:52:43 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-07-02 17:53:28 | 4 | + * @LastEditTime: 2025-07-03 14:01:51 |
| 5 | * @FilePath: /jgdl/src/pages/setAuthCar/index.config.js | 5 | * @FilePath: /jgdl/src/pages/setAuthCar/index.config.js |
| 6 | * @Description: 文件描述 | 6 | * @Description: 文件描述 |
| 7 | */ | 7 | */ |
| 8 | export default { | 8 | export default { |
| 9 | - navigationBarTitleText: '申请认证', | 9 | + navigationBarTitleText: '', |
| 10 | usingComponents: { | 10 | usingComponents: { |
| 11 | }, | 11 | }, |
| 12 | } | 12 | } | ... | ... |
| 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-02 18:23:27 | 4 | + * @LastEditTime: 2025-07-03 14:03:37 |
| 5 | * @FilePath: /jgdl/src/pages/setAuthCar/index.vue | 5 | * @FilePath: /jgdl/src/pages/setAuthCar/index.vue |
| 6 | * @Description: 申请认证 | 6 | * @Description: 申请认证 |
| 7 | --> | 7 | --> |
| 8 | <template> | 8 | <template> |
| 9 | <view class="auth-car-page"> | 9 | <view class="auth-car-page"> |
| 10 | + <!-- 顶部导航 --> | ||
| 11 | + <nut-config-provider :theme-vars="themeVars"> | ||
| 12 | + <nut-sticky top="0"> | ||
| 13 | + <nut-navbar :title="isEditMode ? '编辑认证' : '申请认证'" left-show @on-click-back="goBack"> | ||
| 14 | + <template #left-show> | ||
| 15 | + <RectLeft color="white" /> | ||
| 16 | + </template> | ||
| 17 | + </nut-navbar> | ||
| 18 | + </nut-sticky> | ||
| 19 | + </nut-config-provider> | ||
| 20 | + | ||
| 10 | <!-- 表单内容 --> | 21 | <!-- 表单内容 --> |
| 11 | <view class="form-container"> | 22 | <view class="form-container"> |
| 12 | <!-- 车辆照片上传 --> | 23 | <!-- 车辆照片上传 --> |
| ... | @@ -131,7 +142,7 @@ | ... | @@ -131,7 +142,7 @@ |
| 131 | <!-- 底部按钮 --> | 142 | <!-- 底部按钮 --> |
| 132 | <view class="bottom-actions"> | 143 | <view class="bottom-actions"> |
| 133 | <nut-button color="#f97316" size="large" block @click="onSubmit"> | 144 | <nut-button color="#f97316" size="large" block @click="onSubmit"> |
| 134 | - 提交申请 | 145 | + {{ isEditMode ? '保存修改' : '提交申请' }} |
| 135 | </nut-button> | 146 | </nut-button> |
| 136 | </view> | 147 | </view> |
| 137 | 148 | ||
| ... | @@ -149,11 +160,17 @@ | ... | @@ -149,11 +160,17 @@ |
| 149 | </template> | 160 | </template> |
| 150 | 161 | ||
| 151 | <script setup> | 162 | <script setup> |
| 152 | -import { ref, reactive } from 'vue' | 163 | +import { ref, reactive, onMounted } from 'vue' |
| 153 | import { Plus, Right, RectLeft, Close } from '@nutui/icons-vue-taro' | 164 | import { Plus, Right, RectLeft, Close } from '@nutui/icons-vue-taro' |
| 154 | import Taro from '@tarojs/taro' | 165 | import Taro from '@tarojs/taro' |
| 155 | import './index.less' | 166 | import './index.less' |
| 156 | 167 | ||
| 168 | +// 获取页面参数 | ||
| 169 | +const instance = Taro.getCurrentInstance() | ||
| 170 | +const { id, mode } = instance.router?.params || {} | ||
| 171 | +const isEditMode = ref(mode === 'edit' && id) | ||
| 172 | +const carId = ref(id || '') | ||
| 173 | + | ||
| 157 | const themeVars = ref({ | 174 | const themeVars = ref({ |
| 158 | navbarBackground: '#fb923c', | 175 | navbarBackground: '#fb923c', |
| 159 | navbarColor: '#ffffff', | 176 | navbarColor: '#ffffff', |
| ... | @@ -328,7 +345,7 @@ const onModelConfirm = (options) => { | ... | @@ -328,7 +345,7 @@ const onModelConfirm = (options) => { |
| 328 | } | 345 | } |
| 329 | 346 | ||
| 330 | /** | 347 | /** |
| 331 | - * 提交申请 | 348 | + * 提交申请/保存修改 |
| 332 | */ | 349 | */ |
| 333 | const onSubmit = () => { | 350 | const onSubmit = () => { |
| 334 | // 表单验证 | 351 | // 表单验证 |
| ... | @@ -380,16 +397,29 @@ const onSubmit = () => { | ... | @@ -380,16 +397,29 @@ const onSubmit = () => { |
| 380 | images: uploadedImages | 397 | images: uploadedImages |
| 381 | } | 398 | } |
| 382 | 399 | ||
| 400 | + if (isEditMode.value) { | ||
| 401 | + submitData.id = carId.value | ||
| 402 | + } | ||
| 403 | + | ||
| 404 | + const loadingTitle = isEditMode.value ? '保存中' : '提交中' | ||
| 383 | Taro.showLoading({ | 405 | Taro.showLoading({ |
| 384 | - title: '提交中', | 406 | + title: loadingTitle, |
| 385 | mask: true | 407 | mask: true |
| 386 | }) | 408 | }) |
| 387 | 409 | ||
| 410 | + // TODO: 调用真实API | ||
| 411 | + // if (isEditMode.value) { | ||
| 412 | + // updateAuthApplication(carId.value, submitData) | ||
| 413 | + // } else { | ||
| 414 | + // submitAuthApplication(submitData) | ||
| 415 | + // } | ||
| 416 | + | ||
| 388 | // 模拟提交成功 | 417 | // 模拟提交成功 |
| 389 | setTimeout(() => { | 418 | setTimeout(() => { |
| 390 | Taro.hideLoading() | 419 | Taro.hideLoading() |
| 420 | + const successTitle = isEditMode.value ? '保存成功' : '申请提交成功' | ||
| 391 | Taro.showToast({ | 421 | Taro.showToast({ |
| 392 | - title: '申请提交成功', | 422 | + title: successTitle, |
| 393 | icon: 'success' | 423 | icon: 'success' |
| 394 | }) | 424 | }) |
| 395 | 425 | ||
| ... | @@ -398,10 +428,67 @@ const onSubmit = () => { | ... | @@ -398,10 +428,67 @@ const onSubmit = () => { |
| 398 | Taro.navigateBack() | 428 | Taro.navigateBack() |
| 399 | }, 1500) | 429 | }, 1500) |
| 400 | }, 2000) | 430 | }, 2000) |
| 431 | +} | ||
| 432 | + | ||
| 433 | +/** | ||
| 434 | + * 加载认证数据(编辑模式) | ||
| 435 | + */ | ||
| 436 | +const loadAuthData = async () => { | ||
| 437 | + if (!isEditMode.value || !carId.value) return | ||
| 438 | + | ||
| 439 | + try { | ||
| 440 | + Taro.showLoading({ title: '加载中...' }) | ||
| 441 | + | ||
| 442 | + // TODO: 调用真实API获取认证数据 | ||
| 443 | + // const authData = await getAuthById(carId.value) | ||
| 444 | + | ||
| 445 | + // 暂时不模拟数据,按用户要求 | ||
| 446 | + // 如果需要模拟数据,可以取消下面的注释 | ||
| 447 | + | ||
| 448 | + const mockAuthData = { | ||
| 449 | + brand: '小牛电动', | ||
| 450 | + model: 'NGT', | ||
| 451 | + range: '60', | ||
| 452 | + maxSpeed: '25', | ||
| 453 | + description: '车况良好,电池续航正常,无重大事故。', | ||
| 454 | + images: { | ||
| 455 | + front: 'https://picsum.photos/300/200?random=5', | ||
| 456 | + left: 'https://picsum.photos/300/200?random=6', | ||
| 457 | + right: 'https://picsum.photos/300/200?random=7', | ||
| 458 | + other: 'https://picsum.photos/300/200?random=8' | ||
| 459 | + } | ||
| 460 | + } | ||
| 461 | + | ||
| 462 | + // 填充表单数据 | ||
| 463 | + Object.assign(formData, { | ||
| 464 | + brand: mockAuthData.brand, | ||
| 465 | + model: mockAuthData.model, | ||
| 466 | + range: mockAuthData.range, | ||
| 467 | + maxSpeed: mockAuthData.maxSpeed, | ||
| 468 | + description: mockAuthData.description | ||
| 469 | + }) | ||
| 401 | 470 | ||
| 402 | - // 实际项目中的API调用 | 471 | + // 填充图片数据 |
| 403 | - // submitAuthApplication(submitData) | 472 | + Object.assign(uploadedImages, mockAuthData.images) |
| 473 | + | ||
| 474 | + | ||
| 475 | + Taro.hideLoading() | ||
| 476 | + } catch (error) { | ||
| 477 | + console.error('加载认证数据失败:', error) | ||
| 478 | + Taro.hideLoading() | ||
| 479 | + Taro.showToast({ | ||
| 480 | + title: '加载数据失败', | ||
| 481 | + icon: 'none' | ||
| 482 | + }) | ||
| 483 | + } | ||
| 404 | } | 484 | } |
| 485 | + | ||
| 486 | +// 页面加载时执行 | ||
| 487 | +onMounted(() => { | ||
| 488 | + if (isEditMode.value) { | ||
| 489 | + loadAuthData() | ||
| 490 | + } | ||
| 491 | +}) | ||
| 405 | </script> | 492 | </script> |
| 406 | 493 | ||
| 407 | <script> | 494 | <script> | ... | ... |
-
Please register or login to post a comment