feat(车辆认证): 重构车辆认证表单并接入真实API
- 将表单字段重命名为与后端一致的命名规范 - 添加品牌型号选择器的动态数据加载 - 实现车辆数据的获取、提交和编辑功能 - 优化图片上传逻辑,同步更新表单数据 - 移除模拟数据,完全使用真实API交互
Showing
1 changed file
with
138 additions
and
88 deletions
| 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-04 12:22:02 | 4 | + * @LastEditTime: 2025-07-10 17:24:11 |
| 5 | * @FilePath: /jgdl/src/pages/setAuthCar/index.vue | 5 | * @FilePath: /jgdl/src/pages/setAuthCar/index.vue |
| 6 | * @Description: 申请认证 | 6 | * @Description: 申请认证 |
| 7 | --> | 7 | --> |
| ... | @@ -91,15 +91,16 @@ | ... | @@ -91,15 +91,16 @@ |
| 91 | :rules="[{ required: true, message: '请选择品牌型号' }]"> | 91 | :rules="[{ required: true, message: '请选择品牌型号' }]"> |
| 92 | <view class="form-item-content" @click="showBrandModelPicker"> | 92 | <view class="form-item-content" @click="showBrandModelPicker"> |
| 93 | <text class="form-value"> | 93 | <text class="form-value"> |
| 94 | - {{ formData.brand && formData.model ? `${formData.brand} ${formData.model}` : '请选择品牌型号' }} | 94 | + {{ formData.brand && formData.model ? `${formData.brand} ${formData.model}` : '请选择品牌型号' |
| 95 | + }} | ||
| 95 | </text> | 96 | </text> |
| 96 | <Right class="arrow-icon" /> | 97 | <Right class="arrow-icon" /> |
| 97 | </view> | 98 | </view> |
| 98 | </nut-form-item> | 99 | </nut-form-item> |
| 99 | 100 | ||
| 100 | <!-- 续航里程 --> | 101 | <!-- 续航里程 --> |
| 101 | - <nut-form-item label="续航里程" prop="range" required :rules="[{ required: true, message: '请输入续航里程' }]"> | 102 | + <nut-form-item label="续航里程" prop="range_km"> |
| 102 | - <nut-input v-model="formData.range" placeholder="60" type="number" input-align="right"> | 103 | + <nut-input v-model="formData.range_km" placeholder="60" type="number" input-align="right"> |
| 103 | <template #right> | 104 | <template #right> |
| 104 | <text class="unit">公里</text> | 105 | <text class="unit">公里</text> |
| 105 | </template> | 106 | </template> |
| ... | @@ -107,9 +108,8 @@ | ... | @@ -107,9 +108,8 @@ |
| 107 | </nut-form-item> | 108 | </nut-form-item> |
| 108 | 109 | ||
| 109 | <!-- 最高时速 --> | 110 | <!-- 最高时速 --> |
| 110 | - <nut-form-item label="最高时速" prop="maxSpeed" required | 111 | + <nut-form-item label="最高时速" prop="max_speed_kmh"> |
| 111 | - :rules="[{ required: true, message: '请输入最高时速' }]"> | 112 | + <nut-input v-model="formData.max_speed_kmh" placeholder="25" type="number" input-align="right"> |
| 112 | - <nut-input v-model="formData.maxSpeed" placeholder="25" type="number" input-align="right"> | ||
| 113 | <template #right> | 113 | <template #right> |
| 114 | <text class="unit">km/h</text> | 114 | <text class="unit">km/h</text> |
| 115 | </template> | 115 | </template> |
| ... | @@ -121,8 +121,8 @@ | ... | @@ -121,8 +121,8 @@ |
| 121 | <!-- 车辆描述 --> | 121 | <!-- 车辆描述 --> |
| 122 | <view class="form-section"> | 122 | <view class="form-section"> |
| 123 | <text class="section-title">车辆描述</text> | 123 | <text class="section-title">车辆描述</text> |
| 124 | - <nut-textarea v-model="formData.description" placeholder="请描述车辆详情,如使用感受、车况特点等" :max-length="200" | 124 | + <nut-textarea v-model="formData.note" placeholder="请描述车辆详情,如使用感受、车况特点等" :max-length="200" :rows="4" |
| 125 | - :rows="4" show-word-limit /> | 125 | + show-word-limit /> |
| 126 | </view> | 126 | </view> |
| 127 | </view> | 127 | </view> |
| 128 | 128 | ||
| ... | @@ -134,11 +134,8 @@ | ... | @@ -134,11 +134,8 @@ |
| 134 | </view> | 134 | </view> |
| 135 | 135 | ||
| 136 | <!-- 品牌型号选择器 --> | 136 | <!-- 品牌型号选择器 --> |
| 137 | - <BrandModelPicker | 137 | + <BrandModelPicker ref="brandModelPickerRef" :brand-options="brandOptions" @confirm="onBrandModelConfirm" |
| 138 | - ref="brandModelPickerRef" | 138 | + @cancel="onBrandModelCancel" /> |
| 139 | - @confirm="onBrandModelConfirm" | ||
| 140 | - @cancel="onBrandModelCancel" | ||
| 141 | - /> | ||
| 142 | </view> | 139 | </view> |
| 143 | </template> | 140 | </template> |
| 144 | 141 | ||
| ... | @@ -150,6 +147,10 @@ import './index.less' | ... | @@ -150,6 +147,10 @@ import './index.less' |
| 150 | import BASE_URL from '@/utils/config'; | 147 | import BASE_URL from '@/utils/config'; |
| 151 | import BrandModelPicker from '@/components/BrandModelPicker.vue' | 148 | import BrandModelPicker from '@/components/BrandModelPicker.vue' |
| 152 | 149 | ||
| 150 | +// 导入接口 | ||
| 151 | +import { getBrandsModelsAPI } from '@/api/other'; | ||
| 152 | +import { addVehicleAPI, editVehicleAPI, getVehicleDetailAPI } from '@/api/car'; | ||
| 153 | + | ||
| 153 | // 获取页面参数 | 154 | // 获取页面参数 |
| 154 | const instance = Taro.getCurrentInstance() | 155 | const instance = Taro.getCurrentInstance() |
| 155 | const { id, mode } = instance.router?.params || {} | 156 | const { id, mode } = instance.router?.params || {} |
| ... | @@ -178,14 +179,23 @@ const formData = reactive({ | ... | @@ -178,14 +179,23 @@ const formData = reactive({ |
| 178 | brand: '', | 179 | brand: '', |
| 179 | model: '', | 180 | model: '', |
| 180 | brandModel: '', // 品牌型号组合字段,用于表单验证 | 181 | brandModel: '', // 品牌型号组合字段,用于表单验证 |
| 181 | - range: '', | 182 | + range_km: '', |
| 182 | - maxSpeed: '', | 183 | + max_speed_kmh: '', |
| 183 | - description: '' | 184 | + note: '', |
| 185 | + // 车辆照片字段 | ||
| 186 | + front_photo: '', | ||
| 187 | + left_photo: '', | ||
| 188 | + right_photo: '', | ||
| 189 | + other_photo: '', | ||
| 190 | + verification_status: '' | ||
| 184 | }) | 191 | }) |
| 185 | 192 | ||
| 186 | // 品牌型号选择器组件引用 | 193 | // 品牌型号选择器组件引用 |
| 187 | const brandModelPickerRef = ref(null) | 194 | const brandModelPickerRef = ref(null) |
| 188 | 195 | ||
| 196 | +// 品牌型号选项数据 | ||
| 197 | +const brandOptions = ref([]) | ||
| 198 | + | ||
| 189 | 199 | ||
| 190 | 200 | ||
| 191 | /** | 201 | /** |
| ... | @@ -236,6 +246,8 @@ const uploadImage = (filePath, type) => { | ... | @@ -236,6 +246,8 @@ const uploadImage = (filePath, type) => { |
| 236 | success: () => { | 246 | success: () => { |
| 237 | if (res.statusCode === 200) { | 247 | if (res.statusCode === 200) { |
| 238 | uploadedImages[type] = upload_data.data.src; | 248 | uploadedImages[type] = upload_data.data.src; |
| 249 | + // 同时更新formData中对应的照片字段 | ||
| 250 | + formData[`${type}_photo`] = upload_data.data.src; | ||
| 239 | Taro.showToast({ | 251 | Taro.showToast({ |
| 240 | title: '上传成功', | 252 | title: '上传成功', |
| 241 | icon: 'success' | 253 | icon: 'success' |
| ... | @@ -247,10 +259,10 @@ const uploadImage = (filePath, type) => { | ... | @@ -247,10 +259,10 @@ const uploadImage = (filePath, type) => { |
| 247 | mask: true | 259 | mask: true |
| 248 | }) | 260 | }) |
| 249 | } | 261 | } |
| 250 | - }, | 262 | + } |
| 251 | }); | 263 | }); |
| 252 | }, | 264 | }, |
| 253 | - fail: function (res) { | 265 | + fail: function () { |
| 254 | Taro.hideLoading({ | 266 | Taro.hideLoading({ |
| 255 | success: () => { | 267 | success: () => { |
| 256 | Taro.showToast({ | 268 | Taro.showToast({ |
| ... | @@ -317,69 +329,100 @@ const onBrandModelCancel = () => { | ... | @@ -317,69 +329,100 @@ const onBrandModelCancel = () => { |
| 317 | } | 329 | } |
| 318 | 330 | ||
| 319 | /** | 331 | /** |
| 320 | - * 提交申请/保存修改 | 332 | + * 加载品牌型号数据 |
| 321 | */ | 333 | */ |
| 322 | -const onSubmit = () => { | 334 | +const loadBrandsModels = async () => { |
| 323 | - // 表单验证 | 335 | + try { |
| 336 | + const { code, data } = await getBrandsModelsAPI() | ||
| 337 | + if (code && data) { | ||
| 338 | + // 转换品牌数据格式 | ||
| 339 | + brandOptions.value = data.map(brand => ({ | ||
| 340 | + text: brand.name, | ||
| 341 | + value: brand.id, | ||
| 342 | + models: brand.models || [] | ||
| 343 | + })) | ||
| 344 | + } | ||
| 345 | + } catch (error) { | ||
| 346 | + console.error('加载品牌型号列表失败:', error) | ||
| 347 | + } | ||
| 348 | +} | ||
| 349 | + | ||
| 350 | +/** | ||
| 351 | + * 表单验证 | ||
| 352 | + */ | ||
| 353 | +const validateForm = () => { | ||
| 324 | if (!formData.brandModel) { | 354 | if (!formData.brandModel) { |
| 325 | Taro.showToast({ | 355 | Taro.showToast({ |
| 326 | title: '请选择品牌型号', | 356 | title: '请选择品牌型号', |
| 327 | icon: 'none' | 357 | icon: 'none' |
| 328 | }) | 358 | }) |
| 329 | - return | 359 | + return false |
| 330 | } | 360 | } |
| 331 | 361 | ||
| 332 | - if (!formData.range) { | 362 | + // if (!formData.range_km) { |
| 333 | - Taro.showToast({ | 363 | + // Taro.showToast({ |
| 334 | - title: '请输入续航里程', | 364 | + // title: '请输入续航里程', |
| 335 | - icon: 'none' | 365 | + // icon: 'none' |
| 336 | - }) | 366 | + // }) |
| 337 | - return | 367 | + // return false |
| 338 | - } | 368 | + // } |
| 339 | 369 | ||
| 340 | - if (!formData.maxSpeed) { | 370 | + // if (!formData.max_speed_kmh) { |
| 341 | - Taro.showToast({ | 371 | + // Taro.showToast({ |
| 342 | - title: '请输入最高时速', | 372 | + // title: '请输入最高时速', |
| 343 | - icon: 'none' | 373 | + // icon: 'none' |
| 344 | - }) | 374 | + // }) |
| 345 | - return | 375 | + // return false |
| 346 | - } | 376 | + // } |
| 347 | 377 | ||
| 348 | // 检查是否至少上传了一张照片 | 378 | // 检查是否至少上传了一张照片 |
| 349 | - const hasImage = Object.values(uploadedImages).some(img => img) | 379 | + const hasImage = uploadedImages.front |
| 350 | if (!hasImage) { | 380 | if (!hasImage) { |
| 351 | Taro.showToast({ | 381 | Taro.showToast({ |
| 352 | title: '请至少上传一张车辆照片', | 382 | title: '请至少上传一张车辆照片', |
| 353 | icon: 'none' | 383 | icon: 'none' |
| 354 | }) | 384 | }) |
| 355 | - return | 385 | + return false |
| 356 | } | 386 | } |
| 357 | 387 | ||
| 358 | - // 提交数据 | 388 | + return true |
| 359 | - const submitData = { | 389 | +} |
| 360 | - ...formData, | ||
| 361 | - images: uploadedImages | ||
| 362 | - } | ||
| 363 | 390 | ||
| 364 | - if (isEditMode.value) { | 391 | +/** |
| 365 | - submitData.id = carId.value | 392 | + * 提交申请/保存修改 |
| 393 | + */ | ||
| 394 | +const onSubmit = async () => { | ||
| 395 | + // 表单验证 | ||
| 396 | + if (!validateForm()) { | ||
| 397 | + return | ||
| 366 | } | 398 | } |
| 367 | 399 | ||
| 400 | + // 同步图片数据到表单 | ||
| 401 | + formData.front_photo = uploadedImages.front | ||
| 402 | + formData.left_photo = uploadedImages.left | ||
| 403 | + formData.right_photo = uploadedImages.right | ||
| 404 | + formData.other_photo = uploadedImages.other | ||
| 405 | + | ||
| 368 | const loadingTitle = isEditMode.value ? '保存中' : '提交中' | 406 | const loadingTitle = isEditMode.value ? '保存中' : '提交中' |
| 369 | Taro.showLoading({ | 407 | Taro.showLoading({ |
| 370 | title: loadingTitle, | 408 | title: loadingTitle, |
| 371 | mask: true | 409 | mask: true |
| 372 | }) | 410 | }) |
| 373 | 411 | ||
| 374 | - // TODO: 调用真实API | 412 | + try { |
| 375 | - // if (isEditMode.value) { | 413 | + // 调用真实API |
| 376 | - // updateAuthApplication(carId.value, submitData) | 414 | + if (isEditMode.value) { |
| 377 | - // } else { | 415 | + const { code } = await editVehicleAPI({ id: carId.value, ...formData }) |
| 378 | - // submitAuthApplication(submitData) | 416 | + if (!code) { |
| 379 | - // } | 417 | + throw new Error('更新失败') |
| 418 | + } | ||
| 419 | + } else { | ||
| 420 | + const { code } = await addVehicleAPI(formData) | ||
| 421 | + if (!code) { | ||
| 422 | + throw new Error('提交失败') | ||
| 423 | + } | ||
| 424 | + } | ||
| 380 | 425 | ||
| 381 | - // 模拟提交成功 | ||
| 382 | - setTimeout(() => { | ||
| 383 | Taro.hideLoading() | 426 | Taro.hideLoading() |
| 384 | const successTitle = isEditMode.value ? '保存成功' : '申请提交成功' | 427 | const successTitle = isEditMode.value ? '保存成功' : '申请提交成功' |
| 385 | Taro.showToast({ | 428 | Taro.showToast({ |
| ... | @@ -391,7 +434,14 @@ const onSubmit = () => { | ... | @@ -391,7 +434,14 @@ const onSubmit = () => { |
| 391 | setTimeout(() => { | 434 | setTimeout(() => { |
| 392 | Taro.navigateBack() | 435 | Taro.navigateBack() |
| 393 | }, 1500) | 436 | }, 1500) |
| 394 | - }, 2000) | 437 | + } catch (error) { |
| 438 | + console.error('提交失败:', error) | ||
| 439 | + Taro.hideLoading() | ||
| 440 | + Taro.showToast({ | ||
| 441 | + title: '提交失败,请重试', | ||
| 442 | + icon: 'none' | ||
| 443 | + }) | ||
| 444 | + } | ||
| 395 | } | 445 | } |
| 396 | 446 | ||
| 397 | /** | 447 | /** |
| ... | @@ -403,56 +453,56 @@ const loadAuthData = async () => { | ... | @@ -403,56 +453,56 @@ const loadAuthData = async () => { |
| 403 | try { | 453 | try { |
| 404 | Taro.showLoading({ title: '加载中...' }) | 454 | Taro.showLoading({ title: '加载中...' }) |
| 405 | 455 | ||
| 406 | - // TODO: 调用真实API获取认证数据 | 456 | + // 调用真实API获取车辆数据 |
| 407 | - // const authData = await getAuthById(carId.value) | 457 | + const { code, data } = await getVehicleDetailAPI({ id: carId.value }) |
| 408 | - | 458 | + if (code && data) { |
| 409 | - // 暂时不模拟数据,按用户要求 | ||
| 410 | - // 如果需要模拟数据,可以取消下面的注释 | ||
| 411 | - | ||
| 412 | - const mockAuthData = { | ||
| 413 | - brand: '小牛电动', | ||
| 414 | - model: 'NGT', | ||
| 415 | - range: '60', | ||
| 416 | - maxSpeed: '25', | ||
| 417 | - description: '车况良好,电池续航正常,无重大事故。', | ||
| 418 | - images: { | ||
| 419 | - front: 'https://picsum.photos/300/200?random=5', | ||
| 420 | - left: 'https://picsum.photos/300/200?random=6', | ||
| 421 | - right: 'https://picsum.photos/300/200?random=7', | ||
| 422 | - other: 'https://picsum.photos/300/200?random=8' | ||
| 423 | - } | ||
| 424 | - } | ||
| 425 | - | ||
| 426 | // 填充表单数据 | 459 | // 填充表单数据 |
| 427 | Object.assign(formData, { | 460 | Object.assign(formData, { |
| 428 | - brand: mockAuthData.brand, | 461 | + ...data, |
| 429 | - model: mockAuthData.model, | 462 | + brand: data.brand, |
| 430 | - brandModel: `${mockAuthData.brand} ${mockAuthData.model}`, // 设置组合字段 | 463 | + model: data.model, |
| 431 | - range: mockAuthData.range, | 464 | + brandModel: `${data.brand} ${data.model}`, |
| 432 | - maxSpeed: mockAuthData.maxSpeed, | 465 | + range_km: data.range_km, |
| 433 | - description: mockAuthData.description | 466 | + max_speed_kmh: data.max_speed_kmh, |
| 467 | + note: data.note, | ||
| 468 | + front_photo: data.front_photo, | ||
| 469 | + left_photo: data.left_photo, | ||
| 470 | + right_photo: data.right_photo, | ||
| 471 | + other_photo: data.other_photo, | ||
| 472 | + verification_status: data.verification_status | ||
| 434 | }) | 473 | }) |
| 435 | 474 | ||
| 436 | // 填充图片数据 | 475 | // 填充图片数据 |
| 437 | - Object.assign(uploadedImages, mockAuthData.images) | 476 | + Object.assign(uploadedImages, { |
| 438 | - | 477 | + front: data.front_photo, |
| 439 | - | 478 | + left: data.left_photo, |
| 440 | - Taro.hideLoading() | 479 | + right: data.right_photo, |
| 480 | + other: data.other_photo | ||
| 481 | + }) | ||
| 482 | + } else { | ||
| 483 | + throw new Error('获取车辆数据失败') | ||
| 484 | + } | ||
| 441 | } catch (error) { | 485 | } catch (error) { |
| 442 | - console.error('加载认证数据失败:', error) | 486 | + console.error('加载车辆数据失败:', error) |
| 443 | - Taro.hideLoading() | ||
| 444 | Taro.showToast({ | 487 | Taro.showToast({ |
| 445 | title: '加载数据失败', | 488 | title: '加载数据失败', |
| 446 | icon: 'none' | 489 | icon: 'none' |
| 447 | }) | 490 | }) |
| 491 | + | ||
| 492 | + } finally { | ||
| 493 | + Taro.hideLoading() | ||
| 448 | } | 494 | } |
| 449 | } | 495 | } |
| 450 | 496 | ||
| 451 | // 页面加载时执行 | 497 | // 页面加载时执行 |
| 452 | -onMounted(() => { | 498 | +onMounted(async () => { |
| 499 | + // 并行加载品牌型号数据 | ||
| 500 | + await loadBrandsModels() | ||
| 501 | + | ||
| 453 | if (isEditMode.value) { | 502 | if (isEditMode.value) { |
| 454 | loadAuthData() | 503 | loadAuthData() |
| 455 | } | 504 | } |
| 505 | + | ||
| 456 | // 动态修改标题 | 506 | // 动态修改标题 |
| 457 | wx.setNavigationBarTitle({ | 507 | wx.setNavigationBarTitle({ |
| 458 | title: isEditMode.value ? '编辑认证' : '发布认证' | 508 | title: isEditMode.value ? '编辑认证' : '发布认证' | ... | ... |
-
Please register or login to post a comment