Files
property-resident-side/property-uniapp-project/pageSubPack/my/addCar.vue
zhouyanli ea1acf1bbc 修复bug
2026-08-27 18:01:35 +08:00

496 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="contentStyle">
<view class="status-bar"></view>
<uni-nav-bar :title="type + '车辆'" left-icon="left" @clickLeft="goBack" backgroundColor="transparent"
:border="false">
</uni-nav-bar>
<scroll-view class="page" scroll-y="true">
<view class="form-container">
<view class="form-item">
<view class="item-label">车牌号</view>
<input class="item-input" v-model="formData.carNum" placeholder="请输入"
placeholder-class="input-placeholder" maxlength="8" @input="onCarNumInput" />
</view>
<view class="form-item">
<text class="item-label">车辆品牌</text>
<input class="item-input" v-model="formData.carBrand" placeholder="请输入"
placeholder-class="input-placeholder" maxlength="32"/>
</view>
<!-- <view class="form-item" @click="selectItem('车辆品牌')">
<text class="item-label">车辆品牌</text>
<text class="item-placeholder"
:class="{selected: formData.carBrand}">{{ formData.carBrand || '请选择' }}</text>
<text class="item-arrow"></text>
</view> -->
<view class="form-item">
<view class="item-label">车辆型号</view>
<input class="item-input" v-model="formData.carModel" placeholder="请输入"
placeholder-class="input-placeholder" maxlength="32"/>
</view>
<view class="form-item">
<view class="item-label">车辆颜色</view>
<input class="item-input" v-model="formData.carColor" placeholder="请输入"
placeholder-class="input-placeholder" maxlength="5"/>
</view>
<view class="form-item upload-item">
<view class="item-label">车辆图片</view>
<view class="upload-box" @click="chooseImage">
<view v-if="!carImageUpload.state.previewUrl" class="upload-tip">
<text class="plus-icon">+</text>
<text class="upload-text">上传照片</text>
</view>
<image v-else :src="carImageUpload.state.previewUrl" class="car-image" mode="aspectFill" />
</view>
</view>
</view>
</scroll-view>
<view class="bottom-btn-wrap">
<button class="next-btn" :disabled="isSubmitting" @click="saveCar">保存</button>
</view>
</view>
</template>
<script setup>
import {
ref,
onMounted
} from 'vue'
import {
onReachBottom,
onPullDownRefresh,
onLoad,
onShow
} from '@dcloudio/uni-app'
import {
postCar,
putCar,
getCarDetail,
} from '@/pageSubPack/api/apiSub.js'
import {
useImageUpload
} from '/pageSubPack/api/useImageUpload.js'
const statusBarHeight = ref(20)
const goBack = () => {
uni.navigateBack()
}
const id = ref(undefined)
const type = ref('')
const isSubmitting = ref(false)
const formData = ref({
carNum: '',
carBrand: '',
carModel: '',
carColor: '',
carImageUrl: '',
carBrandId: ''
})
onLoad((option) => {
if (uni.getStorageSync('operationType') == 'update') {
if (option.id) {
id.value = option.id
uni.setStorageSync('parkingSpaceId', option.id)
loadCarDetail()
} else {
id.value = uni.getStorageSync('parkingSpaceId')
}
type.value = '修改'
} else if (uni.getStorageSync('operationType') == 'add') {
id.value = undefined
type.value = '添加'
}
})
onMounted(() => {
})
const loadCarDetail = async () => {
// 防重复请求
try {
const res = await getCarDetail({
id: id.value
})
const data = res.data
formData.value.carNum = data.carNum
// uni.setStorageSync('carNum', data.carNum)
formData.value.carBrand = data.carBrand
// uni.setStorageSync('carBrand', data.carBrand)
formData.value.carModel = data.carModel
// uni.setStorageSync('carModel', data.carModel)
formData.value.carColor = data.carColor
// uni.setStorageSync('carColor', data.carColor)
formData.value.carImageUrl = data.carImageUrl
// uni.setStorageSync('carImageUrl', data.carImageUrl)
formData.value.carBrandId = data.carBrandId
// uni.setStorageSync('carBrandId', data.carBrandId)
// 使用 hook 设置回显图片
carImageUpload.setPreviewUrl(data.carImageUrl?.url || data.carImageUrl || '')
} catch (err) {
uni.showToast({
title: err.msg || '网络异常',
icon: 'none'
})
} finally {
}
}
const carImageUpload = useImageUpload({
uploadUrl: '/api/resident/file/uploadImage'
})
// 车牌号输入限制:省份简称(中文)+ 城市代码(字母)+ 序号(字母数字)
const onCarNumInput = (e) => {
let value = e.detail.value || ''
// 过滤非法字符,只保留中文、大写字母、数字
value = value.replace(/[^一-龥A-Z0-9]/g, '')
// 首字符必须是中文省份简称
if (value.length > 0 && !/^[一-龥]/.test(value)) {
value = value.replace(/[^一-龥]/g, '')
}
// 第二位必须是字母
if (value.length > 1) {
const first = value.charAt(0)
const second = value.charAt(1)
if (!/^[A-Z]$/.test(second)) {
value = first + second.replace(/[^A-Za-z]/g, '').toUpperCase()
}
// 剩余位只允许字母和数字
if (value.length > 2) {
const rest = value.substring(2)
value = value.substring(0, 2) + rest.replace(/[^A-Za-z0-9]/g, '').toUpperCase()
}
}
// 统一转大写
value = value.toUpperCase()
formData.value.carNum = value
}
const inputChange = (typeStr) => {
if (typeStr == '车牌号') {
uni.setStorageSync('carNum', formData.value.carNum)
} else if (typeStr == '车辆型号') {
uni.setStorageSync('carModel', formData.value.carModel)
} else if (typeStr == '车辆品牌') {
uni.setStorageSync('brand', formData.value.brand)
} else if (typeStr == '车辆颜色') {
uni.setStorageSync('carColor', formData.value.carColor)
}
}
const chooseImage = async () => {
try {
await carImageUpload.chooseImage()
} catch (err) {
// 用户取消选择
}
}
// 车牌号正则:省份简称 + 城市代码字母 + 序号
// 蓝牌5位字母数字新能源绿牌6位其中第3位或末位必须是 D/F
const CAR_NUM_REGEX = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤川青藏琼宁][A-Z](?:[A-Z0-9]{5}|[DF][A-Z0-9]{5}|[A-Z0-9]{5}[DF])$/
const saveCar = async () => {
if (isSubmitting.value) return
if (!formData.value.carNum) {
return uni.showToast({
title: "请输入车牌号",
icon: "none"
})
}
if (!CAR_NUM_REGEX.test(formData.value.carNum)) {
return uni.showToast({
title: "请输入正确的车牌号",
icon: "none"
})
}
isSubmitting.value = true
uni.showLoading({
title: '提交中...',
mask: true
})
try {
let carImageUrl = ''
// 编辑模式
if (id.value) {
if (carImageUpload.state.tempFilePath) {
carImageUrl = await carImageUpload.uploadImage()
} else {
carImageUrl = carImageUpload.state.serverUrl || carImageUpload.state.previewUrl || ''
}
} else {
// 新增模式:必须上传
carImageUrl = await carImageUpload.uploadImage()
}
submitCarData(carImageUrl)
} catch (err) {
isSubmitting.value = false
uni.hideLoading()
uni.showToast({
title: err.msg || '网络异常',
icon: 'none'
})
}
}
const submitCarData = (carImageUrl) => {
// 处理 carImageUrl 可能是对象 { url: string } 的情况
const imageUrl = typeof carImageUrl === 'object' ? carImageUrl.url : carImageUrl
const params = {
carNum: formData.value.carNum,
carBrand: formData.value.carBrand,
// carBrandId: formData.value.carBrandId,
carModel: formData.value.carModel,
carColor: formData.value.carColor,
carImageUrl: imageUrl,
villageId:uni.getStorageSync('currentVillageId'),
userId: uni.getStorageSync('userId')
}
// 编辑模式下添加 id
if (id.value) {
params.id = id.value
putCar(params).then(res => {
console.log(res);
uni.hideLoading()
if (res.code === 200) {
uni.showToast({
title: '提交成功',
icon: 'success'
})
setTimeout(() => {
uni.removeStorageSync("carNum")
uni.removeStorageSync("carBrand")
uni.removeStorageSync("carModel")
uni.removeStorageSync("carColor")
uni.removeStorageSync("carImageUrl")
uni.removeStorageSync("carBrandId")
uni.removeStorageSync("checkType")
uni.navigateBack()
}, 1500)
} else {
isSubmitting.value = false
uni.showToast({
title: res.msg || '提交失败',
icon: 'none'
})
}
}).catch((err) => {
isSubmitting.value = false
uni.hideLoading()
uni.showToast({
title: err.msg || '提交失败',
icon: 'none'
})
})
} else {
postCar(params).then(res => {
uni.hideLoading()
if (res.code === 200) {
uni.showToast({
title: '提交成功',
icon: 'success'
})
setTimeout(() => {
uni.removeStorageSync("carNum")
uni.removeStorageSync("carBrand")
uni.removeStorageSync("carModel")
uni.removeStorageSync("carColor")
uni.removeStorageSync("carImageUrl")
uni.removeStorageSync("carBrandId")
uni.removeStorageSync("checkType")
uni.navigateBack()
}, 1500)
} else {
isSubmitting.value = false
uni.showToast({
title: res.msg || '提交失败',
icon: 'none'
})
}
}).catch((err) => {
isSubmitting.value = false
uni.hideLoading()
uni.showToast({
title: err.msg || '提交失败',
icon: 'none'
})
})
}
}
const selectItem = (typeStr) => {
if (typeStr === '车辆品牌') {
uni.navigateTo({
url: '/pageSubPack/my/selectCarBrand'
})
}
}
</script>
<style lang="scss">
page {
background: #f9f9f9;
}
</style>
<style scoped>
.status-bar {
width: 100vw;
height: 46px;
}
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
background-color: #f9f9f9;
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 40rpx;
box-sizing: border-box;
background-color: #fff;
}
.form-container {
padding: 0 0px;
}
.form-item {
display: flex;
align-items: center;
padding: 30rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.item-label {
font-size: 28rpx;
color: #000;
width: 90px;
font-weight: 600;
}
.item-placeholder {
flex: 1;
font-size: 28rpx;
color: #999;
}
.item-placeholder.selected {
flex: 1;
font-size: 28rpx;
color: #333;
}
.item-arrow {
display: inline-block;
width: 10rpx;
height: 10rpx;
border-top: 2rpx solid #999;
border-right: 2rpx solid #999;
transform: rotate(45deg);
margin-left: 10rpx;
margin-right: 10rpx;
}
.bottom-btn-wrap {
padding: 20rpx 30rpx 40rpx;
display: flex;
gap: 20rpx;
background-color: #fff;
}
.next-btn {
width: 100%;
height: 96rpx;
line-height: 96rpx;
background-color: #1677ff;
color: #fff;
font-size: 36rpx;
border-radius: 12rpx;
border: none;
}
.upload-item {
align-items: flex-start;
padding-top: 40rpx;
padding-bottom: 40rpx;
border-bottom: none;
}
.upload-box {
width: 280rpx;
height: 280rpx;
border: 2rpx solid #e5e5e5;
border-radius: 8rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: #f9f9f9;
}
.upload-tip {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.plus-icon {
font-size: 80rpx;
color: #999;
line-height: 1;
margin-bottom: 10rpx;
}
.upload-text {
font-size: 28rpx;
color: #999;
}
.car-image {
width: 100%;
height: 100%;
border-radius: 8rpx;
}
.item-input {
flex: 1;
height: 40rpx;
font-size: 28rpx;
color: #333;
border: none;
outline: none;
background: transparent;
}
.input-placeholder {
color: #999 !important;
font-size: 28rpx !important;
}
</style>