hookehuyr

feat(车辆认证): 重构车辆认证表单并接入真实API

- 将表单字段重命名为与后端一致的命名规范
- 添加品牌型号选择器的动态数据加载
- 实现车辆数据的获取、提交和编辑功能
- 优化图片上传逻辑,同步更新表单数据
- 移除模拟数据,完全使用真实API交互
<!--
* @Date: 2022-09-19 14:11:06
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-07-04 12:22:02
* @LastEditTime: 2025-07-10 17:24:11
* @FilePath: /jgdl/src/pages/setAuthCar/index.vue
* @Description: 申请认证
-->
......@@ -91,15 +91,16 @@
:rules="[{ required: true, message: '请选择品牌型号' }]">
<view class="form-item-content" @click="showBrandModelPicker">
<text class="form-value">
{{ formData.brand && formData.model ? `${formData.brand} ${formData.model}` : '请选择品牌型号' }}
{{ formData.brand && formData.model ? `${formData.brand} ${formData.model}` : '请选择品牌型号'
}}
</text>
<Right class="arrow-icon" />
</view>
</nut-form-item>
<!-- 续航里程 -->
<nut-form-item label="续航里程" prop="range" required :rules="[{ required: true, message: '请输入续航里程' }]">
<nut-input v-model="formData.range" placeholder="60" type="number" input-align="right">
<nut-form-item label="续航里程" prop="range_km">
<nut-input v-model="formData.range_km" placeholder="60" type="number" input-align="right">
<template #right>
<text class="unit">公里</text>
</template>
......@@ -107,9 +108,8 @@
</nut-form-item>
<!-- 最高时速 -->
<nut-form-item label="最高时速" prop="maxSpeed" required
:rules="[{ required: true, message: '请输入最高时速' }]">
<nut-input v-model="formData.maxSpeed" placeholder="25" type="number" input-align="right">
<nut-form-item label="最高时速" prop="max_speed_kmh">
<nut-input v-model="formData.max_speed_kmh" placeholder="25" type="number" input-align="right">
<template #right>
<text class="unit">km/h</text>
</template>
......@@ -121,8 +121,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.note" placeholder="请描述车辆详情,如使用感受、车况特点等" :max-length="200" :rows="4"
show-word-limit />
</view>
</view>
......@@ -134,11 +134,8 @@
</view>
<!-- 品牌型号选择器 -->
<BrandModelPicker
ref="brandModelPickerRef"
@confirm="onBrandModelConfirm"
@cancel="onBrandModelCancel"
/>
<BrandModelPicker ref="brandModelPickerRef" :brand-options="brandOptions" @confirm="onBrandModelConfirm"
@cancel="onBrandModelCancel" />
</view>
</template>
......@@ -150,6 +147,10 @@ import './index.less'
import BASE_URL from '@/utils/config';
import BrandModelPicker from '@/components/BrandModelPicker.vue'
// 导入接口
import { getBrandsModelsAPI } from '@/api/other';
import { addVehicleAPI, editVehicleAPI, getVehicleDetailAPI } from '@/api/car';
// 获取页面参数
const instance = Taro.getCurrentInstance()
const { id, mode } = instance.router?.params || {}
......@@ -178,14 +179,23 @@ const formData = reactive({
brand: '',
model: '',
brandModel: '', // 品牌型号组合字段,用于表单验证
range: '',
maxSpeed: '',
description: ''
range_km: '',
max_speed_kmh: '',
note: '',
// 车辆照片字段
front_photo: '',
left_photo: '',
right_photo: '',
other_photo: '',
verification_status: ''
})
// 品牌型号选择器组件引用
const brandModelPickerRef = ref(null)
// 品牌型号选项数据
const brandOptions = ref([])
/**
......@@ -236,6 +246,8 @@ const uploadImage = (filePath, type) => {
success: () => {
if (res.statusCode === 200) {
uploadedImages[type] = upload_data.data.src;
// 同时更新formData中对应的照片字段
formData[`${type}_photo`] = upload_data.data.src;
Taro.showToast({
title: '上传成功',
icon: 'success'
......@@ -247,10 +259,10 @@ const uploadImage = (filePath, type) => {
mask: true
})
}
},
}
});
},
fail: function (res) {
fail: function () {
Taro.hideLoading({
success: () => {
Taro.showToast({
......@@ -317,69 +329,100 @@ const onBrandModelCancel = () => {
}
/**
* 提交申请/保存修改
* 加载品牌型号数据
*/
const onSubmit = () => {
// 表单验证
const loadBrandsModels = async () => {
try {
const { code, data } = await getBrandsModelsAPI()
if (code && data) {
// 转换品牌数据格式
brandOptions.value = data.map(brand => ({
text: brand.name,
value: brand.id,
models: brand.models || []
}))
}
} catch (error) {
console.error('加载品牌型号列表失败:', error)
}
}
/**
* 表单验证
*/
const validateForm = () => {
if (!formData.brandModel) {
Taro.showToast({
title: '请选择品牌型号',
icon: 'none'
})
return
return false
}
if (!formData.range) {
Taro.showToast({
title: '请输入续航里程',
icon: 'none'
})
return
}
// if (!formData.range_km) {
// Taro.showToast({
// title: '请输入续航里程',
// icon: 'none'
// })
// return false
// }
if (!formData.maxSpeed) {
Taro.showToast({
title: '请输入最高时速',
icon: 'none'
})
return
}
// if (!formData.max_speed_kmh) {
// Taro.showToast({
// title: '请输入最高时速',
// icon: 'none'
// })
// return false
// }
// 检查是否至少上传了一张照片
const hasImage = Object.values(uploadedImages).some(img => img)
const hasImage = uploadedImages.front
if (!hasImage) {
Taro.showToast({
title: '请至少上传一张车辆照片',
icon: 'none'
})
return
return false
}
// 提交数据
const submitData = {
...formData,
images: uploadedImages
}
return true
}
if (isEditMode.value) {
submitData.id = carId.value
/**
* 提交申请/保存修改
*/
const onSubmit = async () => {
// 表单验证
if (!validateForm()) {
return
}
// 同步图片数据到表单
formData.front_photo = uploadedImages.front
formData.left_photo = uploadedImages.left
formData.right_photo = uploadedImages.right
formData.other_photo = uploadedImages.other
const loadingTitle = isEditMode.value ? '保存中' : '提交中'
Taro.showLoading({
title: loadingTitle,
mask: true
})
// TODO: 调用真实API
// if (isEditMode.value) {
// updateAuthApplication(carId.value, submitData)
// } else {
// submitAuthApplication(submitData)
// }
try {
// 调用真实API
if (isEditMode.value) {
const { code } = await editVehicleAPI({ id: carId.value, ...formData })
if (!code) {
throw new Error('更新失败')
}
} else {
const { code } = await addVehicleAPI(formData)
if (!code) {
throw new Error('提交失败')
}
}
// 模拟提交成功
setTimeout(() => {
Taro.hideLoading()
const successTitle = isEditMode.value ? '保存成功' : '申请提交成功'
Taro.showToast({
......@@ -391,7 +434,14 @@ const onSubmit = () => {
setTimeout(() => {
Taro.navigateBack()
}, 1500)
}, 2000)
} catch (error) {
console.error('提交失败:', error)
Taro.hideLoading()
Taro.showToast({
title: '提交失败,请重试',
icon: 'none'
})
}
}
/**
......@@ -403,56 +453,56 @@ const loadAuthData = async () => {
try {
Taro.showLoading({ title: '加载中...' })
// TODO: 调用真实API获取认证数据
// const authData = await getAuthById(carId.value)
// 暂时不模拟数据,按用户要求
// 如果需要模拟数据,可以取消下面的注释
const mockAuthData = {
brand: '小牛电动',
model: 'NGT',
range: '60',
maxSpeed: '25',
description: '车况良好,电池续航正常,无重大事故。',
images: {
front: 'https://picsum.photos/300/200?random=5',
left: 'https://picsum.photos/300/200?random=6',
right: 'https://picsum.photos/300/200?random=7',
other: 'https://picsum.photos/300/200?random=8'
}
}
// 填充表单数据
Object.assign(formData, {
brand: mockAuthData.brand,
model: mockAuthData.model,
brandModel: `${mockAuthData.brand} ${mockAuthData.model}`, // 设置组合字段
range: mockAuthData.range,
maxSpeed: mockAuthData.maxSpeed,
description: mockAuthData.description
})
// 填充图片数据
Object.assign(uploadedImages, mockAuthData.images)
// 调用真实API获取车辆数据
const { code, data } = await getVehicleDetailAPI({ id: carId.value })
if (code && data) {
// 填充表单数据
Object.assign(formData, {
...data,
brand: data.brand,
model: data.model,
brandModel: `${data.brand} ${data.model}`,
range_km: data.range_km,
max_speed_kmh: data.max_speed_kmh,
note: data.note,
front_photo: data.front_photo,
left_photo: data.left_photo,
right_photo: data.right_photo,
other_photo: data.other_photo,
verification_status: data.verification_status
})
Taro.hideLoading()
// 填充图片数据
Object.assign(uploadedImages, {
front: data.front_photo,
left: data.left_photo,
right: data.right_photo,
other: data.other_photo
})
} else {
throw new Error('获取车辆数据失败')
}
} catch (error) {
console.error('加载认证数据失败:', error)
Taro.hideLoading()
console.error('加载车辆数据失败:', error)
Taro.showToast({
title: '加载数据失败',
icon: 'none'
})
} finally {
Taro.hideLoading()
}
}
// 页面加载时执行
onMounted(() => {
onMounted(async () => {
// 并行加载品牌型号数据
await loadBrandsModels()
if (isEditMode.value) {
loadAuthData()
}
// 动态修改标题
wx.setNavigationBarTitle({
title: isEditMode.value ? '编辑认证' : '发布认证'
......