feat(delivery): 优化身份证及凭证图片上传和提交校验

- 在提交前新增必填图片校验,确保身份证正反面及其他凭证全部上传
- 优化图片上传占位样式,新增上传提示文案
- uploadImage函数增加上传中loading提示及成功失败反馈toast
- 格式化后端错误提示,过滤错误码尾巴,提升用户体验
- 明确图片字段标签,标注为“身份证正面(人像面)”等,增强用户指引
This commit is contained in:
2026-07-26 14:09:31 +08:00
parent 96d153ac6d
commit 6abb49c911
6 changed files with 312 additions and 81 deletions

View File

@@ -6,34 +6,49 @@
<view class="reject" v-else-if="form.apply_status == 30">
<u-alert-tips type="error" title="您的申请已被驳回:" :description="form.reject_reason"></u-alert-tips>
</view>
<u-form :model="form" ref="uForm" label-width="140rpx">
<u-form :model="form" ref="uForm" label-width="180rpx">
<!-- 标题 -->
<view class="page-title">身份证信息</view>
<view class="page-title">身份证信息必传</view>
<view class="form-wrapper">
<u-form-item label="正面" prop="img1">
<image :src="orderSourceData[0]" v-if="orderSourceData[0]" mode="aspectFill" style="width: 300rpx;height: 200rpx" @click="chooseImage(1)"></image>
<image src="../../static/not-dealer.png" v-else mode="aspectFill" style="width: 300rpx;height: 200rpx" @click="chooseImage(1)"></image>
<u-form-item label="身份证正面" prop="img1" required>
<image :src="orderSourceData[0]" v-if="orderSourceData[0]" mode="aspectFill" class="upload-img" @click="chooseImage(1)"></image>
<view v-else class="upload-placeholder" @click="chooseImage(1)">
<text class="placeholder-icon">+</text>
<text class="placeholder-text">点击上传身份证正面人像面</text>
</view>
</u-form-item>
<u-form-item label="反面" prop="img2">
<image :src="orderSourceData[1]" v-if="orderSourceData[1]" mode="aspectFill" style="width: 300rpx;height: 200rpx" @click="chooseImage(2)"></image>
<image src="../../static/not-dealer.png" v-else mode="aspectFill" style="width: 300rpx;height: 200rpx" @click="chooseImage(2)"></image>
<u-form-item label="身份证反面" prop="img2" required>
<image :src="orderSourceData[1]" v-if="orderSourceData[1]" mode="aspectFill" class="upload-img" @click="chooseImage(2)"></image>
<view v-else class="upload-placeholder" @click="chooseImage(2)">
<text class="placeholder-icon">+</text>
<text class="placeholder-text">点击上传身份证反面国徽面</text>
</view>
</u-form-item>
</view>
<!-- 标题 -->
<view class="page-title">其他</view>
<view class="page-title">其他凭证必传</view>
<!-- 表单组件 -->
<view class="form-wrapper">
<u-form-item label="人车合照" prop="img3">
<image :src="orderSourceData[2]" v-if="orderSourceData[2]" mode="aspectFill" style="width: 300rpx;height: 200rpx" @click="chooseImage(3)"></image>
<image src="../../static/not-dealer.png" v-else mode="aspectFill" style="width: 300rpx;height: 200rpx" @click="chooseImage(3)"></image>
<u-form-item label="人车合照" prop="img3" required>
<image :src="orderSourceData[2]" v-if="orderSourceData[2]" mode="aspectFill" class="upload-img" @click="chooseImage(3)"></image>
<view v-else class="upload-placeholder" @click="chooseImage(3)">
<text class="placeholder-icon">+</text>
<text class="placeholder-text">点击上传人车合照</text>
</view>
</u-form-item>
<u-form-item label="车子照片" prop="img4">
<image :src="orderSourceData[3]" v-if="orderSourceData[3]" mode="aspectFill" style="width: 300rpx;height: 200rpx" @click="chooseImage(4)"></image>
<image src="../../static/not-dealer.png" v-else mode="aspectFill" style="width: 300rpx;height: 200rpx" @click="chooseImage(4)"></image>
<u-form-item label="车子照片" prop="img4" required>
<image :src="orderSourceData[3]" v-if="orderSourceData[3]" mode="aspectFill" class="upload-img" @click="chooseImage(4)"></image>
<view v-else class="upload-placeholder" @click="chooseImage(4)">
<text class="placeholder-icon">+</text>
<text class="placeholder-text">点击上传车子照片</text>
</view>
</u-form-item>
<u-form-item label="安装照片" prop="img5">
<image :src="orderSourceData[4]" v-if="orderSourceData[4]" mode="aspectFill" style="width: 300rpx;height: 200rpx" @click="chooseImage(5)"></image>
<image src="../../static/not-dealer.png" v-else mode="aspectFill" style="width: 300rpx;height: 200rpx" @click="chooseImage(5)"></image>
<u-form-item label="安装照片" prop="img5" required>
<image :src="orderSourceData[4]" v-if="orderSourceData[4]" mode="aspectFill" class="upload-img" @click="chooseImage(5)"></image>
<view v-else class="upload-placeholder" @click="chooseImage(5)">
<text class="placeholder-icon">+</text>
<text class="placeholder-text">点击上传安装完成照片</text>
</view>
</u-form-item>
</view>
@@ -118,6 +133,15 @@
trigger: ['blur', 'change']
}],
}
// 图片槽位说明(与 orderSourceData 下标一一对应)
const imageSlotLabels = ['身份证正面', '身份证反面', '人车合照', '车子照片', '安装照片']
// 过滤后端返回的错误码尾巴(如 ".FAILUREFP03333"),让提示更友好
function formatErrorMsg(msg) {
if (!msg) return '操作失败,请稍后重试'
return String(msg).replace(/\.[A-Z0-9]{4,}\s*$/, '').trim() || '操作失败,请稍后重试'
}
export default {
data() {
@@ -241,70 +265,87 @@
app.getData()
})
}, */
// 表单提交
handleSubmit() {
// if(orderSourceData.length < 5) {
// return
// }
const that = this
const app = this
const { orderId,orderSourceData,captcha,receiptPhone,emergentUser,officeAddress, homeAddress } = app
receiptOrder({
orderId: orderId,
orderSourceData: JSON.stringify(orderSourceData),
captcha: captcha,
receiptPhone: receiptPhone,
emergentUser: emergentUser,
officeAddress: officeAddress,
homeAddress: homeAddress
}).then(result => {
if(result.code == 0) {
that.$success("确认收货成功!")
// uni.navigateBack()
setTimeout(()=>{
that.$navTo('pages/order/index',{}, 'redirectTo')
}, 1000)
} else {
that.$toast(result.message)
}
}).catch(err => {
that.$toast(err.message)
})
},
// 上传图片
chooseImage(id) {
const app = this
const { orderId } = this
const type = 'photo' + id
// 选择图片
uni.chooseImage({
count: 1,
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success(chooseImageRes) {
const tempFilePaths = chooseImageRes.tempFilePaths;
uploadFile({
filePath: tempFilePaths[0],
fileType: 'image',
name: 'file'
}).then(res => {
// 表单提交
handleSubmit() {
const that = this
const app = this
const { orderId, orderSourceData, captcha, receiptPhone, emergentUser, officeAddress, homeAddress } = app
// 1) 校验 5 张图是否都已上传
const missingIndex = []
for (let i = 0; i < imageSlotLabels.length; i++) {
if (!orderSourceData[i]) {
missingIndex.push(i)
}
}
if (missingIndex.length > 0) {
// 优先提示最关键的那张(身份证正面)
const firstMissing = missingIndex.includes(0) ? 0 : missingIndex[0]
that.$toast(`请先上传${imageSlotLabels[firstMissing]}`, 2500)
return
}
receiptOrder({
orderId: orderId,
orderSourceData: JSON.stringify(orderSourceData),
captcha: captcha,
receiptPhone: receiptPhone,
emergentUser: emergentUser,
officeAddress: officeAddress,
homeAddress: homeAddress
}).then(result => {
if(result.code == 0) {
that.$success("确认收货成功!")
// uni.navigateBack()
setTimeout(()=>{
that.$navTo('pages/order/index',{}, 'redirectTo')
}, 1000)
} else {
that.$toast(formatErrorMsg(result.message), 2500)
}
}).catch(err => {
that.$toast(formatErrorMsg((err && err.message) || '提交失败,请稍后重试'), 2500)
})
},
// 上传图片
chooseImage(id) {
const app = this
const { orderId } = this
const type = 'photo' + id
// 选择图片
uni.chooseImage({
count: 1,
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success(chooseImageRes) {
const tempFilePaths = chooseImageRes.tempFilePaths;
const slotLabel = imageSlotLabels[id - 1] || '图片'
uni.showLoading({ title: '上传中...', mask: true })
uploadFile({
filePath: tempFilePaths[0],
fileType: 'image',
name: 'file'
}).then(res => {
console.log('res', res)
// app.orderSourceData.push(res.data.url)
if(app.orderSourceData[id-1]){
app.orderSourceData.splice(id-1, 1, res.data.url)
} else {
app.orderSourceData.push(res.data.url)
}
console.log('app.orderSourceData', app.orderSourceData)
})
}
});
},
// app.orderSourceData.push(res.data.url)
if(app.orderSourceData[id-1]){
app.orderSourceData.splice(id-1, 1, res.data.url)
} else {
app.orderSourceData.push(res.data.url)
}
console.log('app.orderSourceData', app.orderSourceData)
app.$success(`${slotLabel}上传成功`)
}).catch(err => {
app.$toast(formatErrorMsg((err && (err.errMsg || err.message)) || `${slotLabel}上传失败,请重试`), 2500)
}).finally(() => {
uni.hideLoading()
})
}
});
},
}
@@ -334,6 +375,39 @@
background: #fff;
}
/* 上传图片(已上传时) */
.upload-img {
width: 300rpx;
height: 200rpx;
border-radius: 8rpx;
}
/* 上传占位符(未上传时) */
.upload-placeholder {
width: 300rpx;
height: 200rpx;
border: 2rpx dashed #c8c8c8;
border-radius: 8rpx;
background: #fafafa;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #999;
.placeholder-icon {
font-size: 56rpx;
line-height: 1;
color: #c0c4cc;
}
.placeholder-text {
font-size: 22rpx;
margin-top: 10rpx;
color: #909399;
}
}
/* 底部操作栏 */
.footer {