496 lines
12 KiB
Vue
496 lines
12 KiB
Vue
<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>
|