hookehuyr

refactor(ui): 调整上传组件布局和样式,优化图片上传功能

- 统一调整多个页面的上传组件网格布局和间距
- 优化上传按钮和预览图片的尺寸及样式
- 移除模拟上传逻辑,实现真实文件上传功能
- 更新认证车源页面的UI细节和交互
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:40:46 4 + * @LastEditTime: 2025-07-04 11:38:24
5 * @FilePath: /jgdl/src/pages/authCar/index.vue 5 * @FilePath: /jgdl/src/pages/authCar/index.vue
6 * @Description: 认证车源 6 * @Description: 认证车源
7 --> 7 -->
...@@ -18,9 +18,9 @@ ...@@ -18,9 +18,9 @@
18 </view> 18 </view>
19 19
20 <!-- 我要认证按钮 --> 20 <!-- 我要认证按钮 -->
21 - <view class="px-4 mt-6"> 21 + <view class="px-16 mt-5">
22 <nut-button 22 <nut-button
23 - color="#f97316" 23 + color="#FFA135"
24 size="large" 24 size="large"
25 block 25 block
26 @click="handleAuth" 26 @click="handleAuth"
...@@ -60,9 +60,9 @@ ...@@ -60,9 +60,9 @@
60 class="w-full h-full object-cover rounded-lg" /> 60 class="w-full h-full object-cover rounded-lg" />
61 </view> 61 </view>
62 <view class="flex-1 p-3 relative"> 62 <view class="flex-1 p-3 relative">
63 - <view class="absolute top-3 right-4" @tap.stop="() => toggleFavorite(car.id)"> 63 + <view class="absolute top-2 right-4" @tap.stop="() => toggleFavorite(car.id)">
64 - <Heart1 v-if="!favoriteIds.includes(car.id)" size="16" :color="'#9ca3af'" /> 64 + <Heart1 v-if="!favoriteIds.includes(car.id)" size="22" :color="'#9ca3af'" />
65 - <HeartFill v-else size="16" :color="'#ef4444'" /> 65 + <HeartFill v-else size="22" :color="'#ef4444'" />
66 </view> 66 </view>
67 <text class="font-medium text-sm block">{{ car.name }}</text> 67 <text class="font-medium text-sm block">{{ car.name }}</text>
68 <text class="text-xs text-gray-600 mt-1 block"> 68 <text class="text-xs text-gray-600 mt-1 block">
...@@ -71,7 +71,7 @@ ...@@ -71,7 +71,7 @@
71 <text v-if="car.mileage"> 行驶{{ car.mileage }}公里</text> 71 <text v-if="car.mileage"> 行驶{{ car.mileage }}公里</text>
72 </text> 72 </text>
73 <view class="mt-2"> 73 <view class="mt-2">
74 - <text class="text-orange-500 font-bold"> 74 + <text class="text-orange-500 font-bold" style="font-size: 1.2rem;">
75 ¥{{ car.price.toLocaleString() }} 75 ¥{{ car.price.toLocaleString() }}
76 </text> 76 </text>
77 <text class="text-xs text-gray-500 mt-1 block">{{ car.school }}</text> 77 <text class="text-xs text-gray-500 mt-1 block">{{ car.school }}</text>
......
...@@ -26,8 +26,8 @@ ...@@ -26,8 +26,8 @@
26 26
27 .upload-grid { 27 .upload-grid {
28 display: grid; 28 display: grid;
29 - grid-template-columns: repeat(2, 1fr); 29 + grid-template-columns: repeat(4, 1fr);
30 - gap: 32rpx; 30 + gap: 16rpx;
31 margin-top: 24rpx; 31 margin-top: 24rpx;
32 } 32 }
33 33
...@@ -35,14 +35,14 @@ ...@@ -35,14 +35,14 @@
35 display: flex; 35 display: flex;
36 flex-direction: column; 36 flex-direction: column;
37 align-items: center; 37 align-items: center;
38 - gap: 16rpx; 38 + gap: 8rpx;
39 } 39 }
40 40
41 .upload-button { 41 .upload-button {
42 - width: 160rpx; 42 + width: 120rpx;
43 - height: 160rpx; 43 + height: 120rpx;
44 border: 2rpx dashed #d1d5db; 44 border: 2rpx dashed #d1d5db;
45 - border-radius: 16rpx; 45 + border-radius: 12rpx;
46 display: flex; 46 display: flex;
47 align-items: center; 47 align-items: center;
48 justify-content: center; 48 justify-content: center;
...@@ -56,21 +56,21 @@ ...@@ -56,21 +56,21 @@
56 } 56 }
57 57
58 .upload-icon { 58 .upload-icon {
59 - font-size: 48rpx; 59 + font-size: 36rpx;
60 color: #9ca3af; 60 color: #9ca3af;
61 } 61 }
62 62
63 .upload-label { 63 .upload-label {
64 - font-size: 24rpx; 64 + font-size: 20rpx;
65 color: #6b7280; 65 color: #6b7280;
66 text-align: center; 66 text-align: center;
67 } 67 }
68 68
69 .image-preview { 69 .image-preview {
70 position: relative; 70 position: relative;
71 - width: 160rpx; 71 + width: 120rpx;
72 - height: 160rpx; 72 + height: 120rpx;
73 - border-radius: 16rpx; 73 + border-radius: 12rpx;
74 overflow: hidden; 74 overflow: hidden;
75 } 75 }
76 76
......
...@@ -285,9 +285,9 @@ ...@@ -285,9 +285,9 @@
285 285
286 <script setup> 286 <script setup>
287 import { ref, reactive, onMounted } from 'vue' 287 import { ref, reactive, onMounted } from 'vue'
288 -import { Plus, Right, Location, RectLeft, Close } from '@nutui/icons-vue-taro' 288 +import { Plus, Right, Location, Close } from '@nutui/icons-vue-taro'
289 import Taro from '@tarojs/taro' 289 import Taro from '@tarojs/taro'
290 -// import BASE_URL from '@/utils/config'; 290 +import BASE_URL from '@/utils/config';
291 import './index.less' 291 import './index.less'
292 292
293 // 获取页面参数 293 // 获取页面参数
...@@ -419,14 +419,7 @@ const wearLevelOptions = ref([ ...@@ -419,14 +419,7 @@ const wearLevelOptions = ref([
419 { text: '需要更换', value: '需要更换' } 419 { text: '需要更换', value: '需要更换' }
420 ]) 420 ])
421 421
422 -/** 422 +
423 - * 返回上一页
424 - */
425 -const goBack = () => {
426 - Taro.redirectTo({
427 - url: '/pages/index/index'
428 - })
429 -}
430 423
431 /** 424 /**
432 * 触发图片上传 425 * 触发图片上传
...@@ -462,49 +455,25 @@ const uploadImage = (filePath, type) => { ...@@ -462,49 +455,25 @@ const uploadImage = (filePath, type) => {
462 mask: true 455 mask: true
463 }) 456 })
464 457
465 - // 模拟上传成功(实际项目中替换为真实的上传逻辑) 458 + // 获取上传URL
466 - setTimeout(() => { 459 + wx.uploadFile({
467 - Taro.hideLoading()
468 -
469 - // 模拟服务器返回的图片URL
470 - const mockImageUrl = filePath // 在实际项目中,这里应该是服务器返回的URL
471 -
472 - // 更新对应类型的图片
473 - uploadedImages[type] = mockImageUrl
474 -
475 - Taro.showToast({
476 - title: '上传成功',
477 - icon: 'success'
478 - })
479 - }, 1500)
480 -
481 - // 真实上传逻辑(注释掉的代码供参考)
482 - /*
483 - Taro.uploadFile({
484 url: BASE_URL + '/admin/?m=srv&a=upload', 460 url: BASE_URL + '/admin/?m=srv&a=upload',
485 - filePath: filePath, 461 + filePath,
486 name: 'file', 462 name: 'file',
487 header: { 463 header: {
488 - 'content-type': 'multipart/form-data' 464 + 'content-type': 'multipart/form-data',
489 }, 465 },
490 success: function (res) { 466 success: function (res) {
491 - try { 467 + let upload_data = JSON.parse(res.data);
492 - const upload_data = JSON.parse(res.data) 468 + Taro.hideLoading({
493 - Taro.hideLoading() 469 + success: () => {
494 - 470 + if (res.statusCode === 200) {
495 - if (res.statusCode === 200 && upload_data.data) { 471 + uploadedImages[type] = upload_data.data.src;
496 - // 上传成功,更新对应类型的图片
497 - uploadedImages[type] = upload_data.data.src
498 -
499 Taro.showToast({ 472 Taro.showToast({
500 title: '上传成功', 473 title: '上传成功',
501 icon: 'success' 474 icon: 'success'
502 }) 475 })
503 } else { 476 } else {
504 - throw new Error('上传失败')
505 - }
506 - } catch (error) {
507 - Taro.hideLoading()
508 Taro.showToast({ 477 Taro.showToast({
509 icon: 'error', 478 icon: 'error',
510 title: '服务器错误,稍后重试!', 479 title: '服务器错误,稍后重试!',
...@@ -512,16 +481,9 @@ const uploadImage = (filePath, type) => { ...@@ -512,16 +481,9 @@ const uploadImage = (filePath, type) => {
512 }) 481 })
513 } 482 }
514 }, 483 },
515 - fail: function () { 484 + });
516 - Taro.hideLoading()
517 - Taro.showToast({
518 - icon: 'error',
519 - title: '上传失败,请重试',
520 - mask: true
521 - })
522 } 485 }
523 - }) 486 + });
524 - */
525 } 487 }
526 488
527 /** 489 /**
......
...@@ -27,8 +27,8 @@ ...@@ -27,8 +27,8 @@
27 27
28 .upload-grid { 28 .upload-grid {
29 display: grid; 29 display: grid;
30 - grid-template-columns: repeat(2, 1fr); 30 + grid-template-columns: repeat(4, 1fr);
31 - gap: 32rpx; 31 + gap: 16rpx;
32 margin-top: 24rpx; 32 margin-top: 24rpx;
33 } 33 }
34 34
...@@ -36,14 +36,14 @@ ...@@ -36,14 +36,14 @@
36 display: flex; 36 display: flex;
37 flex-direction: column; 37 flex-direction: column;
38 align-items: center; 38 align-items: center;
39 - gap: 16rpx; 39 + gap: 12rpx;
40 } 40 }
41 41
42 .upload-button { 42 .upload-button {
43 - width: 160rpx; 43 + width: 120rpx;
44 - height: 160rpx; 44 + height: 120rpx;
45 border: 2rpx dashed #d1d5db; 45 border: 2rpx dashed #d1d5db;
46 - border-radius: 16rpx; 46 + border-radius: 12rpx;
47 display: flex; 47 display: flex;
48 align-items: center; 48 align-items: center;
49 justify-content: center; 49 justify-content: center;
...@@ -57,21 +57,21 @@ ...@@ -57,21 +57,21 @@
57 } 57 }
58 58
59 .upload-icon { 59 .upload-icon {
60 - font-size: 48rpx; 60 + font-size: 36rpx;
61 color: #9ca3af; 61 color: #9ca3af;
62 } 62 }
63 63
64 .upload-label { 64 .upload-label {
65 - font-size: 24rpx; 65 + font-size: 20rpx;
66 color: #6b7280; 66 color: #6b7280;
67 text-align: center; 67 text-align: center;
68 } 68 }
69 69
70 .image-preview { 70 .image-preview {
71 position: relative; 71 position: relative;
72 - width: 160rpx; 72 + width: 120rpx;
73 - height: 160rpx; 73 + height: 120rpx;
74 - border-radius: 16rpx; 74 + border-radius: 12rpx;
75 overflow: hidden; 75 overflow: hidden;
76 } 76 }
77 77
......
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 14:27:41 4 + * @LastEditTime: 2025-07-04 12:22:02
5 * @FilePath: /jgdl/src/pages/setAuthCar/index.vue 5 * @FilePath: /jgdl/src/pages/setAuthCar/index.vue
6 * @Description: 申请认证 6 * @Description: 申请认证
7 --> 7 -->
...@@ -79,14 +79,16 @@ ...@@ -79,14 +79,16 @@
79 </view> 79 </view>
80 80
81 <!-- 图片预览组件 --> 81 <!-- 图片预览组件 -->
82 - <nut-image-preview v-model:show="previewVisible" :images="previewImages" :init-no="previewIndex" @close="closePreview" /> 82 + <nut-image-preview v-model:show="previewVisible" :images="previewImages" :init-no="previewIndex"
83 + @close="closePreview" />
83 </view> 84 </view>
84 85
85 <!-- 车辆详情表单 --> 86 <!-- 车辆详情表单 -->
86 <nut-form ref="formRef" :model-value="formData"> 87 <nut-form ref="formRef" :model-value="formData">
87 <view class="form-section"> 88 <view class="form-section">
88 <!-- 车型品牌 --> 89 <!-- 车型品牌 -->
89 - <nut-form-item label-position="top" label="车型品牌" prop="brand" required :rules="[{ required: true, message: '请选择车型品牌' }]"> 90 + <nut-form-item label-position="top" label="车型品牌" prop="brand" required
91 + :rules="[{ required: true, message: '请选择车型品牌' }]">
90 <view class="form-item-content" @click="showBrandPicker"> 92 <view class="form-item-content" @click="showBrandPicker">
91 <text class="form-value">{{ formData.brand || '请选择' }}</text> 93 <text class="form-value">{{ formData.brand || '请选择' }}</text>
92 <Right class="arrow-icon" /> 94 <Right class="arrow-icon" />
...@@ -94,7 +96,8 @@ ...@@ -94,7 +96,8 @@
94 </nut-form-item> 96 </nut-form-item>
95 97
96 <!-- 车辆型号 --> 98 <!-- 车辆型号 -->
97 - <nut-form-item label-position="top" label="车辆型号" prop="model" required :rules="[{ required: true, message: '请选择车辆型号' }]"> 99 + <nut-form-item label-position="top" label="车辆型号" prop="model" required
100 + :rules="[{ required: true, message: '请选择车辆型号' }]">
98 <view class="form-item-content" @click="showModelPicker"> 101 <view class="form-item-content" @click="showModelPicker">
99 <text class="form-value">{{ formData.model || '请选择' }}</text> 102 <text class="form-value">{{ formData.model || '请选择' }}</text>
100 <Right class="arrow-icon" /> 103 <Right class="arrow-icon" />
...@@ -111,7 +114,8 @@ ...@@ -111,7 +114,8 @@
111 </nut-form-item> 114 </nut-form-item>
112 115
113 <!-- 最高时速 --> 116 <!-- 最高时速 -->
114 - <nut-form-item label="最高时速" prop="maxSpeed" required :rules="[{ required: true, message: '请输入最高时速' }]"> 117 + <nut-form-item label="最高时速" prop="maxSpeed" required
118 + :rules="[{ required: true, message: '请输入最高时速' }]">
115 <nut-input v-model="formData.maxSpeed" placeholder="25" type="number" input-align="right"> 119 <nut-input v-model="formData.maxSpeed" placeholder="25" type="number" input-align="right">
116 <template #right> 120 <template #right>
117 <text class="unit">km/h</text> 121 <text class="unit">km/h</text>
...@@ -124,7 +128,8 @@ ...@@ -124,7 +128,8 @@
124 <!-- 车辆描述 --> 128 <!-- 车辆描述 -->
125 <view class="form-section"> 129 <view class="form-section">
126 <text class="section-title">车辆描述</text> 130 <text class="section-title">车辆描述</text>
127 - <nut-textarea v-model="formData.description" placeholder="请描述车辆详情,如使用感受、车况特点等" :max-length="200" :rows="4" show-word-limit /> 131 + <nut-textarea v-model="formData.description" placeholder="请描述车辆详情,如使用感受、车况特点等" :max-length="200"
132 + :rows="4" show-word-limit />
128 </view> 133 </view>
129 </view> 134 </view>
130 135
...@@ -138,21 +143,24 @@ ...@@ -138,21 +143,24 @@
138 <!-- 选择器弹窗 --> 143 <!-- 选择器弹窗 -->
139 <!-- 品牌选择 --> 144 <!-- 品牌选择 -->
140 <nut-popup v-model:visible="brandPickerVisible" position="bottom"> 145 <nut-popup v-model:visible="brandPickerVisible" position="bottom">
141 - <nut-picker v-model="brandValue" :columns="brandOptions" title="选择车型品牌" @confirm="onBrandConfirm" @cancel="brandPickerVisible = false" /> 146 + <nut-picker v-model="brandValue" :columns="brandOptions" title="选择车型品牌" @confirm="onBrandConfirm"
147 + @cancel="brandPickerVisible = false" />
142 </nut-popup> 148 </nut-popup>
143 149
144 <!-- 型号选择 --> 150 <!-- 型号选择 -->
145 <nut-popup v-model:visible="modelPickerVisible" position="bottom"> 151 <nut-popup v-model:visible="modelPickerVisible" position="bottom">
146 - <nut-picker v-model="modelValue" :columns="modelOptions" title="选择车辆型号" @confirm="onModelConfirm" @cancel="modelPickerVisible = false" /> 152 + <nut-picker v-model="modelValue" :columns="modelOptions" title="选择车辆型号" @confirm="onModelConfirm"
153 + @cancel="modelPickerVisible = false" />
147 </nut-popup> 154 </nut-popup>
148 </view> 155 </view>
149 </template> 156 </template>
150 157
151 <script setup> 158 <script setup>
152 import { ref, reactive, onMounted } from 'vue' 159 import { ref, reactive, onMounted } from 'vue'
153 -import { Plus, Right, RectLeft, Close } from '@nutui/icons-vue-taro' 160 +import { Plus, Right, Close } from '@nutui/icons-vue-taro'
154 import Taro from '@tarojs/taro' 161 import Taro from '@tarojs/taro'
155 import './index.less' 162 import './index.less'
163 +import BASE_URL from '@/utils/config';
156 164
157 // 获取页面参数 165 // 获取页面参数
158 const instance = Taro.getCurrentInstance() 166 const instance = Taro.getCurrentInstance()
...@@ -160,11 +168,6 @@ const { id, mode } = instance.router?.params || {} ...@@ -160,11 +168,6 @@ const { id, mode } = instance.router?.params || {}
160 const isEditMode = ref(mode === 'edit' && id) 168 const isEditMode = ref(mode === 'edit' && id)
161 const carId = ref(id || '') 169 const carId = ref(id || '')
162 170
163 -const themeVars = ref({
164 - navbarBackground: '#fb923c',
165 - navbarColor: '#ffffff',
166 -})
167 -
168 // 已上传图片的URL 171 // 已上传图片的URL
169 const uploadedImages = reactive({ 172 const uploadedImages = reactive({
170 front: '', 173 front: '',
...@@ -221,12 +224,7 @@ const modelOptions = ref([ ...@@ -221,12 +224,7 @@ const modelOptions = ref([
221 { text: '其他型号', value: '其他型号' } 224 { text: '其他型号', value: '其他型号' }
222 ]) 225 ])
223 226
224 -/** 227 +
225 - * 返回上一页
226 - */
227 -const goBack = () => {
228 - Taro.navigateBack()
229 -}
230 228
231 /** 229 /**
232 * 触发图片上传 230 * 触发图片上传
...@@ -262,21 +260,35 @@ const uploadImage = (filePath, type) => { ...@@ -262,21 +260,35 @@ const uploadImage = (filePath, type) => {
262 mask: true 260 mask: true
263 }) 261 })
264 262
265 - // 模拟上传成功(实际项目中替换为真实的上传逻辑) 263 + // 获取上传URL
266 - setTimeout(() => { 264 + wx.uploadFile({
267 - Taro.hideLoading() 265 + url: BASE_URL + '/admin/?m=srv&a=upload',
268 - 266 + filePath,
269 - // 模拟服务器返回的图片URL 267 + name: 'file',
270 - const mockImageUrl = filePath // 在实际项目中,这里应该是服务器返回的URL 268 + header: {
271 - 269 + 'content-type': 'multipart/form-data',
272 - // 更新对应类型的图片 270 + },
273 - uploadedImages[type] = mockImageUrl 271 + success: function (res) {
274 - 272 + let upload_data = JSON.parse(res.data);
273 + Taro.hideLoading({
274 + success: () => {
275 + if (res.statusCode === 200) {
276 + uploadedImages[type] = upload_data.data.src;
275 Taro.showToast({ 277 Taro.showToast({
276 title: '上传成功', 278 title: '上传成功',
277 icon: 'success' 279 icon: 'success'
278 }) 280 })
279 - }, 1500) 281 + } else {
282 + Taro.showToast({
283 + icon: 'error',
284 + title: '服务器错误,稍后重试!',
285 + mask: true
286 + })
287 + }
288 + },
289 + });
290 + }
291 + });
280 } 292 }
281 293
282 /** 294 /**
...@@ -296,7 +308,7 @@ const deleteImage = (type) => { ...@@ -296,7 +308,7 @@ const deleteImage = (type) => {
296 * @param {String} imageUrl - 图片URL 308 * @param {String} imageUrl - 图片URL
297 */ 309 */
298 const previewImage = (imageUrl) => { 310 const previewImage = (imageUrl) => {
299 - previewImages.value = [imageUrl] 311 + previewImages.value = [{ src: imageUrl }]
300 previewIndex.value = 0 312 previewIndex.value = 0
301 previewVisible.value = true 313 previewVisible.value = true
302 } 314 }
......