hookehuyr

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

- 统一调整多个页面的上传组件网格布局和间距
- 优化上传按钮和预览图片的尺寸及样式
- 移除模拟上传逻辑,实现真实文件上传功能
- 更新认证车源页面的UI细节和交互
<!--
* @Date: 2022-09-19 14:11:06
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-07-03 12:40:46
* @LastEditTime: 2025-07-04 11:38:24
* @FilePath: /jgdl/src/pages/authCar/index.vue
* @Description: 认证车源
-->
......@@ -18,9 +18,9 @@
</view>
<!-- 我要认证按钮 -->
<view class="px-4 mt-6">
<view class="px-16 mt-5">
<nut-button
color="#f97316"
color="#FFA135"
size="large"
block
@click="handleAuth"
......@@ -60,9 +60,9 @@
class="w-full h-full object-cover rounded-lg" />
</view>
<view class="flex-1 p-3 relative">
<view class="absolute top-3 right-4" @tap.stop="() => toggleFavorite(car.id)">
<Heart1 v-if="!favoriteIds.includes(car.id)" size="16" :color="'#9ca3af'" />
<HeartFill v-else size="16" :color="'#ef4444'" />
<view class="absolute top-2 right-4" @tap.stop="() => toggleFavorite(car.id)">
<Heart1 v-if="!favoriteIds.includes(car.id)" size="22" :color="'#9ca3af'" />
<HeartFill v-else size="22" :color="'#ef4444'" />
</view>
<text class="font-medium text-sm block">{{ car.name }}</text>
<text class="text-xs text-gray-600 mt-1 block">
......@@ -71,7 +71,7 @@
<text v-if="car.mileage"> 行驶{{ car.mileage }}公里</text>
</text>
<view class="mt-2">
<text class="text-orange-500 font-bold">
<text class="text-orange-500 font-bold" style="font-size: 1.2rem;">
¥{{ car.price.toLocaleString() }}
</text>
<text class="text-xs text-gray-500 mt-1 block">{{ car.school }}</text>
......
......@@ -26,8 +26,8 @@
.upload-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 32rpx;
grid-template-columns: repeat(4, 1fr);
gap: 16rpx;
margin-top: 24rpx;
}
......@@ -35,14 +35,14 @@
display: flex;
flex-direction: column;
align-items: center;
gap: 16rpx;
gap: 8rpx;
}
.upload-button {
width: 160rpx;
height: 160rpx;
width: 120rpx;
height: 120rpx;
border: 2rpx dashed #d1d5db;
border-radius: 16rpx;
border-radius: 12rpx;
display: flex;
align-items: center;
justify-content: center;
......@@ -56,21 +56,21 @@
}
.upload-icon {
font-size: 48rpx;
font-size: 36rpx;
color: #9ca3af;
}
.upload-label {
font-size: 24rpx;
font-size: 20rpx;
color: #6b7280;
text-align: center;
}
.image-preview {
position: relative;
width: 160rpx;
height: 160rpx;
border-radius: 16rpx;
width: 120rpx;
height: 120rpx;
border-radius: 12rpx;
overflow: hidden;
}
......
......@@ -285,9 +285,9 @@
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { Plus, Right, Location, RectLeft, Close } from '@nutui/icons-vue-taro'
import { Plus, Right, Location, Close } from '@nutui/icons-vue-taro'
import Taro from '@tarojs/taro'
// import BASE_URL from '@/utils/config';
import BASE_URL from '@/utils/config';
import './index.less'
// 获取页面参数
......@@ -419,14 +419,7 @@ const wearLevelOptions = ref([
{ text: '需要更换', value: '需要更换' }
])
/**
* 返回上一页
*/
const goBack = () => {
Taro.redirectTo({
url: '/pages/index/index'
})
}
/**
* 触发图片上传
......@@ -462,66 +455,35 @@ const uploadImage = (filePath, type) => {
mask: true
})
// 模拟上传成功(实际项目中替换为真实的上传逻辑)
setTimeout(() => {
Taro.hideLoading()
// 模拟服务器返回的图片URL
const mockImageUrl = filePath // 在实际项目中,这里应该是服务器返回的URL
// 更新对应类型的图片
uploadedImages[type] = mockImageUrl
Taro.showToast({
title: '上传成功',
icon: 'success'
})
}, 1500)
// 真实上传逻辑(注释掉的代码供参考)
/*
Taro.uploadFile({
// 获取上传URL
wx.uploadFile({
url: BASE_URL + '/admin/?m=srv&a=upload',
filePath: filePath,
filePath,
name: 'file',
header: {
'content-type': 'multipart/form-data'
'content-type': 'multipart/form-data',
},
success: function (res) {
try {
const upload_data = JSON.parse(res.data)
Taro.hideLoading()
if (res.statusCode === 200 && upload_data.data) {
// 上传成功,更新对应类型的图片
uploadedImages[type] = upload_data.data.src
Taro.showToast({
title: '上传成功',
icon: 'success'
})
} else {
throw new Error('上传失败')
}
} catch (error) {
Taro.hideLoading()
Taro.showToast({
icon: 'error',
title: '服务器错误,稍后重试!',
mask: true
})
}
},
fail: function () {
Taro.hideLoading()
Taro.showToast({
icon: 'error',
title: '上传失败,请重试',
mask: true
})
let upload_data = JSON.parse(res.data);
Taro.hideLoading({
success: () => {
if (res.statusCode === 200) {
uploadedImages[type] = upload_data.data.src;
Taro.showToast({
title: '上传成功',
icon: 'success'
})
} else {
Taro.showToast({
icon: 'error',
title: '服务器错误,稍后重试!',
mask: true
})
}
},
});
}
})
*/
});
}
/**
......
......@@ -27,8 +27,8 @@
.upload-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 32rpx;
grid-template-columns: repeat(4, 1fr);
gap: 16rpx;
margin-top: 24rpx;
}
......@@ -36,14 +36,14 @@
display: flex;
flex-direction: column;
align-items: center;
gap: 16rpx;
gap: 12rpx;
}
.upload-button {
width: 160rpx;
height: 160rpx;
width: 120rpx;
height: 120rpx;
border: 2rpx dashed #d1d5db;
border-radius: 16rpx;
border-radius: 12rpx;
display: flex;
align-items: center;
justify-content: center;
......@@ -57,21 +57,21 @@
}
.upload-icon {
font-size: 48rpx;
font-size: 36rpx;
color: #9ca3af;
}
.upload-label {
font-size: 24rpx;
font-size: 20rpx;
color: #6b7280;
text-align: center;
}
.image-preview {
position: relative;
width: 160rpx;
height: 160rpx;
border-radius: 16rpx;
width: 120rpx;
height: 120rpx;
border-radius: 12rpx;
overflow: hidden;
}
......
<!--
* @Date: 2022-09-19 14:11:06
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-07-03 14:27:41
* @LastEditTime: 2025-07-04 12:22:02
* @FilePath: /jgdl/src/pages/setAuthCar/index.vue
* @Description: 申请认证
-->
......@@ -79,14 +79,16 @@
</view>
<!-- 图片预览组件 -->
<nut-image-preview v-model:show="previewVisible" :images="previewImages" :init-no="previewIndex" @close="closePreview" />
<nut-image-preview v-model:show="previewVisible" :images="previewImages" :init-no="previewIndex"
@close="closePreview" />
</view>
<!-- 车辆详情表单 -->
<nut-form ref="formRef" :model-value="formData">
<view class="form-section">
<!-- 车型品牌 -->
<nut-form-item label-position="top" label="车型品牌" prop="brand" required :rules="[{ required: true, message: '请选择车型品牌' }]">
<nut-form-item label-position="top" label="车型品牌" prop="brand" required
:rules="[{ required: true, message: '请选择车型品牌' }]">
<view class="form-item-content" @click="showBrandPicker">
<text class="form-value">{{ formData.brand || '请选择' }}</text>
<Right class="arrow-icon" />
......@@ -94,7 +96,8 @@
</nut-form-item>
<!-- 车辆型号 -->
<nut-form-item label-position="top" label="车辆型号" prop="model" required :rules="[{ required: true, message: '请选择车辆型号' }]">
<nut-form-item label-position="top" label="车辆型号" prop="model" required
:rules="[{ required: true, message: '请选择车辆型号' }]">
<view class="form-item-content" @click="showModelPicker">
<text class="form-value">{{ formData.model || '请选择' }}</text>
<Right class="arrow-icon" />
......@@ -111,7 +114,8 @@
</nut-form-item>
<!-- 最高时速 -->
<nut-form-item label="最高时速" prop="maxSpeed" required :rules="[{ required: true, message: '请输入最高时速' }]">
<nut-form-item label="最高时速" prop="maxSpeed" required
:rules="[{ required: true, message: '请输入最高时速' }]">
<nut-input v-model="formData.maxSpeed" placeholder="25" type="number" input-align="right">
<template #right>
<text class="unit">km/h</text>
......@@ -124,7 +128,8 @@
<!-- 车辆描述 -->
<view class="form-section">
<text class="section-title">车辆描述</text>
<nut-textarea v-model="formData.description" placeholder="请描述车辆详情,如使用感受、车况特点等" :max-length="200" :rows="4" show-word-limit />
<nut-textarea v-model="formData.description" placeholder="请描述车辆详情,如使用感受、车况特点等" :max-length="200"
:rows="4" show-word-limit />
</view>
</view>
......@@ -138,21 +143,24 @@
<!-- 选择器弹窗 -->
<!-- 品牌选择 -->
<nut-popup v-model:visible="brandPickerVisible" position="bottom">
<nut-picker v-model="brandValue" :columns="brandOptions" title="选择车型品牌" @confirm="onBrandConfirm" @cancel="brandPickerVisible = false" />
<nut-picker v-model="brandValue" :columns="brandOptions" title="选择车型品牌" @confirm="onBrandConfirm"
@cancel="brandPickerVisible = false" />
</nut-popup>
<!-- 型号选择 -->
<nut-popup v-model:visible="modelPickerVisible" position="bottom">
<nut-picker v-model="modelValue" :columns="modelOptions" title="选择车辆型号" @confirm="onModelConfirm" @cancel="modelPickerVisible = false" />
<nut-picker v-model="modelValue" :columns="modelOptions" title="选择车辆型号" @confirm="onModelConfirm"
@cancel="modelPickerVisible = false" />
</nut-popup>
</view>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { Plus, Right, RectLeft, Close } from '@nutui/icons-vue-taro'
import { Plus, Right, Close } from '@nutui/icons-vue-taro'
import Taro from '@tarojs/taro'
import './index.less'
import BASE_URL from '@/utils/config';
// 获取页面参数
const instance = Taro.getCurrentInstance()
......@@ -160,11 +168,6 @@ const { id, mode } = instance.router?.params || {}
const isEditMode = ref(mode === 'edit' && id)
const carId = ref(id || '')
const themeVars = ref({
navbarBackground: '#fb923c',
navbarColor: '#ffffff',
})
// 已上传图片的URL
const uploadedImages = reactive({
front: '',
......@@ -221,12 +224,7 @@ const modelOptions = ref([
{ text: '其他型号', value: '其他型号' }
])
/**
* 返回上一页
*/
const goBack = () => {
Taro.navigateBack()
}
/**
* 触发图片上传
......@@ -262,21 +260,35 @@ const uploadImage = (filePath, type) => {
mask: true
})
// 模拟上传成功(实际项目中替换为真实的上传逻辑)
setTimeout(() => {
Taro.hideLoading()
// 模拟服务器返回的图片URL
const mockImageUrl = filePath // 在实际项目中,这里应该是服务器返回的URL
// 更新对应类型的图片
uploadedImages[type] = mockImageUrl
Taro.showToast({
title: '上传成功',
icon: 'success'
})
}, 1500)
// 获取上传URL
wx.uploadFile({
url: BASE_URL + '/admin/?m=srv&a=upload',
filePath,
name: 'file',
header: {
'content-type': 'multipart/form-data',
},
success: function (res) {
let upload_data = JSON.parse(res.data);
Taro.hideLoading({
success: () => {
if (res.statusCode === 200) {
uploadedImages[type] = upload_data.data.src;
Taro.showToast({
title: '上传成功',
icon: 'success'
})
} else {
Taro.showToast({
icon: 'error',
title: '服务器错误,稍后重试!',
mask: true
})
}
},
});
}
});
}
/**
......@@ -296,7 +308,7 @@ const deleteImage = (type) => {
* @param {String} imageUrl - 图片URL
*/
const previewImage = (imageUrl) => {
previewImages.value = [imageUrl]
previewImages.value = [{ src: imageUrl }]
previewIndex.value = 0
previewVisible.value = true
}
......