hookehuyr

refactor(车辆表单): 统一字段命名规范并优化表单逻辑

- 将表单字段从驼峰命名改为下划线命名以保持一致性
- 移除电池损耗度字段及相关逻辑
- 简化新旧程度选项
- 将年份选择器从普通选择器改为日期选择器
- 添加实际API调用逻辑替代模拟请求
/*
* @Date: 2025-07-09 14:58:51
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-07-09 15:55:25
* @LastEditTime: 2025-07-09 16:07:38
* @FilePath: /jgdl/src/api/car.js
* @Description: 车辆相关API接口
*/
......
......@@ -132,26 +132,25 @@
</nut-form-item>
<!-- 车辆出厂年份 -->
<nut-form-item label-position="top" label="车辆出厂年份" prop="year">
<nut-form-item label-position="top" label="车辆出厂年份" prop="manufacture_year">
<view class="form-item-content" @click="showYearPicker">
<text class="form-value">{{ formData.year || '请选择' }}</text>
<text class="form-value">{{ formData.manufacture_year || '请选择' }}</text>
<Right class="arrow-icon" />
</view>
</nut-form-item>
<!-- 新旧程度 -->
<nut-form-item label-position="top" label="新旧程度" prop="condition" required
:rules="[{ required: true, message: '请选择新旧程度' }]">
<nut-form-item label-position="top" label="新旧程度" prop="new_level">
<view class="form-item-content" @click="showConditionPicker">
<text class="form-value">{{ formData.condition || '请选择' }}</text>
<text class="form-value">{{ formData.new_level || '请选择' }}</text>
<Right class="arrow-icon" />
</view>
</nut-form-item>
<!-- 行驶里程 -->
<nut-form-item label="行驶里程" prop="mileage" required
<nut-form-item label="行驶里程" prop="total_mileage_km" required
:rules="[{ required: true, message: '请输入行驶里程' }]">
<nut-input v-model="formData.mileage" placeholder="1200" type="number" input-align="right">
<nut-input v-model="formData.total_mileage_km" placeholder="1200" type="number" input-align="right">
<template #right>
<text class="unit">公里</text>
</template>
......@@ -159,8 +158,8 @@
</nut-form-item>
<!-- 续航里程 -->
<nut-form-item label="续航里程" prop="range">
<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>
......@@ -177,8 +176,8 @@
</nut-form-item>
<!-- 电池容量 -->
<nut-form-item label="电池容量" prop="batteryCapacity">
<nut-input v-model="formData.batteryCapacity" placeholder="20" type="number"
<nut-form-item label="电池容量" prop="battery_capacity_ah">
<nut-input v-model="formData.battery_capacity_ah" placeholder="20" type="number"
input-align="right">
<template #right>
<text class="unit">Ah</text>
......@@ -187,25 +186,25 @@
</nut-form-item>
<!-- 电池损耗度 -->
<nut-form-item label-position="top" label="电池损耗度" prop="batteryWear">
<!-- <nut-form-item label-position="top" label="电池损耗度" prop="batteryWear">
<view class="form-item-content" @click="showBatteryWearPicker">
<text class="form-value">{{ formData.batteryWear || '请选择' }}</text>
<Right class="arrow-icon" />
</view>
</nut-form-item>
</nut-form-item> -->
<!-- 刹车磨损度 -->
<nut-form-item label-position="top" label="刹车磨损度" prop="brakeWear">
<nut-form-item label-position="top" label="刹车磨损度" prop="brake_wear_level">
<view class="form-item-content" @click="showBrakeWearPicker">
<text class="form-value">{{ formData.brakeWear || '请选择' }}</text>
<text class="form-value">{{ formData.brake_wear_level || '请选择' }}</text>
<Right class="arrow-icon" />
</view>
</nut-form-item>
<!-- 轮胎磨损度 -->
<nut-form-item label-position="top" label="轮胎磨损度" prop="tireWear">
<nut-form-item label-position="top" label="轮胎磨损度" prop="tire_wear_level">
<view class="form-item-content" @click="showTireWearPicker">
<text class="form-value">{{ formData.tireWear || '请选择' }}</text>
<text class="form-value">{{ formData.tire_wear_level || '请选择' }}</text>
<Right class="arrow-icon" />
</view>
</nut-form-item>
......@@ -221,7 +220,7 @@
</view>
<view class="form-item-right">
<text class="price-symbol">¥</text>
<input v-model="formData.sellingPrice" placeholder="3200" type="text" class="price-input" />
<input v-model="formData.price" placeholder="3200" type="text" class="price-input" />
</view>
</view>
......@@ -232,7 +231,7 @@
</view>
<view class="form-item-right">
<text class="market-price-symbol">¥</text>
<input v-model="formData.marketPrice" placeholder="6500" type="text"
<input v-model="formData.market_price" placeholder="6500" type="text"
class="market-price-input" />
</view>
</view>
......@@ -240,7 +239,7 @@
<!-- 车辆描述 -->
<view class="form-section">
<nut-textarea v-model="formData.description" placeholder="请描述车辆详情,如使用感受、车况特点等" />
<nut-textarea v-model="formData.note" placeholder="请描述车辆详情,如使用感受、车况特点等" />
</view>
</view>
......@@ -280,8 +279,13 @@
<!-- 年份选择 -->
<nut-popup v-model:visible="yearPickerVisible" position="bottom">
<nut-picker v-model="yearValue" :columns="yearOptions" title="选择出厂年份" @confirm="onYearConfirm"
@cancel="yearPickerVisible = false" />
<nut-date-picker
v-model="yearValue"
title="选择出厂年份"
type="year-month"
@confirm="onYearConfirm"
@cancel="yearPickerVisible = false"
/>
</nut-popup>
<!-- 新旧程度选择 -->
......@@ -319,6 +323,9 @@ import BrandModelPicker from '@/components/BrandModelPicker.vue'
import { checkPermission, PERMISSION_TYPES } from '@/utils/permission'
import './index.less'
// 导入接口
import { addVehicleAPI, editVehicleAPI, getVehicleDetailAPI } from '@/api/car';
const themeVars = ref({
navbarBackground: '#fb923c',
navbarColor: '#ffffff',
......@@ -341,12 +348,6 @@ const isAuthMode = ref(type === 'auth' && id)
const isMyCarMode = ref(type === 'myCar' && id)
const carId = ref(id || '')
// 文件上传相关
// const frontFileList = ref([]) // 正面照(保留用于兼容旧代码)
// const leftFileList = ref([]) // 左侧照(保留用于兼容旧代码)
// const rightFileList = ref([]) // 右侧照(保留用于兼容旧代码)
// const otherFileList = ref([]) // 其他照片(保留用于兼容旧代码)
// 已上传图片的URL
const uploadedImages = reactive({
front: '',
......@@ -370,18 +371,18 @@ const formData = reactive({
brand: '',
model: '',
brandModel: '', // 品牌型号组合字段,用于表单验证
year: '',
condition: '',
mileage: '1200',
range: '60',
manufacture_year: '',
new_level: '',
total_mileage_km: '1200',
range_km: '60',
maxSpeed: '25',
batteryCapacity: '20',
battery_capacity_ah: '20',
batteryWear: '',
brakeWear: '',
tireWear: '',
sellingPrice: '3200',
marketPrice: '6500',
description: ''
brake_wear_level: '',
tire_wear_level: '',
price: '3200',
market_price: '6500',
note: ''
})
// 选择器显示状态
......@@ -402,7 +403,7 @@ const brandModelPickerRef = ref(null)
const schoolValue = ref([])
const brandValue = ref([])
const modelValue = ref([])
const yearValue = ref([])
const yearValue = ref(new Date())
const conditionValue = ref([])
const batteryWearValue = ref([])
const brakeWearValue = ref([])
......@@ -441,19 +442,10 @@ const modelOptions = ref([
{ text: '其他型号', value: '其他型号' }
])
const yearOptions = ref([
{ text: '2024年', value: '2024年' },
{ text: '2023年', value: '2023年' },
{ text: '2022年', value: '2022年' },
{ text: '2021年', value: '2021年' },
{ text: '2020年', value: '2020年' },
{ text: '2019年', value: '2019年' },
{ text: '2018年及以前', value: '2018年及以前' }
])
const conditionOptions = ref([
{ text: '全新', value: '全新' },
{ text: '9成新', value: '9成新' },
{ text: '8成新', value: '8成新' },
{ text: '7成新', value: '7成新' },
{ text: '6成新', value: '6成新' },
......@@ -468,10 +460,6 @@ const wearLevelOptions = ref([
{ text: '需要更换', value: '需要更换' }
])
// 品牌型号数据已移至 BrandModelPicker 组件中
/**
* 触发图片上传
* @param {String} type - 图片类型 (front/left/right/other)
......@@ -570,15 +558,6 @@ const deleteImage = (type) => {
})
}
// 保留旧的上传逻辑作为备用(已注释)
// const afterRead = (event, type) => { ... }
// 保留旧的删除逻辑作为备用(已注释)
// const onDeleteFront = (file, index) => { frontFileList.value.splice(index, 1) }
// const onDeleteLeft = (file, index) => { leftFileList.value.splice(index, 1) }
// const onDeleteRight = (file, index) => { rightFileList.value.splice(index, 1) }
// const onDeleteOther = (file, index) => { otherFileList.value.splice(index, 1) }
/**
* 显示学校选择器
*/
......@@ -624,9 +603,9 @@ const showConditionPicker = () => {
/**
* 显示电池损耗度选择器
*/
const showBatteryWearPicker = () => {
batteryWearPickerVisible.value = true
}
// const showBatteryWearPicker = () => {
// batteryWearPickerVisible.value = true
// }
/**
* 显示刹车磨损度选择器
......@@ -671,7 +650,7 @@ const onModelConfirm = ({ selectedValue }) => {
* 年份选择确认
*/
const onYearConfirm = ({ selectedValue }) => {
formData.year = selectedValue[0]
formData.manufacture_year = `${selectedValue[0]}年`
yearPickerVisible.value = false
}
......@@ -679,7 +658,7 @@ const onYearConfirm = ({ selectedValue }) => {
* 新旧程度选择确认
*/
const onConditionConfirm = ({ selectedValue }) => {
formData.condition = selectedValue[0]
formData.new_level = selectedValue[0]
conditionPickerVisible.value = false
}
......@@ -695,7 +674,7 @@ const onBatteryWearConfirm = ({ selectedValue }) => {
* 刹车磨损度选择确认
*/
const onBrakeWearConfirm = ({ selectedValue }) => {
formData.brakeWear = selectedValue[0]
formData.brake_wear_level = selectedValue[0]
brakeWearPickerVisible.value = false
}
......@@ -703,7 +682,7 @@ const onBrakeWearConfirm = ({ selectedValue }) => {
* 轮胎磨损度选择确认
*/
const onTireWearConfirm = ({ selectedValue }) => {
formData.tireWear = selectedValue[0]
formData.tire_wear_level = selectedValue[0]
tireWearPickerVisible.value = false
}
......@@ -737,10 +716,32 @@ const onBrandModelCancel = () => {
// 可以在这里处理取消逻辑
}
const createCar = async (data) => {
const { code } = await addVehicleAPI({ ...data })
if (code) {
Taro.showToast({
title: '发布成功',
icon: 'success'
})
Taro.navigateBack()
}
}
const updateCar = async (id, data) => {
const { code } = await updateVehicleAPI({ id, ...data })
if (code) {
Taro.showToast({
title: '保存成功',
icon: 'success'
})
Taro.navigateBack()
}
}
/**
* 发布/保存车辆
*/
const onPublish = () => {
const onPublish = async () => {
if (!validateForm()) return
const loadingTitle = isEditMode.value ? '保存中...' : '发布中...'
......@@ -758,19 +759,21 @@ const onPublish = () => {
const submitData = {
...formData,
images: images,
imageUrls: Object.values(images).filter(url => url) // 过滤空URL
photo_meta_ids: Object.values(images).filter(url => url) // 过滤空URL
}
console.warn(submitData);
if (isEditMode.value) {
submitData.id = carId.value
}
// TODO: 在此处调用实际的API接口提交数据
// if (isEditMode.value) {
// updateCar(carId.value, submitData)
// } else {
// createCar(submitData)
// }
// 在此处调用实际的API接口提交数据
if (isEditMode.value) {
await updateCar(carId.value, submitData)
} else {
await createCar(submitData)
}
// 模拟请求
setTimeout(() => {
......@@ -809,17 +812,17 @@ const validateForm = () => {
return false
}
if (!formData.condition) {
Taro.showToast({ title: '请选择新旧程度', icon: 'none' })
return false
}
// if (!formData.new_level) {
// Taro.showToast({ title: '请选择新旧程度', icon: 'none' })
// return false
// }
if (!formData.mileage || formData.mileage <= 0) {
if (!formData.total_mileage_km || formData.total_mileage_km <= 0) {
Taro.showToast({ title: '请输入正确的行驶里程', icon: 'none' })
return false
}
if (!formData.sellingPrice) {
if (!formData.price) {
Taro.showToast({ title: '请输入出让价格', icon: 'none' })
return false
}
......@@ -844,18 +847,18 @@ const loadCarData = async () => {
school: '上海理工大学',
brand: '小牛电动',
model: 'NGT',
year: '2023年',
condition: '9成新',
mileage: '1200',
range: '60',
manufacture_year: '2023年',
new_level: '9成新',
total_mileage_km: '1200',
range_km: '60',
maxSpeed: '25',
batteryCapacity: '20',
battery_capacity_ah: '20',
batteryWear: '轻微磨损',
brakeWear: '轻微磨损',
tireWear: '轻微磨损',
sellingPrice: '3200',
marketPrice: '6500',
description: '车况良好,电池续航正常,无重大事故,平时保养得当。',
brake_wear_level: '轻微磨损',
tire_wear_level: '轻微磨损',
price: '3200',
market_price: '6500',
note: '车况良好,电池续航正常,无重大事故,平时保养得当。',
images: {
front: 'https://picsum.photos/300/200?random=1',
left: 'https://picsum.photos/300/200?random=2',
......@@ -869,18 +872,18 @@ const loadCarData = async () => {
school: mockCarData.school,
brand: mockCarData.brand,
model: mockCarData.model,
year: mockCarData.year,
condition: mockCarData.condition,
mileage: mockCarData.mileage,
range: mockCarData.range,
manufacture_year: mockCarData.manufacture_year,
new_level: mockCarData.new_level,
total_mileage_km: mockCarData.total_mileage_km,
range_km: mockCarData.range_km,
maxSpeed: mockCarData.maxSpeed,
batteryCapacity: mockCarData.batteryCapacity,
battery_capacity_ah: mockCarData.battery_capacity_ah,
batteryWear: mockCarData.batteryWear,
brakeWear: mockCarData.brakeWear,
tireWear: mockCarData.tireWear,
sellingPrice: mockCarData.sellingPrice,
marketPrice: mockCarData.marketPrice,
description: mockCarData.description
brake_wear_level: mockCarData.brake_wear_level,
tire_wear_level: mockCarData.tire_wear_level,
price: mockCarData.price,
market_price: mockCarData.market_price,
note: mockCarData.note
})
// 填充图片数据
......